<commit_before>/*
Copyright 2013 The Camlistore Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"

	"camlistore.org/pkg/cmdmain"
)

var (
	noBuild  = flag.Bool("nobuild", false, "do not rebuild anything")
	race     = flag.Bool("race", false, "build with race detector")
	quiet, _ = strconv.ParseBool(os.Getenv("CAMLI_QUIET"))
	// Whether to build the subcommand with sqlite support. This only
	// concerns the server subcommand, which sets it to serverCmd.sqlite.
	withSqlite bool
)

// The path to the Camlistore source tree. Any devcam command
// should be run from there.
var camliSrcRoot string

// sysExec is set to syscall.Exec on platforms that support it.
var sysExec func(argv0 string, argv []string, envv []string) (err error)

// runExec execs bin. If the platform doesn't support exec, it runs it and waits
// for it to finish.
func runExec(bin string, args []string, env *Env) error {
	if sysExec != nil {
		sysExec(bin, append([]string{filepath.Base(bin)}, args...), env.Flat())
	}

	cmd := exec.Command(bin, args...)
	cmd.Env = env.Flat()
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Start(); err != nil {
		return fmt.Errorf("Could not run %v: %v", bin, err)
	}
	go handleSignals(cmd.Process)
	return cmd.Wait()
}

// cpDir copies the contents of src dir into dst dir.
// filter is a list of file suffixes to skip. ex: ".go"
func cpDir(src, dst string, filter []string) error {
	return filepath.Walk(src, func(fullpath string, fi os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		for _, suffix := range filter {
			if strings.HasSuffix(fi.Name(), suffix) {
				return nil
			}
		}
		suffix, err := filepath.Rel(src, fullpath)
		if err != nil {
			return fmt.Errorf("Failed to find Rel(%q, %q): %v", src, fullpath, err)
		}
		if fi.IsDir() {
			return nil
		}
		return cpFile(fullpath, filepath.Join(dst, suffix))
	})
}

func cpFile(src, dst string) error {
	sfi, err := os.Stat(src)
	if err != nil {
		return err
	}
	if !sfi.Mode().IsRegular() {
		return fmt.Errorf("cpFile can't deal with non-regular file %s", src)
	}

	dstDir := filepath.Dir(dst)
	if err := os.MkdirAll(dstDir, 0755); err != nil {
		return err
	}

	df, err := os.Create(dst)
	if err != nil {
		return err
	}
	sf, err := os.Open(src)
	if err != nil {
		return err
	}
	defer sf.Close()

	n, err := io.Copy(df, sf)
	if err == nil && n != sfi.Size() {
		err = fmt.Errorf("copied wrong size for %s -> %s: copied %d; want %d", src, dst, n, sfi.Size())
	}
	cerr := df.Close()
	if err == nil {
		err = cerr
	}
	return err
}

func handleSignals(camliProc *os.Process) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
	for {
		sig := <-c
		sysSig, ok := sig.(syscall.Signal)
		if !ok {
			log.Fatal("Not a unix signal")
		}
		switch sysSig {
		case syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT:
			log.Printf("Received %v signal, terminating.", sig)
			err := camliProc.Kill()
			if err != nil {
				log.Fatalf("Failed to kill child: %v ", err)
			}
		default:
			log.Fatal("Received another signal, should not happen.")
		}
	}
}

func checkCamliSrcRoot() {
	if _, err := os.Stat("make.go"); err != nil {
		if !os.IsNotExist(err) {
			log.Fatalf("Could not stat make.go: %v", err)
		}
		log.Fatal("./make.go not found; devcam needs to be run from the Camlistore source tree root.")
	}
	cwd, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}
	camliSrcRoot = cwd
}

// Build builds the camlistore command at the given path from the source tree root.
func build(path string) error {
	if v, _ := strconv.ParseBool(os.Getenv("CAMLI_FAST_DEV")); v {
		// Demo mode. See dev/demo.sh.
		return nil
	}
	_, cmdName := filepath.Split(path)
	target := filepath.Join("camlistore.org", path)
	binPath := filepath.Join("bin", cmdName)
	var modtime int64
	fi, err := os.Stat(binPath)
	if err != nil {
		if !os.IsNotExist(err) {
			return fmt.Errorf("Could not stat %v: %v", binPath, err)
		}
	} else {
		modtime = fi.ModTime().Unix()
	}
	args := []string{
		"run", "make.go",
		"--quiet",
		"--race=" + strconv.FormatBool(*race),
		"--embed_static=false",
		"--sqlite=" + strconv.FormatBool(withSqlite),
		fmt.Sprintf("--if_mods_since=%d", modtime),
		"--targets=" + target,
	}
	cmd := exec.Command("go", args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("Error building %v: %v", target, err)
	}
	return nil
}

func main() {
	checkCamliSrcRoot()
	// TODO(mpl): usage error is not really correct for devcam.
	// See if I can reimplement it while still using cmdmain.Main().
	cmdmain.Main()
}
<commit_msg>devcam: fix slash-vs-backslash bug on Windows<commit_after>/*
Copyright 2013 The Camlistore Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"os/signal"
	pathpkg "path"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"

	"camlistore.org/pkg/cmdmain"
)

var (
	noBuild  = flag.Bool("nobuild", false, "do not rebuild anything")
	race     = flag.Bool("race", false, "build with race detector")
	quiet, _ = strconv.ParseBool(os.Getenv("CAMLI_QUIET"))
	// Whether to build the subcommand with sqlite support. This only
	// concerns the server subcommand, which sets it to serverCmd.sqlite.
	withSqlite bool
)

// The path to the Camlistore source tree. Any devcam command
// should be run from there.
var camliSrcRoot string

// sysExec is set to syscall.Exec on platforms that support it.
var sysExec func(argv0 string, argv []string, envv []string) (err error)

// runExec execs bin. If the platform doesn't support exec, it runs it and waits
// for it to finish.
func runExec(bin string, args []string, env *Env) error {
	if sysExec != nil {
		sysExec(bin, append([]string{filepath.Base(bin)}, args...), env.Flat())
	}

	cmd := exec.Command(bin, args...)
	cmd.Env = env.Flat()
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Start(); err != nil {
		return fmt.Errorf("Could not run %v: %v", bin, err)
	}
	go handleSignals(cmd.Process)
	return cmd.Wait()
}

// cpDir copies the contents of src dir into dst dir.
// filter is a list of file suffixes to skip. ex: ".go"
func cpDir(src, dst string, filter []string) error {
	return filepath.Walk(src, func(fullpath string, fi os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		for _, suffix := range filter {
			if strings.HasSuffix(fi.Name(), suffix) {
				return nil
			}
		}
		suffix, err := filepath.Rel(src, fullpath)
		if err != nil {
			return fmt.Errorf("Failed to find Rel(%q, %q): %v", src, fullpath, err)
		}
		if fi.IsDir() {
			return nil
		}
		return cpFile(fullpath, filepath.Join(dst, suffix))
	})
}

func cpFile(src, dst string) error {
	sfi, err := os.Stat(src)
	if err != nil {
		return err
	}
	if !sfi.Mode().IsRegular() {
		return fmt.Errorf("cpFile can't deal with non-regular file %s", src)
	}

	dstDir := filepath.Dir(dst)
	if err := os.MkdirAll(dstDir, 0755); err != nil {
		return err
	}

	df, err := os.Create(dst)
	if err != nil {
		return err
	}
	sf, err := os.Open(src)
	if err != nil {
		return err
	}
	defer sf.Close()

	n, err := io.Copy(df, sf)
	if err == nil && n != sfi.Size() {
		err = fmt.Errorf("copied wrong size for %s -> %s: copied %d; want %d", src, dst, n, sfi.Size())
	}
	cerr := df.Close()
	if err == nil {
		err = cerr
	}
	return err
}

func handleSignals(camliProc *os.Process) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
	for {
		sig := <-c
		sysSig, ok := sig.(syscall.Signal)
		if !ok {
			log.Fatal("Not a unix signal")
		}
		switch sysSig {
		case syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT:
			log.Printf("Received %v signal, terminating.", sig)
			err := camliProc.Kill()
			if err != nil {
				log.Fatalf("Failed to kill child: %v ", err)
			}
		default:
			log.Fatal("Received another signal, should not happen.")
		}
	}
}

func checkCamliSrcRoot() {
	if _, err := os.Stat("make.go"); err != nil {
		if !os.IsNotExist(err) {
			log.Fatalf("Could not stat make.go: %v", err)
		}
		log.Fatal("./make.go not found; devcam needs to be run from the Camlistore source tree root.")
	}
	cwd, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}
	camliSrcRoot = cwd
}

// Build builds the camlistore command at the given path from the source tree root.
func build(path string) error {
	if v, _ := strconv.ParseBool(os.Getenv("CAMLI_FAST_DEV")); v {
		// Demo mode. See dev/demo.sh.
		return nil
	}
	_, cmdName := filepath.Split(path)
	target := pathpkg.Join("camlistore.org", filepath.ToSlash(path))
	binPath := filepath.Join("bin", cmdName)
	var modtime int64
	fi, err := os.Stat(binPath)
	if err != nil {
		if !os.IsNotExist(err) {
			return fmt.Errorf("Could not stat %v: %v", binPath, err)
		}
	} else {
		modtime = fi.ModTime().Unix()
	}
	args := []string{
		"run", "make.go",
		"--quiet",
		"--race=" + strconv.FormatBool(*race),
		"--embed_static=false",
		"--sqlite=" + strconv.FormatBool(withSqlite),
		fmt.Sprintf("--if_mods_since=%d", modtime),
		"--targets=" + target,
	}
	cmd := exec.Command("go", args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("Error building %v: %v", target, err)
	}
	return nil
}

func main() {
	checkCamliSrcRoot()
	// TODO(mpl): usage error is not really correct for devcam.
	// See if I can reimplement it while still using cmdmain.Main().
	cmdmain.Main()
}
<|endoftext|>


<commit_before>package models

import "time"

// MostUsedTagsByUserInRange returns most used tags of a user in a specified range.
func MostUsedTagsByUserInRange(db XODB, userId int, begin time.Time, end time.Time) ([]*MostUsedTag, error) {
	var err error

	// sql query
	sqlstr := `SELECT ` +
		`id, report_date, user_id, tags ` +
		`FROM trackit.most_used_tags ` +
		`WHERE user_id = ? ` +
		`AND report_date >= '` + begin.String() + `' ` +
		`AND report_date < '` + end.String() + `'`

	// run query
	XOLog(sqlstr, userId)
	q, err := db.Query(sqlstr, userId)
	if err != nil {
		return nil, err
	}
	defer q.Close()

	// load results
	res := []*MostUsedTag{}
	for q.Next() {
		mut := MostUsedTag{
			_exists: true,
		}

		// scan
		err = q.Scan(&mut.ID, &mut.ReportDate, &mut.UserID, &mut.Tags)
		if err != nil {
			return nil, err
		}

		res = append(res, &mut)
	}

	return res, nil
}

// MostUsedTagsInUseByUser returns the currently used most used tags of a user
func MostUsedTagsInUseByUser(db XODB, awsAccountID int) (*MostUsedTag, error) {
	var err error

	// sql query
	sqlstr := `SELECT ` +
		`id, report_date, user_id, tags ` +
		`FROM trackit.most_used_tags ` +
		`WHERE user_id = ? ` +
		`ORDER BY report_date ASC LIMIT 1`

	// run query
	XOLog(sqlstr, awsAccountID)
	q, err := db.Query(sqlstr, awsAccountID)
	if err != nil {
		return nil, err
	}
	defer q.Close()

	// load results
	res := []*MostUsedTag{}
	for q.Next() {
		mut := MostUsedTag{
			_exists: true,
		}

		// scan
		err = q.Scan(&mut.ID, &mut.ReportDate, &mut.UserID, &mut.Tags)
		if err != nil {
			return nil, err
		}

		res = append(res, &mut)
	}

	if len(res) <= 0 {
		return nil, nil
	}

	return res[0], nil
}
<commit_msg>fix(mostUsedTagsOrder): get the latest, instead of the oldest, most used tags<commit_after>package models

import "time"

// MostUsedTagsByUserInRange returns most used tags of a user in a specified range.
func MostUsedTagsByUserInRange(db XODB, userId int, begin time.Time, end time.Time) ([]*MostUsedTag, error) {
	var err error

	// sql query
	sqlstr := `SELECT ` +
		`id, report_date, user_id, tags ` +
		`FROM trackit.most_used_tags ` +
		`WHERE user_id = ? ` +
		`AND report_date >= '` + begin.String() + `' ` +
		`AND report_date < '` + end.String() + `'`

	// run query
	XOLog(sqlstr, userId)
	q, err := db.Query(sqlstr, userId)
	if err != nil {
		return nil, err
	}
	defer q.Close()

	// load results
	res := []*MostUsedTag{}
	for q.Next() {
		mut := MostUsedTag{
			_exists: true,
		}

		// scan
		err = q.Scan(&mut.ID, &mut.ReportDate, &mut.UserID, &mut.Tags)
		if err != nil {
			return nil, err
		}

		res = append(res, &mut)
	}

	return res, nil
}

// MostUsedTagsInUseByUser returns the currently used most used tags of a user
func MostUsedTagsInUseByUser(db XODB, awsAccountID int) (*MostUsedTag, error) {
	var err error

	// sql query
	sqlstr := `SELECT ` +
		`id, report_date, user_id, tags ` +
		`FROM trackit.most_used_tags ` +
		`WHERE user_id = ? ` +
		`ORDER BY report_date DESC LIMIT 1`

	// run query
	XOLog(sqlstr, awsAccountID)
	q, err := db.Query(sqlstr, awsAccountID)
	if err != nil {
		return nil, err
	}
	defer q.Close()

	// load results
	res := []*MostUsedTag{}
	for q.Next() {
		mut := MostUsedTag{
			_exists: true,
		}

		// scan
		err = q.Scan(&mut.ID, &mut.ReportDate, &mut.UserID, &mut.Tags)
		if err != nil {
			return nil, err
		}

		res = append(res, &mut)
	}

	if len(res) <= 0 {
		return nil, nil
	}

	return res[0], nil
}
<|endoftext|>


<commit_before>package anylisp

import (
	"fmt"
	"os"
)

type AnyLister interface {
	Gfi() interface{}
	Sfi(v interface{}) interface{}
	Gbf() AnyLister
	Sbf(v AnyLister) AnyLister
}

type AnyList struct {
	Fi interface{}
	Bf AnyLister
}

var (
	Ps_      AnyLister
	TempRoot AnyLister
)

func Parse(code string) {
	TempRoot = &AnyList{}
	Ps_ = &AnyList{TempRoot, nil}
	tok := ""
	for i := 0; i < len(code); i++ {
		if code[i] == ' ' || code[i] == '\t' || code[i] == '\n' {
			if tok == ")" {
				Assert(Ps_.Gbf() != nil, "Parse WTF! Too many )s.")
				Ps_ = Ps_.Gbf()
			} else if len(tok) > 0 {
				var ls AnyLister
				if tok == "(" {
					ls = &AnyList{nil, nil}
				} else {
					ls = &AnyList{tok, nil}
				}
				if Ps_.Gfi() == nil {
					Ps_.Gbf().Gfi().(AnyLister).Sfi(ls) // 1st token in list
				} else {
					Ps_.Gfi().(AnyLister).Sbf(ls)
				}
				Ps_.Sfi(ls)
				if tok == "(" {
					Ps_ = &AnyList{nil, Ps_}
				}
			}
			tok = ""
		} else {
			tok += string(code[i])
		}
	}
	Assert(Ps_.Gbf() == nil, "Parse WTF! Too few )s.")
}

func PrintTree(ls interface{}) {
	switch t := ls.(type) {
	case string:
		fmt.Print(t + " ")
	case AnyLister:
		fmt.Print("(")
		for ls != nil {
			PrintTree(ls.(AnyLister).Gfi())
			ls = ls.(AnyLister).Gbf()
		}
		fmt.Print(")")
	}
}

func Ln(ls AnyLister) int {
	if ls == nil {
		return 0
	}
	if ls.Gbf() == nil {
		return 1
	}
	return Ln(ls.Gbf()) + 1
}

func Nth(ls AnyLister, n int) AnyLister {
	Assert(ls != nil, "WTF! Out of bounds when calling (nth.")
	if n > 0 {
		return Nth(ls.Gbf(), n-1)
	}
	if n < 0 {
		return Nth(ls, Ln(ls)-n)
	}
	return ls
}

func (ls *AnyList) Gfi() interface{} {
	return ls.Fi
}

func (ls *AnyList) Sfi(v interface{}) interface{} {
	ls.Fi = v
	return v
}

func (ls *AnyList) Gbf() AnyLister {
	return ls.Bf
}

func (ls *AnyList) Sbf(v AnyLister) AnyLister {
	ls.Bf = v
	return v
}

func Assert(cond bool, msg string) {
	if !cond {
		fmt.Println(msg)
		os.Exit(2)
	}
}
<commit_msg>don't begin getters with G<commit_after>package anylisp

import (
	"fmt"
	"os"
)

type AnyLister interface {
	Fi() interface{}
	Sfi(v interface{}) interface{}
	Bf() AnyLister
	Sbf(v AnyLister) AnyLister
}

type AnyList struct {
	fi interface{}
	bf AnyLister
}

var (
	Ps_      AnyLister
	TempRoot AnyLister
)

func Parse(code string) {
	TempRoot = &AnyList{}
	Ps_ = &AnyList{TempRoot, nil}
	tok := ""
	for i := 0; i < len(code); i++ {
		if code[i] == ' ' || code[i] == '\t' || code[i] == '\n' {
			if tok == ")" {
				Assert(Ps_.Bf() != nil, "Parse WTF! Too many )s.")
				Ps_ = Ps_.Bf()
			} else if len(tok) > 0 {
				var ls AnyLister
				if tok == "(" {
					ls = &AnyList{nil, nil}
				} else {
					ls = &AnyList{tok, nil}
				}
				if Ps_.Fi() == nil {
					Ps_.Bf().Fi().(AnyLister).Sfi(ls) // 1st token in list
				} else {
					Ps_.Fi().(AnyLister).Sbf(ls)
				}
				Ps_.Sfi(ls)
				if tok == "(" {
					Ps_ = &AnyList{nil, Ps_}
				}
			}
			tok = ""
		} else {
			tok += string(code[i])
		}
	}
	Assert(Ps_.Bf() == nil, "Parse WTF! Too few )s.")
}

func PrintTree(ls interface{}) {
	switch t := ls.(type) {
	case string:
		fmt.Print(t + " ")
	case AnyLister:
		fmt.Print("(")
		for ls != nil {
			PrintTree(ls.(AnyLister).Fi())
			ls = ls.(AnyLister).Bf()
		}
		fmt.Print(")")
	}
}

func Ln(ls AnyLister) int {
	if ls == nil {
		return 0
	}
	if ls.Bf() == nil {
		return 1
	}
	return Ln(ls.Bf()) + 1
}

func Nth(ls AnyLister, n int) AnyLister {
	Assert(ls != nil, "WTF! Out of bounds when calling (nth.")
	if n > 0 {
		return Nth(ls.Bf(), n-1)
	}
	if n < 0 {
		return Nth(ls, Ln(ls)-n)
	}
	return ls
}

func (ls *AnyList) Fi() interface{} {
	return ls.fi
}

func (ls *AnyList) Sfi(v interface{}) interface{} {
	ls.fi = v
	return v
}

func (ls *AnyList) Bf() AnyLister {
	return ls.bf
}

func (ls *AnyList) Sbf(v AnyLister) AnyLister {
	ls.bf = v
	return v
}

func Assert(cond bool, msg string) {
	if !cond {
		fmt.Println(msg)
		os.Exit(2)
	}
}
<|endoftext|>


<commit_before>//
// DISCLAIMER
//
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package service

import "strings"

type PassthroughOption struct {
	Name   string
	Values struct {
		All          []string
		Coordinators []string
		DBServers    []string
		Agents       []string
	}
}

var (
	// forbiddenPassthroughOptions holds a list of options that are not allowed to be overriden.
	forbiddenPassthroughOptions = []string{
		"agency.activate",
		"agency.endpoint",
		"agency.my-address",
		"agency.size",
		"agency.supervision",
		"cluster.agency-endpoint",
		"cluster.my-address",
		"cluster.my-role",
		"cluster.my-local-info",
		"database.directory",
		"foxx.queues",
		"javascript.startup-directory",
		"javascript.app-path",
		"server.endpoint",
		"server.authentication",
		"server.jwt-secret",
		"server.storage-engine",
		"ssl.cafile",
		"ssl.keyfile",
	}
)

// valueForServerType returns the value for the given option for a specific server type.
// If no value is given for the specific server type, any value for `all` is returned.
func (o *PassthroughOption) valueForServerType(serverType ServerType) []string {
	var result []string
	switch serverType {
	case ServerTypeSingle:
		result = o.Values.All
	case ServerTypeCoordinator:
		result = o.Values.Coordinators
	case ServerTypeDBServer:
		result = o.Values.DBServers
	case ServerTypeAgent:
		result = o.Values.Agents
	}
	if len(result) > 0 {
		return result
	}
	return o.Values.All
}

// IsForbidden returns true if the option cannot be overwritten.
func (o *PassthroughOption) IsForbidden() bool {
	for _, x := range forbiddenPassthroughOptions {
		if x == o.Name {
			return true
		}
	}
	return false
}

// FormattedOptionName returns the option ready to be used in a command line argument,
// prefixed with `--`.
func (o *PassthroughOption) FormattedOptionName() string {
	return "--" + o.Name
}

// sectionName returns the name of the configuration section this option belongs to.
func (o *PassthroughOption) sectionName() string {
	return strings.SplitN(o.Name, ".", 2)[0]
}

// sectionKey returns the name of this option within its configuration section.
func (o *PassthroughOption) sectionKey() string {
	parts := strings.SplitN(o.Name, ".", 2)
	if len(parts) > 1 {
		return parts[1]
	}
	return ""
}

func (c *Config) passthroughOptionValuesForServerType(name string, serverType ServerType) []string {
	for _, ptOpt := range c.PassthroughOptions {
		if ptOpt.Name != name {
			continue
		}
		values := ptOpt.valueForServerType(serverType)
		if len(values) > 0 {
			return values
		}
		return nil
	}
	return nil
}
<commit_msg>Allow foxx.queues to be overwritten<commit_after>//
// DISCLAIMER
//
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package service

import "strings"

type PassthroughOption struct {
	Name   string
	Values struct {
		All          []string
		Coordinators []string
		DBServers    []string
		Agents       []string
	}
}

var (
	// forbiddenPassthroughOptions holds a list of options that are not allowed to be overriden.
	forbiddenPassthroughOptions = []string{
		"agency.activate",
		"agency.endpoint",
		"agency.my-address",
		"agency.size",
		"agency.supervision",
		"cluster.agency-endpoint",
		"cluster.my-address",
		"cluster.my-role",
		"cluster.my-local-info",
		"database.directory",
		"javascript.startup-directory",
		"javascript.app-path",
		"server.endpoint",
		"server.authentication",
		"server.jwt-secret",
		"server.storage-engine",
		"ssl.cafile",
		"ssl.keyfile",
	}
)

// valueForServerType returns the value for the given option for a specific server type.
// If no value is given for the specific server type, any value for `all` is returned.
func (o *PassthroughOption) valueForServerType(serverType ServerType) []string {
	var result []string
	switch serverType {
	case ServerTypeSingle:
		result = o.Values.All
	case ServerTypeCoordinator:
		result = o.Values.Coordinators
	case ServerTypeDBServer:
		result = o.Values.DBServers
	case ServerTypeAgent:
		result = o.Values.Agents
	}
	if len(result) > 0 {
		return result
	}
	return o.Values.All
}

// IsForbidden returns true if the option cannot be overwritten.
func (o *PassthroughOption) IsForbidden() bool {
	for _, x := range forbiddenPassthroughOptions {
		if x == o.Name {
			return true
		}
	}
	return false
}

// FormattedOptionName returns the option ready to be used in a command line argument,
// prefixed with `--`.
func (o *PassthroughOption) FormattedOptionName() string {
	return "--" + o.Name
}

// sectionName returns the name of the configuration section this option belongs to.
func (o *PassthroughOption) sectionName() string {
	return strings.SplitN(o.Name, ".", 2)[0]
}

// sectionKey returns the name of this option within its configuration section.
func (o *PassthroughOption) sectionKey() string {
	parts := strings.SplitN(o.Name, ".", 2)
	if len(parts) > 1 {
		return parts[1]
	}
	return ""
}

func (c *Config) passthroughOptionValuesForServerType(name string, serverType ServerType) []string {
	for _, ptOpt := range c.PassthroughOptions {
		if ptOpt.Name != name {
			continue
		}
		values := ptOpt.valueForServerType(serverType)
		if len(values) > 0 {
			return values
		}
		return nil
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2014 Fredrik Ehnbom
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package container

import "github.com/quarnster/util"

type (
	RemovedData struct {
		Index int
		Data  interface{}
	}

	InsertedData struct {
		Index int
	}

	Array interface {
		Insert(index int, data interface{}) error
		Remove(i int) (olddata interface{}, err error)
		Get(index int) interface{}
		Len() int
	}
	BasicArray struct {
		model []interface{}
	}
	ObservableArray struct {
		util.BasicObservable
		Array
	}
)

func (a *BasicArray) Insert(index int, data interface{}) error {
	if index < 0 {
		index = 0
	} else if index > len(a.model) {
		index = len(a.model)
	}

	nmodel := make([]interface{}, len(a.model)+1)
	copy(nmodel, a.model[:index])
	nmodel[index] = data
	copy(nmodel[index+1:], a.model[index:])
	return nil
}

func (a *BasicArray) Remove(i int) (olddata interface{}, err error) {
	if i < 0 {
		i = 0
	} else if i >= len(a.model)-1 {
		i = len(a.model) - 1
	}

	olddata = a.model[i]
	copy(a.model[i:], a.model[i+1:])
	return olddata, nil
}

func (a *BasicArray) Get(index int) interface{} {
	return a.model[index]
}

func (a *BasicArray) Len() int {
	return len(a.model)
}

func (a *ObservableArray) Insert(index int, data interface{}) error {
	if err := a.Array.Insert(index, data); err != nil {
		return err
	}
	a.NotifyObservers(InsertedData{index})
	return nil
}

func (a *ObservableArray) Remove(i int) (olddata interface{}, err error) {
	if olddata, err = a.Array.Remove(i); err != nil {
		return
	}
	a.NotifyObservers(RemovedData{i, olddata})
	return
}
<commit_msg>container: more array prototyping<commit_after>// Copyright 2014 Fredrik Ehnbom
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package container

import (
	"fmt"
	"github.com/quarnster/util"
	"sort"
)

var (
	ErrNotInt   = fmt.Errorf("Attempting to insert a non-int type")
	ErrIndexOOB = fmt.Errorf("Index is out of bounds")
)

type (
	RemovedData struct {
		Index int
		Data  interface{}
	}

	InsertedData struct {
		Index int
	}

	Array interface {
		Insert(index int, data interface{}) error
		Remove(i int) (olddata interface{}, err error)
		Get(index int) interface{}
		Len() int
	}
	IntArray struct {
		model []int
	}
	BasicArray struct {
		model []interface{}
	}
	BoundsCheckingArray struct {
		Array
	}
	ObservableArray struct {
		util.BasicObservable
		Array
	}
	FilteredArray struct {
		indices IntArray
		accept  func(data interface{}) bool
		Array
	}
)

func (b *BoundsCheckingArray) Insert(index int, data interface{}) error {
	if index < 0 || index > b.Len() {
		return ErrIndexOOB
	}
	return b.Array.Insert(index, data)
}

func (b *BoundsCheckingArray) Remove(index int) (interface{}, error) {
	if index < 0 || index >= b.Len() {
		return nil, ErrIndexOOB
	}
	return b.Array.Remove(index)
}

func (b *BoundsCheckingArray) Get(index int) interface{} {
	if index < 0 || index >= b.Len() {
		return nil
	}
	return b.Array.Get(index)
}

func (i *IntArray) Insert(index int, data interface{}) error {
	ii, ok := data.(int)
	if !ok {
		return ErrNotInt
	}
	nmodel := make([]int, len(i.model)+1)
	copy(nmodel, i.model[:index])
	nmodel[index] = ii
	copy(nmodel[index+1:], i.model[index:])
	return nil
}

func (i *IntArray) Remove(index int) (olddata interface{}, err error) {
	olddata = i.model[index]
	copy(i.model[index:], i.model[index+1:])
	return olddata, nil
}

func (i *IntArray) Get(index int) interface{} {
	return i.model[index]
}

func (i *IntArray) Len() int {
	return len(i.model)
}

func (a *BasicArray) Insert(index int, data interface{}) error {
	nmodel := make([]interface{}, len(a.model)+1)
	copy(nmodel, a.model[:index])
	nmodel[index] = data
	copy(nmodel[index+1:], a.model[index:])
	return nil
}

func (a *BasicArray) Remove(i int) (olddata interface{}, err error) {
	olddata = a.model[i]
	copy(a.model[i:], a.model[i+1:])
	return olddata, nil
}

func (a *BasicArray) Get(index int) interface{} {
	return a.model[index]
}

func (a *BasicArray) Len() int {
	return len(a.model)
}

func (a *ObservableArray) Insert(index int, data interface{}) error {
	if err := a.Array.Insert(index, data); err != nil {
		return err
	}
	a.NotifyObservers(InsertedData{index})
	return nil
}

func (a *ObservableArray) Remove(i int) (olddata interface{}, err error) {
	if olddata, err = a.Array.Remove(i); err != nil {
		return
	}
	a.NotifyObservers(RemovedData{i, olddata})
	return
}

func (fa *FilteredArray) Changed(data interface{}) {
	switch d := data.(type) {
	case RemovedData:
		for i, k := range fa.indices.model {
			if k == d.Index {
				fa.indices.Remove(i)
				break
			}
		}
	case InsertedData:
		data := fa.Get(d.Index)
		if !fa.accept(data) {
			return
		}
		idx := sort.Search(fa.indices.Len(), func(i int) bool {
			return fa.Get(i).(int) < d.Index
		})
		fa.indices.Insert(idx+1, data)
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package utils

import (
    "io/ioutil"
)

const WARNING_SCRIPT = `#!/bin/bash
echo -e "\033[0;33m                                                             \033[0m"
echo -e "\033[0;33m                   >>\.                                      \033[0m"
echo -e "\033[0;33m                  /_  )'.                                    \033[0m"
echo -e "\033[0;33m                 /  _)'^)'.   ___.---. _                     \033[0m"
echo -e "\033[0;33m                (_,' \  '^-)\"\"        '.--__               \033[0m"
echo -e "\033[0;33m                      \                |'-##-.               \033[0m"
echo -e "\033[0;33m                   ____)               /   \#^\              \033[0m"
echo -e "\033[0;33m                  (  ___/--._____.-\  (_    WW\              \033[0m"
echo -e "\033[0;33m                   ^_\_             \ \_^-._                 \033[0m"
echo -e "\033[0;33m                   //._]             )/ --,_\                \033[0m"
echo -e "\033[0;33m                  /_>               |_>                      \033[0m"
echo -e "\033[0;33m                                                             \033[0m"
echo -e "\033[0;33m  ########################[ Welcome ]########################\033[0m"
echo -e "\033[0;33m  #  You have logged in to the guest OS.                    #\033[0m"
echo -e "\033[0;33m  #  To access your containers use 'docker attach' command  #\033[0m"
echo -e "\033[0;33m  ###########################################################\033[0m"
echo -e "\033[0;33m                                                             \033[0m"`

const SCRIPT_DIR = "/host/etc/profile.d"

func WriteWelcomeScript() error {
	data := []byte(WARNING_SCRIPT)
	err := ioutil.WriteFile(SCRIPT_DIR + "/gce-containers-welcome.sh", data, 0755)
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>Do not display horse ASCII art in welcome message.<commit_after>// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package utils

import (
    "io/ioutil"
)

const WARNING_SCRIPT = `#!/bin/bash
echo -e "\033[0;33m  ########################[ Welcome ]########################\033[0m"
echo -e "\033[0;33m  #  You have logged in to the guest OS.                    #\033[0m"
echo -e "\033[0;33m  #  To access your containers use 'docker attach' command  #\033[0m"
echo -e "\033[0;33m  ###########################################################\033[0m"
echo -e "\033[0;33m                                                             \033[0m"`

const SCRIPT_DIR = "/host/etc/profile.d"

func WriteWelcomeScript() error {
	data := []byte(WARNING_SCRIPT)
	err := ioutil.WriteFile(SCRIPT_DIR + "/gce-containers-welcome.sh", data, 0755)
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package batch

import "sync"

// MockItemGenerator generates mock Items with unique IDs. Items are generated in a
// separate goroutine and added to a channel, which can be retrieved by calling
// GetCh.
type MockItemGenerator struct {
	closeOnce sync.Once
	done      chan struct{}
	ch        chan *Item

	mu     sync.Mutex
	nextID uint64
}

// NewMockItemGenerator returns a new MockItemGenerator.
//
// After using it, call Close to prevent a goroutine leak.
func NewMockItemGenerator() *MockItemGenerator {
	m := &MockItemGenerator{
		done: make(chan struct{}),
		ch:   make(chan *Item),
	}

	go func() {
		id := uint64(0)
		nextItem := &Item{
			id: id,
		}
		for {
			select {
			case m.ch <- nextItem:
				id++
				nextItem = &Item{
					id: id,
				}

			case <-m.done:
				return
			}
		}
	}()

	return m
}

// Close stops a MockItemGenerator's goroutine
func (m *MockItemGenerator) Close() {
	m.closeOnce.Do(func() {
		close(m.done)
	})
}

// GetCh returns a channel of Items with unique IDs.
func (m *MockItemGenerator) GetCh() <-chan *Item {
	return m.ch
}
<commit_msg>Remove unused fields in MockItemGenerator<commit_after>package batch

import "sync"

// MockItemGenerator generates mock Items with unique IDs. Items are generated in a
// separate goroutine and added to a channel, which can be retrieved by calling
// GetCh.
type MockItemGenerator struct {
	closeOnce sync.Once
	done      chan struct{}
	ch        chan *Item
}

// NewMockItemGenerator returns a new MockItemGenerator.
//
// After using it, call Close to prevent a goroutine leak.
func NewMockItemGenerator() *MockItemGenerator {
	m := &MockItemGenerator{
		done: make(chan struct{}),
		ch:   make(chan *Item),
	}

	go func() {
		id := uint64(0)
		nextItem := &Item{
			id: id,
		}
		for {
			select {
			case m.ch <- nextItem:
				id++
				nextItem = &Item{
					id: id,
				}

			case <-m.done:
				return
			}
		}
	}()

	return m
}

// Close stops a MockItemGenerator's goroutine
func (m *MockItemGenerator) Close() {
	m.closeOnce.Do(func() {
		close(m.done)
	})
}

// GetCh returns a channel of Items with unique IDs.
func (m *MockItemGenerator) GetCh() <-chan *Item {
	return m.ch
}
<|endoftext|>


<commit_before>// Copyright 2019 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package module

import (
	"context"
	"errors"
	"fmt"
	"log"
	"os"
	"testing"
	"time"

	"github.com/issue9/assert"

	"github.com/issue9/web/internal/webconfig"
)

const (
	tickTimer  = 500 * time.Microsecond
	panicTimer = 5 * tickTimer
)

func buildSrv1() (f ServiceFunc, start, exit chan struct{}) {
	exit = make(chan struct{}, 1)
	start = make(chan struct{}, 1)

	return func(ctx context.Context) error {
		defer func() {
			exit <- struct{}{}
		}()

		inited := false
		for now := range time.Tick(tickTimer) {
			select {
			case <-ctx.Done():
				fmt.Println("cancel srv1")
				return ctx.Err()
			default:
				fmt.Println("srv1:", now)
				if !inited {
					inited = true
					start <- struct{}{}
				}
			}
		}
		return nil
	}, start, exit
}

// panic
func buildSrv2() (f ServiceFunc, start, exit chan struct{}) {
	exit = make(chan struct{}, 1)
	start = make(chan struct{}, 1)

	return func(ctx context.Context) error {
		defer func() {
			exit <- struct{}{}
		}()

		inited := false
		timer := time.NewTimer(panicTimer)
		for now := range time.Tick(tickTimer) {
			select {
			case <-ctx.Done():
				fmt.Println("cancel srv2")
				return ctx.Err()
			case <-timer.C:
				fmt.Println("panic srv2")
				panic("panic srv2")
			default:
				if !inited {
					inited = true
					start <- struct{}{}
				}
				fmt.Println("srv2:", now)
			}
		}
		return nil
	}, start, exit
}

// error
func buildSrv3() (f ServiceFunc, start, exit chan struct{}) {
	exit = make(chan struct{}, 1)
	start = make(chan struct{}, 1)

	return func(ctx context.Context) error {
		defer func() {
			exit <- struct{}{}
		}()

		inited := false
		for now := range time.Tick(tickTimer) {
			select {
			case <-ctx.Done():
				fmt.Println("cancel srv3")
				return ctx.Err()
			default:
				fmt.Println("srv3:", now)
				if !inited {
					inited = true
					start <- struct{}{}
				} else {
					return errors.New("Error")
				}
			}
		}
		return nil
	}, start, exit
}

func TestModule_AddService(t *testing.T) {
	a := assert.New(t)
	ms, err := NewModules(&webconfig.WebConfig{})
	a.NotError(err).NotNil(ms)
	m := newModule(ms, "m1", "m1 desc")
	a.NotNil(m)

	srv1 := func(ctx context.Context) error { return nil }

	ml := len(m.inits)
	m.AddService(srv1, "srv1")
	a.Equal(ml+1, len(m.inits))
}

func TestService_srv1(t *testing.T) {
	a := assert.New(t)
	ms, err := NewModules(&webconfig.WebConfig{})
	a.NotError(err).NotNil(ms)

	m := ms.NewModule("m1", "m1 desc")
	a.NotNil(m)
	a.Empty(ms.services)

	srv1, start, exit := buildSrv1()
	m.AddService(srv1, "srv1")
	a.NotError(ms.Init("", log.New(os.Stdout, "", 0))) // 注册并运行服务
	<-start
	time.Sleep(20 * time.Microsecond) // 等待其它内容初始化完成
	a.Equal(1, len(ms.services))
	s1 := ms.services[0]
	a.Equal(s1.Module, m)
	a.Equal(s1.State(), ServiceRunning)
	s1.Stop()
	<-exit
	a.Equal(s1.State(), ServiceStop)

	s1.Run()
	s1.Run() // 在运行状态再次运行，不启作用
	<-start
	a.Equal(s1.State(), ServiceRunning)
	s1.Stop()
	<-exit
	a.Equal(s1.State(), ServiceStop)
}

func TestService_srv2(t *testing.T) {
	a := assert.New(t)
	ms, err := NewModules(&webconfig.WebConfig{})
	a.NotError(err).NotNil(ms)

	m := ms.NewModule("m1", "m1 desc")
	a.NotNil(m)
	a.Empty(ms.services)

	srv2, start, exit := buildSrv2()
	m.AddService(srv2, "srv2")
	a.NotError(ms.Init("", nil)) // 注册并运行服务
	s2 := ms.services[0]
	<-start
	time.Sleep(20 * time.Microsecond) // 等待服务启动完成
	a.Equal(s2.State(), ServiceRunning)
	s2.Stop()
	<-exit
	a.Equal(s2.State(), ServiceStop)

	// 再次运行，等待 panic
	s2.Run()
	<-start
	<-exit
	a.Equal(s2.State(), ServiceFailed)
	a.NotEmpty(s2.Err())

	// 出错后，还能正确运行和结束
	s2.Run()
	<-start
	a.Equal(s2.State(), ServiceRunning)
	s2.Stop()
	<-exit
	a.Equal(s2.State(), ServiceStop)
}

func TestService_srv3(t *testing.T) {
	a := assert.New(t)
	ms, err := NewModules(&webconfig.WebConfig{})
	a.NotError(err).NotNil(ms)

	m := ms.NewModule("m1", "m1 desc")
	a.NotNil(m)
	a.Empty(ms.services)

	srv3, start, exit := buildSrv3()
	m.AddService(srv3, "srv3")
	a.NotError(ms.Init("", nil)) // 注册并运行服务
	s3 := ms.services[0]
	<-start
	time.Sleep(20 * time.Microsecond) // 等待服务启动完成
	a.Equal(s3.State(), ServiceRunning)

	<-exit // 等待超过返回错误
	a.Equal(s3.State(), ServiceFailed)
	a.NotNil(s3.Err())

	// 再次运行
	s3.Run()
	<-start
	a.Equal(s3.State(), ServiceRunning)
	s3.Stop()
	<-exit
	a.Equal(s3.State(), ServiceStop)
}
<commit_msg>调整 windows 下测试错误<commit_after>// Copyright 2019 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package module

import (
	"context"
	"errors"
	"fmt"
	"log"
	"os"
	"testing"
	"time"

	"github.com/issue9/assert"

	"github.com/issue9/web/internal/webconfig"
)

const (
	tickTimer  = 500 * time.Microsecond
	panicTimer = 50 * tickTimer // windows 下此值不能过小，否则测试容易出错
)

func buildSrv1() (f ServiceFunc, start, exit chan struct{}) {
	exit = make(chan struct{}, 1)
	start = make(chan struct{}, 1)

	return func(ctx context.Context) error {
		defer func() {
			exit <- struct{}{}
		}()

		inited := false
		for now := range time.Tick(tickTimer) {
			select {
			case <-ctx.Done():
				fmt.Println("cancel srv1")
				return ctx.Err()
			default:
				fmt.Println("srv1:", now)
				if !inited {
					inited = true
					start <- struct{}{}
				}
			}
		}
		return nil
	}, start, exit
}

// panic
func buildSrv2() (f ServiceFunc, start, exit chan struct{}) {
	exit = make(chan struct{}, 1)
	start = make(chan struct{}, 1)

	return func(ctx context.Context) error {
		defer func() {
			exit <- struct{}{}
		}()

		inited := false
		timer := time.NewTimer(panicTimer)
		for now := range time.Tick(tickTimer) {
			select {
			case <-ctx.Done():
				fmt.Println("cancel srv2")
				return ctx.Err()
			case <-timer.C:
				fmt.Println("panic srv2")
				panic("panic srv2")
			default:
				if !inited {
					inited = true
					start <- struct{}{}
				}
				fmt.Println("srv2:", now)
			}
		}
		return nil
	}, start, exit
}

// error
func buildSrv3() (f ServiceFunc, start, exit chan struct{}) {
	exit = make(chan struct{}, 1)
	start = make(chan struct{}, 1)

	return func(ctx context.Context) error {
		defer func() {
			exit <- struct{}{}
		}()

		inited := false
		timer := time.NewTimer(panicTimer)
		for now := range time.Tick(tickTimer) {
			select {
			case <-ctx.Done():
				fmt.Println("cancel srv3")
				return ctx.Err()
			case <-timer.C:
				fmt.Println("panic srv2")
				return errors.New("Error")
			default:
				fmt.Println("srv3:", now)
				if !inited {
					inited = true
					start <- struct{}{}
				}
			}
		}
		return nil
	}, start, exit
}

func TestModule_AddService(t *testing.T) {
	a := assert.New(t)
	ms, err := NewModules(&webconfig.WebConfig{})
	a.NotError(err).NotNil(ms)
	m := newModule(ms, "m1", "m1 desc")
	a.NotNil(m)

	srv1 := func(ctx context.Context) error { return nil }

	ml := len(m.inits)
	m.AddService(srv1, "srv1")
	a.Equal(ml+1, len(m.inits))
}

func TestService_srv1(t *testing.T) {
	a := assert.New(t)
	ms, err := NewModules(&webconfig.WebConfig{})
	a.NotError(err).NotNil(ms)

	m := ms.NewModule("m1", "m1 desc")
	a.NotNil(m)
	a.Empty(ms.services)

	srv1, start, exit := buildSrv1()
	m.AddService(srv1, "srv1")
	a.NotError(ms.Init("", log.New(os.Stdout, "", 0))) // 注册并运行服务
	<-start
	time.Sleep(20 * time.Microsecond) // 等待其它内容初始化完成
	a.Equal(1, len(ms.services))
	s1 := ms.services[0]
	a.Equal(s1.Module, m)
	a.Equal(s1.State(), ServiceRunning)
	s1.Stop()
	<-exit
	a.Equal(s1.State(), ServiceStop)

	s1.Run()
	s1.Run() // 在运行状态再次运行，不启作用
	<-start
	a.Equal(s1.State(), ServiceRunning)
	s1.Stop()
	<-exit
	a.Equal(s1.State(), ServiceStop)
}

func TestService_srv2(t *testing.T) {
	a := assert.New(t)
	ms, err := NewModules(&webconfig.WebConfig{})
	a.NotError(err).NotNil(ms)

	m := ms.NewModule("m1", "m1 desc")
	a.NotNil(m)
	a.Empty(ms.services)

	srv2, start, exit := buildSrv2()
	m.AddService(srv2, "srv2")
	a.NotError(ms.Init("", nil)) // 注册并运行服务
	s2 := ms.services[0]
	<-start
	time.Sleep(20 * time.Microsecond) // 等待服务启动完成
	a.Equal(s2.State(), ServiceRunning)
	s2.Stop()
	<-exit
	a.Equal(s2.State(), ServiceStop)

	// 再次运行，等待 panic
	s2.Run()
	<-start
	<-exit
	a.Equal(s2.State(), ServiceFailed)
	a.NotEmpty(s2.Err())

	// 出错后，还能正确运行和结束
	s2.Run()
	<-start
	a.Equal(s2.State(), ServiceRunning)
	s2.Stop()
	<-exit
	a.Equal(s2.State(), ServiceStop)
}

func TestService_srv3(t *testing.T) {
	a := assert.New(t)
	ms, err := NewModules(&webconfig.WebConfig{})
	a.NotError(err).NotNil(ms)

	m := ms.NewModule("m1", "m1 desc")
	a.NotNil(m)
	a.Empty(ms.services)

	srv3, start, exit := buildSrv3()
	m.AddService(srv3, "srv3")
	a.NotError(ms.Init("", nil)) // 注册并运行服务
	s3 := ms.services[0]
	<-start
	time.Sleep(20 * time.Microsecond) // 等待服务启动完成
	a.Equal(s3.State(), ServiceRunning)

	<-exit // 等待超过返回错误
	a.Equal(s3.State(), ServiceFailed)
	a.NotNil(s3.Err())

	// 再次运行
	s3.Run()
	<-start
	a.Equal(s3.State(), ServiceRunning)
	s3.Stop()
	<-exit
	a.Equal(s3.State(), ServiceStop)
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */

package analyzer

import (
	"net"
	"strconv"

	"github.com/skydive-project/skydive/flow"
	"github.com/skydive-project/skydive/logging"
)

type Client struct {
	Addr string
	Port int

	connection *AgentAnalyzerClientConn
}

func (c *Client) SendFlow(f *flow.Flow) error {
	data, err := f.GetData()
	if err != nil {
		return err
	}

	c.connection.Write(data)

	return nil
}

func (c *Client) SendFlows(flows []*flow.Flow) {
	for _, flow := range flows {
		err := c.SendFlow(flow)
		if err != nil {
			logging.GetLogger().Errorf("Unable to send flow: %s", err.Error())
		}
	}
}

func NewClient(addr string, port int) (*Client, error) {
	client := &Client{Addr: addr, Port: port}

	srv, err := net.ResolveUDPAddr("udp", addr+":"+strconv.FormatInt(int64(port), 10))
	if err != nil {
		return nil, err
	}

	connection, err := NewAgentAnalyzerClientConn(srv)
	if err != nil {
		return nil, err
	}

	client.connection = connection

	return client, nil
}
<commit_msg>[flow] reconnect to the analyzer on error<commit_after>/*
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */

package analyzer

import (
	"net"
	"strconv"
	"time"

	"github.com/skydive-project/skydive/flow"
	"github.com/skydive-project/skydive/logging"
)

