<commit_before>// This package provides a simple health check server.
//
// The package lets you register arbitrary health check endpoints by
// providing a name (== URL path) and a callback function.
//
// The health check server listens for HTTP requests on a given port;
// probes are executed by issuing requests to the endpoints' named
// URL paths.
//
// GETing the "/" path provides a list of registered endpoints, one
// per line. GETing "/_ALL_" probes each registered endpoint sequentially,
// returning each endpoint's path, HTTP status code and body per line.
//
// The package works as a "singleton" with just one server in order to
// avoid cluttering the main program by passing handles around.
package healthcheck

import (
	"net/http"
	"fmt"
	"bytes"
)

// Code wishing to get probed by the health-checker needs to provide this callback
type CallbackFunc func () (code int, body string)

// The HTTP server
var server *http.Server

// The HTTP request multiplexer
var serveMux *http.ServeMux

// List of endpoints known by the server
var endpoints map[string]CallbackFunc

// Init
func init() {
	// Create the request multiplexer
	serveMux = http.NewServeMux()
	// Initialize the enpoint list
	endpoints = make(map[string]CallbackFunc)
}

// Configures the health check server
//
//  listenAddr: an address understood by http.ListenAndServe(), e.g. ":8008"
func Configure(listenAddr string) {
	// Create the HTTP server
	server = &http.Server{
		Addr:    listenAddr,
		Handler: serveMux,
	}

	// Add default wildcard handler
	serveMux.HandleFunc("/",
		func(responseWriter http.ResponseWriter, httpRequest *http.Request) {
			path := httpRequest.URL.Path

			// Handle "/": list all our registered endpoints
			if path == "/" {
				fmt.Fprintf(responseWriter, "/_ALL_\n")

				// TBD: Collate
				for endpointPath, _ := range endpoints {
					fmt.Fprintf(responseWriter, "%s\n", endpointPath)
				}
				return
			}

			// Default action: 404
			responseWriter.WriteHeader(http.StatusNotFound)
			fmt.Fprintf(responseWriter, "Path not found\n")
			return
		},
	)

	// Add magical "/_ALL" handler
	serveMux.HandleFunc("/_ALL_",
		func(responseWriter http.ResponseWriter, httpRequest *http.Request) {
			// Response code needs to be set before writing the response body,
			// so we need to pool the body temporarily into resultBody
			var resultCode = 200
			var resultBody bytes.Buffer

			// Call all endpoints sequentially
			// (TBD: if there were _a lot_ of these we could do them in parallel)
			for endpointPath, callback := range endpoints {
				// Call the callback
				code, body := callback()
				// Append path, code, body to response body
				fmt.Fprintf(&resultBody,
					"%s %d %s\n",
					endpointPath,
					code,
					body,
				)
				// Naive assumption: the bigger the code the more serious it is
				if code > resultCode {
					resultCode = code
				}
			}

			// Set HTTP response code
			responseWriter.WriteHeader(resultCode)

			// Write HTTP response body
			// (TBD: more efficient way, buffer -> writer?)
			fmt.Fprintf(responseWriter, resultBody.String())
		},
	)

	// Add a static "ping" endpoint
	AddEndpoint("/ping", func()(code int, body string){
		return 200, "PONG"
	})

	// Debugging
	//AddEndpoint("/ping", func()(code int, body string){ return 400, "DUPLICATEPONG" })
	//AddEndpoint("/fail", func()(code int, body string){ return 500, "FAIL" })
	//AddEndpoint("", func()(code int, body string){ return 500, "EMPTYFAIL" })
	//AddEndpoint("noprecedingslash", func()(code int, body string){ return 500, "FAIL" })
	//AddEndpoint("/hasfinalslash/", func()(code int, body string){ return 500, "FAIL" })

}

// Registers an endpoint with the health checker.
//
// The urlPath must be unique. The callback must return an HTTP response code
// and body text.
//
// Boilerplate:
//
//  healthcheck.AddEndpoint("/my/arbitrary/path" func()(code int, body string) {
//      return 200, "Foobar Plugin is OK"
//  })
//
func AddEndpoint(urlPath string, callback CallbackFunc){
	// Check parameters
	// -syntax
	if len(urlPath) == 0 || urlPath[:1] != "/" || urlPath[len(urlPath)-1:] == "/" {
		panic(fmt.Sprintf(
			"ERROR: Health check endpoint must begin and may not end with a slash: \"%s\"",
			urlPath))
	}
	// - reserved paths
	for _, path := range []string{"/", "/_ALL_"} {
		if urlPath == path {
			panic(fmt.Sprintf(
				"ERROR: Health check path \"%s\" is reserved", path))
		}
	}
	// - registered paths
	_, exists := endpoints[urlPath]
	if exists {
		panic(fmt.Sprintf(
			"ERROR: Health check endpoint \"%s\" already registered", urlPath))
	}

	// Register the HTTP route & handler
	serveMux.HandleFunc(
		urlPath,
		func(responseWriter http.ResponseWriter, httpRequest *http.Request){
			// Call the callback
			code, body := callback()
			// Set HTTP response code
			responseWriter.WriteHeader(code)
			// Write HTTP response body
			fmt.Fprintf(responseWriter, body)
			fmt.Fprintf(responseWriter, "\n")
		},
	)

	// Store the endpoint
	endpoints[urlPath] = callback
}

// Starts the HTTP server
//
// Call this after Configure() and AddEndpoint() calls.
//
// TBD: is it possible to AddEndpoint() after Start()ing?
func Start(){
	err := server.ListenAndServe()

	if err != nil {
		panic(err.Error())
	}
}

// TBD: Cleanup?
func Stop(){

}
<commit_msg>Add AddEndpointPathArray()<commit_after>// This package provides a simple health check server.
//
// The package lets you register arbitrary health check endpoints by
// providing a name (== URL path) and a callback function.
//
// The health check server listens for HTTP requests on a given port;
// probes are executed by issuing requests to the endpoints' named
// URL paths.
//
// GETing the "/" path provides a list of registered endpoints, one
// per line. GETing "/_ALL_" probes each registered endpoint sequentially,
// returning each endpoint's path, HTTP status code and body per line.
//
// The package works as a "singleton" with just one server in order to
// avoid cluttering the main program by passing handles around.
package healthcheck

import (
	"net/http"
	"fmt"
	"bytes"
)

// Code wishing to get probed by the health-checker needs to provide this callback
type CallbackFunc func () (code int, body string)

// The HTTP server
var server *http.Server

// The HTTP request multiplexer
var serveMux *http.ServeMux

// List of endpoints known by the server
var endpoints map[string]CallbackFunc

// Init
func init() {
	// Create the request multiplexer
	serveMux = http.NewServeMux()
	// Initialize the enpoint list
	endpoints = make(map[string]CallbackFunc)
}

// Configures the health check server
//
//  listenAddr: an address understood by http.ListenAndServe(), e.g. ":8008"
func Configure(listenAddr string) {
	// Create the HTTP server
	server = &http.Server{
		Addr:    listenAddr,
		Handler: serveMux,
	}

	// Add default wildcard handler
	serveMux.HandleFunc("/",
		func(responseWriter http.ResponseWriter, httpRequest *http.Request) {
			path := httpRequest.URL.Path

			// Handle "/": list all our registered endpoints
			if path == "/" {
				fmt.Fprintf(responseWriter, "/_ALL_\n")

				// TBD: Collate
				for endpointPath, _ := range endpoints {
					fmt.Fprintf(responseWriter, "%s\n", endpointPath)
				}
				return
			}

			// Default action: 404
			responseWriter.WriteHeader(http.StatusNotFound)
			fmt.Fprintf(responseWriter, "Path not found\n")
			return
		},
	)

	// Add magical "/_ALL" handler
	serveMux.HandleFunc("/_ALL_",
		func(responseWriter http.ResponseWriter, httpRequest *http.Request) {
			// Response code needs to be set before writing the response body,
			// so we need to pool the body temporarily into resultBody
			var resultCode = 200
			var resultBody bytes.Buffer

			// Call all endpoints sequentially
			// (TBD: if there were _a lot_ of these we could do them in parallel)
			// TBD: Collate (output at least)
			for endpointPath, callback := range endpoints {
				// Call the callback
				code, body := callback()
				// Append path, code, body to response body
				fmt.Fprintf(&resultBody,
					"%s %d %s\n",
					endpointPath,
					code,
					body,
				)
				// Naive assumption: the bigger the code the more serious it is
				if code > resultCode {
					resultCode = code
				}
			}

			// Set HTTP response code
			responseWriter.WriteHeader(resultCode)

			// Write HTTP response body
			// (TBD: more efficient way, buffer -> writer?)
			fmt.Fprintf(responseWriter, resultBody.String())
		},
	)

	// Add a static "ping" endpoint
	AddEndpoint("/ping", func()(code int, body string){
		return 200, "PONG"
	})

	// Debugging
	//AddEndpoint("/ping", func()(code int, body string){ return 400, "DUPLICATEPONG" })
	//AddEndpoint("/fail", func()(code int, body string){ return 500, "FAIL" })
	//AddEndpoint("", func()(code int, body string){ return 500, "EMPTYFAIL" })
	//AddEndpoint("noprecedingslash", func()(code int, body string){ return 500, "FAIL" })
	//AddEndpoint("/hasfinalslash/", func()(code int, body string){ return 500, "FAIL" })

}

// Registers an endpoint with the health checker.
//
// The urlPath must be unique. The callback must return an HTTP response code
// and body text.
//
// Boilerplate:
//
//  healthcheck.AddEndpoint("/my/arbitrary/path" func()(code int, body string) {
//      return 200, "Foobar Plugin is OK"
//  })
//
func AddEndpoint(urlPath string, callback CallbackFunc){
	// Check parameters
	// -syntax
	if len(urlPath) == 0 || urlPath[:1] != "/" || urlPath[len(urlPath)-1:] == "/" {
		panic(fmt.Sprintf(
			"ERROR: Health check endpoint must begin and may not end with a slash: \"%s\"",
			urlPath))
	}
	// - reserved paths
	for _, path := range []string{"/", "/_ALL_"} {
		if urlPath == path {
			panic(fmt.Sprintf(
				"ERROR: Health check path \"%s\" is reserved", path))
		}
	}
	// - registered paths
	_, exists := endpoints[urlPath]
	if exists {
		panic(fmt.Sprintf(
			"ERROR: Health check endpoint \"%s\" already registered", urlPath))
	}

	// Register the HTTP route & handler
	serveMux.HandleFunc(
		urlPath,
		func(responseWriter http.ResponseWriter, httpRequest *http.Request){
			// Call the callback
			code, body := callback()
			// Set HTTP response code
			responseWriter.WriteHeader(code)
			// Write HTTP response body
			fmt.Fprintf(responseWriter, body)
			fmt.Fprintf(responseWriter, "\n")
		},
	)

	// Store the endpoint
	endpoints[urlPath] = callback
}

// Registers an endpoint with the health checker.
//
// This is a convenience version of AddEndpoint() that takes
// the urlPath's components as a list of strings and catenates
// them.
func AddEndpointPathArray(urlPath []string, callback CallbackFunc) {
	// Catenate path
	var cat bytes.Buffer
	for _, pathComponent := range urlPath {
		fmt.Fprintf(&cat, "/%s", pathComponent)
	}
	// Call it
	AddEndpoint(cat.String(), callback)
}

// Starts the HTTP server
//
// Call this after Configure() and AddEndpoint() calls.
//
// TBD: is it possible to AddEndpoint() after Start()ing?
func Start(){
	err := server.ListenAndServe()

	if err != nil {
		panic(err.Error())
	}
}

// TBD: Cleanup?
func Stop(){

}
<|endoftext|>


<commit_before>package iniflags

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"path"
	"strings"
	"syscall"
	"time"
)

var (
	config               = flag.String("config", "", "Path to ini config for using in go flags. May be relative to the current executable path.")
	configUpdateInterval = flag.Duration("configUpdateInterval", 0, "Update interval for re-reading config file set via -config flag. Zero disables config file re-reading.")
	dumpflags            = flag.Bool("dumpflags", false, "Dumps values for all flags defined in the app into stdout in ini-compatible syntax and terminates the app.")
)

var (
	flagChangeCallbacks = make(map[string][]FlagChangeCallback)
	importStack         []string
	parsed              bool
)

// Generation is flags' generation number.
//
// It is modified on each flags' modification
// via either -configUpdateInterval or SIGHUP.
var Generation int

// Parse obtains flag values from config file set via -config.
//
// It obtains flag values from command line like flag.Parse(), then overrides
// them by values parsed from config file set via -config.
//
// Path to config file can also be set via SetConfigFile() before Parse() call.
func Parse() {
	if parsed {
		panic("iniflags: duplicate call to iniflags.Parse() detected")
	}

	parsed = true
	flag.Parse()
	_, ok := parseConfigFlags()
	if !ok {
		os.Exit(1)
	}

	if *dumpflags {
		dumpFlags()
		os.Exit(0)
	}

	for flagName := range flagChangeCallbacks {
		verifyFlagChangeFlagName(flagName)
	}
	Generation++
	issueAllFlagChangeCallbacks()

	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGHUP)
	go sighupHandler(ch)

	go configUpdater()
}

func configUpdater() {
	if *configUpdateInterval != 0 {
		for {
			// Use time.Sleep() instead of time.Tick() for the sake of dynamic flag update.
			time.Sleep(*configUpdateInterval)
			updateConfig()
		}
	}
}

func updateConfig() {
	if oldFlagValues, ok := parseConfigFlags(); ok && len(oldFlagValues) > 0 {
		modifiedFlags := make(map[string]string)
		for k := range oldFlagValues {
			modifiedFlags[k] = flag.Lookup(k).Value.String()
		}
		log.Printf("iniflags: read updated config. Modified flags are: %v\n", modifiedFlags)
		Generation++
		issueFlagChangeCallbacks(oldFlagValues)
	}
}

