<commit_before>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path"
)

type Config struct {
	DestPath string
	Includes []string
	Excludes []string
}

var (
	destPath = flag.String("dest", defaultDestPath(), "destination directory for all backed-up files")

	noConfigErr = errors.New("config file not present")
)

func flagsAndConfig() (*Config, error) {

	flag.Parse()

	cfg := &Config{}

	// 1) read configuration file, if present
	err := cfg.load(configPath())
	if err != nil {
		log.Println(err)
	}

	// 2) if destination path specified on command line,
	//    it overrides entry in configuration file
	if destPath != nil && *destPath != "" {
		cfg.DestPath = path.Clean(*destPath)

	}

	// 3) write back configuration
	//    (NOTE: overriden DestPath is saved)
	err = cfg.save(configPath())

	return cfg, err
}

func (cfg *Config) load(configPath string) error {

	// check existence of config file
	if _, err := os.Stat(configPath); err != nil {
		return noConfigErr
	}

	file, err := ioutil.ReadFile(configPath)
	if err != nil {
		log.Printf("File error: %v\n", err)
		os.Exit(1)
	}
	log.Printf("%s\n", string(file))

	err = json.Unmarshal(file, cfg)
	log.Printf("cfg.load results: %v\nErr: %v\n", cfg, err)

	return err
}

func (cfg *Config) save(configPath string) error {

	data, err := json.Marshal(cfg)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	err = ioutil.WriteFile(configPath, data, os.ModePerm)
	if err != nil {
		fmt.Printf("File error: %v\n", err)
		os.Exit(1)
	}

	return err
}

func configPath() string {

	// get user home dir
	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("home=%q", usr.HomeDir)

	return path.Join(usr.HomeDir, "/.savvy.config")
}

func defaultDestPath() string {

	// get user home dir
	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("home=%q", usr.HomeDir)

	return path.Join(usr.HomeDir, "BACKUP/")
}
<commit_msg>Incorrect name of config field<commit_after>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path"
)

type Config struct {
	DestPath string
	Excludes []string
	Recurses []string
}

var (
	destPath = flag.String("dest", defaultDestPath(), "destination directory for all backed-up files")

	noConfigErr = errors.New("config file not present")
)

func flagsAndConfig() (*Config, error) {

	flag.Parse()

	cfg := &Config{}

	// 1) read configuration file, if present
	err := cfg.load(configPath())
	if err != nil {
		log.Println(err)
	}

	// 2) if destination path specified on command line,
	//    it overrides entry in configuration file
	if destPath != nil && *destPath != "" {
		cfg.DestPath = path.Clean(*destPath)

	}

	// 3) write back configuration
	//    (NOTE: overriden DestPath is saved)
	err = cfg.save(configPath())

	return cfg, err
}

func (cfg *Config) load(configPath string) error {

	// check existence of config file
	if _, err := os.Stat(configPath); err != nil {
		return noConfigErr
	}

	file, err := ioutil.ReadFile(configPath)
	if err != nil {
		log.Printf("File error: %v\n", err)
		os.Exit(1)
	}
	log.Printf("%s\n", string(file))

	err = json.Unmarshal(file, cfg)
	log.Printf("cfg.load results: %v\nErr: %v\n", cfg, err)

	return err
}

func (cfg *Config) save(configPath string) error {

	data, err := json.Marshal(cfg)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	err = ioutil.WriteFile(configPath, data, os.ModePerm)
	if err != nil {
		fmt.Printf("File error: %v\n", err)
		os.Exit(1)
	}

	return err
}

func configPath() string {

	// get user home dir
	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("home=%q", usr.HomeDir)

	return path.Join(usr.HomeDir, "/.savvy.config")
}

func defaultDestPath() string {

	// get user home dir
	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("home=%q", usr.HomeDir)

	return path.Join(usr.HomeDir, "BACKUP/")
}
<|endoftext|>


<commit_before>package pool

import (
	"errors"
	"fmt"
	"math"
	"sync"
)

const (
	// DefaultMaxTotal is the default value of ObjectPoolConfig.MaxTotal
	DefaultMaxTotal = 8
	// DefaultMaxIdle is the default value of ObjectPoolConfig.MaxIdle
	DefaultMaxIdle = 8
	// DefaultMinIdle is the default value of ObjectPoolConfig.MinIdle
	DefaultMinIdle = 0
	// DefaultLifo is the default value of ObjectPoolConfig.Lifo
	DefaultLifo = true

	// TODO
	// DEFAULT_FAIRNESS = false

	// DefaultMaxWaitMillis is the default value of ObjectPoolConfig.MaxWaitMillis
	DefaultMaxWaitMillis = int64(-1)
	// DefaultMinEvictableIdleTimeMillis is the default value of ObjectPoolConfig.MinEvictableIdleTimeMillis
	DefaultMinEvictableIdleTimeMillis = int64(1000 * 60 * 30)
	// DefaultSoftMinEvictableIdleTimeMillis is the default value of ObjectPoolConfig.SoftMinEvictableIdleTimeMillis
	DefaultSoftMinEvictableIdleTimeMillis = int64(math.MaxInt64)
	// DefaultNumTestsPerEvictionRun is the default value of ObjectPoolConfig.NumTestsPerEvictionRun
	DefaultNumTestsPerEvictionRun = 3
	// DefaultTestOnCreate is the default value of ObjectPoolConfig.TestOnCreate
	DefaultTestOnCreate = false
	// DefaultTestOnBorrow is the default value of ObjectPoolConfig.TestOnBorrow
	DefaultTestOnBorrow = false
	// DefaultTestOnReturn is the default value of ObjectPoolConfig.TestOnReturn
	DefaultTestOnReturn = false
	// DefaultTestWhileIdle is the default value of ObjectPoolConfig.TestWhileIdle
	DefaultTestWhileIdle = false
	// DefaultTimeBetweenEvictionRunsMillis is the default value of ObjectPoolConfig.TimeBetweenEvictionRunsMillis
	DefaultTimeBetweenEvictionRunsMillis = int64(-1)
	// DefaultBlockWhenExhausted is the default value of ObjectPoolConfig.BlockWhenExhausted
	DefaultBlockWhenExhausted = true
	// DefaultEvictionPolicyName is the default value of ObjectPoolConfig.EvictionPolicyName
	DefaultEvictionPolicyName = "github.com/jolestar/go-commons-pool/DefaultEvictionPolicy"
)

// ObjectPoolConfig is ObjectPool config, include cap, block, valid strategy, evict strategy etc.
type ObjectPoolConfig struct {
	/**
	 * Whether the pool has LIFO (last in, first out) behaviour with
	 * respect to idle objects - always returning the most recently used object
	 * from the pool, or as a FIFO (first in, first out) queue, where the pool
	 * always returns the oldest object in the idle object pool.
	 */
	Lifo bool

	/**
	 * The cap on the number of objects that can be allocated by the pool
	 * (checked out to clients, or idle awaiting checkout) at a given time. Use
	 * a negative value for no limit.
	 */
	MaxTotal int

	/**
	 * The cap on the number of "idle" instances in the pool. Use a
	 * negative value to indicate an unlimited number of idle instances.
	 * If MaxIdle
	 * is set too low on heavily loaded systems it is possible you will see
	 * objects being destroyed and almost immediately new objects being created.
	 * This is a result of the active goroutines momentarily returning objects
	 * faster than they are requesting them them, causing the number of idle
	 * objects to rise above maxIdle. The best value for maxIdle for heavily
	 * loaded system will vary but the default is a good starting point.
	 */
	MaxIdle int

	/**
	 * The minimum number of idle objects to maintain in
	 * the pool. This setting only has an effect if it is positive and
	 * TimeBetweenEvictionRunsMillis is greater than zero. If this
	 * is the case, an attempt is made to ensure that the pool has the required
	 * minimum number of instances during idle object eviction runs.
	 *
	 * If the configured value of MinIdle is greater than the configured value
	 * for MaxIdle then the value of MaxIdle will be used instead.
	 *
	 */
	MinIdle int

	/**
	* Whether objects created for the pool will be validated before
	* being returned from the ObjectPool.BorrowObject() method. Validation is
	* performed by the ValidateObject() method of the factory
	* associated with the pool. If the object fails to validate, then
	* ObjectPool.BorrowObject() will fail.
	 */
	TestOnCreate bool

	/**
	 * Whether objects borrowed from the pool will be validated before
	 * being returned from the ObjectPool.BorrowObject() method. Validation is
	 * performed by the ValidateObject() method of the factory
	 * associated with the pool. If the object fails to validate, it will be
	 * removed from the pool and destroyed, and a new attempt will be made to
	 * borrow an object from the pool.
	 */
	TestOnBorrow bool

	/**
	 * Whether objects borrowed from the pool will be validated when
	 * they are returned to the pool via the ObjectPool.ReturnObject() method.
	 * Validation is performed by the ValidateObject() method of
	 * the factory associated with the pool. Returning objects that fail validation
	 * are destroyed rather then being returned the pool.
	 */
	TestOnReturn bool

	/**
	* Whether objects sitting idle in the pool will be validated by the
	* idle object evictor (if any - see
	*  TimeBetweenEvictionRunsMillis ). Validation is performed
	* by the ValidateObject() method of the factory associated
	* with the pool. If the object fails to validate, it will be removed from
	* the pool and destroyed.  Note that setting this property has no effect
	* unless the idle object evictor is enabled by setting
	* TimeBetweenEvictionRunsMillis  to a positive value.
	 */
	TestWhileIdle bool

	/**
	* Whether to block when the ObjectPool.BorrowObject() method is
	* invoked when the pool is exhausted (the maximum number of "active"
	* objects has been reached).
	 */
	BlockWhenExhausted bool

	//TODO support fairness config
	//Fairness                       bool

	/**
	 * The maximum amount of time (in milliseconds) the
	 * ObjectPool.BorrowObject() method should block before return
	 * a error when the pool is exhausted and
	 *  BlockWhenExhausted is true. When less than 0, the
	 * ObjectPool.BorrowObject() method may block indefinitely.
	 *
	 */
	MaxWaitMillis int64

	/**
	 * The minimum amount of time an object may sit idle in the pool
	 * before it is eligible for eviction by the idle object evictor (if any -
	 * see TimeBetweenEvictionRunsMillis . When non-positive,
	 * no objects will be evicted from the pool due to idle time alone.
	 */
	MinEvictableIdleTimeMillis int64

	/**
	 * The minimum amount of time an object may sit idle in the pool
	 * before it is eligible for eviction by the idle object evictor (if any -
	 * see TimeBetweenEvictionRunsMillis ),
	 * with the extra condition that at least MinIdle object
	 * instances remain in the pool. This setting is overridden by
	 *  MinEvictableIdleTimeMillis (that is, if
	 *  MinEvictableIdleTimeMillis is positive, then
	 *  SoftMinEvictableIdleTimeMillis is ignored).
	 */
	SoftMinEvictableIdleTimeMillis int64

	/**
	 * The maximum number of objects to examine during each run (if any)
	 * of the idle object evictor goroutine. When positive, the number of tests
	 * performed for a run will be the minimum of the configured value and the
	 * number of idle instances in the pool. When negative, the number of tests
	 * performed will be math.Ceil(ObjectPool.GetNumIdle()/math.
	 * Abs(PoolConfig.NumTestsPerEvictionRun)) which means that when the
	 * value is -n roughly one nth of the idle objects will be
	 * tested per run.
	 */
	NumTestsPerEvictionRun int

	/**
	 * The name of the EvictionPolicy implementation that is
	 * used by this pool. Please register policy by RegistryEvictionPolicy(name, policy)
	 */
	EvictionPolicyName string

	/**
	* The number of milliseconds to sleep between runs of the idle
	* object evictor goroutine. When non-positive, no idle object evictor goroutine
	* will be run.
	* if this value changed after ObjectPool created, should call ObjectPool.StartEvictor to take effect.
	 */
	TimeBetweenEvictionRunsMillis int64
}

// NewDefaultPoolConfig return a ObjectPoolConfig instance init with default value.
func NewDefaultPoolConfig() *ObjectPoolConfig {
	return &ObjectPoolConfig{
		Lifo:                           DefaultLifo,
		MaxTotal:                       DefaultMaxTotal,
		MaxIdle:                        DefaultMaxIdle,
		MinIdle:                        DefaultMinIdle,
		MaxWaitMillis:                  DefaultMaxWaitMillis,
		MinEvictableIdleTimeMillis:     DefaultMinEvictableIdleTimeMillis,
		SoftMinEvictableIdleTimeMillis: DefaultSoftMinEvictableIdleTimeMillis,
		NumTestsPerEvictionRun:         DefaultNumTestsPerEvictionRun,
		EvictionPolicyName:             DefaultEvictionPolicyName,
		TestOnCreate:                   DefaultTestOnCreate,
		TestOnBorrow:                   DefaultTestOnBorrow,
		TestOnReturn:                   DefaultTestOnReturn,
		TestWhileIdle:                  DefaultTestWhileIdle,
		TimeBetweenEvictionRunsMillis:  DefaultTimeBetweenEvictionRunsMillis,
		BlockWhenExhausted:             DefaultBlockWhenExhausted}
}