type Client struct {
	Addr string
	Port int

	connection *AgentAnalyzerClientConn
}

func (c *Client) connect() {
	strAddr := c.Addr + ":" + strconv.FormatInt(int64(c.Port), 10)
	srv, err := net.ResolveUDPAddr("udp", strAddr)
	if err != nil {
		logging.GetLogger().Errorf("Can't resolv address to %s", strAddr)
		time.Sleep(200 * time.Millisecond)
		return
	}
	connection, err := NewAgentAnalyzerClientConn(srv)
	if err != nil {
		logging.GetLogger().Errorf("Connection error to %s : %s", strAddr, err.Error())
		time.Sleep(200 * time.Millisecond)
		return
	}
	c.connection = connection
}

func (c *Client) SendFlow(f *flow.Flow) error {
	data, err := f.GetData()
	if err != nil {
		return err
	}

retry:
	_, err = c.connection.Write(data)
	if err != nil {
		logging.GetLogger().Errorf("flows connection to analyzer error %s : try to reconnect" + err.Error())
		c.connection.Close()
		c.connect()
		goto retry
	}

	return nil
}

func (c *Client) SendFlows(flows []*flow.Flow) {
	for _, flow := range flows {
		err := c.SendFlow(flow)
		if err != nil {
			logging.GetLogger().Errorf("Unable to send flow: %s", err.Error())
		}
	}
}