// FlagChangeCallback is called when the given flag is changed.
//
// The callback may be registered for any flag via OnFlagChange().
type FlagChangeCallback func()

// OnFlagChange registers the callback, which is called after the given flag
// value is initialized and/or changed.
//
// Flag values are initialized during iniflags.Parse() call.
// Flag value can be changed on config re-read after obtaining SIGHUP signal
// or if periodic config re-read is enabled with -configUpdateInterval flag.
//
// Note that flags set via command-line cannot be overriden via config file modifications.
func OnFlagChange(flagName string, callback FlagChangeCallback) {
	if parsed {
		verifyFlagChangeFlagName(flagName)
	}
	flagChangeCallbacks[flagName] = append(flagChangeCallbacks[flagName], callback)
}

func verifyFlagChangeFlagName(flagName string) {
	if flag.Lookup(flagName) == nil {
		log.Fatalf("iniflags: cannot register FlagChangeCallback for non-existing flag [%s]\n", flagName)
	}
}

func issueFlagChangeCallbacks(oldFlagValues map[string]string) {
	for flagName := range oldFlagValues {
		if fs, ok := flagChangeCallbacks[flagName]; ok {
			for _, f := range fs {
				f()
			}
		}
	}
}

func issueAllFlagChangeCallbacks() {
	for _, fs := range flagChangeCallbacks {
		for _, f := range fs {
			f()
		}
	}
}

func sighupHandler(ch <-chan os.Signal) {
	for _ = range ch {
		updateConfig()
	}
}

func parseConfigFlags() (oldFlagValues map[string]string, ok bool) {
	configPath := *config
	if !strings.HasPrefix(configPath, "./") {
		if configPath, ok = combinePath(os.Args[0], *config); !ok {
			return nil, false
		}
	}
	if configPath == "" {
		return nil, true
	}
	parsedArgs, ok := getArgsFromConfig(configPath)
	if !ok {
		return nil, false
	}
	missingFlags := getMissingFlags()

	ok = true
	oldFlagValues = make(map[string]string)
	for _, arg := range parsedArgs {
		f := flag.Lookup(arg.Key)
		if f == nil {
			log.Printf("iniflags: unknown flag name=[%s] found at line [%d] of file [%s]\n", arg.Key, arg.LineNum, arg.FilePath)
			ok = false
			continue
		}

		if _, found := missingFlags[f.Name]; found {
			oldValue := f.Value.String()
			if oldValue == arg.Value {
				continue
			}
			if err := f.Value.Set(arg.Value); err != nil {
				log.Printf("iniflags: error when parsing flag [%s] value [%s] at line [%d] of file [%s]: [%s]\n", arg.Key, arg.Value, arg.LineNum, arg.FilePath, err)
				ok = false
				continue
			}
			if oldValue != f.Value.String() {
				oldFlagValues[arg.Key] = oldValue
			}
		}
	}

	if !ok {
		// restore old flag values
		for k, v := range oldFlagValues {
			flag.Set(k, v)
		}
		oldFlagValues = nil
	}

	return oldFlagValues, ok
}

func checkImportRecursion(configPath string) bool {
	for _, path := range importStack {
		if path == configPath {
			log.Printf("iniflags: import recursion found for [%s]: %v\n", configPath, importStack)
			return false
		}
	}
	return true
}

type flagArg struct {
	Key      string
	Value    string
	FilePath string
	LineNum  int
}

func stripBOM(s string) string {
	if len(s) < 3 {
		return s
	}
	bom := s[:3]
	if bom == "\ufeff" || bom == "\ufffe" {
		return s[3:]
	}
	return s
}

func getArgsFromConfig(configPath string) (args []flagArg, ok bool) {
	if !checkImportRecursion(configPath) {
		return nil, false
	}
	importStack = append(importStack, configPath)
	defer func() {
		importStack = importStack[:len(importStack)-1]
	}()

	file := openConfigFile(configPath)
	if file == nil {
		return nil, false
	}
	defer file.Close()
	r := bufio.NewReader(file)

	var lineNum int
	for {
		lineNum++
		line, err := r.ReadString('\n')
		if err != nil && line == "" {
			if err == io.EOF {
				break
			}
			log.Printf("iniflags: error when reading file [%s] at line %d: [%s]\n", configPath, lineNum, err)
			return nil, false
		}
		if lineNum == 1 {
			line = stripBOM(line)
		}
		line = strings.TrimSpace(line)
		if strings.HasPrefix(line, "#import ") {
			importPath, ok := unquoteValue(line[7:], lineNum, configPath)
			if !ok {
				return nil, false
			}
			if importPath, ok = combinePath(configPath, importPath); !ok {
				return nil, false
			}
			importArgs, ok := getArgsFromConfig(importPath)
			if !ok {
				return nil, false
			}
			args = append(args, importArgs...)
			continue
		}
		if line == "" || line[0] == ';' || line[0] == '#' || line[0] == '[' {
			continue
		}
		parts := strings.SplitN(line, "=", 2)
		if len(parts) != 2 {
			log.Printf("iniflags: cannot split [%s] at line %d into key and value in config file [%s]\n", line, lineNum, configPath)
			return nil, false
		}
		key := strings.TrimSpace(parts[0])
		value, ok := unquoteValue(parts[1], lineNum, configPath)
		if !ok {
			return nil, false
		}
		args = append(args, flagArg{Key: key, Value: value, FilePath: configPath, LineNum: lineNum})
	}

	return args, true
}

func openConfigFile(path string) io.ReadCloser {
	if isHTTP(path) {
		resp, err := http.Get(path)
		if err != nil {
			log.Printf("iniflags: cannot load config file at [%s]: [%s]\n", path, err)
			return nil
		}
		if resp.StatusCode != http.StatusOK {
			log.Printf("iniflags: unexpected http status code when obtaining config file [%s]: %d. Expected %d\n", path, resp.StatusCode, http.StatusOK)
			return nil
		}
		return resp.Body
	}

	file, err := os.Open(path)
	if err != nil {
		log.Printf("iniflags: cannot open config file at [%s]: [%s]\n", path, err)
		return nil
	}
	return file
}

func combinePath(basePath, relPath string) (string, bool) {
	if isHTTP(basePath) {
		base, err := url.Parse(basePath)
		if err != nil {
			log.Printf("iniflags: error when parsing http base path [%s]: %s\n", basePath, err)
			return "", false
		}
		rel, err := url.Parse(relPath)
		if err != nil {
			log.Printf("iniflags: error when parsing http rel path [%s] for base [%s]: %s\n", relPath, basePath, err)
			return "", false
		}
		return base.ResolveReference(rel).String(), true
	}

	if relPath == "" || relPath[0] == '/' || isHTTP(relPath) {
		return relPath, true
	}
	return path.Join(path.Dir(basePath), relPath), true
}

func isHTTP(path string) bool {
	return strings.HasPrefix(strings.ToLower(path), "http://") || strings.HasPrefix(strings.ToLower(path), "https://")
}

func getMissingFlags() map[string]bool {
	setFlags := make(map[string]bool)
	flag.Visit(func(f *flag.Flag) {
		setFlags[f.Name] = true
	})

	missingFlags := make(map[string]bool)
	flag.VisitAll(func(f *flag.Flag) {
		if _, ok := setFlags[f.Name]; !ok {
			missingFlags[f.Name] = true
		}
	})
	return missingFlags
}

func dumpFlags() {
	flag.VisitAll(func(f *flag.Flag) {
		if f.Name != "config" && f.Name != "dumpflags" {
			fmt.Printf("%s = %s  # %s\n", f.Name, quoteValue(f.Value.String()), escapeUsage(f.Usage))
		}
	})
}

func escapeUsage(s string) string {
	return strings.Replace(s, "\n", "\n    # ", -1)
}

func quoteValue(v string) string {
	if !strings.ContainsAny(v, "\n#;") && strings.TrimSpace(v) == v {
		return v
	}
	v = strings.Replace(v, "\\", "\\\\", -1)
	v = strings.Replace(v, "\n", "\\n", -1)
	v = strings.Replace(v, "\"", "\\\"", -1)
	return fmt.Sprintf("\"%s\"", v)
}

func unquoteValue(v string, lineNum int, configPath string) (string, bool) {
	v = strings.TrimSpace(v)
	if len(v) == 0 {
		return "", true
	}
	if v[0] != '"' {
		return removeTrailingComments(v), true
	}
	n := strings.LastIndex(v, "\"")
	if n == -1 {
		log.Printf("iniflags: unclosed string found [%s] at line %d in config file [%s]\n", v, lineNum, configPath)
		return "", false
	}
	v = v[1:n]
	v = strings.Replace(v, "\\\"", "\"", -1)
	v = strings.Replace(v, "\\n", "\n", -1)
	return strings.Replace(v, "\\\\", "\\", -1), true
}

func removeTrailingComments(v string) string {
	v = strings.Split(v, "#")[0]
	v = strings.Split(v, ";")[0]
	return strings.TrimSpace(v)
}

// SetConfigFile sets path to config file.
//
// Call this function before Parse() if you need default path to config file
// when -config command-line flag is not set.
func SetConfigFile(path string) {
	if parsed {
		panic("iniflags: SetConfigFile() must be called before Parse()")
	}
	*config = path
}
<commit_msg>Adding flag option to allow unknown ini flags.<commit_after>package iniflags

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"path"
	"strings"
	"syscall"
	"time"
)

var (
	allowUnknownFlags    = flag.Bool("allowUnknownFlags", false, "Don't terminate the app if ini file contains unknown flags.")
	config               = flag.String("config", "", "Path to ini config for using in go flags. May be relative to the current executable path.")
	configUpdateInterval = flag.Duration("configUpdateInterval", 0, "Update interval for re-reading config file set via -config flag. Zero disables config file re-reading.")
	dumpflags            = flag.Bool("dumpflags", false, "Dumps values for all flags defined in the app into stdout in ini-compatible syntax and terminates the app.")
)

var (
	flagChangeCallbacks = make(map[string][]FlagChangeCallback)
	importStack         []string
	parsed              bool
)

// Generation is flags' generation number.
//
// It is modified on each flags' modification
// via either -configUpdateInterval or SIGHUP.
var Generation int

// Parse obtains flag values from config file set via -config.
//
// It obtains flag values from command line like flag.Parse(), then overrides
// them by values parsed from config file set via -config.
//
// Path to config file can also be set via SetConfigFile() before Parse() call.
func Parse() {
	if parsed {
		panic("iniflags: duplicate call to iniflags.Parse() detected")
	}

	parsed = true
	flag.Parse()
	_, ok := parseConfigFlags()
	if !ok {
		os.Exit(1)
	}

	if *dumpflags {
		dumpFlags()
		os.Exit(0)
	}

	for flagName := range flagChangeCallbacks {
		verifyFlagChangeFlagName(flagName)
	}
	Generation++
	issueAllFlagChangeCallbacks()

	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGHUP)
	go sighupHandler(ch)

	go configUpdater()
}

func configUpdater() {
	if *configUpdateInterval != 0 {
		for {
			// Use time.Sleep() instead of time.Tick() for the sake of dynamic flag update.
			time.Sleep(*configUpdateInterval)
			updateConfig()
		}
	}
}

func updateConfig() {
	if oldFlagValues, ok := parseConfigFlags(); ok && len(oldFlagValues) > 0 {
		modifiedFlags := make(map[string]string)
		for k := range oldFlagValues {
			modifiedFlags[k] = flag.Lookup(k).Value.String()
		}
		log.Printf("iniflags: read updated config. Modified flags are: %v\n", modifiedFlags)
		Generation++
		issueFlagChangeCallbacks(oldFlagValues)
	}
}

// FlagChangeCallback is called when the given flag is changed.
//
// The callback may be registered for any flag via OnFlagChange().
type FlagChangeCallback func()

// OnFlagChange registers the callback, which is called after the given flag
// value is initialized and/or changed.
//
// Flag values are initialized during iniflags.Parse() call.
// Flag value can be changed on config re-read after obtaining SIGHUP signal
// or if periodic config re-read is enabled with -configUpdateInterval flag.
//
// Note that flags set via command-line cannot be overriden via config file modifications.
func OnFlagChange(flagName string, callback FlagChangeCallback) {
	if parsed {
		verifyFlagChangeFlagName(flagName)
	}
	flagChangeCallbacks[flagName] = append(flagChangeCallbacks[flagName], callback)
}

func verifyFlagChangeFlagName(flagName string) {
	if flag.Lookup(flagName) == nil {
		log.Fatalf("iniflags: cannot register FlagChangeCallback for non-existing flag [%s]\n", flagName)
	}
}

func issueFlagChangeCallbacks(oldFlagValues map[string]string) {
	for flagName := range oldFlagValues {
		if fs, ok := flagChangeCallbacks[flagName]; ok {
			for _, f := range fs {
				f()
			}
		}
	}
}

func issueAllFlagChangeCallbacks() {
	for _, fs := range flagChangeCallbacks {
		for _, f := range fs {
			f()
		}
	}
}

func sighupHandler(ch <-chan os.Signal) {
	for _ = range ch {
		updateConfig()
	}
}

