<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"strings"
	"syscall"

	"github.com/docker/docker/opts"
	"github.com/docker/docker/pkg/reexec"
	"github.com/golang/glog"
	"github.com/hyperhq/hyperd/daemon"
	"github.com/hyperhq/hyperd/server"
	"github.com/hyperhq/hyperd/serverrpc"
	"github.com/hyperhq/hyperd/types"
	"github.com/hyperhq/hyperd/utils"

	"github.com/docker/docker/pkg/parsers/kernel"
)

type Options struct {
	DisableIptables    bool
	Config             string
	Hosts              string
	Mirrors            string
	InsecureRegistries string
}

func main() {
	if reexec.Init() {
		return
	}

	if os.Geteuid() != 0 {
		glog.Errorf("The Hyper daemon needs to be run as root")
		return
	}

	// hyper needs Linux kernel 3.8.0+
	if err := checkKernel(3, 8, 0); err != nil {
		glog.Errorf(err.Error())
		return
	}

	fnd := flag.Bool("nondaemon", false, "[deprecated flag]") // TODO: remove it when 0.8 is released
	flDisableIptables := flag.Bool("noniptables", false, "Don't enable iptables rules")
	flConfig := flag.String("config", "", "Config file for hyperd")
	flHost := flag.String("host", "", "Host for hyperd")
	flMirrors := flag.String("registry_mirror", "", "Prefered docker registry mirror")
	flInsecureRegistries := flag.String("insecure_registry", "", "Enable insecure registry communication")
	flHelp := flag.Bool("help", false, "Print help message for Hyperd daemon")
	flag.Set("log_dir", "/var/log/hyper/")
	os.MkdirAll("/var/log/hyper/", 0755)
	flag.Usage = func() { printHelp() }
	flag.Parse()
	if *flHelp == true {
		printHelp()
		return
	}

	if *fnd {
		fmt.Printf("flag --nondaemon is deprecated\n")
	}

	var opt = &Options{
		DisableIptables:    *flDisableIptables,
		Config:             *flConfig,
		Hosts:              *flHost,
		Mirrors:            *flMirrors,
		InsecureRegistries: *flInsecureRegistries,
	}

	mainDaemon(opt)
}

func printHelp() {
	var helpMessage = `Usage:
  %s [OPTIONS]

Application Options:
  --config=""            Configuration for %s
  --v=0                  Log level for V logs
  --log_dir              Log directory
  --host                 Host address and port for hyperd(such as --host=tcp://127.0.0.1:12345)
  --registry_mirror      Prefered docker registry mirror, multiple values separated by a comma
  --insecure_registry    Enable insecure registry communication, multiple values separated by a comma
  --logtostderr          Log to standard error instead of files
  --alsologtostderr      Log to standard error as well as files

Help Options:
  -h, --help             Show this help message

`
	fmt.Printf(helpMessage, os.Args[0], os.Args[0])
}

func mainDaemon(opt *Options) {
	c := types.NewHyperConfig(opt.Config)
	if c == nil {
		return
	}
	c.DisableIptables = c.DisableIptables || opt.DisableIptables

	c.AdvertiseEnv()
	if _, err := os.Stat(c.Root); err != nil {
		if err := os.MkdirAll(c.Root, 0755); err != nil {
			glog.Errorf(err.Error())
			return
		}
	}

	daemon.InitDockerCfg(strings.Split(opt.Mirrors, ","), strings.Split(opt.InsecureRegistries, ","), c.StorageDriver, c.Root)
	d, err := daemon.NewDaemon(c)
	if err != nil {
		glog.Errorf("The hyperd create failed, %s", err.Error())
		return
	}

	// Set the daemon object as the global varibal
	// which will be used for puller and builder
	utils.SetDaemon(d)

	if err := d.Restore(); err != nil {
		glog.Warningf("Fail to restore the previous VM")
		return
	}

	serverConfig := &server.Config{}

	defaultHost := "unix:///var/run/hyper.sock"
	Hosts := []string{defaultHost}

	if opt.Hosts != "" {
		Hosts = append(Hosts, opt.Hosts)
	}
	if d.Host != "" {
		Hosts = append(Hosts, d.Host)
	}

	for i := 0; i < len(Hosts); i++ {
		var err error
		if Hosts[i], err = opts.ParseHost(defaultHost, Hosts[i]); err != nil {
			glog.Errorf("error parsing -H %s : %v", Hosts[i], err)
			return
		}

		protoAddr := Hosts[i]
		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
		if len(protoAddrParts) != 2 {
			glog.Errorf("bad format %s, expected PROTO://ADDR", protoAddr)
			return
		}
		serverConfig.Addrs = append(serverConfig.Addrs, server.Addr{Proto: protoAddrParts[0], Addr: protoAddrParts[1]})
	}

	api, err := server.New(serverConfig)
	if err != nil {
		glog.Errorf(err.Error())
		return
	}

	api.InitRouters(d)

	if c.GRPCHost != "" {
		rpcServer := serverrpc.NewServerRPC(d)
		defer rpcServer.Stop()

		go func() {
			err := rpcServer.Serve(c.GRPCHost)
			if err != nil {
				glog.Fatalf("Hyper serve RPC error: %v", err)
			}
		}()
	}

	// The serve API routine never exits unless an error occurs
	// We need to start it as a goroutine and wait on it so
	// daemon doesn't exit
	serveAPIWait := make(chan error)
	go api.Wait(serveAPIWait)

	stopAll := make(chan os.Signal, 1)
	signal.Notify(stopAll, syscall.SIGINT, syscall.SIGTERM)
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, syscall.SIGHUP)

	glog.V(0).Infof("Hyper daemon: %s %s", utils.VERSION, utils.GITCOMMIT)

	// Daemon is fully initialized and handling API traffic
	// Wait for serve API job to complete
	select {
	case errAPI := <-serveAPIWait:
		// If we have an error here it is unique to API (as daemonErr would have
		// exited the daemon process above)
		if errAPI != nil {
			glog.Warningf("Shutting down due to ServeAPI error: %v", errAPI)
		}
		break
	case <-stop:
		d.DestroyAndKeepVm()
		break
	case <-stopAll:
		d.DestroyAllVm()
		break
	}
	api.Close()
	d.Shutdown()
}

func checkKernel(k, major, minor int) error {
	leastVersionInfo := kernel.VersionInfo{
		Kernel: k,
		Major:  major,
		Minor:  minor,
	}

	if v, err := kernel.GetKernelVersion(); err != nil {
		return err
	} else {
		if kernel.CompareKernelVersion(*v, leastVersionInfo) < 0 {
			msg := fmt.Sprintf("Your Linux kernel(%d.%d.%d) is too old to support Hyper daemon(%d.%d.%d+)",
				v.Kernel, v.Major, v.Minor, k, major, minor)
			return fmt.Errorf(msg)
		}
		return nil
	}
}
<commit_msg>close api before release vm<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"strings"
	"syscall"

	"github.com/docker/docker/opts"
	"github.com/docker/docker/pkg/reexec"
	"github.com/golang/glog"
	"github.com/hyperhq/hyperd/daemon"
	"github.com/hyperhq/hyperd/server"
	"github.com/hyperhq/hyperd/serverrpc"
	"github.com/hyperhq/hyperd/types"
	"github.com/hyperhq/hyperd/utils"

	"github.com/docker/docker/pkg/parsers/kernel"
)

type Options struct {
	DisableIptables    bool
	Config             string
	Hosts              string
	Mirrors            string
	InsecureRegistries string
}

func main() {
	if reexec.Init() {
		return
	}

	if os.Geteuid() != 0 {
		glog.Errorf("The Hyper daemon needs to be run as root")
		return
	}

	// hyper needs Linux kernel 3.8.0+
	if err := checkKernel(3, 8, 0); err != nil {
		glog.Errorf(err.Error())
		return
	}

	fnd := flag.Bool("nondaemon", false, "[deprecated flag]") // TODO: remove it when 0.8 is released
	flDisableIptables := flag.Bool("noniptables", false, "Don't enable iptables rules")
	flConfig := flag.String("config", "", "Config file for hyperd")
	flHost := flag.String("host", "", "Host for hyperd")
	flMirrors := flag.String("registry_mirror", "", "Prefered docker registry mirror")
	flInsecureRegistries := flag.String("insecure_registry", "", "Enable insecure registry communication")
	flHelp := flag.Bool("help", false, "Print help message for Hyperd daemon")
	flag.Set("log_dir", "/var/log/hyper/")
	os.MkdirAll("/var/log/hyper/", 0755)
	flag.Usage = func() { printHelp() }
	flag.Parse()
	if *flHelp == true {
		printHelp()
		return
	}

	if *fnd {
		fmt.Printf("flag --nondaemon is deprecated\n")
	}

	var opt = &Options{
		DisableIptables:    *flDisableIptables,
		Config:             *flConfig,
		Hosts:              *flHost,
		Mirrors:            *flMirrors,
		InsecureRegistries: *flInsecureRegistries,
	}

	mainDaemon(opt)
}

func printHelp() {
	var helpMessage = `Usage:
  %s [OPTIONS]

Application Options:
  --config=""            Configuration for %s
  --v=0                  Log level for V logs
  --log_dir              Log directory
  --host                 Host address and port for hyperd(such as --host=tcp://127.0.0.1:12345)
  --registry_mirror      Prefered docker registry mirror, multiple values separated by a comma
  --insecure_registry    Enable insecure registry communication, multiple values separated by a comma
  --logtostderr          Log to standard error instead of files
  --alsologtostderr      Log to standard error as well as files

Help Options:
  -h, --help             Show this help message

`
	fmt.Printf(helpMessage, os.Args[0], os.Args[0])
}

func mainDaemon(opt *Options) {
	c := types.NewHyperConfig(opt.Config)
	if c == nil {
		return
	}
	c.DisableIptables = c.DisableIptables || opt.DisableIptables

	c.AdvertiseEnv()
	if _, err := os.Stat(c.Root); err != nil {
		if err := os.MkdirAll(c.Root, 0755); err != nil {
			glog.Errorf(err.Error())
			return
		}
	}

	daemon.InitDockerCfg(strings.Split(opt.Mirrors, ","), strings.Split(opt.InsecureRegistries, ","), c.StorageDriver, c.Root)
	d, err := daemon.NewDaemon(c)
	if err != nil {
		glog.Errorf("The hyperd create failed, %s", err.Error())
		return
	}

	// Set the daemon object as the global varibal
	// which will be used for puller and builder
	utils.SetDaemon(d)

	if err := d.Restore(); err != nil {
		glog.Warningf("Fail to restore the previous VM")
		return
	}

	serverConfig := &server.Config{}

	defaultHost := "unix:///var/run/hyper.sock"
	Hosts := []string{defaultHost}

	if opt.Hosts != "" {
		Hosts = append(Hosts, opt.Hosts)
	}
	if d.Host != "" {
		Hosts = append(Hosts, d.Host)
	}

	for i := 0; i < len(Hosts); i++ {
		var err error
		if Hosts[i], err = opts.ParseHost(defaultHost, Hosts[i]); err != nil {
			glog.Errorf("error parsing -H %s : %v", Hosts[i], err)
			return
		}

		protoAddr := Hosts[i]
		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
		if len(protoAddrParts) != 2 {
			glog.Errorf("bad format %s, expected PROTO://ADDR", protoAddr)
			return
		}
		serverConfig.Addrs = append(serverConfig.Addrs, server.Addr{Proto: protoAddrParts[0], Addr: protoAddrParts[1]})
	}

	api, err := server.New(serverConfig)
	if err != nil {
		glog.Errorf(err.Error())
		return
	}

	api.InitRouters(d)

	var rpcServer *serverrpc.ServerRPC = nil
	if c.GRPCHost != "" {
		rpcServer = serverrpc.NewServerRPC(d)

		go func() {
			err := rpcServer.Serve(c.GRPCHost)
			if err != nil {
				glog.Fatalf("Hyper serve RPC error: %v", err)
			}
		}()
	}

	// The serve API routine never exits unless an error occurs
	// We need to start it as a goroutine and wait on it so
	// daemon doesn't exit
	serveAPIWait := make(chan error)
	go api.Wait(serveAPIWait)

	stopAll := make(chan os.Signal, 1)
	signal.Notify(stopAll, syscall.SIGINT, syscall.SIGTERM)
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, syscall.SIGHUP)

	glog.V(0).Infof("Hyper daemon: %s %s", utils.VERSION, utils.GITCOMMIT)

	stopServer := func() {
		api.Close()
		if rpcServer != nil {
			rpcServer.Stop()
		}
	}

	// Daemon is fully initialized and handling API traffic
	// Wait for serve API job to complete
	select {
	case errAPI := <-serveAPIWait:
		// If we have an error here it is unique to API (as daemonErr would have
		// exited the daemon process above)
		if errAPI != nil {
			glog.Warningf("Shutting down due to ServeAPI error: %v", errAPI)
		}
		stopServer()
		break
	case <-stop:
		stopServer()
		d.DestroyAndKeepVm()
		break
	case <-stopAll:
		stopServer()
		d.DestroyAllVm()
		break
	}
	d.Shutdown()
}