// AbandonedConfig ObjectPool abandoned strategy config
type AbandonedConfig struct {
	RemoveAbandonedOnBorrow      bool
	RemoveAbandonedOnMaintenance bool
	// Timeout in seconds before an abandoned object can be removed.
	RemoveAbandonedTimeout int
}

// NewDefaultAbandonedConfig return a new AbandonedConfig instance init with default.
func NewDefaultAbandonedConfig() *AbandonedConfig {
	return &AbandonedConfig{RemoveAbandonedOnBorrow: false, RemoveAbandonedOnMaintenance: false, RemoveAbandonedTimeout: 300}
}

// EvictionConfig is config for ObjectPool EvictionPolicy
type EvictionConfig struct {
	IdleEvictTime     int64
	IdleSoftEvictTime int64
	MinIdle           int
}

// EvictionPolicy is a interface support custom EvictionPolicy
type EvictionPolicy interface {
	// Evict do evict by config
	Evict(config *EvictionConfig, underTest *PooledObject, idleCount int) bool
}

// DefaultEvictionPolicy is a default EvictionPolicy impl
type DefaultEvictionPolicy struct {
}

// Evict do evict by config
func (p *DefaultEvictionPolicy) Evict(config *EvictionConfig, underTest *PooledObject, idleCount int) bool {
	idleTime := underTest.GetIdleTimeMillis()

	if (config.IdleSoftEvictTime < idleTime &&
		config.MinIdle < idleCount) ||
		config.IdleEvictTime < idleTime {
		return true
	}
	return false
}

var (
	policiesMutex sync.Mutex
	policies      = make(map[string]EvictionPolicy)
)

// RegistryEvictionPolicy registry a custom EvictionPolicy with gaven name.
func RegistryEvictionPolicy(name string, policy EvictionPolicy) {
	if name == "" || policy == nil {
		panic(errors.New("invalid argument"))
	}
	fmt.Println("RegistryEvictionPolicy", name)
	policiesMutex.Lock()
	policies[name] = policy
	policiesMutex.Unlock()
}

// GetEvictionPolicy return a EvictionPolicy by gaven name
func GetEvictionPolicy(name string) EvictionPolicy {
	policiesMutex.Lock()
	defer policiesMutex.Unlock()
	return policies[name]

}

func init() {
	RegistryEvictionPolicy(DefaultEvictionPolicyName, new(DefaultEvictionPolicy))
}
<commit_msg>made config init silent<commit_after>package pool

import (
	"errors"
	"math"
	"sync"
)

const (
	// DefaultMaxTotal is the default value of ObjectPoolConfig.MaxTotal
	DefaultMaxTotal = 8
	// DefaultMaxIdle is the default value of ObjectPoolConfig.MaxIdle
	DefaultMaxIdle = 8
	// DefaultMinIdle is the default value of ObjectPoolConfig.MinIdle
	DefaultMinIdle = 0
	// DefaultLifo is the default value of ObjectPoolConfig.Lifo
	DefaultLifo = true

	// TODO
	// DEFAULT_FAIRNESS = false

	// DefaultMaxWaitMillis is the default value of ObjectPoolConfig.MaxWaitMillis
	DefaultMaxWaitMillis = int64(-1)
	// DefaultMinEvictableIdleTimeMillis is the default value of ObjectPoolConfig.MinEvictableIdleTimeMillis
	DefaultMinEvictableIdleTimeMillis = int64(1000 * 60 * 30)
	// DefaultSoftMinEvictableIdleTimeMillis is the default value of ObjectPoolConfig.SoftMinEvictableIdleTimeMillis
	DefaultSoftMinEvictableIdleTimeMillis = int64(math.MaxInt64)
	// DefaultNumTestsPerEvictionRun is the default value of ObjectPoolConfig.NumTestsPerEvictionRun
	DefaultNumTestsPerEvictionRun = 3
	// DefaultTestOnCreate is the default value of ObjectPoolConfig.TestOnCreate
	DefaultTestOnCreate = false
	// DefaultTestOnBorrow is the default value of ObjectPoolConfig.TestOnBorrow
	DefaultTestOnBorrow = false
	// DefaultTestOnReturn is the default value of ObjectPoolConfig.TestOnReturn
	DefaultTestOnReturn = false
	// DefaultTestWhileIdle is the default value of ObjectPoolConfig.TestWhileIdle
	DefaultTestWhileIdle = false
	// DefaultTimeBetweenEvictionRunsMillis is the default value of ObjectPoolConfig.TimeBetweenEvictionRunsMillis
	DefaultTimeBetweenEvictionRunsMillis = int64(-1)
	// DefaultBlockWhenExhausted is the default value of ObjectPoolConfig.BlockWhenExhausted
	DefaultBlockWhenExhausted = true
	// DefaultEvictionPolicyName is the default value of ObjectPoolConfig.EvictionPolicyName
	DefaultEvictionPolicyName = "github.com/jolestar/go-commons-pool/DefaultEvictionPolicy"
)

// ObjectPoolConfig is ObjectPool config, include cap, block, valid strategy, evict strategy etc.
type ObjectPoolConfig struct {
	/**
	 * Whether the pool has LIFO (last in, first out) behaviour with
	 * respect to idle objects - always returning the most recently used object
	 * from the pool, or as a FIFO (first in, first out) queue, where the pool
	 * always returns the oldest object in the idle object pool.
	 */
	Lifo bool

	/**
	 * The cap on the number of objects that can be allocated by the pool
	 * (checked out to clients, or idle awaiting checkout) at a given time. Use
	 * a negative value for no limit.
	 */
	MaxTotal int

	/**
	 * The cap on the number of "idle" instances in the pool. Use a
	 * negative value to indicate an unlimited number of idle instances.
	 * If MaxIdle
	 * is set too low on heavily loaded systems it is possible you will see
	 * objects being destroyed and almost immediately new objects being created.
	 * This is a result of the active goroutines momentarily returning objects
	 * faster than they are requesting them them, causing the number of idle
	 * objects to rise above maxIdle. The best value for maxIdle for heavily
	 * loaded system will vary but the default is a good starting point.
	 */
	MaxIdle int

	/**
	 * The minimum number of idle objects to maintain in
	 * the pool. This setting only has an effect if it is positive and
	 * TimeBetweenEvictionRunsMillis is greater than zero. If this
	 * is the case, an attempt is made to ensure that the pool has the required
	 * minimum number of instances during idle object eviction runs.
	 *
	 * If the configured value of MinIdle is greater than the configured value
	 * for MaxIdle then the value of MaxIdle will be used instead.
	 *
	 */
	MinIdle int

	/**
	* Whether objects created for the pool will be validated before
	* being returned from the ObjectPool.BorrowObject() method. Validation is
	* performed by the ValidateObject() method of the factory
	* associated with the pool. If the object fails to validate, then
	* ObjectPool.BorrowObject() will fail.
	 */
	TestOnCreate bool

	/**
	 * Whether objects borrowed from the pool will be validated before
	 * being returned from the ObjectPool.BorrowObject() method. Validation is
	 * performed by the ValidateObject() method of the factory
	 * associated with the pool. If the object fails to validate, it will be
	 * removed from the pool and destroyed, and a new attempt will be made to
	 * borrow an object from the pool.
	 */
	TestOnBorrow bool

	/**
	 * Whether objects borrowed from the pool will be validated when
	 * they are returned to the pool via the ObjectPool.ReturnObject() method.
	 * Validation is performed by the ValidateObject() method of
	 * the factory associated with the pool. Returning objects that fail validation
	 * are destroyed rather then being returned the pool.
	 */
	TestOnReturn bool

	/**
	* Whether objects sitting idle in the pool will be validated by the
	* idle object evictor (if any - see
	*  TimeBetweenEvictionRunsMillis ). Validation is performed
	* by the ValidateObject() method of the factory associated
	* with the pool. If the object fails to validate, it will be removed from
	* the pool and destroyed.  Note that setting this property has no effect
	* unless the idle object evictor is enabled by setting
	* TimeBetweenEvictionRunsMillis  to a positive value.
	 */
	TestWhileIdle bool

	/**
	* Whether to block when the ObjectPool.BorrowObject() method is
	* invoked when the pool is exhausted (the maximum number of "active"
	* objects has been reached).
	 */
	BlockWhenExhausted bool

	//TODO support fairness config
	//Fairness                       bool

	/**
	 * The maximum amount of time (in milliseconds) the
	 * ObjectPool.BorrowObject() method should block before return
	 * a error when the pool is exhausted and
	 *  BlockWhenExhausted is true. When less than 0, the
	 * ObjectPool.BorrowObject() method may block indefinitely.
	 *
	 */
	MaxWaitMillis int64

	/**
	 * The minimum amount of time an object may sit idle in the pool
	 * before it is eligible for eviction by the idle object evictor (if any -
	 * see TimeBetweenEvictionRunsMillis . When non-positive,
	 * no objects will be evicted from the pool due to idle time alone.
	 */
	MinEvictableIdleTimeMillis int64

	/**
	 * The minimum amount of time an object may sit idle in the pool
	 * before it is eligible for eviction by the idle object evictor (if any -
	 * see TimeBetweenEvictionRunsMillis ),
	 * with the extra condition that at least MinIdle object
	 * instances remain in the pool. This setting is overridden by
	 *  MinEvictableIdleTimeMillis (that is, if
	 *  MinEvictableIdleTimeMillis is positive, then
	 *  SoftMinEvictableIdleTimeMillis is ignored).
	 */
	SoftMinEvictableIdleTimeMillis int64

	/**
	 * The maximum number of objects to examine during each run (if any)
	 * of the idle object evictor goroutine. When positive, the number of tests
	 * performed for a run will be the minimum of the configured value and the
	 * number of idle instances in the pool. When negative, the number of tests
	 * performed will be math.Ceil(ObjectPool.GetNumIdle()/math.
	 * Abs(PoolConfig.NumTestsPerEvictionRun)) which means that when the
	 * value is -n roughly one nth of the idle objects will be
	 * tested per run.
	 */
	NumTestsPerEvictionRun int

	/**
	 * The name of the EvictionPolicy implementation that is
	 * used by this pool. Please register policy by RegistryEvictionPolicy(name, policy)
	 */
	EvictionPolicyName string

	/**
	* The number of milliseconds to sleep between runs of the idle
	* object evictor goroutine. When non-positive, no idle object evictor goroutine
	* will be run.
	* if this value changed after ObjectPool created, should call ObjectPool.StartEvictor to take effect.
	 */
	TimeBetweenEvictionRunsMillis int64
}

// NewDefaultPoolConfig return a ObjectPoolConfig instance init with default value.
func NewDefaultPoolConfig() *ObjectPoolConfig {
	return &ObjectPoolConfig{
		Lifo:                           DefaultLifo,
		MaxTotal:                       DefaultMaxTotal,
		MaxIdle:                        DefaultMaxIdle,
		MinIdle:                        DefaultMinIdle,
		MaxWaitMillis:                  DefaultMaxWaitMillis,
		MinEvictableIdleTimeMillis:     DefaultMinEvictableIdleTimeMillis,
		SoftMinEvictableIdleTimeMillis: DefaultSoftMinEvictableIdleTimeMillis,
		NumTestsPerEvictionRun:         DefaultNumTestsPerEvictionRun,
		EvictionPolicyName:             DefaultEvictionPolicyName,
		TestOnCreate:                   DefaultTestOnCreate,
		TestOnBorrow:                   DefaultTestOnBorrow,
		TestOnReturn:                   DefaultTestOnReturn,
		TestWhileIdle:                  DefaultTestWhileIdle,
		TimeBetweenEvictionRunsMillis:  DefaultTimeBetweenEvictionRunsMillis,
		BlockWhenExhausted:             DefaultBlockWhenExhausted}
}

// AbandonedConfig ObjectPool abandoned strategy config
type AbandonedConfig struct {
	RemoveAbandonedOnBorrow      bool
	RemoveAbandonedOnMaintenance bool
	// Timeout in seconds before an abandoned object can be removed.
	RemoveAbandonedTimeout int
}

// NewDefaultAbandonedConfig return a new AbandonedConfig instance init with default.
func NewDefaultAbandonedConfig() *AbandonedConfig {
	return &AbandonedConfig{RemoveAbandonedOnBorrow: false, RemoveAbandonedOnMaintenance: false, RemoveAbandonedTimeout: 300}
}

// EvictionConfig is config for ObjectPool EvictionPolicy
type EvictionConfig struct {
	IdleEvictTime     int64
	IdleSoftEvictTime int64
	MinIdle           int
}