func parseConfigFlags() (oldFlagValues map[string]string, ok bool) {
	configPath := *config
	if !strings.HasPrefix(configPath, "./") {
		if configPath, ok = combinePath(os.Args[0], *config); !ok {
			return nil, false
		}
	}
	if configPath == "" {
		return nil, true
	}
	parsedArgs, ok := getArgsFromConfig(configPath)
	if !ok {
		return nil, false
	}
	missingFlags := getMissingFlags()

	ok = true
	oldFlagValues = make(map[string]string)
	for _, arg := range parsedArgs {
		f := flag.Lookup(arg.Key)
		if f == nil {
			log.Printf("iniflags: unknown flag name=[%s] found at line [%d] of file [%s]\n", arg.Key, arg.LineNum, arg.FilePath)
			if !*allowUnknownFlags {
				ok = false
			}
			continue
		}

		if _, found := missingFlags[f.Name]; found {
			oldValue := f.Value.String()
			if oldValue == arg.Value {
				continue
			}
			if err := f.Value.Set(arg.Value); err != nil {
				log.Printf("iniflags: error when parsing flag [%s] value [%s] at line [%d] of file [%s]: [%s]\n", arg.Key, arg.Value, arg.LineNum, arg.FilePath, err)
				ok = false
				continue
			}
			if oldValue != f.Value.String() {
				oldFlagValues[arg.Key] = oldValue
			}
		}
	}

	if !ok {
		// restore old flag values
		for k, v := range oldFlagValues {
			flag.Set(k, v)
		}
		oldFlagValues = nil
	}

	return oldFlagValues, ok
}

func checkImportRecursion(configPath string) bool {
	for _, path := range importStack {
		if path == configPath {
			log.Printf("iniflags: import recursion found for [%s]: %v\n", configPath, importStack)
			return false
		}
	}
	return true
}

type flagArg struct {
	Key      string
	Value    string
	FilePath string
	LineNum  int
}

func stripBOM(s string) string {
	if len(s) < 3 {
		return s
	}
	bom := s[:3]
	if bom == "\ufeff" || bom == "\ufffe" {
		return s[3:]
	}
	return s
}

func getArgsFromConfig(configPath string) (args []flagArg, ok bool) {
	if !checkImportRecursion(configPath) {
		return nil, false
	}
	importStack = append(importStack, configPath)
	defer func() {
		importStack = importStack[:len(importStack)-1]
	}()

	file := openConfigFile(configPath)
	if file == nil {
		return nil, false
	}
	defer file.Close()
	r := bufio.NewReader(file)

	var lineNum int
	for {
		lineNum++
		line, err := r.ReadString('\n')
		if err != nil && line == "" {
			if err == io.EOF {
				break
			}
			log.Printf("iniflags: error when reading file [%s] at line %d: [%s]\n", configPath, lineNum, err)
			return nil, false
		}
		if lineNum == 1 {
			line = stripBOM(line)
		}
		line = strings.TrimSpace(line)
		if strings.HasPrefix(line, "#import ") {
			importPath, ok := unquoteValue(line[7:], lineNum, configPath)
			if !ok {
				return nil, false
			}
			if importPath, ok = combinePath(configPath, importPath); !ok {
				return nil, false
			}
			importArgs, ok := getArgsFromConfig(importPath)
			if !ok {
				return nil, false
			}
			args = append(args, importArgs...)
			continue
		}
		if line == "" || line[0] == ';' || line[0] == '#' || line[0] == '[' {
			continue
		}
		parts := strings.SplitN(line, "=", 2)
		if len(parts) != 2 {
			log.Printf("iniflags: cannot split [%s] at line %d into key and value in config file [%s]\n", line, lineNum, configPath)
			return nil, false
		}
		key := strings.TrimSpace(parts[0])
		value, ok := unquoteValue(parts[1], lineNum, configPath)
		if !ok {
			return nil, false
		}
		args = append(args, flagArg{Key: key, Value: value, FilePath: configPath, LineNum: lineNum})
	}

	return args, true
}

func openConfigFile(path string) io.ReadCloser {
	if isHTTP(path) {
		resp, err := http.Get(path)
		if err != nil {
			log.Printf("iniflags: cannot load config file at [%s]: [%s]\n", path, err)
			return nil
		}
		if resp.StatusCode != http.StatusOK {
			log.Printf("iniflags: unexpected http status code when obtaining config file [%s]: %d. Expected %d\n", path, resp.StatusCode, http.StatusOK)
			return nil
		}
		return resp.Body
	}

	file, err := os.Open(path)
	if err != nil {
		log.Printf("iniflags: cannot open config file at [%s]: [%s]\n", path, err)
		return nil
	}
	return file
}

func combinePath(basePath, relPath string) (string, bool) {
	if isHTTP(basePath) {
		base, err := url.Parse(basePath)
		if err != nil {
			log.Printf("iniflags: error when parsing http base path [%s]: %s\n", basePath, err)
			return "", false
		}
		rel, err := url.Parse(relPath)
		if err != nil {
			log.Printf("iniflags: error when parsing http rel path [%s] for base [%s]: %s\n", relPath, basePath, err)
			return "", false
		}
		return base.ResolveReference(rel).String(), true
	}

	if relPath == "" || relPath[0] == '/' || isHTTP(relPath) {
		return relPath, true
	}
	return path.Join(path.Dir(basePath), relPath), true
}

func isHTTP(path string) bool {
	return strings.HasPrefix(strings.ToLower(path), "http://") || strings.HasPrefix(strings.ToLower(path), "https://")
}

func getMissingFlags() map[string]bool {
	setFlags := make(map[string]bool)
	flag.Visit(func(f *flag.Flag) {
		setFlags[f.Name] = true
	})

	missingFlags := make(map[string]bool)
	flag.VisitAll(func(f *flag.Flag) {
		if _, ok := setFlags[f.Name]; !ok {
			missingFlags[f.Name] = true
		}
	})
	return missingFlags
}

func dumpFlags() {
	flag.VisitAll(func(f *flag.Flag) {
		if f.Name != "config" && f.Name != "dumpflags" {
			fmt.Printf("%s = %s  # %s\n", f.Name, quoteValue(f.Value.String()), escapeUsage(f.Usage))
		}
	})
}

func escapeUsage(s string) string {
	return strings.Replace(s, "\n", "\n    # ", -1)
}

func quoteValue(v string) string {
	if !strings.ContainsAny(v, "\n#;") && strings.TrimSpace(v) == v {
		return v
	}
	v = strings.Replace(v, "\\", "\\\\", -1)
	v = strings.Replace(v, "\n", "\\n", -1)
	v = strings.Replace(v, "\"", "\\\"", -1)
	return fmt.Sprintf("\"%s\"", v)
}

func unquoteValue(v string, lineNum int, configPath string) (string, bool) {
	v = strings.TrimSpace(v)
	if len(v) == 0 {
		return "", true
	}
	if v[0] != '"' {
		return removeTrailingComments(v), true
	}
	n := strings.LastIndex(v, "\"")
	if n == -1 {
		log.Printf("iniflags: unclosed string found [%s] at line %d in config file [%s]\n", v, lineNum, configPath)
		return "", false
	}
	v = v[1:n]
	v = strings.Replace(v, "\\\"", "\"", -1)
	v = strings.Replace(v, "\\n", "\n", -1)
	return strings.Replace(v, "\\\\", "\\", -1), true
}

func removeTrailingComments(v string) string {
	v = strings.Split(v, "#")[0]
	v = strings.Split(v, ";")[0]
	return strings.TrimSpace(v)
}

// SetConfigFile sets path to config file.
//
// Call this function before Parse() if you need default path to config file
// when -config command-line flag is not set.
func SetConfigFile(path string) {
	if parsed {
		panic("iniflags: SetConfigFile() must be called before Parse()")
	}
	*config = path
}
<|endoftext|>


<commit_before>package statistics

<commit_msg>tests for geometric mean<commit_after>package statistics

import(
	"testing"
	"strconv"
)

func TestGeometricMean(t *testing.T) {
	if gm, _ := GeometricMean([]float64{2, 8}); gm != 4 {
		t.Error("The geometric mean must be equal to 4")
	}

	if gm, _ := GeometricMean([]float64{4, 1, 1.0 / 32}); strconv.FormatFloat(gm, 'g', 1, 64) != "0.5" {
		t.Error("The geometric mean must be equal to 0.5")
	}

	if _, err := GeometricMean([]float64{}); err == nil {
		t.Error("Empty list for geometric mean should return an error")
	}
}<|endoftext|>


<commit_before>package consensus

import (
	"container/vector"
	"doozer/store"
	"github.com/bmizerany/assert"
	"goprotobuf.googlecode.com/hg/proto"
	"testing"
)

func mustMarshal(p interface{}) []byte {
	buf, err := proto.Marshal(p)
	if err != nil {
		panic(err)
	}
	return buf
}

func TestManagerRuns(t *testing.T) {
	runs := make(chan *run)
	defer close(runs)

	m := newManager("", nil, nil, runs, nil)

	r1 := &run{seqn: 1}
	r2 := &run{seqn: 2}
	r3 := &run{seqn: 3}

	runs <- r1
	runs <- r2
	runs <- r3

	assert.Equal(t, 3, (<-m).Runs)
	assert.NotEqual(t, (chan<- int64)(nil), r1.ticks)
	assert.NotEqual(t, (chan<- int64)(nil), r2.ticks)
	assert.NotEqual(t, (chan<- int64)(nil), r3.ticks)
}

func TestManagerPacketQueue(t *testing.T) {
	in := make(chan Packet)

	m := newManager("", nil, in, nil, nil)

	in <- Packet{"x", mustMarshal(&M{Seqn: proto.Int64(1)})}

	assert.Equal(t, 1, (<-m).WaitPackets)
}

func TestRecvPacket(t *testing.T) {
	q := new(vector.Vector)

	recvPacket(q, Packet{"x", mustMarshal(&M{Seqn: proto.Int64(1)})})
	recvPacket(q, Packet{"x", mustMarshal(&M{Seqn: proto.Int64(2)})})
	recvPacket(q, Packet{"x", mustMarshal(&M{Seqn: proto.Int64(3)})})

	assert.Equal(t, 3, q.Len())
}

func TestRecvEmptyPacket(t *testing.T) {
	q := new(vector.Vector)

	recvPacket(q, Packet{"x", []byte{}})
	assert.Equal(t, 0, q.Len())
}

func TestRecvInvalidPacket(t *testing.T) {
	q := new(vector.Vector)

	// The first element in a protobuf stream is always a varint.
	// The high bit of a varint byte indicates continuation;
	// Here we're supplying a continuation bit without a
	// subsequent byte. See also
	// http://code.google.com/apis/protocolbuffers/docs/encoding.html#varints.
	recvPacket(q, Packet{"x", []byte{0x80}})
	assert.Equal(t, 0, q.Len())
}

func TestSchedTick(t *testing.T) {
	q := new(vector.Vector)

	schedTick(q, 1)

	assert.Equal(t, 1, q.Len())
	assert.Equal(t, packet{M: M{Seqn: proto.Int64(1), Cmd: tick}}, q.At(0))
}

func TestManagerPacketProcessing(t *testing.T) {
	runs := make(chan *run)
	defer close(runs)

	in := make(chan Packet)
	m := newManager("", nil, in, runs, nil)

	run := run{seqn: 1, ops: make(chan store.Op, 100)}
	runs <- &run

	in <- Packet{
		Data: mustMarshal(&M{Seqn: proto.Int64(1), Cmd: learn, Value: []byte("foo")}),
		Addr: "127.0.0.1:9999",
	}

	<-m
	assert.Equal(t, true, run.l.done)
}

func TestManagerTick(t *testing.T) {
	runs := make(chan *run)
	defer close(runs)

	m := newManager("", nil, nil, runs, nil)

	// get our hands on the ticks chan
	r := &run{seqn: 1}
	runs <- r
	ticks := r.ticks

	// send it a tick for seqn 2
	ticks <- 2

	assert.Equal(t, 1, (<-m).WaitPackets)
}

func TestManagerFilterPropSeqn(t *testing.T) {
	ps := make(chan int64, 100)
	runs := make(chan *run)
	defer close(runs)

	go filterPropSeqns("b", runs, ps)

	runs <- &run{seqn: 3, cals: []string{"a", "b"}}
	runs <- &run{seqn: 4, cals: []string{"a", "b"}}
	runs <- &run{seqn: 5, cals: []string{"a", "b"}}
	assert.Equal(t, int64(3), <-ps)
	assert.Equal(t, int64(5), <-ps)

	runs <- &run{seqn: 6, cals: []string{"a", "b"}}
	runs <- &run{seqn: 7, cals: []string{"a", "b"}}
	assert.Equal(t, int64(7), <-ps)
}

func TestManagerProposalQueue(t *testing.T) {
	props := make(chan *Prop)

	m := newManager("", nil, nil, nil, props)
	props <- &Prop{Seqn: 1, Mut: []byte("foo")}

	assert.Equal(t, 1, (<-m).WaitPackets)
}
<commit_msg>consensus: fix race in unit test<commit_after>package consensus

import (
	"container/vector"
	"doozer/store"
	"github.com/bmizerany/assert"
	"goprotobuf.googlecode.com/hg/proto"
	"testing"
)

func mustMarshal(p interface{}) []byte {
	buf, err := proto.Marshal(p)
	if err != nil {
		panic(err)
	}
	return buf
}

func TestManagerRuns(t *testing.T) {
	runs := make(chan *run)
	defer close(runs)

	m := newManager("", nil, nil, runs, nil)

	r1 := &run{seqn: 1}
	r2 := &run{seqn: 2}
	r3 := &run{seqn: 3}

	runs <- r1
	runs <- r2
	runs <- r3

	assert.Equal(t, 3, (<-m).Runs)
	assert.NotEqual(t, (chan<- int64)(nil), r1.ticks)
	assert.NotEqual(t, (chan<- int64)(nil), r2.ticks)
	assert.NotEqual(t, (chan<- int64)(nil), r3.ticks)
}

func TestManagerPacketQueue(t *testing.T) {
	in := make(chan Packet)

	m := newManager("", nil, in, nil, nil)

	in <- Packet{"x", mustMarshal(&M{Seqn: proto.Int64(1)})}

	assert.Equal(t, 1, (<-m).WaitPackets)
}