func checkKernel(k, major, minor int) error {
	leastVersionInfo := kernel.VersionInfo{
		Kernel: k,
		Major:  major,
		Minor:  minor,
	}

	if v, err := kernel.GetKernelVersion(); err != nil {
		return err
	} else {
		if kernel.CompareKernelVersion(*v, leastVersionInfo) < 0 {
			msg := fmt.Sprintf("Your Linux kernel(%d.%d.%d) is too old to support Hyper daemon(%d.%d.%d+)",
				v.Kernel, v.Major, v.Minor, k, major, minor)
			return fmt.Errorf(msg)
		}
		return nil
	}
}
<|endoftext|>


<commit_before>package api_test

import (
	"bytes"
	"encoding/json"
	"github.com/tywkeene/autobd/api"
	"github.com/tywkeene/autobd/index"
	"github.com/tywkeene/autobd/node"
	"github.com/tywkeene/autobd/nodelist"
	"github.com/tywkeene/autobd/options"
	"github.com/tywkeene/autobd/utils"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"strconv"
	"testing"
	"time"
)

func getResponseFromBody(t *testing.T, recorder *httptest.ResponseRecorder) string {
	buffer, err := ioutil.ReadAll(recorder.Body)
	var response string
	if err = json.Unmarshal(buffer, &response); err != nil {
		t.Fatal(err)
	}
	return response
}

//Ensure the server serves gzip encoded content if we say we can handle it
func TestGzip(t *testing.T) {
	recorder := httptest.NewRecorder()
	handler := http.HandlerFunc(api.GzipHandler(api.ServeServerVer))

	req, err := http.NewRequest("GET", "/version", nil)
	if err != nil {
		t.Fatal(err)
	}
	req.Header.Set("Accept-Encoding", "application/x-gzip")
	handler.ServeHTTP(recorder, req)

	if status := recorder.Code; status != http.StatusOK {
		t.Errorf("handler returned wrong status code: got %v want %v",
			status, http.StatusOK)
	}

	isGzipped := recorder.HeaderMap.Get("Content-Encoding")
	if isGzipped == "" {
		t.Errorf("Server did not gzip response")
	}
}

//Ensure the /index endpoint fails if we specify a directory to index but no UUID
func TestServeIndexNoUUID(t *testing.T) {
	req, err := http.NewRequest("GET", "/index?dir=/", nil)
	if err != nil {
		t.Fatal(err)
	}

	recorder := httptest.NewRecorder()
	handler := http.HandlerFunc(api.ServeIndex)

	handler.ServeHTTP(recorder, req)

	if recorder.Code != http.StatusUnauthorized {
		t.Fatalf("handler returned wrong status code: got %v want %v",
			recorder.Code, http.StatusUnauthorized)
	}
	expected := &utils.APIError{
		ErrorMessage: "Invalid node UUID",
		HTTPStatus:   http.StatusUnauthorized,
	}
	var response *utils.APIError
	buffer, err := ioutil.ReadAll(recorder.Body)
	if err = json.Unmarshal(buffer, &response); err != nil {
		t.Fatal(err)
	}
	if response.ErrorMessage != expected.ErrorMessage ||
		response.HTTPStatus != expected.HTTPStatus {
		t.Errorf("handler returned unexpected body: got %v want %v",
			recorder.Body.String(), expected)
	}
}

//Ensure the /index endpoint fails if we don't specify a UUID but no directory to index
func TestServeIndexNoDir(t *testing.T) {
	recorder := httptest.NewRecorder()
	handler := http.HandlerFunc(api.ServeIndex)

	options.Config.HeartBeatTrackInterval = "1s"
	options.Config.HeartBeatOffline = "3s"

	nodelist.AddNode("test", &nodelist.Node{
		Address:    "0.0.0.0",
		LastOnline: time.Now().Format(time.RFC850),
		IsOnline:   true,
		Synced:     false,
		Meta: &nodelist.NodeMetadata{
			UUID:    "test",
			Version: "0.0.0",
		},
	})

	req, err := http.NewRequest("GET", "/index?uuid=test", nil)
	if err != nil {
		t.Fatal(err)
	}
	handler.ServeHTTP(recorder, req)

	if recorder.Code != http.StatusBadRequest {
		t.Fatalf("handler returned wrong status code: got %v want %v",
			recorder.Code, http.StatusBadRequest)
	}
	expected := &utils.APIError{
		ErrorMessage: "Must specify directory",
		HTTPStatus:   http.StatusBadRequest,
	}
	var response *utils.APIError
	buffer, err := ioutil.ReadAll(recorder.Body)
	if err = json.Unmarshal(buffer, &response); err != nil {
		t.Fatal(err)
	}

	if response == nil {
		t.Errorf("empty response from server")
	}
	if response.ErrorMessage != expected.ErrorMessage ||
		response.HTTPStatus != expected.HTTPStatus {
		t.Errorf("handler returned unexpected body: got %v want %v",
			response, expected)
	}
}

//Ensure the /index endpoint succeeds specify a UUID and directory to index
func TestServeIndex(t *testing.T) {
	recorder := httptest.NewRecorder()
	handler := http.HandlerFunc(api.ServeIndex)

	options.Config.HeartBeatTrackInterval = "1s"
	options.Config.HeartBeatOffline = "3s"

	nodelist.AddNode("test", &nodelist.Node{
		Address:    "0.0.0.0",
		LastOnline: time.Now().Format(time.RFC850),
		IsOnline:   true,
		Synced:     false,
		Meta: &nodelist.NodeMetadata{
			UUID:    "test",
			Version: "0.0.0",
		},
	})

	req, err := http.NewRequest("GET", "/index?dir=/&uuid=test", nil)
	if err != nil {
		t.Fatal(err)
	}
	handler.ServeHTTP(recorder, req)

	if status := recorder.Code; status != http.StatusOK {
		t.Errorf("handler returned wrong status code: got %v want %v",
			status, http.StatusOK)
	}

	respJSON, err := ioutil.ReadAll(recorder.Body)
	if err != nil {
		t.Fatal(err)
	}
	var respIndex *index.Index
	if err := json.Unmarshal(respJSON, &respIndex); err != nil {
		t.Fatal(err)
	}

	expectedIndex, err := index.GetIndex("./")
	if err != nil {
		t.Fatal(err)
	}

	if need := node.CompareDirs(expectedIndex, respIndex.Files); len(need) > 0 {
		t.Fatal("Index mismatch")
	}
}

func BenchmarkServeIndex(b *testing.B) {
	recorder := httptest.NewRecorder()
	handler := http.HandlerFunc(api.ServeIndex)

	options.Config.HeartBeatTrackInterval = "1s"
	options.Config.HeartBeatOffline = "3s"

	nodelist.AddNode("test", &nodelist.Node{
		Address:    "0.0.0.0",
		LastOnline: time.Now().Format(time.RFC850),
		IsOnline:   true,
		Synced:     false,
		Meta: &nodelist.NodeMetadata{
			UUID:    "test",
			Version: "0.0.0",
		},
	})

	req, err := http.NewRequest("GET", "/index?dir=/&uuid=test", nil)
	if err != nil {
		b.Fatal(err)
	}
	for n := 0; n < 20000; n++ {
		handler.ServeHTTP(recorder, req)
	}
}

//Ensure we can get a version from the server
func TestServeServerVer(t *testing.T) {
	recorder := httptest.NewRecorder()
	handler := http.HandlerFunc(api.ServeServerVer)

	req, err := http.NewRequest("GET", "/version", nil)
	if err != nil {
		t.Fatal(err)
	}
	handler.ServeHTTP(recorder, req)

	if status := recorder.Code; status != http.StatusOK {
		t.Errorf("handler returned wrong status code: got %v want %v",
			status, http.StatusOK)
	}
}

//Ensure we get content when trying to sync a file
func TestServeSync(t *testing.T) {
	recorder := httptest.NewRecorder()
	handler := http.HandlerFunc(api.ServeSync)

	nodelist.AddNode("test", &nodelist.Node{
		Address:    "0.0.0.0",
		LastOnline: time.Now().Format(time.RFC850),
		IsOnline:   true,
		Synced:     false,
		Meta: &nodelist.NodeMetadata{
			UUID:    "test",
			Version: "0.0.0",
		},
	})

	req, err := http.NewRequest("GET", "/sync?grab=api.go&uuid=test", nil)
	if err != nil {
		t.Fatal(err)
	}
	handler.ServeHTTP(recorder, req)

	response, err := ioutil.ReadAll(recorder.Body)
	if err != nil {
		t.Error(err)
	}
	if string(response) == "" {
		t.Errorf("Server failed to sync file")
	}
}

//Ensure we get a consistent list of nodes
func TestListNodes(t *testing.T) {
	recorder := httptest.NewRecorder()
	handler := http.HandlerFunc(api.ListNodes)
	nodelist.AddNode("test0", &nodelist.Node{
		Address:    "0.0.0.0",
		LastOnline: time.Now().Format(time.RFC850),
		IsOnline:   true,
		Synced:     false,
		Meta: &nodelist.NodeMetadata{
			UUID:    "test0",
			Version: "0.0.0",
		},
	})
	nodelist.AddNode("test1", &nodelist.Node{
		Address:    "0.0.0.1",
		LastOnline: time.Now().Format(time.RFC850),
		IsOnline:   true,
		Synced:     false,
		Meta: &nodelist.NodeMetadata{
			UUID:    "test1",
			Version: "0.0.0",
		},
	})
	nodelist.AddNode("test2", &nodelist.Node{
		Address:    "0.0.0.2",
		LastOnline: time.Now().Format(time.RFC850),
		IsOnline:   true,
		Synced:     false,
		Meta: &nodelist.NodeMetadata{
			UUID:    "test2",
			Version: "0.0.0",
		},
	})
	nodelist.AddNode("test3", &nodelist.Node{
		Address:    "0.0.0.3",
		LastOnline: time.Now().Format(time.RFC850),
		IsOnline:   true,
		Synced:     false,
		Meta: &nodelist.NodeMetadata{
			UUID:    "test3",
			Version: "0.0.0",
		},
	})

	req, err := http.NewRequest("GET", "/nodes?uuid=test0", nil)
	if err != nil {
		t.Fatal(err)
	}
	handler.ServeHTTP(recorder, req)

	if recorder.Code != http.StatusOK {
		t.Errorf("handler returned wrong status code: got %v want %v",
			recorder.Code, http.StatusOK)
	}

	buffer, err := ioutil.ReadAll(recorder.Body)
	var response map[string]*nodelist.Node
	if err = json.Unmarshal(buffer, &response); err != nil {
		t.Error(err)
	}

	if response == nil {
		t.Error("Failed to get node list from server")
	}
	for key, _ := range response {
		if _, ok := nodelist.CurrentNodes[key]; ok == false {
			t.Error("Node not in node list")
		}
	}
}

//Ensure we can identify as a node with the server
func TestIdentify(t *testing.T) {
	recorder := httptest.NewRecorder()
	handler := http.HandlerFunc(api.Identify)

	nodelist.CurrentNodes = nil

	serial, err := json.Marshal(&nodelist.NodeMetadata{
		Version: "0.0.0",
		UUID:    "test",
	})
	if err != nil {
		t.Fatal(err)
	}

	req, err := http.NewRequest("POST", "/identify", bytes.NewBuffer(serial))
	if err != nil {
		t.Fatal(err)
	}
	req.Header.Set("Content-Type", "application/json")
	handler.ServeHTTP(recorder, req)

	if status := recorder.Code; status != http.StatusOK {
		t.Fatal("handler returned wrong status code: got %v want %v", status, http.StatusOK)
	}

	if nodelist.GetNodeByUUID("test") == nil {
		t.Fatal("Node was not properly registered")
	}
}

