<commit_before>/*

Config Package.

	package main

	import "github.com/ije/gox/config"

	func main() {
	    conf, err := config.New("a.conf")
	    if err != nil {
			return
	    }
	    log.Printf(conf.String("key", "defaultValue"))
	    log.Printf(conf.Section("sectionName").String("key", "defaultValue"))
	}

*/
package config

import (
	"bytes"
	"io"
	"os"
	"regexp"
)

type Config struct {
	defaultSection   Section
	extendedSections map[string]Section
}

func New(configFile string) (config *Config, err error) {
	config = &Config{}
	if len(configFile) > 0 {
		var file *os.File
		file, err = os.Open(configFile)
		if err != nil {
			if os.IsNotExist(err) {
				err = nil
				config.defaultSection = Section{}
				return
			}
			config = nil
			return
		}
		defer file.Close()
		config.defaultSection, config.extendedSections, err = Parse(file)
	}
	return
}

func Parse(r io.Reader) (defaultSection Section, extendedSections map[string]Section, err error) {
	var n int
	var c byte
	var sectionKey string
	var section Section
	regSplitKV := regexp.MustCompile(`^([^ ]+)\s+(.+)$`)
	regSplitKVWithLongKey := regexp.MustCompile(`^"([^"]+)"\s+(.+)$`)
	parse := func(line []byte) {
		line = bytes.TrimSpace(line)
		if ll := len(line); ll > 0 {
			switch line[0] {
			case '#':
				return
			case '[':
				if ll >= 3 && line[ll-1] == ']' {
					if len(sectionKey) == 0 {
						defaultSection = section
					} else {
						extendedSections[sectionKey] = section
					}
					sectionKey = string(line[1 : ll-1])
					section = Section{}
					return
				}
			case '"':
				if ll >= 5 {
					matches := regSplitKVWithLongKey.FindSubmatch(line)
					if len(matches) == 3 {
						section[string(matches[1])] = string(matches[2])
					}
					return
				}
			default:
				matches := regSplitKV.FindSubmatch(line)
				if len(matches) == 3 {
					section[string(matches[1])] = string(matches[2])
				} else {
					section[string(line)] = ""
				}
			}
		}
	}
	buf := make([]byte, 1)
	line := bytes.NewBuffer(nil)

	section = Section{}
	extendedSections = map[string]Section{}

	for {
		n, err = r.Read(buf)
		if err != nil {
			if err != io.EOF {
				return
			}
			err = nil
			break
		}
		if n == 0 {
			break
		}
		c = buf[0]
		if c == '\r' || c == '\n' {
			parse(line.Bytes())
			line.Reset()
		} else {
			line.WriteByte(c)
		}
	}

	if len(sectionKey) == 0 {
		defaultSection = section
	} else {
		extendedSections[sectionKey] = section
	}
	return
}

func (config *Config) IsEmpty() bool {
	return config.defaultSection.IsEmpty() && len(config.extendedSections) == 0
}

func (config *Config) Contains(key string) bool {
	return config.defaultSection.Contains(key)
}

func (config *Config) String(key, def string) string {
	return config.defaultSection.String(key, def)
}

func (config *Config) Bool(key string, def bool) bool {
	return config.defaultSection.Bool(key, def)
}

func (config *Config) Int(key string, def int) int {
	return config.defaultSection.Int(key, def)
}

func (config *Config) Int64(key string, def int64) int64 {
	return config.defaultSection.Int64(key, def)
}

func (config *Config) Bytes(key string, def int64) int64 {
	return config.defaultSection.Bytes(key, def)
}

func (config *Config) Float64(key string, def float64) float64 {
	return config.defaultSection.Float64(key, def)
}

func (config *Config) Set(key string, value interface{}) {
	config.defaultSection.Set(key, value)
}

func (config *Config) Section(name string) (section Section) {
	if len(name) == 0 {
		section = config.defaultSection
		return
	}
	section, ok := config.extendedSections[name]
	if ok {
		return
	}
	section = Section{}
	config.extendedSections[name] = section
	return
}

func (config *Config) ExtendedSections() map[string]Section {
	return config.extendedSections
}
<commit_msg>package config arguments fix<commit_after>/*

Config Package.

	package main

	import "github.com/ije/gox/config"

	func main() {
	    conf, err := config.New("a.conf")
	    if err != nil {
			return
	    }
	    log.Printf(conf.String("key", "defaultValue"))
	    log.Printf(conf.Section("sectionName").String("key", "defaultValue"))
	}

*/
package config

import (
	"bytes"
	"io"
	"os"
	"regexp"
)

type Config struct {
	defaultSection   Section
	extendedSections map[string]Section
}

func New(configFile string) (config *Config, err error) {
	config = &Config{}
	if len(configFile) > 0 {
		var file *os.File
		file, err = os.Open(configFile)
		if err != nil {
			if os.IsNotExist(err) {
				err = nil
				config.defaultSection = Section{}
				return
			}
			config = nil
			return
		}
		defer file.Close()
		config.defaultSection, config.extendedSections, err = Parse(file)
	}
	return
}

func Parse(r io.Reader) (defaultSection Section, extendedSections map[string]Section, err error) {
	var n int
	var c byte
	var sectionKey string
	var section Section
	regSplitKV := regexp.MustCompile(`^([^ ]+)\s+(.+)$`)
	regSplitKVWithLongKey := regexp.MustCompile(`^"([^"]+)"\s+(.+)$`)
	parse := func(line []byte) {
		line = bytes.TrimSpace(line)
		if ll := len(line); ll > 0 {
			switch line[0] {
			case '#':
				return
			case '[':
				if ll >= 3 && line[ll-1] == ']' {
					if len(sectionKey) == 0 {
						defaultSection = section
					} else {
						extendedSections[sectionKey] = section
					}
					sectionKey = string(line[1 : ll-1])
					section = Section{}
					return
				}
			case '"':
				if ll >= 5 {
					matches := regSplitKVWithLongKey.FindSubmatch(line)
					if len(matches) == 3 {
						section[string(matches[1])] = string(matches[2])
					}
					return
				}
			default:
				matches := regSplitKV.FindSubmatch(line)
				if len(matches) == 3 {
					section[string(matches[1])] = string(matches[2])
				} else {
					section[string(line)] = ""
				}
			}
		}
	}
	buf := make([]byte, 1)
	line := bytes.NewBuffer(nil)

	section = Section{}
	extendedSections = map[string]Section{}

	for {
		n, err = r.Read(buf)
		if err != nil {
			if err != io.EOF {
				return
			}
			err = nil
			break
		}
		if n == 0 {
			break
		}
		c = buf[0]
		if c == '\r' || c == '\n' {
			parse(line.Bytes())
			line.Reset()
		} else {
			line.WriteByte(c)
		}
	}

	if len(sectionKey) == 0 {
		defaultSection = section
	} else {
		extendedSections[sectionKey] = section
	}
	return
}

func (config *Config) IsEmpty() bool {
	return config.defaultSection.IsEmpty() && len(config.extendedSections) == 0
}

func (config *Config) Contains(key string) bool {
	return config.defaultSection.Contains(key)
}

func (config *Config) String(key string, extra ...string) string {
	return config.defaultSection.String(key, extra...)
}

func (config *Config) Bool(key string, def ...bool) bool {
	return config.defaultSection.Bool(key, def...)
}

func (config *Config) Int(key string, def ...int) int {
	return config.defaultSection.Int(key, def...)
}

func (config *Config) Int64(key string, def ...int64) int64 {
	return config.defaultSection.Int64(key, def...)
}

func (config *Config) Bytes(key string, def ...int64) int64 {
	return config.defaultSection.Bytes(key, def...)
}

func (config *Config) Float64(key string, def ...float64) float64 {
	return config.defaultSection.Float64(key, def...)
}

func (config *Config) Set(key string, value interface{}) {
	config.defaultSection.Set(key, value)
}

func (config *Config) Section(name string) (section Section) {
	if len(name) == 0 {
		section = config.defaultSection
		return
	}
	section, ok := config.extendedSections[name]
	if ok {
		return
	}
	section = Section{}
	config.extendedSections[name] = section
	return
}

func (config *Config) ExtendedSections() map[string]Section {
	return config.extendedSections
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"regexp"
	"strconv"
	"strings"

	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
)

// Config is a configuration for browsing in a particualr path.
type Config struct {
	*User
	BaseURL     string
	AbsoluteURL string
	AddrPath    string
	Token       string // Anti CSRF token
	HugoEnabled bool   // Enables the Hugo plugin for File Manager
	Users       map[string]*User
	CurrentUser *User
}

// Rule is a dissalow/allow rule
type Rule struct {
	Regex  bool
	Allow  bool
	Path   string
	Regexp *regexp.Regexp
}

// Parse parses the configuration set by the user so it can
// be used by the middleware
func Parse(c *caddy.Controller) ([]Config, error) {
	var configs []Config

	appendConfig := func(cfg Config) error {
		for _, c := range configs {
			if c.PathScope == cfg.PathScope {
				return fmt.Errorf("duplicate file managing config for %s", c.PathScope)
			}
		}
		configs = append(configs, cfg)
		return nil
	}

	var err error
	var cCfg *User
	var baseURL string

	for c.Next() {
		var cfg = Config{User: &User{}}
		cfg.PathScope = "."
		cfg.Root = http.Dir(cfg.PathScope)
		cfg.BaseURL = ""
		cfg.FrontMatter = "yaml"
		cfg.HugoEnabled = false
		cfg.Users = map[string]*User{}
		cfg.AllowCommands = true
		cfg.AllowEdit = true
		cfg.AllowNew = true
		cfg.Commands = []string{"git", "svn", "hg"}
		cfg.Rules = []*Rule{&Rule{
			Regex:  true,
			Allow:  false,
			Regexp: regexp.MustCompile("\\/\\..+"),
		}}

		baseURL = ""
		cCfg = cfg.User

		for c.NextBlock() {
			switch c.Val() {
			case "on":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				baseURL = c.Val()
			case "frontmatter":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cCfg.FrontMatter = c.Val()
				if cCfg.FrontMatter != "yaml" && cCfg.FrontMatter != "json" && cCfg.FrontMatter != "toml" {
					return configs, c.Err("frontmatter type not supported")
				}
			case "show":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cCfg.PathScope = c.Val()
				cCfg.PathScope = strings.TrimSuffix(cCfg.PathScope, "/")
				cCfg.Root = http.Dir(cCfg.PathScope)
			case "styles":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				var tplBytes []byte
				tplBytes, err = ioutil.ReadFile(c.Val())
				if err != nil {
					return configs, err
				}
				cCfg.StyleSheet = string(tplBytes)
			case "allow_new":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cCfg.AllowNew, err = strconv.ParseBool(c.Val())
				if err != nil {
					return configs, err
				}
			case "allow_edit":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cCfg.AllowEdit, err = strconv.ParseBool(c.Val())
				if err != nil {
					return configs, err
				}
			case "allow_commands":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cCfg.AllowCommands, err = strconv.ParseBool(c.Val())
				if err != nil {
					return configs, err
				}
			case "allow_command":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}

				cCfg.Commands = append(cCfg.Commands, c.Val())
			case "block_command":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}

				index := 0

				for i, val := range cCfg.Commands {
					if val == c.Val() {
						index = i
					}
				}

				cCfg.Commands = append(cCfg.Commands[:index], cCfg.Commands[index+1:]...)
			case "allow":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}

				cCfg.Rules = append(cCfg.Rules, &Rule{
					Regex:  false,
					Allow:  true,
					Path:   c.Val(),
					Regexp: nil,
				})
			case "allow_r":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}

				cCfg.Rules = append(cCfg.Rules, &Rule{
					Regex:  true,
					Allow:  true,
					Path:   "",
					Regexp: regexp.MustCompile(c.Val()),
				})
			case "block":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}

				cCfg.Rules = append(cCfg.Rules, &Rule{
					Regex:  false,
					Allow:  false,
					Path:   c.Val(),
					Regexp: nil,
				})
			case "block_r":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}

				cCfg.Rules = append(cCfg.Rules, &Rule{
					Regex:  true,
					Allow:  false,
					Path:   "",
					Regexp: regexp.MustCompile(c.Val()),
				})
			// NEW USER BLOCK?
			default:
				val := c.Val()
				// Checks if it's a new user
				if !strings.HasSuffix(val, ":") {
					fmt.Println("Unknown option " + val)
				}

				// Get the username, sets the current user, and initializes it
				val = strings.TrimSuffix(val, ":")
				cfg.Users[val] = &User{}

				// Initialize the new user
				cCfg = cfg.Users[val]
				cCfg.AllowCommands = cfg.AllowCommands
				cCfg.AllowEdit = cfg.AllowEdit
				cCfg.AllowNew = cfg.AllowEdit
				cCfg.Commands = cfg.Commands
				cCfg.FrontMatter = cfg.FrontMatter
				cCfg.PathScope = cfg.PathScope
				cCfg.Root = cfg.Root
				cCfg.Rules = cfg.Rules
				cCfg.StyleSheet = cfg.StyleSheet
			}
		}

		// Set global base url
		cfg.BaseURL = baseURL
		cfg.BaseURL = strings.TrimPrefix(cfg.BaseURL, "/")
		cfg.BaseURL = strings.TrimSuffix(cfg.BaseURL, "/")
		cfg.BaseURL = "/" + cfg.BaseURL

		caddyConf := httpserver.GetConfig(c)
		cfg.AbsoluteURL = strings.TrimSuffix(caddyConf.Addr.Path, "/") + "/" + cfg.BaseURL
		cfg.AbsoluteURL = strings.Replace(cfg.AbsoluteURL, "//", "/", -1)
		cfg.AbsoluteURL = strings.TrimSuffix(cfg.AbsoluteURL, "/")
		cfg.AddrPath = strings.TrimSuffix(caddyConf.Addr.Path, "/")
		if err := appendConfig(cfg); err != nil {
			return configs, err
		}

	}

	return configs, nil
}
<commit_msg>close #21 - still a bit unstable and not revised<commit_after>package config

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"regexp"
	"strconv"
	"strings"

	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
)