func TestRecvPacket(t *testing.T) {
	q := new(vector.Vector)

	recvPacket(q, Packet{"x", mustMarshal(&M{Seqn: proto.Int64(1)})})
	recvPacket(q, Packet{"x", mustMarshal(&M{Seqn: proto.Int64(2)})})
	recvPacket(q, Packet{"x", mustMarshal(&M{Seqn: proto.Int64(3)})})

	assert.Equal(t, 3, q.Len())
}

func TestRecvEmptyPacket(t *testing.T) {
	q := new(vector.Vector)

	recvPacket(q, Packet{"x", []byte{}})
	assert.Equal(t, 0, q.Len())
}

func TestRecvInvalidPacket(t *testing.T) {
	q := new(vector.Vector)

	// The first element in a protobuf stream is always a varint.
	// The high bit of a varint byte indicates continuation;
	// Here we're supplying a continuation bit without a
	// subsequent byte. See also
	// http://code.google.com/apis/protocolbuffers/docs/encoding.html#varints.
	recvPacket(q, Packet{"x", []byte{0x80}})
	assert.Equal(t, 0, q.Len())
}

func TestSchedTick(t *testing.T) {
	q := new(vector.Vector)

	schedTick(q, 1)

	assert.Equal(t, 1, q.Len())
	assert.Equal(t, packet{M: M{Seqn: proto.Int64(1), Cmd: tick}}, q.At(0))
}

func TestManagerPacketProcessing(t *testing.T) {
	runs := make(chan *run)
	defer close(runs)

	in := make(chan Packet)
	m := newManager("", nil, in, runs, nil)

	run := run{seqn: 1, ops: make(chan store.Op, 100)}
	runs <- &run

	in <- Packet{
		Data: mustMarshal(&M{Seqn: proto.Int64(1), Cmd: learn, Value: []byte("foo")}),
		Addr: "127.0.0.1:9999",
	}

	<-m
	assert.Equal(t, true, run.l.done)
}

func TestManagerTick(t *testing.T) {
	runs := make(chan *run)
	defer close(runs)

	m := newManager("", nil, nil, runs, nil)

	// get our hands on the ticks chan
	r := &run{seqn: 1}
	runs <- r
	<-m
	ticks := r.ticks

	// send it a tick for seqn 2
	ticks <- 2

	assert.Equal(t, 1, (<-m).WaitPackets)
}

func TestManagerFilterPropSeqn(t *testing.T) {
	ps := make(chan int64, 100)
	runs := make(chan *run)
	defer close(runs)

	go filterPropSeqns("b", runs, ps)

	runs <- &run{seqn: 3, cals: []string{"a", "b"}}
	runs <- &run{seqn: 4, cals: []string{"a", "b"}}
	runs <- &run{seqn: 5, cals: []string{"a", "b"}}
	assert.Equal(t, int64(3), <-ps)
	assert.Equal(t, int64(5), <-ps)

	runs <- &run{seqn: 6, cals: []string{"a", "b"}}
	runs <- &run{seqn: 7, cals: []string{"a", "b"}}
	assert.Equal(t, int64(7), <-ps)
}

func TestManagerProposalQueue(t *testing.T) {
	props := make(chan *Prop)

	m := newManager("", nil, nil, nil, props)
	props <- &Prop{Seqn: 1, Mut: []byte("foo")}

	assert.Equal(t, 1, (<-m).WaitPackets)
}
<|endoftext|>


<commit_before>package docker

import (
	"bufio"
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"regexp"
	"strings"

	"github.com/ViBiOh/dashboard/auth"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/gorilla/websocket"
)

const ignoredByteLogSize = 8
const tailSize = `100`
const start = `start`
const stop = `stop`
const busPrefix = `/bus`

var (
	eventsDemand = regexp.MustCompile(`^events (\S+)`)
	logsDemand   = regexp.MustCompile(`^logs (\S+)(?: (.+))?`)
	statsDemand  = regexp.MustCompile(`^stats (\S+)(?: (.+))?`)
)

var (
	eventsPrefix = []byte(`events `)
	logsPrefix   = []byte(`logs `)
	statsPrefix  = []byte(`stats `)
)

var (
	hostCheck       *regexp.Regexp
	websocketOrigin = flag.String(`ws`, `^dashboard`, `Allowed WebSocket Origin pattern`)
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		// return hostCheck.MatchString(r.Host)
		return true
	},
}

// InitWebsocket configure websocket handler
func InitWebsocket() error {
	hostCheck = regexp.MustCompile(*websocketOrigin)

	return nil
}

func upgradeAndAuth(w http.ResponseWriter, r *http.Request) (*websocket.Conn, *auth.User, error) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if ws != nil {
		defer ws.Close()
	}

	if err != nil {
		return nil, nil, fmt.Errorf(`Error while upgrading connection: %v`, err)
	}

	_, basicAuth, err := ws.ReadMessage()
	if err != nil {
		defer ws.Close()
		return nil, nil, fmt.Errorf(`Error while reading authentification message: %v`, err)
	}

	user, err := auth.IsAuthenticatedByAuth(string(basicAuth), ws.RemoteAddr().String())
	if err != nil {
		ws.WriteMessage(websocket.TextMessage, []byte(err.Error()))
		defer ws.Close()
		return nil, nil, fmt.Errorf(`Error while checking authentification: %v`, err)
	}

	return ws, user, nil
}

func readContent(user *auth.User, ws *websocket.Conn, name string, done chan<- int, content chan<- []byte) {
	for {
		messageType, message, err := ws.ReadMessage()

		if messageType == websocket.CloseMessage {
			close(done)
			return
		}

		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway, websocket.CloseNoStatusReceived, websocket.CloseAbnormalClosure) {
				log.Printf(`[%s] Error while reading from %s socket: %v`, user.Username, name, err)
			}

			close(done)
			return
		}

		content <- message
	}
}

func streamEvents(ctx context.Context, cancel context.CancelFunc, user *auth.User, _ string, output chan<- []byte) {
	defer cancel()

	filtersArgs := filters.NewArgs()
	labelFilters(user, &filtersArgs, ``)
	eventFilters(&filtersArgs)

	messages, errors := docker.Events(ctx, types.EventsOptions{Filters: filtersArgs})

	for {
		select {
		case <-ctx.Done():
			return

		case message := <-messages:
			messageJSON, err := json.Marshal(message)
			if err != nil {
				log.Printf(`[%s] Events marshalling in error: %v`, user.Username, err)
				cancel()
			} else {
				output <- append(eventsPrefix, messageJSON...)
			}

		case err := <-errors:
			log.Printf(`[%s] Events reading in error: %v`, user.Username, err)
			cancel()
		}
	}
}

func streamLogs(ctx context.Context, cancel context.CancelFunc, user *auth.User, containerID string, output chan<- []byte) {
	logs, err := docker.ContainerLogs(ctx, containerID, types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: tailSize})
	defer cancel()

	if err != nil {
		log.Printf(`[%s] Logs opening in error: %v`, user.Username, err)
		return
	}
	defer logs.Close()

	scanner := bufio.NewScanner(logs)
	for scanner.Scan() {
		logLine := scanner.Bytes()
		if len(logLine) > ignoredByteLogSize {
			output <- append(logsPrefix, logLine[ignoredByteLogSize:]...)
		}
	}
}

func streamStats(ctx context.Context, cancel context.CancelFunc, user *auth.User, containerID string, output chan<- []byte) {
	stats, err := docker.ContainerStats(ctx, containerID, true)
	defer cancel()

	if err != nil {
		log.Printf(`[%s] Stats opening in error for %s: %v`, user.Username, containerID, err)
		return
	}
	defer stats.Body.Close()

	scanner := bufio.NewScanner(stats.Body)
	for scanner.Scan() {
		output <- append(statsPrefix, scanner.Bytes()...)
	}
}

func handleBusDemand(user *auth.User, name string, input []byte, demand *regexp.Regexp, cancel context.CancelFunc, output chan<- []byte, streamFn func(context.Context, context.CancelFunc, *auth.User, string, chan<- []byte)) context.CancelFunc {
	demandGroups := demand.FindSubmatch(input)
	if len(demandGroups) < 2 {
		log.Printf(`[%s] Unable to parse bus demand %s for %s`, user.Username, input, name)
	}

	action := string(demandGroups[1])

	containerID := ``
	if len(demandGroups) > 2 {
		containerID = string(demandGroups[2])
	}

	if action == stop && cancel != nil {
		cancel()
	} else if action == start {
		if cancel != nil {
			cancel()
		}

		ctx, newCancel := context.WithCancel(context.Background())
		go streamFn(ctx, newCancel, user, string(containerID), output)

		return newCancel
	}

	return nil
}

func busWebsocketHandler(w http.ResponseWriter, r *http.Request) {
	ws, user, err := upgradeAndAuth(w, r)
	if err != nil {
		log.Printf(`Error while upgrading connection to websocket: %v`, err)
		return
	}
	defer ws.Close()

	done := make(chan int)

	output := make(chan []byte)
	defer close(output)

	input := make(chan []byte)
	defer close(input)

	go readContent(user, ws, `streaming`, done, input)

	var eventsCancelFunc context.CancelFunc
	var logsCancelFunc context.CancelFunc
	var statsCancelFunc context.CancelFunc

	if err = ws.WriteMessage(websocket.TextMessage, []byte(`ready`)); err != nil {
		log.Printf(`[%s] Error while saying ready: %v`, user.Username, err)
	}

	for {
		select {
		case <-done:
			return

		case inputBytes := <-input:
			if eventsDemand.Match(inputBytes) {
				eventsCancelFunc = handleBusDemand(user, `events`, inputBytes, eventsDemand, eventsCancelFunc, output, streamEvents)
				if eventsCancelFunc != nil {
					defer eventsCancelFunc()
				}
			} else if logsDemand.Match(inputBytes) {
				logsCancelFunc = handleBusDemand(user, `logs`, inputBytes, logsDemand, logsCancelFunc, output, streamLogs)
				if logsCancelFunc != nil {
					defer logsCancelFunc()
				}
			} else if statsDemand.Match(inputBytes) {
				statsCancelFunc = handleBusDemand(user, `stats`, inputBytes, statsDemand, statsCancelFunc, output, streamStats)
				if statsCancelFunc != nil {
					defer statsCancelFunc()
				}
			}

		case outputBytes := <-output:
			if err = ws.WriteMessage(websocket.TextMessage, outputBytes); err != nil {
				log.Printf(`[%s] Error while writing to streaming: %v`, user.Username, err)
				close(done)
			}
		}
	}
}

// WebsocketHandler for Docker Websocket request. Should be use with net/http
func WebsocketHandler() http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if strings.HasPrefix(r.URL.Path, busPrefix) {
			busWebsocketHandler(w, r)
		}
	})
}
<commit_msg>Restoring domain check<commit_after>package docker

import (
	"bufio"
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"regexp"
	"strings"

	"github.com/ViBiOh/dashboard/auth"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/gorilla/websocket"
)

const ignoredByteLogSize = 8
const tailSize = `100`
const start = `start`
const stop = `stop`
const busPrefix = `/bus`

var (
	eventsDemand = regexp.MustCompile(`^events (\S+)`)
	logsDemand   = regexp.MustCompile(`^logs (\S+)(?: (.+))?`)
	statsDemand  = regexp.MustCompile(`^stats (\S+)(?: (.+))?`)
)

var (
	eventsPrefix = []byte(`events `)
	logsPrefix   = []byte(`logs `)
	statsPrefix  = []byte(`stats `)
)

var (
	hostCheck       *regexp.Regexp
	websocketOrigin = flag.String(`ws`, `^dashboard`, `Allowed WebSocket Origin pattern`)
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return hostCheck.MatchString(r.Host)
	},
}

// InitWebsocket configure websocket handler
func InitWebsocket() error {
	hostCheck = regexp.MustCompile(*websocketOrigin)

	return nil
}

func upgradeAndAuth(w http.ResponseWriter, r *http.Request) (*websocket.Conn, *auth.User, error) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if ws != nil {
		defer ws.Close()
	}

	if err != nil {
		return nil, nil, fmt.Errorf(`Error while upgrading connection: %v`, err)
	}

	_, basicAuth, err := ws.ReadMessage()
	if err != nil {
		defer ws.Close()
		return nil, nil, fmt.Errorf(`Error while reading authentification message: %v`, err)
	}

	user, err := auth.IsAuthenticatedByAuth(string(basicAuth), ws.RemoteAddr().String())
	if err != nil {
		ws.WriteMessage(websocket.TextMessage, []byte(err.Error()))
		defer ws.Close()
		return nil, nil, fmt.Errorf(`Error while checking authentification: %v`, err)
	}

	return ws, user, nil
}

func readContent(user *auth.User, ws *websocket.Conn, name string, done chan<- int, content chan<- []byte) {
	for {
		messageType, message, err := ws.ReadMessage()

		if messageType == websocket.CloseMessage {
			close(done)
			return
		}

		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway, websocket.CloseNoStatusReceived, websocket.CloseAbnormalClosure) {
				log.Printf(`[%s] Error while reading from %s socket: %v`, user.Username, name, err)
			}

			close(done)
			return
		}

		content <- message
	}
}

func streamEvents(ctx context.Context, cancel context.CancelFunc, user *auth.User, _ string, output chan<- []byte) {
	defer cancel()

	filtersArgs := filters.NewArgs()
	labelFilters(user, &filtersArgs, ``)
	eventFilters(&filtersArgs)

	messages, errors := docker.Events(ctx, types.EventsOptions{Filters: filtersArgs})

	for {
		select {
		case <-ctx.Done():
			return

		case message := <-messages:
			messageJSON, err := json.Marshal(message)
			if err != nil {
				log.Printf(`[%s] Events marshalling in error: %v`, user.Username, err)
				cancel()
			} else {
				output <- append(eventsPrefix, messageJSON...)
			}

		case err := <-errors:
			log.Printf(`[%s] Events reading in error: %v`, user.Username, err)
			cancel()
		}
	}
}

