<commit_before>package main

import (
	"bytes"
	"fmt"
	"net"
	"os"
	"path/filepath"
	"regexp"
	"runtime"

	toml "github.com/BurntSushi/toml"
	"github.com/boot2docker/boot2docker-cli/driver"
	flag "github.com/ogier/pflag"
)

var (
	// Pattern to parse a key=value line in config profile.
	reFlagLine = regexp.MustCompile(`^\s*(\w+)\s*=\s*([^#;]+)`)
	B2D        = driver.MachineConfig{}
)

func homeDir() (string, error) {
	dir := ""

	// *nix and MSYS Windows
	if dir = os.Getenv("HOME"); dir == "" {
		// Windows (if not running under MSYS)
		dir = os.Getenv("USERPROFILE")
	}
	if _, err := os.Stat(dir); err != nil {
		return "", err
	}

	return dir, nil
}

func cfgDir(name string) (string, error) {
	if name == ".boot2docker" {
		if b2dDir := os.Getenv("BOOT2DOCKER_DIR"); b2dDir != "" {
			return b2dDir, nil
		}
	}

	dir, err := homeDir()
	if err != nil {
		return "", err
	}
	dir = filepath.Join(dir, name)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return "", err
	}
	return dir, nil
}

func cfgFilename(dir string) string {
	filename := os.Getenv("BOOT2DOCKER_PROFILE")
	if filename == "" {
		filename = filepath.Join(dir, "profile")
	}
	return filename
}

// Write configuration set by the combination of profile and flags
//    Should result in a format that can be piped into a profile file
func printConfig() string {
	var buf bytes.Buffer
	e := toml.NewEncoder(&buf)
	err := e.Encode(B2D)
	if err != nil {
		return ""
	}
	return buf.String()
}