// EvictionPolicy is a interface support custom EvictionPolicy
type EvictionPolicy interface {
	// Evict do evict by config
	Evict(config *EvictionConfig, underTest *PooledObject, idleCount int) bool
}

// DefaultEvictionPolicy is a default EvictionPolicy impl
type DefaultEvictionPolicy struct {
}

// Evict do evict by config
func (p *DefaultEvictionPolicy) Evict(config *EvictionConfig, underTest *PooledObject, idleCount int) bool {
	idleTime := underTest.GetIdleTimeMillis()

	if (config.IdleSoftEvictTime < idleTime &&
		config.MinIdle < idleCount) ||
		config.IdleEvictTime < idleTime {
		return true
	}
	return false
}

var (
	policiesMutex sync.Mutex
	policies      = make(map[string]EvictionPolicy)
)

// RegistryEvictionPolicy registry a custom EvictionPolicy with gaven name.
func RegistryEvictionPolicy(name string, policy EvictionPolicy) {
	if name == "" || policy == nil {
		panic(errors.New("invalid argument"))
	}
	policiesMutex.Lock()
	policies[name] = policy
	policiesMutex.Unlock()
}

// GetEvictionPolicy return a EvictionPolicy by gaven name
func GetEvictionPolicy(name string) EvictionPolicy {
	policiesMutex.Lock()
	defer policiesMutex.Unlock()
	return policies[name]

}

func init() {
	RegistryEvictionPolicy(DefaultEvictionPolicyName, new(DefaultEvictionPolicy))
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"log"
	"net"
	"os"

	"github.com/BurntSushi/toml"
)

type configuration struct {
	Listen              string     `toml:"listen"`
	AccessLog           string     `toml:"access_log"`
	ActivityLog         string     `toml:"activity_log"`
	AllowedConnectPorts []int      `toml:"allowed_connect_ports"`
	AllowedNetworks     []string   `toml:"allowed_networks"`
	DisallowedNetworks  []string   `toml:"disallowed_networks"`
	AuthRealm           string     `toml:"auth_realm"`
	AuthType            string     `toml:"auth_type"`
	AuthFile            string     `toml:"auth_file"`
	ForwardedForHeader  string     `toml:"forwarded_for_header"`
	BindIP              string     `toml:"bind_ip"`
	ViaHeader           string     `toml:"via_header"`
	ViaProxyName        string     `toml:"via_proxy_name"`
	AddHeaders          [][]string `toml:"add_headers"`
}

const (
	defaultListenAddress      = "127.0.0.1:3128"
	defaultAllowedNetwork     = "127.0.0.1/32"
	defaultAllowedConnectPort = 443
)

func validateNetworks(networks []string) {
	if networks != nil && len(networks) > 0 {
		for i, network := range networks {
			_, _, err := net.ParseCIDR(network)
			if err != nil {
				if ip := net.ParseIP(network); ip != nil {
					if ip4 := ip.To4(); ip4 != nil {
						networks[i] = network + "/32"
						continue
					}
					if ip16 := ip.To16(); ip16 != nil {
						networks[i] = network + "/128"
						continue
					}
				}
				log.Fatalf("couldn't parse network %s: %v", network, err)
			}
		}
	}
}

func validateIP(addr string) {
	if addr != "" {
		ip := net.ParseIP(addr)
		if ip == nil {
			log.Fatalf("incorrect IP address %s", addr)
		}
	}
}

func validateAuthType(authType string) {
	validValues := map[string]bool{
		"":       true,
		"basic":  true,
		"digest": true,
	}

	_, ok := validValues[authType]
	if !ok {
		log.Fatalf("Incorrect authentication type '%s'", authType)
	}
}

func validateForwardedForHeaderAction(action string) {
	validValues := map[string]bool{
		"on":       true,
		"off":      true,
		"delete":   true,
		"truncate": true,
	}

	_, ok := validValues[action]
	if !ok {
		log.Fatalf("Incorrect 'Forwarded-For' header action '%s'", action)
	}
}

func validateViaHeaderAction(action string) {
	validValues := map[string]bool{
		"on":     true,
		"off":    true,
		"delete": true,
	}

	_, ok := validValues[action]
	if !ok {
		log.Fatalf("Incorrect 'Via' header action '%s'", action)
	}
}

func newConfigurationFromFile(path string) *configuration {
	file, err := os.Open(path)
	if err != nil {
		log.Fatal("can't open configuration file: ", err)
	}

	conf := newConfiguration(file)

	return conf
}

func newConfiguration(data io.Reader) *configuration {
	var conf configuration
	if _, err := toml.DecodeReader(data, &conf); err != nil {
		log.Fatalf("Couldn't parse configuration file: %v", err)
	}

	if conf.Listen == "" {
		conf.Listen = defaultListenAddress
	}

	// if no auth. enabled allow only from 127.0.0.1/32 if not deliberatly specified otherwise
	if conf.AllowedNetworks == nil || len(conf.AllowedNetworks) == 0 {
		if conf.AuthFile == "" || conf.AuthType == "" {
			conf.AllowedNetworks = make([]string, 1)
			conf.AllowedNetworks[0] = defaultAllowedNetwork
		}
	}

	validateNetworks(conf.AllowedNetworks)
	validateNetworks(conf.DisallowedNetworks)
	validateIP(conf.BindIP)

	// by default allow connect only to the https protocol port
	if conf.AllowedConnectPorts == nil || len(conf.AllowedConnectPorts) == 0 {
		conf.AllowedConnectPorts = make([]int, 1)
		conf.AllowedConnectPorts[0] = defaultAllowedConnectPort
	}

	if conf.AuthType == "" && conf.AuthFile != "" {
		log.Fatal("missed mandatoty configuration parameter 'auth_type'")
	}

	// by default set X-Forwarded-For header
	if conf.ForwardedForHeader == "" {
		conf.ForwardedForHeader = "on"
	}

	if conf.ViaHeader == "" {
		conf.ViaHeader = "on"
	}

	if conf.ViaProxyName == "" && conf.ViaHeader == "on" {
		hostname, err := os.Hostname()
		if err != nil {
			log.Fatalf("os.Hostname() failed: %v\n", err)
		}
		conf.ViaProxyName = hostname
	}

	validateAuthType(conf.AuthType)
	validateForwardedForHeaderAction(conf.ForwardedForHeader)
	validateViaHeaderAction(conf.ViaHeader)

	return &conf
}
<commit_msg>fix misspelled word<commit_after>package main

import (
	"io"
	"log"
	"net"
	"os"

	"github.com/BurntSushi/toml"
)

type configuration struct {
	Listen              string     `toml:"listen"`
	AccessLog           string     `toml:"access_log"`
	ActivityLog         string     `toml:"activity_log"`
	AllowedConnectPorts []int      `toml:"allowed_connect_ports"`
	AllowedNetworks     []string   `toml:"allowed_networks"`
	DisallowedNetworks  []string   `toml:"disallowed_networks"`
	AuthRealm           string     `toml:"auth_realm"`
	AuthType            string     `toml:"auth_type"`
	AuthFile            string     `toml:"auth_file"`
	ForwardedForHeader  string     `toml:"forwarded_for_header"`
	BindIP              string     `toml:"bind_ip"`
	ViaHeader           string     `toml:"via_header"`
	ViaProxyName        string     `toml:"via_proxy_name"`
	AddHeaders          [][]string `toml:"add_headers"`
}

const (
	defaultListenAddress      = "127.0.0.1:3128"
	defaultAllowedNetwork     = "127.0.0.1/32"
	defaultAllowedConnectPort = 443
)

func validateNetworks(networks []string) {
	if networks != nil && len(networks) > 0 {
		for i, network := range networks {
			_, _, err := net.ParseCIDR(network)
			if err != nil {
				if ip := net.ParseIP(network); ip != nil {
					if ip4 := ip.To4(); ip4 != nil {
						networks[i] = network + "/32"
						continue
					}
					if ip16 := ip.To16(); ip16 != nil {
						networks[i] = network + "/128"
						continue
					}
				}
				log.Fatalf("couldn't parse network %s: %v", network, err)
			}
		}
	}
}

func validateIP(addr string) {
	if addr != "" {
		ip := net.ParseIP(addr)
		if ip == nil {
			log.Fatalf("incorrect IP address %s", addr)
		}
	}
}

func validateAuthType(authType string) {
	validValues := map[string]bool{
		"":       true,
		"basic":  true,
		"digest": true,
	}

	_, ok := validValues[authType]
	if !ok {
		log.Fatalf("Incorrect authentication type '%s'", authType)
	}
}

func validateForwardedForHeaderAction(action string) {
	validValues := map[string]bool{
		"on":       true,
		"off":      true,
		"delete":   true,
		"truncate": true,
	}

	_, ok := validValues[action]
	if !ok {
		log.Fatalf("Incorrect 'Forwarded-For' header action '%s'", action)
	}
}

func validateViaHeaderAction(action string) {
	validValues := map[string]bool{
		"on":     true,
		"off":    true,
		"delete": true,
	}

	_, ok := validValues[action]
	if !ok {
		log.Fatalf("Incorrect 'Via' header action '%s'", action)
	}
}

func newConfigurationFromFile(path string) *configuration {
	file, err := os.Open(path)
	if err != nil {
		log.Fatal("can't open configuration file: ", err)
	}

	conf := newConfiguration(file)

	return conf
}

func newConfiguration(data io.Reader) *configuration {
	var conf configuration
	if _, err := toml.DecodeReader(data, &conf); err != nil {
		log.Fatalf("Couldn't parse configuration file: %v", err)
	}

	if conf.Listen == "" {
		conf.Listen = defaultListenAddress
	}

	// if no auth. enabled allow only from 127.0.0.1/32 if not deliberately specified otherwise
	if conf.AllowedNetworks == nil || len(conf.AllowedNetworks) == 0 {
		if conf.AuthFile == "" || conf.AuthType == "" {
			conf.AllowedNetworks = make([]string, 1)
			conf.AllowedNetworks[0] = defaultAllowedNetwork
		}
	}

	validateNetworks(conf.AllowedNetworks)
	validateNetworks(conf.DisallowedNetworks)
	validateIP(conf.BindIP)

	// by default allow connect only to the https protocol port
	if conf.AllowedConnectPorts == nil || len(conf.AllowedConnectPorts) == 0 {
		conf.AllowedConnectPorts = make([]int, 1)
		conf.AllowedConnectPorts[0] = defaultAllowedConnectPort
	}

	if conf.AuthType == "" && conf.AuthFile != "" {
		log.Fatal("missed mandatoty configuration parameter 'auth_type'")
	}

	// by default set X-Forwarded-For header
	if conf.ForwardedForHeader == "" {
		conf.ForwardedForHeader = "on"
	}

	if conf.ViaHeader == "" {
		conf.ViaHeader = "on"
	}

	if conf.ViaProxyName == "" && conf.ViaHeader == "on" {
		hostname, err := os.Hostname()
		if err != nil {
			log.Fatalf("os.Hostname() failed: %v\n", err)
		}
		conf.ViaProxyName = hostname
	}

	validateAuthType(conf.AuthType)
	validateForwardedForHeaderAction(conf.ForwardedForHeader)
	validateViaHeaderAction(conf.ViaHeader)

	return &conf
}
<|endoftext|>


<commit_before>package groupcache

import (
	"time"
	"strings"
	"errors"
	"fmt"
	"strconv"

	"github.com/samuel/go-zookeeper/zk"
	log "github.com/Sirupsen/logrus" // todo: use log provider
)

const (
	GroupCacheZNode = "groupcache"
	GroupCacheNodesZNode = GroupCacheZNode + "/nodes"
)

var (
	zkFlagPermanent = int32(0)
	defaultACL = zk.WorldACL(zk.PermAll)
)

type ConfigOpts struct {
	ZkServers      string

	ParentZNode    string

	SessionTimeout time.Duration
}

type ConfigWatcher struct {
	conn        *zk.Conn
	parentZNode string
}

func NewConfigWatcher(zkServers string, parentZNode string, sessionTimeout time.Duration) (w *ConfigWatcher, err error) {
	servers := strings.Split(zkServers, ",")
	log.Infof("connecting to servers: %v", servers)
	conn, _, err := zk.Connect(servers, sessionTimeout) // Note: sessionTimeout will be overwrite by TimeOut of server connectResponse when authenticating.
	if err != nil {
		return
	}
	if strings.HasSuffix(parentZNode, "/") {
		err = errors.New("invalid znode: " + parentZNode)
		return
	}
	w = &ConfigWatcher{conn: conn, parentZNode: parentZNode}
	return
}

func joinZNode(nodes... string) string {
	return strings.Join(nodes, "/")
}