func streamLogs(ctx context.Context, cancel context.CancelFunc, user *auth.User, containerID string, output chan<- []byte) {
	logs, err := docker.ContainerLogs(ctx, containerID, types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: tailSize})
	defer cancel()

	if err != nil {
		log.Printf(`[%s] Logs opening in error: %v`, user.Username, err)
		return
	}
	defer logs.Close()

	scanner := bufio.NewScanner(logs)
	for scanner.Scan() {
		logLine := scanner.Bytes()
		if len(logLine) > ignoredByteLogSize {
			output <- append(logsPrefix, logLine[ignoredByteLogSize:]...)
		}
	}
}

func streamStats(ctx context.Context, cancel context.CancelFunc, user *auth.User, containerID string, output chan<- []byte) {
	stats, err := docker.ContainerStats(ctx, containerID, true)
	defer cancel()

	if err != nil {
		log.Printf(`[%s] Stats opening in error for %s: %v`, user.Username, containerID, err)
		return
	}
	defer stats.Body.Close()

	scanner := bufio.NewScanner(stats.Body)
	for scanner.Scan() {
		output <- append(statsPrefix, scanner.Bytes()...)
	}
}

func handleBusDemand(user *auth.User, name string, input []byte, demand *regexp.Regexp, cancel context.CancelFunc, output chan<- []byte, streamFn func(context.Context, context.CancelFunc, *auth.User, string, chan<- []byte)) context.CancelFunc {
	demandGroups := demand.FindSubmatch(input)
	if len(demandGroups) < 2 {
		log.Printf(`[%s] Unable to parse bus demand %s for %s`, user.Username, input, name)
	}

	action := string(demandGroups[1])

	containerID := ``
	if len(demandGroups) > 2 {
		containerID = string(demandGroups[2])
	}

	if action == stop && cancel != nil {
		cancel()
	} else if action == start {
		if cancel != nil {
			cancel()
		}

		ctx, newCancel := context.WithCancel(context.Background())
		go streamFn(ctx, newCancel, user, string(containerID), output)

		return newCancel
	}

	return nil
}

func busWebsocketHandler(w http.ResponseWriter, r *http.Request) {
	ws, user, err := upgradeAndAuth(w, r)
	if err != nil {
		log.Printf(`Error while upgrading connection to websocket: %v`, err)
		return
	}
	defer ws.Close()

	done := make(chan int)

	output := make(chan []byte)
	defer close(output)

	input := make(chan []byte)
	defer close(input)

	go readContent(user, ws, `streaming`, done, input)

	var eventsCancelFunc context.CancelFunc
	var logsCancelFunc context.CancelFunc
	var statsCancelFunc context.CancelFunc

	if err = ws.WriteMessage(websocket.TextMessage, []byte(`ready`)); err != nil {
		log.Printf(`[%s] Error while saying ready: %v`, user.Username, err)
	}

	for {
		select {
		case <-done:
			return

		case inputBytes := <-input:
			if eventsDemand.Match(inputBytes) {
				eventsCancelFunc = handleBusDemand(user, `events`, inputBytes, eventsDemand, eventsCancelFunc, output, streamEvents)
				if eventsCancelFunc != nil {
					defer eventsCancelFunc()
				}
			} else if logsDemand.Match(inputBytes) {
				logsCancelFunc = handleBusDemand(user, `logs`, inputBytes, logsDemand, logsCancelFunc, output, streamLogs)
				if logsCancelFunc != nil {
					defer logsCancelFunc()
				}
			} else if statsDemand.Match(inputBytes) {
				statsCancelFunc = handleBusDemand(user, `stats`, inputBytes, statsDemand, statsCancelFunc, output, streamStats)
				if statsCancelFunc != nil {
					defer statsCancelFunc()
				}
			}

		case outputBytes := <-output:
			if err = ws.WriteMessage(websocket.TextMessage, outputBytes); err != nil {
				log.Printf(`[%s] Error while writing to streaming: %v`, user.Username, err)
				close(done)
			}
		}
	}
}

// WebsocketHandler for Docker Websocket request. Should be use with net/http
func WebsocketHandler() http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if strings.HasPrefix(r.URL.Path, busPrefix) {
			busWebsocketHandler(w, r)
		}
	})
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"time"

	"github.com/moov-io/ach"
)

func main() {
	// Example transfer to write an ACH PPD file to send/credit a external institutions account
	// Important: All financial institutions are different and will require registration and exact field values.

	// Set originator bank ODFI and destination Operator for the financial institution
	// this is the funding/receiving source of the transfer
	fh := ach.NewFileHeader()
	fh.ImmediateDestination = "231380104" // Routing Number of the ACH Operator or receiving point to which the file is being sent
	fh.ImmediateOrigin = "121042882"      // Routing Number of the ACH Operator or sending point that is sending the file
	fh.FileCreationDate = time.Now()      // Today's Date
	fh.ImmediateDestinationName = "Bank"
	fh.ImmediateOriginName = "My Bank Name"

	// BatchHeader identifies the originating entity and the type of transactions contained in the batch
	bh := ach.NewIATBatchHeader()
	bh.ServiceClassCode = 220
	bh.ForeignExchangeIndicator = "FF"
	bh.ForeignExchangeReferenceIndicator = 3
	bh.ISODestinationCountryCode = "US"
	bh.OriginatorIdentification = "123456789"
	bh.StandardEntryClassCode = "IAT"
	bh.CompanyEntryDescription = "TRADEPAYMT"
	bh.ISOOriginatingCurrencyCode = "CAD"
	bh.ISODestinationCurrencyCode = "USD"
	bh.ODFIIdentification = "23138010"

	// Identifies the receivers account information
	// can be multiple entry's per batch
	entry := ach.NewIATEntryDetail()
	entry.TransactionCode = 27
	entry.SetRDFI("121042882")
	entry.AddendaRecords = 007
	entry.DFIAccountNumber = "123456789"
	entry.Amount = 100000 // 1000.00
	entry.SetTraceNumber("23138010", 1)
	entry.Category = ach.CategoryForward

	//addenda

	addenda10 := ach.NewAddenda10()
	addenda10.TransactionTypeCode = "ANN"
	addenda10.ForeignPaymentAmount = 100000
	addenda10.ForeignTraceNumber = "928383-23938"
	addenda10.Name = "BEK Enterprises"
	addenda10.EntryDetailSequenceNumber = 00000001
	entry.Addenda10 = addenda10

	addenda11 := ach.NewAddenda11()
	addenda11.OriginatorName = "BEK Solutions"
	addenda11.OriginatorStreetAddress = "15 West Place Street"
	addenda11.EntryDetailSequenceNumber = 00000001
	entry.Addenda11 = addenda11

	addenda12 := ach.NewAddenda12()
	addenda12.OriginatorCityStateProvince = "JacobsTown*PA\\"
	addenda12.OriginatorCountryPostalCode = "US*19305\\"
	addenda12.EntryDetailSequenceNumber = 00000001
	entry.Addenda12 = addenda12

	addenda13 := ach.NewAddenda13()
	addenda13.ODFIName = "Wells Fargo"
	addenda13.ODFIIDNumberQualifier = "01"
	addenda13.ODFIIdentification = "121042882"
	addenda13.ODFIBranchCountryCode = "US"
	addenda13.EntryDetailSequenceNumber = 00000001
	entry.Addenda13 = addenda13

	addenda14 := ach.NewAddenda14()
	addenda14.RDFIName = "Citadel Bank"
	addenda14.RDFIIDNumberQualifier = "01"
	addenda14.RDFIIdentification = "231380104"
	addenda14.RDFIBranchCountryCode = "CA"
	addenda14.EntryDetailSequenceNumber = 00000001
	entry.Addenda14 = addenda14

	addenda15 := ach.NewAddenda15()
	addenda15.ReceiverIDNumber = "987465493213987"
	addenda15.ReceiverStreetAddress = "2121 Front Street"
	addenda15.EntryDetailSequenceNumber = 00000001
	entry.Addenda15 = addenda15

	addenda16 := ach.NewAddenda16()
	addenda16.ReceiverCityStateProvince = "LetterTown*AB\\"
	addenda16.ReceiverCountryPostalCode = "CA*80014\\"
	addenda16.EntryDetailSequenceNumber = 00000001
	entry.Addenda16 = addenda16

	addenda17 := ach.NewAddenda17()
	addenda17.PaymentRelatedInformation = "This is an international payment"
	addenda17.SequenceNumber = 1
	addenda17.EntryDetailSequenceNumber = 0000001
	entry.AddIATAddenda(addenda17)

	addenda18 := ach.NewAddenda18()
	addenda18.ForeignCorrespondentBankName = "Bank of France"
	addenda18.ForeignCorrespondentBankIDNumberQualifier = "01"
	addenda18.ForeignCorrespondentBankIDNumber = "456456456987987"
	addenda18.ForeignCorrespondentBankBranchCountryCode = "FR"
	addenda18.SequenceNumber = 3
	addenda18.EntryDetailSequenceNumber = 0000001
	entry.AddIATAddenda(addenda18)

	// build the batch
	batch := ach.NewIATBatch(bh)
	batch.AddEntry(entry)
	if err := batch.Create(); err != nil {
		log.Fatalf("Unexpected error building batch: %s\n", err)
	}

	// build the file
	file := ach.NewFile()
	file.SetHeader(fh)
	file.AddIATBatch(batch)
	if err := file.Create(); err != nil {
		log.Fatalf("Unexpected error building file: %s\n", err)
	}

	// write the file to std out. Anything io.Writer
	w := ach.NewWriter(os.Stdout)
	if err := w.Write(file); err != nil {
		log.Fatalf("Unexpected error: %s\n", err)
	}
	w.Flush()
}
<commit_msg>#211 changed IATNewBatch to newIATBatch<commit_after>package main

import (
	"log"
	"os"
	"time"

	"github.com/moov-io/ach"
)

func main() {
	// Example transfer to write an ACH PPD file to send/credit a external institutions account
	// Important: All financial institutions are different and will require registration and exact field values.

	// Set originator bank ODFI and destination Operator for the financial institution
	// this is the funding/receiving source of the transfer
	fh := ach.NewFileHeader()
	fh.ImmediateDestination = "231380104" // Routing Number of the ACH Operator or receiving point to which the file is being sent
	fh.ImmediateOrigin = "121042882"      // Routing Number of the ACH Operator or sending point that is sending the file
	fh.FileCreationDate = time.Now()      // Today's Date
	fh.ImmediateDestinationName = "Bank"
	fh.ImmediateOriginName = "My Bank Name"

	// BatchHeader identifies the originating entity and the type of transactions contained in the batch
	bh := ach.NewIATBatchHeader()
	bh.ServiceClassCode = 220
	bh.ForeignExchangeIndicator = "FF"
	bh.ForeignExchangeReferenceIndicator = 3
	bh.ISODestinationCountryCode = "US"
	bh.OriginatorIdentification = "123456789"
	bh.StandardEntryClassCode = "IAT"
	bh.CompanyEntryDescription = "TRADEPAYMT"
	bh.ISOOriginatingCurrencyCode = "CAD"
	bh.ISODestinationCurrencyCode = "USD"
	bh.ODFIIdentification = "23138010"

	// Identifies the receivers account information
	// can be multiple entry's per batch
	entry := ach.NewIATEntryDetail()
	entry.TransactionCode = 27
	entry.SetRDFI("121042882")
	entry.AddendaRecords = 007
	entry.DFIAccountNumber = "123456789"
	entry.Amount = 100000 // 1000.00
	entry.SetTraceNumber("23138010", 1)
	entry.Category = ach.CategoryForward

	//addenda

	addenda10 := ach.NewAddenda10()
	addenda10.TransactionTypeCode = "ANN"
	addenda10.ForeignPaymentAmount = 100000
	addenda10.ForeignTraceNumber = "928383-23938"
	addenda10.Name = "BEK Enterprises"
	addenda10.EntryDetailSequenceNumber = 00000001
	entry.Addenda10 = addenda10

	addenda11 := ach.NewAddenda11()
	addenda11.OriginatorName = "BEK Solutions"
	addenda11.OriginatorStreetAddress = "15 West Place Street"
	addenda11.EntryDetailSequenceNumber = 00000001
	entry.Addenda11 = addenda11

	addenda12 := ach.NewAddenda12()
	addenda12.OriginatorCityStateProvince = "JacobsTown*PA\\"
	addenda12.OriginatorCountryPostalCode = "US*19305\\"
	addenda12.EntryDetailSequenceNumber = 00000001
	entry.Addenda12 = addenda12

	addenda13 := ach.NewAddenda13()
	addenda13.ODFIName = "Wells Fargo"
	addenda13.ODFIIDNumberQualifier = "01"
	addenda13.ODFIIdentification = "121042882"
	addenda13.ODFIBranchCountryCode = "US"
	addenda13.EntryDetailSequenceNumber = 00000001
	entry.Addenda13 = addenda13

	addenda14 := ach.NewAddenda14()
	addenda14.RDFIName = "Citadel Bank"
	addenda14.RDFIIDNumberQualifier = "01"
	addenda14.RDFIIdentification = "231380104"
	addenda14.RDFIBranchCountryCode = "CA"
	addenda14.EntryDetailSequenceNumber = 00000001
	entry.Addenda14 = addenda14

	addenda15 := ach.NewAddenda15()
	addenda15.ReceiverIDNumber = "987465493213987"
	addenda15.ReceiverStreetAddress = "2121 Front Street"
	addenda15.EntryDetailSequenceNumber = 00000001
	entry.Addenda15 = addenda15

	addenda16 := ach.NewAddenda16()
	addenda16.ReceiverCityStateProvince = "LetterTown*AB\\"
	addenda16.ReceiverCountryPostalCode = "CA*80014\\"
	addenda16.EntryDetailSequenceNumber = 00000001
	entry.Addenda16 = addenda16

	addenda17 := ach.NewAddenda17()
	addenda17.PaymentRelatedInformation = "This is an international payment"
	addenda17.SequenceNumber = 1
	addenda17.EntryDetailSequenceNumber = 0000001
	entry.AddIATAddenda(addenda17)

	addenda18 := ach.NewAddenda18()
	addenda18.ForeignCorrespondentBankName = "Bank of France"
	addenda18.ForeignCorrespondentBankIDNumberQualifier = "01"
	addenda18.ForeignCorrespondentBankIDNumber = "456456456987987"
	addenda18.ForeignCorrespondentBankBranchCountryCode = "FR"
	addenda18.SequenceNumber = 3
	addenda18.EntryDetailSequenceNumber = 0000001
	entry.AddIATAddenda(addenda18)

	// build the batch
	batch := ach.IATNewBatch(bh)
	batch.AddEntry(entry)
	if err := batch.Create(); err != nil {
		log.Fatalf("Unexpected error building batch: %s\n", err)
	}

	// build the file
	file := ach.NewFile()
	file.SetHeader(fh)
	file.AddIATBatch(batch)
	if err := file.Create(); err != nil {
		log.Fatalf("Unexpected error building file: %s\n", err)
	}

	// write the file to std out. Anything io.Writer
	w := ach.NewWriter(os.Stdout)
	if err := w.Write(file); err != nil {
		log.Fatalf("Unexpected error: %s\n", err)
	}
	w.Flush()
}
<|endoftext|>