//Ensure the server properly handles heartbeats from a node
func TestHeartBeat(t *testing.T) {
	recorder := httptest.NewRecorder()
	handler := http.HandlerFunc(api.HeartBeat)

	nodelist.AddNode("test", &nodelist.Node{
		Address:    "0.0.0.0",
		LastOnline: time.Now().Format(time.RFC850),
		IsOnline:   true,
		Synced:     false,
		Meta: &nodelist.NodeMetadata{
			UUID:    "test",
			Version: "0.0.0",
		},
	})
	heartbeat := &nodelist.NodeHeartbeat{
		UUID:   "test",
		Synced: strconv.FormatBool(true),
	}

	serial, err := json.Marshal(&heartbeat)
	if err != nil {
		t.Fatal(err)
	}

	req, err := http.NewRequest("POST", "/heartbeat", bytes.NewBuffer(serial))
	if err != nil {
		t.Fatal(err)
	}
	handler.ServeHTTP(recorder, req)

	if status := recorder.Code; status != http.StatusOK {
		t.Errorf("handler returned wrong status code: got %v want %v",
			status, http.StatusOK)
	}
	if nodelist.GetNodeByUUID("test").Synced == false {
		t.Errorf("Node was not updated")
	}
}
<commit_msg>Fixed api/api_test.go<commit_after>package api_test

import (
	"bytes"
	"encoding/json"
	"github.com/tywkeene/autobd/api"
	"github.com/tywkeene/autobd/index"
	"github.com/tywkeene/autobd/node"
	"github.com/tywkeene/autobd/nodelist"
	"github.com/tywkeene/autobd/options"
	"github.com/tywkeene/autobd/utils"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"strconv"
	"testing"
	"time"
)

func getResponseFromBody(t *testing.T, recorder *httptest.ResponseRecorder) string {
	buffer, err := ioutil.ReadAll(recorder.Body)
	var response string
	if err = json.Unmarshal(buffer, &response); err != nil {
		t.Fatal(err)
	}
	return response
}

//Ensure the server serves gzip encoded content if we say we can handle it
func TestGzip(t *testing.T) {
	recorder := httptest.NewRecorder()
	handler := http.HandlerFunc(api.GzipHandler(api.ServeServerVer))

	req, err := http.NewRequest("GET", "/version", nil)
	if err != nil {
		t.Fatal(err)
	}
	req.Header.Set("Accept-Encoding", "application/x-gzip")
	handler.ServeHTTP(recorder, req)

	if status := recorder.Code; status != http.StatusOK {
		t.Errorf("handler returned wrong status code: got %v want %v",
			status, http.StatusOK)
	}

	isGzipped := recorder.HeaderMap.Get("Content-Encoding")
	if isGzipped == "" {
		t.Errorf("Server did not gzip response")
	}
}

//Ensure the /index endpoint fails if we specify a directory to index but no UUID
func TestServeIndexNoUUID(t *testing.T) {
	req, err := http.NewRequest("GET", "/index?dir=/", nil)
	if err != nil {
		t.Fatal(err)
	}

	recorder := httptest.NewRecorder()
	handler := http.HandlerFunc(api.ServeIndex)

	handler.ServeHTTP(recorder, req)

	if recorder.Code != http.StatusUnauthorized {
		t.Fatalf("handler returned wrong status code: got %v want %v",
			recorder.Code, http.StatusUnauthorized)
	}
	expected := &utils.APIError{
		ErrorMessage: "Invalid node UUID",
		HTTPStatus:   http.StatusUnauthorized,
	}
	var response *utils.APIError
	buffer, err := ioutil.ReadAll(recorder.Body)
	if err = json.Unmarshal(buffer, &response); err != nil {
		t.Fatal(err)
	}
	if response.ErrorMessage != expected.ErrorMessage ||
		response.HTTPStatus != expected.HTTPStatus {
		t.Errorf("handler returned unexpected body: got %v want %v",
			recorder.Body.String(), expected)
	}
}

//Ensure the /index endpoint fails if we don't specify a UUID but no directory to index
func TestServeIndexNoDir(t *testing.T) {
	recorder := httptest.NewRecorder()
	handler := http.HandlerFunc(api.ServeIndex)

	options.Config.HeartBeatTrackInterval = "1s"
	options.Config.HeartBeatOffline = "3s"

	nodelist.AddNode("test", &nodelist.Node{
		Address:    "0.0.0.0",
		LastOnline: time.Now().Format(time.RFC850),
		IsOnline:   true,
		Synced:     false,
		Meta: &nodelist.NodeMetadata{
			UUID:    "test",
			Version: "0.0.0",
		},
	})

	req, err := http.NewRequest("GET", "/index?uuid=test", nil)
	if err != nil {
		t.Fatal(err)
	}
	handler.ServeHTTP(recorder, req)

	if recorder.Code != http.StatusBadRequest {
		t.Fatalf("handler returned wrong status code: got %v want %v",
			recorder.Code, http.StatusBadRequest)
	}
	expected := &utils.APIError{
		ErrorMessage: "Must specify directory",
		HTTPStatus:   http.StatusBadRequest,
	}
	var response *utils.APIError
	buffer, err := ioutil.ReadAll(recorder.Body)
	if err = json.Unmarshal(buffer, &response); err != nil {
		t.Fatal(err)
	}

	if response == nil {
		t.Errorf("empty response from server")
	}
	if response.ErrorMessage != expected.ErrorMessage ||
		response.HTTPStatus != expected.HTTPStatus {
		t.Errorf("handler returned unexpected body: got %v want %v",
			response, expected)
	}
}

//Ensure the /index endpoint succeeds specify a UUID and directory to index
func TestServeIndex(t *testing.T) {
	recorder := httptest.NewRecorder()
	handler := http.HandlerFunc(api.ServeIndex)

	options.Config.HeartBeatTrackInterval = "1s"
	options.Config.HeartBeatOffline = "3s"

	nodelist.AddNode("test", &nodelist.Node{
		Address:    "0.0.0.0",
		LastOnline: time.Now().Format(time.RFC850),
		IsOnline:   true,
		Synced:     false,
		Meta: &nodelist.NodeMetadata{
			UUID:    "test",
			Version: "0.0.0",
		},
	})

	req, err := http.NewRequest("GET", "/index?dir=/&uuid=test", nil)
	if err != nil {
		t.Fatal(err)
	}
	handler.ServeHTTP(recorder, req)

	if status := recorder.Code; status != http.StatusOK {
		t.Errorf("handler returned wrong status code: got %v want %v",
			status, http.StatusOK)
	}

	respJSON, err := ioutil.ReadAll(recorder.Body)
	if err != nil {
		t.Fatal(err)
	}
	var respIndex *index.Index
	if err := json.Unmarshal(respJSON, &respIndex); err != nil {
		t.Fatal(err)
	}

	expectedIndex, err := index.GetIndex("./")
	if err != nil {
		t.Fatal(err)
	}

	if need := node.CompareDirs(expectedIndex, respIndex.Files); len(need) > 0 {
		t.Fatal("Index mismatch")
	}
}

func BenchmarkServeIndex(b *testing.B) {
	recorder := httptest.NewRecorder()
	handler := http.HandlerFunc(api.ServeIndex)

	options.Config.HeartBeatTrackInterval = "1s"
	options.Config.HeartBeatOffline = "3s"

	nodelist.AddNode("test", &nodelist.Node{
		Address:    "0.0.0.0",
		LastOnline: time.Now().Format(time.RFC850),
		IsOnline:   true,
		Synced:     false,
		Meta: &nodelist.NodeMetadata{
			UUID:    "test",
			Version: "0.0.0",
		},
	})

	req, err := http.NewRequest("GET", "/index?dir=/&uuid=test", nil)
	if err != nil {
		b.Fatal(err)
	}
	for n := 0; n < 20000; n++ {
		handler.ServeHTTP(recorder, req)
	}
}

//Ensure we can get a version from the server
func TestServeServerVer(t *testing.T) {
	recorder := httptest.NewRecorder()
	handler := http.HandlerFunc(api.ServeServerVer)

	req, err := http.NewRequest("GET", "/version", nil)
	if err != nil {
		t.Fatal(err)
	}
	handler.ServeHTTP(recorder, req)

	if status := recorder.Code; status != http.StatusOK {
		t.Errorf("handler returned wrong status code: got %v want %v",
			status, http.StatusOK)
	}
}

//Ensure we get content when trying to sync a file
func TestServeSync(t *testing.T) {
	recorder := httptest.NewRecorder()
	handler := http.HandlerFunc(api.ServeSync)

	nodelist.AddNode("test", &nodelist.Node{
		Address:    "0.0.0.0",
		LastOnline: time.Now().Format(time.RFC850),
		IsOnline:   true,
		Synced:     false,
		Meta: &nodelist.NodeMetadata{
			UUID:    "test",
			Version: "0.0.0",
		},
	})

	req, err := http.NewRequest("GET", "/sync?grab=api.go&uuid=test", nil)
	if err != nil {
		t.Fatal(err)
	}
	handler.ServeHTTP(recorder, req)

	response, err := ioutil.ReadAll(recorder.Body)
	if err != nil {
		t.Error(err)
	}
	if string(response) == "" {
		t.Errorf("Server failed to sync file")
	}
}

//Ensure we get a consistent list of nodes
func TestListNodes(t *testing.T) {
	recorder := httptest.NewRecorder()
	handler := http.HandlerFunc(api.ListNodes)
	nodelist.AddNode("test0", &nodelist.Node{
		Address:    "0.0.0.0",
		LastOnline: time.Now().Format(time.RFC850),
		IsOnline:   true,
		Synced:     false,
		Meta: &nodelist.NodeMetadata{
			UUID:    "test0",
			Version: "0.0.0",
		},
	})
	nodelist.AddNode("test1", &nodelist.Node{
		Address:    "0.0.0.1",
		LastOnline: time.Now().Format(time.RFC850),
		IsOnline:   true,
		Synced:     false,
		Meta: &nodelist.NodeMetadata{
			UUID:    "test1",
			Version: "0.0.0",
		},
	})
	nodelist.AddNode("test2", &nodelist.Node{
		Address:    "0.0.0.2",
		LastOnline: time.Now().Format(time.RFC850),
		IsOnline:   true,
		Synced:     false,
		Meta: &nodelist.NodeMetadata{
			UUID:    "test2",
			Version: "0.0.0",
		},
	})
	nodelist.AddNode("test3", &nodelist.Node{
		Address:    "0.0.0.3",
		LastOnline: time.Now().Format(time.RFC850),
		IsOnline:   true,
		Synced:     false,
		Meta: &nodelist.NodeMetadata{
			UUID:    "test3",
			Version: "0.0.0",
		},
	})

	req, err := http.NewRequest("GET", "/nodes?uuid=test0", nil)
	if err != nil {
		t.Fatal(err)
	}
	handler.ServeHTTP(recorder, req)

	if recorder.Code != http.StatusOK {
		t.Errorf("handler returned wrong status code: got %v want %v",
			recorder.Code, http.StatusOK)
	}

	buffer, err := ioutil.ReadAll(recorder.Body)
	var response map[string]*nodelist.Node
	if err = json.Unmarshal(buffer, &response); err != nil {
		t.Error(err)
	}

	if response == nil {
		t.Error("Failed to get node list from server")
	}
	for key, _ := range response {
		if _, ok := nodelist.CurrentNodes[key]; ok == false {
			t.Error("Node not in node list")
		}
	}
}

//Ensure we can identify as a node with the server
func TestIdentify(t *testing.T) {
	recorder := httptest.NewRecorder()
	handler := http.HandlerFunc(api.Identify)

	nodelist.CurrentNodes = nil

	serial, err := json.Marshal(&nodelist.NodeMetadata{
		Version: "0.0.0",
		UUID:    "test",
		Target:  "/",
	})
	if err != nil {
		t.Fatal(err)
	}

	req, err := http.NewRequest("POST", "/identify", bytes.NewBuffer(serial))
	if err != nil {
		t.Fatal(err)
	}
	req.Header.Set("Content-Type", "application/json")
	handler.ServeHTTP(recorder, req)

	if status := recorder.Code; status != http.StatusOK {
		t.Fatalf("handler returned wrong status code: got %v want %v", status, http.StatusOK)
	}

	if nodelist.GetNodeByUUID("test") == nil {
		t.Fatal("Node was not properly registered")
	}
}