func NewClient(addr string, port int) (*Client, error) {
	client := &Client{Addr: addr, Port: port}
	client.connect()
	return client, nil
}
<|endoftext|>


<commit_before>package resource

import (
	"fmt"
	"os"
	"os/user"
	"path/filepath"
	"strings"

	"github.com/dnaeon/gru/utils"
)

// The file types we manage
const (
	fileTypeRegular   = "regular"
	fileTypeDirectory = "directory"
)

// Flags used to indicate what has changed on a file
const (
	flagOutdatedContent     = 0x01
	flagOutdatedPermissions = 0x02
	flagOutdatedOwner       = 0x04
)

// outdatedFile type describes a file which
// has been identified as being out of date
type outdatedFile struct {
	// Source file to use when reconstructing file's content
	src string

	// Destination file which is identified as being out of date
	dst string

	// Flags used to indicate what has changed on the file
	flags int
}

// File resource manages files and directories.
//
// Example:
//   foo = resource.file.new("/tmp/foo")
//   foo.state = "present"
//   foo.mode = tonumber("0600", 8)
//
// Example:
//   bar = resource.file.new("/tmp/bar")
//   bar.state = "present"
//   bar.filetype = "directory"
type File struct {
	Base

	// Path to the file. Defaults to the resource name.
	Path string `luar:"-"`

	// Permission bits to set on the file. Defaults to 0644.
	Mode os.FileMode `luar:"mode"`

	// Owner of the file. Defaults to the currently running user.
	Owner string `luar:"owner"`

	// Group of the file.
	// Defaults to the group of the currently running user.
	Group string `luar:"group"`

	// Source file to use when creating/updating the file
	Source string `luar:"source"`

	// The file type we manage.
	FileType string `luar:"filetype"`

	// Recursively manage the directory if set to true.
	// Defaults to false.
	Recursive bool `luar:"recursive"`

	// Purge extra files in the target directory if set to true.
	// Defaults to false.
	Purge bool `luar:"purge"`

	// Files identified as being out of date
	outdated []*outdatedFile `luar:"-"`

	// Extra files found in the target directory
	extra map[string]struct{} `luar:"-"`
}