// Read configuration from both profile and flags. Flags override profile.
func config() (*flag.FlagSet, error) {
	dir, err := cfgDir(".boot2docker")
	if err != nil {
		return nil, fmt.Errorf("failed to get boot2docker directory: %s", err)
	}

	flags := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
	flags.Usage = func() { usageLong(flags) }

	// Find out which driver we're usng and add its flags
	flags.StringVar(&B2D.Driver, "driver", "virtualbox", "hypervisor driver.")
	flags.BoolVarP(&B2D.Verbose, "verbose", "v", false, "display verbose command invocations.")

	if err := flags.Parse([]string{}); err != nil {
		return nil, err
	}
	// Over-ride from the profile file
	filename := cfgFilename(B2D.Dir)
	if _, err := os.Lstat(filename); err == nil {
		if _, err := toml.DecodeFile(filename, &B2D); err != nil {
			return nil, err
		}
	}
	// for cmd==ssh only:
	// only pass the params up to and including the `ssh` command - after that,
	// there might be other -flags that are destined for the ssh cmd
	sshIdx := 1
	for sshIdx < len(os.Args) && os.Args[sshIdx-1] != "ssh" {
		sshIdx++
	}
	// Command-line overrides profile config.
	if err := flags.Parse(os.Args[1:sshIdx]); err != nil {
		return nil, err
	}
	if B2D.Verbose {
		fmt.Printf("Using %s driver\n", B2D.Driver)
	}
	driver.ConfigFlags(&B2D, flags)

	// Add the generic flags

	flags.StringVar(&B2D.VM, "vm", "boot2docker-vm", "virtual machine name.")
	// removed for now, requires re-parsing a new config file which is too messy
	//flags.StringVarP(&B2D.Dir, "dir", "d", dir, "boot2docker config directory.")
	B2D.Dir = dir
	flags.StringVar(&B2D.ISO, "iso", filepath.Join(dir, "boot2docker.iso"), "path to boot2docker ISO image.")

	// Sven disabled this, as it is broken - if I user with a fresh computer downloads
	// just the boot2docker-cli, and then runs `boot2docker --init ip`, we create a vm
	// which cannot run, because it fails to have have the boot2docker.iso and the ssh keys
	B2D.Init = false
	//flags.BoolVarP(&B2D.Init, "init", "i", false, "auto initialize vm instance.")

	flags.StringVar(&B2D.SSH, "ssh", "ssh", "path to SSH client utility.")
	flags.StringVar(&B2D.SSHGen, "ssh-keygen", "ssh-keygen", "path to ssh-keygen utility.")

	sshdir, _ := cfgDir(".ssh")
	flags.StringVar(&B2D.SSHKey, "sshkey", filepath.Join(sshdir, "id_boot2docker"), "path to SSH key to use.")
	flags.UintVarP(&B2D.DiskSize, "disksize", "s", 20000, "boot2docker disk image size (in MB).")
	flags.UintVarP(&B2D.Memory, "memory", "m", 2048, "virtual machine memory size (in MB).")
	flags.Uint16Var(&B2D.SSHPort, "sshport", 2022, "host SSH port (forward to port 22 in VM).")
	flags.Uint16Var(&B2D.DockerPort, "dockerport", 0, "host Docker port (forward to port 2375 in VM). (deprecated - use with care)")
	flags.IPVar(&B2D.HostIP, "hostip", net.ParseIP("192.168.59.3"), "VirtualBox host-only network IP address.")
	flags.IPMaskVar(&B2D.NetMask, "netmask", flag.ParseIPv4Mask("255.255.255.0"), "VirtualBox host-only network mask.")
	flags.BoolVar(&B2D.DHCPEnabled, "dhcp", true, "enable VirtualBox host-only network DHCP.")
	flags.IPVar(&B2D.DHCPIP, "dhcpip", net.ParseIP("192.168.59.99"), "VirtualBox host-only network DHCP server address.")
	flags.IPVar(&B2D.LowerIP, "lowerip", net.ParseIP("192.168.59.103"), "VirtualBox host-only network DHCP lower bound.")
	flags.IPVar(&B2D.UpperIP, "upperip", net.ParseIP("192.168.59.254"), "VirtualBox host-only network DHCP upper bound.")

	if runtime.GOOS != "windows" {
		//SerialFile ~~ filepath.Join(dir, B2D.vm+".sock")
		flags.StringVar(&B2D.SerialFile, "serialfile", "", "path to the serial socket/pipe.")
		flags.BoolVar(&B2D.Serial, "serial", false, "try serial console to get IP address (experimental)")
	} else {
		B2D.Serial = false
	}

	// Set the defaults
	if err := flags.Parse([]string{}); err != nil {
		return nil, err
	}
	// Over-ride from the profile file
	if _, err := os.Lstat(filename); err == nil {
		if _, err := toml.DecodeFile(filename, &B2D); err != nil {
			return nil, err
		}
	}

	// Command-line overrides profile config.
	if err := flags.Parse(os.Args[1:sshIdx]); err != nil {
		return nil, err
	}

	if B2D.SerialFile == "" {
		if runtime.GOOS == "windows" {
			//SerialFile ~~ filepath.Join(dir, B2D.vm+".sock")
			B2D.SerialFile = `\\.\pipe\` + B2D.VM
		} else {
			B2D.SerialFile = filepath.Join(dir, B2D.VM+".sock")
		}
	}

	return flags, nil
}

func usageShort() {
	fmt.Fprintf(os.Stderr, "Usage: %s [<options>] {help|init|up|ssh|save|down|poweroff|reset|restart|config|status|info|ip|socket|shellinit|delete|destroy|download|version} [<args>]\n", os.Args[0])
}

func usageLong(flags *flag.FlagSet) {
	// NOTE: the help message uses spaces, not tabs for indentation!
	fmt.Fprintf(os.Stderr, `Usage: %s [<options>] <command> [<args>]

Boot2Docker management utility.

Commands:
   init                Create a new Boot2Docker VM.
   up|start|boot       Start VM from any states.
   ssh [ssh-command]   Login to VM via SSH.
   save|suspend        Suspend VM and save state to disk.
   down|stop|halt      Gracefully shutdown the VM.
   restart             Gracefully reboot the VM.
   poweroff            Forcefully power off the VM (may corrupt disk image).
   reset               Forcefully power cycle the VM (may corrupt disk image).
   delete|destroy      Delete Boot2Docker VM and its disk image.
   config|cfg          Show selected profile file settings.
   info                Display detailed information of VM.
   ip                  Display the IP address of the VM's Host-only network.
   socket              Display the DOCKER_HOST socket to connect to.
   shellinit           Display the shell command to set up the Docker client.
   status              Display current state of VM.
   download            Download Boot2Docker ISO image.
   upgrade             Upgrade the Boot2Docker ISO image (restart if running).
   version             Display version information.

Options:
`, os.Args[0])
	flags.PrintDefaults()
}
<commit_msg>This makes cli arguments other than verbose and driver function again.<commit_after>package main

import (
	"bytes"
	"fmt"
	"net"
	"os"
	"path/filepath"
	"regexp"
	"runtime"

	toml "github.com/BurntSushi/toml"
	"github.com/boot2docker/boot2docker-cli/driver"
	flag "github.com/ogier/pflag"
)

var (
	// Pattern to parse a key=value line in config profile.
	reFlagLine = regexp.MustCompile(`^\s*(\w+)\s*=\s*([^#;]+)`)
	B2D        = driver.MachineConfig{}
)

func homeDir() (string, error) {
	dir := ""

	// *nix and MSYS Windows
	if dir = os.Getenv("HOME"); dir == "" {
		// Windows (if not running under MSYS)
		dir = os.Getenv("USERPROFILE")
	}
	if _, err := os.Stat(dir); err != nil {
		return "", err
	}

	return dir, nil
}

func cfgDir(name string) (string, error) {
	if name == ".boot2docker" {
		if b2dDir := os.Getenv("BOOT2DOCKER_DIR"); b2dDir != "" {
			return b2dDir, nil
		}
	}

	dir, err := homeDir()
	if err != nil {
		return "", err
	}
	dir = filepath.Join(dir, name)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return "", err
	}
	return dir, nil
}

func cfgFilename(dir string) string {
	filename := os.Getenv("BOOT2DOCKER_PROFILE")
	if filename == "" {
		filename = filepath.Join(dir, "profile")
	}
	return filename
}

// Write configuration set by the combination of profile and flags
//    Should result in a format that can be piped into a profile file
func printConfig() string {
	var buf bytes.Buffer
	e := toml.NewEncoder(&buf)
	err := e.Encode(B2D)
	if err != nil {
		return ""
	}
	return buf.String()
}

// Read configuration from both profile and flags. Flags override profile.
func config() (*flag.FlagSet, error) {
	dir, err := cfgDir(".boot2docker")
	if err != nil {
		return nil, fmt.Errorf("failed to get boot2docker directory: %s", err)
	}

	flags := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
	flags.Usage = func() { usageLong(flags) }

	// Find out which driver we're usng and add its flags
	flags.StringVar(&B2D.Driver, "driver", "virtualbox", "hypervisor driver.")
	flags.BoolVarP(&B2D.Verbose, "verbose", "v", false, "display verbose command invocations.")

	if err := flags.Parse([]string{}); err != nil {
		return nil, err
	}
	// Over-ride from the profile file
	filename := cfgFilename(B2D.Dir)
	if _, err := os.Lstat(filename); err == nil {
		if _, err := toml.DecodeFile(filename, &B2D); err != nil {
			return nil, err
		}
	}
	if B2D.Verbose {
		fmt.Printf("Using %s driver\n", B2D.Driver)
	}
	driver.ConfigFlags(&B2D, flags)

	// Add the generic flags

	flags.StringVar(&B2D.VM, "vm", "boot2docker-vm", "virtual machine name.")
	// removed for now, requires re-parsing a new config file which is too messy
	//flags.StringVarP(&B2D.Dir, "dir", "d", dir, "boot2docker config directory.")
	B2D.Dir = dir
	flags.StringVar(&B2D.ISO, "iso", filepath.Join(dir, "boot2docker.iso"), "path to boot2docker ISO image.")

	// Sven disabled this, as it is broken - if I user with a fresh computer downloads
	// just the boot2docker-cli, and then runs `boot2docker --init ip`, we create a vm
	// which cannot run, because it fails to have have the boot2docker.iso and the ssh keys
	B2D.Init = false
	//flags.BoolVarP(&B2D.Init, "init", "i", false, "auto initialize vm instance.")

	flags.StringVar(&B2D.SSH, "ssh", "ssh", "path to SSH client utility.")
	flags.StringVar(&B2D.SSHGen, "ssh-keygen", "ssh-keygen", "path to ssh-keygen utility.")

	sshdir, _ := cfgDir(".ssh")
	flags.StringVar(&B2D.SSHKey, "sshkey", filepath.Join(sshdir, "id_boot2docker"), "path to SSH key to use.")
	flags.UintVarP(&B2D.DiskSize, "disksize", "s", 20000, "boot2docker disk image size (in MB).")
	flags.UintVarP(&B2D.Memory, "memory", "m", 2048, "virtual machine memory size (in MB).")
	flags.Uint16Var(&B2D.SSHPort, "sshport", 2022, "host SSH port (forward to port 22 in VM).")
	flags.Uint16Var(&B2D.DockerPort, "dockerport", 0, "host Docker port (forward to port 2375 in VM). (deprecated - use with care)")
	flags.IPVar(&B2D.HostIP, "hostip", net.ParseIP("192.168.59.3"), "VirtualBox host-only network IP address.")
	flags.IPMaskVar(&B2D.NetMask, "netmask", flag.ParseIPv4Mask("255.255.255.0"), "VirtualBox host-only network mask.")
	flags.BoolVar(&B2D.DHCPEnabled, "dhcp", true, "enable VirtualBox host-only network DHCP.")
	flags.IPVar(&B2D.DHCPIP, "dhcpip", net.ParseIP("192.168.59.99"), "VirtualBox host-only network DHCP server address.")
	flags.IPVar(&B2D.LowerIP, "lowerip", net.ParseIP("192.168.59.103"), "VirtualBox host-only network DHCP lower bound.")
	flags.IPVar(&B2D.UpperIP, "upperip", net.ParseIP("192.168.59.254"), "VirtualBox host-only network DHCP upper bound.")

	if runtime.GOOS != "windows" {
		//SerialFile ~~ filepath.Join(dir, B2D.vm+".sock")
		flags.StringVar(&B2D.SerialFile, "serialfile", "", "path to the serial socket/pipe.")
		flags.BoolVar(&B2D.Serial, "serial", false, "try serial console to get IP address (experimental)")
	} else {
		B2D.Serial = false
	}

	// Set the defaults
	if err := flags.Parse([]string{}); err != nil {
		return nil, err
	}
	// Over-ride from the profile file
	if _, err := os.Lstat(filename); err == nil {
		if _, err := toml.DecodeFile(filename, &B2D); err != nil {
			return nil, err
		}
	}

	if B2D.SerialFile == "" {
		if runtime.GOOS == "windows" {
			//SerialFile ~~ filepath.Join(dir, B2D.vm+".sock")
			B2D.SerialFile = `\\.\pipe\` + B2D.VM
		} else {
			B2D.SerialFile = filepath.Join(dir, B2D.VM+".sock")
		}
	}
	// for cmd==ssh only:
	// only pass the params up to and including the `ssh` command - after that,
	// there might be other -flags that are destined for the ssh cmd
	sshIdx := 1
	for sshIdx < len(os.Args) && os.Args[sshIdx-1] != "ssh" {
		sshIdx++
	}
	// Command-line overrides profile config.
	if err := flags.Parse(os.Args[1:sshIdx]); err != nil {
		return nil, err
	}

	return flags, nil
}

func usageShort() {
	fmt.Fprintf(os.Stderr, "Usage: %s [<options>] {help|init|up|ssh|save|down|poweroff|reset|restart|config|status|info|ip|socket|shellinit|delete|destroy|download|version} [<args>]\n", os.Args[0])
}

func usageLong(flags *flag.FlagSet) {
	// NOTE: the help message uses spaces, not tabs for indentation!
	fmt.Fprintf(os.Stderr, `Usage: %s [<options>] <command> [<args>]

Boot2Docker management utility.

Commands:
   init                Create a new Boot2Docker VM.
   up|start|boot       Start VM from any states.
   ssh [ssh-command]   Login to VM via SSH.
   save|suspend        Suspend VM and save state to disk.
   down|stop|halt      Gracefully shutdown the VM.
   restart             Gracefully reboot the VM.
   poweroff            Forcefully power off the VM (may corrupt disk image).
   reset               Forcefully power cycle the VM (may corrupt disk image).
   delete|destroy      Delete Boot2Docker VM and its disk image.
   config|cfg          Show selected profile file settings.
   info                Display detailed information of VM.
   ip                  Display the IP address of the VM's Host-only network.
   socket              Display the DOCKER_HOST socket to connect to.
   shellinit           Display the shell command to set up the Docker client.
   status              Display current state of VM.
   download            Download Boot2Docker ISO image.
   upgrade             Upgrade the Boot2Docker ISO image (restart if running).
   version             Display version information.

Options:
`, os.Args[0])
	flags.PrintDefaults()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"io/ioutil"
)

type Rule struct {
	Path    string
	Run     string
	Pattern string
}

func ParseConfig(path string) (rules []*Rule, err error) {
	rules = make([]*Rule, 0)

	c, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	var f interface{}
	err = json.Unmarshal(c, &f)
	if err != nil {
		return nil, err
	}

	config := f.(map[string]interface{})
	for path, v := range config {
		for _, v = range v.([]interface{}) {
			attributes := v.(map[string]interface{})
			run := attributes["run"].(string)
			pattern := attributes["pattern"].(string)
			path = stripTrailingSlash(path)
			rules = append(rules, &Rule{path, run, pattern})
		}
	}

	return rules, nil
}

func stripTrailingSlash(path string) string {
	for len(path) > 0 && path[len(path)-1] == '/' {
		path = path[0 : len(path)-1]
	}
	return path
}
<commit_msg>Refactor config parsing<commit_after>package main

import (
	"encoding/json"
	"io/ioutil"
)

type Rule struct {
	Path    string
	Run     string
	Pattern string
}

func ParseConfig(configpath string) (rules []*Rule, err error) {
	rules = make([]*Rule, 0)

	c, err := ioutil.ReadFile(configpath)
	if err != nil {
		return nil, err
	}

	var f interface{}
	err = json.Unmarshal(c, &f)
	if err != nil {
		return nil, err
	}

	paths := f.(map[string]interface{})
	for path, pathRules := range paths {
		for _, ruleAttributes := range pathRules.([]interface{}) {
			attributes := ruleAttributes.(map[string]interface{})
			rules = append(rules, attributesToRule(path, attributes))
		}
	}

	return rules, nil
}

func attributesToRule(p string, attr map[string]interface{}) *Rule {
	run := attr["run"].(string)
	pattern := attr["pattern"].(string)

	return &Rule{stripTrailingSlash(p), run, pattern}
}

func stripTrailingSlash(path string) string {
	for len(path) > 0 && path[len(path)-1] == '/' {
		path = path[0 : len(path)-1]
	}
	return path
}
<|endoftext|>


<commit_before>package config

import (
	"os"
	"log"
	"os/signal"
	"syscall"
	"bufio"
	"bytes"
	"reflect"
	"errors"
	"strings"
	"strconv"
	"fmt"
	"path/filepath"
)

type Config struct {
	filename string
	data interface{}
	queue []reflect.Value
	current reflect.Value
	searchVal bool
	searchKey bool
	inBlock int
	inInclude int
	canSkip bool
	skip bool
	bkMulti int
	mapKey reflect.Value
	setVar bool
	searchVar bool
	searchVarBlock bool
	vars []map[string]string
	currentVar map[string]string
}

func New(filename string) *Config {
	return &Config{filename:filename}
}

func (this *Config) Unmarshal(v interface{}) error{
	rev := reflect.ValueOf(v)
	if rev.Kind() != reflect.Ptr {
		err := errors.New("non-pointer passed to Unmarshal")
		return err
	}
	this.data = v
	this.current = rev.Elem()
	this.inSearchKey()
	this.inBlock = 0
	this.inInclude = 0
	this.currentVar = make(map[string]string)
	this.searchVar = false
	this.setVar = false
	this.searchVarBlock = false
	return this.parse()
}

func (this *Config) Reload() error {
	return this.parse()
}

func (this *Config) watch() {
	l := log.New(os.Stderr, "", 0)
	sighup := make(chan os.Signal, 1)
	signal.Notify(sighup, syscall.SIGHUP)
	for {
		<-sighup
		l.Println("Caught SIGHUP, reloading config...")
		this.parse()
	}
}

func (this *Config) parse() error {
	var err error
	var s bytes.Buffer
	var vs bytes.Buffer
	var vsb bytes.Buffer
	if _, err = os.Stat(this.filename);os.IsNotExist(err) {
		return err
	}

	var fp *os.File
	fp, err = os.Open(this.filename)
	if err != nil {
		return err
	}
	defer fp.Close()

	reader := bufio.NewReader(fp)
	var b byte
	for err == nil {
		b, err = reader.ReadByte()
		if err == bufio.ErrBufferFull {
			return nil
		}

		if this.canSkip && b == '#' {
			reader.ReadLine()
			continue
		}
		if this.canSkip && b == '/' {
			if this.skip {
				reader.ReadLine()
				this.skip = false
				continue
			}
			this.skip = true
			continue
		}
		if this.searchKey {
			if b == ' ' || b == '\r' || b == '\n' || b == '\t' {
				if s.Len() > 0 {
					this.inSearchVal()
					if strings.Compare(s.String(), "include") == 0 {
						s.Reset()
						this.inInclude++
						if this.inInclude > 100 {
							return errors.New("too many include, exceeds 100 limit!")
						}
						continue
					}else if strings.Compare(s.String(), "set") == 0 {
						s.Reset()
						this.setVar = true
						continue
					}
					this.getElement(s.String())
					s.Reset()
				}
				continue
			}
		}

		if b == '{' && !this.searchVar && vs.Len() == 0 {
			if err := this.createBlock(&s); err != nil {
				return err
			}
			continue
		}

		if this.searchKey && b == '}' && this.inBlock > 0 {
			this.closeBlock(&s)
			continue
		}

		if this.searchVal {
			if b == '$' {
				this.searchVar = true
				vs.Reset()
				vsb.Reset()
				vsb.WriteByte(b)
				continue;
			}

			if this.searchVar {
				if b == '{' {
					if vs.Len() == 0 {
						this.searchVarBlock = true
						vsb.WriteByte(b)
						continue
					}
					if !this.searchVarBlock {
						if !this.replace(&s, &vs) {
							s.Write(vsb.Bytes())
						}

						// is block?
						if err := this.createBlock(&s);err != nil {
							return err
						}else{
							continue
						}
					}
				} // if b == '{'
				if b == ' ' || b == '\r' || b == '\n' || b == '\t' {
					if !this.replace(&s, &vs) {
						s.Write(vsb.Bytes())
					}
				}
			}

			if this.searchVarBlock && b == '}' {
				this.searchVarBlock = false
			    // 判定是不是有值
				this.searchVar = false
				if !this.replace(&s, &vs) {
					vsb.WriteByte(b)
					s.Write(vsb.Bytes())
				}
				continue
			}

			if b == ';' {
				//	这里是要处理数据到this.current
				this.inSearchKey()
				if this.searchVar {
					if this.searchVarBlock {
						return errors.New(vsb.String() + " is not terminated by }")
					}
					this.searchVar = false
					this.searchVarBlock = false
					if !this.replace(&s, &vs) {
						s.Write(vsb.Bytes())
					}
				}

				// set to map
				if this.setVar {
					sf := strings.Fields(s.String())
					if len(sf) != 2 {
						return errors.New("Invalid Config")
					}
					this.currentVar[sf[0]] = sf[1]
					this.setVar = false
					s.Reset()
					continue
				}

				if this.inInclude > 0 {
					this.filename = strings.TrimSpace(s.String())
					s.Reset()
					this.inInclude--
					files, err := filepath.Glob(this.filename)
					if err != nil{
						return err
					}
					for _,file := range files {
						this.filename = file
						if err := this.parse(); err != nil {
							return err
						}
					}
					continue
				}

				err := this.set(s.String())
				if err != nil {
					return err
				}

				s.Reset()
				this.popElement()
				continue
			}else if(this.searchVar) { // if b == ';'
				vs.WriteByte(b)
				vsb.WriteByte(b)
				if ! this.searchVarBlock{
					this.replace(&s, &vs)
				}
				continue
			}
		}

		s.WriteByte(b)
	}

	if !this.searchKey && this.inBlock > 0 {
		return errors.New("Invalid config file!")
	}

	return nil
}

func (this *Config) set(s string) error {
	s = strings.TrimSpace(s)
	if this.current.Kind() == reflect.Ptr {
		if this.current.IsNil() {
			this.current.Set(reflect.New(this.current.Type().Elem()))
		}
		this.current = this.current.Elem()
	}

	switch this.current.Kind() {
	case reflect.String:
		this.current.SetString(s)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		itmp,err := strconv.ParseInt(s, 10, this.current.Type().Bits())
		if  err != nil {
			return err
		}
		this.current.SetInt(itmp)
	case  reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		itmp,err := strconv.ParseUint(s, 10, this.current.Type().Bits())
		if  err != nil {
			return err
		}
		this.current.SetUint(itmp)
	case reflect.Float32, reflect.Float64:
		ftmp, err := strconv.ParseFloat(s, this.current.Type().Bits())
		if err != nil {
			return err
		}
		this.current.SetFloat(ftmp)
	case reflect.Bool:
		if s == "yes" || s == "on" {
			this.current.SetBool(true)
		}else if s == "no" || s == "off" {
			this.current.SetBool(false)
		}else{
			btmp, err := strconv.ParseBool(s)
			if err != nil {
				return err
			}
			this.current.SetBool(btmp)
		}
	case reflect.Slice:
		sf := strings.Fields(s)
		for _,sv := range sf {
			n := this.current.Len()
			this.current.Set(reflect.Append(this.current, reflect.Zero(this.current.Type().Elem())))
			this.pushElement(this.current.Index(n))
			this.set(sv)
			this.popElement()
		}
	case reflect.Map:
		if this.current.IsNil() {
			this.current.Set(reflect.MakeMap(this.current.Type()))
		}

		sf := strings.Fields(s)
		if len(sf) != 2 {
			return errors.New("Invalid Config")
		}
		var v reflect.Value
		v=reflect.New(this.current.Type().Key())
		this.pushElement(v)
		this.set(sf[0])
		key := this.current
		this.popElement()
		v = reflect.New(this.current.Type().Elem())
		this.pushElement(v)
		this.set(sf[1])
		val := this.current
		this.popElement()

		this.current.SetMapIndex(key, val)
	default:
		return errors.New(fmt.Sprintf("Invalid Type:%s", this.current.Kind()))
	}
	return nil
}

func (this *Config) replace(s *bytes.Buffer, vs *bytes.Buffer) bool {
	if vs.Len() == 0 {
		return false
	}

	for k,v := range this.currentVar{
		if strings.Compare(k, vs.String()) == 0 {
			// found
			this.searchVar = false
			s.WriteString(v)
			vs.Reset()
			return true
		}
	}

	for i:= len(this.vars) - 1; i >= 0 ;i-- {
		for k, v := range this.vars[i] {
			if strings.Compare(k, vs.String()) == 0 {
				s.WriteString(v)
				this.searchVar = false
				vs.Reset()
				return true
			}
		}
	}

	return false
}

func (this *Config) createBlock(s *bytes.Buffer) error {
	// fixed { be close to key like server{
	if this.searchKey && s.Len() > 0 {
		this.getElement(s.String())
		s.Reset()
		this.inSearchVal()
	}

	// vars
	vars := make(map[string]string)
	this.vars = append(this.vars, this.currentVar)
	this.currentVar = vars

	this.inBlock++
	//	这里说明了可能是切片或者map
	if this.searchVal && s.Len() > 0 && this.current.Kind() == reflect.Map {
		this.bkMulti++
		if this.current.IsNil() {
			this.current.Set(reflect.MakeMap(this.current.Type()))
		}
		var v reflect.Value
		v = reflect.New(this.current.Type().Key())
		this.pushElement(v)
		err := this.set(s.String())
		if err != nil {
			return err
		}
		this.mapKey = this.current
		this.popElement()
		val := reflect.New(this.current.Type().Elem())
		this.pushElement(val)
	}

	if this.current.Kind() == reflect.Slice {
		this.bkMulti++
		n := this.current.Len()
		if this.current.Type().Elem().Kind() == reflect.Ptr {
			this.current.Set(reflect.Append(this.current, reflect.New(this.current.Type().Elem().Elem())))
		}else{
			this.current.Set(reflect.Append(this.current, reflect.Zero(this.current.Type().Elem())))
		}
		this.pushElement(this.current.Index(n))
	}
	this.inSearchKey()
	s.Reset()
	return nil
}

func (this *Config) closeBlock(s *bytes.Buffer) {
	if this.bkMulti > 0{
		this.bkMulti--
		val := this.current
		this.popElement()
		if this.current.Kind() == reflect.Map {
			this.current.SetMapIndex(this.mapKey, val)
		}
	}

	// vars
	this.currentVar = this.vars[len(this.vars) - 1]
	this.vars = this.vars[:len(this.vars) - 1]

	this.inBlock--
	this.popElement()
	this.inSearchKey()
}

func (this *Config) inSearchKey() {
	this.searchVal = false
	this.searchKey = true
	this.canSkip = true
}

func (this *Config) inSearchVal() {
	this.searchKey = false
	this.searchVal = true
	this.canSkip = false
}

func (this *Config) getElement(s string){
	s = strings.TrimSpace(s)
	if this.current.Kind() == reflect.Ptr {
		if this.current.IsNil() {
			this.current.Set(reflect.New(this.current.Type().Elem()))
		}
		this.current = this.current.Elem()
	}
	this.queue = append(this.queue, this.current)
	this.current = this.current.FieldByName(strings.Title(s))
}

func (this *Config) pushElement(v reflect.Value) {
	this.queue = append(this.queue, this.current)
	this.current = v
}

func (this *Config) popElement() {
	this.current = this.queue[len(this.queue) - 1]
	this.queue = this.queue[:len(this.queue) - 1]
}
<commit_msg>opt:delimiter,clear<commit_after>package config

import (
	"os"
	"bufio"
	"bytes"
	"reflect"
	"errors"
	"strings"
	"strconv"
	"fmt"
	"path/filepath"
	"unicode"
)

type Config struct {
	filename string
	queue []reflect.Value
	current reflect.Value
	searchVal bool
	searchKey bool
	inBlock int
	inInclude int
	canSkip bool
	skip bool
	bkMulti int
	mapKey reflect.Value
	setVar bool
	searchVar bool
	searchVarBlock bool
	vars []map[string]string
	currentVar map[string]string
}

func New(filename string) *Config {
	return &Config{filename:filename}
}

func (this *Config) Unmarshal(v interface{}) error{
	rev := reflect.ValueOf(v)
	if rev.Kind() != reflect.Ptr {
		err := errors.New("non-pointer passed to Unmarshal")
		return err
	}
	this.current = rev.Elem()
	this.inSearchKey()
	this.inBlock = 0
	this.inInclude = 0
	this.currentVar = make(map[string]string)
	this.searchVar = false
	this.setVar = false
	this.searchVarBlock = false
	return this.parse()
}

func (this *Config) Reload() error {
	return this.parse()
}

func (this *Config) parse() error {
	var err error
	var s bytes.Buffer
	var vs bytes.Buffer
	var vsb bytes.Buffer
	if _, err = os.Stat(this.filename);os.IsNotExist(err) {
		return err
	}

	var fp *os.File
	fp, err = os.Open(this.filename)
	if err != nil {
		return err
	}
	defer fp.Close()

	reader := bufio.NewReader(fp)
	var b byte
	for err == nil {
		b, err = reader.ReadByte()
		if err == bufio.ErrBufferFull {
			return nil
		}

		if this.canSkip && b == '#' {
			reader.ReadLine()
			continue
		}
		if this.canSkip && b == '/' {
			if this.skip {
				reader.ReadLine()
				this.skip = false
				continue
			}
			this.skip = true
			continue
		}
		if this.searchKey {
			if this.delimiter(b) {
				if s.Len() > 0 {
					this.inSearchVal()
					if strings.Compare(s.String(), "include") == 0 {
						s.Reset()
						this.inInclude++
						if this.inInclude > 100 {
							return errors.New("too many include, exceeds 100 limit!")
						}
						continue
					}else if strings.Compare(s.String(), "set") == 0 {
						s.Reset()
						this.setVar = true
						continue
					}
					this.getElement(s.String())
					s.Reset()
				}
				continue
			}
		}

		if b == '{' && !this.searchVar && vs.Len() == 0 {
			if err := this.createBlock(&s); err != nil {
				return err
			}
			continue
		}

		if this.searchKey && b == '}' && this.inBlock > 0 {
			this.closeBlock(&s)
			continue
		}

		if this.searchVal {
			if b == '$' {
				this.searchVar = true
				vs.Reset()
				vsb.Reset()
				vsb.WriteByte(b)
				continue;
			}

			if this.searchVar {
				if b == '{' {
					if vs.Len() == 0 {
						this.searchVarBlock = true
						vsb.WriteByte(b)
						continue
					}
					if !this.searchVarBlock {
						if !this.replace(&s, &vs) {
							s.Write(vsb.Bytes())
						}

						// is block?
						if err := this.createBlock(&s);err != nil {
							return err
						}else{
							continue
						}
					}
				} // if b == '{'
				// Is space
				if this.delimiter(b) {
					if !this.replace(&s, &vs) {
						s.Write(vsb.Bytes())
					}
				}
			}

			if this.searchVarBlock && b == '}' {
				this.searchVarBlock = false
			    // 判定是不是有值
				this.searchVar = false
				if !this.replace(&s, &vs) {
					vsb.WriteByte(b)
					s.Write(vsb.Bytes())
				}
				continue
			}

			if b == ';' {
				//	这里是要处理数据到this.current
				this.inSearchKey()
				if this.searchVar {
					if this.searchVarBlock {
						return errors.New(vsb.String() + " is not terminated by }")
					}
					this.searchVar = false
					this.searchVarBlock = false
					if !this.replace(&s, &vs) {
						s.Write(vsb.Bytes())
					}
				}

				// set to map
				if this.setVar {
					sf := strings.Fields(s.String())
					if len(sf) != 2 {
						return errors.New("Invalid Config")
					}
					this.currentVar[sf[0]] = sf[1]
					this.setVar = false
					s.Reset()
					continue
				}

				if this.inInclude > 0 {
					this.filename = strings.TrimSpace(s.String())
					s.Reset()
					this.inInclude--
					files, err := filepath.Glob(this.filename)
					if err != nil{
						return err
					}
					for _,file := range files {
						this.filename = file
						if err := this.parse(); err != nil {
							return err
						}
					}
					continue
				}

				err := this.set(s.String())
				if err != nil {
					return err
				}

				s.Reset()
				this.popElement()
				continue
			}else if(this.searchVar) { // if b == ';'
				vs.WriteByte(b)
				vsb.WriteByte(b)
				if ! this.searchVarBlock{
					this.replace(&s, &vs)
				}
				continue
			}
		}

		s.WriteByte(b)
	}

	if !this.searchKey && this.inBlock > 0 {
		return errors.New("Invalid config file!")
	}

	return nil
}

func (this *Config) set(s string) error {
	s = strings.TrimSpace(s)
	if this.current.Kind() == reflect.Ptr {
		if this.current.IsNil() {
			this.current.Set(reflect.New(this.current.Type().Elem()))
		}
		this.current = this.current.Elem()
	}

	switch this.current.Kind() {
	case reflect.String:
		this.current.SetString(s)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		itmp,err := strconv.ParseInt(s, 10, this.current.Type().Bits())
		if  err != nil {
			return err
		}
		this.current.SetInt(itmp)
	case  reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		itmp,err := strconv.ParseUint(s, 10, this.current.Type().Bits())
		if  err != nil {
			return err
		}
		this.current.SetUint(itmp)
	case reflect.Float32, reflect.Float64:
		ftmp, err := strconv.ParseFloat(s, this.current.Type().Bits())
		if err != nil {
			return err
		}
		this.current.SetFloat(ftmp)
	case reflect.Bool:
		if s == "yes" || s == "on" {
			this.current.SetBool(true)
		}else if s == "no" || s == "off" {
			this.current.SetBool(false)
		}else{
			btmp, err := strconv.ParseBool(s)
			if err != nil {
				return err
			}
			this.current.SetBool(btmp)
		}
	case reflect.Slice:
		sf := strings.Fields(s)
		for _,sv := range sf {
			n := this.current.Len()
			this.current.Set(reflect.Append(this.current, reflect.Zero(this.current.Type().Elem())))
			this.pushElement(this.current.Index(n))
			this.set(sv)
			this.popElement()
		}
	case reflect.Map:
		if this.current.IsNil() {
			this.current.Set(reflect.MakeMap(this.current.Type()))
		}

		sf := strings.Fields(s)
		if len(sf) != 2 {
			return errors.New("Invalid Config")
		}
		var v reflect.Value
		v=reflect.New(this.current.Type().Key())
		this.pushElement(v)
		this.set(sf[0])
		key := this.current
		this.popElement()
		v = reflect.New(this.current.Type().Elem())
		this.pushElement(v)
		this.set(sf[1])
		val := this.current
		this.popElement()

		this.current.SetMapIndex(key, val)
	default:
		return errors.New(fmt.Sprintf("Invalid Type:%s", this.current.Kind()))
	}
	return nil
}

func (this *Config) replace(s *bytes.Buffer, vs *bytes.Buffer) bool {
	if vs.Len() == 0 {
		return false
	}

	for k,v := range this.currentVar{
		if strings.Compare(k, vs.String()) == 0 {
			// found
			this.searchVar = false
			s.WriteString(v)
			vs.Reset()
			return true
		}
	}

	for i:= len(this.vars) - 1; i >= 0 ;i-- {
		for k, v := range this.vars[i] {
			if strings.Compare(k, vs.String()) == 0 {
				s.WriteString(v)
				this.searchVar = false
				vs.Reset()
				return true
			}
		}
	}

	return false
}

func (this *Config) createBlock(s *bytes.Buffer) error {
	// fixed { be close to key like server{
	if this.searchKey && s.Len() > 0 {
		this.getElement(s.String())
		s.Reset()
		this.inSearchVal()
	}

	// vars
	vars := make(map[string]string)
	this.vars = append(this.vars, this.currentVar)
	this.currentVar = vars

	this.inBlock++
	//	这里说明了可能是切片或者map
	if this.searchVal && s.Len() > 0 && this.current.Kind() == reflect.Map {
		this.bkMulti++
		if this.current.IsNil() {
			this.current.Set(reflect.MakeMap(this.current.Type()))
		}
		var v reflect.Value
		v = reflect.New(this.current.Type().Key())
		this.pushElement(v)
		err := this.set(s.String())
		if err != nil {
			return err
		}
		this.mapKey = this.current
		this.popElement()
		val := reflect.New(this.current.Type().Elem())
		this.pushElement(val)
	}

	if this.current.Kind() == reflect.Slice {
		this.bkMulti++
		n := this.current.Len()
		if this.current.Type().Elem().Kind() == reflect.Ptr {
			this.current.Set(reflect.Append(this.current, reflect.New(this.current.Type().Elem().Elem())))
		}else{
			this.current.Set(reflect.Append(this.current, reflect.Zero(this.current.Type().Elem())))
		}
		this.pushElement(this.current.Index(n))
	}
	this.inSearchKey()
	s.Reset()
	return nil
}

func (this *Config) closeBlock(s *bytes.Buffer) {
	if this.bkMulti > 0{
		this.bkMulti--
		val := this.current
		this.popElement()
		if this.current.Kind() == reflect.Map {
			this.current.SetMapIndex(this.mapKey, val)
		}
	}

	// vars
	this.currentVar = this.vars[len(this.vars) - 1]
	this.vars = this.vars[:len(this.vars) - 1]

	this.inBlock--
	this.popElement()
	this.inSearchKey()
}

func (this *Config) inSearchKey() {
	this.searchVal = false
	this.searchKey = true
	this.canSkip = true
}

func (this *Config) inSearchVal() {
	this.searchKey = false
	this.searchVal = true
	this.canSkip = false
}

func (this *Config) getElement(s string){
	s = strings.TrimSpace(s)
	if this.current.Kind() == reflect.Ptr {
		if this.current.IsNil() {
			this.current.Set(reflect.New(this.current.Type().Elem()))
		}
		this.current = this.current.Elem()
	}
	this.queue = append(this.queue, this.current)
	this.current = this.current.FieldByName(strings.Title(s))
}

func (this *Config) pushElement(v reflect.Value) {
	this.queue = append(this.queue, this.current)
	this.current = v
}

func (this *Config) popElement() {
	this.current = this.queue[len(this.queue) - 1]
	this.queue = this.queue[:len(this.queue) - 1]
}

func (this *Config) delimiter(b byte) bool {
	return unicode.IsSpace(rune(b))
}
<|endoftext|>


<commit_before>package main

import (
	"os"

	"github.com/BurntSushi/toml"
	"github.com/Nitro/sidecar/haproxy"
	log "github.com/Sirupsen/logrus"
)

type Config struct {
	HAproxy    *haproxy.HAproxy `toml:"haproxy"`
	HAproxyApi *ApiConfig       `toml:"haproxy_api"`
	Sidecar    *SidecarConfig   `tom:"sidecar"`
}

type ApiConfig struct {
	BindIP       string `toml:"bind_ip"`
	BindPort     int    `toml:"bind_port"`
	LoggingLevel string `toml:"logging_level"`
}

type SidecarConfig struct {
	StateUrl string `toml:"state_url"`
}

func parseConfig(path string) *Config {
	var config Config
	_, err := toml.DecodeFile(path, &config)
	if err != nil {
		exitWithError(err, "Failed to parse config file")
	}

	proxy := config.HAproxy
	if proxy == nil {
		log.Error("Missing 'haproxy' section of config file")
		os.Exit(1)
	}

	// Set some defaults if not provided. These should mostly
	// do the right thing unless this is not running in the
	// standard Docker container.
	if proxy.ReloadCmd == "" {
		proxy.ReloadCmd = "haproxy -f " + proxy.ConfigFile + " -p " +
			proxy.PidFile + " `[[ -f " +
			proxy.PidFile + " ]] && echo \"-sf $(cat " + proxy.PidFile + ")\"]]`"
	}

	if proxy.VerifyCmd == "" {
		proxy.VerifyCmd = "haproxy -c -f " + proxy.ConfigFile
	}

	if config.HAproxyApi.BindIP == "" {
		config.HAproxyApi.BindIP = "0.0.0.0"
	}

	if config.HAproxyApi.BindPort == 0 {
		config.HAproxyApi.BindPort = 7778
	}

	configureLoggingLevel(config.HAproxyApi.LoggingLevel)

	return &config
}

func configureLoggingLevel(level string) {
	switch {
	case len(level) == 0:
		log.SetLevel(log.InfoLevel)
	case level == "info":
		log.SetLevel(log.InfoLevel)
	case level == "warn":
		log.SetLevel(log.WarnLevel)
	case level == "error":
		log.SetLevel(log.ErrorLevel)
	case level == "debug":
		log.SetLevel(log.DebugLevel)
	}
}
<commit_msg>Use rubberneck to print config on startup<commit_after>package main

import (
	"os"

	"github.com/BurntSushi/toml"
	"github.com/Nitro/sidecar/haproxy"
	log "github.com/Sirupsen/logrus"
	"github.com/kelseyhightower/envconfig"
	"github.com/relistan/rubberneck"
)

type Config struct {
	HAproxyApi *ApiConfig       `toml:"haproxy_api" envconfig:"haproxy_api"`
	Sidecar    *SidecarConfig   `tom:"sidecar"`
	HAproxy    *haproxy.HAproxy `toml:"haproxy"`
}

type ApiConfig struct {
	BindIP       string `toml:"bind_ip" split_words:"true"`
	BindPort     int    `toml:"bind_port" split_words:"true"`
	LoggingLevel string `toml:"logging_level" split_words:"true"`
}

type SidecarConfig struct {
	StateUrl string `toml:"state_url" split_words:"true"`
}

func parseConfig(path string) *Config {
	var config Config
	_, err := toml.DecodeFile(path, &config)
	if err != nil {
		log.Errorf("Failed to parse config file: %s", err)
	}

	err = envconfig.Process("haproxy_api", &config)
	if err != nil {
		log.Errorf("Error processing envconfig: %s", err)
	}

	proxy := config.HAproxy
	if proxy == nil {
		log.Error("Missing 'haproxy' section of config file")
		os.Exit(1)
	}

	// Set some defaults if not provided. These should mostly
	// do the right thing unless this is not running in the
	// standard Docker container.
	if proxy.ReloadCmd == "" {
		proxy.ReloadCmd = "haproxy -f " + proxy.ConfigFile + " -p " +
			proxy.PidFile + " `[[ -f " +
			proxy.PidFile + " ]] && echo \"-sf $(cat " + proxy.PidFile + ")\"]]`"
	}

	if proxy.VerifyCmd == "" {
		proxy.VerifyCmd = "haproxy -c -f " + proxy.ConfigFile
	}

	if config.HAproxyApi.BindIP == "" {
		config.HAproxyApi.BindIP = "0.0.0.0"
	}

	if config.HAproxyApi.BindPort == 0 {
		config.HAproxyApi.BindPort = 7778
	}

	configureLoggingLevel(config.HAproxyApi.LoggingLevel)

	printConfig(&config)

	return &config
}

func printConfig(config *Config) {
	printer := rubberneck.NewPrinter(log.Printf, rubberneck.NoAddLineFeed)
	printer.Print(config)
}

func configureLoggingLevel(level string) {
	switch {
	case len(level) == 0:
		log.SetLevel(log.InfoLevel)
	case level == "info":
		log.SetLevel(log.InfoLevel)
	case level == "warn":
		log.SetLevel(log.WarnLevel)
	case level == "error":
		log.SetLevel(log.ErrorLevel)
	case level == "debug":
		log.SetLevel(log.DebugLevel)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Rohith All rights reserved.

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

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

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

package marathon

import (
	"io"
	"io/ioutil"
)

// EventsTransport describes which transport should be used to deliver Marathon events
type EventsTransport int

// Config holds the settings and options for the client
type Config struct {
	// the url for marathon
	URL string
	// events transport: EventsTransportCallback or EventsTransportSSE
	EventsTransport EventsTransport
	// event handler port
	EventsPort int
	// the interface we should be listening on for events
	EventsInterface string
	// the output for logging
	LogOutput io.Writer
	// the timeout for requests
	RequestTimeout int
	// http basic auth
	HTTPBasicAuthUser string
	// http basic password
	HTTPBasicPassword string
	// custom callback url
	CallbackURL string
}

// NewDefaultConfig create a default client config
func NewDefaultConfig() Config {
	return Config{
		URL:             "http://127.0.0.1:8080",
		EventsTransport: EventsTransportCallback,
		EventsPort:      10001,
		EventsInterface: "eth0",
		LogOutput:       ioutil.Discard,
		RequestTimeout:  5,
	}
}
<commit_msg>remove LogOutput as it's not being used anywhere<commit_after>/*
Copyright 2014 Rohith All rights reserved.

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

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

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

package marathon

import (
	"io"
	"io/ioutil"
)

// EventsTransport describes which transport should be used to deliver Marathon events
type EventsTransport int

// Config holds the settings and options for the client
type Config struct {
	// the url for marathon
	URL string
	// events transport: EventsTransportCallback or EventsTransportSSE
	EventsTransport EventsTransport
	// event handler port
	EventsPort int
	// the interface we should be listening on for events
	EventsInterface string
	// the timeout for requests
	RequestTimeout int
	// http basic auth
	HTTPBasicAuthUser string
	// http basic password
	HTTPBasicPassword string
	// custom callback url
	CallbackURL string
}

// NewDefaultConfig create a default client config
func NewDefaultConfig() Config {
	return Config{
		URL:             "http://127.0.0.1:8080",
		EventsTransport: EventsTransportCallback,
		EventsPort:      10001,
		EventsInterface: "eth0",
		RequestTimeout:  5,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/boltdb/bolt"
)

var (
	configKey   string
	configValue *string
)

func init() {
	config.Arg("key", "specific service setting").StringVar(&configKey)
}

func displayConfig() error {
	return db.View(func(tx *bolt.Tx) error {
		b, err := request.ServiceBucket(tx)
		if err != nil {
			return err
		}

		if configKey != "" {
			displayServiceKey(b, request.Service, configKey)
		} else {
			fmt.Println(request.Service)
			printBucket(b, 1)
		}

		return nil
	})
}

func displayServiceKey(b *bolt.Bucket, service, key string) {
	if b == nil {
		return
	}
	if v := b.Get([]byte(key)); v == nil {
		printBucket(b.Bucket([]byte(key)), 0)
	} else {
		fmt.Println(string(v))
	}
}
<commit_msg>Simplify config display<commit_after>package main

import (
	"fmt"

	"github.com/boltdb/bolt"
)

var (
	configKey   string
	configValue *string
)

func init() {
	config.Arg("key", "specific service setting").StringVar(&configKey)
}

func displayConfig() error {
	return db.View(func(tx *bolt.Tx) error {
		b, err := request.ServiceBucket(tx)
		if err != nil {
			return err
		}

		if configKey != "" {
			displayServiceKey(b, request.Service, configKey)
		} else {
			fmt.Println(request.Service)
			printBucket(b, 1)
		}

		return nil
	})
}

func displayServiceKey(b *bolt.Bucket, service, key string) {
	if b == nil {
		return
	}

	if asBucket := getBucketFromBucket(b, key); asBucket != nil {
		printBucket(asBucket, 0)
		return
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"github.com/mitchellh/osext"
	"github.com/mitchellh/packer/packer"
	"github.com/mitchellh/packer/packer/plugin"
	"io"
	"log"
	"os/exec"
	"path/filepath"
)

// This is the default, built-in configuration that ships with
// Packer.
const defaultConfig = `
{
	"plugin_min_port": 10000,
	"plugin_max_port": 25000,

	"builders": {
		"amazon-ebs": "packer-builder-amazon-ebs",
		"amazon-chroot": "packer-builder-amazon-chroot",
		"amazon-instance": "packer-builder-amazon-instance",
		"digitalocean": "packer-builder-digitalocean",
		"openstack": "packer-builder-openstack",
		"virtualbox": "packer-builder-virtualbox",
		"vmware": "packer-builder-vmware"
	},

	"commands": {
		"build": "packer-command-build",
		"fix": "packer-command-fix",
		"inspect": "packer-command-inspect",
		"validate": "packer-command-validate"
	},

	"post-processors": {
		"vagrant": "packer-post-processor-vagrant"
	},

	"provisioners": {
		"chef-solo": "packer-provisioner-chef-solo",
		"file": "packer-provisioner-file",
		"puppet-masterless": "packer-provisioner-puppet-masterless",
		"shell": "packer-provisioner-shell",
		"salt-masterless": "packer-provisioner-salt-masterless"
	}
}
`

type config struct {
	PluginMinPort uint
	PluginMaxPort uint

	Builders       map[string]string
	Commands       map[string]string
	PostProcessors map[string]string `json:"post-processors"`
	Provisioners   map[string]string
}

// Decodes configuration in JSON format from the given io.Reader into
// the config object pointed to.
func decodeConfig(r io.Reader, c *config) error {
	decoder := json.NewDecoder(r)
	return decoder.Decode(c)
}

// Returns an array of defined command names.
func (c *config) CommandNames() (result []string) {
	result = make([]string, 0, len(c.Commands))
	for name := range c.Commands {
		result = append(result, name)
	}
	return
}

// This is a proper packer.BuilderFunc that can be used to load packer.Builder
// implementations from the defined plugins.
func (c *config) LoadBuilder(name string) (packer.Builder, error) {
	log.Printf("Loading builder: %s\n", name)
	bin, ok := c.Builders[name]
	if !ok {
		log.Printf("Builder not found: %s\n", name)
		return nil, nil
	}

	return c.pluginClient(bin).Builder()
}

// This is a proper packer.CommandFunc that can be used to load packer.Command
// implementations from the defined plugins.
func (c *config) LoadCommand(name string) (packer.Command, error) {
	log.Printf("Loading command: %s\n", name)
	bin, ok := c.Commands[name]
	if !ok {
		log.Printf("Command not found: %s\n", name)
		return nil, nil
	}

	return c.pluginClient(bin).Command()
}

// This is a proper implementation of packer.HookFunc that can be used
// to load packer.Hook implementations from the defined plugins.
func (c *config) LoadHook(name string) (packer.Hook, error) {
	log.Printf("Loading hook: %s\n", name)
	return c.pluginClient(name).Hook()
}

// This is a proper packer.PostProcessorFunc that can be used to load
// packer.PostProcessor implementations from defined plugins.
func (c *config) LoadPostProcessor(name string) (packer.PostProcessor, error) {
	log.Printf("Loading post-processor: %s", name)
	bin, ok := c.PostProcessors[name]
	if !ok {
		log.Printf("Post-processor not found: %s", name)
		return nil, nil
	}

	return c.pluginClient(bin).PostProcessor()
}

// This is a proper packer.ProvisionerFunc that can be used to load
// packer.Provisioner implementations from defined plugins.
func (c *config) LoadProvisioner(name string) (packer.Provisioner, error) {
	log.Printf("Loading provisioner: %s\n", name)
	bin, ok := c.Provisioners[name]
	if !ok {
		log.Printf("Provisioner not found: %s\n", name)
		return nil, nil
	}

	return c.pluginClient(bin).Provisioner()
}

func (c *config) pluginClient(path string) *plugin.Client {
	originalPath := path

	// First attempt to find the executable by consulting the PATH.
	path, err := exec.LookPath(path)
	if err != nil {
		// If that doesn't work, look for it in the same directory
		// as the `packer` executable (us).
		log.Printf("Plugin could not be found. Checking same directory as executable.")
		exePath, err := osext.Executable()
		if err != nil {
			log.Printf("Couldn't get current exe path: %s", err)
		} else {
			log.Printf("Current exe path: %s", exePath)
			path = filepath.Join(filepath.Dir(exePath), filepath.Base(originalPath))
		}
	}

	// If everything failed, just use the original path and let the error
	// bubble through.
	if path == "" {
		path = originalPath
	}

	log.Printf("Creating plugin client for path: %s", path)
	var config plugin.ClientConfig
	config.Cmd = exec.Command(path)
	config.Managed = true
	config.MinPort = c.PluginMinPort
	config.MaxPort = c.PluginMaxPort
	return plugin.NewClient(&config)
}
<commit_msg>Enable ansible-local provisioner<commit_after>package main

import (
	"encoding/json"
	"github.com/mitchellh/osext"
	"github.com/mitchellh/packer/packer"
	"github.com/mitchellh/packer/packer/plugin"
	"io"
	"log"
	"os/exec"
	"path/filepath"
)

// This is the default, built-in configuration that ships with
// Packer.
const defaultConfig = `
{
	"plugin_min_port": 10000,
	"plugin_max_port": 25000,

	"builders": {
		"amazon-ebs": "packer-builder-amazon-ebs",
		"amazon-chroot": "packer-builder-amazon-chroot",
		"amazon-instance": "packer-builder-amazon-instance",
		"digitalocean": "packer-builder-digitalocean",
		"openstack": "packer-builder-openstack",
		"virtualbox": "packer-builder-virtualbox",
		"vmware": "packer-builder-vmware"
	},

	"commands": {
		"build": "packer-command-build",
		"fix": "packer-command-fix",
		"inspect": "packer-command-inspect",
		"validate": "packer-command-validate"
	},

	"post-processors": {
		"vagrant": "packer-post-processor-vagrant"
	},

	"provisioners": {
		"ansible-local": "packer-provisioner-ansible-local",
		"chef-solo": "packer-provisioner-chef-solo",
		"file": "packer-provisioner-file",
		"puppet-masterless": "packer-provisioner-puppet-masterless",
		"shell": "packer-provisioner-shell",
		"salt-masterless": "packer-provisioner-salt-masterless"
	}
}
`

type config struct {
	PluginMinPort uint
	PluginMaxPort uint

	Builders       map[string]string
	Commands       map[string]string
	PostProcessors map[string]string `json:"post-processors"`
	Provisioners   map[string]string
}

// Decodes configuration in JSON format from the given io.Reader into
// the config object pointed to.
func decodeConfig(r io.Reader, c *config) error {
	decoder := json.NewDecoder(r)
	return decoder.Decode(c)
}

// Returns an array of defined command names.
func (c *config) CommandNames() (result []string) {
	result = make([]string, 0, len(c.Commands))
	for name := range c.Commands {
		result = append(result, name)
	}
	return
}

// This is a proper packer.BuilderFunc that can be used to load packer.Builder
// implementations from the defined plugins.
func (c *config) LoadBuilder(name string) (packer.Builder, error) {
	log.Printf("Loading builder: %s\n", name)
	bin, ok := c.Builders[name]
	if !ok {
		log.Printf("Builder not found: %s\n", name)
		return nil, nil
	}

	return c.pluginClient(bin).Builder()
}

// This is a proper packer.CommandFunc that can be used to load packer.Command
// implementations from the defined plugins.
func (c *config) LoadCommand(name string) (packer.Command, error) {
	log.Printf("Loading command: %s\n", name)
	bin, ok := c.Commands[name]
	if !ok {
		log.Printf("Command not found: %s\n", name)
		return nil, nil
	}

	return c.pluginClient(bin).Command()
}

// This is a proper implementation of packer.HookFunc that can be used
// to load packer.Hook implementations from the defined plugins.
func (c *config) LoadHook(name string) (packer.Hook, error) {
	log.Printf("Loading hook: %s\n", name)
	return c.pluginClient(name).Hook()
}

// This is a proper packer.PostProcessorFunc that can be used to load
// packer.PostProcessor implementations from defined plugins.
func (c *config) LoadPostProcessor(name string) (packer.PostProcessor, error) {
	log.Printf("Loading post-processor: %s", name)
	bin, ok := c.PostProcessors[name]
	if !ok {
		log.Printf("Post-processor not found: %s", name)
		return nil, nil
	}

	return c.pluginClient(bin).PostProcessor()
}

// This is a proper packer.ProvisionerFunc that can be used to load
// packer.Provisioner implementations from defined plugins.
func (c *config) LoadProvisioner(name string) (packer.Provisioner, error) {
	log.Printf("Loading provisioner: %s\n", name)
	bin, ok := c.Provisioners[name]
	if !ok {
		log.Printf("Provisioner not found: %s\n", name)
		return nil, nil
	}

	return c.pluginClient(bin).Provisioner()
}

func (c *config) pluginClient(path string) *plugin.Client {
	originalPath := path

	// First attempt to find the executable by consulting the PATH.
	path, err := exec.LookPath(path)
	if err != nil {
		// If that doesn't work, look for it in the same directory
		// as the `packer` executable (us).
		log.Printf("Plugin could not be found. Checking same directory as executable.")
		exePath, err := osext.Executable()
		if err != nil {
			log.Printf("Couldn't get current exe path: %s", err)
		} else {
			log.Printf("Current exe path: %s", exePath)
			path = filepath.Join(filepath.Dir(exePath), filepath.Base(originalPath))
		}
	}

	// If everything failed, just use the original path and let the error
	// bubble through.
	if path == "" {
		path = originalPath
	}

	log.Printf("Creating plugin client for path: %s", path)
	var config plugin.ClientConfig
	config.Cmd = exec.Command(path)
	config.Managed = true
	config.MinPort = c.PluginMinPort
	config.MaxPort = c.PluginMaxPort
	return plugin.NewClient(&config)
}
<|endoftext|>


<commit_before>package titan

import (
	"log"
	"os"
)

const (
	// titan server envrinment variables
	goEnv    = "GO_ENV"
	titanEnv = "ENV"
	debug    = "DEBUG"
	port     = "PORT"
	jwtPass  = "PASS"

	// possible TITAN_ENV values
	envDev  = "development"
	envTest = "test"
	envProd = "production"

	// GCM environment variables
	gcmSenderID = "GCM_SENDER_ID"
	gcmCcsHost  = "GCM_CCS_HOST"

	// Google environment variables
	googleAPIKey = "GOOGLE_API_KEY"

	// Default listener port configuration
	portDefault = "3000"
	portTest    = "3001"
)

// Conf contains all the global configuration for the titan server.
var Conf Config

// Config describes the global configuration for the titan server.
type Config struct {
	App App
	GCM GCM
}

// App contains the global application variables.
type App struct {
	Env   string // One of the following: development, test, production.
	Debug bool   // Enables verbose logging to stdout.
	Port  string // Listener port.
}

// JWTPass retrieves the JWT signing password.
func (app *App) JWTPass() string {
	pass := os.Getenv(jwtPass)
	if pass == "" {
		pass = "pass"
	}
	return pass
}

// GCM describes the Google Cloud Messaging parameters as described here: https://developer.android.com/google/gcm/gs.html
type GCM struct {
	CCSHost  string
	SenderID string
}

// APIKey gets the GCM API key from environment variable.
func (gcm *GCM) APIKey() string {
	return os.Getenv(googleAPIKey)
}

// InitConf initializes application configuration.
// If given, env parameter overrides environment configuration. This is useful for testing.
func InitConf(env string) {
	if env == "" {
		env = os.Getenv(titanEnv)
	}
	if env == "" {
		if env = os.Getenv(goEnv); env == "" {
			env = envDev
		}
	}
	debug := os.Getenv(debug) != "" || (env != envProd)
	port := os.Getenv(port)
	if port == "" {
		switch env {
		case envTest:
			port = portTest
		default:
			port = portDefault
		}
	}

	app := App{Env: env, Debug: debug, Port: port}
	gcm := GCM{CCSHost: os.Getenv(gcmCcsHost), SenderID: os.Getenv(gcmSenderID)}
	Conf = Config{App: app, GCM: gcm}
	log.Printf("conf: initialized: %+v\n", Conf)
}
<commit_msg>fix typo<commit_after>package titan

import (
	"log"
	"os"
)

const (
	// titan server envrinment variables
	goEnv    = "GO_ENV"
	titanEnv = "ENV"
	debug    = "DEBUG"
	port     = "PORT"
	jwtPass  = "PASS"

	// possible TITAN_ENV values
	envDev  = "development"
	envTest = "test"
	envProd = "production"

	// GCM environment variables
	gcmSenderID = "GCM_SENDER_ID"
	gcmCcsHost  = "GCM_CCS_HOST"

	// Google environment variables
	googleAPIKey = "GOOGLE_API_KEY"

	// Default listener port configuration
	portDefault = "3000"
	portTest    = "3001"
)

// Conf contains all the global configuration for the titan server.
var Conf Config

// Config describes the global configuration for the titan server.
type Config struct {
	App App
	GCM GCM
}

// App contains the global application variables.
type App struct {
	Env   string // One of the following: development, test, production.
	Debug bool   // Enables verbose logging to stdout.
	Port  string // Listener port.
}

// JWTPass retrieves the JWT signing password.
func (app *App) JWTPass() string {
	pass := os.Getenv(jwtPass)
	if pass == "" {
		return "pass"
	}
	return pass
}

// GCM describes the Google Cloud Messaging parameters as described here: https://developer.android.com/google/gcm/gs.html
type GCM struct {
	CCSHost  string
	SenderID string
}

// APIKey gets the GCM API key from environment variable.
func (gcm *GCM) APIKey() string {
	return os.Getenv(googleAPIKey)
}

// InitConf initializes application configuration.
// If given, env parameter overrides environment configuration. This is useful for testing.
func InitConf(env string) {
	if env == "" {
		env = os.Getenv(titanEnv)
	}
	if env == "" {
		if env = os.Getenv(goEnv); env == "" {
			env = envDev
		}
	}
	debug := os.Getenv(debug) != "" || (env != envProd)
	port := os.Getenv(port)
	if port == "" {
		switch env {
		case envTest:
			port = portTest
		default:
			port = portDefault
		}
	}

	app := App{Env: env, Debug: debug, Port: port}
	gcm := GCM{CCSHost: os.Getenv(gcmCcsHost), SenderID: os.Getenv(gcmSenderID)}
	Conf = Config{App: app, GCM: gcm}
	log.Printf("conf: initialized: %+v\n", Conf)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/devangel/config"
	"os"
	"path/filepath"
	"strings"
	//"fmt"
)

var Config = config.NewConfig("force")

func GetSourceDir() (src string, err error) {
	// Last element is default
	var sourceDirs = []string{
		"src",
		"metadata",
	}

	wd, err := os.Getwd()

	err = nil
	for _, src = range sourceDirs {
		if strings.Contains(wd, src) {
			// our working directory contains a src dir above us, we need to move up the file syste
			nsrc := wd
			for {
				nsrc = filepath.Dir(nsrc)
				if filepath.Base(nsrc) == src {
					src = nsrc
					return
				}
			}
		} else {
			_, err = os.Stat(filepath.Join(wd, src)) //, "package.xml"))
			// Found a real source dir
			if err == nil {
				return
			}
		}
	}

	return
}

func ExitIfNoSourceDir(err error) {
	if err != nil {
		if os.IsNotExist(err) {
			ErrorAndExit("Current directory does not contain a metadata or src directory")
		}

		ErrorAndExit(err.Error())
	}
}
<commit_msg>GetSourceDir() returns a less-surprising source directory<commit_after>package main

import (
	"os"
	"path/filepath"

	"github.com/devangel/config"
)

var Config = config.NewConfig("force")

var sourceDirs = []string{
	"src",
	"metadata",
}

// IsSourceDir returns a boolean indicating that dir is actually a Salesforce
// source directory.
func IsSourceDir(dir string) bool {
	for _, src := range sourceDirs {
		// Look for src properly for case-insensitive filesystems
		manifest := filepath.Join(filepath.Dir(dir), src, "package.xml")
		if _, err := os.Stat(manifest); err == nil {
			return true
		}
	}
	return false
}

// GetSourceDir returns a rooted path name of the Salesforce source directory,
// relative to the current directory. GetSourceDir will look for a source
// directory in the nearest subdirectory. If no such directory exists, it will
// look at its parents, assuming that it is within a source directory already.
func GetSourceDir() (dir string, err error) {
	base, err := os.Getwd()
	if err != nil {
		return
	}

	// Look down to our nearest subdirectories
	for _, src := range sourceDirs {
		dir = filepath.Join(base, src)
		if IsSourceDir(dir) {
			return
		}
	}

	// Check the current directory and then start looking up at our parents.
	// When dir's parent is identical, it means we're at the root
	for dir != filepath.Dir(dir) {
		dir = filepath.Dir(dir)
		if IsSourceDir(dir) {
			return
		}
	}

	// Not a source directory
	return "", os.ErrNotExist
}

func ExitIfNoSourceDir(err error) {
	if err != nil {
		if os.IsNotExist(err) {
			ErrorAndExit("Current directory does not contain a metadata or src directory")
		}

		ErrorAndExit(err.Error())
	}
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"os"
	"path"
	"time"
)

type cacheEntry struct {
	ModTime  time.Time
	Contents interface{}
}

type Config struct {
	ConfigDir string
	Cache     map[string]cacheEntry
}

type Context struct {
	Network string
	Target  string
	Source  string
}

var C Config

func lookupvar(key, path string) interface{} {
	var f interface{}
	i, err := os.Stat(path)
	if err != nil {
		return nil
	}

	_, ok := C.Cache[path]
	if C.Cache[path].ModTime.Before(i.ModTime()) || !ok {
		log.Println("Stale cache for", path)
		data, err := ioutil.ReadFile(path)
		if err != nil {
			return nil
		}

		err = json.Unmarshal(data, &f)
		if err != nil {
			log.Println("Cannot parse", path)
			return nil
		}

		log.Println("Updating cache for", path)
		C.Cache[path] = cacheEntry{
			ModTime:  i.ModTime(),
			Contents: f,
		}

		return f.(map[string]interface{})[key]
	} else {
		log.Println("Found cache for", path)
		return C.Cache[path].Contents.(map[string]interface{})[key]
	}
}

func (c *Config) Lookup(context Context, key string) interface{} {
	var fpath string
	var value interface{}
	if context.Network != "" {
		if context.Source != "" {
			if context.Target != "" {
				fpath = path.Join(c.ConfigDir, context.Network, context.Source, context.Target+".json")

				log.Println("Context:", context, "Looking up", key, "in", fpath)
				value = lookupvar(key, fpath)
				if value != nil {
					log.Println("Context:", context, "Found", key, value)
					return value
				}
			}

			fpath = path.Join(c.ConfigDir, context.Network, context.Source+".json")

			log.Println("Context:", context, "Looking up", key, "in", fpath)
			value = lookupvar(key, fpath)
			if value != nil {
				log.Println("Context:", context, "Found", key, value)
				return value
			}
		}

		fpath = path.Join(c.ConfigDir, context.Network+".json")

		log.Println("Context:", context, "Looking up", key, "in", fpath)
		value = lookupvar(key, fpath)
		if value != nil {
			log.Println("Context:", context, "Found", key, value)
			return value
		}
	}

	fpath = path.Join(c.ConfigDir, "common.json")

	log.Println("Context:", context, "Looking up", key, "in", fpath)
	value = lookupvar(key, fpath)
	if value != nil {
		log.Println("Context:", context, "Found", key, value)
		return value
	} else {
		log.Println("Key", key, "not found")
		return nil
	}
}
<commit_msg>Forgot about cache initalization<commit_after>package config

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"os"
	"path"
	"time"
)

type cacheEntry struct {
	ModTime  time.Time
	Contents interface{}
}

type Config struct {
	ConfigDir string
	Cache     map[string]cacheEntry
}

type Context struct {
	Network string
	Target  string
	Source  string
}

var C Config

func init() {
	log.Println("Initializing configuration cache")
	C.Cache = make(map[string]cacheEntry)
}

func lookupvar(key, path string) interface{} {
	var f interface{}
	i, err := os.Stat(path)
	if err != nil {
		return nil
	}

	_, ok := C.Cache[path]
	if C.Cache[path].ModTime.Before(i.ModTime()) || !ok {
		log.Println("Stale cache for", path)
		data, err := ioutil.ReadFile(path)
		if err != nil {
			return nil
		}

		err = json.Unmarshal(data, &f)
		if err != nil {
			log.Println("Cannot parse", path)
			return nil
		}

		log.Println("Updating cache for", path)
		C.Cache[path] = cacheEntry{
			ModTime:  i.ModTime(),
			Contents: f,
		}

		return f.(map[string]interface{})[key]
	} else {
		log.Println("Found cache for", path)
		return C.Cache[path].Contents.(map[string]interface{})[key]
	}
}

func (c *Config) Lookup(context Context, key string) interface{} {
	var fpath string
	var value interface{}
	if context.Network != "" {
		if context.Source != "" {
			if context.Target != "" {
				fpath = path.Join(c.ConfigDir, context.Network, context.Source, context.Target+".json")

				log.Println("Context:", context, "Looking up", key, "in", fpath)
				value = lookupvar(key, fpath)
				if value != nil {
					log.Println("Context:", context, "Found", key, value)
					return value
				}
			}

			fpath = path.Join(c.ConfigDir, context.Network, context.Source+".json")

			log.Println("Context:", context, "Looking up", key, "in", fpath)
			value = lookupvar(key, fpath)
			if value != nil {
				log.Println("Context:", context, "Found", key, value)
				return value
			}
		}

		fpath = path.Join(c.ConfigDir, context.Network+".json")

		log.Println("Context:", context, "Looking up", key, "in", fpath)
		value = lookupvar(key, fpath)
		if value != nil {
			log.Println("Context:", context, "Found", key, value)
			return value
		}
	}

	fpath = path.Join(c.ConfigDir, "common.json")

	log.Println("Context:", context, "Looking up", key, "in", fpath)
	value = lookupvar(key, fpath)
	if value != nil {
		log.Println("Context:", context, "Found", key, value)
		return value
	} else {
		log.Println("Key", key, "not found")
		return nil
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"

	"github.com/mackerelio/mackerel-agent/command"
	"github.com/mackerelio/mackerel-agent/config"
)

// LoadApikeyFromConfig gets mackerel.io apikey from mackerel-agent.conf if it's installed mackerel-agent on localhost
func LoadApikeyFromConfig() string {
	conf, err := config.LoadConfig(config.DefaultConfig.Conffile)
	if err != nil {
		return ""
	}
	return conf.Apikey
}

// LoadApikeyFromEnvOrConfig is similar to LoadApikeyFromConfig. return MACKEREL_APIKEY environment value if defined MACKEREL_APIKEY
func LoadApikeyFromEnvOrConfig() string {
	if apiKey := os.Getenv("MACKEREL_APIKEY"); apiKey != "" {
		return apiKey
	}
	key := LoadApikeyFromConfig()
	return key
}

// LoadHostIDFromConfig gets localhost's hostID from conf.Root (ex. /var/lib/mackerel/id) if it's installed mackerel-agent on localhost
func LoadHostIDFromConfig() string {
	conf, err := config.LoadConfig(config.DefaultConfig.Conffile)
	if err != nil {
		return ""
	}
	hostID, err := command.LoadHostId(conf.Root)
	if err != nil {
		return ""
	}
	return hostID
}
<commit_msg>Bring mkr/config loading hostid logic from mackerel-agent/command bacause LoadHostId func becomes private<commit_after>package main

import (
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/mackerelio/mackerel-agent/config"
)

const idFileName = "id"

func idFilePath(root string) string {
	return filepath.Join(root, idFileName)
}

func loadHostID(root string) (string, error) {
	content, err := ioutil.ReadFile(idFilePath(root))
	if err != nil {
		return "", err
	}
	return string(content), nil
}

// LoadApikeyFromConfig gets mackerel.io apikey from mackerel-agent.conf if it's installed mackerel-agent on localhost
func LoadApikeyFromConfig() string {
	conf, err := config.LoadConfig(config.DefaultConfig.Conffile)
	if err != nil {
		return ""
	}
	return conf.Apikey
}

// LoadApikeyFromEnvOrConfig is similar to LoadApikeyFromConfig. return MACKEREL_APIKEY environment value if defined MACKEREL_APIKEY
func LoadApikeyFromEnvOrConfig() string {
	if apiKey := os.Getenv("MACKEREL_APIKEY"); apiKey != "" {
		return apiKey
	}
	key := LoadApikeyFromConfig()
	return key
}

// LoadHostIDFromConfig gets localhost's hostID from conf.Root (ex. /var/lib/mackerel/id) if it's installed mackerel-agent on localhost
func LoadHostIDFromConfig() string {
	conf, err := config.LoadConfig(config.DefaultConfig.Conffile)
	if err != nil {
		return ""
	}
	hostID, err := loadHostID(conf.Root)
	if err != nil {
		return ""
	}
	return hostID
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 Ludovic Fauvet
// Licensed under the MIT license

package main

import (
	"fmt"
	"io/ioutil"
	"launchpad.net/goyaml"
	"os"
	"sync"
)

var (
	defaultConfig = configuration{
		Repository:              "",
		Templates:               "",
		OutputMode:              "auto",
		ListenAddress:           ":8080",
		Gzip:                    false,
		RedisAddress:            "127.0.0.1:6379",
		RedisPassword:           "",
		LogDir:                  "",
		GeoipDatabasePath:       "/usr/share/GeoIP/",
		ConcurrentSync:          2,
		DisallowRedirects:       false,
		WeightDistributionRange: 1.5,
		DisableOnMissingFile:    false,
	}
	config      *configuration
	configMutex sync.RWMutex
)

type configuration struct {
	Repository              string     `yaml:"Repository"`
	Templates               string     `yaml:"Templates"`
	OutputMode              string     `yaml:"OutputMode"`
	ListenAddress           string     `yaml:"ListenAddress"`
	Gzip                    bool       `yaml:"Gzip"`
	RedisAddress            string     `yaml:"RedisAddress"`
	RedisPassword           string     `yaml:"RedisPassword"`
	LogDir                  string     `yaml:"LogDir"`
	GeoipDatabasePath       string     `yaml:"GeoipDatabasePath"`
	ConcurrentSync          int        `yaml:"ConcurrentSync"`
	DisallowRedirects       bool       `yaml:"DisallowRedirects"`
	WeightDistributionRange float32    `yaml:"WeightDistributionRange"`
	DisableOnMissingFile    bool       `yaml:"DisableOnMissingFile"`
	Fallbacks               []fallback `yaml:"Fallbacks"`
}

type fallback struct {
	Url           string `yaml:"URL"`
	CountryCode   string `yaml:"CountryCode"`
	ContinentCode string `yaml:"ContinentCode"`
}

// LoadConfig loads the configuration file if it has not yet been loaded
func LoadConfig() {
	if config != nil {
		return
	}
	err := ReloadConfig()
	if err != nil {
		log.Fatal(err)
	}
}

// ReloadConfig reloads the configuration file and update it globally
func ReloadConfig() error {
	if configFile == "" {
		if fileExists("./mirrorbits.conf") {
			configFile = "./mirrorbits.conf"
		} else if fileExists("/etc/mirrorbits.conf") {
			configFile = "/etc/mirrorbits.conf"
		}
	}

	content, err := ioutil.ReadFile(configFile)
	if err != nil {
		fmt.Println("Configuration could not be found.\n\tUse -config <path>")
		os.Exit(-1)
	}

	if os.Getenv("DEBUG") != "" {
		fmt.Println("Reading configuration from", configFile)
	}

	c := defaultConfig

	// Overload the default configuration with the user's one
	err = goyaml.Unmarshal(content, &c)
	if err != nil {
		return fmt.Errorf("%s in %s", err, configFile)
	}

	// Sanitize
	if c.WeightDistributionRange <= 0 {
		return fmt.Errorf("WeightDistributionRange must be > 0")
	}
	if !isInSlice(c.OutputMode, []string{"auto", "json", "redirect"}) {
		return fmt.Errorf("Config: outputMode can only be set to 'auto', 'json' or 'redirect'")
	}

	configMutex.Lock()
	config = &c
	configMutex.Unlock()
	return nil
}

// GetConfig returns a pointer to a configuration object
// FIXME reading from the pointer could cause a race!
func GetConfig() *configuration {
	configMutex.RLock()
	defer configMutex.RUnlock()

	if config == nil {
		panic("Configuration not loaded")
	}

	return config
}

func fileExists(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil
}
<commit_msg>config: trim repository's trailing slash<commit_after>// Copyright (c) 2014 Ludovic Fauvet
// Licensed under the MIT license

package main

import (
	"fmt"
	"io/ioutil"
	"launchpad.net/goyaml"
	"os"
	"strings"
	"sync"
)

var (
	defaultConfig = configuration{
		Repository:              "",
		Templates:               "",
		OutputMode:              "auto",
		ListenAddress:           ":8080",
		Gzip:                    false,
		RedisAddress:            "127.0.0.1:6379",
		RedisPassword:           "",
		LogDir:                  "",
		GeoipDatabasePath:       "/usr/share/GeoIP/",
		ConcurrentSync:          2,
		DisallowRedirects:       false,
		WeightDistributionRange: 1.5,
		DisableOnMissingFile:    false,
	}
	config      *configuration
	configMutex sync.RWMutex
)

type configuration struct {
	Repository              string     `yaml:"Repository"`
	Templates               string     `yaml:"Templates"`
	OutputMode              string     `yaml:"OutputMode"`
	ListenAddress           string     `yaml:"ListenAddress"`
	Gzip                    bool       `yaml:"Gzip"`
	RedisAddress            string     `yaml:"RedisAddress"`
	RedisPassword           string     `yaml:"RedisPassword"`
	LogDir                  string     `yaml:"LogDir"`
	GeoipDatabasePath       string     `yaml:"GeoipDatabasePath"`
	ConcurrentSync          int        `yaml:"ConcurrentSync"`
	DisallowRedirects       bool       `yaml:"DisallowRedirects"`
	WeightDistributionRange float32    `yaml:"WeightDistributionRange"`
	DisableOnMissingFile    bool       `yaml:"DisableOnMissingFile"`
	Fallbacks               []fallback `yaml:"Fallbacks"`
}

type fallback struct {
	Url           string `yaml:"URL"`
	CountryCode   string `yaml:"CountryCode"`
	ContinentCode string `yaml:"ContinentCode"`
}

// LoadConfig loads the configuration file if it has not yet been loaded
func LoadConfig() {
	if config != nil {
		return
	}
	err := ReloadConfig()
	if err != nil {
		log.Fatal(err)
	}
}

// ReloadConfig reloads the configuration file and update it globally
func ReloadConfig() error {
	if configFile == "" {
		if fileExists("./mirrorbits.conf") {
			configFile = "./mirrorbits.conf"
		} else if fileExists("/etc/mirrorbits.conf") {
			configFile = "/etc/mirrorbits.conf"
		}
	}

	content, err := ioutil.ReadFile(configFile)
	if err != nil {
		fmt.Println("Configuration could not be found.\n\tUse -config <path>")
		os.Exit(-1)
	}

	if os.Getenv("DEBUG") != "" {
		fmt.Println("Reading configuration from", configFile)
	}

	c := defaultConfig

	// Overload the default configuration with the user's one
	err = goyaml.Unmarshal(content, &c)
	if err != nil {
		return fmt.Errorf("%s in %s", err, configFile)
	}

	// Sanitize
	if c.WeightDistributionRange <= 0 {
		return fmt.Errorf("WeightDistributionRange must be > 0")
	}
	if !isInSlice(c.OutputMode, []string{"auto", "json", "redirect"}) {
		return fmt.Errorf("Config: outputMode can only be set to 'auto', 'json' or 'redirect'")
	}
	c.Repository = strings.TrimRight(c.Repository, "/")

	configMutex.Lock()
	config = &c
	configMutex.Unlock()
	return nil
}

// GetConfig returns a pointer to a configuration object
// FIXME reading from the pointer could cause a race!
func GetConfig() *configuration {
	configMutex.RLock()
	defer configMutex.RUnlock()

	if config == nil {
		panic("Configuration not loaded")
	}

	return config
}

func fileExists(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Robert S. Gerus. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package config

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"os"
	"sort"
	"sync"
	"time"
)

type cacheEntry struct {
	modTime  time.Time
	contents interface{}
}

type config struct {
	cache         map[string]cacheEntry
	buildFileList func(map[string]string) []string
	l             sync.Mutex
}

var c config

func init() {
	log.Println("Initializing configuration cache")
	c.cache = make(map[string]cacheEntry)
}

func lookupvar(key, path string) interface{} {
	var f interface{}
	i, err := os.Stat(path)
	_, ok := c.cache[path]
	if os.IsNotExist(err) {
		log.Println("Config does not exist", path)
		if ok {
			log.Println("Purging", path, "from cache")
			delete(c.cache, path)
		}
	}
	if err != nil {
		return nil
	}

	if c.cache[path].modTime.Before(i.ModTime()) || !ok {
		log.Println("Stale cache for", path)
		data, err := ioutil.ReadFile(path)
		if err != nil {
			return nil
		}

		err = json.Unmarshal(data, &f)
		if err != nil {
			log.Println("Cannot parse", path)
			return nil
		}

		log.Println("Updating cache for", path)
		c.cache[path] = cacheEntry{
			modTime:  i.ModTime(),
			contents: f,
		}

		return f.(map[string]interface{})[key]
	}

	log.Println("Found cache for", path)
	return c.cache[path].contents.(map[string]interface{})[key]
}

// SetFileListBuilder registers file list builder function.
//
// Registered function takes context (key-value map[string]string) as the only
// argument and return a slice of strings - file paths.
func SetFileListBuilder(f func(map[string]string) []string) {
	c.buildFileList = f
}

// Lookup searches for requested configuration key in file list built using
// context.
func Lookup(context map[string]string, key string) interface{} {
	var value interface{}

	c.l.Lock()
	defer c.l.Unlock()

	for _, fpath := range c.buildFileList(context) {
		log.Println("Context:", context, "Looking up", key, "in", fpath)
		value = lookupvar(key, fpath)
		if value != nil {
			log.Println("Context:", context, "Found", key, value)
			return value
		}
	}

	log.Println("Context:", context, "Key", key, "not found")
	return nil
}

// LookupString is analogous to Lookup(), but does the cast to string.
func LookupString(context map[string]string, key string) string {
	var value interface{}

	c.l.Lock()
	defer c.l.Unlock()

	for _, fpath := range c.buildFileList(context) {
		log.Println("Context:", context, "Looking up", key, "in", fpath)
		value = lookupvar(key, fpath)
		if value != nil {
			log.Println("Context:", context, "Found", key, value)
			return value.(string)
		}
	}

	log.Println("Context:", context, "Key", key, "not found")
	return ""
}

// LookupStringSlice is analogous to Lookup(), but does the cast to []string
func LookupStringSlice(context map[string]string, key string) (retval []string) {
	var value []interface{}

	c.l.Lock()
	defer c.l.Unlock()

	for _, fpath := range c.buildFileList(context) {
		log.Println("Context:", context, "Looking up", key, "in", fpath)
		value = lookupvar(key, fpath).([]interface{})
		if value != nil {
			log.Println("Context:", context, "Found", key, value)
			for _, s := range value {
				retval = append(retval, s.(string))
			}
			sort.Strings(retval)
			return retval
		}
	}

	log.Println("Context:", context, "Key", key, "not found")
	return []string{""}
}
<commit_msg>Fix config.LookupStringSlice.<commit_after>// Copyright 2015 Robert S. Gerus. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package config

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"os"
	"sort"
	"sync"
	"time"
)

type cacheEntry struct {
	modTime  time.Time
	contents interface{}
}

type config struct {
	cache         map[string]cacheEntry
	buildFileList func(map[string]string) []string
	l             sync.Mutex
}

var c config

func init() {
	log.Println("Initializing configuration cache")
	c.cache = make(map[string]cacheEntry)
}

func lookupvar(key, path string) interface{} {
	var f interface{}
	i, err := os.Stat(path)
	_, ok := c.cache[path]
	if os.IsNotExist(err) {
		log.Println("Config does not exist", path)
		if ok {
			log.Println("Purging", path, "from cache")
			delete(c.cache, path)
		}
	}
	if err != nil {
		return nil
	}

	if c.cache[path].modTime.Before(i.ModTime()) || !ok {
		log.Println("Stale cache for", path)
		data, err := ioutil.ReadFile(path)
		if err != nil {
			return nil
		}

		err = json.Unmarshal(data, &f)
		if err != nil {
			log.Println("Cannot parse", path)
			return nil
		}

		log.Println("Updating cache for", path)
		c.cache[path] = cacheEntry{
			modTime:  i.ModTime(),
			contents: f,
		}

		return f.(map[string]interface{})[key]
	}

	log.Println("Found cache for", path)
	return c.cache[path].contents.(map[string]interface{})[key]
}

// SetFileListBuilder registers file list builder function.
//
// Registered function takes context (key-value map[string]string) as the only
// argument and return a slice of strings - file paths.
func SetFileListBuilder(f func(map[string]string) []string) {
	c.buildFileList = f
}

// Lookup searches for requested configuration key in file list built using
// context.
func Lookup(context map[string]string, key string) interface{} {
	var value interface{}

	c.l.Lock()
	defer c.l.Unlock()

	for _, fpath := range c.buildFileList(context) {
		log.Println("Context:", context, "Looking up", key, "in", fpath)
		value = lookupvar(key, fpath)
		if value != nil {
			log.Println("Context:", context, "Found", key, value)
			return value
		}
	}

	log.Println("Context:", context, "Key", key, "not found")
	return nil
}

// LookupString is analogous to Lookup(), but does the cast to string.
func LookupString(context map[string]string, key string) string {
	var value interface{}

	c.l.Lock()
	defer c.l.Unlock()

	for _, fpath := range c.buildFileList(context) {
		log.Println("Context:", context, "Looking up", key, "in", fpath)
		value = lookupvar(key, fpath)
		if value != nil {
			log.Println("Context:", context, "Found", key, value)
			return value.(string)
		}
	}

	log.Println("Context:", context, "Key", key, "not found")
	return ""
}

// LookupStringSlice is analogous to Lookup(), but does the cast to []string
func LookupStringSlice(context map[string]string, key string) (retval []string) {
	var value interface{}

	c.l.Lock()
	defer c.l.Unlock()

	for _, fpath := range c.buildFileList(context) {
		log.Println("Context:", context, "Looking up", key, "in", fpath)
		value = lookupvar(key, fpath)
		if value != nil {
			log.Println("Context:", context, "Found", key, value)
			for _, s := range value.([]interface{}) {
				retval = append(retval, s.(string))
			}
			sort.Strings(retval)
			return retval
		}
	}

	log.Println("Context:", context, "Key", key, "not found")
	return []string{""}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"time"

	"github.com/nahanni/go-ucl"
)

type Config struct {
	Api        string       `json:"api"`
	Cert       string       `json:"cert"`
	LogDir     string       `json:"logdir"`
	Timeout    uint         `json:"timeout,string"`
	Activation string       `json:"activation.mode"`
	AsyncWait  bool         `json:"async.wait,string"`
	Auth       AuthConfig   `json:"auth"`
	AdminAuth  AuthConfig   `json:"admin.auth"`
	BoltDB     ConfigBoltDB `json:"boltdb"`
	Run        RunTimeConfig
}

type AuthConfig struct {
	User  string `json:"user"`
	Pass  string `json:"pass"`
	Token string `json:"token"`
}

type ConfigBoltDB struct {
	Path    string `json:"path"`
	File    string `json:"file"`
	Mode    string `json:"mode"`
	Timeout uint   `json:"open.timeout,string"`
}

type RunTimeConfig struct {
	SomaAPI       *url.URL      `json:"-"`
	PathLogs      string        `json:"-"`
	PathBoltDB    string        `json:"-"`
	ModeBoltDB    uint64        `json:"-"`
	CertPath      string        `json:"-"`
	TimeoutBoltDB time.Duration `json:"-"`
	TimeoutResty  time.Duration `json:"-"`
	Logger        *log.Logger   `json:"-"`
}

func (c *Config) populateFromFile(fname string) error {
	file, err := ioutil.ReadFile(fname)
	if err != nil {
		return err
	}

	// UCL parses into map[string]interface{}
	fileBytes := bytes.NewBuffer([]byte(file))
	parser := ucl.NewParser(fileBytes)
	uclData, err := parser.Ucl()
	if err != nil {
		fmt.Fprintf(os.Stderr, "UCL error: %s\n", err.Error())
		os.Exit(1)
	}

	// take detour via JSON to load UCL into struct
	uclJson, err := json.Marshal(uclData)
	if err != nil {
		fmt.Fprintf(os.Stderr, "JSON marshal error: %s\n", err.Error())
		os.Exit(1)
	}
	json.Unmarshal([]byte(uclJson), &c)

	return nil
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>New config setting: save.jobs<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"time"

	"github.com/nahanni/go-ucl"
)

type Config struct {
	Api        string       `json:"api"`
	Cert       string       `json:"cert"`
	LogDir     string       `json:"logdir"`
	Timeout    uint         `json:"timeout,string"`
	Activation string       `json:"activation.mode"`
	AsyncWait  bool         `json:"async.wait,string"`
	JobSave    bool         `json:"save.jobs,string"`
	Auth       AuthConfig   `json:"auth"`
	AdminAuth  AuthConfig   `json:"admin.auth"`
	BoltDB     ConfigBoltDB `json:"boltdb"`
	Run        RunTimeConfig
}

type AuthConfig struct {
	User  string `json:"user"`
	Pass  string `json:"pass"`
	Token string `json:"token"`
}

type ConfigBoltDB struct {
	Path    string `json:"path"`
	File    string `json:"file"`
	Mode    string `json:"mode"`
	Timeout uint   `json:"open.timeout,string"`
}

type RunTimeConfig struct {
	SomaAPI       *url.URL      `json:"-"`
	PathLogs      string        `json:"-"`
	PathBoltDB    string        `json:"-"`
	ModeBoltDB    uint64        `json:"-"`
	CertPath      string        `json:"-"`
	TimeoutBoltDB time.Duration `json:"-"`
	TimeoutResty  time.Duration `json:"-"`
	Logger        *log.Logger   `json:"-"`
}

func (c *Config) populateFromFile(fname string) error {
	file, err := ioutil.ReadFile(fname)
	if err != nil {
		return err
	}

	// UCL parses into map[string]interface{}
	fileBytes := bytes.NewBuffer([]byte(file))
	parser := ucl.NewParser(fileBytes)
	uclData, err := parser.Ucl()
	if err != nil {
		fmt.Fprintf(os.Stderr, "UCL error: %s\n", err.Error())
		os.Exit(1)
	}

	// take detour via JSON to load UCL into struct
	uclJson, err := json.Marshal(uclData)
	if err != nil {
		fmt.Fprintf(os.Stderr, "JSON marshal error: %s\n", err.Error())
		os.Exit(1)
	}
	json.Unmarshal([]byte(uclJson), &c)

	return nil
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>// Copyright (c) 2014 Couchbase, 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 indexer

import (
	"errors"
	"github.com/couchbase/indexing/secondary/common"
	"github.com/couchbase/indexing/secondary/logging"
)

var (
	ErrUnsupportedInclusion = errors.New("Unsupported range inclusion option")
	nilKey                  = &NilIndexKey{}
)

//Counter interface
func (s *fdbSnapshot) CountTotal(stopch StopChannel) (uint64, error) {
	return s.CountRange(nilKey, nilKey, Both, stopch)
}

// Approximate items count
func (s *fdbSnapshot) StatCountTotal() (uint64, error) {
	s.lock.Lock()
	defer s.lock.Unlock()

	info, err := s.main.Info()
	if err != nil {
		return 0, err
	}
	return info.DocCount(), nil
}

//Exister interface
func (s *fdbSnapshot) Exists(key IndexKey, stopch StopChannel) (bool, error) {

	var totalRows uint64
	var err error
	if totalRows, err = s.CountRange(key, key, Both, stopch); err != nil {
		return false, nil
	} else {
		return false, err
	}

	if totalRows > 0 {
		return true, nil
	}
	return false, nil
}

//Looker interface
func (s *fdbSnapshot) Lookup(key IndexKey, stopch StopChannel) (chan IndexEntry, chan error) {
	chentry := make(chan IndexEntry)
	cherr := make(chan error)

	go s.GetEntriesForKeyRange(key, key, Both, chentry, cherr, stopch)
	return chentry, cherr
}

func (s *fdbSnapshot) KeySet(stopch StopChannel) (chan IndexEntry, chan error) {
	chentry := make(chan IndexEntry)
	cherr := make(chan error)

	go s.GetEntriesForKeyRange(nilKey, nilKey, Both, chentry, cherr, stopch)
	return chentry, cherr
}

func (s *fdbSnapshot) KeyRange(low, high IndexKey, inclusion Inclusion,
	stopch StopChannel) (chan IndexEntry, chan error, SortOrder) {

	chentry := make(chan IndexEntry)
	cherr := make(chan error)

	go s.GetEntriesForKeyRange(low, high, inclusion, chentry, cherr, stopch)
	return chentry, cherr, Asc
}

func (s *fdbSnapshot) newIndexEntry(b []byte) IndexEntry {
	var entry IndexEntry
	var err error

	if s.slice.(*fdbSlice).isPrimary {
		entry, err = BytesToPrimaryIndexEntry(b)
	} else {
		entry, err = BytesToSecondaryIndexEntry(b)
	}
	common.CrashOnError(err)
	return entry
}

func (s *fdbSnapshot) GetEntriesForKeyRange(low, high IndexKey,
	inclusion Inclusion, chentry chan IndexEntry, cherr chan error, stopch StopChannel) {

	defer close(chentry)

	logging.Debugf("ForestDB Received Key Low - %s High - %s for Scan",
		low.String(), high.String())

	it, err := newFDBSnapshotIterator(s)
	if err != nil {
		cherr <- err
		return
	}
	defer closeIterator(it)

	if low.Bytes() == nil {
		it.SeekFirst()
	} else {
		it.Seek(low.Bytes())

		// Discard equal keys if low inclusion is requested
		if inclusion == Neither || inclusion == High {
			s.readEqualKeys(low, it, chentry, cherr, stopch, true)
		}
	}

	var entry IndexEntry
loop:
	for ; it.Valid(); it.Next() {
		select {
		case <-stopch:
			// stop signalled, end processing
			return

		default:
			logging.Tracef("ForestDB Got Key - %s", string(it.Key()))

			entry = s.newIndexEntry(it.Key())

			var highcmp int
			if high.Bytes() == nil {
				highcmp = 1 // if high key is nil, iterate through the fullset
			} else {
				highcmp = high.ComparePrefixFields(entry)
			}

			// if we have reached past the high key, no need to scan further
			if highcmp <= 0 {
				logging.Tracef("ForestDB Discarding Key - %s since >= high", string(it.Key()))
				break loop
			}

			chentry <- entry
		}
	}

	// Include equal keys if high inclusion is requested
	if inclusion == Both || inclusion == High {
		s.readEqualKeys(high, it, chentry, cherr, stopch, false)
	}
}

//RangeCounter interface
func (s *fdbSnapshot) CountRange(low, high IndexKey, inclusion Inclusion,
	stopch StopChannel) (uint64, error) {

	logging.Debugf("ForestDB Received Key Low - %s High - %s for Scan",
		low.String(), high.String())

	chentry := make(chan IndexEntry)
	cherr := make(chan error)
	go s.GetEntriesForKeyRange(low, high, inclusion, chentry, cherr, stopch)

	var count uint64
loop:
	for {
		select {
		case _, ok := <-chentry:
			if !ok {
				break loop
			}
			count++
		case err := <-cherr:
			return 0, err
		}
	}
	return count, nil
}

func (s *fdbSnapshot) readEqualKeys(k IndexKey, it *ForestDBIterator,
	chentry chan IndexEntry, cherr chan error, stopch StopChannel, discard bool) {

	var entry IndexEntry
	for ; it.Valid(); it.Next() {
		entry = s.newIndexEntry(it.Key())
		cmp := k.ComparePrefixFields(entry)
		if cmp == 0 {
			if !discard {
				chentry <- entry
			}
		} else {
			break
		}
	}
}

func closeIterator(it *ForestDBIterator) {
	err := it.Close()
	if err != nil {
		logging.Errorf("ForestDB iterator: dealloc failed (%v)", err)
	}
}
<commit_msg>MB-14612 Fix broken index.Lookup() API for compound indexes<commit_after>// Copyright (c) 2014 Couchbase, 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 indexer

import (
	"errors"
	"github.com/couchbase/indexing/secondary/common"
	"github.com/couchbase/indexing/secondary/logging"
)

var (
	ErrUnsupportedInclusion = errors.New("Unsupported range inclusion option")
	nilKey                  = &NilIndexKey{}
)

//Counter interface
func (s *fdbSnapshot) CountTotal(stopch StopChannel) (uint64, error) {
	return s.CountRange(nilKey, nilKey, Both, stopch)
}

// Approximate items count
func (s *fdbSnapshot) StatCountTotal() (uint64, error) {
	s.lock.Lock()
	defer s.lock.Unlock()

	info, err := s.main.Info()
	if err != nil {
		return 0, err
	}
	return info.DocCount(), nil
}

//Exister interface
func (s *fdbSnapshot) Exists(key IndexKey, stopch StopChannel) (bool, error) {

	var totalRows uint64
	var err error
	if totalRows, err = s.CountRange(key, key, Both, stopch); err != nil {
		return false, nil
	} else {
		return false, err
	}

	if totalRows > 0 {
		return true, nil
	}
	return false, nil
}

//Looker interface
func (s *fdbSnapshot) Lookup(key IndexKey, stopch StopChannel) (chan IndexEntry, chan error) {
	chentry := make(chan IndexEntry)
	cherr := make(chan error)

	go s.GetEntriesForKeyRange(key, key, Both, false, chentry, cherr, stopch)
	return chentry, cherr
}

func (s *fdbSnapshot) KeySet(stopch StopChannel) (chan IndexEntry, chan error) {
	chentry := make(chan IndexEntry)
	cherr := make(chan error)

	go s.GetEntriesForKeyRange(nilKey, nilKey, Both, true, chentry, cherr, stopch)
	return chentry, cherr
}

func (s *fdbSnapshot) KeyRange(low, high IndexKey, inclusion Inclusion,
	stopch StopChannel) (chan IndexEntry, chan error, SortOrder) {

	chentry := make(chan IndexEntry)
	cherr := make(chan error)

	go s.GetEntriesForKeyRange(low, high, inclusion, true, chentry, cherr, stopch)
	return chentry, cherr, Asc
}

func (s *fdbSnapshot) newIndexEntry(b []byte) IndexEntry {
	var entry IndexEntry
	var err error

	if s.slice.(*fdbSlice).isPrimary {
		entry, err = BytesToPrimaryIndexEntry(b)
	} else {
		entry, err = BytesToSecondaryIndexEntry(b)
	}
	common.CrashOnError(err)
	return entry
}

func (s *fdbSnapshot) GetEntriesForKeyRange(low, high IndexKey, inclusion Inclusion,
	prefixCmp bool, chentry chan IndexEntry, cherr chan error, stopch StopChannel) {

	defer close(chentry)

	logging.Debugf("ForestDB Received Key Low - %s High - %s for Scan",
		low.String(), high.String())

	it, err := newFDBSnapshotIterator(s)
	if err != nil {
		cherr <- err
		return
	}
	defer closeIterator(it)

	if low.Bytes() == nil {
		it.SeekFirst()
	} else {
		it.Seek(low.Bytes())

		// Discard equal keys if low inclusion is requested
		if inclusion == Neither || inclusion == High {
			s.readEqualKeys(low, it, prefixCmp, chentry, cherr, stopch, true)
		}
	}

	var entry IndexEntry
loop:
	for ; it.Valid(); it.Next() {
		select {
		case <-stopch:
			// stop signalled, end processing
			return

		default:
			logging.Tracef("ForestDB Got Key - %s", string(it.Key()))

			entry = s.newIndexEntry(it.Key())

			var highcmp int
			if high.Bytes() == nil {
				// if high key is nil, iterate through the fullset
				highcmp = 1
			} else {
				highcmp = keyCmp(prefixCmp, high, entry)
			}

			// if we have reached past the high key, no need to scan further
			if highcmp <= 0 {
				logging.Tracef("ForestDB Discarding Key - %s since >= high", string(it.Key()))
				break loop
			}

			chentry <- entry
		}
	}

	// Include equal keys if high inclusion is requested
	if inclusion == Both || inclusion == High {
		s.readEqualKeys(high, it, prefixCmp, chentry, cherr, stopch, false)
	}
}

//RangeCounter interface
func (s *fdbSnapshot) CountRange(low, high IndexKey, inclusion Inclusion,
	stopch StopChannel) (uint64, error) {

	logging.Debugf("ForestDB Received Key Low - %s High - %s for Scan",
		low.String(), high.String())

	chentry := make(chan IndexEntry)
	cherr := make(chan error)
	go s.GetEntriesForKeyRange(low, high, inclusion, true, chentry, cherr, stopch)

	var count uint64
loop:
	for {
		select {
		case _, ok := <-chentry:
			if !ok {
				break loop
			}
			count++
		case err := <-cherr:
			return 0, err
		}
	}
	return count, nil
}

func (s *fdbSnapshot) readEqualKeys(k IndexKey, it *ForestDBIterator, prefixCmp bool,
	chentry chan IndexEntry, cherr chan error, stopch StopChannel, discard bool) {

	var entry IndexEntry
	for ; it.Valid(); it.Next() {
		entry = s.newIndexEntry(it.Key())
		if keyCmp(prefixCmp, k, entry) == 0 {
			if !discard {
				chentry <- entry
			}
		} else {
			break
		}
	}
}

func closeIterator(it *ForestDBIterator) {
	err := it.Close()
	if err != nil {
		logging.Errorf("ForestDB iterator: dealloc failed (%v)", err)
	}
}

func keyCmp(prefixCmp bool, k IndexKey, entry IndexEntry) int {
	var cmp int
	if prefixCmp {
		cmp = k.ComparePrefixFields(entry)
	} else {
		cmp = k.Compare(entry)
	}
	return cmp
}
<|endoftext|>