//Ensure the server properly handles heartbeats from a node
func TestHeartBeat(t *testing.T) {
	recorder := httptest.NewRecorder()
	handler := http.HandlerFunc(api.HeartBeat)

	nodelist.AddNode("test", &nodelist.Node{
		Address:    "0.0.0.0",
		LastOnline: time.Now().Format(time.RFC850),
		IsOnline:   true,
		Synced:     false,
		Meta: &nodelist.NodeMetadata{
			UUID:    "test",
			Version: "0.0.0",
		},
	})
	heartbeat := &nodelist.NodeHeartbeat{
		UUID:   "test",
		Synced: strconv.FormatBool(true),
	}

	serial, err := json.Marshal(&heartbeat)
	if err != nil {
		t.Fatal(err)
	}

	req, err := http.NewRequest("POST", "/heartbeat", bytes.NewBuffer(serial))
	if err != nil {
		t.Fatal(err)
	}
	handler.ServeHTTP(recorder, req)

	if status := recorder.Code; status != http.StatusOK {
		t.Errorf("handler returned wrong status code: got %v want %v",
			status, http.StatusOK)
	}
	if nodelist.GetNodeByUUID("test").Synced == false {
		t.Errorf("Node was not updated")
	}
}
<|endoftext|>


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

package main

import (
	"flag"
	"fmt"
	"io/ioutil"

	"github.com/dcos/dcos-go/dcos"
	"github.com/dcos/dcos-go/dcos/nodeutil"
	mesosAgent "github.com/dcos/dcos-metrics/collectors/mesos/agent"
	"github.com/dcos/dcos-metrics/collectors/node"
	httpProducer "github.com/dcos/dcos-metrics/producers/http"
	httpHelpers "github.com/dcos/dcos-metrics/util/http/helpers"

	log "github.com/Sirupsen/logrus"
	yaml "gopkg.in/yaml.v2"
)

var (
	// VERSION set by $(git describe --always)
	// Set by scripts/build.sh, executed by `make build`
	VERSION = "unset"
	// REVISION set by $(git rev-parse --shore HEAD)
	// Set by scripts/build.sh, executed by `make build`
	REVISION = "unset"
)

// Config defines the top-level configuration options for the dcos-metrics-collector project.
// It is (currently) broken up into two main sections: collectors and producers.
type Config struct {
	// Config from the service config file
	Collector         CollectorConfig `yaml:"collector"`
	Producers         ProducersConfig `yaml:"producers"`
	IAMConfigPath     string          `yaml:"iam_config_path"`
	CACertificatePath string          `yaml:"ca_certificate_path"`

	// Generated by dcos.NodeInfo{}
	MesosID   string
	IPAddress string
	ClusterID string

	// Flag configuration
	DCOSRole    string
	ConfigPath  string
	LogLevel    string
	VersionFlag bool
}

// CollectorConfig contains configuration options relevant to the "collector"
// portion of this project. That is, the code responsible for querying Mesos,
// et. al to gather metrics and send them to a "producer".
type CollectorConfig struct {
	HTTPProfiler bool                  `yaml:"http_profiler"`
	Node         *node.Collector       `yaml:"node,omitempty"`
	MesosAgent   *mesosAgent.Collector `yaml:"mesos_agent,omitempty"`
}

// ProducersConfig contains references to other structs that provide individual producer configs.
// The configuration for all producers is then located in their corresponding packages.
//
// For example: Config.Producers.KafkaProducerConfig references kafkaProducer.Config. This struct
// contains an optional Kafka configuration. This configuration is available in the source file
// 'producers/kafka/kafka.go'. It is then the responsibility of the individual producers to
// validate the configuration the user has provided and panic if necessary.
type ProducersConfig struct {
	HTTPProducerConfig httpProducer.Config `yaml:"http,omitempty"`
	//KafkaProducerConfig  kafkaProducer.Config  `yaml:"kafka,omitempty"`
	//StatsdProducerConfig statsdProducer.Config `yaml:"statsd,omitempty"`
}

func (c *Config) setFlags(fs *flag.FlagSet) {
	fs.StringVar(&c.ConfigPath, "config", c.ConfigPath, "The path to the config file.")
	fs.StringVar(&c.LogLevel, "loglevel", c.LogLevel, "Logging level (default: info). Must be one of: debug, info, warn, error, fatal, panic.")
	fs.StringVar(&c.DCOSRole, "role", c.DCOSRole, "The DC/OS role this instance runs on.")
	fs.BoolVar(&c.VersionFlag, "version", c.VersionFlag, "Print version and revsion then exit")
}

func (c *Config) loadConfig() error {
	fileByte, err := ioutil.ReadFile(c.ConfigPath)
	if err != nil {
		return err
	}

	if err = yaml.Unmarshal(fileByte, &c); err != nil {
		return err
	}

	return nil
}

func (c *Config) getNodeInfo() error {
	log.Debug("Getting node info")
	client, err := httpHelpers.NewMetricsClient(c.CACertificatePath, c.IAMConfigPath)
	if err != nil {
		return err
	}

	// Get NodeInfo
	var stateURL = "http://leader.mesos:5050/state"
	if len(c.IAMConfigPath) > 0 {
		stateURL = "https://leader.mesos:5050/state"
	}
	nodeInfo, err := nodeutil.NewNodeInfo(client, nodeutil.OptionMesosStateURL(stateURL))
	if err != nil {
		log.Errorf("Error getting NodeInfo{}: err")
	}

	ip, err := nodeInfo.DetectIP()
	if err != nil {
		log.Error(err)
	}
	c.Collector.MesosAgent.NodeInfo.IPAddress = ip.String()
	c.Collector.Node.NodeInfo.IPAddress = ip.String()

	mid, err := nodeInfo.MesosID(nil)
	if err != nil {
		log.Error(err)
	}
	c.Collector.MesosAgent.NodeInfo.MesosID = mid
	c.Collector.Node.NodeInfo.MesosID = mid

	if c.DCOSRole == dcos.RoleMaster {
		c.Collector.Node.NodeInfo.ClusterID, err = nodeInfo.ClusterID()
		if err != nil {
			return err
		}
	}

	return nil
}

// newConfig establishes our default, base configuration.
func newConfig() Config {
	return Config{
		Collector: CollectorConfig{
			HTTPProfiler: true,
			MesosAgent: &mesosAgent.Collector{
				PollPeriod: 15,
				Port:       5051,
			},
			Node: &node.Collector{
				PollPeriod: 15,
			},
		},
		Producers: ProducersConfig{
			HTTPProducerConfig: httpProducer.Config{
				Port: 8000,
			},
		},
		ConfigPath: "dcos-metrics-config.yaml",
		LogLevel:   "info",
	}
}