// watchNodeList watches children of GroupCacheNodesZkNode and run callback process
func (w *ConfigWatcher) watchNodeList(process func(map[string]*[]byte) error) error {
	path := joinZNode(w.parentZNode, GroupCacheNodesZNode)

	// get children and watch,
	// then run callback process
	watch := func() (<-chan zk.Event, error) {
		nodes, _, ech, err := w.conn.ChildrenW(path)
		if err != nil {
			return nil, err
		}

		log.Infoln("nodes:", nodes)
		nodeConfig := make(map[string]*[]byte)
		for _, node := range nodes {
			nodePath := joinZNode(path, node)
			log.Infoln("getting path:", nodePath)
			data, _, err := w.conn.Get(nodePath)
			if err != nil {
				log.Errorln("getting path data failed, error:", err)
				continue
			}
			nodeConfig[node] = &data
		}

		err = process(nodeConfig)
		if err != nil {
			log.Errorln("processing nodelist falied, error :", err)
			return ech, err
		}

		return ech, nil
	}

	log.Infoln("watching path:", path)
	var ech <-chan zk.Event
	var err error
	if ech, err = watch(); err != nil {
		log.Errorf("watching path: %s failed.", path)
		return err
	}

	go func() {
		for { // TODO: 处理其他events，以及conn/session close
			evt := <-ech
			log.Infof("node list changed, evt: %+v, watching path: %s", evt, path)
			if ech, err = watch(); err != nil {
				log.Errorf("watching path: %s failed.", path)
			}
		}
	}()

	return nil
}

func (w *ConfigWatcher) Close() {
	w.conn.Close()
}

func createZNodeIfNotExist(conn *zk.Conn, path string, data []byte, flags int32, acl []zk.ACL) error {
	if exists, _, err := conn.Exists(path); err != nil {
		log.Errorln("failed to check existence:", path)
		return err
	} else if !exists {
		_, err = conn.Create(path, data, flags, acl)
		if err != nil {
			log.Errorln("failed to create path:", path)
			if err != errors.New("zk: node already exists") {
				return err
			}
		}
	}
	return nil
}

func checkAddress(addr string) bool {
	parts := strings.Split(addr, ":")
	if len(parts) != 2 {
		return false
	}

	if _, err := strconv.Atoi(parts[1]); err != nil {
		return false
	}

	portions := strings.Split(parts[0], ".")
	if len(portions) != 4 {
		return false
	}

	for _, portion := range portions {
		if num, err := strconv.Atoi(portion); err != nil || !(num >= 0 && num <= 255) {
			return false
		}
	}

	return true
}

// registerSelf registers self as child of GroupCacheNodesZkNode, parentZNode should be
// existed in zookeeper beforehand.
func (p *HTTPPool) registerSelf() error {
	if p.self == "" || !strings.HasPrefix(p.self, "http://") {
		return errors.New("invalid self address")
	}

	selfAddr := p.self[strings.Index(p.self, "//") + 2:]
	if !checkAddress(selfAddr) {
		return errors.New("invalid self address")
	}

	path := joinZNode(p.configWatcher.parentZNode, GroupCacheZNode)
	log.Infoln("creating path if not exists. path:", path)
	if err := createZNodeIfNotExist(p.configWatcher.conn, path, nil, zkFlagPermanent, defaultACL); err != nil {
		return err
	}

	path = joinZNode(p.configWatcher.parentZNode, GroupCacheNodesZNode)
	log.Infoln("creating path if not exists. path:", path)
	if err := createZNodeIfNotExist(p.configWatcher.conn, path, nil, zkFlagPermanent, defaultACL); err != nil {
		return err
	}

	path = joinZNode(p.configWatcher.parentZNode, GroupCacheNodesZNode, selfAddr)
	log.Infoln("checking path:", path)
	for tries := 0; ; tries++ {
		if tries >= 5 {
			return errors.New(fmt.Sprintf("failed to checking existence of path: %s", path))
		}

		if exists, _, err := p.configWatcher.conn.Exists(path); err != nil {
			log.Infof("checking existence failed, err: %v", err)
		} else if exists {
			log.Infof("%s already exist, sleeping...", path)
			time.Sleep(p.opts.SessionTimeout * time.Duration(tries))
		} else {
			break
		}
	}

	log.Infoln("creating path:", path)
	_, err := p.configWatcher.conn.Create(path, nil, zk.FlagEphemeral, defaultACL)
	return err
}

func contains(arr []string, t string) bool {
	for _, s := range arr {
		if s == t {
			return true
		}
	}
	return false
}

func (p *HTTPPool) watchConfig() error {
	return p.configWatcher.watchNodeList(func(nodeConfig map[string]*[]byte) (err error) {
		nodes := make([]string, len(nodeConfig))
		i := 0
		for k := range nodeConfig {
			nodes[i] = k
			i++
		}

		nodeSelf := p.self[strings.Index(p.self, "//") + 2:]
		if !contains(nodes, nodeSelf) {
			log.Warnln("Node list not contains self, adding self to node list. node list: %v, self: %s", nodes, nodeSelf)
			// Add self to avoid cyclic loop of peer requests
			nodes = append(nodes, nodeSelf)
		}

		var urls []string
		for _, addr := range nodes {
			if !checkAddress(addr) {
				log.Errorf("invalid address: %s", addr)
				continue
			}
			urls = append(urls, fmt.Sprintf("http://%s", addr))
		}

		log.Infof("updating pool, urls: %v", urls)
		p.Set(urls...)
		return
	})
}

func (p *HTTPPool) RegisterAndWatch() (err error) {
	if err = p.registerSelf() ; err != nil {
		return
	}
	if err = p.watchConfig() ; err != nil {
		return
	}
	return
}

func (p *HTTPPool) HashRingKeys() []int {
	return p.peers.Keys()
}

type noWatchImplementation struct {}
func (noWatchImplementation) RegisterAndWatch() error {
	return errors.New("no implementation")
}<commit_msg>create zk parent node if not exists.<commit_after>package groupcache

import (
	"time"
	"strings"
	"errors"
	"fmt"
	"strconv"

	"github.com/samuel/go-zookeeper/zk"
	log "github.com/Sirupsen/logrus" // todo: use log provider
)

const (
	GroupCacheZNode = "groupcache"
	GroupCacheNodesZNode = GroupCacheZNode + "/nodes"
)

var (
	zkFlagPermanent = int32(0)
	defaultACL = zk.WorldACL(zk.PermAll)
)

type ConfigOpts struct {
	ZkServers      string

	ParentZNode    string

	SessionTimeout time.Duration
}

type ConfigWatcher struct {
	conn        *zk.Conn
	parentZNode string
}

func NewConfigWatcher(zkServers string, parentZNode string, sessionTimeout time.Duration) (w *ConfigWatcher, err error) {
	servers := strings.Split(zkServers, ",")
	log.Infof("connecting to servers: %v", servers)
	conn, _, err := zk.Connect(servers, sessionTimeout) // Note: sessionTimeout will be overwrite by TimeOut of server connectResponse when authenticating.
	if err != nil {
		return
	}
	if strings.HasSuffix(parentZNode, "/") {
		err = errors.New("invalid znode: " + parentZNode)
		return
	}
	w = &ConfigWatcher{conn: conn, parentZNode: parentZNode}
	return
}

func joinZNode(nodes... string) string {
	return strings.Join(nodes, "/")
}

// watchNodeList watches children of GroupCacheNodesZkNode and run callback process
func (w *ConfigWatcher) watchNodeList(process func(map[string]*[]byte) error) error {
	path := joinZNode(w.parentZNode, GroupCacheNodesZNode)

	// get children and watch,
	// then run callback process
	watch := func() (<-chan zk.Event, error) {
		nodes, _, ech, err := w.conn.ChildrenW(path)
		if err != nil {
			return nil, err
		}

		log.Infoln("nodes:", nodes)
		nodeConfig := make(map[string]*[]byte)
		for _, node := range nodes {
			nodePath := joinZNode(path, node)
			log.Infoln("getting path:", nodePath)
			data, _, err := w.conn.Get(nodePath)
			if err != nil {
				log.Errorln("getting path data failed, error:", err)
				continue
			}
			nodeConfig[node] = &data
		}

		err = process(nodeConfig)
		if err != nil {
			log.Errorln("processing nodelist falied, error :", err)
			return ech, err
		}

		return ech, nil
	}

	log.Infoln("watching path:", path)
	var ech <-chan zk.Event
	var err error
	if ech, err = watch(); err != nil {
		log.Errorf("watching path: %s failed.", path)
		return err
	}

	go func() {
		for { // TODO: 处理其他events，以及conn/session close
			evt := <-ech
			log.Infof("node list changed, evt: %+v, watching path: %s", evt, path)
			if ech, err = watch(); err != nil {
				log.Errorf("watching path: %s failed.", path)
			}
		}
	}()

	return nil
}

func (w *ConfigWatcher) Close() {
	w.conn.Close()
}

func createZNodeIfNotExist(conn *zk.Conn, path string, data []byte, flags int32, acl []zk.ACL) error {
	if exists, _, err := conn.Exists(path); err != nil {
		log.Errorln("failed to check existence:", path)
		return err
	} else if !exists {
		_, err = conn.Create(path, data, flags, acl)
		if err != nil {
			log.Errorln("failed to create path:", path)
			if err != zk.ErrNodeExists {
				return err
			}
		}
	}
	return nil
}

func checkAddress(addr string) bool {
	parts := strings.Split(addr, ":")
	if len(parts) != 2 {
		return false
	}

	if _, err := strconv.Atoi(parts[1]); err != nil {
		return false
	}

	portions := strings.Split(parts[0], ".")
	if len(portions) != 4 {
		return false
	}

	for _, portion := range portions {
		if num, err := strconv.Atoi(portion); err != nil || !(num >= 0 && num <= 255) {
			return false
		}
	}

	return true
}

// registerSelf registers self as child of GroupCacheNodesZkNode, parentZNode should be
// existed in zookeeper beforehand.
func (p *HTTPPool) registerSelf() error {
	if p.self == "" || !strings.HasPrefix(p.self, "http://") {
		return errors.New("invalid self address")
	}

	selfAddr := p.self[strings.Index(p.self, "//") + 2:]
	if !checkAddress(selfAddr) {
		return errors.New("invalid self address")
	}

	log.Infoln("creating parent node if not exists. path:", p.configWatcher.parentZNode)
	if err := createZNodeIfNotExist(p.configWatcher.conn, p.configWatcher.parentZNode, nil, zkFlagPermanent, defaultACL); err != nil {
		return err
	}

	path := joinZNode(p.configWatcher.parentZNode, GroupCacheZNode)
	log.Infoln("creating path if not exists. path:", path)
	if err := createZNodeIfNotExist(p.configWatcher.conn, path, nil, zkFlagPermanent, defaultACL); err != nil {
		return err
	}

	path = joinZNode(p.configWatcher.parentZNode, GroupCacheNodesZNode)
	log.Infoln("creating path if not exists. path:", path)
	if err := createZNodeIfNotExist(p.configWatcher.conn, path, nil, zkFlagPermanent, defaultACL); err != nil {
		return err
	}

	path = joinZNode(p.configWatcher.parentZNode, GroupCacheNodesZNode, selfAddr)
	log.Infoln("checking path:", path)
	for tries := 0; ; tries++ {
		if tries >= 5 {
			return errors.New(fmt.Sprintf("failed to checking existence of path: %s", path))
		}

		if exists, _, err := p.configWatcher.conn.Exists(path); err != nil {
			log.Infof("checking existence failed, err: %v", err)
		} else if exists {
			log.Infof("%s already exist, sleeping...", path)
			time.Sleep(p.opts.SessionTimeout * time.Duration(tries))
		} else {
			break
		}
	}

	log.Infoln("creating path:", path)
	_, err := p.configWatcher.conn.Create(path, nil, zk.FlagEphemeral, defaultACL)
	return err
}

func contains(arr []string, t string) bool {
	for _, s := range arr {
		if s == t {
			return true
		}
	}
	return false
}

func (p *HTTPPool) watchConfig() error {
	return p.configWatcher.watchNodeList(func(nodeConfig map[string]*[]byte) (err error) {
		nodes := make([]string, len(nodeConfig))
		i := 0
		for k := range nodeConfig {
			nodes[i] = k
			i++
		}

		nodeSelf := p.self[strings.Index(p.self, "//") + 2:]
		if !contains(nodes, nodeSelf) {
			log.Warnln("Node list not contains self, adding self to node list. node list: %v, self: %s", nodes, nodeSelf)
			// Add self to avoid cyclic loop of peer requests
			nodes = append(nodes, nodeSelf)
		}

		var urls []string
		for _, addr := range nodes {
			if !checkAddress(addr) {
				log.Errorf("invalid address: %s", addr)
				continue
			}
			urls = append(urls, fmt.Sprintf("http://%s", addr))
		}

		log.Infof("updating pool, urls: %v", urls)
		p.Set(urls...)
		return
	})
}

func (p *HTTPPool) RegisterAndWatch() (err error) {
	if err = p.registerSelf() ; err != nil {
		return
	}
	if err = p.watchConfig() ; err != nil {
		return
	}
	return
}