// Config is a configuration for browsing in a particualr path.
type Config struct {
	*User
	BaseURL     string
	AbsoluteURL string
	AddrPath    string
	Token       string // Anti CSRF token
	HugoEnabled bool   // Enables the Hugo plugin for File Manager
	Users       map[string]*User
	CurrentUser *User
}

// Rule is a dissalow/allow rule
type Rule struct {
	Regex  bool
	Allow  bool
	Path   string
	Regexp *regexp.Regexp
}

// Parse parses the configuration set by the user so it can
// be used by the middleware
func Parse(c *caddy.Controller) ([]Config, error) {
	var configs []Config

	appendConfig := func(cfg Config) error {
		for _, c := range configs {
			if c.PathScope == cfg.PathScope {
				return fmt.Errorf("duplicate file managing config for %s", c.PathScope)
			}
		}
		configs = append(configs, cfg)
		return nil
	}

	var err error
	var cCfg *User
	var baseURL string

	for c.Next() {
		var cfg = Config{User: &User{}}
		cfg.PathScope = "."
		cfg.Root = http.Dir(cfg.PathScope)
		cfg.BaseURL = ""
		cfg.FrontMatter = "yaml"
		cfg.HugoEnabled = false
		cfg.Users = map[string]*User{}
		cfg.AllowCommands = true
		cfg.AllowEdit = true
		cfg.AllowNew = true
		cfg.Commands = []string{"git", "svn", "hg"}
		cfg.Rules = []*Rule{&Rule{
			Regex:  true,
			Allow:  false,
			Regexp: regexp.MustCompile("\\/\\..+"),
		}}

		baseURL = ""
		cCfg = cfg.User

		for c.NextBlock() {
			switch c.Val() {
			case "on":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				baseURL = c.Val()
			case "frontmatter":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cCfg.FrontMatter = c.Val()
				if cCfg.FrontMatter != "yaml" && cCfg.FrontMatter != "json" && cCfg.FrontMatter != "toml" {
					return configs, c.Err("frontmatter type not supported")
				}
			case "show":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cCfg.PathScope = c.Val()
				cCfg.PathScope = strings.TrimSuffix(cCfg.PathScope, "/")
				cCfg.Root = http.Dir(cCfg.PathScope)
			case "styles":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				var tplBytes []byte
				tplBytes, err = ioutil.ReadFile(c.Val())
				if err != nil {
					return configs, err
				}
				cCfg.StyleSheet = string(tplBytes)
			case "allow_new":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cCfg.AllowNew, err = strconv.ParseBool(c.Val())
				if err != nil {
					return configs, err
				}
			case "allow_edit":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cCfg.AllowEdit, err = strconv.ParseBool(c.Val())
				if err != nil {
					return configs, err
				}
			case "allow_commands":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}
				cCfg.AllowCommands, err = strconv.ParseBool(c.Val())
				if err != nil {
					return configs, err
				}
			case "allow_command":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}

				cCfg.Commands = append(cCfg.Commands, c.Val())
			case "block_command":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}

				index := 0

				for i, val := range cCfg.Commands {
					if val == c.Val() {
						index = i
					}
				}

				cCfg.Commands = append(cCfg.Commands[:index], cCfg.Commands[index+1:]...)
			case "allow":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}

				if c.Val() == "dotfiles" {
					cCfg.Rules = append(cCfg.Rules, &Rule{
						Regex:  true,
						Allow:  true,
						Regexp: regexp.MustCompile("\\/\\..+"),
					})
				} else {
					cCfg.Rules = append(cCfg.Rules, &Rule{
						Regex:  false,
						Allow:  true,
						Path:   c.Val(),
						Regexp: nil,
					})
				}
			case "allow_r":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}

				cCfg.Rules = append(cCfg.Rules, &Rule{
					Regex:  true,
					Allow:  true,
					Path:   "",
					Regexp: regexp.MustCompile(c.Val()),
				})
			case "block":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}

				if c.Val() == "dotfiles" {
					cCfg.Rules = append(cCfg.Rules, &Rule{
						Regex:  true,
						Allow:  false,
						Regexp: regexp.MustCompile("\\/\\..+"),
					})
				} else {
					cCfg.Rules = append(cCfg.Rules, &Rule{
						Regex:  false,
						Allow:  false,
						Path:   c.Val(),
						Regexp: nil,
					})
				}
			case "block_r":
				if !c.NextArg() {
					return configs, c.ArgErr()
				}

				cCfg.Rules = append(cCfg.Rules, &Rule{
					Regex:  true,
					Allow:  false,
					Path:   "",
					Regexp: regexp.MustCompile(c.Val()),
				})
			// NEW USER BLOCK?
			default:
				val := c.Val()
				// Checks if it's a new user
				if !strings.HasSuffix(val, ":") {
					fmt.Println("Unknown option " + val)
				}

				// Get the username, sets the current user, and initializes it
				val = strings.TrimSuffix(val, ":")
				cfg.Users[val] = &User{}

				// Initialize the new user
				cCfg = cfg.Users[val]
				cCfg.AllowCommands = cfg.AllowCommands
				cCfg.AllowEdit = cfg.AllowEdit
				cCfg.AllowNew = cfg.AllowEdit
				cCfg.Commands = cfg.Commands
				cCfg.FrontMatter = cfg.FrontMatter
				cCfg.PathScope = cfg.PathScope
				cCfg.Root = cfg.Root
				cCfg.Rules = cfg.Rules
				cCfg.StyleSheet = cfg.StyleSheet
			}
		}

		// Set global base url
		cfg.BaseURL = baseURL
		cfg.BaseURL = strings.TrimPrefix(cfg.BaseURL, "/")
		cfg.BaseURL = strings.TrimSuffix(cfg.BaseURL, "/")
		cfg.BaseURL = "/" + cfg.BaseURL

		caddyConf := httpserver.GetConfig(c)
		cfg.AbsoluteURL = strings.TrimSuffix(caddyConf.Addr.Path, "/") + "/" + cfg.BaseURL
		cfg.AbsoluteURL = strings.Replace(cfg.AbsoluteURL, "//", "/", -1)
		cfg.AbsoluteURL = strings.TrimSuffix(cfg.AbsoluteURL, "/")
		cfg.AddrPath = strings.TrimSuffix(caddyConf.Addr.Path, "/")
		if err := appendConfig(cfg); err != nil {
			return configs, err
		}

	}

	return configs, nil
}
<|endoftext|>


<commit_before>/**
 * Copyright (c) Mainflux
 *
 * Mainflux server is licensed under an Apache license, version 2.0.
 * All rights not explicitly granted in the Apache license, version 2.0 are reserved.
 * See the included LICENSE file for more details.
 */

package config

import (
	"fmt"
	"github.com/BurntSushi/toml"
	"os"
)

type Config struct {
	// HTTP
	HttpHost		string
	HttpPort		int

	// Mongo
	MongoHost		string
	MongoPort		int
	MongoDatabase	string

	// MQTT
	MqttHost		string
	MqttPort		int

	// Influx
	InfluxHost		string
	InfluxPort		int
	InfluxDatabase	string
}

func (cfg *Config) Parse() {

	var confFile string

	testEnv := os.Getenv("TEST_ENV")
	if testEnv == "" && len(os.Args) > 1 {
		// We are not in the TEST_ENV (where different args are provided)
		// and provided config file as an argument
		confFile = os.Args[1]
	} else {
		// default cfg path to source dir, as we keep cfg.yml there
		confFile = os.Getenv("GOPATH") + "/src/github.com/mainflux/mainflux/config/config.toml"
	}

	if _, err := toml.DecodeFile(confFile, &cfg); err != nil {
		// handle error
		fmt.Println("Error parsing Toml")
	}

	fmt.Println(cfg)
}
<commit_msg>Remove print of config<commit_after>/**
 * Copyright (c) Mainflux
 *
 * Mainflux server is licensed under an Apache license, version 2.0.
 * All rights not explicitly granted in the Apache license, version 2.0 are reserved.
 * See the included LICENSE file for more details.
 */

package config

import (
	"fmt"
	"github.com/BurntSushi/toml"
	"os"
)

type Config struct {
	// HTTP
	HttpHost		string
	HttpPort		int

	// Mongo
	MongoHost		string
	MongoPort		int
	MongoDatabase	string

	// MQTT
	MqttHost		string
	MqttPort		int

	// Influx
	InfluxHost		string
	InfluxPort		int
	InfluxDatabase	string
}

func (cfg *Config) Parse() {

	var confFile string

	testEnv := os.Getenv("TEST_ENV")
	if testEnv == "" && len(os.Args) > 1 {
		// We are not in the TEST_ENV (where different args are provided)
		// and provided config file as an argument
		confFile = os.Args[1]
	} else {
		// default cfg path to source dir, as we keep cfg.yml there
		confFile = os.Getenv("GOPATH") + "/src/github.com/mainflux/mainflux/config/config.toml"
	}

	if _, err := toml.DecodeFile(confFile, &cfg); err != nil {
		// handle error
		fmt.Println("Error parsing Toml")
	}
}
<|endoftext|>


<commit_before>package config

import (
	"crypto/rand"
	"fmt"
	"time"

	"github.com/libp2p/go-libp2p/core/connmgr"
	"github.com/libp2p/go-libp2p/core/crypto"
	"github.com/libp2p/go-libp2p/core/host"
	"github.com/libp2p/go-libp2p/core/metrics"
	"github.com/libp2p/go-libp2p/core/network"
	"github.com/libp2p/go-libp2p/core/peer"
	"github.com/libp2p/go-libp2p/core/peerstore"
	"github.com/libp2p/go-libp2p/core/pnet"
	"github.com/libp2p/go-libp2p/core/routing"
	"github.com/libp2p/go-libp2p/core/transport"
	"github.com/libp2p/go-libp2p/p2p/host/autonat"
	"github.com/libp2p/go-libp2p/p2p/host/autorelay"
	bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
	blankhost "github.com/libp2p/go-libp2p/p2p/host/blank"
	"github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem"
	routed "github.com/libp2p/go-libp2p/p2p/host/routed"
	"github.com/libp2p/go-libp2p/p2p/net/swarm"
	tptu "github.com/libp2p/go-libp2p/p2p/net/upgrader"
	circuitv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client"
	relayv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay"
	"github.com/libp2p/go-libp2p/p2p/protocol/holepunch"

	ma "github.com/multiformats/go-multiaddr"
	madns "github.com/multiformats/go-multiaddr-dns"
	"go.uber.org/fx"
	"go.uber.org/fx/fxevent"
)

// AddrsFactory is a function that takes a set of multiaddrs we're listening on and
// returns the set of multiaddrs we should advertise to the network.
type AddrsFactory = bhost.AddrsFactory

// NATManagerC is a NATManager constructor.
type NATManagerC func(network.Network) bhost.NATManager

type RoutingC func(host.Host) (routing.PeerRouting, error)

// AutoNATConfig defines the AutoNAT behavior for the libp2p host.
type AutoNATConfig struct {
	ForceReachability   *network.Reachability
	EnableService       bool
	ThrottleGlobalLimit int
	ThrottlePeerLimit   int
	ThrottleInterval    time.Duration
}

// Config describes a set of settings for a libp2p node
//
// This is *not* a stable interface. Use the options defined in the root
// package.
type Config struct {
	// UserAgent is the identifier this node will send to other peers when
	// identifying itself, e.g. via the identify protocol.
	//
	// Set it via the UserAgent option function.
	UserAgent string

	// ProtocolVersion is the protocol version that identifies the family
	// of protocols used by the peer in the Identify protocol. It is set
	// using the [ProtocolVersion] option.
	ProtocolVersion string

	PeerKey crypto.PrivKey

	Transports         []fx.Option
	Muxers             []tptu.StreamMuxer
	SecurityTransports []fx.Option
	Insecure           bool
	PSK                pnet.PSK

	DialTimeout time.Duration

	RelayCustom bool
	Relay       bool // should the relay transport be used

	EnableRelayService bool // should we run a circuitv2 relay (if publicly reachable)
	RelayServiceOpts   []relayv2.Option

	ListenAddrs     []ma.Multiaddr
	AddrsFactory    bhost.AddrsFactory
	ConnectionGater connmgr.ConnectionGater

	ConnManager     connmgr.ConnManager
	ResourceManager network.ResourceManager

	NATManager NATManagerC
	Peerstore  peerstore.Peerstore
	Reporter   metrics.Reporter

	MultiaddrResolver *madns.Resolver

	DisablePing bool

	Routing RoutingC

	EnableAutoRelay bool
	AutoRelayOpts   []autorelay.Option
	AutoNATConfig

	EnableHolePunching  bool
	HolePunchingOptions []holepunch.Option
}