<commit_before>package main

import (
	"time"

	"github.com/grafana/grafana/pkg/log"
	"github.com/nsqio/go-nsq"
	"github.com/raintank/raintank-metric/metric_tank/defcache"
	"github.com/raintank/raintank-metric/metric_tank/mdata"
	"github.com/raintank/raintank-metric/metric_tank/usage"
	"github.com/raintank/raintank-metric/msg"
	"github.com/raintank/raintank-metric/schema"
)

type Handler struct {
	metrics  mdata.Metrics
	defCache *defcache.DefCache
	usage    *usage.Usage
	tmp      msg.MetricData
}

func NewHandler(metrics mdata.Metrics, defCache *defcache.DefCache, usg *usage.Usage) *Handler {
	return &Handler{
		metrics:  metrics,
		defCache: defCache,
		usage:    usg,
		tmp:      msg.MetricData{Metrics: make([]*schema.MetricData, 1)},
	}
}

func (h *Handler) HandleMessage(m *nsq.Message) error {
	err := h.tmp.InitFromMsg(m.Body)
	if err != nil {
		log.Error(3, "skipping message. %s", err)
		return nil
	}
	msgsAge.Value(time.Now().Sub(h.tmp.Produced).Nanoseconds() / 1000)

	err = h.tmp.DecodeMetricData() // reads metrics from h.tmp.Msg and unsets it
	if err != nil {
		log.Error(3, "skipping message. %s", err)
		return nil
	}
	metricsPerMessage.Value(int64(len(h.tmp.Metrics)))

	metricsReceived.Inc(int64(len(h.tmp.Metrics)))

	for _, metric := range h.tmp.Metrics {
		if metric == nil {
			continue
		}
		if metric.Id == "" {
			log.Fatal(3, "empty metric.Id - fix your datastream")
		}
		if metric.Time == 0 {
			log.Warn("invalid metric. metric.Time is 0. %s", metric.Id)
		} else {
			h.defCache.Add(metric)
			m := h.metrics.GetOrCreate(metric.Id)
			m.Add(uint32(metric.Time), metric.Value)
			if h.usage != nil {
				h.usage.Add(metric.OrgId, metric.Id)
			}
		}
	}

	return nil
}
<commit_msg>log error and skip metric without id rather than panic<commit_after>package main

import (
	"time"

	"github.com/grafana/grafana/pkg/log"
	"github.com/nsqio/go-nsq"
	"github.com/raintank/raintank-metric/metric_tank/defcache"
	"github.com/raintank/raintank-metric/metric_tank/mdata"
	"github.com/raintank/raintank-metric/metric_tank/usage"
	"github.com/raintank/raintank-metric/msg"
	"github.com/raintank/raintank-metric/schema"
)

type Handler struct {
	metrics  mdata.Metrics
	defCache *defcache.DefCache
	usage    *usage.Usage
	tmp      msg.MetricData
}

func NewHandler(metrics mdata.Metrics, defCache *defcache.DefCache, usg *usage.Usage) *Handler {
	return &Handler{
		metrics:  metrics,
		defCache: defCache,
		usage:    usg,
		tmp:      msg.MetricData{Metrics: make([]*schema.MetricData, 1)},
	}
}

func (h *Handler) HandleMessage(m *nsq.Message) error {
	err := h.tmp.InitFromMsg(m.Body)
	if err != nil {
		log.Error(3, "skipping message. %s", err)
		return nil
	}
	msgsAge.Value(time.Now().Sub(h.tmp.Produced).Nanoseconds() / 1000)

	err = h.tmp.DecodeMetricData() // reads metrics from h.tmp.Msg and unsets it
	if err != nil {
		log.Error(3, "skipping message. %s", err)
		return nil
	}
	metricsPerMessage.Value(int64(len(h.tmp.Metrics)))

	metricsReceived.Inc(int64(len(h.tmp.Metrics)))

	for _, metric := range h.tmp.Metrics {
		if metric == nil {
			continue
		}
		if metric.Id == "" {
			log.Error(3, "empty metric.Id - fix your datastream")
			continue
		}
		if metric.Time == 0 {
			log.Warn("invalid metric. metric.Time is 0. %s", metric.Id)
		} else {
			h.defCache.Add(metric)
			m := h.metrics.GetOrCreate(metric.Id)
			m.Add(uint32(metric.Time), metric.Value)
			if h.usage != nil {
				h.usage.Add(metric.OrgId, metric.Id)
			}
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package configurations

import (
	"os"
	"os/user"
	"path"
)

var applicationName = "banksaurus"

// IsDev returns if in dev environment or not
func IsDev() bool {
	if os.Getenv("GO_BANK_CLI_DEV") == "true" {
		return true
	}
	return false
}

// DatabasePath returns the path nad name for the database
// taking into account the type of environment
func DatabasePath() (string, string) {
	dbName := "bank"
	if IsDev() {
		return dbName, os.TempDir()
	}

	return dbName, ApplicationHomePath()
}

// LogPath returns the path to the log file
func LogPath() string {
	return path.Join(ApplicationHomePath(), applicationName+".log")
}

// ApplicationHomePath builds the path to application data in the user home,
// something like ~/.bankservices
func ApplicationHomePath() string {
	usr, err := user.Current()
	if err != nil {
		// TODO: no panic here...
		panic(err)
	}
	return path.Join(usr.HomeDir, ".bankservices")
}
<commit_msg>Fix for wrong application home path<commit_after>package configurations

import (
	"os"
	"os/user"
	"path"
)

var applicationName = "banksaurus"

// IsDev returns if in dev environment or not
func IsDev() bool {
	if os.Getenv("GO_BANK_CLI_DEV") == "true" {
		return true
	}
	return false
}

// DatabasePath returns the path nad name for the database
// taking into account the type of environment
func DatabasePath() (string, string) {
	dbName := "bank"
	if IsDev() {
		return dbName, os.TempDir()
	}

	return dbName, ApplicationHomePath()
}

// LogPath returns the path to the log file
func LogPath() string {
	return path.Join(ApplicationHomePath(), applicationName+".log")
}

// ApplicationHomePath builds the path to application data in the user home,
// something like ~/.bankservices
func ApplicationHomePath() string {
	usr, err := user.Current()
	if err != nil {
		// TODO: no panic here...
		panic(err)
	}
	return path.Join(usr.HomeDir, ".bank")
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"net/http"

	consul "github.com/hashicorp/consul/api"
	nomad "github.com/hashicorp/nomad/api"
	"github.com/jippi/hashi-ui/backend/config"
)

// StatusHandler establishes the websocket connection and calls the connection handler.
func StatusHandler(cfg *config.Config, nomadClient *nomad.Client, consulClient *consul.Client) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		var healthy *bool

		status := struct {
			Healty *bool
			Consul map[string]interface{}
			Nomad  map[string]interface{}
		}{
			Consul: make(map[string]interface{}),
			Nomad:  make(map[string]interface{}),
		}

		healthy = newBool(true)
		if nomadClient != nil {
			leader, err := nomadClient.Status().Leader()
			status.Nomad["enabled"] = true
			status.Nomad["status"] = struct {
				Leader string
				Error  error
			}{leader, err}

			if err != nil {
				healthy = newBool(false)
			}
		} else {
			status.Nomad["enabled"] = false
		}

		if consulClient != nil {
			leader, err := consulClient.Status().Leader()
			status.Consul["enabled"] = true
			status.Consul["status"] = struct {
				Leader string
				Error  error
			}{leader, err}

			if err != nil {
				healthy = newBool(false)
			} else if healthy != nil {
				healthy = newBool(*healthy && true)
			}
		} else {
			status.Consul["enabled"] = false
		}

		status.Healty = healthy
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")

		if *healthy {
			w.WriteHeader(http.StatusOK)
		} else {
			w.WriteHeader(http.StatusInternalServerError)
		}

		json.NewEncoder(w).Encode(status)
	}
}

func newBool(b bool) *bool {
	return &b
}
<commit_msg>Set healthy as false by default<commit_after>package main

import (
	"encoding/json"
	"net/http"

	consul "github.com/hashicorp/consul/api"
	nomad "github.com/hashicorp/nomad/api"
	"github.com/jippi/hashi-ui/backend/config"
)

// StatusHandler establishes the websocket connection and calls the connection handler.
func StatusHandler(cfg *config.Config, nomadClient *nomad.Client, consulClient *consul.Client) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		var healthy *bool

		status := struct {
			Healty *bool
			Consul map[string]interface{}
			Nomad  map[string]interface{}
		}{
			Consul: make(map[string]interface{}),
			Nomad:  make(map[string]interface{}),
		}

		healthy = newBool(false)
		if nomadClient != nil {
			leader, err := nomadClient.Status().Leader()
			status.Nomad["enabled"] = true
			status.Nomad["status"] = struct {
				Leader string
				Error  error
			}{leader, err}

			if err != nil {
				healthy = newBool(false)
			} else {
				healthy = newBool(true)
			}
		} else {
			status.Nomad["enabled"] = false
			healthy = newBool(true)
		}

		if consulClient != nil {
			leader, err := consulClient.Status().Leader()
			status.Consul["enabled"] = true
			status.Consul["status"] = struct {
				Leader string
				Error  error
			}{leader, err}

			if err != nil {
				healthy = newBool(false)
			} else if healthy != nil {
				healthy = newBool(*healthy && true)
			}
		} else {
			status.Consul["enabled"] = false
		}

		status.Healty = healthy
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")

		if *healthy {
			w.WriteHeader(http.StatusOK)
		} else {
			w.WriteHeader(http.StatusInternalServerError)
		}

		json.NewEncoder(w).Encode(status)
	}
}

func newBool(b bool) *bool {
	return &b
}
<|endoftext|>


<commit_before>// +build windows

package ole

import "unsafe"

func NewVariant(vt VT, val int64) VARIANT {
	return VARIANT{VT: vt, Val: val}
}

func (v *VARIANT) ToIUnknown() *IUnknown {
	return (*IUnknown)(unsafe.Pointer(uintptr(v.Val)))
}

func (v *VARIANT) ToIDispatch() *IDispatch {
	return (*IDispatch)(unsafe.Pointer(uintptr(v.Val)))
}

func (v *VARIANT) ToArray() *SafeArrayConversion {
	var safeArray *SafeArray = (*SafeArray)(unsafe.Pointer(uintptr(v.Val)))
	return &SafeArrayConversion{safeArray}
}

func (v *VARIANT) ToString() string {
	return BstrToString(*(**uint16)(unsafe.Pointer(&v.Val)))
}

func (v *VARIANT) Clear() error {
	return VariantClear(v)
}

// Returns v's value based on its VALTYPE.
// Currently supported types: 2- and 4-byte integers, strings, bools.
// Note that 64-bit integers, datetimes, and other types are stored as strings
// and will be returned as strings.
func (v *VARIANT) Value() interface{} {
	switch v.VT {
	case VT_I2, VT_I4:
		return v.Val
	case VT_BSTR:
		return v.ToString()
	case VT_BOOL:
		return v.Val != 0
	}
	return nil
}
<commit_msg>type check<commit_after>// +build windows

package ole

import "unsafe"

func NewVariant(vt VT, val int64) VARIANT {
	return VARIANT{VT: vt, Val: val}
}

func (v *VARIANT) ToIUnknown() *IUnknown {
	if v.VT != VT_UNKNOWN {
		return nil
	}
	return (*IUnknown)(unsafe.Pointer(uintptr(v.Val)))
}

func (v *VARIANT) ToIDispatch() *IDispatch {
	if v.VT != VT_DISPATCH {
		return nil
	}
	return (*IDispatch)(unsafe.Pointer(uintptr(v.Val)))
}

func (v *VARIANT) ToArray() *SafeArrayConversion {
	if v.VT != VT_SAFEARRAY {
		return nil
	}
	var safeArray *SafeArray = (*SafeArray)(unsafe.Pointer(uintptr(v.Val)))
	return &SafeArrayConversion{safeArray}
}