// NewFile creates a resource for managing files and directories
func NewFile(name string) (Resource, error) {
	// Defaults for owner and group
	currentUser, err := user.Current()
	if err != nil {
		return nil, err
	}

	currentGroup, err := user.LookupGroupId(currentUser.Gid)
	if err != nil {
		return nil, err
	}

	// Resource defaults
	f := &File{
		Base: Base{
			Name:          name,
			Type:          "file",
			State:         "present",
			Require:       make([]string, 0),
			PresentStates: []string{"present"},
			AbsentStates:  []string{"absent"},
			Concurrent:    true,
			Subscribe:     make(TriggerMap),
		},
		Path:      name,
		Mode:      0644,
		Owner:     currentUser.Username,
		Group:     currentGroup.Name,
		FileType:  fileTypeRegular,
		Recursive: false,
		Purge:     false,
		outdated:  make([]*outdatedFile, 0),
		extra:     make(map[string]struct{}),
	}

	return f, nil
}

// Validate validates the resource
func (f *File) Validate() error {
	if err := f.Base.Validate(); err != nil {
		return err
	}

	// Validate that we have a valid file type
	if f.FileType != fileTypeRegular && f.FileType != fileTypeDirectory {
		return fmt.Errorf("Invalid file type '%s'", f.FileType)
	}

	// If we have a source, ensure that it exists
	if f.Source != "" {
		dst := utils.NewFileUtil(filepath.Join(DefaultConfig.SiteRepo, f.Source))
		if !dst.Exists() {
			return fmt.Errorf("source file '%s' does not exist", f.Source)
		}
	}

	return nil
}

// Evaluate evaluates the file resource
func (f *File) Evaluate() (State, error) {
	s := State{
		Current:  "unknown",
		Want:     f.State,
		Outdated: false,
	}

	// Check for file presence
	fi, err := os.Stat(f.Path)
	if os.IsNotExist(err) {
		s.Current = "absent"
		return s, nil
	}

	s.Current = "present"

	// Check the file(s) content, permissions and ownership
	switch f.FileType {
	case fileTypeRegular:
		if !fi.Mode().IsRegular() {
			return s, fmt.Errorf("%s exists, but is not a regular file", f.Path)
		}

		outdated, err := f.isRegularFileContentOutdated()
		if err != nil {
			return s, err
		}

		if outdated {
			s.Outdated = true
		}
	case fileTypeDirectory:
		if !fi.IsDir() {
			return s, fmt.Errorf("%s exists, but is not a directory", f.Path)
		}

		outdated, err := f.isDirectoryContentOutdated()
		if err != nil {
			return s, err
		}

		if outdated {
			s.Outdated = true
		}
	}

	outdated, err := f.isPermissionsOutdated()
	if err != nil {
		return s, err
	}

	if outdated {
		s.Outdated = true
	}

	outdated, err = f.isOwnerOutdated()
	if err != nil {
		return s, err
	}

	if outdated {
		s.Outdated = true
	}

	// Report on what has been identified as being out of date
	if f.Purge {
		for name := range f.extra {
			Log(f, "%s exists, but is not part of source\n", name)
			s.Outdated = true
		}
	}

	for _, item := range f.outdated {
		if item.flags&flagOutdatedContent != 0 {
			Log(f, "content of %s is out of date\n", item.dst)
		}
		if item.flags&flagOutdatedPermissions != 0 {
			Log(f, "permissions of %s are out of date\n", item.dst)
		}
		if item.flags&flagOutdatedOwner != 0 {
			Log(f, "ownership of %s is out of date\n", item.dst)
		}
	}

	return s, nil
}

// Create creates the file managed by the resource
func (f *File) Create() error {
	Log(f, "creating resource\n")

	switch f.FileType {
	case fileTypeRegular:
		if err := f.createRegularFile(); err != nil {
			return err
		}

		dst := utils.NewFileUtil(f.Path)
		if err := dst.Chmod(f.Mode); err != nil {
			return err
		}
		if err := dst.SetOwner(f.Owner, f.Group); err != nil {
			return err
		}
	case fileTypeDirectory:
		if err := f.createDirectory(); err != nil {
			return err
		}

		dstRegistry, err := directoryFileRegistry(f.Path, []string{})
		if err != nil {
			return err
		}

		for _, path := range dstRegistry {
			dst := utils.NewFileUtil(path)
			if err := dst.Chmod(f.Mode); err != nil {
				return err
			}
			if err := dst.SetOwner(f.Owner, f.Group); err != nil {
				return err
			}
		}
	}

	return nil
}

// Delete deletes the file managed by the resource
func (f *File) Delete() error {
	Log(f, "removing resource\n")

	if f.Recursive {
		return os.RemoveAll(f.Path)
	}

	return os.Remove(f.Path)
}

// Update updates the files managed by the resource
func (f *File) Update() error {
	// Purge extra files
	if f.Purge {
		for name := range f.extra {
			dstFile := utils.NewFileUtil(name)
			Log(f, "purging %s\n", name)
			if err := dstFile.Remove(); err != nil {
				return err
			}
		}
	}

	// Fix outdated files
	for _, item := range f.outdated {
		dstFile := utils.NewFileUtil(item.dst)

		// Update file content if needed
		if item.flags&flagOutdatedContent != 0 {
			// Create parent directory for file if missing
			dstDir := filepath.Dir(item.dst)
			_, err := os.Stat(dstDir)
			if os.IsNotExist(err) {
				if err := os.MkdirAll(dstDir, 0755); err != nil {
					return err
				}
			}

			srcFile := utils.NewFileUtil(item.src)
			srcMd5, err := srcFile.Md5()
			if err != nil {
				return err
			}

			Log(f, "setting content of %s to md5:%s\n", item.dst, srcMd5)
			if err := dstFile.CopyFrom(item.src, true); err != nil {
				return err
			}
		}

		// Update permissions if needed
		if item.flags&flagOutdatedPermissions != 0 {
			Log(f, "setting permissions of %s to %#o\n", item.dst, f.Mode)
			if err := dstFile.Chmod(f.Mode); err != nil {
				return err
			}
		}

		// Update ownership if needed
		if item.flags&flagOutdatedOwner != 0 {
			Log(f, "setting owner of %s to %s:%s\n", item.dst, f.Owner, f.Group)
			if err := dstFile.SetOwner(f.Owner, f.Group); err != nil {
				return err
			}
		}
	}

	return nil
}

// directoryFileRegistry creates a map of all files found in a
// given directory. The keys of the map are the file names with the
// leading source path trimmed and the values are the
// full path to the discovered files.
func directoryFileRegistry(path string, skip []string) (map[string]string, error) {
	registry := make(map[string]string)

	found, err := utils.WalkPath(path, skip)
	if err != nil {
		return registry, err
	}

	for _, name := range found {
		fi, err := os.Stat(name)
		if err != nil {
			return registry, err
		}

		if fi.Mode().IsRegular() {
			trimmed := strings.TrimPrefix(name, path+"/")
			registry[trimmed] = name
		}
	}

	return registry, nil
}

// createRegularFile creates the file and content managed by the resource
func (f *File) createRegularFile() error {
	dst := utils.NewFileUtil(f.Path)

	switch {
	case f.Source != "":
		// We have a source file, use it
		srcPath := filepath.Join(DefaultConfig.SiteRepo, f.Source)
		if err := dst.CopyFrom(srcPath, false); err != nil {
			return err
		}
	case f.Source == "" && dst.Exists():
		// We have no source, do nothing
		break
	case f.Source == "" && !dst.Exists():
		// Create an empty file
		if _, err := os.Create(f.Path); err != nil {
			return err
		}
	}

	return nil
}