func (cfg *Config) makeSwarm() (*swarm.Swarm, error) {
	if cfg.Peerstore == nil {
		return nil, fmt.Errorf("no peerstore specified")
	}

	// Check this early. Prevents us from even *starting* without verifying this.
	if pnet.ForcePrivateNetwork && len(cfg.PSK) == 0 {
		log.Error("tried to create a libp2p node with no Private" +
			" Network Protector but usage of Private Networks" +
			" is forced by the enviroment")
		// Note: This is *also* checked the upgrader itself so it'll be
		// enforced even *if* you don't use the libp2p constructor.
		return nil, pnet.ErrNotInPrivateNetwork
	}

	if cfg.PeerKey == nil {
		return nil, fmt.Errorf("no peer key specified")
	}

	// Obtain Peer ID from public key
	pid, err := peer.IDFromPublicKey(cfg.PeerKey.GetPublic())
	if err != nil {
		return nil, err
	}

	if err := cfg.Peerstore.AddPrivKey(pid, cfg.PeerKey); err != nil {
		return nil, err
	}
	if err := cfg.Peerstore.AddPubKey(pid, cfg.PeerKey.GetPublic()); err != nil {
		return nil, err
	}

	opts := make([]swarm.Option, 0, 3)
	if cfg.Reporter != nil {
		opts = append(opts, swarm.WithMetrics(cfg.Reporter))
	}
	if cfg.ConnectionGater != nil {
		opts = append(opts, swarm.WithConnectionGater(cfg.ConnectionGater))
	}
	if cfg.DialTimeout != 0 {
		opts = append(opts, swarm.WithDialTimeout(cfg.DialTimeout))
	}
	if cfg.ResourceManager != nil {
		opts = append(opts, swarm.WithResourceManager(cfg.ResourceManager))
	}
	if cfg.MultiaddrResolver != nil {
		opts = append(opts, swarm.WithMultiaddrResolver(cfg.MultiaddrResolver))
	}
	// TODO: Make the swarm implementation configurable.
	return swarm.NewSwarm(pid, cfg.Peerstore, opts...)
}

func (cfg *Config) addTransports(h host.Host) error {
	swrm, ok := h.Network().(transport.TransportNetwork)
	if !ok {
		// Should probably skip this if no transports.
		return fmt.Errorf("swarm does not support transports")
	}

	var security []fx.Option
	if cfg.Insecure {
		security = append(security, fx.Provide(makeInsecureTransport))
	} else {
		security = cfg.SecurityTransports
	}

	fxopts := []fx.Option{
		fx.WithLogger(func() fxevent.Logger { return getFXLogger() }),
		fx.Provide(tptu.New),
		fx.Provide(fx.Annotate(
			makeSecurityMuxer,
			fx.ParamTags(`group:"security"`),
		)),
		fx.Supply(cfg.Muxers),
		fx.Provide(func() host.Host { return h }),
		fx.Provide(func() crypto.PrivKey { return h.Peerstore().PrivKey(h.ID()) }),
		fx.Provide(func() connmgr.ConnectionGater { return cfg.ConnectionGater }),
		fx.Provide(func() pnet.PSK { return cfg.PSK }),
		fx.Provide(func() network.ResourceManager { return cfg.ResourceManager }),
		fx.Provide(func() *madns.Resolver { return cfg.MultiaddrResolver }),
	}
	fxopts = append(fxopts, cfg.Transports...)
	if !cfg.Insecure {
		fxopts = append(fxopts, security...)
	}

	fxopts = append(fxopts, fx.Invoke(
		fx.Annotate(
			func(tpts []transport.Transport) error {
				for _, t := range tpts {
					if err := swrm.AddTransport(t); err != nil {
						return err
					}
				}
				return nil
			},
			fx.ParamTags(`group:"transport"`),
		)),
	)
	if cfg.Relay {
		fxopts = append(fxopts, fx.Invoke(circuitv2.AddTransport))
	}
	app := fx.New(fxopts...)
	if err := app.Err(); err != nil {
		h.Close()
		return err
	}
	return nil
}

// NewNode constructs a new libp2p Host from the Config.
//
// This function consumes the config. Do not reuse it (really!).
func (cfg *Config) NewNode() (host.Host, error) {
	swrm, err := cfg.makeSwarm()
	if err != nil {
		return nil, err
	}

	h, err := bhost.NewHost(swrm, &bhost.HostOpts{
		ConnManager:         cfg.ConnManager,
		AddrsFactory:        cfg.AddrsFactory,
		NATManager:          cfg.NATManager,
		EnablePing:          !cfg.DisablePing,
		UserAgent:           cfg.UserAgent,
		ProtocolVersion:     cfg.ProtocolVersion,
		EnableHolePunching:  cfg.EnableHolePunching,
		HolePunchingOptions: cfg.HolePunchingOptions,
		EnableRelayService:  cfg.EnableRelayService,
		RelayServiceOpts:    cfg.RelayServiceOpts,
	})
	if err != nil {
		swrm.Close()
		return nil, err
	}

	if cfg.Relay {
		// If we've enabled the relay, we should filter out relay
		// addresses by default.
		//
		// TODO: We shouldn't be doing this here.
		oldFactory := h.AddrsFactory
		h.AddrsFactory = func(addrs []ma.Multiaddr) []ma.Multiaddr {
			return oldFactory(autorelay.Filter(addrs))
		}
	}

	if err := cfg.addTransports(h); err != nil {
		h.Close()
		return nil, err
	}

	// TODO: This method succeeds if listening on one address succeeds. We
	// should probably fail if listening on *any* addr fails.
	if err := h.Network().Listen(cfg.ListenAddrs...); err != nil {
		h.Close()
		return nil, err
	}

	// Configure routing and autorelay
	var router routing.PeerRouting
	if cfg.Routing != nil {
		router, err = cfg.Routing(h)
		if err != nil {
			h.Close()
			return nil, err
		}
	}

	// Note: h.AddrsFactory may be changed by relayFinder, but non-relay version is
	// used by AutoNAT below.
	var ar *autorelay.AutoRelay
	addrF := h.AddrsFactory
	if cfg.EnableAutoRelay {
		if !cfg.Relay {
			h.Close()
			return nil, fmt.Errorf("cannot enable autorelay; relay is not enabled")
		}

		ar, err = autorelay.NewAutoRelay(h, cfg.AutoRelayOpts...)
		if err != nil {
			return nil, err
		}
	}

	autonatOpts := []autonat.Option{
		autonat.UsingAddresses(func() []ma.Multiaddr {
			return addrF(h.AllAddrs())
		}),
	}
	if cfg.AutoNATConfig.ThrottleInterval != 0 {
		autonatOpts = append(autonatOpts,
			autonat.WithThrottling(cfg.AutoNATConfig.ThrottleGlobalLimit, cfg.AutoNATConfig.ThrottleInterval),
			autonat.WithPeerThrottling(cfg.AutoNATConfig.ThrottlePeerLimit))
	}
	if cfg.AutoNATConfig.EnableService {
		autonatPrivKey, _, err := crypto.GenerateEd25519Key(rand.Reader)
		if err != nil {
			return nil, err
		}
		ps, err := pstoremem.NewPeerstore()
		if err != nil {
			return nil, err
		}

		// Pull out the pieces of the config that we _actually_ care about.
		// Specifically, don't setup things like autorelay, listeners,
		// identify, etc.
		autoNatCfg := Config{
			Transports:         cfg.Transports,
			Muxers:             cfg.Muxers,
			SecurityTransports: cfg.SecurityTransports,
			Insecure:           cfg.Insecure,
			PSK:                cfg.PSK,
			ConnectionGater:    cfg.ConnectionGater,
			Reporter:           cfg.Reporter,
			PeerKey:            autonatPrivKey,
			Peerstore:          ps,
		}

		dialer, err := autoNatCfg.makeSwarm()
		if err != nil {
			h.Close()
			return nil, err
		}
		dialerHost := blankhost.NewBlankHost(dialer)
		if err := autoNatCfg.addTransports(dialerHost); err != nil {
			dialerHost.Close()
			h.Close()
			return nil, err
		}
		// NOTE: We're dropping the blank host here but that's fine. It
		// doesn't really _do_ anything and doesn't even need to be
		// closed (as long as we close the underlying network).
		autonatOpts = append(autonatOpts, autonat.EnableService(dialerHost.Network()))
	}
	if cfg.AutoNATConfig.ForceReachability != nil {
		autonatOpts = append(autonatOpts, autonat.WithReachability(*cfg.AutoNATConfig.ForceReachability))
	}

	autonat, err := autonat.New(h, autonatOpts...)
	if err != nil {
		h.Close()
		return nil, fmt.Errorf("cannot enable autorelay; autonat failed to start: %v", err)
	}
	h.SetAutoNat(autonat)

	// start the host background tasks
	h.Start()

	var ho host.Host
	ho = h
	if router != nil {
		ho = routed.Wrap(h, router)
	}
	if ar != nil {
		return autorelay.NewAutoRelayHost(ho, ar), nil
	}
	return ho, nil
}

// Option is a libp2p config option that can be given to the libp2p constructor
// (`libp2p.New`).
type Option func(cfg *Config) error