// getNewConfig loads the configuration and sets precedence of configuration values.
// For example: command line flags override values provided in the config file.
func getNewConfig(args []string) (Config, error) {
	c := newConfig()
	thisFlagSet := flag.NewFlagSet("", flag.ExitOnError)
	c.setFlags(thisFlagSet)
	// Override default config with CLI flags if any
	if err := thisFlagSet.Parse(args); err != nil {
		fmt.Println("Errors encountered parsing flags.")
		return c, err
	}

	if err := c.loadConfig(); err != nil {
		return c, err
	}

	// Note: .getNodeInfo() is last so we are sure we have all the
	// configuration we need from flags and config file to make
	// this run correctly.
	if err := c.getNodeInfo(); err != nil {
		return c, err
	}

	// Set the client for the collector to reuse in GET operations
	// to local state and other HTTP sessions
	collectorClient, err := httpHelpers.NewMetricsClient(c.CACertificatePath, c.IAMConfigPath)
	if err != nil {
		return c, err
	}

	c.Collector.MesosAgent.HTTPClient = collectorClient

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

package main

import (
	"flag"
	"fmt"
	"io/ioutil"

	"github.com/dcos/dcos-go/dcos"
	"github.com/dcos/dcos-go/dcos/nodeutil"
	mesosAgent "github.com/dcos/dcos-metrics/collectors/mesos/agent"
	"github.com/dcos/dcos-metrics/collectors/node"
	httpProducer "github.com/dcos/dcos-metrics/producers/http"
	httpHelpers "github.com/dcos/dcos-metrics/util/http/helpers"

	log "github.com/Sirupsen/logrus"
	yaml "gopkg.in/yaml.v2"
)

var (
	// VERSION set by $(git describe --always)
	// Set by scripts/build.sh, executed by `make build`
	VERSION = "unset"
	// REVISION set by $(git rev-parse --shore HEAD)
	// Set by scripts/build.sh, executed by `make build`
	REVISION = "unset"
)

// Config defines the top-level configuration options for the dcos-metrics-collector project.
// It is (currently) broken up into two main sections: collectors and producers.
type Config struct {
	// Config from the service config file
	Collector         CollectorConfig `yaml:"collector"`
	Producers         ProducersConfig `yaml:"producers"`
	IAMConfigPath     string          `yaml:"iam_config_path"`
	CACertificatePath string          `yaml:"ca_certificate_path"`

	// Generated by dcos.NodeInfo{}
	MesosID   string
	IPAddress string
	ClusterID string

	// Flag configuration
	DCOSRole    string
	ConfigPath  string
	LogLevel    string
	VersionFlag bool
}

// CollectorConfig contains configuration options relevant to the "collector"
// portion of this project. That is, the code responsible for querying Mesos,
// et. al to gather metrics and send them to a "producer".
type CollectorConfig struct {
	HTTPProfiler bool                  `yaml:"http_profiler"`
	Node         *node.Collector       `yaml:"node,omitempty"`
	MesosAgent   *mesosAgent.Collector `yaml:"mesos_agent,omitempty"`
}

// ProducersConfig contains references to other structs that provide individual producer configs.
// The configuration for all producers is then located in their corresponding packages.
//
// For example: Config.Producers.KafkaProducerConfig references kafkaProducer.Config. This struct
// contains an optional Kafka configuration. This configuration is available in the source file
// 'producers/kafka/kafka.go'. It is then the responsibility of the individual producers to
// validate the configuration the user has provided and panic if necessary.
type ProducersConfig struct {
	HTTPProducerConfig httpProducer.Config `yaml:"http,omitempty"`
	//KafkaProducerConfig  kafkaProducer.Config  `yaml:"kafka,omitempty"`
	//StatsdProducerConfig statsdProducer.Config `yaml:"statsd,omitempty"`
}

func (c *Config) setFlags(fs *flag.FlagSet) {
	fs.StringVar(&c.ConfigPath, "config", c.ConfigPath, "The path to the config file.")
	fs.StringVar(&c.LogLevel, "loglevel", c.LogLevel, "Logging level (default: info). Must be one of: debug, info, warn, error, fatal, panic.")
	fs.StringVar(&c.DCOSRole, "role", c.DCOSRole, "The DC/OS role this instance runs on.")
	fs.BoolVar(&c.VersionFlag, "version", c.VersionFlag, "Print version and revsion then exit")
}

func (c *Config) loadConfig() error {
	fileByte, err := ioutil.ReadFile(c.ConfigPath)
	if err != nil {
		log.Warnf("%s not found. Using all defaults", c.ConfigPath)
		return nil
	}

	if err = yaml.Unmarshal(fileByte, &c); err != nil {
		return err
	}

	return nil
}

func (c *Config) getNodeInfo() error {
	log.Debug("Getting node info")
	client, err := httpHelpers.NewMetricsClient(c.CACertificatePath, c.IAMConfigPath)
	if err != nil {
		return err
	}

	// Get NodeInfo
	var stateURL = "http://leader.mesos:5050/state"
	if len(c.IAMConfigPath) > 0 {
		stateURL = "https://leader.mesos:5050/state"
	}
	nodeInfo, err := nodeutil.NewNodeInfo(client, nodeutil.OptionMesosStateURL(stateURL))
	if err != nil {
		log.Errorf("Error getting NodeInfo{}: err")
	}

	ip, err := nodeInfo.DetectIP()
	if err != nil {
		log.Error(err)
	}
	c.Collector.MesosAgent.NodeInfo.IPAddress = ip.String()
	c.Collector.Node.NodeInfo.IPAddress = ip.String()

	mid, err := nodeInfo.MesosID(nil)
	if err != nil {
		log.Error(err)
	}
	c.Collector.MesosAgent.NodeInfo.MesosID = mid
	c.Collector.Node.NodeInfo.MesosID = mid

	if c.DCOSRole == dcos.RoleMaster {
		c.Collector.Node.NodeInfo.ClusterID, err = nodeInfo.ClusterID()
		if err != nil {
			return err
		}
	}

	return nil
}

// newConfig establishes our default, base configuration.
func newConfig() Config {
	return Config{
		Collector: CollectorConfig{
			HTTPProfiler: false,
			MesosAgent: &mesosAgent.Collector{
				PollPeriod:      60,
				Port:            5051,
				RequestProtocol: "http",
			},
			Node: &node.Collector{
				PollPeriod: 60,
			},
		},
		Producers: ProducersConfig{
			HTTPProducerConfig: httpProducer.Config{
				Port: 9000,
			},
		},
		ConfigPath: "dcos-metrics-config.yaml",
		LogLevel:   "info",
	}
}

// getNewConfig loads the configuration and sets precedence of configuration values.
// For example: command line flags override values provided in the config file.
func getNewConfig(args []string) (Config, error) {
	c := newConfig()
	thisFlagSet := flag.NewFlagSet("", flag.ExitOnError)
	c.setFlags(thisFlagSet)
	// Override default config with CLI flags if any
	if err := thisFlagSet.Parse(args); err != nil {
		fmt.Println("Errors encountered parsing flags.")
		return c, err
	}

	if err := c.loadConfig(); err != nil {
		return c, err
	}

	// Note: .getNodeInfo() is last so we are sure we have all the
	// configuration we need from flags and config file to make
	// this run correctly.
	if err := c.getNodeInfo(); err != nil {
		return c, err
	}

	// Set the client for the collector to reuse in GET operations
	// to local state and other HTTP sessions
	collectorClient, err := httpHelpers.NewMetricsClient(c.CACertificatePath, c.IAMConfigPath)
	if err != nil {
		return c, err
	}

	c.Collector.MesosAgent.HTTPClient = collectorClient

	return c, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.

package main

import (
	"encoding/json"
	"fmt"
	"net"
	"strings"
	"time"

	"github.com/coreos/go-etcd/etcd"
	"github.com/miekg/dns"
)

// Config provides options to the SkyDNS resolver.
type Config struct {
	DnsAddr      string        `json:"dns_addr,omitempty"`
	Domain       string        `json:"domain,omitempty"`
	DomainLabels int           `json:"-"`
	DNSSEC       string        `json:"dnssec,omitempty"`
	RoundRobin   bool          `json:"round_robin,omitempty"`
	Nameservers  []string      `json:"nameservers,omitempty"`
	ReadTimeout  time.Duration `json:"read_timeout,omitempty"`
	WriteTimeout time.Duration `json:"write_timeout,omitempty"`
	Ttl          uint32        `json:"ttl,omitempty"`
	MinTtl       uint32        `json:"min_ttl,omitempty"`

	// DNSSEC key material
	PubKey  *dns.DNSKEY    `json:"-"`
	KeyTag  uint16         `json:"-"`
	PrivKey dns.PrivateKey `json:"-"`
}

func LoadConfig(client *etcd.Client) (*Config, error) {
	config := &Config{ReadTimeout: 0, WriteTimeout: 0, Domain: "", DnsAddr: "", DNSSEC: ""}
	n, err := client.Get("/skydns/config", false, false)
	if err != nil {
		c, err := dns.ClientConfigFromFile("/etc/resolv.conf")
		if err != nil {
			return nil, err
		}
		for _, s := range c.Servers {
			config.Nameservers = append(config.Nameservers, net.JoinHostPort(s, c.Port))
		}
		return config, nil
	}
	if err := json.Unmarshal([]byte(n.Node.Value), &config); err != nil {
		return nil, err
	}
	if err := setDefaults(config); err != nil {
		return nil, err
	}
	return config, nil
}

func setDefaults(config *Config) error {
	if config.ReadTimeout == 0 {
		config.ReadTimeout = 2 * time.Second
	}
	if config.WriteTimeout == 0 {
		config.WriteTimeout = 2 * time.Second
	}
	if config.DnsAddr == "" {
		config.DnsAddr = "127.0.0.1:53"
	}
	if config.Domain == "" {
		config.Domain = "skydns.local"
	}
	if config.MinTtl == 0 {
		config.MinTtl = 60
	}
	if config.Ttl == 0 {
		config.Ttl = 3600
	}

	if len(config.Nameservers) == 0 {
		c, err := dns.ClientConfigFromFile("/etc/resolv.conf")
		if err != nil {
			return err
		}
		for _, s := range c.Servers {
			config.Nameservers = append(config.Nameservers, net.JoinHostPort(s, c.Port))
		}
	}
	if config.DNSSEC != "" {
		k, p, err := ParseKeyFile(config.DNSSEC)
		if err != nil {
			return err
		}
		if k.Header().Name != dns.Fqdn(config.Domain) {
			return fmt.Errorf("ownername of DNSKEY must match SkyDNS domain")
		}
		config.PubKey = k
		config.KeyTag = k.KeyTag()
		config.PrivKey = p
	}
	config.Domain = dns.Fqdn(strings.ToLower(config.Domain))
	config.DomainLabels = dns.CountLabel(config.Domain)
	return nil
}
<commit_msg>Allow for Priority to be set<commit_after>// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.

package main

import (
	"encoding/json"
	"fmt"
	"net"
	"strings"
	"time"

	"github.com/coreos/go-etcd/etcd"
	"github.com/miekg/dns"
)

// Config provides options to the SkyDNS resolver.
type Config struct {
	DnsAddr      string        `json:"dns_addr,omitempty"`
	Domain       string        `json:"domain,omitempty"`
	DomainLabels int           `json:"-"`
	DNSSEC       string        `json:"dnssec,omitempty"`
	RoundRobin   bool          `json:"round_robin,omitempty"`
	Nameservers  []string      `json:"nameservers,omitempty"`
	ReadTimeout  time.Duration `json:"read_timeout,omitempty"`
	WriteTimeout time.Duration `json:"write_timeout,omitempty"`
	Priority     uint16	   `json:"priority"`
	Ttl          uint32        `json:"ttl,omitempty"`
	MinTtl       uint32        `json:"min_ttl,omitempty"`

	// DNSSEC key material
	PubKey  *dns.DNSKEY    `json:"-"`
	KeyTag  uint16         `json:"-"`
	PrivKey dns.PrivateKey `json:"-"`
}

func LoadConfig(client *etcd.Client) (*Config, error) {
	config := &Config{ReadTimeout: 0, WriteTimeout: 0, Domain: "", DnsAddr: "", DNSSEC: ""}
	n, err := client.Get("/skydns/config", false, false)
	if err != nil {
		c, err := dns.ClientConfigFromFile("/etc/resolv.conf")
		if err != nil {
			return nil, err
		}
		for _, s := range c.Servers {
			config.Nameservers = append(config.Nameservers, net.JoinHostPort(s, c.Port))
		}
		return config, nil
	}
	if err := json.Unmarshal([]byte(n.Node.Value), &config); err != nil {
		return nil, err
	}
	if err := setDefaults(config); err != nil {
		return nil, err
	}
	return config, nil
}

func setDefaults(config *Config) error {
	if config.ReadTimeout == 0 {
		config.ReadTimeout = 2 * time.Second
	}
	if config.WriteTimeout == 0 {
		config.WriteTimeout = 2 * time.Second
	}
	if config.DnsAddr == "" {
		config.DnsAddr = "127.0.0.1:53"
	}
	if config.Domain == "" {
		config.Domain = "skydns.local"
	}
	if config.MinTtl == 0 {
		config.MinTtl = 60
	}
	if config.Ttl == 0 {
		config.Ttl = 3600
	}
	if config.Priority == 0 {
		config.Priority = 10
	}

	if len(config.Nameservers) == 0 {
		c, err := dns.ClientConfigFromFile("/etc/resolv.conf")
		if err != nil {
			return err
		}
		for _, s := range c.Servers {
			config.Nameservers = append(config.Nameservers, net.JoinHostPort(s, c.Port))
		}
	}
	if config.DNSSEC != "" {
		k, p, err := ParseKeyFile(config.DNSSEC)
		if err != nil {
			return err
		}
		if k.Header().Name != dns.Fqdn(config.Domain) {
			return fmt.Errorf("ownername of DNSKEY must match SkyDNS domain")
		}
		config.PubKey = k
		config.KeyTag = k.KeyTag()
		config.PrivKey = p
	}
	config.Domain = dns.Fqdn(strings.ToLower(config.Domain))
	config.DomainLabels = dns.CountLabel(config.Domain)
	return nil
}
<|endoftext|>


<commit_before>package goConfig

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"strconv"
	"strings"

	"github.com/pkg/errors"
)

// Settings default
type Settings struct {
	// Path sets default config path
	Path string
	// File name of default config file
	File string
	// FileRequired config file required
	FileRequired bool
	// Tag set the main tag
	Tag string
	// TagDefault set tag default
	TagDefault string
	// TagDisabled used to not process an input
	TagDisabled string
	// EnvironmentVarSeparator separe names on environment variables
	EnvironmentVarSeparator string
}

// Setup Pointer to internal variables
var Setup *Settings

var parseMap map[reflect.Kind]func(
	field *reflect.StructField,
	value *reflect.Value,
	tag string) (err error)

func init() {
	Setup = &Settings{
		Path:                    "./",
		File:                    "config.json",
		Tag:                     "cfg",
		TagDefault:              "cfgDefault",
		TagDisabled:             "-",
		EnvironmentVarSeparator: "_",
		FileRequired:            false,
	}

	parseMap = make(map[reflect.Kind]func(
		field *reflect.StructField,
		value *reflect.Value, tag string) (err error))

	parseMap[reflect.Struct] = reflectStruct
	parseMap[reflect.Int] = reflectInt
	parseMap[reflect.String] = reflectString

}

// LoadJSON config file
func LoadJSON(config interface{}) (err error) {
	configFile := Setup.Path + Setup.File
	file, err := os.Open(configFile)
	if os.IsNotExist(err) && !Setup.FileRequired {
		err = nil
		return
	} else if err != nil {
		return
	}
	defer file.Close()

	decoder := json.NewDecoder(file)
	err = decoder.Decode(&config)
	if err != nil {
		return
	}

	return
}

// Load config file
func Load(config interface{}) (err error) {

	err = LoadJSON(config)
	if err != nil {
		return
	}

	err = parseTags(config, "")
	if err != nil {
		return
	}

	return
}

// Save config file
func Save(config interface{}) (err error) {
	_, err = os.Stat(Setup.Path)
	if os.IsNotExist(err) {
		os.Mkdir(Setup.Path, 0700)
	} else if err != nil {
		return
	}

	configFile := Setup.Path + Setup.File

	_, err = os.Stat(configFile)
	if err != nil {
		return
	}

	b, err := json.MarshalIndent(config, "", "\t")
	if err != nil {
		return
	}

	err = ioutil.WriteFile(configFile, b, 0644)
	if err != nil {
		return
	}
	return
}

func parseTags(s interface{}, superTag string) (err error) {

	st := reflect.TypeOf(s)

	if st.Kind() != reflect.Ptr {
		err = errors.New("Not a pointer")
		return
	}

	refField := st.Elem()
	if refField.Kind() != reflect.Struct {
		err = errors.New("Not a struct")
		return
	}

	//vt := reflect.ValueOf(s)
	refValue := reflect.ValueOf(s).Elem()
	for i := 0; i < refField.NumField(); i++ {
		field := refField.Field(i)
		value := refValue.Field(i)
		kind := field.Type.Kind()

		if field.PkgPath != "" {
			continue
		}

		t := updateTag(&field, superTag)
		if t == "" {
			continue
		}

		if f, ok := parseMap[kind]; ok {
			err = f(&field, &value, t)
			if err != nil {
				return
			}
		} else {
			err = errors.New("Type not supported " + kind.String())
			return
		}

		fmt.Println("name:", field.Name,
			"| cfg:", field.Tag.Get(Setup.Tag),
			"| cfgDefault:", field.Tag.Get(Setup.TagDefault),
			"| type:", field.Type)

	}
	return
}

func updateTag(field *reflect.StructField, superTag string) (ret string) {
	ret = field.Tag.Get(Setup.Tag)
	if ret == Setup.TagDisabled {
		return
	}

	if ret == "" {
		ret = strings.ToUpper(field.Name)
	}

	if superTag != "" {
		ret = superTag + Setup.EnvironmentVarSeparator + ret
	}
	return
}

func getNewValue(field *reflect.StructField, tag string) (ret string) {

	ret = os.Getenv(tag)
	if ret != "" {
		return
	}

	ret = field.Tag.Get(Setup.TagDefault)
	if ret != "" {
		return
	}

	return

}

func reflectStruct(field *reflect.StructField, value *reflect.Value, tag string) (err error) {
	err = parseTags(value.Addr().Interface(), tag)
	return
}

func reflectInt(field *reflect.StructField, value *reflect.Value, tag string) (err error) {
	//value.SetInt(999)

	newValue := getNewValue(field, tag)

	var intNewValue int64
	intNewValue, err = strconv.ParseInt(newValue, 10, 64)
	if err != nil {
		return
	}
	value.SetInt(intNewValue)

	return
}

func reflectString(field *reflect.StructField, value *reflect.Value, tag string) (err error) {
	//value.SetString("TEST")
	newValue := getNewValue(field, tag)

	value.SetString(newValue)

	return
}
<commit_msg>new type ReflectFunc<commit_after>package goConfig

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"strconv"
	"strings"

	"github.com/pkg/errors"
)