// createDirectory creates the directory and content managed by the resource
func (f *File) createDirectory() error {
	switch {
	case !f.Recursive:
		return os.Mkdir(f.Path, 0755)
	case f.Recursive && f.Source != "":
		srcPath := filepath.Join(DefaultConfig.SiteRepo, f.Source)
		return utils.CopyDir(srcPath, f.Path)
	case f.Recursive && f.Source == "":
		return os.MkdirAll(f.Path, 0755)
	}

	// Not reached
	return nil
}

// isRegularFileContentOutdated returns a boolean indicating whether the
// content managed by the resource is outdated compared to the source
// file defined by the resource.
// If the file is identified as being out of date it will be appended to the
// list of outdated files for the resource, so it can be further
// processed if needed.
func (f *File) isRegularFileContentOutdated() (bool, error) {
	if f.Source != "" {
		srcPath := filepath.Join(DefaultConfig.SiteRepo, f.Source)
		same, err := utils.SameContent(srcPath, f.Path)
		if err != nil {
			return false, err
		}

		if !same {
			item := &outdatedFile{
				src: srcPath,
				dst: f.Path,
			}
			item.flags |= flagOutdatedContent
			f.outdated = append(f.outdated, item)
			return true, nil
		}
	}

	return false, nil
}

// isDirectoryContentOutdated returns a boolean indicating whether the
// content of the directory managed by the resource is outdated
// compared to the source directory defined by the resource.
// The files identified as being out of date will be appended to the
// list of outdated files for the resource, so they can be further
// processed if needed.
func (f *File) isDirectoryContentOutdated() (bool, error) {
	isOutdated := false
	if f.Source != "" && f.Recursive {
		srcPath := filepath.Join(DefaultConfig.SiteRepo, f.Source)

		// Exclude the ".git" repo directory from the source path,
		// since our source files reside in a git repo
		srcRegistry, err := directoryFileRegistry(srcPath, []string{".git"})
		if err != nil {
			return false, err
		}

		dstRegistry, err := directoryFileRegistry(f.Path, []string{})
		if err != nil {
			return false, err
		}

		// Check source and destination files' content
		for name := range srcRegistry {
			item := &outdatedFile{
				src: srcRegistry[name],
				dst: dstRegistry[name],
			}
			item.flags |= flagOutdatedContent

			// File is missing
			if _, ok := dstRegistry[name]; !ok {
				item.dst = filepath.Join(f.Path, name)
				f.outdated = append(f.outdated, item)
				isOutdated = true
				continue
			}

			// Check if content has changed
			same, err := utils.SameContent(srcRegistry[name], dstRegistry[name])
			if err != nil {
				return false, err
			}

			if !same {
				f.outdated = append(f.outdated, item)
				isOutdated = true
			}
		}

		// Check for extra files in the managed directory
		for name := range dstRegistry {
			if _, ok := srcRegistry[name]; !ok {
				f.extra[dstRegistry[name]] = struct{}{}
			}
		}
	}

	return isOutdated, nil
}

// isPermissionsOutdated returns a boolean indicating whether the
// file's permissions managed by the resource are outdated compared
// to the ones defined by the resource.
// Each file identified as being out of date will be appended to the
// list of outdated files for the resource, so they can be further
// processed if needed.
func (f *File) isPermissionsOutdated() (bool, error) {
	dstRegistry, err := directoryFileRegistry(f.Path, []string{})
	if err != nil {
		return false, err
	}

	isOutdated := false
	for name := range dstRegistry {
		// Skip extra files
		if _, ok := f.extra[dstRegistry[name]]; ok {
			continue
		}

		item := &outdatedFile{
			dst: dstRegistry[name],
		}
		item.flags |= flagOutdatedPermissions

		dst := utils.NewFileUtil(dstRegistry[name])
		mode, err := dst.Mode()
		if err != nil {
			return false, err
		}

		if mode.Perm() != f.Mode {
			f.outdated = append(f.outdated, item)
			isOutdated = true
		}
	}

	return isOutdated, nil
}

// isOwnerOutdated returns a boolean indicating whether the
// file's owner managed by the resource is outdated compared to the
// ones defined by the resource.
// Each file identified as being out of date will be appended to the
// list of outdated files for the resource, so they can be further
// processed if needed.
func (f *File) isOwnerOutdated() (bool, error) {
	dstRegistry, err := directoryFileRegistry(f.Path, []string{})
	if err != nil {
		return false, err
	}

	isOutdated := false
	for name := range dstRegistry {
		// Skip extra files
		if _, ok := f.extra[dstRegistry[name]]; ok {
			continue
		}

		item := &outdatedFile{
			dst: dstRegistry[name],
		}
		item.flags |= flagOutdatedOwner
		dst := utils.NewFileUtil(dstRegistry[name])
		owner, err := dst.Owner()
		if err != nil {
			return false, err
		}

		if f.Owner != owner.User.Username || f.Group != owner.Group.Name {
			f.outdated = append(f.outdated, item)
			isOutdated = true
		}
	}

	return isOutdated, nil
}

func init() {
	item := ProviderItem{
		Type:      "file",
		Provider:  NewFile,
		Namespace: DefaultResourceNamespace,
	}

	RegisterProvider(item)
}
<commit_msg>resource: refactor File resource type<commit_after>package resource

import (
	"crypto/md5"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"

	"github.com/dnaeon/gru/utils"
)

// BaseFile type is the base type which is embedded by
// File, Directory and Link resources.
type BaseFile struct {
	Base

	// Path to the file. Defaults to the resource name.
	Path string `luar:"-"`

	// Permission bits to set on the file.
	// For regular files defaults to 0644.
	// For directories defaults to 0755.
	Mode os.FileMode `luar:"mode"`

	// Owner of the file. Defaults to the currently running user.
	Owner string `luar:"owner"`

	// Group of the file.
	// Defaults to the group of the currently running user.
	Group string `luar:"group"`
}

// isModeSynced returns a boolean indicating whether the
// permissions of the file managed by the resource are in sync.
func (bf *BaseFile) isModeSynced() (bool, error) {
	dst := utils.NewFileUtil(bf.Path)

	if !dst.Exists() {
		return false, ErrResourceAbsent
	}

	mode, err := dst.Mode()
	if err != nil {
		return false, err
	}

	return mode == bf.Mode, nil
}

// setMode sets the permissions on the file managed by the resource.
func (bf *BaseFile) setMode() error {
	dst := utils.NewFileUtil(bf.Path)

	return dst.Chmod(bf.Mode)
}

// isOwnerSynced checks whether the file ownership is correct.
func (bf *BaseFile) isOwnerSynced() (bool, error) {
	dst := utils.NewFileUtil(bf.Path)

	if !dst.Exists() {
		return false, ErrResourceAbsent
	}

	owner, err := dst.Owner()
	if err != nil {
		return false, err
	}

	return owner.User.Username == bf.Owner && owner.Group.Name == bf.Group, nil
}

// setOwner sets the ownership of the file.
func (bf *BaseFile) setOwner() error {
	dst := utils.NewFileUtil(bf.Path)

	return dst.SetOwner(bf.Owner, bf.Group)
}

// File resource manages files.
//
// Example:
//   foo = resource.file.new("/tmp/foo")
//   foo.state = "present"
//   foo.mode = tonumber("0600", 8)
//   foo.owner = "root"
//   foo.group = "wheel"
//   foo.content = "content of file foo"
type File struct {
	BaseFile

	// Content of file to set.
	Content []byte `luar:"content"`

	// Source file to use for the file content.
	Source string `luar:"source"`
}

// isContentSynced checks if the file content is in sync with the
// given content.
func (f *File) isContentSynced() (bool, error) {
	// We don't have a content, assume content is correct
	if f.Content == nil {
		return true, nil
	}

	dst := utils.NewFileUtil(f.Path)
	if !dst.Exists() {
		return false, ErrResourceAbsent
	}

	dstMd5, err := dst.Md5()
	if err != nil {
		return false, err
	}

	srcMd5 := fmt.Sprintf("%x", md5.Sum(f.Content))

	return srcMd5 == dstMd5, nil
}

// setContent sets the content of the file.
func (f *File) setContent() error {
	return ioutil.WriteFile(f.Path, f.Content, f.Mode)
}

// NewFile creates a resource for managing regular files.
func NewFile(name string) (Resource, error) {
	// Defaults for owner and group
	currentUser, err := user.Current()
	if err != nil {
		return nil, err
	}

	currentGroup, err := user.LookupGroupId(currentUser.Gid)
	if err != nil {
		return nil, err
	}

	// Resource defaults
	f := &File{
		BaseFile: BaseFile{
			Base: Base{
				Name:          name,
				Type:          "file",
				State:         "present",
				Require:       make([]string, 0),
				PresentStates: []string{"present"},
				AbsentStates:  []string{"absent"},
				Concurrent:    true,
				Subscribe:     make(TriggerMap),
			},
			Path:  name,
			Mode:  0644,
			Owner: currentUser.Username,
			Group: currentGroup.Name,
		},
		Content: nil,
		Source:  "",
	}

	// Set resource properties
	f.Properties = []Property{
		Property{
			Name:     "mode",
			Set:      f.setMode,
			IsSynced: f.isModeSynced,
		},
		Property{
			Name:     "ownership",
			Set:      f.setOwner,
			IsSynced: f.isOwnerSynced,
		},
		Property{
			Name:     "content",
			Set:      f.setContent,
			IsSynced: f.isContentSynced,
		},
	}

	return f, nil
}

// Validate validates the file resource.
func (f *File) Validate() error {
	if err := f.Base.Validate(); err != nil {
		return err
	}

	if f.Source != "" && f.Content != nil {
		return errors.New("cannot use both 'source' and 'content'")
	}

	return nil
}

// Initialize initializes the file resource.
func (f *File) Initialize() error {
	// Set file content from the given source file if any.
	// TODO: Currenly this works only for files in the site repo.
	// TODO: Implement a generic file content fetcher.
	if f.Source != "" {
		src := filepath.Join(DefaultConfig.SiteRepo, f.Source)
		content, err := ioutil.ReadFile(src)
		if err != nil {
			return err
		}
		f.Content = content
	}

	return nil
}

// Evaluate evaluates the state of the file resource.
func (f *File) Evaluate() (State, error) {
	state := State{
		Current: "unknown",
		Want:    f.State,
	}

	fi, err := os.Stat(f.Path)
	if os.IsNotExist(err) {
		state.Current = "absent"
		return state, nil
	}

	state.Current = "present"

	if !fi.Mode().IsRegular() {
		return state, errors.New("path exists, but is not a regular file")
	}

	return state, nil
}

// Create creates the file managed by the resource.
func (f *File) Create() error {
	return ioutil.WriteFile(f.Path, f.Content, f.Mode)
}

// Delete deletes the file managed by the resource.
func (f *File) Delete() error {
	return os.Remove(f.Path)
}

// Directory resource manages directories.
//
// Example:
//   bar = resource.directory.new("/tmp/bar")
//   bar.state = "present"
//   bar.mode = tonumber("0700", 8)
//   bar.owner = "root"
//   bar.group = "wheel"
type Directory struct {
	BaseFile

	// Parents flag specifies whether or not to create/delete
	// parent directories. Defaults to false.
	Parents bool `luar:"parents"`
}

// NewDirectory creates a resource for managing directories.
func NewDirectory(name string) (Resource, error) {
	// Defaults for owner and group
	currentUser, err := user.Current()
	if err != nil {
		return nil, err
	}

	currentGroup, err := user.LookupGroupId(currentUser.Gid)
	if err != nil {
		return nil, err
	}

	// Resource defaults
	d := &Directory{
		BaseFile: BaseFile{
			Base: Base{
				Name:          name,
				Type:          "directory",
				State:         "present",
				Require:       make([]string, 0),
				PresentStates: []string{"present"},
				AbsentStates:  []string{"absent"},
				Concurrent:    true,
				Subscribe:     make(TriggerMap),
			},
			Path:  name,
			Mode:  0755,
			Owner: currentUser.Username,
			Group: currentGroup.Name,
		},
		Parents: false,
	}

	// Set resource properties
	d.Properties = []Property{
		Property{
			Name:     "mode",
			Set:      d.setMode,
			IsSynced: d.isModeSynced,
		},
		Property{
			Name:     "ownership",
			Set:      d.setOwner,
			IsSynced: d.isOwnerSynced,
		},
	}

	return d, nil
}