func (v *VARIANT) ToString() string {
	if v.VT != VT_BSTR {
		return ""
	}
	return BstrToString(*(**uint16)(unsafe.Pointer(&v.Val)))
}

func (v *VARIANT) Clear() error {
	return VariantClear(v)
}

// Returns v's value based on its VALTYPE.
// Currently supported types: 2- and 4-byte integers, strings, bools.
// Note that 64-bit integers, datetimes, and other types are stored as strings
// and will be returned as strings.
func (v *VARIANT) Value() interface{} {
	switch v.VT {
	case VT_I2, VT_I4:
		return v.Val
	case VT_BSTR:
		return v.ToString()
	case VT_BOOL:
		return v.Val != 0
	}
	return nil
}
<|endoftext|>


<commit_before>package config

import (
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"sync/atomic"
	"time"

	"code.google.com/p/go-uuid/uuid"

	"github.com/getlantern/appdir"
	"github.com/getlantern/fronted"
	"github.com/getlantern/golog"
	"github.com/getlantern/launcher"
	"github.com/getlantern/proxiedsites"
	"github.com/getlantern/yaml"
	"github.com/getlantern/yamlconf"

	"github.com/getlantern/flashlight/client"
	"github.com/getlantern/flashlight/globals"
	"github.com/getlantern/flashlight/server"
	"github.com/getlantern/flashlight/statreporter"
)

const (
	CloudConfigPollInterval = 1 * time.Minute
	cloudflare              = "cloudflare"
	etag                    = "X-Lantern-Etag"
	ifNoneMatch             = "X-Lantern-If-None-Match"
)

var (
	log                 = golog.LoggerFor("flashlight.config")
	m                   *yamlconf.Manager
	lastCloudConfigETag = map[string]string{}
	httpClient          atomic.Value
)

type Config struct {
	Version       int
	CloudConfig   string
	CloudConfigCA string
	Addr          string
	Role          string
	InstanceId    string
	CpuProfile    string
	MemProfile    string
	UIAddr        string // UI HTTP server address
	AutoReport    *bool  // Report anonymous usage to GA
	AutoLaunch    *bool  // Automatically launch Lantern on system startup
	Stats         *statreporter.Config
	Server        *server.ServerConfig
	Client        *client.ClientConfig
	ProxiedSites  *proxiedsites.Config // List of proxied site domains that get routed through Lantern rather than accessed directly
	TrustedCAs    []*CA
}

func Configure(c *http.Client) {
	httpClient.Store(c)
	// No-op if already started.
	m.StartPolling()
}

// CA represents a certificate authority
type CA struct {
	CommonName string
	Cert       string // PEM-encoded
}

// Init initializes the configuration system.
func Init(version string) (*Config, error) {
	configPath, err := InConfigDir("lantern-" + version + ".yaml")
	if err != nil {
		log.Errorf("Could not get config path? %v", err)
		return nil, err
	}
	m = &yamlconf.Manager{
		FilePath:         configPath,
		FilePollInterval: 1 * time.Second,
		EmptyConfig: func() yamlconf.Config {
			return &Config{}
		},
		OneTimeSetup: func(ycfg yamlconf.Config) error {
			cfg := ycfg.(*Config)
			return cfg.applyFlags()
		},
		CustomPoll: func(currentCfg yamlconf.Config) (mutate func(yamlconf.Config) error, waitTime time.Duration, err error) {
			// By default, do nothing
			mutate = func(ycfg yamlconf.Config) error {
				// do nothing
				return nil
			}
			cfg := currentCfg.(*Config)
			waitTime = cfg.cloudPollSleepTime()
			if cfg.CloudConfig == "" {
				// Config doesn't have a CloudConfig, just ignore
				return
			}

			var bytes []byte
			if bytes, err = cfg.fetchCloudConfig(); err == nil {
				if bytes != nil {
					mutate = func(ycfg yamlconf.Config) error {
						log.Debugf("Merging cloud configuration")
						cfg := ycfg.(*Config)
						return cfg.updateFrom(bytes)
					}
				} else {
					log.Errorf("Nil bytes?")
				}
			} else {
				log.Errorf("Could not fetch cloud config %v", err)
			}
			return
		},
	}
	initial, err := m.Init()
	var cfg *Config
	if err == nil {
		cfg = initial.(*Config)
		err = updateGlobals(cfg)
		if err != nil {
			return nil, err
		}
	}
	return cfg, err
}

// Run runs the configuration system.
func Run(updateHandler func(updated *Config)) error {
	for {
		next := m.Next()
		nextCfg := next.(*Config)
		err := updateGlobals(nextCfg)
		if err != nil {
			return err
		}
		updateHandler(nextCfg)
	}
}

func updateGlobals(cfg *Config) error {
	globals.InstanceId = cfg.InstanceId
	err := globals.SetTrustedCAs(cfg.TrustedCACerts())
	if err != nil {
		return fmt.Errorf("Unable to configure trusted CAs: %s", err)
	}
	return nil
}

// Update updates the configuration using the given mutator function.
func Update(mutate func(cfg *Config) error) error {
	return m.Update(func(ycfg yamlconf.Config) error {
		return mutate(ycfg.(*Config))
	})
}

// InConfigDir returns the path to the given filename inside of the configdir.
func InConfigDir(filename string) (string, error) {
	cdir := *configdir

	if cdir == "" {
		cdir = appdir.General("Lantern")
	}

	log.Debugf("Placing configuration in %v", cdir)
	if _, err := os.Stat(cdir); err != nil {
		if os.IsNotExist(err) {
			// Create config dir
			if err := os.MkdirAll(cdir, 0750); err != nil {
				return "", fmt.Errorf("Unable to create configdir at %s: %s", cdir, err)
			}
		}
	}

	return filepath.Join(cdir, filename), nil
}

// TrustedCACerts returns a slice of PEM-encoded certs for the trusted CAs
func (cfg *Config) TrustedCACerts() []string {
	certs := make([]string, 0, len(cfg.TrustedCAs))
	for _, ca := range cfg.TrustedCAs {
		certs = append(certs, ca.Cert)
	}
	return certs
}

// GetVersion implements the method from interface yamlconf.Config
func (cfg *Config) GetVersion() int {
	return cfg.Version
}

// SetVersion implements the method from interface yamlconf.Config
func (cfg *Config) SetVersion(version int) {
	cfg.Version = version
}

// ApplyDefaults implements the method from interface yamlconf.Config
//
// ApplyDefaults populates default values on a Config to make sure that we have
// a minimum viable config for running.  As new settings are added to
// flashlight, this function should be updated to provide sensible defaults for
// those settings.
func (cfg *Config) ApplyDefaults() {
	if cfg.Role == "" {
		cfg.Role = "client"
	}

	if cfg.Addr == "" {
		cfg.Addr = "127.0.0.1:8787"
	}

	if cfg.UIAddr == "" {
		cfg.UIAddr = "127.0.0.1:16823"
	}

	if cfg.CloudConfig == "" {
		cfg.CloudConfig = "https://config.getiantem.org/cloud.yaml.gz"
	}

	if cfg.InstanceId == "" {
		cfg.InstanceId = uuid.New()
	}

	// Make sure we always have a stats config
	if cfg.Stats == nil {
		cfg.Stats = &statreporter.Config{}
	}

	if cfg.Stats.StatshubAddr == "" {
		cfg.Stats.StatshubAddr = *statshubAddr
	}

	if cfg.Client != nil && cfg.Role == "client" {
		cfg.applyClientDefaults()
	}

	if cfg.ProxiedSites == nil {
		log.Debugf("Adding empty proxiedsites")
		cfg.ProxiedSites = &proxiedsites.Config{
			Delta: &proxiedsites.Delta{
				Additions: []string{},
				Deletions: []string{},
			},
			Cloud: []string{},
		}
	}

	if cfg.ProxiedSites.Cloud == nil || len(cfg.ProxiedSites.Cloud) == 0 {
		log.Debugf("Loading default cloud proxiedsites")
		cfg.ProxiedSites.Cloud = defaultProxiedSites
	}

	if cfg.TrustedCAs == nil || len(cfg.TrustedCAs) == 0 {
		cfg.TrustedCAs = defaultTrustedCAs
	}
}

func (cfg *Config) applyClientDefaults() {
	// Make sure we always have at least one masquerade set
	if cfg.Client.MasqueradeSets == nil {
		cfg.Client.MasqueradeSets = make(map[string][]*fronted.Masquerade)
	}
	if len(cfg.Client.MasqueradeSets) == 0 {
		cfg.Client.MasqueradeSets[cloudflare] = cloudflareMasquerades
	}

	// Make sure we always have at least one server
	if cfg.Client.FrontedServers == nil {
		cfg.Client.FrontedServers = make([]*client.FrontedServerInfo, 0)
	}
	if len(cfg.Client.FrontedServers) == 0 && len(cfg.Client.ChainedServers) == 0 {
		cfg.Client.FrontedServers = []*client.FrontedServerInfo{
			&client.FrontedServerInfo{
				Host:           "nl.fallbacks.getiantem.org",
				Port:           443,
				PoolSize:       0,
				MasqueradeSet:  cloudflare,
				MaxMasquerades: 20,
				QOS:            10,
				Weight:         4000,
				Trusted:        true,
			},
		}

		cfg.Client.ChainedServers = make(map[string]*client.ChainedServerInfo, len(fallbacks))
		for key, fb := range fallbacks {
			cfg.Client.ChainedServers[key] = fb
		}
	}

	if cfg.AutoReport == nil {
		cfg.AutoReport = new(bool)
		*cfg.AutoReport = true
	}

	if cfg.AutoLaunch == nil {
		cfg.AutoLaunch = new(bool)
		*cfg.AutoLaunch = true
		launcher.CreateLaunchFile(*cfg.AutoLaunch)
	}

	// Make sure all servers have a QOS and Weight configured
	for _, server := range cfg.Client.FrontedServers {
		if server.QOS == 0 {
			server.QOS = 5
		}
		if server.Weight == 0 {
			server.Weight = 100
		}
		if server.RedialAttempts == 0 {
			server.RedialAttempts = 2
		}
	}

	// Always make sure we have a map of ChainedServers
	if cfg.Client.ChainedServers == nil {
		cfg.Client.ChainedServers = make(map[string]*client.ChainedServerInfo)
	}

	// Sort servers so that they're always in a predictable order
	cfg.Client.SortServers()
}

func (cfg *Config) IsDownstream() bool {
	return cfg.Role == "client"
}

func (cfg *Config) IsUpstream() bool {
	return !cfg.IsDownstream()
}

func (cfg Config) cloudPollSleepTime() time.Duration {
	return time.Duration((CloudConfigPollInterval.Nanoseconds() / 2) + rand.Int63n(CloudConfigPollInterval.Nanoseconds()))
}

func (cfg Config) fetchCloudConfig() ([]byte, error) {
	url := cfg.CloudConfig
	log.Debugf("Checking for cloud configuration at: %s", url)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("Unable to construct request for cloud config at %s: %s", url, err)
	}
	if lastCloudConfigETag[url] != "" {
		// Don't bother fetching if unchanged
		req.Header.Set(ifNoneMatch, lastCloudConfigETag[url])
	}

	// Prevents intermediate nodes (CloudFlare) from caching the content
	req.Header.Set("Cache-Control", "no-cache")

	// make sure to close the connection after reading the Body
	// this prevents the occasional EOFs errors we're seeing with
	// successive requests
	req.Close = true

	resp, err := httpClient.Load().(*http.Client).Do(req)
	if err != nil {
		return nil, fmt.Errorf("Unable to fetch cloud config at %s: %s", url, err)
	}
	defer func() {
		if err := resp.Body.Close(); err != nil {
			log.Debugf("Error closing response body: %v", err)
		}
	}()

	if resp.StatusCode == 304 {
		log.Debugf("Config unchanged in cloud")
		return nil, nil
	} else if resp.StatusCode != 200 {
		return nil, fmt.Errorf("Unexpected response status: %d", resp.StatusCode)
	}

	gzReader, err := gzip.NewReader(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("Unable to open gzip reader: %s", err)
	}
	lastCloudConfigETag[url] = resp.Header.Get(etag)
	log.Debugf("Fetched cloud config")
	return ioutil.ReadAll(gzReader)
}

// updateFrom creates a new Config by 'merging' the given yaml into this Config.
// The masquerade sets, the collections of servers, and the trusted CAs in the
// update yaml  completely replace the ones in the original Config.
func (updated *Config) updateFrom(updateBytes []byte) error {
	// XXX: does this need a mutex, along with everyone that uses the config?
	oldFrontedServers := updated.Client.FrontedServers
	oldChainedServers := updated.Client.ChainedServers
	oldMasqueradeSets := updated.Client.MasqueradeSets
	oldTrustedCAs := updated.TrustedCAs
	updated.Client.FrontedServers = []*client.FrontedServerInfo{}
	updated.Client.ChainedServers = map[string]*client.ChainedServerInfo{}
	updated.Client.MasqueradeSets = map[string][]*fronted.Masquerade{}
	updated.TrustedCAs = []*CA{}
	err := yaml.Unmarshal(updateBytes, updated)
	if err != nil {
		updated.Client.FrontedServers = oldFrontedServers
		updated.Client.ChainedServers = oldChainedServers
		updated.Client.MasqueradeSets = oldMasqueradeSets
		updated.TrustedCAs = oldTrustedCAs
		return fmt.Errorf("Unable to unmarshal YAML for update: %s", err)
	}
	// Deduplicate global proxiedsites
	if len(updated.ProxiedSites.Cloud) > 0 {
		wlDomains := make(map[string]bool)
		for _, domain := range updated.ProxiedSites.Cloud {
			wlDomains[domain] = true
		}
		updated.ProxiedSites.Cloud = make([]string, 0, len(wlDomains))
		for domain, _ := range wlDomains {
			updated.ProxiedSites.Cloud = append(updated.ProxiedSites.Cloud, domain)
		}
		sort.Strings(updated.ProxiedSites.Cloud)
	}
	return nil
}
<commit_msg>move back to old location because we do not want to continually refetch a bad config closes #3076<commit_after>package config