// Settings default
type Settings struct {
	// Path sets default config path
	Path string
	// File name of default config file
	File string
	// FileRequired config file required
	FileRequired bool
	// Tag set the main tag
	Tag string
	// TagDefault set tag default
	TagDefault string
	// TagDisabled used to not process an input
	TagDisabled string
	// EnvironmentVarSeparator separe names on environment variables
	EnvironmentVarSeparator string
}

// Setup Pointer to internal variables
var Setup *Settings

// ReflectFunc type used to create funcrions to parse struct and tags
type ReflectFunc func(
	field *reflect.StructField,
	value *reflect.Value,
	tag string) (err error)

var parseMap map[reflect.Kind]ReflectFunc

func init() {
	Setup = &Settings{
		Path:                    "./",
		File:                    "config.json",
		Tag:                     "cfg",
		TagDefault:              "cfgDefault",
		TagDisabled:             "-",
		EnvironmentVarSeparator: "_",
		FileRequired:            false,
	}

	parseMap = make(map[reflect.Kind]ReflectFunc)

	parseMap[reflect.Struct] = reflectStruct
	parseMap[reflect.Int] = reflectInt
	parseMap[reflect.String] = reflectString

}

// LoadJSON config file
func LoadJSON(config interface{}) (err error) {
	configFile := Setup.Path + Setup.File
	file, err := os.Open(configFile)
	if os.IsNotExist(err) && !Setup.FileRequired {
		err = nil
		return
	} else if err != nil {
		return
	}
	defer file.Close()

	decoder := json.NewDecoder(file)
	err = decoder.Decode(&config)
	if err != nil {
		return
	}

	return
}

// Load config file
func Load(config interface{}) (err error) {

	err = LoadJSON(config)
	if err != nil {
		return
	}

	err = parseTags(config, "")
	if err != nil {
		return
	}

	return
}

// Save config file
func Save(config interface{}) (err error) {
	_, err = os.Stat(Setup.Path)
	if os.IsNotExist(err) {
		os.Mkdir(Setup.Path, 0700)
	} else if err != nil {
		return
	}

	configFile := Setup.Path + Setup.File

	_, err = os.Stat(configFile)
	if err != nil {
		return
	}

	b, err := json.MarshalIndent(config, "", "\t")
	if err != nil {
		return
	}

	err = ioutil.WriteFile(configFile, b, 0644)
	if err != nil {
		return
	}
	return
}

func parseTags(s interface{}, superTag string) (err error) {

	st := reflect.TypeOf(s)

	if st.Kind() != reflect.Ptr {
		err = errors.New("Not a pointer")
		return
	}

	refField := st.Elem()
	if refField.Kind() != reflect.Struct {
		err = errors.New("Not a struct")
		return
	}

	//vt := reflect.ValueOf(s)
	refValue := reflect.ValueOf(s).Elem()
	for i := 0; i < refField.NumField(); i++ {
		field := refField.Field(i)
		value := refValue.Field(i)
		kind := field.Type.Kind()

		if field.PkgPath != "" {
			continue
		}

		t := updateTag(&field, superTag)
		if t == "" {
			continue
		}

		if f, ok := parseMap[kind]; ok {
			err = f(&field, &value, t)
			if err != nil {
				return
			}
		} else {
			err = errors.New("Type not supported " + kind.String())
			return
		}

		fmt.Println("name:", field.Name,
			"| cfg:", field.Tag.Get(Setup.Tag),
			"| cfgDefault:", field.Tag.Get(Setup.TagDefault),
			"| type:", field.Type)

	}
	return
}

func updateTag(field *reflect.StructField, superTag string) (ret string) {
	ret = field.Tag.Get(Setup.Tag)
	if ret == Setup.TagDisabled {
		return
	}

	if ret == "" {
		ret = strings.ToUpper(field.Name)
	}

	if superTag != "" {
		ret = superTag + Setup.EnvironmentVarSeparator + ret
	}
	return
}

func getNewValue(field *reflect.StructField, tag string) (ret string) {

	ret = os.Getenv(tag)
	if ret != "" {
		return
	}

	ret = field.Tag.Get(Setup.TagDefault)
	if ret != "" {
		return
	}

	return

}

func reflectStruct(field *reflect.StructField, value *reflect.Value, tag string) (err error) {
	err = parseTags(value.Addr().Interface(), tag)
	return
}

func reflectInt(field *reflect.StructField, value *reflect.Value, tag string) (err error) {
	//value.SetInt(999)

	newValue := getNewValue(field, tag)

	var intNewValue int64
	intNewValue, err = strconv.ParseInt(newValue, 10, 64)
	if err != nil {
		return
	}
	value.SetInt(intNewValue)

	return
}

func reflectString(field *reflect.StructField, value *reflect.Value, tag string) (err error) {
	//value.SetString("TEST")
	newValue := getNewValue(field, tag)

	value.SetString(newValue)

	return
}
<|endoftext|>


<commit_before>package main

import (
	"time"

	"github.com/BurntSushi/toml"
)

type duration struct {
	time.Duration
}

func (d *duration) UnmarshalText(text []byte) (err error) {
	d.Duration, err = time.ParseDuration(string(text))
	return err
}

type GithubConfig struct {
	Listen string `toml:"listen"`
	Secret string `toml:"secret"`
}

type BroadcastConfig struct {
	Channel string   `toml:"channel"`
	Topic   string   `toml:"topic"`
	Timeout duration `toml:"timeout"`
}

type ArchiveConfig struct {
	ArchiveTable     string `toml:"archive_table"`
	SubscribersTable string `toml:"subscribers_table"`
	BroadcastTopic   string `toml:"broadcast_hooks"`
	HooksTopic       string `toml:"hooks_topic"`
}

type Config struct {
	Debug             bool            `toml:"debug"`
	NSQD              string          `toml:"nsqd"`
	Lookupd           string          `toml:"lookupd"`
	RethinkdbAddress  string          `toml:"rethinkdb_address"`
	RethinkdbKey      string          `toml:"rethinkdb_key"`
	RethinkdbDatabase string          `toml:"rethinkdb_database"`
	Github            GithubConfig    `toml:"github"`
	Archive           ArchiveConfig   `toml:"archive"`
	Broadcast         BroadcastConfig `toml:"broadcast"`
}

func loadConfig(path string) *Config {
	var c *Config
	if _, err := toml.DecodeFile(path, &c); err != nil {
		logger.Fatal(err)
	}
	return c
}
<commit_msg>Fix toml tag for procast queue<commit_after>package main

import (
	"time"

	"github.com/BurntSushi/toml"
)

type duration struct {
	time.Duration
}

func (d *duration) UnmarshalText(text []byte) (err error) {
	d.Duration, err = time.ParseDuration(string(text))
	return err
}

type GithubConfig struct {
	Listen string `toml:"listen"`
	Secret string `toml:"secret"`
}

type BroadcastConfig struct {
	Channel string   `toml:"channel"`
	Topic   string   `toml:"topic"`
	Timeout duration `toml:"timeout"`
}

type ArchiveConfig struct {
	ArchiveTable     string `toml:"archive_table"`
	SubscribersTable string `toml:"subscribers_table"`
	BroadcastTopic   string `toml:"broadcast_topic"`
	HooksTopic       string `toml:"hooks_topic"`
}

type Config struct {
	Debug             bool            `toml:"debug"`
	NSQD              string          `toml:"nsqd"`
	Lookupd           string          `toml:"lookupd"`
	RethinkdbAddress  string          `toml:"rethinkdb_address"`
	RethinkdbKey      string          `toml:"rethinkdb_key"`
	RethinkdbDatabase string          `toml:"rethinkdb_database"`
	Github            GithubConfig    `toml:"github"`
	Archive           ArchiveConfig   `toml:"archive"`
	Broadcast         BroadcastConfig `toml:"broadcast"`
}

func loadConfig(path string) *Config {
	var c *Config
	if _, err := toml.DecodeFile(path, &c); err != nil {
		logger.Fatal(err)
	}
	return c
}
<|endoftext|>


<commit_before>package gnosis

import (
	"encoding/json"
	"html/template"
	"io/ioutil"
	"log"
	"sync"
)

var staticConfig *Config
var configLock = new(sync.RWMutex)

//var templates = template.Must(template.ParseFiles("/var/wiki-backend/wiki.html"))
//template.New("allTemplates")
var allTemplates = template.New("allTemplates")
var templateLock = new(sync.RWMutex)

type GlobalSection struct {
	Port        string
	Hostname    string
	TemplateDir string
	Templates   []string
	Redirects   map[string]string
}

type ServerSection struct {
	Path        string
	Prefix      string
	DefaultPage string
	Template    string
	ServerType  string
	Restricted  []string
}

type RedirectSection struct {
	Requested string
	Target    string
	Code      int
}

type Config struct {
	Global     GlobalSection
	Redirects  []RedirectSection
	Mainserver ServerSection
	Server     []ServerSection
}

var defaultConfig = []byte(`{
  "Global": {
    "Port": "8080",
    "Hostname": "localhost"
  },
  "Mainserver": {
      "Path": "/var/www/wiki/",
      "Prefix": "/",
      "DefaultPage": "index",
      "ServerType": "markdown",
      "Template": "wiki.html",
      "Restricted": [
        "internal",
        "handbook"
      ]
    },
  "Server": [
  ]
}`)

func GetConfig() *Config {
	configLock.RLock()
	defer configLock.RUnlock()
	return staticConfig
}

func LoadConfig(configFile string) bool {

	if configFile == "" {
		log.Println("no configuration file specified, using ./config.json")
		// return an empty config file
		configFile = "config.json"
	}

	// have to read in the line into a byte[] array
	fileContents, err := ioutil.ReadFile(configFile)
	if err != nil {
		log.Printf("Problem loading config file: %s", err.Error())
	}

	// UnMarshal the config file that was read in
	temp := new(Config)

	err = json.Unmarshal(defaultConfig, temp)

	if err != nil {
		log.Println("problem parsing built in default configuration - this should not happen")
		return false
	}

	err = json.Unmarshal(fileContents, temp)
	//Make sure you were able to read it in
	if err != nil {
		log.Printf("parse config error: %s", err.Error())
		return false
	}

	configLock.Lock()
	staticConfig = temp
	configLock.Unlock()

	return true
}

func ParseTemplates(globalConfig GlobalSection) {
	var err error
	//newTemplate := template.New("newTemplate")
	newTemplate, err := template.ParseGlob(globalConfig.TemplateDir + "*")
	if err != nil {
		log.Println("Found an invalid template, abandoning updating templates")
		return
	}

	loadedTemplates := newTemplate.Templates()
	for _, individualTemplate := range loadedTemplates {
		log.Printf("Loaded template %s ", individualTemplate.Name())
	}

	/*
		for _, templateFile := range globalConfig.Templates {
			nextTemplate, err = newTemplate.ParseFiles(globalConfig.TemplateDir + templateFile)
			if nextTemplate != nil {
				newTemplate = nextTemplate
			} else {
				log.Println("Found an invalid template, abandoning updating templates")
			}
		}
	*/

	// log.Printf("loaded templates - %s", newTemplate.Name())

	if err == nil {
		templateLock.Lock()
		defer templateLock.Unlock()
		allTemplates = newTemplate
	}

}
<commit_msg>added TopicURL to the config<commit_after>package gnosis