// Evaluate evaluates the state of the directory.
func (d *Directory) Evaluate() (State, error) {
	state := State{
		Current: "unknown",
		Want:    d.State,
	}

	fi, err := os.Stat(d.Path)
	if os.IsNotExist(err) {
		state.Current = "absent"
		return state, nil
	}

	state.Current = "present"

	if !fi.Mode().IsDir() {
		return state, errors.New("path exists, but is not a directory")
	}

	return state, nil
}

// Create creates the directory.
func (d *Directory) Create() error {
	if d.Parents {
		return os.MkdirAll(d.Path, d.Mode)
	}

	return os.Mkdir(d.Path, d.Mode)
}

// Delete removes the directory.
func (d *Directory) Delete() error {
	if d.Parents {
		return os.RemoveAll(d.Path)
	}

	return os.Remove(d.Path)
}

func init() {
	file := ProviderItem{
		Type:      "file",
		Provider:  NewFile,
		Namespace: DefaultResourceNamespace,
	}

	dir := ProviderItem{
		Type:      "directory",
		Provider:  NewDirectory,
		Namespace: DefaultResourceNamespace,
	}

	RegisterProvider(file, dir)
}
<|endoftext|>


<commit_before>// Copyright 2016 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package discovery

import (
	"context"
	"fmt"
	"sort"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"

	"github.com/prometheus/prometheus/config"
	sd_config "github.com/prometheus/prometheus/discovery/config"
	"github.com/prometheus/prometheus/discovery/targetgroup"

	"github.com/prometheus/prometheus/discovery/azure"
	"github.com/prometheus/prometheus/discovery/consul"
	"github.com/prometheus/prometheus/discovery/dns"
	"github.com/prometheus/prometheus/discovery/ec2"
	"github.com/prometheus/prometheus/discovery/file"
	"github.com/prometheus/prometheus/discovery/gce"
	"github.com/prometheus/prometheus/discovery/kubernetes"
	"github.com/prometheus/prometheus/discovery/marathon"
	"github.com/prometheus/prometheus/discovery/openstack"
	"github.com/prometheus/prometheus/discovery/triton"
	"github.com/prometheus/prometheus/discovery/zookeeper"
)

// Discoverer provides information about target groups. It maintains a set
// of sources from which TargetGroups can originate. Whenever a discovery provider
// detects a potential change, it sends the TargetGroup through its channel.
//
// Discoverer does not know if an actual change happened.
// It does guarantee that it sends the new TargetGroup whenever a change happens.
//
// Discoverers should initially send a full set of all discoverable TargetGroups.
type Discoverer interface {
	// Run hands a channel to the discovery provider(consul,dns etc) through which it can send
	// updated target groups.
	// Must returns if the context gets canceled. It should not close the update
	// channel on returning.
	Run(ctx context.Context, up chan<- []*targetgroup.Group)
}

type poolKey struct {
	setName  string
	provider string
}

// byProvider implements sort.Interface for []poolKey based on the provider field.
// Sorting is needed so that we can have predictable tests.
type byProvider []poolKey

func (a byProvider) Len() int           { return len(a) }
func (a byProvider) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a byProvider) Less(i, j int) bool { return a[i].provider < a[j].provider }

// NewManager is the Discovery Manager constructor
func NewManager(logger log.Logger) *Manager {
	return &Manager{
		logger:         logger,
		actionCh:       make(chan func(context.Context)),
		syncCh:         make(chan map[string][]*targetgroup.Group),
		targets:        make(map[poolKey][]*targetgroup.Group),
		discoverCancel: []context.CancelFunc{},
	}
}

// Manager maintains a set of discovery providers and sends each update to a channel used by other packages.
type Manager struct {
	logger         log.Logger
	actionCh       chan func(context.Context)
	discoverCancel []context.CancelFunc
	targets        map[poolKey][]*targetgroup.Group
	// The sync channels sends the updates in map[targetSetName] where targetSetName is the job value from the scrape config.
	syncCh chan map[string][]*targetgroup.Group
}

// Run starts the background processing
func (m *Manager) Run(ctx context.Context) error {
	for {
		select {
		case f := <-m.actionCh:
			f(ctx)
		case <-ctx.Done():
			m.cancelDiscoverers()
			return ctx.Err()
		}
	}
}

// SyncCh returns a read only channel used by all Discoverers to send target updates.
func (m *Manager) SyncCh() <-chan map[string][]*targetgroup.Group {
	return m.syncCh
}

// ApplyConfig removes all running discovery providers and starts new ones using the provided config.
func (m *Manager) ApplyConfig(cfg *config.Config) error {
	err := make(chan error)
	m.actionCh <- func(ctx context.Context) {
		m.cancelDiscoverers()
		for _, scfg := range cfg.ScrapeConfigs {
			for provName, prov := range m.providersFromConfig(scfg.ServiceDiscoveryConfig) {
				m.startProvider(ctx, poolKey{setName: scfg.JobName, provider: provName}, prov)
			}
		}
		close(err)
	}

	return <-err
}

func (m *Manager) startProvider(ctx context.Context, poolKey poolKey, worker Discoverer) {
	ctx, cancel := context.WithCancel(ctx)
	updates := make(chan []*targetgroup.Group)

	m.discoverCancel = append(m.discoverCancel, cancel)

	go worker.Run(ctx, updates)
	go m.runProvider(ctx, poolKey, updates)
}

func (m *Manager) runProvider(ctx context.Context, poolKey poolKey, updates chan []*targetgroup.Group) {
	for {
		select {
		case <-ctx.Done():
			return
		case tgs, ok := <-updates:
			// Handle the case that a target provider exits and closes the channel
			// before the context is done.
			if !ok {
				return
			}
			m.addGroup(poolKey, tgs)
			m.syncCh <- m.allGroups(poolKey)
		}
	}
}

func (m *Manager) cancelDiscoverers() {
	for _, c := range m.discoverCancel {
		c()
	}
	m.targets = make(map[poolKey][]*targetgroup.Group)
	m.discoverCancel = nil
}

func (m *Manager) addGroup(poolKey poolKey, tg []*targetgroup.Group) {
	done := make(chan struct{})

	m.actionCh <- func(ctx context.Context) {
		if tg != nil {
			m.targets[poolKey] = tg
		}
		close(done)

	}
	<-done
}

func (m *Manager) allGroups(pk poolKey) map[string][]*targetgroup.Group {
	tSets := make(chan map[string][]*targetgroup.Group)

	m.actionCh <- func(ctx context.Context) {

		// Sorting by the poolKey is needed so that we can have predictable tests.
		var pKeys []poolKey
		for pk := range m.targets {
			pKeys = append(pKeys, pk)
		}
		sort.Sort(byProvider(pKeys))

		tSetsAll := map[string][]*targetgroup.Group{}
		for _, pk := range pKeys {
			for _, tg := range m.targets[pk] {
				if tg.Source != "" { // Don't add empty targets.
					tSetsAll[pk.setName] = append(tSetsAll[pk.setName], tg)
				}
			}
		}
		tSets <- tSetsAll
	}
	return <-tSets

}

func (m *Manager) providersFromConfig(cfg sd_config.ServiceDiscoveryConfig) map[string]Discoverer {
	providers := map[string]Discoverer{}

	app := func(mech string, i int, tp Discoverer) {
		providers[fmt.Sprintf("%s/%d", mech, i)] = tp
	}

	for i, c := range cfg.DNSSDConfigs {
		app("dns", i, dns.NewDiscovery(*c, log.With(m.logger, "discovery", "dns")))
	}
	for i, c := range cfg.FileSDConfigs {
		app("file", i, file.NewDiscovery(c, log.With(m.logger, "discovery", "file")))
	}
	for i, c := range cfg.ConsulSDConfigs {
		k, err := consul.NewDiscovery(c, log.With(m.logger, "discovery", "consul"))
		if err != nil {
			level.Error(m.logger).Log("msg", "Cannot create Consul discovery", "err", err)
			continue
		}
		app("consul", i, k)
	}
	for i, c := range cfg.MarathonSDConfigs {
		t, err := marathon.NewDiscovery(*c, log.With(m.logger, "discovery", "marathon"))
		if err != nil {
			level.Error(m.logger).Log("msg", "Cannot create Marathon discovery", "err", err)
			continue
		}
		app("marathon", i, t)
	}
	for i, c := range cfg.KubernetesSDConfigs {
		k, err := kubernetes.New(log.With(m.logger, "discovery", "k8s"), c)
		if err != nil {
			level.Error(m.logger).Log("msg", "Cannot create Kubernetes discovery", "err", err)
			continue
		}
		app("kubernetes", i, k)
	}
	for i, c := range cfg.ServersetSDConfigs {
		app("serverset", i, zookeeper.NewServersetDiscovery(c, log.With(m.logger, "discovery", "zookeeper")))
	}
	for i, c := range cfg.NerveSDConfigs {
		app("nerve", i, zookeeper.NewNerveDiscovery(c, log.With(m.logger, "discovery", "nerve")))
	}
	for i, c := range cfg.EC2SDConfigs {
		app("ec2", i, ec2.NewDiscovery(c, log.With(m.logger, "discovery", "ec2")))
	}
	for i, c := range cfg.OpenstackSDConfigs {
		openstackd, err := openstack.NewDiscovery(c, log.With(m.logger, "discovery", "openstack"))
		if err != nil {
			level.Error(m.logger).Log("msg", "Cannot initialize OpenStack discovery", "err", err)
			continue
		}
		app("openstack", i, openstackd)
	}

	for i, c := range cfg.GCESDConfigs {
		gced, err := gce.NewDiscovery(*c, log.With(m.logger, "discovery", "gce"))
		if err != nil {
			level.Error(m.logger).Log("msg", "Cannot initialize GCE discovery", "err", err)
			continue
		}
		app("gce", i, gced)
	}
	for i, c := range cfg.AzureSDConfigs {
		app("azure", i, azure.NewDiscovery(c, log.With(m.logger, "discovery", "azure")))
	}
	for i, c := range cfg.TritonSDConfigs {
		t, err := triton.New(log.With(m.logger, "discovery", "trition"), c)
		if err != nil {
			level.Error(m.logger).Log("msg", "Cannot create Triton discovery", "err", err)
			continue
		}
		app("triton", i, t)
	}
	if len(cfg.StaticConfigs) > 0 {
		app("static", 0, NewStaticProvider(cfg.StaticConfigs))
	}

	return providers
}

// StaticProvider holds a list of target groups that never change.
type StaticProvider struct {
	TargetGroups []*targetgroup.Group
}

// NewStaticProvider returns a StaticProvider configured with the given
// target groups.
func NewStaticProvider(groups []*targetgroup.Group) *StaticProvider {
	for i, tg := range groups {
		tg.Source = fmt.Sprintf("%d", i)
	}
	return &StaticProvider{groups}
}

// Run implements the Worker interface.
func (sd *StaticProvider) Run(ctx context.Context, ch chan<- []*targetgroup.Group) {
	// We still have to consider that the consumer exits right away in which case
	// the context will be canceled.
	select {
	case ch <- sd.TargetGroups:
	case <-ctx.Done():
	}
	close(ch)
}
<commit_msg>discovery - handle Discoverers that send only target Group updates rather than all Targets on every update.<commit_after>// Copyright 2016 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package discovery