func (p *HTTPPool) HashRingKeys() []int {
	return p.peers.Keys()
}

type noWatchImplementation struct {}
func (noWatchImplementation) RegisterAndWatch() error {
	return errors.New("no implementation")
}<|endoftext|>


<commit_before>package main

import "fmt"
import "github.com/aws/aws-sdk-go/service/iam"
import "github.com/aws/aws-sdk-go/aws"
import "github.com/tj/go-debug"
import "strings"

/*

# lambda-phage config file sample

name: my-first-lambda-function
description: provides some sample stuff
pkg:
  name: my-first-lambda-function.zip
deploy:
  type: s3
  s3-bucket: test-bucket
  use-versioning: true
*/

type Config struct {
	Name        *string
	Description *string
	Archive     *string
	EntryPoint  *string
	MemorySize  *uint
	Runtime     *string
	Timeout     *uint
	Region      *string
	IamRole     struct {
		Arn  *string
		Name *string
	}
	Location *struct {
		S3Bucket        *string
		S3Key           *string
		S3ObjectVersion *string
	}
}

// returns the arn for the role specified
func (c *Config) getRoleArn() (*string, error) {
	if c.IamRole.Arn == nil &&
		c.IamRole.Name == nil {
		return nil, fmt.Errorf("Missing ARN config!")
	}

	// if the config file has an ARN listed,
	// that takes precedence
	if *c.IamRole.Arn != "" {
		return c.IamRole.Arn, nil
	} else if *c.IamRole.Name != "" {
		// look up the iam role name
		iamRole, err := getIamPolicy(*c.IamRole.Name)
		if err != nil {
			return nil, err
		}
		return iamRole, err
	} else {
		// TODO: create a default standard role
		// and update config file
	}

	return nil, fmt.Errorf("how did you get here")
}

// returns a normalized S3 path to a file
// based on config information
//
// requires the file name of the archive you'll upload
//
// returns the bucket and the key
func (c *Config) getS3Info(fName string) (bucket, key *string) {
	debug := debug.Debug("config.getS3Info")
	loc := c.Location
	if loc == nil {
		debug("no upload location info found")
		return nil, nil
	}

	if loc.S3Bucket == nil {
		// TODO: make these return an error instead??
		debug("upload location info found, but s3 bucket missing")
		return nil, nil
	}

	b := *loc.S3Bucket
	var k string
	if loc.S3Key == nil {
		debug("no s3 key in location config, using file name")
		// no key in config?
		// then the key is the name of the file
		// being passed in
		k = fName
	} else if loc.S3Key != nil &&
		len(*loc.S3Key) > 0 {
		// key in config? let's see
		// if it looks like a zip file
		if strings.Index(*loc.S3Key, ".zip") > -1 {
			// great, we can use this one for the key
			k = *loc.S3Key
		} else {
			// if there's no .zip in the s3Key config
			// setting, then assume this is to
			// be the first part in a directory
			dir := *loc.S3Key
			sl := []byte("/")
			if dir[len(dir)-1] != sl[0] {
				dir += "/"
			}

			k = dir + fName
		}
	} else {
		debug("empty s3key found in config file")
		k = fName
	}

	return &b, &k
}

// gets an IAM policy by name
func getIamPolicy(name string) (*string, error) {
	i := iam.New(nil)

	r, err := i.GetRole(&iam.GetRoleInput{
		RoleName: &name,
	})

	if err != nil {
		return aws.String(""), err
	}

	return r.Role.ARN, nil
}
<commit_msg>update for new aws APIs<commit_after>package main

import "fmt"
import "github.com/aws/aws-sdk-go/service/iam"
import "github.com/aws/aws-sdk-go/aws"
import "github.com/tj/go-debug"
import "strings"

/*

# lambda-phage config file sample

name: my-first-lambda-function
description: provides some sample stuff
pkg:
  name: my-first-lambda-function.zip
deploy:
  type: s3
  s3-bucket: test-bucket
  use-versioning: true
*/

type Config struct {
	Name        *string
	Description *string
	Archive     *string
	EntryPoint  *string
	MemorySize  *uint
	Runtime     *string
	Timeout     *uint
	Region      *string
	IamRole     struct {
		Arn  *string
		Name *string
	}
	Location *struct {
		S3Bucket        *string
		S3Key           *string
		S3ObjectVersion *string
	}
}

// returns the arn for the role specified
func (c *Config) getRoleArn() (*string, error) {
	if c.IamRole.Arn == nil &&
		c.IamRole.Name == nil {
		return nil, fmt.Errorf("Missing ARN config!")
	}

	// if the config file has an ARN listed,
	// that takes precedence
	if *c.IamRole.Arn != "" {
		return c.IamRole.Arn, nil
	} else if *c.IamRole.Name != "" {
		// look up the iam role name
		iamRole, err := getIamPolicy(*c.IamRole.Name)
		if err != nil {
			return nil, err
		}
		return iamRole, err
	} else {
		// TODO: create a default standard role
		// and update config file
	}

	return nil, fmt.Errorf("how did you get here")
}

// returns a normalized S3 path to a file
// based on config information
//
// requires the file name of the archive you'll upload
//
// returns the bucket and the key
func (c *Config) getS3Info(fName string) (bucket, key *string) {
	debug := debug.Debug("config.getS3Info")
	loc := c.Location
	if loc == nil {
		debug("no upload location info found")
		return nil, nil
	}

	if loc.S3Bucket == nil {
		// TODO: make these return an error instead??
		debug("upload location info found, but s3 bucket missing")
		return nil, nil
	}

	b := *loc.S3Bucket
	var k string
	if loc.S3Key == nil {
		debug("no s3 key in location config, using file name")
		// no key in config?
		// then the key is the name of the file
		// being passed in
		k = fName
	} else if loc.S3Key != nil &&
		len(*loc.S3Key) > 0 {
		// key in config? let's see
		// if it looks like a zip file
		if strings.Index(*loc.S3Key, ".zip") > -1 {
			// great, we can use this one for the key
			k = *loc.S3Key
		} else {
			// if there's no .zip in the s3Key config
			// setting, then assume this is to
			// be the first part in a directory
			dir := *loc.S3Key
			sl := []byte("/")
			if dir[len(dir)-1] != sl[0] {
				dir += "/"
			}

			k = dir + fName
		}
	} else {
		debug("empty s3key found in config file")
		k = fName
	}

	return &b, &k
}

// gets an IAM policy by name
func getIamPolicy(name string) (*string, error) {
	i := iam.New(nil)

	r, err := i.GetRole(&iam.GetRoleInput{
		RoleName: &name,
	})

	if err != nil {
		return aws.String(""), err
	}

	return r.Role.Arn, nil
}
<|endoftext|>


<commit_before>package mimic

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

// Config stores configuration loaded from config.json
type Config struct {
	Port  int    `json:"port"`
	Rules []Rule `json:"rules"`
}

// Rule stores configuration of a routing rule.
type Rule struct {
	Method  string
	Path    string
	Content string
}

// NewConfig returns Config stores cnofigration
// read from json at configPath.
func NewConfig(configPath string) (Config, error) {
	content, err := ioutil.ReadFile(configPath)

	if err != nil {
		return Config{}, err
	}

	c := Config{}
	err = json.Unmarshal([]byte(content), &c)

	if err != nil {
		return Config{}, err
	}

	return c, nil
}
<commit_msg>default port is 3390<commit_after>package mimic

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

// Config stores configuration loaded from config.json
type Config struct {
	Port  int    `json:"port"`
	Rules []Rule `json:"rules"`
}

// Rule stores configuration of a routing rule.
type Rule struct {
	Method  string
	Path    string
	Content string
}

// NewConfig returns Config stores cnofigration
// read from json at configPath.
func NewConfig(configPath string) (Config, error) {
	content, err := ioutil.ReadFile(configPath)

	if err != nil {
		return Config{}, err
	}

	c := Config{}
	err = json.Unmarshal([]byte(content), &c)

	if err != nil {
		return Config{}, err
	}

	if c.Port == 0 {
		c.Port = 3390
	}

	return c, nil
}
<|endoftext|>


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

package config

import (
  "encoding/json"
  "encoding/xml"
  "fmt"
  "gopkg.in/v1/yaml"
  "io/ioutil"
  "reflect"
  "regexp"
  "strings"

  "github.com/demdxx/gocast"
)

var (
  escapeEx = regexp.MustCompile(`\{\}\*\+\(\)\[\]`)
)

type Config map[string]interface{}

func From(c interface{}) (Config, error) {
  return FromQuick(c)
}

func FromQuick(c interface{}) (Config, error) {
  if nil == c {
    return make(Config), nil
  }

  sm, err := gocast.ToSiMap(c, "field")
  if nil != err {
    return nil, err
  }

  conf := make(Config)
  for k, v := range sm {
    if nil == v {
      conf[k] = v
      continue
    }

    t := reflect.TypeOf(v)
    switch t.Kind() {
    case reflect.Map:
      conf[k], _ = FromQuick(v)
      break
    case reflect.Slice:
      conf[k], _ = FromSliceQuick(v)
      break
    default:
      conf[k] = v
      break
    }
  }
  return conf, nil
}

func New() Config {
  return make(Config)
}

func FromFile(filename, ftype string) (Config, error) {
  bytes, err := ioutil.ReadFile(filename)
  if nil != err {
    return nil, err
  }
  return FromData(bytes, ftype)
}

func FromData(data []byte, dtype string) (conf Config, err error) {
  var info interface{}
  switch strings.ToLower(dtype) {
  case "json":
    err = json.Unmarshal(data, &info)
    break
  case "xml":
    err = xml.Unmarshal(data, &info)
    break
  case "yaml":
    err = yaml.Unmarshal(data, &info)
    break
  default:
    err = ErrInvalidConfigFormat
  }
  if nil == err {
    conf, err = From(info)
  }
  return
}

///////////////////////////////////////////////////////////////////////////////
/// Getters/Setters
///////////////////////////////////////////////////////////////////////////////

func (conf Config) Get(path string) (interface{}, error) {
  return conf.GetPath(strings.Split(path, "."))
}

func (conf Config) GetPath(path []string) (interface{}, error) {
  if len(path) < 1 {
    return nil, ErrInvalidPath
  }

  curConf := conf
  for i, key := range path {
    if len(key) < 1 {
      return nil, ErrInvalidPath
    }

    isLast := i >= len(path)-1

    if "*" == key { // Response as array
      if isLast {
        return curConf, nil
      }
      response := make([]interface{}, 0)

      for _, v := range curConf {
        switch a := v.(type) {
        case ConfigArr:
          if r, err := a.GetPath(path[i:]); nil == err {
            response = append(response, r)
          }
          break
        case Config:
          if r, err := a.GetPath(path[i:]); nil == err {
            response = append(response, r)
          }
          break
        }
      }
      return response, nil
    } else {
      if it, ok := curConf[key]; !ok || nil == it {
        return nil, ErrNoValue
      } else {
        if isLast {
          return it, nil
        }

        switch a := it.(type) {
        case ConfigArr:
          return a.GetPath(path[i:])
          break
        case Config:
          curConf = a
          break
        default:
          return it, ErrNoValid
        }
      }
    }
  }
  return nil, nil
}

func (conf Config) GetDefault(path string, def interface{}) interface{} {
  val, err := conf.Get(path)
  if nil == val || nil != err {
    return def
  }
  return val
}
func (conf Config) String(path string) string {
  return conf.StringOrDefault(path, "")
}

func (conf Config) StringOrDefault(path string, def string) string {
  return gocast.ToString(conf.GetDefault(path, def))
}

func (conf Config) IntOrDefault(path string, def int) int {
  return gocast.ToInt(conf.GetDefault(path, def))
}

func (conf Config) Float64OrDefault(path string, def float64) float64 {
  return gocast.ToFloat64(conf.GetDefault(path, def))
}

func (conf Config) BoolOrDefault(path string, def bool) bool {
  return gocast.ToBool(conf.GetDefault(path, def))
}

/// Set

func (conf Config) Set(path string, value interface{}) Config {
  return conf.SetPath(strings.Split(path, "."), value)
}