import (
	"encoding/json"
	"html/template"
	"io/ioutil"
	"log"
	"sync"
)

var staticConfig *Config
var configLock = new(sync.RWMutex)

//var templates = template.Must(template.ParseFiles("/var/wiki-backend/wiki.html"))
//template.New("allTemplates")
var allTemplates = template.New("allTemplates")
var templateLock = new(sync.RWMutex)

type GlobalSection struct {
	Port        string
	Hostname    string
	TemplateDir string
	Templates   []string
	Redirects   map[string]string
}

type ServerSection struct {
	Path        string
	Prefix      string
	DefaultPage string
	Template    string
	ServerType  string
	TopicURL    string
	Restricted  []string
}

type RedirectSection struct {
	Requested string
	Target    string
	Code      int
}

type Config struct {
	Global     GlobalSection
	Redirects  []RedirectSection
	Mainserver ServerSection
	Server     []ServerSection
}

var defaultConfig = []byte(`{
  "Global": {
    "Port": "8080",
    "Hostname": "localhost"
  },
  "Mainserver": {
      "Path": "/var/www/wiki/",
      "Prefix": "/",
      "DefaultPage": "index",
      "ServerType": "markdown",
      "Template": "wiki.html",
      "Restricted": [
        "internal",
        "handbook"
      ]
    },
  "Server": [
  ]
}`)

func GetConfig() *Config {
	configLock.RLock()
	defer configLock.RUnlock()
	return staticConfig
}

func LoadConfig(configFile string) bool {

	if configFile == "" {
		log.Println("no configuration file specified, using ./config.json")
		// return an empty config file
		configFile = "config.json"
	}

	// have to read in the line into a byte[] array
	fileContents, err := ioutil.ReadFile(configFile)
	if err != nil {
		log.Printf("Problem loading config file: %s", err.Error())
	}

	// UnMarshal the config file that was read in
	temp := new(Config)

	err = json.Unmarshal(defaultConfig, temp)

	if err != nil {
		log.Println("problem parsing built in default configuration - this should not happen")
		return false
	}

	err = json.Unmarshal(fileContents, temp)
	//Make sure you were able to read it in
	if err != nil {
		log.Printf("parse config error: %s", err.Error())
		return false
	}

	configLock.Lock()
	staticConfig = temp
	configLock.Unlock()

	return true
}

func ParseTemplates(globalConfig GlobalSection) {
	var err error
	//newTemplate := template.New("newTemplate")
	newTemplate, err := template.ParseGlob(globalConfig.TemplateDir + "*")
	if err != nil {
		log.Println("Found an invalid template, abandoning updating templates")
		return
	}

	loadedTemplates := newTemplate.Templates()
	for _, individualTemplate := range loadedTemplates {
		log.Printf("Loaded template %s ", individualTemplate.Name())
	}

	/*
		for _, templateFile := range globalConfig.Templates {
			nextTemplate, err = newTemplate.ParseFiles(globalConfig.TemplateDir + templateFile)
			if nextTemplate != nil {
				newTemplate = nextTemplate
			} else {
				log.Println("Found an invalid template, abandoning updating templates")
			}
		}
	*/

	// log.Printf("loaded templates - %s", newTemplate.Name())

	if err == nil {
		templateLock.Lock()
		defer templateLock.Unlock()
		allTemplates = newTemplate
	}

}
<|endoftext|>


<commit_before>// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved.

package log4go

import (
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
)

type xmlProperty struct {
	Name  string `xml:"name,attr"`
	Value string `xml:",chardata"`
}

type xmlFilter struct {
	Enabled  string        `xml:"enabled,attr"`
	Tag      string        `xml:"tag"`
	Level    string        `xml:"level"`
	Type     string        `xml:"type"`
	Property []xmlProperty `xml:"property"`
}

type xmlLoggerConfig struct {
	Filter []xmlFilter `xml:"filter"`
}

// Load XML configuration; see examples/example.xml for documentation
func (log Logger) LoadConfiguration(filename string) {
	log.Close()

	// Open the configuration file
	fd, err := os.Open(filename)
	if err != nil {
		fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not open %q for reading: %s\n", filename, err)
		os.Exit(1)
	}

	contents, err := ioutil.ReadAll(fd)
	if err != nil {
		fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not read %q: %s\n", filename, err)
		os.Exit(1)
	}

	xc := new(xmlLoggerConfig)
	if err := xml.Unmarshal(contents, xc); err != nil {
		fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not parse XML configuration in %q: %s\n", filename, err)
		os.Exit(1)
	}

	for _, xmlfilt := range xc.Filter {
		var filt LogWriter
		var lvl level
		bad, good, enabled := false, true, false

		// Check required children
		if len(xmlfilt.Enabled) == 0 {
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required attribute %s for filter missing in %s\n", "enabled", filename)
			bad = true
		} else {
			enabled = xmlfilt.Enabled != "false"
		}
		if len(xmlfilt.Tag) == 0 {
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "tag", filename)
			bad = true
		}
		if len(xmlfilt.Type) == 0 {
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "type", filename)
			bad = true
		}
		if len(xmlfilt.Level) == 0 {
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "level", filename)
			bad = true
		}

		switch xmlfilt.Level {
		case "FINEST":
			lvl = FINEST
		case "FINE":
			lvl = FINE
		case "DEBUG":
			lvl = DEBUG
		case "TRACE":
			lvl = TRACE
		case "INFO":
			lvl = INFO
		case "WARNING":
			lvl = WARNING
		case "ERROR":
			lvl = ERROR
		case "CRITICAL":
			lvl = CRITICAL
		default:
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter has unknown value in %s: %s\n", "level", filename, xmlfilt.Level)
			bad = true
		}

		// Just so all of the required attributes are errored at the same time if missing
		if bad {
			os.Exit(1)
		}

		switch xmlfilt.Type {
		case "console":
			filt, good = xmlToConsoleLogWriter(filename, xmlfilt.Property, enabled)
		case "file":
			filt, good = xmlToFileLogWriter(filename, xmlfilt.Property, enabled)
		case "xml":
			filt, good = xmlToXMLLogWriter(filename, xmlfilt.Property, enabled)
		case "socket":
			filt, good = xmlToSocketLogWriter(filename, xmlfilt.Property, enabled)
		default:
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not load XML configuration in %s: unknown filter type \"%s\"\n", filename, xmlfilt.Type)
			os.Exit(1)
		}

		// Just so all of the required params are errored at the same time if wrong
		if !good {
			os.Exit(1)
		}

		// If we're disabled (syntax and correctness checks only), don't add to logger
		if !enabled {
			continue
		}

		log[xmlfilt.Tag] = &Filter{lvl, filt}
	}
}

func xmlToConsoleLogWriter(filename string, props []xmlProperty, enabled bool) (ConsoleLogWriter, bool) {
	// Parse properties
	for _, prop := range props {
		switch prop.Name {
		default:
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for console filter in %s\n", prop.Name, filename)
		}
	}

	// If it's disabled, we're just checking syntax
	if !enabled {
		return nil, true
	}

	return NewConsoleLogWriter(), true
}

// Parse a number with K/M/G suffixes based on thousands (1000) or 2^10 (1024)
func strToNumSuffix(str string, mult int) int {
	num := 1
	if len(str) > 1 {
		switch str[len(str)-1] {
		case 'G', 'g':
			num *= mult
			fallthrough
		case 'M', 'm':
			num *= mult
			fallthrough
		case 'K', 'k':
			num *= mult
			str = str[0 : len(str)-1]
		}
	}
	parsed, _ := strconv.Atoi(str)
	return parsed * num
}
func xmlToFileLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) {
	file := ""
	format := "[%D %T] [%L] (%S) %M"
	maxlines := 0
	maxsize := 0
	daily := false
	rotate := false

	// Parse properties
	for _, prop := range props {
		switch prop.Name {
		case "filename":
			file = strings.Trim(prop.Value, " \r\n")
		case "format":
			format = strings.Trim(prop.Value, " \r\n")
		case "maxlines":
			maxlines = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000)
		case "maxsize":
			maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024)
		case "daily":
			daily = strings.Trim(prop.Value, " \r\n") != "false"
		case "rotate":
			rotate = strings.Trim(prop.Value, " \r\n") != "false"
		default:
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename)
		}
	}

	// Check properties
	if len(file) == 0 {
		fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "filename", filename)
		return nil, false
	}

	// If it's disabled, we're just checking syntax
	if !enabled {
		return nil, true
	}

	flw := NewFileLogWriter(file, rotate)
	flw.SetFormat(format)
	flw.SetRotateLines(maxlines)
	flw.SetRotateSize(maxsize)
	flw.SetRotateDaily(daily)
	return flw, true
}

func xmlToXMLLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) {
	file := ""
	maxrecords := 0
	maxsize := 0
	daily := false
	rotate := false

	// Parse properties
	for _, prop := range props {
		switch prop.Name {
		case "filename":
			file = strings.Trim(prop.Value, " \r\n")
		case "maxrecords":
			maxrecords = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000)
		case "maxsize":
			maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024)
		case "daily":
			daily = strings.Trim(prop.Value, " \r\n") != "false"
		case "rotate":
			rotate = strings.Trim(prop.Value, " \r\n") != "false"
		default:
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for xml filter in %s\n", prop.Name, filename)
		}
	}

	// Check properties
	if len(file) == 0 {
		fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for xml filter missing in %s\n", "filename", filename)
		return nil, false
	}

	// If it's disabled, we're just checking syntax
	if !enabled {
		return nil, true
	}

	xlw := NewXMLLogWriter(file, rotate)
	xlw.SetRotateLines(maxrecords)
	xlw.SetRotateSize(maxsize)
	xlw.SetRotateDaily(daily)
	return xlw, true
}

func xmlToSocketLogWriter(filename string, props []xmlProperty, enabled bool) (SocketLogWriter, bool) {
	endpoint := ""
	protocol := "udp"

	// Parse properties
	for _, prop := range props {
		switch prop.Name {
		case "endpoint":
			endpoint = strings.Trim(prop.Value, " \r\n")
		case "protocol":
			protocol = strings.Trim(prop.Value, " \r\n")
		default:
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename)
		}
	}

	// Check properties
	if len(endpoint) == 0 {
		fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "endpoint", filename)
		return nil, false
	}

	// If it's disabled, we're just checking syntax
	if !enabled {
		return nil, true
	}

	return NewSocketLogWriter(protocol, endpoint), true
}
<commit_msg>fix compile error<commit_after>// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved.

package log4go

import (
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
)

type xmlProperty struct {
	Name  string `xml:"name,attr"`
	Value string `xml:",chardata"`
}

type xmlFilter struct {
	Enabled  string        `xml:"enabled,attr"`
	Tag      string        `xml:"tag"`
	Level    string        `xml:"level"`
	Type     string        `xml:"type"`
	Property []xmlProperty `xml:"property"`
}

type xmlLoggerConfig struct {
	Filter []xmlFilter `xml:"filter"`
}