import (
	"context"
	"fmt"
	"sort"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"

	"github.com/prometheus/prometheus/config"
	sd_config "github.com/prometheus/prometheus/discovery/config"
	"github.com/prometheus/prometheus/discovery/targetgroup"

	"github.com/prometheus/prometheus/discovery/azure"
	"github.com/prometheus/prometheus/discovery/consul"
	"github.com/prometheus/prometheus/discovery/dns"
	"github.com/prometheus/prometheus/discovery/ec2"
	"github.com/prometheus/prometheus/discovery/file"
	"github.com/prometheus/prometheus/discovery/gce"
	"github.com/prometheus/prometheus/discovery/kubernetes"
	"github.com/prometheus/prometheus/discovery/marathon"
	"github.com/prometheus/prometheus/discovery/openstack"
	"github.com/prometheus/prometheus/discovery/triton"
	"github.com/prometheus/prometheus/discovery/zookeeper"
)

// Discoverer provides information about target groups. It maintains a set
// of sources from which TargetGroups can originate. Whenever a discovery provider
// detects a potential change, it sends the TargetGroup through its channel.
//
// Discoverer does not know if an actual change happened.
// It does guarantee that it sends the new TargetGroup whenever a change happens.
//
// Discoverers should initially send a full set of all discoverable TargetGroups.
type Discoverer interface {
	// Run hands a channel to the discovery provider(consul,dns etc) through which it can send
	// updated target groups.
	// Must returns if the context gets canceled. It should not close the update
	// channel on returning.
	Run(ctx context.Context, up chan<- []*targetgroup.Group)
}

type poolKey struct {
	setName  string
	provider string
}

// byProvider implements sort.Interface for []poolKey based on the provider field.
// Sorting is needed so that we can have predictable tests.
type byProvider []poolKey

func (a byProvider) Len() int           { return len(a) }
func (a byProvider) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a byProvider) Less(i, j int) bool { return a[i].provider < a[j].provider }

// NewManager is the Discovery Manager constructor
func NewManager(logger log.Logger) *Manager {
	return &Manager{
		logger:         logger,
		actionCh:       make(chan func(context.Context)),
		syncCh:         make(chan map[string][]*targetgroup.Group),
		targets:        make(map[poolKey]map[string]*targetgroup.Group),
		discoverCancel: []context.CancelFunc{},
	}
}

// Manager maintains a set of discovery providers and sends each update to a channel used by other packages.
type Manager struct {
	logger         log.Logger
	actionCh       chan func(context.Context)
	discoverCancel []context.CancelFunc
	// We use map[string]*targetgroup.Group to handle Discoverers that send only updates instead of all targets on every update.
	targets map[poolKey]map[string]*targetgroup.Group
	// The sync channels sends the updates in map[targetSetName] where targetSetName is the job value from the scrape config.
	syncCh chan map[string][]*targetgroup.Group
}

// Run starts the background processing
func (m *Manager) Run(ctx context.Context) error {
	for {
		select {
		case f := <-m.actionCh:
			f(ctx)
		case <-ctx.Done():
			m.cancelDiscoverers()
			return ctx.Err()
		}
	}
}

// SyncCh returns a read only channel used by all Discoverers to send target updates.
func (m *Manager) SyncCh() <-chan map[string][]*targetgroup.Group {
	return m.syncCh
}

// ApplyConfig removes all running discovery providers and starts new ones using the provided config.
func (m *Manager) ApplyConfig(cfg *config.Config) error {
	err := make(chan error)
	m.actionCh <- func(ctx context.Context) {
		m.cancelDiscoverers()
		for _, scfg := range cfg.ScrapeConfigs {
			for provName, prov := range m.providersFromConfig(scfg.ServiceDiscoveryConfig) {
				m.startProvider(ctx, poolKey{setName: scfg.JobName, provider: provName}, prov)
			}
		}
		close(err)
	}

	return <-err
}

func (m *Manager) startProvider(ctx context.Context, poolKey poolKey, worker Discoverer) {
	ctx, cancel := context.WithCancel(ctx)
	updates := make(chan []*targetgroup.Group)

	m.discoverCancel = append(m.discoverCancel, cancel)

	go worker.Run(ctx, updates)
	go m.runProvider(ctx, poolKey, updates)
}

func (m *Manager) runProvider(ctx context.Context, poolKey poolKey, updates chan []*targetgroup.Group) {
	for {
		select {
		case <-ctx.Done():
			return
		case tgs, ok := <-updates:
			// Handle the case that a target provider exits and closes the channel
			// before the context is done.
			if !ok {
				return
			}
			m.updateGroup(poolKey, tgs)
			m.syncCh <- m.allGroups()
		}
	}
}

func (m *Manager) cancelDiscoverers() {
	for _, c := range m.discoverCancel {
		c()
	}
	m.targets = make(map[poolKey]map[string]*targetgroup.Group)
	m.discoverCancel = nil
}

func (m *Manager) updateGroup(poolKey poolKey, tg []*targetgroup.Group) {
	done := make(chan struct{})

	m.actionCh <- func(ctx context.Context) {
		if tg != nil {
			for _, t := range tg {
				if _, ok := m.targets[poolKey]; !ok {
					m.targets[poolKey] = make(map[string]*targetgroup.Group)
				}
				m.targets[poolKey][t.Source] = t
			}
		}
		close(done)

	}
	<-done
}

func (m *Manager) allGroups() map[string][]*targetgroup.Group {
	tSets := make(chan map[string][]*targetgroup.Group)

	m.actionCh <- func(ctx context.Context) {

		// Sorting by the poolKey is needed so that we can have predictable tests.
		var pKeys []poolKey
		for pk := range m.targets {
			pKeys = append(pKeys, pk)
		}
		sort.Sort(byProvider(pKeys))

		tSetsAll := map[string][]*targetgroup.Group{}
		for _, pk := range pKeys {
			for _, tg := range m.targets[pk] {
				// Don't add empty targets.
				// Some Discoverers(eg. k8s) send only the updates so removed targets will be updated with an empty Source value.
				if tg.Source != "" {
					tSetsAll[pk.setName] = append(tSetsAll[pk.setName], tg)
				}
			}
		}
		tSets <- tSetsAll
	}
	return <-tSets

}

func (m *Manager) providersFromConfig(cfg sd_config.ServiceDiscoveryConfig) map[string]Discoverer {
	providers := map[string]Discoverer{}

	app := func(mech string, i int, tp Discoverer) {
		providers[fmt.Sprintf("%s/%d", mech, i)] = tp
	}

	for i, c := range cfg.DNSSDConfigs {
		app("dns", i, dns.NewDiscovery(*c, log.With(m.logger, "discovery", "dns")))
	}
	for i, c := range cfg.FileSDConfigs {
		app("file", i, file.NewDiscovery(c, log.With(m.logger, "discovery", "file")))
	}
	for i, c := range cfg.ConsulSDConfigs {
		k, err := consul.NewDiscovery(c, log.With(m.logger, "discovery", "consul"))
		if err != nil {
			level.Error(m.logger).Log("msg", "Cannot create Consul discovery", "err", err)
			continue
		}
		app("consul", i, k)
	}
	for i, c := range cfg.MarathonSDConfigs {
		t, err := marathon.NewDiscovery(*c, log.With(m.logger, "discovery", "marathon"))
		if err != nil {
			level.Error(m.logger).Log("msg", "Cannot create Marathon discovery", "err", err)
			continue
		}
		app("marathon", i, t)
	}
	for i, c := range cfg.KubernetesSDConfigs {
		k, err := kubernetes.New(log.With(m.logger, "discovery", "k8s"), c)
		if err != nil {
			level.Error(m.logger).Log("msg", "Cannot create Kubernetes discovery", "err", err)
			continue
		}
		app("kubernetes", i, k)
	}
	for i, c := range cfg.ServersetSDConfigs {
		app("serverset", i, zookeeper.NewServersetDiscovery(c, log.With(m.logger, "discovery", "zookeeper")))
	}
	for i, c := range cfg.NerveSDConfigs {
		app("nerve", i, zookeeper.NewNerveDiscovery(c, log.With(m.logger, "discovery", "nerve")))
	}
	for i, c := range cfg.EC2SDConfigs {
		app("ec2", i, ec2.NewDiscovery(c, log.With(m.logger, "discovery", "ec2")))
	}
	for i, c := range cfg.OpenstackSDConfigs {
		openstackd, err := openstack.NewDiscovery(c, log.With(m.logger, "discovery", "openstack"))
		if err != nil {
			level.Error(m.logger).Log("msg", "Cannot initialize OpenStack discovery", "err", err)
			continue
		}
		app("openstack", i, openstackd)
	}

	for i, c := range cfg.GCESDConfigs {
		gced, err := gce.NewDiscovery(*c, log.With(m.logger, "discovery", "gce"))
		if err != nil {
			level.Error(m.logger).Log("msg", "Cannot initialize GCE discovery", "err", err)
			continue
		}
		app("gce", i, gced)
	}
	for i, c := range cfg.AzureSDConfigs {
		app("azure", i, azure.NewDiscovery(c, log.With(m.logger, "discovery", "azure")))
	}
	for i, c := range cfg.TritonSDConfigs {
		t, err := triton.New(log.With(m.logger, "discovery", "trition"), c)
		if err != nil {
			level.Error(m.logger).Log("msg", "Cannot create Triton discovery", "err", err)
			continue
		}
		app("triton", i, t)
	}
	if len(cfg.StaticConfigs) > 0 {
		app("static", 0, NewStaticProvider(cfg.StaticConfigs))
	}

	return providers
}

// StaticProvider holds a list of target groups that never change.
type StaticProvider struct {
	TargetGroups []*targetgroup.Group
}

// NewStaticProvider returns a StaticProvider configured with the given
// target groups.
func NewStaticProvider(groups []*targetgroup.Group) *StaticProvider {
	for i, tg := range groups {
		tg.Source = fmt.Sprintf("%d", i)
	}
	return &StaticProvider{groups}
}

// Run implements the Worker interface.
func (sd *StaticProvider) Run(ctx context.Context, ch chan<- []*targetgroup.Group) {
	// We still have to consider that the consumer exits right away in which case
	// the context will be canceled.
	select {
	case ch <- sd.TargetGroups:
	case <-ctx.Done():
	}
	close(ch)
}
<|endoftext|>


<commit_before>package collision2d_test

import (
	"github.com/Tarliton/collision2d"
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestResponseString(t *testing.T) {
	response := collision2d.NewResponse()
	expected := string("Response:\n{A: %!s(<nil>)\nB: %!s(<nil>)\nOverlap: 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000\nOverlapN: {X:0.000000, Y:0.000000}\nOverlapV: {X:0.000000, Y:0.000000}\nAInB: true, BInA: true}")
	output := string(response.String())
	assert.Equal(t, expected, output, "they should be equal")
}
<commit_msg>add missing response test<commit_after>package collision2d_test

import (
	"github.com/Tarliton/collision2d"
	"github.com/stretchr/testify/assert"
	"math"
	"testing"
)

func TestResponseString(t *testing.T) {
	response := collision2d.NewResponse()
	expected := string("Response:\n{A: %!s(<nil>)\nB: %!s(<nil>)\nOverlap: 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000\nOverlapN: {X:0.000000, Y:0.000000}\nOverlapV: {X:0.000000, Y:0.000000}\nAInB: true, BInA: true}")
	output := string(response.String())
	assert.Equal(t, expected, output, "they should be equal")
}

func TestResponseNotColliding(t *testing.T) {
	response := collision2d.NewResponse()
	notCollidingResponse := response.NotColliding()
	assert.Equal(t, nil, notCollidingResponse.A, "they should be equal")
	assert.Equal(t, nil, notCollidingResponse.B, "they should be equal")
	assert.Equal(t, -math.MaxFloat64, notCollidingResponse.Overlap, "they should be equal")
	assert.Equal(t, collision2d.NewVector(0, 0), notCollidingResponse.OverlapN, "they should be equal")
	assert.Equal(t, collision2d.NewVector(0, 0), notCollidingResponse.OverlapV, "they should be equal")
	assert.Equal(t, false, notCollidingResponse.AInB, "they should be equal")
	assert.Equal(t, false, notCollidingResponse.BInA, "they should be equal")
}
<|endoftext|>