// Apply applies the given options to the config, returning the first error
// encountered (if any).
func (cfg *Config) Apply(opts ...Option) error {
	for _, opt := range opts {
		if opt == nil {
			continue
		}
		if err := opt(cfg); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>config: supply the host ID to fx<commit_after>package config

import (
	"crypto/rand"
	"fmt"
	"time"

	"github.com/libp2p/go-libp2p/core/connmgr"
	"github.com/libp2p/go-libp2p/core/crypto"
	"github.com/libp2p/go-libp2p/core/host"
	"github.com/libp2p/go-libp2p/core/metrics"
	"github.com/libp2p/go-libp2p/core/network"
	"github.com/libp2p/go-libp2p/core/peer"
	"github.com/libp2p/go-libp2p/core/peerstore"
	"github.com/libp2p/go-libp2p/core/pnet"
	"github.com/libp2p/go-libp2p/core/routing"
	"github.com/libp2p/go-libp2p/core/transport"
	"github.com/libp2p/go-libp2p/p2p/host/autonat"
	"github.com/libp2p/go-libp2p/p2p/host/autorelay"
	bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
	blankhost "github.com/libp2p/go-libp2p/p2p/host/blank"
	"github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem"
	routed "github.com/libp2p/go-libp2p/p2p/host/routed"
	"github.com/libp2p/go-libp2p/p2p/net/swarm"
	tptu "github.com/libp2p/go-libp2p/p2p/net/upgrader"
	circuitv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client"
	relayv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay"
	"github.com/libp2p/go-libp2p/p2p/protocol/holepunch"

	ma "github.com/multiformats/go-multiaddr"
	madns "github.com/multiformats/go-multiaddr-dns"
	"go.uber.org/fx"
	"go.uber.org/fx/fxevent"
)

// AddrsFactory is a function that takes a set of multiaddrs we're listening on and
// returns the set of multiaddrs we should advertise to the network.
type AddrsFactory = bhost.AddrsFactory

// NATManagerC is a NATManager constructor.
type NATManagerC func(network.Network) bhost.NATManager

type RoutingC func(host.Host) (routing.PeerRouting, error)

// AutoNATConfig defines the AutoNAT behavior for the libp2p host.
type AutoNATConfig struct {
	ForceReachability   *network.Reachability
	EnableService       bool
	ThrottleGlobalLimit int
	ThrottlePeerLimit   int
	ThrottleInterval    time.Duration
}

// Config describes a set of settings for a libp2p node
//
// This is *not* a stable interface. Use the options defined in the root
// package.
type Config struct {
	// UserAgent is the identifier this node will send to other peers when
	// identifying itself, e.g. via the identify protocol.
	//
	// Set it via the UserAgent option function.
	UserAgent string

	// ProtocolVersion is the protocol version that identifies the family
	// of protocols used by the peer in the Identify protocol. It is set
	// using the [ProtocolVersion] option.
	ProtocolVersion string

	PeerKey crypto.PrivKey

	Transports         []fx.Option
	Muxers             []tptu.StreamMuxer
	SecurityTransports []fx.Option
	Insecure           bool
	PSK                pnet.PSK

	DialTimeout time.Duration

	RelayCustom bool
	Relay       bool // should the relay transport be used

	EnableRelayService bool // should we run a circuitv2 relay (if publicly reachable)
	RelayServiceOpts   []relayv2.Option

	ListenAddrs     []ma.Multiaddr
	AddrsFactory    bhost.AddrsFactory
	ConnectionGater connmgr.ConnectionGater

	ConnManager     connmgr.ConnManager
	ResourceManager network.ResourceManager

	NATManager NATManagerC
	Peerstore  peerstore.Peerstore
	Reporter   metrics.Reporter

	MultiaddrResolver *madns.Resolver

	DisablePing bool

	Routing RoutingC

	EnableAutoRelay bool
	AutoRelayOpts   []autorelay.Option
	AutoNATConfig

	EnableHolePunching  bool
	HolePunchingOptions []holepunch.Option
}

func (cfg *Config) makeSwarm() (*swarm.Swarm, error) {
	if cfg.Peerstore == nil {
		return nil, fmt.Errorf("no peerstore specified")
	}

	// Check this early. Prevents us from even *starting* without verifying this.
	if pnet.ForcePrivateNetwork && len(cfg.PSK) == 0 {
		log.Error("tried to create a libp2p node with no Private" +
			" Network Protector but usage of Private Networks" +
			" is forced by the enviroment")
		// Note: This is *also* checked the upgrader itself so it'll be
		// enforced even *if* you don't use the libp2p constructor.
		return nil, pnet.ErrNotInPrivateNetwork
	}

	if cfg.PeerKey == nil {
		return nil, fmt.Errorf("no peer key specified")
	}

	// Obtain Peer ID from public key
	pid, err := peer.IDFromPublicKey(cfg.PeerKey.GetPublic())
	if err != nil {
		return nil, err
	}

	if err := cfg.Peerstore.AddPrivKey(pid, cfg.PeerKey); err != nil {
		return nil, err
	}
	if err := cfg.Peerstore.AddPubKey(pid, cfg.PeerKey.GetPublic()); err != nil {
		return nil, err
	}

	opts := make([]swarm.Option, 0, 3)
	if cfg.Reporter != nil {
		opts = append(opts, swarm.WithMetrics(cfg.Reporter))
	}
	if cfg.ConnectionGater != nil {
		opts = append(opts, swarm.WithConnectionGater(cfg.ConnectionGater))
	}
	if cfg.DialTimeout != 0 {
		opts = append(opts, swarm.WithDialTimeout(cfg.DialTimeout))
	}
	if cfg.ResourceManager != nil {
		opts = append(opts, swarm.WithResourceManager(cfg.ResourceManager))
	}
	if cfg.MultiaddrResolver != nil {
		opts = append(opts, swarm.WithMultiaddrResolver(cfg.MultiaddrResolver))
	}
	// TODO: Make the swarm implementation configurable.
	return swarm.NewSwarm(pid, cfg.Peerstore, opts...)
}

func (cfg *Config) addTransports(h host.Host) error {
	swrm, ok := h.Network().(transport.TransportNetwork)
	if !ok {
		// Should probably skip this if no transports.
		return fmt.Errorf("swarm does not support transports")
	}

	var security []fx.Option
	if cfg.Insecure {
		security = append(security, fx.Provide(makeInsecureTransport))
	} else {
		security = cfg.SecurityTransports
	}

	fxopts := []fx.Option{
		fx.WithLogger(func() fxevent.Logger { return getFXLogger() }),
		fx.Provide(tptu.New),
		fx.Provide(fx.Annotate(
			makeSecurityMuxer,
			fx.ParamTags(`group:"security"`),
		)),
		fx.Supply(cfg.Muxers),
		fx.Supply(h.ID()),
		fx.Provide(func() host.Host { return h }),
		fx.Provide(func() crypto.PrivKey { return h.Peerstore().PrivKey(h.ID()) }),
		fx.Provide(func() connmgr.ConnectionGater { return cfg.ConnectionGater }),
		fx.Provide(func() pnet.PSK { return cfg.PSK }),
		fx.Provide(func() network.ResourceManager { return cfg.ResourceManager }),
		fx.Provide(func() *madns.Resolver { return cfg.MultiaddrResolver }),
	}
	fxopts = append(fxopts, cfg.Transports...)
	if !cfg.Insecure {
		fxopts = append(fxopts, security...)
	}

	fxopts = append(fxopts, fx.Invoke(
		fx.Annotate(
			func(tpts []transport.Transport) error {
				for _, t := range tpts {
					if err := swrm.AddTransport(t); err != nil {
						return err
					}
				}
				return nil
			},
			fx.ParamTags(`group:"transport"`),
		)),
	)
	if cfg.Relay {
		fxopts = append(fxopts, fx.Invoke(circuitv2.AddTransport))
	}
	app := fx.New(fxopts...)
	if err := app.Err(); err != nil {
		h.Close()
		return err
	}
	return nil
}

// NewNode constructs a new libp2p Host from the Config.
//
// This function consumes the config. Do not reuse it (really!).
func (cfg *Config) NewNode() (host.Host, error) {
	swrm, err := cfg.makeSwarm()
	if err != nil {
		return nil, err
	}

	h, err := bhost.NewHost(swrm, &bhost.HostOpts{
		ConnManager:         cfg.ConnManager,
		AddrsFactory:        cfg.AddrsFactory,
		NATManager:          cfg.NATManager,
		EnablePing:          !cfg.DisablePing,
		UserAgent:           cfg.UserAgent,
		ProtocolVersion:     cfg.ProtocolVersion,
		EnableHolePunching:  cfg.EnableHolePunching,
		HolePunchingOptions: cfg.HolePunchingOptions,
		EnableRelayService:  cfg.EnableRelayService,
		RelayServiceOpts:    cfg.RelayServiceOpts,
	})
	if err != nil {
		swrm.Close()
		return nil, err
	}

	if cfg.Relay {
		// If we've enabled the relay, we should filter out relay
		// addresses by default.
		//
		// TODO: We shouldn't be doing this here.
		oldFactory := h.AddrsFactory
		h.AddrsFactory = func(addrs []ma.Multiaddr) []ma.Multiaddr {
			return oldFactory(autorelay.Filter(addrs))
		}
	}

	if err := cfg.addTransports(h); err != nil {
		h.Close()
		return nil, err
	}

	// TODO: This method succeeds if listening on one address succeeds. We
	// should probably fail if listening on *any* addr fails.
	if err := h.Network().Listen(cfg.ListenAddrs...); err != nil {
		h.Close()
		return nil, err
	}

	// Configure routing and autorelay
	var router routing.PeerRouting
	if cfg.Routing != nil {
		router, err = cfg.Routing(h)
		if err != nil {
			h.Close()
			return nil, err
		}
	}

	// Note: h.AddrsFactory may be changed by relayFinder, but non-relay version is
	// used by AutoNAT below.
	var ar *autorelay.AutoRelay
	addrF := h.AddrsFactory
	if cfg.EnableAutoRelay {
		if !cfg.Relay {
			h.Close()
			return nil, fmt.Errorf("cannot enable autorelay; relay is not enabled")
		}

		ar, err = autorelay.NewAutoRelay(h, cfg.AutoRelayOpts...)
		if err != nil {
			return nil, err
		}
	}

	autonatOpts := []autonat.Option{
		autonat.UsingAddresses(func() []ma.Multiaddr {
			return addrF(h.AllAddrs())
		}),
	}
	if cfg.AutoNATConfig.ThrottleInterval != 0 {
		autonatOpts = append(autonatOpts,
			autonat.WithThrottling(cfg.AutoNATConfig.ThrottleGlobalLimit, cfg.AutoNATConfig.ThrottleInterval),
			autonat.WithPeerThrottling(cfg.AutoNATConfig.ThrottlePeerLimit))
	}
	if cfg.AutoNATConfig.EnableService {
		autonatPrivKey, _, err := crypto.GenerateEd25519Key(rand.Reader)
		if err != nil {
			return nil, err
		}
		ps, err := pstoremem.NewPeerstore()
		if err != nil {
			return nil, err
		}

		// Pull out the pieces of the config that we _actually_ care about.
		// Specifically, don't setup things like autorelay, listeners,
		// identify, etc.
		autoNatCfg := Config{
			Transports:         cfg.Transports,
			Muxers:             cfg.Muxers,
			SecurityTransports: cfg.SecurityTransports,
			Insecure:           cfg.Insecure,
			PSK:                cfg.PSK,
			ConnectionGater:    cfg.ConnectionGater,
			Reporter:           cfg.Reporter,
			PeerKey:            autonatPrivKey,
			Peerstore:          ps,
		}

		dialer, err := autoNatCfg.makeSwarm()
		if err != nil {
			h.Close()
			return nil, err
		}
		dialerHost := blankhost.NewBlankHost(dialer)
		if err := autoNatCfg.addTransports(dialerHost); err != nil {
			dialerHost.Close()
			h.Close()
			return nil, err
		}
		// NOTE: We're dropping the blank host here but that's fine. It
		// doesn't really _do_ anything and doesn't even need to be
		// closed (as long as we close the underlying network).
		autonatOpts = append(autonatOpts, autonat.EnableService(dialerHost.Network()))
	}
	if cfg.AutoNATConfig.ForceReachability != nil {
		autonatOpts = append(autonatOpts, autonat.WithReachability(*cfg.AutoNATConfig.ForceReachability))
	}

	autonat, err := autonat.New(h, autonatOpts...)
	if err != nil {
		h.Close()
		return nil, fmt.Errorf("cannot enable autorelay; autonat failed to start: %v", err)
	}
	h.SetAutoNat(autonat)

	// start the host background tasks
	h.Start()

	var ho host.Host
	ho = h
	if router != nil {
		ho = routed.Wrap(h, router)
	}
	if ar != nil {
		return autorelay.NewAutoRelayHost(ho, ar), nil
	}
	return ho, nil
}

// Option is a libp2p config option that can be given to the libp2p constructor
// (`libp2p.New`).
type Option func(cfg *Config) error

// Apply applies the given options to the config, returning the first error
// encountered (if any).
func (cfg *Config) Apply(opts ...Option) error {
	for _, opt := range opts {
		if opt == nil {
			continue
		}
		if err := opt(cfg); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package config

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

	"github.com/gsamokovarov/jump/scoring"
)

const (
	defaultScoreFile  = "scores.json"
	defaultSearchFile = "search.json"
	defaultPinsFile   = "pins.json"
	defaultDirName    = ".jump"
)

// Config represents the config directory and all the miscellaneous
// configuration files we can have in there.
type Config interface {
	ReadEntries() (scoring.Entries, error)
	WriteEntries(scoring.Entries) error

	ReadSearch() Search
	WriteSearch(string, int) error

	ReadPins() (map[string]string, error)
	FindPin(string) (string, bool)
	WritePin(string, string) error
	RemovePin(string) error
}

type fileConfig struct {
	Dir    string
	Scores string
	Search string
	Pins   string
}

// Setup setups the config folder from a directory path.
//
// If the directories don't already exists, they are created and if the score
// file is present, it is loaded.
func Setup(dir string) (Config, error) {
	// We get the directory check for free form os.MkdirAll.
	if err := os.MkdirAll(dir, 0755); err != nil {
		return nil, err
	}

	scores := filepath.Join(dir, defaultScoreFile)
	search := filepath.Join(dir, defaultSearchFile)
	pins := filepath.Join(dir, defaultPinsFile)

	return &fileConfig{dir, scores, search, pins}, nil
}

// SetupDefault setups the config folder from a directory path.
//
// If the directory path is an empty string, the path is automatically guessed.
func SetupDefault(dir string) (Config, error) {
	dir, err := normalizeDir(dir)
	if err != nil {
		return nil, err
	}

	return Setup(dir)
}

func normalizeDir(dir string) (string, error) {
	if dir != "" {
		return dir, nil
	}

	home, err := homeDir()
	if err != nil {
		return dir, err
	}

	return filepath.Join(home, defaultDirName), nil
}

// See https://github.com/golang/go/issues/26463
func homeDir() (string, error) {
	home := os.Getenv("HOME")
	if home != "" {
		return home, nil
	}
	usr, err := user.Current()
	if err != nil {
		return "", err
	}

	return usr.HomeDir, nil
}
<commit_msg>add support xdg base dir<commit_after>package config

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

	"github.com/gsamokovarov/jump/scoring"
)

const (
	defaultScoreFile  = "scores.json"
	defaultSearchFile = "search.json"
	defaultPinsFile   = "pins.json"
	defaultDirName    = ".jump"
)

// Config represents the config directory and all the miscellaneous
// configuration files we can have in there.
type Config interface {
	ReadEntries() (scoring.Entries, error)
	WriteEntries(scoring.Entries) error

	ReadSearch() Search
	WriteSearch(string, int) error

	ReadPins() (map[string]string, error)
	FindPin(string) (string, bool)
	WritePin(string, string) error
	RemovePin(string) error
}

type fileConfig struct {
	Dir    string
	Scores string
	Search string
	Pins   string
}

// Setup setups the config folder from a directory path.
//
// If the directories don't already exists, they are created and if the score
// file is present, it is loaded.
func Setup(dir string) (Config, error) {
	// We get the directory check for free form os.MkdirAll.
	if err := os.MkdirAll(dir, 0755); err != nil {
		return nil, err
	}

	scores := filepath.Join(dir, defaultScoreFile)
	search := filepath.Join(dir, defaultSearchFile)
	pins := filepath.Join(dir, defaultPinsFile)

	return &fileConfig{dir, scores, search, pins}, nil
}

// SetupDefault setups the config folder from a directory path.
//
// If the directory path is an empty string, the path is automatically guessed.
func SetupDefault(dir string) (Config, error) {
	dir, err := normalizeDir(dir)
	if err != nil {
		return nil, err
	}

	return Setup(dir)
}

func normalizeDir(dir string) (string, error) {
	if dir != "" {
		return dir, nil
	}

	if xdg := os.Getenv("XDG_CONFIG_HOME"); xdg != "" {
		return filepath.Join(xdg, "jump"), nil
	}

	home, err := homeDir()
	if err != nil {
		return dir, err
	}

	return filepath.Join(home, defaultDirName), nil
}

// See https://github.com/golang/go/issues/26463
func homeDir() (string, error) {
	home := os.Getenv("HOME")
	if home != "" {
		return home, nil
	}
	usr, err := user.Current()
	if err != nil {
		return "", err
	}

	return usr.HomeDir, nil
}
<|endoftext|>


<commit_before>package config

import (
	"errors"
	"os"
	"path/filepath"
	"runtime"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/mitchellh/go-homedir"
)

var ContentTypes = map[string]string{
	"json":      "application/json",
	"form":      "application/x-www-form-urlencoded",
	"multipart": "multipart/form-data",
}

// Duration is used to automatically unmarshal timeout strings to
// time.Duration values
type Duration struct {
	time.Duration
}

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

type Config struct {
	General GeneralOptions
	Keys    map[string]map[string]string
}

type GeneralOptions struct {
	ContextSpecificSearch  bool
	DefaultURLScheme       string
	Editor                 string
	FollowRedirects        bool
	FormatJSON             bool
	Insecure               bool
	PreserveScrollPosition bool
	StatusLine             string
	TLSVersionMax          uint16
	TLSVersionMin          uint16
	Timeout                Duration
}

var defaultTimeoutDuration, _ = time.ParseDuration("1m")

var DefaultKeys = map[string]map[string]string{
	"global": {
		"CtrlR": "submit",
		"CtrlC": "quit",
		"CtrlS": "saveResponse",
		"CtrlF": "loadRequest",
		"CtrlE": "saveRequest",
		"CtrlD": "deleteLine",
		"CtrlW": "deleteWord",
		"CtrlO": "openEditor",
		"CtrlT": "toggleContextSpecificSearch",
		"CtrlX": "clearHistory",
		"Tab":   "nextView",
		"CtrlJ": "nextView",
		"CtrlK": "prevView",
		"AltH":  "history",
		"F2":    "focus url",
		"F3":    "focus get",
		"F4":    "focus method",
		"F5":    "focus data",
		"F6":    "focus headers",
		"F7":    "focus search",
		"F8":    "focus response-headers",
		"F9":    "focus response-body",
		"F12":   "redirectRestriction",
	},
	"url": {
		"Enter": "submit",
	},
	"response-headers": {
		"ArrowUp":   "scrollUp",
		"ArrowDown": "scrollDown",
		"PageUp":    "pageUp",
		"PageDown":  "pageDown",
	},
	"response-body": {
		"ArrowUp":   "scrollUp",
		"ArrowDown": "scrollDown",
		"PageUp":    "pageUp",
		"PageDown":  "pageDown",
	},
	"help": {
		"ArrowUp":   "scrollUp",
		"ArrowDown": "scrollDown",
		"PageUp":    "pageUp",
		"PageDown":  "pageDown",
	},
}

var DefaultConfig = Config{
	General: GeneralOptions{
		DefaultURLScheme:       "https",
		Editor:                 "vim",
		FollowRedirects:        true,
		FormatJSON:             true,
		Insecure:               false,
		PreserveScrollPosition: true,
		StatusLine:             "[wuzz {{.Version}}]{{if .Duration}} [Response time: {{.Duration}}]{{end}} [Request no.: {{.RequestNumber}}/{{.HistorySize}}] [Search type: {{.SearchType}}]{{if .DisableRedirect}} [Redirects Restricted Mode {{.DisableRedirect}}]{{end}}",
		Timeout: Duration{
			defaultTimeoutDuration,
		},
	},
}

func init() {
	if os.Getenv("EDITOR") != "" {
		DefaultConfig.General.Editor = os.Getenv("EDITOR")
	}
}

func LoadConfig(configFile string) (*Config, error) {
	if _, err := os.Stat(configFile); os.IsNotExist(err) {
		return nil, errors.New("Config file does not exist.")
	} else if err != nil {
		return nil, err
	}

	conf := DefaultConfig
	if _, err := toml.DecodeFile(configFile, &conf); err != nil {
		return nil, err
	}

	if conf.Keys == nil {
		conf.Keys = DefaultKeys
	} else {
		// copy default keys
		for keyCategory, keys := range DefaultKeys {
			confKeys, found := conf.Keys[keyCategory]
			if found {
				for key, action := range keys {
					if _, found := confKeys[key]; !found {
						conf.Keys[keyCategory][key] = action
					}
				}
			} else {
				conf.Keys[keyCategory] = keys
			}
		}
	}

	return &conf, nil
}

func GetDefaultConfigLocation() string {
	var configFolderLocation string
	switch runtime.GOOS {
	case "linux":
		// Use the XDG_CONFIG_HOME variable if it is set, otherwise
		// $HOME/.config/wuzz/config.toml
		xdgConfigHome := os.Getenv("XDG_CONFIG_HOME")
		if xdgConfigHome != "" {
			configFolderLocation = xdgConfigHome
		} else {
			configFolderLocation, _ = homedir.Expand("~/.config/wuzz/")
		}

	default:
		// On other platforms we just use $HOME/.wuzz
		configFolderLocation, _ = homedir.Expand("~/.wuzz/")
	}

	return filepath.Join(configFolderLocation, "config.toml")
}
<commit_msg>[mod] rebind redirect restriction to F11 to be able to use it on linux/urxwt<commit_after>package config

import (
	"errors"
	"os"
	"path/filepath"
	"runtime"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/mitchellh/go-homedir"
)

var ContentTypes = map[string]string{
	"json":      "application/json",
	"form":      "application/x-www-form-urlencoded",
	"multipart": "multipart/form-data",
}

// Duration is used to automatically unmarshal timeout strings to
// time.Duration values
type Duration struct {
	time.Duration
}

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

type Config struct {
	General GeneralOptions
	Keys    map[string]map[string]string
}

type GeneralOptions struct {
	ContextSpecificSearch  bool
	DefaultURLScheme       string
	Editor                 string
	FollowRedirects        bool
	FormatJSON             bool
	Insecure               bool
	PreserveScrollPosition bool
	StatusLine             string
	TLSVersionMax          uint16
	TLSVersionMin          uint16
	Timeout                Duration
}

var defaultTimeoutDuration, _ = time.ParseDuration("1m")

var DefaultKeys = map[string]map[string]string{
	"global": {
		"CtrlR": "submit",
		"CtrlC": "quit",
		"CtrlS": "saveResponse",
		"CtrlF": "loadRequest",
		"CtrlE": "saveRequest",
		"CtrlD": "deleteLine",
		"CtrlW": "deleteWord",
		"CtrlO": "openEditor",
		"CtrlT": "toggleContextSpecificSearch",
		"CtrlX": "clearHistory",
		"Tab":   "nextView",
		"CtrlJ": "nextView",
		"CtrlK": "prevView",
		"AltH":  "history",
		"F2":    "focus url",
		"F3":    "focus get",
		"F4":    "focus method",
		"F5":    "focus data",
		"F6":    "focus headers",
		"F7":    "focus search",
		"F8":    "focus response-headers",
		"F9":    "focus response-body",
		"F11":   "redirectRestriction",
	},
	"url": {
		"Enter": "submit",
	},
	"response-headers": {
		"ArrowUp":   "scrollUp",
		"ArrowDown": "scrollDown",
		"PageUp":    "pageUp",
		"PageDown":  "pageDown",
	},
	"response-body": {
		"ArrowUp":   "scrollUp",
		"ArrowDown": "scrollDown",
		"PageUp":    "pageUp",
		"PageDown":  "pageDown",
	},
	"help": {
		"ArrowUp":   "scrollUp",
		"ArrowDown": "scrollDown",
		"PageUp":    "pageUp",
		"PageDown":  "pageDown",
	},
}

var DefaultConfig = Config{
	General: GeneralOptions{
		DefaultURLScheme:       "https",
		Editor:                 "vim",
		FollowRedirects:        true,
		FormatJSON:             true,
		Insecure:               false,
		PreserveScrollPosition: true,
		StatusLine:             "[wuzz {{.Version}}]{{if .Duration}} [Response time: {{.Duration}}]{{end}} [Request no.: {{.RequestNumber}}/{{.HistorySize}}] [Search type: {{.SearchType}}]{{if .DisableRedirect}} [Redirects Restricted Mode {{.DisableRedirect}}]{{end}}",
		Timeout: Duration{
			defaultTimeoutDuration,
		},
	},
}

func init() {
	if os.Getenv("EDITOR") != "" {
		DefaultConfig.General.Editor = os.Getenv("EDITOR")
	}
}

func LoadConfig(configFile string) (*Config, error) {
	if _, err := os.Stat(configFile); os.IsNotExist(err) {
		return nil, errors.New("Config file does not exist.")
	} else if err != nil {
		return nil, err
	}

	conf := DefaultConfig
	if _, err := toml.DecodeFile(configFile, &conf); err != nil {
		return nil, err
	}

	if conf.Keys == nil {
		conf.Keys = DefaultKeys
	} else {
		// copy default keys
		for keyCategory, keys := range DefaultKeys {
			confKeys, found := conf.Keys[keyCategory]
			if found {
				for key, action := range keys {
					if _, found := confKeys[key]; !found {
						conf.Keys[keyCategory][key] = action
					}
				}
			} else {
				conf.Keys[keyCategory] = keys
			}
		}
	}

	return &conf, nil
}

func GetDefaultConfigLocation() string {
	var configFolderLocation string
	switch runtime.GOOS {
	case "linux":
		// Use the XDG_CONFIG_HOME variable if it is set, otherwise
		// $HOME/.config/wuzz/config.toml
		xdgConfigHome := os.Getenv("XDG_CONFIG_HOME")
		if xdgConfigHome != "" {
			configFolderLocation = xdgConfigHome
		} else {
			configFolderLocation, _ = homedir.Expand("~/.config/wuzz/")
		}

	default:
		// On other platforms we just use $HOME/.wuzz
		configFolderLocation, _ = homedir.Expand("~/.wuzz/")
	}

	return filepath.Join(configFolderLocation, "config.toml")
}
<|endoftext|>


<commit_before>package config

import (
	"bytes"
	"strings"
	"sync"

	"veyron.io/veyron/veyron2/verror"
	"veyron.io/veyron/veyron2/vom"
)

var ErrKeyNotFound = verror.NotFoundf("config key not found")

// TODO(caprita): Move the interface to veyron2 and integrate with
// veyron/services/config.

// Config defines a simple key-value configuration.  Keys and values are
// strings, and a key can have exactly one value.  The client is responsible for
// encoding structured values, or multiple values, in the provided string.
//
// Config data can come from several sources:
// - passed from parent process to child process through pipe;
// - using environment variables or flags;
// - via the neighborhood-based config service;
// - by RPCs using the Config idl;
// - manually, by calling the Set method.
//
// This interface makes no assumptions about the source of the configuration,
// but provides a unified API for accessing it.
type Config interface {
	// Set sets the value for the key.  If the key already exists in the
	// config, its value is overwritten.
	Set(key, value string)
	// Get returns the value for the key.  If the key doesn't exist in the
	// config, Get returns an error.
	Get(key string) (string, error)
	// Serialize serializes the config to a string.
	Serialize() (string, error)
	// MergeFrom deserializes config information from a string created using
	// Serialize(), and merges this information into the config, updating
	// values for keys that already exist and creating new key-value pairs
	// for keys that don't.
	MergeFrom(string) error
}

type cfg struct {
	sync.RWMutex
	m map[string]string
}

// New creates a new empty config.
func New() Config {
	return &cfg{m: make(map[string]string)}
}

func (c cfg) Set(key, value string) {
	c.Lock()
	defer c.Unlock()
	c.m[key] = value
}

func (c cfg) Get(key string) (string, error) {
	c.RLock()
	defer c.RUnlock()
	v, ok := c.m[key]
	if !ok {
		return "", ErrKeyNotFound
	}
	return v, nil
}

func (c cfg) Serialize() (string, error) {
	var buf bytes.Buffer
	c.RLock()
	defer c.RUnlock()
	if err := vom.NewEncoder(&buf).Encode(c.m); err != nil {
		return "", err
	}
	return buf.String(), nil
}

func (c cfg) MergeFrom(serialized string) error {
	var newM map[string]string
	if err := vom.NewDecoder(strings.NewReader(serialized)).Decode(&newM); err != nil {
		return err
	}
	c.Lock()
	defer c.Unlock()
	for k, v := range newM {
		c.m[k] = v
	}
	return nil
}
<commit_msg>veyron2/verror: Rename NotFound and NotAuthorized, introduce NoExistOrNoAccess.<commit_after>package config

import (
	"bytes"
	"strings"
	"sync"

	"veyron.io/veyron/veyron2/verror"
	"veyron.io/veyron/veyron2/vom"
)

var ErrKeyNotFound = verror.NoExistf("config key not found")

// TODO(caprita): Move the interface to veyron2 and integrate with
// veyron/services/config.

// Config defines a simple key-value configuration.  Keys and values are
// strings, and a key can have exactly one value.  The client is responsible for
// encoding structured values, or multiple values, in the provided string.
//
// Config data can come from several sources:
// - passed from parent process to child process through pipe;
// - using environment variables or flags;
// - via the neighborhood-based config service;
// - by RPCs using the Config idl;
// - manually, by calling the Set method.
//
// This interface makes no assumptions about the source of the configuration,
// but provides a unified API for accessing it.
type Config interface {
	// Set sets the value for the key.  If the key already exists in the
	// config, its value is overwritten.
	Set(key, value string)
	// Get returns the value for the key.  If the key doesn't exist in the
	// config, Get returns an error.
	Get(key string) (string, error)
	// Serialize serializes the config to a string.
	Serialize() (string, error)
	// MergeFrom deserializes config information from a string created using
	// Serialize(), and merges this information into the config, updating
	// values for keys that already exist and creating new key-value pairs
	// for keys that don't.
	MergeFrom(string) error
}

type cfg struct {
	sync.RWMutex
	m map[string]string
}

// New creates a new empty config.
func New() Config {
	return &cfg{m: make(map[string]string)}
}

func (c cfg) Set(key, value string) {
	c.Lock()
	defer c.Unlock()
	c.m[key] = value
}

func (c cfg) Get(key string) (string, error) {
	c.RLock()
	defer c.RUnlock()
	v, ok := c.m[key]
	if !ok {
		return "", ErrKeyNotFound
	}
	return v, nil
}

func (c cfg) Serialize() (string, error) {
	var buf bytes.Buffer
	c.RLock()
	defer c.RUnlock()
	if err := vom.NewEncoder(&buf).Encode(c.m); err != nil {
		return "", err
	}
	return buf.String(), nil
}

func (c cfg) MergeFrom(serialized string) error {
	var newM map[string]string
	if err := vom.NewDecoder(strings.NewReader(serialized)).Decode(&newM); err != nil {
		return err
	}
	c.Lock()
	defer c.Unlock()
	for k, v := range newM {
		c.m[k] = v
	}
	return nil
}
<|endoftext|>


<commit_before>package indicators_test

import (
	. "github.com/onsi/ginkgo"
	"github.com/thetruetrade/gotrade"
	. "github.com/thetruetrade/gotrade/indicators"
)

var _ = Describe("when calculating a triangular moving average (trima)", func() {
	var (
		period    int = 6
		indicator *Trima
		inputs    IndicatorWithFloatBoundsSharedSpecInputs
	)

	BeforeEach(func() {
		indicator, _ = NewTrima(period, gotrade.UseClosePrice)

		inputs = NewIndicatorWithFloatBoundsSharedSpecInputs(indicator, len(sourceDOHLCVData), indicator,
			func() float64 {
				return GetFloatDataMax(indicator.Data)
			},
			func() float64 {
				return GetFloatDataMin(indicator.Data)
			})
	})

	Context("and the indicator has not yet received any ticks", func() {
		ShouldBeAnInitialisedIndicator(&inputs)

		ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
	})

	Context("and the indicator has received less ticks than the lookback period", func() {

		BeforeEach(func() {
			for i := 0; i < indicator.GetLookbackPeriod(); i++ {
				indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
			}
		})

		ShouldBeAnIndicatorThatHasReceivedFewerTicksThanItsLookbackPeriod(&inputs)

		ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
	})

	Context("and the indicator has received ticks equal to the lookback period", func() {

		BeforeEach(func() {
			for i := 0; i <= indicator.GetLookbackPeriod(); i++ {
				indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
			}
		})

		ShouldBeAnIndicatorThatHasReceivedTicksEqualToItsLookbackPeriod(&inputs)

		ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)
	})

	Context("and the indicator has received more ticks than the lookback period", func() {

		BeforeEach(func() {
			for i := range sourceDOHLCVData {
				indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
			}
		})

		ShouldBeAnIndicatorThatHasReceivedMoreTicksThanItsLookbackPeriod(&inputs)

		ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)
	})

	Context("and the indicator has recieved all of its ticks", func() {
		BeforeEach(func() {
			for i := 0; i < len(sourceDOHLCVData); i++ {
				indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
			}
		})

		ShouldBeAnIndicatorThatHasReceivedAllOfItsTicks(&inputs)

		ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)
	})
})
<commit_msg>#77 achieve 100% test coverage for indicators - trima<commit_after>package indicators_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/thetruetrade/gotrade"
	"github.com/thetruetrade/gotrade/indicators"
)