func (conf Config) SetPath(fullpath []string, value interface{}) Config {
  if len(fullpath) < 1 {
    return conf
  }

  key := fullpath[len(fullpath)-1]
  path := fullpath[:len(fullpath)-1]
  curConf := conf

  if len(path) > 0 {
    if isArrayChain(fullpath[0]) {
      return conf // Invalid path
    }

    for i, key := range path {
      isArray := false
      if i < len(path)-1 {
        isArray = isArrayChain(path[i+1])
      }

      if it, ok := curConf[key]; !ok || nil == it { // We have path key
        if isArray {
          newConf := make(ConfigArr, 0)
          curConf[key] = newConf.SetPath(fullpath[i:], value)
          return conf
        } else {
          newConf := make(Config)
          curConf[key] = newConf
          curConf = newConf
        }
        continue
      } else { // It`s something eles
        switch a := it.(type) {
        case ConfigArr:
          if isArray {
            a.SetPath(fullpath[i:], value)
            return conf
          } else {
            newConf := make(Config)
            curConf[key] = newConf
            curConf = newConf
          }
          break
        case Config:
          if isArray {
            newConf := make(ConfigArr, 0)
            curConf[key] = newConf.SetPath(fullpath[i:], value)
            return conf
          } else {
            curConf = a
          }
          break
        default: // Replace any other value
          newConf := make(Config)
          curConf[key] = newConf
          curConf = newConf
        }
      }
    }
  }

  if isArrayChain(key) {
    // Invalid! I cant set as for array
  } else {
    curConf[key] = prepareValueForSet(value)
  }
  return conf
}

func (conf Config) Update(conf2 Config) Config {
  if nil != conf2 {
    for k, v := range conf2 {
      if v2, ok := conf[k]; ok {
        switch c := v2.(type) {
        case Config:
          switch nv := v.(type) {
          case Config:
            c.Update(nv)
            break
          default:
            conf[k] = v
            break
          }
          break
        default:
          conf[k] = v
          break
        }
      } else {
        conf[k] = v
      }
    }
  }
  return conf
}

func (conf Config) UpdateByPath(path string) Config {
  conf2, _ := conf.Get(path)
  if nil != conf2 {
    switch conf2.(type) {
    case Config:
      return conf.Update(conf2.(Config))
      break
    }
  }
  return conf
}

///////////////////////////////////////////////////////////////////////////////
/// Conversion
///////////////////////////////////////////////////////////////////////////////

func (conf Config) JSON() ([]byte, error) {
  return json.Marshal(conf)
}

func (conf Config) JSONPrettify() ([]byte, error) {
  return json.MarshalIndent(conf, "", "\t")
}

///////////////////////////////////////////////////////////////////////////////
/// Processing
///////////////////////////////////////////////////////////////////////////////

func (conf Config) Prepare(escLeft, escRight string) {
  escLeft = escapeEx.ReplaceAllString(escLeft, `\$1`)
  escRight = escapeEx.ReplaceAllString(escRight, `\$1`)
  r := regexp.MustCompile(fmt.Sprintf(`%s([\w\.\d+*$]+)%s`, escLeft, escRight))
  prepare(conf, conf, r)
}

///////////////////////////////////////////////////////////////////////////////
/// Helpers
///////////////////////////////////////////////////////////////////////////////

func prepare(root, conf Config, r *regexp.Regexp) {
  if nil == conf || len(conf) < 1 {
    return
  }

  for k, v := range conf {
    if V := prepareItem(root, v, r); nil != V {
      conf[k] = V
    }
  }
}

func prepareArray(root Config, a ConfigArr, r *regexp.Regexp) {
  if nil == a || len(a) < 1 {
    return
  }
  for i, it := range a {
    if V := prepareItem(root, it, r); nil != V {
      a[i] = V
    }
  }
}

func prepareItem(root Config, it interface{}, r *regexp.Regexp) interface{} {
  switch s := it.(type) {
  case string:
    return r.ReplaceAllStringFunc(s, func(s string) string {
      return fmt.Sprintf("%v", root.GetDefault(s[2:len(s)-2], ""))
    })
    break
  case Config:
    prepare(root, s, r)
    break
  case ConfigArr:
    prepareArray(root, s, r)
    break
  }
  return nil
}
<commit_msg>Fix gocast.ToSiMap convert recursive flag<commit_after>//
// The MIT License (MIT)
//
// Copyright (c) 2015 Dmiptry Ponomarev <demdxx@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//

package config

import (
  "encoding/json"
  "encoding/xml"
  "fmt"
  "gopkg.in/v1/yaml"
  "io/ioutil"
  "reflect"
  "regexp"
  "strings"

  "github.com/demdxx/gocast"
)

var (
  escapeEx = regexp.MustCompile(`\{\}\*\+\(\)\[\]`)
)

type Config map[string]interface{}

func From(c interface{}) (Config, error) {
  return FromQuick(c)
}

func FromQuick(c interface{}) (Config, error) {
  if nil == c {
    return make(Config), nil
  }

  sm, err := gocast.ToSiMap(c, "field", true)
  if nil != err {
    return nil, err
  }

  conf := make(Config)
  for k, v := range sm {
    if nil == v {
      conf[k] = v
      continue
    }

    t := reflect.TypeOf(v)
    switch t.Kind() {
    case reflect.Map:
      conf[k], _ = FromQuick(v)
      break
    case reflect.Slice:
      conf[k], _ = FromSliceQuick(v)
      break
    default:
      conf[k] = v
      break
    }
  }
  return conf, nil
}

func New() Config {
  return make(Config)
}

func FromFile(filename, ftype string) (Config, error) {
  bytes, err := ioutil.ReadFile(filename)
  if nil != err {
    return nil, err
  }
  return FromData(bytes, ftype)
}

func FromData(data []byte, dtype string) (conf Config, err error) {
  var info interface{}
  switch strings.ToLower(dtype) {
  case "json":
    err = json.Unmarshal(data, &info)
    break
  case "xml":
    err = xml.Unmarshal(data, &info)
    break
  case "yaml":
    err = yaml.Unmarshal(data, &info)
    break
  default:
    err = ErrInvalidConfigFormat
  }
  if nil == err {
    conf, err = From(info)
  }
  return
}

///////////////////////////////////////////////////////////////////////////////
/// Getters/Setters
///////////////////////////////////////////////////////////////////////////////

func (conf Config) Get(path string) (interface{}, error) {
  return conf.GetPath(strings.Split(path, "."))
}

func (conf Config) GetPath(path []string) (interface{}, error) {
  if len(path) < 1 {
    return nil, ErrInvalidPath
  }

  curConf := conf
  for i, key := range path {
    if len(key) < 1 {
      return nil, ErrInvalidPath
    }

    isLast := i >= len(path)-1

    if "*" == key { // Response as array
      if isLast {
        return curConf, nil
      }
      response := make([]interface{}, 0)

      for _, v := range curConf {
        switch a := v.(type) {
        case ConfigArr:
          if r, err := a.GetPath(path[i:]); nil == err {
            response = append(response, r)
          }
          break
        case Config:
          if r, err := a.GetPath(path[i:]); nil == err {
            response = append(response, r)
          }
          break
        }
      }
      return response, nil
    } else {
      if it, ok := curConf[key]; !ok || nil == it {
        return nil, ErrNoValue
      } else {
        if isLast {
          return it, nil
        }

        switch a := it.(type) {
        case ConfigArr:
          return a.GetPath(path[i:])
          break
        case Config:
          curConf = a
          break
        default:
          return it, ErrNoValid
        }
      }
    }
  }
  return nil, nil
}

func (conf Config) GetDefault(path string, def interface{}) interface{} {
  val, err := conf.Get(path)
  if nil == val || nil != err {
    return def
  }
  return val
}
func (conf Config) String(path string) string {
  return conf.StringOrDefault(path, "")
}

func (conf Config) StringOrDefault(path string, def string) string {
  return gocast.ToString(conf.GetDefault(path, def))
}

func (conf Config) IntOrDefault(path string, def int) int {
  return gocast.ToInt(conf.GetDefault(path, def))
}

func (conf Config) Float64OrDefault(path string, def float64) float64 {
  return gocast.ToFloat64(conf.GetDefault(path, def))
}

func (conf Config) BoolOrDefault(path string, def bool) bool {
  return gocast.ToBool(conf.GetDefault(path, def))
}

/// Set

func (conf Config) Set(path string, value interface{}) Config {
  return conf.SetPath(strings.Split(path, "."), value)
}

func (conf Config) SetPath(fullpath []string, value interface{}) Config {
  if len(fullpath) < 1 {
    return conf
  }

  key := fullpath[len(fullpath)-1]
  path := fullpath[:len(fullpath)-1]
  curConf := conf

  if len(path) > 0 {
    if isArrayChain(fullpath[0]) {
      return conf // Invalid path
    }

    for i, key := range path {
      isArray := false
      if i < len(path)-1 {
        isArray = isArrayChain(path[i+1])
      }

      if it, ok := curConf[key]; !ok || nil == it { // We have path key
        if isArray {
          newConf := make(ConfigArr, 0)
          curConf[key] = newConf.SetPath(fullpath[i:], value)
          return conf
        } else {
          newConf := make(Config)
          curConf[key] = newConf
          curConf = newConf
        }
        continue
      } else { // It`s something eles
        switch a := it.(type) {
        case ConfigArr:
          if isArray {
            a.SetPath(fullpath[i:], value)
            return conf
          } else {
            newConf := make(Config)
            curConf[key] = newConf
            curConf = newConf
          }
          break
        case Config:
          if isArray {
            newConf := make(ConfigArr, 0)
            curConf[key] = newConf.SetPath(fullpath[i:], value)
            return conf
          } else {
            curConf = a
          }
          break
        default: // Replace any other value
          newConf := make(Config)
          curConf[key] = newConf
          curConf = newConf
        }
      }
    }
  }

  if isArrayChain(key) {
    // Invalid! I cant set as for array
  } else {
    curConf[key] = prepareValueForSet(value)
  }
  return conf
}

func (conf Config) Update(conf2 Config) Config {
  if nil != conf2 {
    for k, v := range conf2 {
      if v2, ok := conf[k]; ok {
        switch c := v2.(type) {
        case Config:
          switch nv := v.(type) {
          case Config:
            c.Update(nv)
            break
          default:
            conf[k] = v
            break
          }
          break
        default:
          conf[k] = v
          break
        }
      } else {
        conf[k] = v
      }
    }
  }
  return conf
}

func (conf Config) UpdateByPath(path string) Config {
  conf2, _ := conf.Get(path)
  if nil != conf2 {
    switch conf2.(type) {
    case Config:
      return conf.Update(conf2.(Config))
      break
    }
  }
  return conf
}

///////////////////////////////////////////////////////////////////////////////
/// Conversion
///////////////////////////////////////////////////////////////////////////////

func (conf Config) JSON() ([]byte, error) {
  return json.Marshal(conf)
}

func (conf Config) JSONPrettify() ([]byte, error) {
  return json.MarshalIndent(conf, "", "\t")
}

///////////////////////////////////////////////////////////////////////////////
/// Processing
///////////////////////////////////////////////////////////////////////////////

func (conf Config) Prepare(escLeft, escRight string) {
  escLeft = escapeEx.ReplaceAllString(escLeft, `\$1`)
  escRight = escapeEx.ReplaceAllString(escRight, `\$1`)
  r := regexp.MustCompile(fmt.Sprintf(`%s([\w\.\d+*$]+)%s`, escLeft, escRight))
  prepare(conf, conf, r)
}

///////////////////////////////////////////////////////////////////////////////
/// Helpers
///////////////////////////////////////////////////////////////////////////////

func prepare(root, conf Config, r *regexp.Regexp) {
  if nil == conf || len(conf) < 1 {
    return
  }

  for k, v := range conf {
    if V := prepareItem(root, v, r); nil != V {
      conf[k] = V
    }
  }
}

func prepareArray(root Config, a ConfigArr, r *regexp.Regexp) {
  if nil == a || len(a) < 1 {
    return
  }
  for i, it := range a {
    if V := prepareItem(root, it, r); nil != V {
      a[i] = V
    }
  }
}

func prepareItem(root Config, it interface{}, r *regexp.Regexp) interface{} {
  switch s := it.(type) {
  case string:
    return r.ReplaceAllStringFunc(s, func(s string) string {
      return fmt.Sprintf("%v", root.GetDefault(s[2:len(s)-2], ""))
    })
    break
  case Config:
    prepare(root, s, r)
    break
  case ConfigArr:
    prepareArray(root, s, r)
    break
  }
  return nil
}
<|endoftext|>


<commit_before>package cors

import (
	"net/http"

	"gopkg.in/gin-gonic/gin.v1"
)

type cors struct {
	allowAllOrigins  bool
	allowOriginFunc  func(string) bool
	allowOrigins     []string
	exposeHeaders    []string
	normalHeaders    http.Header
	preflightHeaders http.Header
}

func newCors(config Config) *cors {
	if err := config.Validate(); err != nil {
		panic(err.Error())
	}
	return &cors{
		allowOriginFunc:  config.AllowOriginFunc,
		allowAllOrigins:  config.AllowAllOrigins,
		allowOrigins:     normalize(config.AllowOrigins),
		normalHeaders:    generateNormalHeaders(config),
		preflightHeaders: generatePreflightHeaders(config),
	}
}