// Load XML configuration; see examples/example.xml for documentation
func (log Logger) LoadConfiguration(filename string) {
	log.Close()

	// Open the configuration file
	fd, err := os.Open(filename)
	if err != nil {
		fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not open %q for reading: %s\n", filename, err)
		os.Exit(1)
	}

	contents, err := ioutil.ReadAll(fd)
	if err != nil {
		fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not read %q: %s\n", filename, err)
		os.Exit(1)
	}

	xc := new(xmlLoggerConfig)
	if err := xml.Unmarshal(contents, xc); err != nil {
		fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not parse XML configuration in %q: %s\n", filename, err)
		os.Exit(1)
	}

	for _, xmlfilt := range xc.Filter {
		var filt LogWriter
		var lvl Level
		bad, good, enabled := false, true, false

		// Check required children
		if len(xmlfilt.Enabled) == 0 {
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required attribute %s for filter missing in %s\n", "enabled", filename)
			bad = true
		} else {
			enabled = xmlfilt.Enabled != "false"
		}
		if len(xmlfilt.Tag) == 0 {
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "tag", filename)
			bad = true
		}
		if len(xmlfilt.Type) == 0 {
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "type", filename)
			bad = true
		}
		if len(xmlfilt.Level) == 0 {
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "level", filename)
			bad = true
		}

		switch xmlfilt.Level {
		case "FINEST":
			lvl = FINEST
		case "FINE":
			lvl = FINE
		case "DEBUG":
			lvl = DEBUG
		case "TRACE":
			lvl = TRACE
		case "INFO":
			lvl = INFO
		case "WARNING":
			lvl = WARNING
		case "ERROR":
			lvl = ERROR
		case "CRITICAL":
			lvl = CRITICAL
		default:
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter has unknown value in %s: %s\n", "level", filename, xmlfilt.Level)
			bad = true
		}

		// Just so all of the required attributes are errored at the same time if missing
		if bad {
			os.Exit(1)
		}

		switch xmlfilt.Type {
		case "console":
			filt, good = xmlToConsoleLogWriter(filename, xmlfilt.Property, enabled)
		case "file":
			filt, good = xmlToFileLogWriter(filename, xmlfilt.Property, enabled)
		case "xml":
			filt, good = xmlToXMLLogWriter(filename, xmlfilt.Property, enabled)
		case "socket":
			filt, good = xmlToSocketLogWriter(filename, xmlfilt.Property, enabled)
		default:
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not load XML configuration in %s: unknown filter type \"%s\"\n", filename, xmlfilt.Type)
			os.Exit(1)
		}

		// Just so all of the required params are errored at the same time if wrong
		if !good {
			os.Exit(1)
		}

		// If we're disabled (syntax and correctness checks only), don't add to logger
		if !enabled {
			continue
		}

		log[xmlfilt.Tag] = &Filter{lvl, filt}
	}
}

func xmlToConsoleLogWriter(filename string, props []xmlProperty, enabled bool) (ConsoleLogWriter, bool) {
	// Parse properties
	for _, prop := range props {
		switch prop.Name {
		default:
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for console filter in %s\n", prop.Name, filename)
		}
	}

	// If it's disabled, we're just checking syntax
	if !enabled {
		return nil, true
	}

	return NewConsoleLogWriter(), true
}

// Parse a number with K/M/G suffixes based on thousands (1000) or 2^10 (1024)
func strToNumSuffix(str string, mult int) int {
	num := 1
	if len(str) > 1 {
		switch str[len(str)-1] {
		case 'G', 'g':
			num *= mult
			fallthrough
		case 'M', 'm':
			num *= mult
			fallthrough
		case 'K', 'k':
			num *= mult
			str = str[0 : len(str)-1]
		}
	}
	parsed, _ := strconv.Atoi(str)
	return parsed * num
}
func xmlToFileLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) {
	file := ""
	format := "[%D %T] [%L] (%S) %M"
	maxlines := 0
	maxsize := 0
	daily := false
	rotate := false

	// Parse properties
	for _, prop := range props {
		switch prop.Name {
		case "filename":
			file = strings.Trim(prop.Value, " \r\n")
		case "format":
			format = strings.Trim(prop.Value, " \r\n")
		case "maxlines":
			maxlines = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000)
		case "maxsize":
			maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024)
		case "daily":
			daily = strings.Trim(prop.Value, " \r\n") != "false"
		case "rotate":
			rotate = strings.Trim(prop.Value, " \r\n") != "false"
		default:
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename)
		}
	}

	// Check properties
	if len(file) == 0 {
		fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "filename", filename)
		return nil, false
	}

	// If it's disabled, we're just checking syntax
	if !enabled {
		return nil, true
	}

	flw := NewFileLogWriter(file, rotate)
	flw.SetFormat(format)
	flw.SetRotateLines(maxlines)
	flw.SetRotateSize(maxsize)
	flw.SetRotateDaily(daily)
	return flw, true
}

func xmlToXMLLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) {
	file := ""
	maxrecords := 0
	maxsize := 0
	daily := false
	rotate := false

	// Parse properties
	for _, prop := range props {
		switch prop.Name {
		case "filename":
			file = strings.Trim(prop.Value, " \r\n")
		case "maxrecords":
			maxrecords = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000)
		case "maxsize":
			maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024)
		case "daily":
			daily = strings.Trim(prop.Value, " \r\n") != "false"
		case "rotate":
			rotate = strings.Trim(prop.Value, " \r\n") != "false"
		default:
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for xml filter in %s\n", prop.Name, filename)
		}
	}

	// Check properties
	if len(file) == 0 {
		fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for xml filter missing in %s\n", "filename", filename)
		return nil, false
	}

	// If it's disabled, we're just checking syntax
	if !enabled {
		return nil, true
	}

	xlw := NewXMLLogWriter(file, rotate)
	xlw.SetRotateLines(maxrecords)
	xlw.SetRotateSize(maxsize)
	xlw.SetRotateDaily(daily)
	return xlw, true
}

func xmlToSocketLogWriter(filename string, props []xmlProperty, enabled bool) (SocketLogWriter, bool) {
	endpoint := ""
	protocol := "udp"

	// Parse properties
	for _, prop := range props {
		switch prop.Name {
		case "endpoint":
			endpoint = strings.Trim(prop.Value, " \r\n")
		case "protocol":
			protocol = strings.Trim(prop.Value, " \r\n")
		default:
			fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename)
		}
	}

	// Check properties
	if len(endpoint) == 0 {
		fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "endpoint", filename)
		return nil, false
	}

	// If it's disabled, we're just checking syntax
	if !enabled {
		return nil, true
	}

	return NewSocketLogWriter(protocol, endpoint), true
}
<|endoftext|>


<commit_before>// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// See http://formwork-io.github.io/ for more.

package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strconv"
	"strings"
	toml "github.com/BurntSushi/toml"
)

type rail struct {
	Name         string
	Pattern      string
	Ingress      int
	Egress       int
}

type rails struct {
	Rail         []rail
}

func ReadConfigFile(path string) ([]rail, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("configuration file error: %s", err.Error()))
	}

	var rails rails
	_, err = toml.Decode(string(data), &rails)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("configuration file error: %s", err.Error()))
	}

	_, err = validateRails(rails.Rail)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("configuration file error: " + err.Error()))
	}

	return rails.Rail, nil
}

func ReadEnvironment() ([]rail, error) {
	GL_RAIL_NAME_TMPL    := "GL_RAIL_%d_NAME"
	GL_RAIL_PATTERN_TMPL := "GL_RAIL_%d_PATTERN"
	GL_RAIL_INGRESS_TMPL := "GL_RAIL_%d_INGRESS_PORT"
	GL_RAIL_EGRESS_TMPL  := "GL_RAIL_%d_EGRESS_PORT"

	var rails []rail
	index := 0
	for {
		name, err := getenv(fmt.Sprintf(GL_RAIL_NAME_TMPL, index))
		if err != nil {
			return nil, err
		}
		if name == "" {
			break
		}

		pattern, err      := getenv(fmt.Sprintf(GL_RAIL_PATTERN_TMPL, index))
		if err != nil {
			return nil, err
		}
		ingress, err      := getenv(fmt.Sprintf(GL_RAIL_INGRESS_TMPL, index))
		if err != nil {
			return nil, err
		}
		egress, err       := getenv(fmt.Sprintf(GL_RAIL_EGRESS_TMPL, index))
		if err != nil {
			return nil, err
		}
		ingress_port, err := asPort(ingress)
		if err != nil {
			return nil, err
		}
		egress_port, err  := asPort(egress)
		if err != nil {
			return nil, err
		}

		rails = append(rails, rail {
			Name:    name,
			Pattern: pattern,
			Ingress: ingress_port,
			Egress:  egress_port,
		})

		index++
	}

	_, err := validateRails(rails)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("configuration file error: " + err.Error()))
	}

	return rails, nil
}

func validateRails(rails []rail) (*rail, error) {
	re := regexp.MustCompile("[[:punct:]]|[[:space:]]")
	for i, rail := range rails {
		normalizedPattern := strings.ToLower(re.ReplaceAllLiteralString(rail.Pattern, ""))
		if normalizedPattern != "pubsub" && normalizedPattern != "reqrep" {
			msg := fmt.Sprintf("invalid pattern \"%s\" for rail \"%s\"", rail.Pattern, rail.Name)
			return &rail, errors.New(msg)
		}
		rails[i].Pattern = normalizedPattern
	}

	return nil, nil
}

func getenv(env string) (string, error) {
	_env := os.Getenv(env)
	if len(_env) == 0 {
		return "", errors.New(fmt.Sprintf("no %s is set", env))
	}
	return _env, nil
}

func asPort(env string) (int, error) {
	port, err := strconv.Atoi(env)
	if err != nil {
		die("invalid port: %s", env)
		return -1, errors.New(fmt.Sprintf("invalid port: %v - %s", env, err.Error()))
	} else if port < 1 || port > 65535 {
		die("invalid port: %s", env)
		return -1, errors.New(fmt.Sprintf("invalid port: %v - %s", env, err.Error()))
	}
	return port, nil
}
<commit_msg>reformat config.go for some reason<commit_after>// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// See http://formwork-io.github.io/ for more.

package main

import (
	"errors"
	"fmt"
	toml "github.com/BurntSushi/toml"
	"io/ioutil"
	"os"
	"regexp"
	"strconv"
	"strings"
)

type rail struct {
	Name    string
	Pattern string
	Ingress int
	Egress  int
}

type rails struct {
	Rail []rail
}

func ReadConfigFile(path string) ([]rail, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("configuration file error: %s", err.Error()))
	}

	var rails rails
	_, err = toml.Decode(string(data), &rails)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("configuration file error: %s", err.Error()))
	}

	_, err = validateRails(rails.Rail)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("configuration file error: " + err.Error()))
	}

	return rails.Rail, nil
}

func ReadEnvironment() ([]rail, error) {
	GL_RAIL_NAME_TMPL := "GL_RAIL_%d_NAME"
	GL_RAIL_PATTERN_TMPL := "GL_RAIL_%d_PATTERN"
	GL_RAIL_INGRESS_TMPL := "GL_RAIL_%d_INGRESS_PORT"
	GL_RAIL_EGRESS_TMPL := "GL_RAIL_%d_EGRESS_PORT"

	var rails []rail
	index := 0
	for {
		name, err := getenv(fmt.Sprintf(GL_RAIL_NAME_TMPL, index))
		if err != nil {
			return nil, err
		}
		if name == "" {
			break
		}

		pattern, err := getenv(fmt.Sprintf(GL_RAIL_PATTERN_TMPL, index))
		if err != nil {
			return nil, err
		}
		ingress, err := getenv(fmt.Sprintf(GL_RAIL_INGRESS_TMPL, index))
		if err != nil {
			return nil, err
		}
		egress, err := getenv(fmt.Sprintf(GL_RAIL_EGRESS_TMPL, index))
		if err != nil {
			return nil, err
		}
		ingress_port, err := asPort(ingress)
		if err != nil {
			return nil, err
		}
		egress_port, err := asPort(egress)
		if err != nil {
			return nil, err
		}

		rails = append(rails, rail{
			Name:    name,
			Pattern: pattern,
			Ingress: ingress_port,
			Egress:  egress_port,
		})

		index++
	}

	_, err := validateRails(rails)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("configuration file error: " + err.Error()))
	}

	return rails, nil
}

func validateRails(rails []rail) (*rail, error) {
	re := regexp.MustCompile("[[:punct:]]|[[:space:]]")
	for i, rail := range rails {
		normalizedPattern := strings.ToLower(re.ReplaceAllLiteralString(rail.Pattern, ""))
		if normalizedPattern != "pubsub" && normalizedPattern != "reqrep" {
			msg := fmt.Sprintf("invalid pattern \"%s\" for rail \"%s\"", rail.Pattern, rail.Name)
			return &rail, errors.New(msg)
		}
		rails[i].Pattern = normalizedPattern
	}

	return nil, nil
}

func getenv(env string) (string, error) {
	_env := os.Getenv(env)
	if len(_env) == 0 {
		return "", errors.New(fmt.Sprintf("no %s is set", env))
	}
	return _env, nil
}

func asPort(env string) (int, error) {
	port, err := strconv.Atoi(env)
	if err != nil {
		die("invalid port: %s", env)
		return -1, errors.New(fmt.Sprintf("invalid port: %v - %s", env, err.Error()))
	} else if port < 1 || port > 65535 {
		die("invalid port: %s", env)
		return -1, errors.New(fmt.Sprintf("invalid port: %v - %s", env, err.Error()))
	}
	return port, nil
}
<|endoftext|>