var _ = Describe("when creating an trimawithoutstorage", func() {
	var (
		indicator      *indicators.TrimaWithoutStorage
		indicatorError error
	)

	Context("and the indicator was not given a value available action", func() {
		BeforeEach(func() {
			indicator, indicatorError = indicators.NewTrimaWithoutStorage(4, nil)
		})

		It("the indicator should not be created and return the appropriate error message", func() {
			Expect(indicator).To(BeNil())
			Expect(indicatorError).To(Equal(indicators.ErrValueAvailableActionIsNil))
		})
	})

	Context("and the indicator was given a timePeriod below the minimum", func() {
		BeforeEach(func() {
			indicator, indicatorError = indicators.NewTrimaWithoutStorage(1, fakeFloatValAvailable)
		})

		It("the indicator should not be created and return the appropriate error message", func() {
			Expect(indicator).To(BeNil())
		})
	})

	Context("and the indicator was given a timePeriod above the maximum", func() {
		BeforeEach(func() {
			indicator, indicatorError = indicators.NewTrimaWithoutStorage(indicators.MaximumLookbackPeriod+1, fakeFloatValAvailable)
		})

		It("the indicator should not be created and return the appropriate error message", func() {
			Expect(indicator).To(BeNil())
		})
	})
})

var _ = Describe("when calculating a triangular moving average (trima)", func() {
	var (
		period    int = 6
		indicator *indicators.Trima
		inputs    IndicatorWithFloatBoundsSharedSpecInputs
		stream    *fakeDOHLCVStreamSubscriber
	)

	Context("given the indicator is created via the standard constructor", func() {
		BeforeEach(func() {
			indicator, _ = indicators.NewTrima(period, gotrade.UseClosePrice)

			inputs = NewIndicatorWithFloatBoundsSharedSpecInputs(indicator, len(sourceDOHLCVData), indicator,
				func() float64 {
					return GetFloatDataMax(indicator.Data)
				},
				func() float64 {
					return GetFloatDataMin(indicator.Data)
				})
		})

		Context("and the indicator has not yet received any ticks", func() {
			ShouldBeAnInitialisedIndicator(&inputs)

			ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
		})

		Context("and the indicator has received less ticks than the lookback period", func() {

			BeforeEach(func() {
				for i := 0; i < indicator.GetLookbackPeriod(); i++ {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedFewerTicksThanItsLookbackPeriod(&inputs)

			ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
		})

		Context("and the indicator has received ticks equal to the lookback period", func() {

			BeforeEach(func() {
				for i := 0; i <= indicator.GetLookbackPeriod(); i++ {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedTicksEqualToItsLookbackPeriod(&inputs)

			ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)
		})

		Context("and the indicator has received more ticks than the lookback period", func() {

			BeforeEach(func() {
				for i := range sourceDOHLCVData {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedMoreTicksThanItsLookbackPeriod(&inputs)

			ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)
		})

		Context("and the indicator has recieved all of its ticks", func() {
			BeforeEach(func() {
				for i := 0; i < len(sourceDOHLCVData); i++ {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedAllOfItsTicks(&inputs)

			ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)
		})
	})

	Context("given the indicator is created via the constructor with defaulted parameters", func() {
		BeforeEach(func() {
			indicator, _ = indicators.NewDefaultTrima()
			inputs = NewIndicatorWithFloatBoundsSharedSpecInputs(indicator, len(sourceDOHLCVData), indicator,
				func() float64 {
					return GetFloatDataMax(indicator.Data)
				},
				func() float64 {
					return GetFloatDataMin(indicator.Data)
				})
		})

		Context("and the indicator has not yet received any ticks", func() {
			ShouldBeAnInitialisedIndicator(&inputs)

			ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
		})

		Context("and the indicator has recieved all of its ticks", func() {
			BeforeEach(func() {
				for i := 0; i < len(sourceDOHLCVData); i++ {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedAllOfItsTicks(&inputs)

			ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)
		})
	})

	Context("given the indicator is created via the constructor with fixed source length", func() {
		BeforeEach(func() {
			indicator, _ = indicators.NewTrimaWithSrcLen(uint(len(sourceDOHLCVData)), 4, gotrade.UseClosePrice)
			inputs = NewIndicatorWithFloatBoundsSharedSpecInputs(indicator, len(sourceDOHLCVData), indicator,
				func() float64 {
					return GetFloatDataMax(indicator.Data)
				},
				func() float64 {
					return GetFloatDataMin(indicator.Data)
				})
		})

		It("should have pre-allocated storge for the output data", func() {
			Expect(cap(indicator.Data)).To(Equal(len(sourceDOHLCVData) - indicator.GetLookbackPeriod()))
		})

		Context("and the indicator has not yet received any ticks", func() {
			ShouldBeAnInitialisedIndicator(&inputs)

			ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
		})

		Context("and the indicator has recieved all of its ticks", func() {
			BeforeEach(func() {
				for i := 0; i < len(sourceDOHLCVData); i++ {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedAllOfItsTicks(&inputs)

			ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)

			It("no new storage capcity should have been allocated", func() {
				Expect(len(indicator.Data)).To(Equal(cap(indicator.Data)))
			})
		})
	})

	Context("given the indicator is created via the constructor with defaulted parameters and fixed source length", func() {
		BeforeEach(func() {
			indicator, _ = indicators.NewDefaultTrimaWithSrcLen(uint(len(sourceDOHLCVData)))
			inputs = NewIndicatorWithFloatBoundsSharedSpecInputs(indicator, len(sourceDOHLCVData), indicator,
				func() float64 {
					return GetFloatDataMax(indicator.Data)
				},
				func() float64 {
					return GetFloatDataMin(indicator.Data)
				})
		})

		It("should have pre-allocated storge for the output data", func() {
			Expect(cap(indicator.Data)).To(Equal(len(sourceDOHLCVData) - indicator.GetLookbackPeriod()))
		})

		Context("and the indicator has not yet received any ticks", func() {
			ShouldBeAnInitialisedIndicator(&inputs)

			ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
		})

		Context("and the indicator has recieved all of its ticks", func() {
			BeforeEach(func() {
				for i := 0; i < len(sourceDOHLCVData); i++ {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedAllOfItsTicks(&inputs)

			ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)

			It("no new storage capcity should have been allocated", func() {
				Expect(len(indicator.Data)).To(Equal(cap(indicator.Data)))
			})
		})
	})

	Context("given the indicator is created via the constructor for use with a price stream", func() {
		BeforeEach(func() {
			stream = newFakeDOHLCVStreamSubscriber()
			indicator, _ = indicators.NewTrimaForStream(stream, 4, gotrade.UseClosePrice)
			inputs = NewIndicatorWithFloatBoundsSharedSpecInputs(indicator, len(sourceDOHLCVData), indicator,
				func() float64 {
					return GetFloatDataMax(indicator.Data)
				},
				func() float64 {
					return GetFloatDataMin(indicator.Data)
				})
		})

		It("should have requested to be attached to the stream", func() {
			Expect(stream.lastCallToAddTickSubscriptionArg).To(Equal(indicator))
		})

		Context("and the indicator has not yet received any ticks", func() {
			ShouldBeAnInitialisedIndicator(&inputs)

			ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
		})

		Context("and the indicator has recieved all of its ticks", func() {
			BeforeEach(func() {
				for i := 0; i < len(sourceDOHLCVData); i++ {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedAllOfItsTicks(&inputs)

			ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)
		})
	})

	Context("given the indicator is created via the constructor for use with a price stream with defaulted parameters", func() {
		BeforeEach(func() {
			stream = newFakeDOHLCVStreamSubscriber()
			indicator, _ = indicators.NewDefaultTrimaForStream(stream)
			inputs = NewIndicatorWithFloatBoundsSharedSpecInputs(indicator, len(sourceDOHLCVData), indicator,
				func() float64 {
					return GetFloatDataMax(indicator.Data)
				},
				func() float64 {
					return GetFloatDataMin(indicator.Data)
				})
		})

		It("should have requested to be attached to the stream", func() {
			Expect(stream.lastCallToAddTickSubscriptionArg).To(Equal(indicator))
		})

		Context("and the indicator has not yet received any ticks", func() {
			ShouldBeAnInitialisedIndicator(&inputs)

			ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
		})

		Context("and the indicator has recieved all of its ticks", func() {
			BeforeEach(func() {
				for i := 0; i < len(sourceDOHLCVData); i++ {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedAllOfItsTicks(&inputs)

			ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)
		})
	})

	Context("given the indicator is created via the constructor for use with a price stream with fixed source length", func() {
		BeforeEach(func() {
			stream = newFakeDOHLCVStreamSubscriber()
			indicator, _ = indicators.NewTrimaForStreamWithSrcLen(uint(len(sourceDOHLCVData)), stream, 5, gotrade.UseClosePrice)
			inputs = NewIndicatorWithFloatBoundsSharedSpecInputs(indicator, len(sourceDOHLCVData), indicator,
				func() float64 {
					return GetFloatDataMax(indicator.Data)
				},
				func() float64 {
					return GetFloatDataMin(indicator.Data)
				})
		})

		It("should have pre-allocated storge for the output data", func() {
			Expect(cap(indicator.Data)).To(Equal(len(sourceDOHLCVData) - indicator.GetLookbackPeriod()))
		})

		It("should have requested to be attached to the stream", func() {
			Expect(stream.lastCallToAddTickSubscriptionArg).To(Equal(indicator))
		})

		Context("and the indicator has not yet received any ticks", func() {
			ShouldBeAnInitialisedIndicator(&inputs)

			ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
		})

		Context("and the indicator has recieved all of its ticks", func() {
			BeforeEach(func() {
				for i := 0; i < len(sourceDOHLCVData); i++ {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedAllOfItsTicks(&inputs)

			ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)

			It("no new storage capcity should have been allocated", func() {
				Expect(len(indicator.Data)).To(Equal(cap(indicator.Data)))
			})
		})
	})

	Context("given the indicator is created via the constructor for use with a price stream with fixed source length with defaulted parmeters", func() {
		BeforeEach(func() {
			stream = newFakeDOHLCVStreamSubscriber()
			indicator, _ = indicators.NewDefaultTrimaForStreamWithSrcLen(uint(len(sourceDOHLCVData)), stream)
			inputs = NewIndicatorWithFloatBoundsSharedSpecInputs(indicator, len(sourceDOHLCVData), indicator,
				func() float64 {
					return GetFloatDataMax(indicator.Data)
				},
				func() float64 {
					return GetFloatDataMin(indicator.Data)
				})
		})

		It("should have pre-allocated storge for the output data", func() {
			Expect(cap(indicator.Data)).To(Equal(len(sourceDOHLCVData) - indicator.GetLookbackPeriod()))
		})

		It("should have requested to be attached to the stream", func() {
			Expect(stream.lastCallToAddTickSubscriptionArg).To(Equal(indicator))
		})

		Context("and the indicator has not yet received any ticks", func() {
			ShouldBeAnInitialisedIndicator(&inputs)

			ShouldNotHaveAnyFloatBoundsSetYet(&inputs)
		})

		Context("and the indicator has recieved all of its ticks", func() {
			BeforeEach(func() {
				for i := 0; i < len(sourceDOHLCVData); i++ {
					indicator.ReceiveDOHLCVTick(sourceDOHLCVData[i], i+1)
				}
			})

			ShouldBeAnIndicatorThatHasReceivedAllOfItsTicks(&inputs)

			ShouldHaveFloatBoundsSetToMinMaxOfResults(&inputs)

			It("no new storage capcity should have been allocated", func() {
				Expect(len(indicator.Data)).To(Equal(cap(indicator.Data)))
			})
		})
	})
})
<|endoftext|>


<commit_before>package aws

import (
	"bytes"
	"fmt"
	"log"
	"sort"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/awsutil"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsSecurityGroupRule() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSecurityGroupRuleCreate,
		Read:   resourceAwsSecurityGroupRuleRead,
		Delete: resourceAwsSecurityGroupRuleDelete,

		SchemaVersion: 1,
		MigrateState:  resourceAwsSecurityGroupRuleMigrateState,

		Schema: map[string]*schema.Schema{
			"type": &schema.Schema{
				Type:        schema.TypeString,
				Required:    true,
				ForceNew:    true,
				Description: "Type of rule, ingress (inbound) or egress (outbound).",
			},

			"from_port": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: true,
			},

			"to_port": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: true,
			},

			"protocol": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"cidr_blocks": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},

			"security_group_id": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"source_security_group_id": &schema.Schema{
				Type:          schema.TypeString,
				Optional:      true,
				ForceNew:      true,
				Computed:      true,
				ConflictsWith: []string{"cidr_blocks"},
			},

			"self": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
				ForceNew: true,
			},
		},
	}
}

func resourceAwsSecurityGroupRuleCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn
	sg_id := d.Get("security_group_id").(string)
	sg, err := findResourceSecurityGroup(conn, sg_id)

	if err != nil {
		return fmt.Errorf("sorry")
	}

	perm := expandIPPerm(d, sg)

	ruleType := d.Get("type").(string)

	var autherr error
	switch ruleType {
	case "ingress":
		log.Printf("[DEBUG] Authorizing security group %s %s rule: %s",
			sg_id, "Ingress", awsutil.StringValue(perm))

		req := &ec2.AuthorizeSecurityGroupIngressInput{
			GroupID:       sg.GroupID,
			IPPermissions: []*ec2.IPPermission{perm},
		}

		if sg.VPCID == nil || *sg.VPCID == "" {
			req.GroupID = nil
			req.GroupName = sg.GroupName
		}

		_, autherr = conn.AuthorizeSecurityGroupIngress(req)

	case "egress":
		log.Printf("[DEBUG] Authorizing security group %s %s rule: %#v",
			sg_id, "Egress", perm)

		req := &ec2.AuthorizeSecurityGroupEgressInput{
			GroupID:       sg.GroupID,
			IPPermissions: []*ec2.IPPermission{perm},
		}

		_, autherr = conn.AuthorizeSecurityGroupEgress(req)

	default:
		return fmt.Errorf("Security Group Rule must be type 'ingress' or type 'egress'")
	}

	if autherr != nil {
		if awsErr, ok := autherr.(awserr.Error); ok {
			if awsErr.Code() == "InvalidPermission.Duplicate" {
				return fmt.Errorf(`[WARN] A duplicate Security Group rule was found. This may be
a side effect of a now-fixed Terraform issue causing two security groups with
identical attributes but different source_security_group_ids to overwrite each
other in the state. See https://github.com/hashicorp/teraform/pull/2376 for more
information and instructions for recovery. Error message: %s`, awsErr.Message())
			}
		}

		return fmt.Errorf(
			"Error authorizing security group rule type %s: %s",
			ruleType, autherr)
	}

	d.SetId(ipPermissionIDHash(ruleType, perm))

	return resourceAwsSecurityGroupRuleRead(d, meta)
}

func resourceAwsSecurityGroupRuleRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn
	sg_id := d.Get("security_group_id").(string)
	sg, err := findResourceSecurityGroup(conn, sg_id)
	if err != nil {
		d.SetId("")
	}

	var rule *ec2.IPPermission
	ruleType := d.Get("type").(string)
	var rl []*ec2.IPPermission
	switch ruleType {
	case "ingress":
		rl = sg.IPPermissions
	default:
		rl = sg.IPPermissionsEgress
	}

	for _, r := range rl {
		if d.Id() == ipPermissionIDHash(ruleType, r) {
			rule = r
		}
	}

	if rule == nil {
		log.Printf("[DEBUG] Unable to find matching %s Security Group Rule for Group %s",
			ruleType, sg_id)
		d.SetId("")
		return nil
	}

	d.Set("from_port", rule.FromPort)
	d.Set("to_port", rule.ToPort)
	d.Set("protocol", rule.IPProtocol)
	d.Set("type", ruleType)

	var cb []string
	for _, c := range rule.IPRanges {
		cb = append(cb, *c.CIDRIP)
	}

	d.Set("cidr_blocks", cb)

	if len(rule.UserIDGroupPairs) > 0 {
		s := rule.UserIDGroupPairs[0]
		d.Set("source_security_group_id", *s.GroupID)
	}

	return nil
}

func resourceAwsSecurityGroupRuleDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn
	sg_id := d.Get("security_group_id").(string)
	sg, err := findResourceSecurityGroup(conn, sg_id)

	if err != nil {
		return fmt.Errorf("sorry")
	}

	perm := expandIPPerm(d, sg)
	ruleType := d.Get("type").(string)
	switch ruleType {
	case "ingress":
		log.Printf("[DEBUG] Revoking rule (%s) from security group %s:\n%s",
			"ingress", sg_id, awsutil.StringValue(perm))
		req := &ec2.RevokeSecurityGroupIngressInput{
			GroupID:       sg.GroupID,
			IPPermissions: []*ec2.IPPermission{perm},
		}

		_, err = conn.RevokeSecurityGroupIngress(req)

		if err != nil {
			return fmt.Errorf(
				"Error revoking security group %s rules: %s",
				sg_id, err)
		}
	case "egress":

		log.Printf("[DEBUG] Revoking security group %#v %s rule: %#v",
			sg_id, "egress", perm)
		req := &ec2.RevokeSecurityGroupEgressInput{
			GroupID:       sg.GroupID,
			IPPermissions: []*ec2.IPPermission{perm},
		}

		_, err = conn.RevokeSecurityGroupEgress(req)

		if err != nil {
			return fmt.Errorf(
				"Error revoking security group %s rules: %s",
				sg_id, err)
		}
	}

	d.SetId("")

	return nil
}

func findResourceSecurityGroup(conn *ec2.EC2, id string) (*ec2.SecurityGroup, error) {
	req := &ec2.DescribeSecurityGroupsInput{
		GroupIDs: []*string{aws.String(id)},
	}
	resp, err := conn.DescribeSecurityGroups(req)
	if err != nil {
		if ec2err, ok := err.(awserr.Error); ok {
			if ec2err.Code() == "InvalidSecurityGroupID.NotFound" ||
				ec2err.Code() == "InvalidGroup.NotFound" {
				resp = nil
				err = nil
			}
		}

		if err != nil {
			log.Printf("Error on findResourceSecurityGroup: %s", err)
			return nil, err
		}
	}

	return resp.SecurityGroups[0], nil
}

// ByGroupPair implements sort.Interface for []*ec2.UserIDGroupPairs based on
// GroupID or GroupName field (only one should be set).
type ByGroupPair []*ec2.UserIDGroupPair

func (b ByGroupPair) Len() int      { return len(b) }
func (b ByGroupPair) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func (b ByGroupPair) Less(i, j int) bool {
	if b[i].GroupID != nil && b[j].GroupID != nil {
		return *b[i].GroupID < *b[j].GroupID
	}
	if b[i].GroupName != nil && b[j].GroupName != nil {
		return *b[i].GroupName < *b[j].GroupName
	}

	panic("mismatched security group rules, may be a terraform bug")
}

func ipPermissionIDHash(ruleType string, ip *ec2.IPPermission) string {
	var buf bytes.Buffer
	if ip.FromPort != nil && *ip.FromPort > 0 {
		buf.WriteString(fmt.Sprintf("%d-", *ip.FromPort))
	}
	if ip.ToPort != nil && *ip.ToPort > 0 {
		buf.WriteString(fmt.Sprintf("%d-", *ip.ToPort))
	}
	buf.WriteString(fmt.Sprintf("%s-", *ip.IPProtocol))
	buf.WriteString(fmt.Sprintf("%s-", ruleType))

	// We need to make sure to sort the strings below so that we always
	// generate the same hash code no matter what is in the set.
	if len(ip.IPRanges) > 0 {
		s := make([]string, len(ip.IPRanges))
		for i, r := range ip.IPRanges {
			s[i] = *r.CIDRIP
		}
		sort.Strings(s)

		for _, v := range s {
			buf.WriteString(fmt.Sprintf("%s-", v))
		}
	}

	if len(ip.UserIDGroupPairs) > 0 {
		sort.Sort(ByGroupPair(ip.UserIDGroupPairs))
		for _, pair := range ip.UserIDGroupPairs {
			if pair.GroupID != nil {
				buf.WriteString(fmt.Sprintf("%s-", *pair.GroupID))
			} else {
				buf.WriteString("-")
			}
			if pair.GroupName != nil {
				buf.WriteString(fmt.Sprintf("%s-", *pair.GroupName))
			} else {
				buf.WriteString("-")
			}
		}
	}

	return fmt.Sprintf("sg-%d", hashcode.String(buf.String()))
}

func expandIPPerm(d *schema.ResourceData, sg *ec2.SecurityGroup) *ec2.IPPermission {
	var perm ec2.IPPermission

	perm.FromPort = aws.Long(int64(d.Get("from_port").(int)))
	perm.ToPort = aws.Long(int64(d.Get("to_port").(int)))
	perm.IPProtocol = aws.String(d.Get("protocol").(string))

	// build a group map that behaves like a set
	groups := make(map[string]bool)
	if raw, ok := d.GetOk("source_security_group_id"); ok {
		groups[raw.(string)] = true
	}

	if v, ok := d.GetOk("self"); ok && v.(bool) {
		if sg.VPCID != nil && *sg.VPCID != "" {
			groups[*sg.GroupID] = true
		} else {
			groups[*sg.GroupName] = true
		}
	}

	if len(groups) > 0 {
		perm.UserIDGroupPairs = make([]*ec2.UserIDGroupPair, len(groups))
		// build string list of group name/ids
		var gl []string
		for k, _ := range groups {
			gl = append(gl, k)
		}

		for i, name := range gl {
			ownerId, id := "", name
			if items := strings.Split(id, "/"); len(items) > 1 {
				ownerId, id = items[0], items[1]
			}

			perm.UserIDGroupPairs[i] = &ec2.UserIDGroupPair{
				GroupID: aws.String(id),
				UserID:  aws.String(ownerId),
			}

			if sg.VPCID == nil || *sg.VPCID == "" {
				perm.UserIDGroupPairs[i].GroupID = nil
				perm.UserIDGroupPairs[i].GroupName = aws.String(id)
				perm.UserIDGroupPairs[i].UserID = nil
			}
		}
	}

	if raw, ok := d.GetOk("cidr_blocks"); ok {
		list := raw.([]interface{})
		perm.IPRanges = make([]*ec2.IPRange, len(list))
		for i, v := range list {
			perm.IPRanges[i] = &ec2.IPRange{CIDRIP: aws.String(v.(string))}
		}
	}

	return &perm
}
<commit_msg>update link to actually work<commit_after>package aws

import (
	"bytes"
	"fmt"
	"log"
	"sort"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/awsutil"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsSecurityGroupRule() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSecurityGroupRuleCreate,
		Read:   resourceAwsSecurityGroupRuleRead,
		Delete: resourceAwsSecurityGroupRuleDelete,

		SchemaVersion: 1,
		MigrateState:  resourceAwsSecurityGroupRuleMigrateState,

		Schema: map[string]*schema.Schema{
			"type": &schema.Schema{
				Type:        schema.TypeString,
				Required:    true,
				ForceNew:    true,
				Description: "Type of rule, ingress (inbound) or egress (outbound).",
			},

			"from_port": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: true,
			},

			"to_port": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: true,
			},

			"protocol": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"cidr_blocks": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},

			"security_group_id": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"source_security_group_id": &schema.Schema{
				Type:          schema.TypeString,
				Optional:      true,
				ForceNew:      true,
				Computed:      true,
				ConflictsWith: []string{"cidr_blocks"},
			},

			"self": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
				ForceNew: true,
			},
		},
	}
}

func resourceAwsSecurityGroupRuleCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn
	sg_id := d.Get("security_group_id").(string)
	sg, err := findResourceSecurityGroup(conn, sg_id)

	if err != nil {
		return fmt.Errorf("sorry")
	}

	perm := expandIPPerm(d, sg)

	ruleType := d.Get("type").(string)

	var autherr error
	switch ruleType {
	case "ingress":
		log.Printf("[DEBUG] Authorizing security group %s %s rule: %s",
			sg_id, "Ingress", awsutil.StringValue(perm))

		req := &ec2.AuthorizeSecurityGroupIngressInput{
			GroupID:       sg.GroupID,
			IPPermissions: []*ec2.IPPermission{perm},
		}

		if sg.VPCID == nil || *sg.VPCID == "" {
			req.GroupID = nil
			req.GroupName = sg.GroupName
		}

		_, autherr = conn.AuthorizeSecurityGroupIngress(req)

	case "egress":
		log.Printf("[DEBUG] Authorizing security group %s %s rule: %#v",
			sg_id, "Egress", perm)

		req := &ec2.AuthorizeSecurityGroupEgressInput{
			GroupID:       sg.GroupID,
			IPPermissions: []*ec2.IPPermission{perm},
		}

		_, autherr = conn.AuthorizeSecurityGroupEgress(req)

	default:
		return fmt.Errorf("Security Group Rule must be type 'ingress' or type 'egress'")
	}

	if autherr != nil {
		if awsErr, ok := autherr.(awserr.Error); ok {
			if awsErr.Code() == "InvalidPermission.Duplicate" {
				return fmt.Errorf(`[WARN] A duplicate Security Group rule was found. This may be
a side effect of a now-fixed Terraform issue causing two security groups with
identical attributes but different source_security_group_ids to overwrite each
other in the state. See https://github.com/hashicorp/terraform/pull/2376 for more
information and instructions for recovery. Error message: %s`, awsErr.Message())
			}
		}

		return fmt.Errorf(
			"Error authorizing security group rule type %s: %s",
			ruleType, autherr)
	}

	d.SetId(ipPermissionIDHash(ruleType, perm))

	return resourceAwsSecurityGroupRuleRead(d, meta)
}

func resourceAwsSecurityGroupRuleRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn
	sg_id := d.Get("security_group_id").(string)
	sg, err := findResourceSecurityGroup(conn, sg_id)
	if err != nil {
		d.SetId("")
	}

	var rule *ec2.IPPermission
	ruleType := d.Get("type").(string)
	var rl []*ec2.IPPermission
	switch ruleType {
	case "ingress":
		rl = sg.IPPermissions
	default:
		rl = sg.IPPermissionsEgress
	}

	for _, r := range rl {
		if d.Id() == ipPermissionIDHash(ruleType, r) {
			rule = r
		}
	}

	if rule == nil {
		log.Printf("[DEBUG] Unable to find matching %s Security Group Rule for Group %s",
			ruleType, sg_id)
		d.SetId("")
		return nil
	}

	d.Set("from_port", rule.FromPort)
	d.Set("to_port", rule.ToPort)
	d.Set("protocol", rule.IPProtocol)
	d.Set("type", ruleType)

	var cb []string
	for _, c := range rule.IPRanges {
		cb = append(cb, *c.CIDRIP)
	}

	d.Set("cidr_blocks", cb)

	if len(rule.UserIDGroupPairs) > 0 {
		s := rule.UserIDGroupPairs[0]
		d.Set("source_security_group_id", *s.GroupID)
	}

	return nil
}

func resourceAwsSecurityGroupRuleDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn
	sg_id := d.Get("security_group_id").(string)
	sg, err := findResourceSecurityGroup(conn, sg_id)

	if err != nil {
		return fmt.Errorf("sorry")
	}

	perm := expandIPPerm(d, sg)
	ruleType := d.Get("type").(string)
	switch ruleType {
	case "ingress":
		log.Printf("[DEBUG] Revoking rule (%s) from security group %s:\n%s",
			"ingress", sg_id, awsutil.StringValue(perm))
		req := &ec2.RevokeSecurityGroupIngressInput{
			GroupID:       sg.GroupID,
			IPPermissions: []*ec2.IPPermission{perm},
		}

		_, err = conn.RevokeSecurityGroupIngress(req)

		if err != nil {
			return fmt.Errorf(
				"Error revoking security group %s rules: %s",
				sg_id, err)
		}
	case "egress":

		log.Printf("[DEBUG] Revoking security group %#v %s rule: %#v",
			sg_id, "egress", perm)
		req := &ec2.RevokeSecurityGroupEgressInput{
			GroupID:       sg.GroupID,
			IPPermissions: []*ec2.IPPermission{perm},
		}

		_, err = conn.RevokeSecurityGroupEgress(req)

		if err != nil {
			return fmt.Errorf(
				"Error revoking security group %s rules: %s",
				sg_id, err)
		}
	}

	d.SetId("")

	return nil
}

func findResourceSecurityGroup(conn *ec2.EC2, id string) (*ec2.SecurityGroup, error) {
	req := &ec2.DescribeSecurityGroupsInput{
		GroupIDs: []*string{aws.String(id)},
	}
	resp, err := conn.DescribeSecurityGroups(req)
	if err != nil {
		if ec2err, ok := err.(awserr.Error); ok {
			if ec2err.Code() == "InvalidSecurityGroupID.NotFound" ||
				ec2err.Code() == "InvalidGroup.NotFound" {
				resp = nil
				err = nil
			}
		}

		if err != nil {
			log.Printf("Error on findResourceSecurityGroup: %s", err)
			return nil, err
		}
	}

	return resp.SecurityGroups[0], nil
}

// ByGroupPair implements sort.Interface for []*ec2.UserIDGroupPairs based on
// GroupID or GroupName field (only one should be set).
type ByGroupPair []*ec2.UserIDGroupPair

func (b ByGroupPair) Len() int      { return len(b) }
func (b ByGroupPair) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func (b ByGroupPair) Less(i, j int) bool {
	if b[i].GroupID != nil && b[j].GroupID != nil {
		return *b[i].GroupID < *b[j].GroupID
	}
	if b[i].GroupName != nil && b[j].GroupName != nil {
		return *b[i].GroupName < *b[j].GroupName
	}

	panic("mismatched security group rules, may be a terraform bug")
}

func ipPermissionIDHash(ruleType string, ip *ec2.IPPermission) string {
	var buf bytes.Buffer
	if ip.FromPort != nil && *ip.FromPort > 0 {
		buf.WriteString(fmt.Sprintf("%d-", *ip.FromPort))
	}
	if ip.ToPort != nil && *ip.ToPort > 0 {
		buf.WriteString(fmt.Sprintf("%d-", *ip.ToPort))
	}
	buf.WriteString(fmt.Sprintf("%s-", *ip.IPProtocol))
	buf.WriteString(fmt.Sprintf("%s-", ruleType))

	// We need to make sure to sort the strings below so that we always
	// generate the same hash code no matter what is in the set.
	if len(ip.IPRanges) > 0 {
		s := make([]string, len(ip.IPRanges))
		for i, r := range ip.IPRanges {
			s[i] = *r.CIDRIP
		}
		sort.Strings(s)

		for _, v := range s {
			buf.WriteString(fmt.Sprintf("%s-", v))
		}
	}

	if len(ip.UserIDGroupPairs) > 0 {
		sort.Sort(ByGroupPair(ip.UserIDGroupPairs))
		for _, pair := range ip.UserIDGroupPairs {
			if pair.GroupID != nil {
				buf.WriteString(fmt.Sprintf("%s-", *pair.GroupID))
			} else {
				buf.WriteString("-")
			}
			if pair.GroupName != nil {
				buf.WriteString(fmt.Sprintf("%s-", *pair.GroupName))
			} else {
				buf.WriteString("-")
			}
		}
	}

	return fmt.Sprintf("sg-%d", hashcode.String(buf.String()))
}

func expandIPPerm(d *schema.ResourceData, sg *ec2.SecurityGroup) *ec2.IPPermission {
	var perm ec2.IPPermission

	perm.FromPort = aws.Long(int64(d.Get("from_port").(int)))
	perm.ToPort = aws.Long(int64(d.Get("to_port").(int)))
	perm.IPProtocol = aws.String(d.Get("protocol").(string))

	// build a group map that behaves like a set
	groups := make(map[string]bool)
	if raw, ok := d.GetOk("source_security_group_id"); ok {
		groups[raw.(string)] = true
	}

	if v, ok := d.GetOk("self"); ok && v.(bool) {
		if sg.VPCID != nil && *sg.VPCID != "" {
			groups[*sg.GroupID] = true
		} else {
			groups[*sg.GroupName] = true
		}
	}

	if len(groups) > 0 {
		perm.UserIDGroupPairs = make([]*ec2.UserIDGroupPair, len(groups))
		// build string list of group name/ids
		var gl []string
		for k, _ := range groups {
			gl = append(gl, k)
		}

		for i, name := range gl {
			ownerId, id := "", name
			if items := strings.Split(id, "/"); len(items) > 1 {
				ownerId, id = items[0], items[1]
			}

			perm.UserIDGroupPairs[i] = &ec2.UserIDGroupPair{
				GroupID: aws.String(id),
				UserID:  aws.String(ownerId),
			}

			if sg.VPCID == nil || *sg.VPCID == "" {
				perm.UserIDGroupPairs[i].GroupID = nil
				perm.UserIDGroupPairs[i].GroupName = aws.String(id)
				perm.UserIDGroupPairs[i].UserID = nil
			}
		}
	}

	if raw, ok := d.GetOk("cidr_blocks"); ok {
		list := raw.([]interface{})
		perm.IPRanges = make([]*ec2.IPRange, len(list))
		for i, v := range list {
			perm.IPRanges[i] = &ec2.IPRange{CIDRIP: aws.String(v.(string))}
		}
	}

	return &perm
}
<|endoftext|>


<commit_before><commit_msg>tests: fix expected filename in TestRequestBodyMultipartFile<commit_after><|endoftext|>


<commit_before>package web

import (
	"math"

	"github.com/dustin/go-humanize"
	"github.com/montanaflynn/stats"
)

var humanThresholds = []string{
	"Thousand",
	"Million",
	"Billion",
	"Trillion",
	"Quadrillion",
	"Quintillion",
	"Sextillion",
	"Septillion",
	"Octillion",
	"Nonillion",
	"Decillion",
}

func HumanLargeNumber(n float64) string {
	if math.Abs(n) < 1000 {
		return humanize.Commaf(n)
	}

	exp := int((math.Log(math.Abs(n)) / math.Log(1000)))
	suffix := humanThresholds[int(math.Min(float64(exp-1), 10))]
	val, _ := stats.Round(n/math.Pow(1000, float64(exp)), 2)
	return humanize.Commaf(val) + " " + suffix
}

func humanizeCommaf(f float64) string {
	if f < 10 && f > 0 {
		return humanize.Commaf(f)
	}
	val, _ := stats.Round(f, 0)
	return humanize.Commaf(val)
}
<commit_msg>Improve logic for displaying floats to show decimals<commit_after>package web

import (
	"math"

	"github.com/dustin/go-humanize"
	"github.com/montanaflynn/stats"
)

var humanThresholds = []string{
	"Thousand",
	"Million",
	"Billion",
	"Trillion",
	"Quadrillion",
	"Quintillion",
	"Sextillion",
	"Septillion",
	"Octillion",
	"Nonillion",
	"Decillion",
}

func HumanLargeNumber(n float64) string {
	if math.Abs(n) < 1000 {
		return humanize.Commaf(n)
	}

	exp := int((math.Log(math.Abs(n)) / math.Log(1000)))
	suffix := humanThresholds[int(math.Min(float64(exp-1), 10))]
	val, _ := stats.Round(n/math.Pow(1000, float64(exp)), 2)
	return humanize.Commaf(val) + " " + suffix
}

func humanizeCommaf(f float64) string {
	if float64(int64(f)) != f {
		return humanize.Commaf(f)
	}
	val, _ := stats.Round(f, 0)
	return humanize.Commaf(val)
}
<|endoftext|>