func (cors *cors) applyCors(c *gin.Context) {
	origin := c.Request.Header.Get("Origin")
	if len(origin) == 0 {
		// request is not a CORS request
		return
	}
	if !cors.validateOrigin(origin) {
		c.AbortWithStatus(http.StatusForbidden)
		return
	}

	if c.Request.Method == "OPTIONS" {
		cors.handlePreflight(c)
		defer c.AbortWithStatus(200)
	} else {
		cors.handleNormal(c)
	}

	if !cors.allowAllOrigins {
		c.Header("Access-Control-Allow-Origin", origin)
	}
}

func (cors *cors) validateOrigin(origin string) bool {
	if cors.allowAllOrigins {
		return true
	}
	for _, value := range cors.allowOrigins {
		if value == origin {
			return true
		}
	}
	if cors.allowOriginFunc != nil {
		return cors.allowOriginFunc(origin)
	}
	return false
}

func (cors *cors) handlePreflight(c *gin.Context) {
	header := c.Writer.Header()
	for key, value := range cors.preflightHeaders {
		header[key] = value
	}
}

func (cors *cors) handleNormal(c *gin.Context) {
	header := c.Writer.Header()
	for key, value := range cors.normalHeaders {
		header[key] = value
	}
}
<commit_msg>Fix logic error for when setting allowedAllOrigins and allowedCredentials<commit_after>package cors

import (
	"net/http"

	"gopkg.in/gin-gonic/gin.v1"
)

type cors struct {
	allowAllOrigins  bool
	allowCredentials bool
	allowOriginFunc  func(string) bool
	allowOrigins     []string
	exposeHeaders    []string
	normalHeaders    http.Header
	preflightHeaders http.Header
}

func newCors(config Config) *cors {
	if err := config.Validate(); err != nil {
		panic(err.Error())
	}
	return &cors{
		allowOriginFunc:  config.AllowOriginFunc,
		allowAllOrigins:  config.AllowAllOrigins,
		allowCredentials: config.AllowCredentials,
		allowOrigins:     normalize(config.AllowOrigins),
		normalHeaders:    generateNormalHeaders(config),
		preflightHeaders: generatePreflightHeaders(config),
	}
}

func (cors *cors) applyCors(c *gin.Context) {
	origin := c.Request.Header.Get("Origin")
	if len(origin) == 0 {
		// request is not a CORS request
		return
	}
	if !cors.validateOrigin(origin) {
		c.AbortWithStatus(http.StatusForbidden)
		return
	}

	if c.Request.Method == "OPTIONS" {
		cors.handlePreflight(c)
		defer c.AbortWithStatus(200)
	} else {
		cors.handleNormal(c)
	}

	if !cors.allowAllOrigins && !cors.allowCredentials {
		c.Header("Access-Control-Allow-Origin", origin)
	}
}

func (cors *cors) validateOrigin(origin string) bool {
	if cors.allowAllOrigins {
		return true
	}
	for _, value := range cors.allowOrigins {
		if value == origin {
			return true
		}
	}
	if cors.allowOriginFunc != nil {
		return cors.allowOriginFunc(origin)
	}
	return false
}

func (cors *cors) handlePreflight(c *gin.Context) {
	header := c.Writer.Header()
	for key, value := range cors.preflightHeaders {
		header[key] = value
	}
}

func (cors *cors) handleNormal(c *gin.Context) {
	header := c.Writer.Header()
	for key, value := range cors.normalHeaders {
		header[key] = value
	}
}
<|endoftext|>


<commit_before>package config

import (
	"github.com/vaughan0/go-ini"
)

type (
	// Parser 接口结构体
	Parser interface {
		// Parse 解析方法
		Parse() (map[string]ini.File, error)
	}
	// config config 结构体对象
	config struct {
		// data 存储着具体的 config 参数
		data map[string]ini.File
		// Debug 是否是 debug 模式
		Debug bool
		// Parser 解析器
		parser Parser
	}
)

var (
	// Config 对外提供的config参数
	c = config{}
	// 兼容旧的调用方法,不建议使用
	Config = c.data
)

// SetDebug 设置测试环境
func SetDebug(b bool) {
	c.Debug = b
}

// Debug 返回debug状态
func Debug() bool {
	return c.Debug
}

// Data 获取数据
func Data(fileName string) ini.File {
	return c.data[fileName]
}

// Init 初始化
func Init(debug bool, parser ...Parser) (err error) {
	return c.init(debug, parser...)
}

// Reload 重新载入
func Reload() error {
	return c.Load()
}

// Init 初始化配置文件
func (c *config) init(debug bool, parser ...Parser) (err error) {
	c.Debug = debug
	if len(parser) == 0 {
		parser[0] = FileParser{Debug: c.Debug}
	}
	c.parser = parser[0]
	return c.Load()
}

func (c *config) Load() (err error) {
	c.data, err = c.parser.Parse()
	Config = c.data
	return
}
<commit_msg>添加版本号<commit_after>package config

import (
	"github.com/vaughan0/go-ini"
)

type (
	// Parser 接口结构体
	Parser interface {
		// Parse 解析方法
		Parse() (map[string]ini.File, error)
	}
	// config config 结构体对象
	config struct {
		// data 存储着具体的 config 参数
		data map[string]ini.File
		// Debug 是否是 debug 模式
		Debug bool
		// Parser 解析器
		parser Parser
	}
)

var (
	// Config 对外提供的config参数
	c = config{}
	// 兼容旧的调用方法,不建议使用
	Config = c.data
	// 版本号
	Version = "v2.0"
)

// SetDebug 设置测试环境
func SetDebug(b bool) {
	c.Debug = b
}

// Debug 返回debug状态
func Debug() bool {
	return c.Debug
}

// Data 获取数据
func Data(fileName string) ini.File {
	return c.data[fileName]
}

// Init 初始化
func Init(debug bool, parser ...Parser) (err error) {
	return c.init(debug, parser...)
}

// Reload 重新载入
func Reload() error {
	return c.Load()
}

// Init 初始化配置文件
func (c *config) init(debug bool, parser ...Parser) (err error) {
	c.Debug = debug
	if len(parser) == 0 {
		parser[0] = FileParser{Debug: c.Debug}
	}
	c.parser = parser[0]
	return c.Load()
}

func (c *config) Load() (err error) {
	c.data, err = c.parser.Parse()
	Config = c.data
	return
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"regexp"

	"github.com/BurntSushi/toml"
)

var (
	HOTURL     = `http://www2.medis.or.jp/hcode/` //HOTURL
	ConfigFile = `config.toml`
	CONFIG     = LoadConfig(ConfigFile)
)

//Config binds config.toml
type Config struct {
	Proxy   string
	SaveDir string
	Y       *Y
	HOT     *HOT
	HOTAdd  *HOTAdd
	HOTDel  *HOTDel
}

//NewConfig create a Config struct.
func NewConfig() Config {
	return Config{
		Proxy:   "",
		SaveDir: "save",
		Y:       NewY(),
		HOT:     NewHOT(),
		HOTAdd:  NewHOTAdd(),
		HOTDel:  NewHOTDel(),
	}
}

//Init initializes Config.
func (config *Config) Init() {
	sites := []Initer{
		config.Y,
		config.HOT,
		config.HOTAdd,
		config.HOTDel,
	}
	for _, s := range sites {
		s.Init()
	}
}

//Dump create config.toml
func (config Config) Dump(path string) error {
	w, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		fmt.Printf("file open error: %s", path)
		return err
	}
	enc := toml.NewEncoder(w)
	err = enc.Encode(&config)
	return err
}

//LoadConfig create Config from config.toml.
func LoadConfig(path string) Config {
	_, err := os.Stat(path)
	if err != nil {
		fmt.Printf("Cannot read file: %s", path)
		config := NewConfig()
		err = config.Dump(path)
		if err != nil {
			fmt.Printf("Failed create config.toml: %s", path)
		}
		return config
	}
	return readConfig(path)
}

func readConfig(path string) Config {
	var config Config
	_, err := toml.DecodeFile(path, &config)
	if err != nil {
		fmt.Printf("TOML decode error: %s", path)
		fmt.Println(err)
	}
	return config
}

//Initer has Init function
type Initer interface {
	Init()
}

//Site has setting to download files.
type Site struct {
	URL          string
	TargetRegexp string
	Dirname      string
}

//CompiledTarget return *regexp.Regexp compiling TargetRegexp.
func (s Site) CompiledTarget() *regexp.Regexp {
	compiledRegexp, err := regexp.Compile(s.TargetRegexp)
	if err != nil {
		fmt.Printf("Failed regexp compile: %s", s.TargetRegexp)
		panic("regexp error")
	}
	return compiledRegexp
}

//Y site
type Y struct {
	Site
	Initer
}

//NewY create a new Y
func NewY() *Y {
	y := &Y{}
	y.Init()
	return y
}

//Init initialize Y
func (y *Y) Init() {
	if y.URL == "" {
		y.URL = "https://www.shinryohoshu.mhlw.go.jp/shinryohoshu/downloadMenu/"
		y.TargetRegexp = `/shinryohoshu/downloadMenu/yFile;jsessionid=[0-9A-Z]+`
		y.Dirname = `y`
	}
}

//HOT site
type HOT struct {
	Site
	Initer
}

//NewHOT create a new HOT
func NewHOT() *HOT {
	h := &HOT{}
	h.Init()
	return h
}

//Init initialize HOT
func (h *HOT) Init() {
	if h.URL == "" {
		h.URL = HOTURL
		h.TargetRegexp = HOTURL + `moto_data/h[0-9]{8}.zip`
		h.Dirname = `hot`
	}
}

//HOTAdd site
type HOTAdd struct {
	Site
	Initer
}

//NewHOTAdd create a new HOTAdd
func NewHOTAdd() *HOTAdd {
	ha := &HOTAdd{}
	ha.Init()
	return ha
}

//Init initialize HOTAdd
func (ha *HOTAdd) Init() {
	if ha.URL == "" {
		ha.URL = HOTURL + `tuika/index.html`
		ha.TargetRegexp = HOTURL + `tuika/data/[0-9]{4}/[0-9]{8}.txt`
		ha.Dirname = `hot`
	}
}

//HOTDel site
type HOTDel struct {
	Site
	Initer
}

//NewHOTDel create a new HOTDel
func NewHOTDel() *HOTDel {
	hd := &HOTDel{}
	hd.Init()
	return hd
}

//Init initialize HOTDel
func (hd *HOTDel) Init() {
	if hd.URL == "" {
		hd.URL = HOTURL
		hd.TargetRegexp = HOTURL + `moto_data/h[0-9]{8}del.zip`
		hd.Dirname = `hot`
	}
}
<commit_msg>薬価マスタのURLの誤記修正<commit_after>package main

import (
	"fmt"
	"os"
	"regexp"

	"github.com/BurntSushi/toml"
)

var (
	HOTURL     = `http://www2.medis.or.jp/hcode/` //HOTURL
	ConfigFile = `config.toml`
	CONFIG     = LoadConfig(ConfigFile)
)

//Config binds config.toml
type Config struct {
	Proxy   string
	SaveDir string
	Y       *Y
	HOT     *HOT
	HOTAdd  *HOTAdd
	HOTDel  *HOTDel
}

//NewConfig create a Config struct.
func NewConfig() Config {
	return Config{
		Proxy:   "",
		SaveDir: "save",
		Y:       NewY(),
		HOT:     NewHOT(),
		HOTAdd:  NewHOTAdd(),
		HOTDel:  NewHOTDel(),
	}
}

//Init initializes Config.
func (config *Config) Init() {
	sites := []Initer{
		config.Y,
		config.HOT,
		config.HOTAdd,
		config.HOTDel,
	}
	for _, s := range sites {
		s.Init()
	}
}

//Dump create config.toml
func (config Config) Dump(path string) error {
	w, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		fmt.Printf("file open error: %s", path)
		return err
	}
	enc := toml.NewEncoder(w)
	err = enc.Encode(&config)
	return err
}

//LoadConfig create Config from config.toml.
func LoadConfig(path string) Config {
	_, err := os.Stat(path)
	if err != nil {
		fmt.Printf("Cannot read file: %s", path)
		config := NewConfig()
		err = config.Dump(path)
		if err != nil {
			fmt.Printf("Failed create config.toml: %s", path)
		}
		return config
	}
	return readConfig(path)
}

func readConfig(path string) Config {
	var config Config
	_, err := toml.DecodeFile(path, &config)
	if err != nil {
		fmt.Printf("TOML decode error: %s", path)
		fmt.Println(err)
	}
	return config
}

//Initer has Init function
type Initer interface {
	Init()
}

//Site has setting to download files.
type Site struct {
	URL          string
	TargetRegexp string
	Dirname      string
}

//CompiledTarget return *regexp.Regexp compiling TargetRegexp.
func (s Site) CompiledTarget() *regexp.Regexp {
	compiledRegexp, err := regexp.Compile(s.TargetRegexp)
	if err != nil {
		fmt.Printf("Failed regexp compile: %s", s.TargetRegexp)
		panic("regexp error")
	}
	return compiledRegexp
}

//Y site
type Y struct {
	Site
	Initer
}