import (
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"sync/atomic"
	"time"

	"code.google.com/p/go-uuid/uuid"

	"github.com/getlantern/appdir"
	"github.com/getlantern/fronted"
	"github.com/getlantern/golog"
	"github.com/getlantern/launcher"
	"github.com/getlantern/proxiedsites"
	"github.com/getlantern/yaml"
	"github.com/getlantern/yamlconf"

	"github.com/getlantern/flashlight/client"
	"github.com/getlantern/flashlight/globals"
	"github.com/getlantern/flashlight/server"
	"github.com/getlantern/flashlight/statreporter"
)

const (
	CloudConfigPollInterval = 1 * time.Minute
	cloudflare              = "cloudflare"
	etag                    = "X-Lantern-Etag"
	ifNoneMatch             = "X-Lantern-If-None-Match"
)

var (
	log                 = golog.LoggerFor("flashlight.config")
	m                   *yamlconf.Manager
	lastCloudConfigETag = map[string]string{}
	httpClient          atomic.Value
)

type Config struct {
	Version       int
	CloudConfig   string
	CloudConfigCA string
	Addr          string
	Role          string
	InstanceId    string
	CpuProfile    string
	MemProfile    string
	UIAddr        string // UI HTTP server address
	AutoReport    *bool  // Report anonymous usage to GA
	AutoLaunch    *bool  // Automatically launch Lantern on system startup
	Stats         *statreporter.Config
	Server        *server.ServerConfig
	Client        *client.ClientConfig
	ProxiedSites  *proxiedsites.Config // List of proxied site domains that get routed through Lantern rather than accessed directly
	TrustedCAs    []*CA
}

func Configure(c *http.Client) {
	httpClient.Store(c)
	// No-op if already started.
	m.StartPolling()
}

// CA represents a certificate authority
type CA struct {
	CommonName string
	Cert       string // PEM-encoded
}

// Init initializes the configuration system.
func Init(version string) (*Config, error) {
	configPath, err := InConfigDir("lantern-" + version + ".yaml")
	if err != nil {
		log.Errorf("Could not get config path? %v", err)
		return nil, err
	}
	m = &yamlconf.Manager{
		FilePath:         configPath,
		FilePollInterval: 1 * time.Second,
		EmptyConfig: func() yamlconf.Config {
			return &Config{}
		},
		OneTimeSetup: func(ycfg yamlconf.Config) error {
			cfg := ycfg.(*Config)
			return cfg.applyFlags()
		},
		CustomPoll: func(currentCfg yamlconf.Config) (mutate func(yamlconf.Config) error, waitTime time.Duration, err error) {
			// By default, do nothing
			mutate = func(ycfg yamlconf.Config) error {
				// do nothing
				return nil
			}
			cfg := currentCfg.(*Config)
			waitTime = cfg.cloudPollSleepTime()
			if cfg.CloudConfig == "" {
				// Config doesn't have a CloudConfig, just ignore
				return
			}

			var bytes []byte
			if bytes, err = cfg.fetchCloudConfig(); err == nil {
				if bytes != nil {
					mutate = func(ycfg yamlconf.Config) error {
						log.Debugf("Merging cloud configuration")
						cfg := ycfg.(*Config)
						return cfg.updateFrom(bytes)
					}
				} else {
					log.Errorf("Nil bytes?")
				}
			} else {
				log.Errorf("Could not fetch cloud config %v", err)
			}
			return
		},
	}
	initial, err := m.Init()
	var cfg *Config
	if err == nil {
		cfg = initial.(*Config)
		err = updateGlobals(cfg)
		if err != nil {
			return nil, err
		}
	}
	return cfg, err
}

// Run runs the configuration system.
func Run(updateHandler func(updated *Config)) error {
	for {
		next := m.Next()
		nextCfg := next.(*Config)
		err := updateGlobals(nextCfg)
		if err != nil {
			return err
		}
		updateHandler(nextCfg)
	}
}

func updateGlobals(cfg *Config) error {
	globals.InstanceId = cfg.InstanceId
	err := globals.SetTrustedCAs(cfg.TrustedCACerts())
	if err != nil {
		return fmt.Errorf("Unable to configure trusted CAs: %s", err)
	}
	return nil
}

// Update updates the configuration using the given mutator function.
func Update(mutate func(cfg *Config) error) error {
	return m.Update(func(ycfg yamlconf.Config) error {
		return mutate(ycfg.(*Config))
	})
}

// InConfigDir returns the path to the given filename inside of the configdir.
func InConfigDir(filename string) (string, error) {
	cdir := *configdir

	if cdir == "" {
		cdir = appdir.General("Lantern")
	}

	log.Debugf("Placing configuration in %v", cdir)
	if _, err := os.Stat(cdir); err != nil {
		if os.IsNotExist(err) {
			// Create config dir
			if err := os.MkdirAll(cdir, 0750); err != nil {
				return "", fmt.Errorf("Unable to create configdir at %s: %s", cdir, err)
			}
		}
	}

	return filepath.Join(cdir, filename), nil
}

// TrustedCACerts returns a slice of PEM-encoded certs for the trusted CAs
func (cfg *Config) TrustedCACerts() []string {
	certs := make([]string, 0, len(cfg.TrustedCAs))
	for _, ca := range cfg.TrustedCAs {
		certs = append(certs, ca.Cert)
	}
	return certs
}

// GetVersion implements the method from interface yamlconf.Config
func (cfg *Config) GetVersion() int {
	return cfg.Version
}

// SetVersion implements the method from interface yamlconf.Config
func (cfg *Config) SetVersion(version int) {
	cfg.Version = version
}

// ApplyDefaults implements the method from interface yamlconf.Config
//
// ApplyDefaults populates default values on a Config to make sure that we have
// a minimum viable config for running.  As new settings are added to
// flashlight, this function should be updated to provide sensible defaults for
// those settings.
func (cfg *Config) ApplyDefaults() {
	if cfg.Role == "" {
		cfg.Role = "client"
	}

	if cfg.Addr == "" {
		cfg.Addr = "127.0.0.1:8787"
	}

	if cfg.UIAddr == "" {
		cfg.UIAddr = "127.0.0.1:16823"
	}

	if cfg.CloudConfig == "" {
		cfg.CloudConfig = "https://config.getiantem.org/cloud.yaml.gz"
	}

	if cfg.InstanceId == "" {
		cfg.InstanceId = uuid.New()
	}

	// Make sure we always have a stats config
	if cfg.Stats == nil {
		cfg.Stats = &statreporter.Config{}
	}

	if cfg.Stats.StatshubAddr == "" {
		cfg.Stats.StatshubAddr = *statshubAddr
	}

	if cfg.Client != nil && cfg.Role == "client" {
		cfg.applyClientDefaults()
	}

	if cfg.ProxiedSites == nil {
		log.Debugf("Adding empty proxiedsites")
		cfg.ProxiedSites = &proxiedsites.Config{
			Delta: &proxiedsites.Delta{
				Additions: []string{},
				Deletions: []string{},
			},
			Cloud: []string{},
		}
	}

	if cfg.ProxiedSites.Cloud == nil || len(cfg.ProxiedSites.Cloud) == 0 {
		log.Debugf("Loading default cloud proxiedsites")
		cfg.ProxiedSites.Cloud = defaultProxiedSites
	}

	if cfg.TrustedCAs == nil || len(cfg.TrustedCAs) == 0 {
		cfg.TrustedCAs = defaultTrustedCAs
	}
}

func (cfg *Config) applyClientDefaults() {
	// Make sure we always have at least one masquerade set
	if cfg.Client.MasqueradeSets == nil {
		cfg.Client.MasqueradeSets = make(map[string][]*fronted.Masquerade)
	}
	if len(cfg.Client.MasqueradeSets) == 0 {
		cfg.Client.MasqueradeSets[cloudflare] = cloudflareMasquerades
	}

	// Make sure we always have at least one server
	if cfg.Client.FrontedServers == nil {
		cfg.Client.FrontedServers = make([]*client.FrontedServerInfo, 0)
	}
	if len(cfg.Client.FrontedServers) == 0 && len(cfg.Client.ChainedServers) == 0 {
		cfg.Client.FrontedServers = []*client.FrontedServerInfo{
			&client.FrontedServerInfo{
				Host:           "nl.fallbacks.getiantem.org",
				Port:           443,
				PoolSize:       0,
				MasqueradeSet:  cloudflare,
				MaxMasquerades: 20,
				QOS:            10,
				Weight:         4000,
				Trusted:        true,
			},
		}

		cfg.Client.ChainedServers = make(map[string]*client.ChainedServerInfo, len(fallbacks))
		for key, fb := range fallbacks {
			cfg.Client.ChainedServers[key] = fb
		}
	}

	if cfg.AutoReport == nil {
		cfg.AutoReport = new(bool)
		*cfg.AutoReport = true
	}

	if cfg.AutoLaunch == nil {
		cfg.AutoLaunch = new(bool)
		*cfg.AutoLaunch = true
		launcher.CreateLaunchFile(*cfg.AutoLaunch)
	}

	// Make sure all servers have a QOS and Weight configured
	for _, server := range cfg.Client.FrontedServers {
		if server.QOS == 0 {
			server.QOS = 5
		}
		if server.Weight == 0 {
			server.Weight = 100
		}
		if server.RedialAttempts == 0 {
			server.RedialAttempts = 2
		}
	}

	// Always make sure we have a map of ChainedServers
	if cfg.Client.ChainedServers == nil {
		cfg.Client.ChainedServers = make(map[string]*client.ChainedServerInfo)
	}

	// Sort servers so that they're always in a predictable order
	cfg.Client.SortServers()
}

func (cfg *Config) IsDownstream() bool {
	return cfg.Role == "client"
}

func (cfg *Config) IsUpstream() bool {
	return !cfg.IsDownstream()
}

func (cfg Config) cloudPollSleepTime() time.Duration {
	return time.Duration((CloudConfigPollInterval.Nanoseconds() / 2) + rand.Int63n(CloudConfigPollInterval.Nanoseconds()))
}

func (cfg Config) fetchCloudConfig() ([]byte, error) {
	url := cfg.CloudConfig
	log.Debugf("Checking for cloud configuration at: %s", url)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("Unable to construct request for cloud config at %s: %s", url, err)
	}
	if lastCloudConfigETag[url] != "" {
		// Don't bother fetching if unchanged
		req.Header.Set(ifNoneMatch, lastCloudConfigETag[url])
	}

	// Prevents intermediate nodes (CloudFlare) from caching the content
	req.Header.Set("Cache-Control", "no-cache")

	// make sure to close the connection after reading the Body
	// this prevents the occasional EOFs errors we're seeing with
	// successive requests
	req.Close = true

	resp, err := httpClient.Load().(*http.Client).Do(req)
	if err != nil {
		return nil, fmt.Errorf("Unable to fetch cloud config at %s: %s", url, err)
	}
	defer func() {
		if err := resp.Body.Close(); err != nil {
			log.Debugf("Error closing response body: %v", err)
		}
	}()
	lastCloudConfigETag[url] = resp.Header.Get(etag)

	if resp.StatusCode == 304 {
		log.Debugf("Config unchanged in cloud")
		return nil, nil
	} else if resp.StatusCode != 200 {
		return nil, fmt.Errorf("Unexpected response status: %d", resp.StatusCode)
	}

	gzReader, err := gzip.NewReader(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("Unable to open gzip reader: %s", err)
	}
	log.Debugf("Fetched cloud config")
	return ioutil.ReadAll(gzReader)
}

// updateFrom creates a new Config by 'merging' the given yaml into this Config.
// The masquerade sets, the collections of servers, and the trusted CAs in the
// update yaml  completely replace the ones in the original Config.
func (updated *Config) updateFrom(updateBytes []byte) error {
	// XXX: does this need a mutex, along with everyone that uses the config?
	oldFrontedServers := updated.Client.FrontedServers
	oldChainedServers := updated.Client.ChainedServers
	oldMasqueradeSets := updated.Client.MasqueradeSets
	oldTrustedCAs := updated.TrustedCAs
	updated.Client.FrontedServers = []*client.FrontedServerInfo{}
	updated.Client.ChainedServers = map[string]*client.ChainedServerInfo{}
	updated.Client.MasqueradeSets = map[string][]*fronted.Masquerade{}
	updated.TrustedCAs = []*CA{}
	err := yaml.Unmarshal(updateBytes, updated)
	if err != nil {
		updated.Client.FrontedServers = oldFrontedServers
		updated.Client.ChainedServers = oldChainedServers
		updated.Client.MasqueradeSets = oldMasqueradeSets
		updated.TrustedCAs = oldTrustedCAs
		return fmt.Errorf("Unable to unmarshal YAML for update: %s", err)
	}
	// Deduplicate global proxiedsites
	if len(updated.ProxiedSites.Cloud) > 0 {
		wlDomains := make(map[string]bool)
		for _, domain := range updated.ProxiedSites.Cloud {
			wlDomains[domain] = true
		}
		updated.ProxiedSites.Cloud = make([]string, 0, len(wlDomains))
		for domain, _ := range wlDomains {
			updated.ProxiedSites.Cloud = append(updated.ProxiedSites.Cloud, domain)
		}
		sort.Strings(updated.ProxiedSites.Cloud)
	}
	return nil
}
<|endoftext|>