//NewY create a new Y
func NewY() *Y {
	y := &Y{}
	y.Init()
	return y
}

//Init initialize Y
func (y *Y) Init() {
	if y.URL == "" {
		y.URL = "https://shinryohoshu.mhlw.go.jp/shinryohoshu/downloadMenu/"
		y.TargetRegexp = `/shinryohoshu/downloadMenu/yFile;jsessionid=[0-9A-Z]+`
		y.Dirname = `y`
	}
}

//HOT site
type HOT struct {
	Site
	Initer
}

//NewHOT create a new HOT
func NewHOT() *HOT {
	h := &HOT{}
	h.Init()
	return h
}

//Init initialize HOT
func (h *HOT) Init() {
	if h.URL == "" {
		h.URL = HOTURL
		h.TargetRegexp = HOTURL + `moto_data/h[0-9]{8}.zip`
		h.Dirname = `hot`
	}
}

//HOTAdd site
type HOTAdd struct {
	Site
	Initer
}

//NewHOTAdd create a new HOTAdd
func NewHOTAdd() *HOTAdd {
	ha := &HOTAdd{}
	ha.Init()
	return ha
}

//Init initialize HOTAdd
func (ha *HOTAdd) Init() {
	if ha.URL == "" {
		ha.URL = HOTURL + `tuika/index.html`
		ha.TargetRegexp = HOTURL + `tuika/data/[0-9]{4}/[0-9]{8}.txt`
		ha.Dirname = `hot`
	}
}

//HOTDel site
type HOTDel struct {
	Site
	Initer
}

//NewHOTDel create a new HOTDel
func NewHOTDel() *HOTDel {
	hd := &HOTDel{}
	hd.Init()
	return hd
}

//Init initialize HOTDel
func (hd *HOTDel) Init() {
	if hd.URL == "" {
		hd.URL = HOTURL
		hd.TargetRegexp = HOTURL + `moto_data/h[0-9]{8}del.zip`
		hd.Dirname = `hot`
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"github.com/hashicorp/go-cleanhttp"

	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
	"github.com/aws/aws-sdk-go/aws/defaults"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"

	alks "github.com/Cox-Automotive/alks-go"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/sts"
)

// Version number, to be injected at link time
// to set, add `-ldflags "-X main.versionNumber=1.2.3"` to the go build command
var versionNumber string

// Config stores ALKS configuration and credentials
type Config struct {
	URL           string
	AccessKey     string
	SecretKey     string
	Token         string
	CredsFilename string
	Profile       string
	AssumeRole    assumeRoleDetails
}

type assumeRoleDetails struct {
	RoleARN     string
	SessionName string
	ExternalID  string
	Policy      string
}

func getCredentials(c *Config) *credentials.Credentials {
	// Follow the  same priority as the AWS Terraform Provider
	// https://www.terraform.io/docs/providers/aws/#authentication

	// needed for the EC2MetaData service
	sess := session.Must(session.NewSession())

	providers := []credentials.Provider{
		&credentials.StaticProvider{Value: credentials.Value{
			AccessKeyID:     c.AccessKey,
			SecretAccessKey: c.SecretKey,
			SessionToken:    c.Token,
		}},
		&credentials.EnvProvider{},
		&credentials.SharedCredentialsProvider{
			Filename: c.CredsFilename,
			Profile:  c.Profile,
		},
		&ec2rolecreds.EC2RoleProvider{
			Client: ec2metadata.New(sess),
		},
	}

	// Check for ECS container, for more details see:
	// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html
	if uri := os.Getenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"); len(uri) > 0 {
		client := cleanhttp.DefaultClient()
		client.Timeout = 100 * time.Millisecond
		cfg := &aws.Config{
			HTTPClient: client,
		}

		providers = append(providers, defaults.RemoteCredProvider(*cfg, defaults.Handlers()))
	}

	return credentials.NewChainCredentials(providers)
}

// Client returns a properly configured ALKS client or an appropriate error if initialization fails
func (c *Config) Client() (*alks.Client, error) {
	log.Println("[DEBUG] Validting STS credentials")

	// lookup credentials
	creds := getCredentials(c)
	cp, cpErr := creds.Get()

	// validate we have credentials
	if cpErr != nil {
		return nil, errors.New(`No valid credential sources found for ALKS Provider.
Please see https://github.com/Cox-Automotive/terraform-provider-alks#authentication for more information on
providing credentials for the ALKS Provider`)
	}

	// create a new session to test credentails
	sess, err := session.NewSession(&aws.Config{
		Region:      aws.String("us-east-1"),
		Credentials: creds,
	})

	// validate session
	if err != nil {
		return nil, fmt.Errorf("Error creating session from STS. (%v)", err)
	}

	var stsconn *sts.STS
	// we need to assume another role before creating an ALKS client
	if c.AssumeRole.RoleARN != "" {
		arCreds := stscreds.NewCredentials(sess, c.AssumeRole.RoleARN, func(p *stscreds.AssumeRoleProvider) {
			if c.AssumeRole.SessionName != "" {
				p.RoleSessionName = c.AssumeRole.SessionName
			}

			if c.AssumeRole.ExternalID != "" {
				p.ExternalID = &c.AssumeRole.ExternalID
			}

			if c.AssumeRole.Policy != "" {
				p.Policy = &c.AssumeRole.Policy
			}
		})

		cp, cpErr = arCreds.Get()
		if cpErr != nil {
			return nil, fmt.Errorf("The role %q cannot be assumed. Please verify the role ARN, role policies and your base AWS credentials", c.AssumeRole.RoleARN)
		}

		stsconn = sts.New(sess, &aws.Config{
			Region:      aws.String("us-east-1"),
			Credentials: arCreds,
		})
	} else {
		stsconn = sts.New(sess)
	}

	// make a basic api call to test creds are valid
	cident, serr := stsconn.GetCallerIdentity(&sts.GetCallerIdentityInput{})

	// check for valid creds
	if serr != nil {
		return nil, serr
	}

	// check if the user is using a assume-role IAM admin session
	if isValidIAM(cident.Arn) != true {
		return nil, errors.New("Looks like you are not using ALKS IAM credentials. This will result in errors when creating roles. \n " +
			"Note: If using ALKS CLI to get credentials, be sure to use the '-i' flag. \n Please see https://coxautoinc.sharepoint.com/sites/service-internal-tools-team/SitePages/ALKS-Terraform-Provider---Troubleshooting.aspx for more information.")
	}

	// got good creds, create alks sts client
	client, err := alks.NewSTSClient(c.URL, cp.AccessKeyID, cp.SecretAccessKey, cp.SessionToken)

	if err != nil {
		return nil, err
	}

	client.SetUserAgent(fmt.Sprintf("alks-terraform-provider-%s", getPluginVersion()))

	log.Println("[INFO] ALKS Client configured")

	return client, nil
}

func getPluginVersion() string {
	if versionNumber != "" {
		return versionNumber
	}

	return "unknown"
}

func isValidIAM(arn *string) bool {

	/*
		Validates ARN for assumed-role of:
			- Admin
			- IAMAdmin
			-Machine Identities.
	*/
	if strings.Contains(*arn, "assumed-role/Admin/") || strings.Contains(*arn, "assumed-role/IAMAdmin/") || strings.Contains(*arn, "arn:aws:sts::") {
		return true
	}

	return false
}
<commit_msg>Call endpoint to validate MI from interactive user (ALKS user).<commit_after>package main

import (
	"errors"
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"github.com/hashicorp/go-cleanhttp"

	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
	"github.com/aws/aws-sdk-go/aws/defaults"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"

	alks "github.com/Cox-Automotive/alks-go"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/sts"
)

// Version number, to be injected at link time
// to set, add `-ldflags "-X main.versionNumber=1.2.3"` to the go build command
var versionNumber string

// Config stores ALKS configuration and credentials
type Config struct {
	URL           string
	AccessKey     string
	SecretKey     string
	Token         string
	CredsFilename string
	Profile       string
	AssumeRole    assumeRoleDetails
}

type assumeRoleDetails struct {
	RoleARN     string
	SessionName string
	ExternalID  string
	Policy      string
}

func getCredentials(c *Config) *credentials.Credentials {
	// Follow the  same priority as the AWS Terraform Provider
	// https://www.terraform.io/docs/providers/aws/#authentication

	// needed for the EC2MetaData service
	sess := session.Must(session.NewSession())

	providers := []credentials.Provider{
		&credentials.StaticProvider{Value: credentials.Value{
			AccessKeyID:     c.AccessKey,
			SecretAccessKey: c.SecretKey,
			SessionToken:    c.Token,
		}},
		&credentials.EnvProvider{},
		&credentials.SharedCredentialsProvider{
			Filename: c.CredsFilename,
			Profile:  c.Profile,
		},
		&ec2rolecreds.EC2RoleProvider{
			Client: ec2metadata.New(sess),
		},
	}

	// Check for ECS container, for more details see:
	// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html
	if uri := os.Getenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"); len(uri) > 0 {
		client := cleanhttp.DefaultClient()
		client.Timeout = 100 * time.Millisecond
		cfg := &aws.Config{
			HTTPClient: client,
		}

		providers = append(providers, defaults.RemoteCredProvider(*cfg, defaults.Handlers()))
	}

	return credentials.NewChainCredentials(providers)
}

// Client returns a properly configured ALKS client or an appropriate error if initialization fails
func (c *Config) Client() (*alks.Client, error) {
	log.Println("[DEBUG] Validting STS credentials")

	// lookup credentials
	creds := getCredentials(c)
	cp, cpErr := creds.Get()

	// validate we have credentials
	if cpErr != nil {
		return nil, errors.New(`No valid credential sources found for ALKS Provider.
Please see https://github.com/Cox-Automotive/terraform-provider-alks#authentication for more information on
providing credentials for the ALKS Provider`)
	}

	// create a new session to test credentails
	sess, err := session.NewSession(&aws.Config{
		Region:      aws.String("us-east-1"),
		Credentials: creds,
	})

	// validate session
	if err != nil {
		return nil, fmt.Errorf("Error creating session from STS. (%v)", err)
	}

	var stsconn *sts.STS
	// we need to assume another role before creating an ALKS client
	if c.AssumeRole.RoleARN != "" {
		arCreds := stscreds.NewCredentials(sess, c.AssumeRole.RoleARN, func(p *stscreds.AssumeRoleProvider) {
			if c.AssumeRole.SessionName != "" {
				p.RoleSessionName = c.AssumeRole.SessionName
			}

			if c.AssumeRole.ExternalID != "" {
				p.ExternalID = &c.AssumeRole.ExternalID
			}

			if c.AssumeRole.Policy != "" {
				p.Policy = &c.AssumeRole.Policy
			}
		})

		cp, cpErr = arCreds.Get()
		if cpErr != nil {
			return nil, fmt.Errorf("The role %q cannot be assumed. Please verify the role ARN, role policies and your base AWS credentials", c.AssumeRole.RoleARN)
		}

		stsconn = sts.New(sess, &aws.Config{
			Region:      aws.String("us-east-1"),
			Credentials: arCreds,
		})
	} else {
		stsconn = sts.New(sess)
	}

	// make a basic api call to test creds are valid
	cident, serr := stsconn.GetCallerIdentity(&sts.GetCallerIdentityInput{})

	// check for valid creds
	if serr != nil {
		return nil, serr
	}

	// got good creds, create alks sts client
	client, err := alks.NewSTSClient(c.URL, cp.AccessKeyID, cp.SecretAccessKey, cp.SessionToken)

	// check if the user is using a assume-role IAM admin session
	if isValidIAM(cident.Arn, client) != true {
		return nil, errors.New("Looks like you are not using ALKS IAM credentials. This will result in errors when creating roles. \n " +
			"Note: If using ALKS CLI to get credentials, be sure to use the '-i' flag. \n Please see https://coxautoinc.sharepoint.com/sites/service-internal-tools-team/SitePages/ALKS-Terraform-Provider---Troubleshooting.aspx for more information.")
	}

	if err != nil {
		return nil, err
	}

	client.SetUserAgent(fmt.Sprintf("alks-terraform-provider-%s", getPluginVersion()))

	log.Println("[INFO] ALKS Client configured")

	return client, nil
}

func getPluginVersion() string {
	if versionNumber != "" {
		return versionNumber
	}

	return "unknown"
}

func isValidIAM(arn *string, client *alks.Client) bool {

	/*
		Validates ARN for assumed-role of:
			- Admin
			- IAMAdmin
			- Machine Identities.
	*/
	if strings.Contains(*arn, "assumed-role/Admin/") || strings.Contains(*arn, "assumed-role/IAMAdmin/") {
		return true
	} else if strings.Contains(*arn, "arn:aws:sts::") {
		// Check if MI...
		_, err := client.SearchRoleMachineIdentity(*arn)
		if err != nil {
			return false
		}
		return true
	}

	return false
}
<|endoftext|>