<commit_before>package memberlist

import (
	"io"
	"log"
	"os"
	"time"
)

type Config struct {
	// The name of this node. This must be unique in the cluster.
	Name string

	// Configuration related to what address to bind to and ports to
	// listen on. The port is used for both UDP and TCP gossip.
	// It is assumed other nodes are running on this port, but they
	// do not need to.
	BindAddr string
	BindPort int

	// Configuration related to what address to advertise to other
	// cluster members. Used for nat traversal.
	AdvertiseAddr string
	AdvertisePort int

	// ProtocolVersion is the configured protocol version that we
	// will _speak_. This must be between ProtocolVersionMin and
	// ProtocolVersionMax.
	ProtocolVersion uint8

	// TCPTimeout is the timeout for establishing a TCP connection with
	// a remote node for a full state sync.
	TCPTimeout time.Duration

	// IndirectChecks is the number of nodes that will be asked to perform
	// an indirect probe of a node in the case a direct probe fails. Memberlist
	// waits for an ack from any single indirect node, so increasing this
	// number will increase the likelihood that an indirect probe will succeed
	// at the expense of bandwidth.
	IndirectChecks int

	// RetransmitMult is the multiplier for the number of retransmissions
	// that are attempted for messages broadcasted over gossip. The actual
	// count of retransmissions is calculated using the formula:
	//
	//   Retransmits = RetransmitMult * log(N+1)
	//
	// This allows the retransmits to scale properly with cluster size. The
	// higher the multiplier, the more likely a failed broadcast is to converge
	// at the expense of increased bandwidth.
	RetransmitMult int

	// SuspicionMult is the multiplier for determining the time an
	// inaccessible node is considered suspect before declaring it dead.
	// The actual timeout is calculated using the formula:
	//
	//   SuspicionTimeout = SuspicionMult * log(N+1) * ProbeInterval
	//
	// This allows the timeout to scale properly with expected propagation
	// delay with a larger cluster size. The higher the multiplier, the longer
	// an inaccessible node is considered part of the cluster before declaring
	// it dead, giving that suspect node more time to refute if it is indeed
	// still alive.
	SuspicionMult int

	// SuspicionMaxTimeoutMult is the multiplier applied to the
	// SuspicionTimeout used as an upper bound on detection time. This max
	// timeout is calculated using the formula:
	//
	// SuspicionMaxTimeout = SuspicionMaxTimeoutMult * SuspicionTimeout
	//
	// If everything is working properly, confirmations from other nodes will
	// accelerate suspicion timers in a manner which will cause the timeout
	// to reach the base SuspicionTimeout before that elapses, so this value
	// will typically only come into play if a node is experiencing issues
	// communicating with other nodes. It should be set to a something fairly
	// large so that a node having problems will have a lot of chances to
	// recover before falsely declaring other nodes as failed, but short
	// enough for a legitimately isolated node to still make progress marking
	// nodes failed in a reasonable amount of time.
	SuspicionMaxTimeoutMult int

	// PushPullInterval is the interval between complete state syncs.
	// Complete state syncs are done with a single node over TCP and are
	// quite expensive relative to standard gossiped messages. Setting this
	// to zero will disable state push/pull syncs completely.
	//
	// Setting this interval lower (more frequent) will increase convergence
	// speeds across larger clusters at the expense of increased bandwidth
	// usage.
	PushPullInterval time.Duration

	// ProbeInterval and ProbeTimeout are used to configure probing
	// behavior for memberlist.
	//
	// ProbeInterval is the interval between random node probes. Setting
	// this lower (more frequent) will cause the memberlist cluster to detect
	// failed nodes more quickly at the expense of increased bandwidth usage.
	//
	// ProbeTimeout is the timeout to wait for an ack from a probed node
	// before assuming it is unhealthy. This should be set to 99-percentile
	// of RTT (round-trip time) on your network.
	ProbeInterval time.Duration
	ProbeTimeout  time.Duration

	// DisableTcpPings will turn off the fallback TCP pings that are attempted
	// if the direct UDP ping fails. These get pipelined along with the
	// indirect UDP pings.
	DisableTcpPings bool

	// AwarenessMaxMultiplier will increase the probe interval if the node
	// becomes aware that it might be degraded and not meeting the soft real
	// time requirements to reliably probe other nodes.
	AwarenessMaxMultiplier int

	// GossipInterval and GossipNodes are used to configure the gossip
	// behavior of memberlist.
	//
	// GossipInterval is the interval between sending messages that need
	// to be gossiped that haven't been able to piggyback on probing messages.
	// If this is set to zero, non-piggyback gossip is disabled. By lowering
	// this value (more frequent) gossip messages are propagated across
	// the cluster more quickly at the expense of increased bandwidth.
	//
	// GossipNodes is the number of random nodes to send gossip messages to
	// per GossipInterval. Increasing this number causes the gossip messages
	// to propagate across the cluster more quickly at the expense of
	// increased bandwidth.
	GossipInterval time.Duration
	GossipNodes    int

	// EnableCompression is used to control message compression. This can
	// be used to reduce bandwidth usage at the cost of slightly more CPU
	// utilization. This is only available starting at protocol version 1.
	EnableCompression bool

	// SecretKey is used to initialize the primary encryption key in a keyring.
	// The primary encryption key is the only key used to encrypt messages and
	// the first key used while attempting to decrypt messages. Providing a
	// value for this primary key will enable message-level encryption and
	// verification, and automatically install the key onto the keyring.
	// The value should be either 16, 24, or 32 bytes to select AES-128,
	// AES-192, or AES-256.
	SecretKey []byte

	// The keyring holds all of the encryption keys used internally. It is
	// automatically initialized using the SecretKey and SecretKeys values.
	Keyring *Keyring

	// Delegate and Events are delegates for receiving and providing
	// data to memberlist via callback mechanisms. For Delegate, see
	// the Delegate interface. For Events, see the EventDelegate interface.
	//
	// The DelegateProtocolMin/Max are used to guarantee protocol-compatibility
	// for any custom messages that the delegate might do (broadcasts,
	// local/remote state, etc.). If you don't set these, then the protocol
	// versions will just be zero, and version compliance won't be done.
	Delegate                Delegate
	DelegateProtocolVersion uint8
	DelegateProtocolMin     uint8
	DelegateProtocolMax     uint8
	Events                  EventDelegate
	Conflict                ConflictDelegate
	Merge                   MergeDelegate
	Ping                    PingDelegate
	Alive                   AliveDelegate

	// DNSConfigPath points to the system's DNS config file, usually located
	// at /etc/resolv.conf. It can be overridden via config for easier testing.
	DNSConfigPath string

	// LogOutput is the writer where logs should be sent. If this is not
	// set, logging will go to stderr by default. You cannot specify both LogOutput
	// and Logger at the same time.
	LogOutput io.Writer

	// Logger is a custom logger which you provide. If Logger is set, it will use
	// this for the internal logger. If Logger is not set, it will fall back to the
	// behavior for using LogOutput. You cannot specify both LogOutput and Logger
	// at the same time.
	Logger *log.Logger

	// Size of Memberlist's internal channel which handles UDP messages. The
	// size of this determines the size of the queue which Memberlist will keep
	// while UDP messages are handled.
	HandoffQueueDepth int
}

// DefaultLANConfig returns a sane set of configurations for Memberlist.
// It uses the hostname as the node name, and otherwise sets very conservative
// values that are sane for most LAN environments. The default configuration
// errs on the side of caution, choosing values that are optimized
// for higher convergence at the cost of higher bandwidth usage. Regardless,
// these values are a good starting point when getting started with memberlist.
func DefaultLANConfig() *Config {
	hostname, _ := os.Hostname()
	return &Config{
		Name:                    hostname,
		BindAddr:                "0.0.0.0",
		BindPort:                7946,
		AdvertiseAddr:           "",
		AdvertisePort:           7946,
		ProtocolVersion:         ProtocolVersion2Compatible,
		TCPTimeout:              10 * time.Second,       // Timeout after 10 seconds
		IndirectChecks:          3,                      // Use 3 nodes for the indirect ping
		RetransmitMult:          4,                      // Retransmit a message 4 * log(N+1) nodes
		SuspicionMult:           5,                      // Suspect a node for 5 * log(N+1) * Interval
		SuspicionMaxTimeoutMult: 6,                      // For 10k nodes this will give a max timeout of 120 seconds
		PushPullInterval:        30 * time.Second,       // Low frequency
		ProbeTimeout:            500 * time.Millisecond, // Reasonable RTT time for LAN
		ProbeInterval:           1 * time.Second,        // Failure check every second
		DisableTcpPings:         false,                  // TCP pings are safe, even with mixed versions
		AwarenessMaxMultiplier:  8,                      // Probe interval backs off to 8 seconds

		GossipNodes:    3,                      // Gossip to 3 nodes
		GossipInterval: 200 * time.Millisecond, // Gossip more rapidly

		EnableCompression: true, // Enable compression by default

		SecretKey: nil,
		Keyring:   nil,

		DNSConfigPath: "/etc/resolv.conf",

		HandoffQueueDepth: 1024,
	}
}

// DefaultWANConfig works like DefaultConfig, however it returns a configuration
// that is optimized for most WAN environments. The default configuration is
// still very conservative and errs on the side of caution.
func DefaultWANConfig() *Config {
	conf := DefaultLANConfig()
	conf.TCPTimeout = 30 * time.Second
	conf.SuspicionMult = 6
	conf.PushPullInterval = 60 * time.Second
	conf.ProbeTimeout = 3 * time.Second
	conf.ProbeInterval = 5 * time.Second
	conf.GossipNodes = 4 // Gossip less frequently, but to an additional node
	conf.GossipInterval = 500 * time.Millisecond
	conf.HandoffQueueDepth = 1024
	return conf
}

// DefaultLocalConfig works like DefaultConfig, however it returns a configuration
// that is optimized for a local loopback environments. The default configuration is
// still very conservative and errs on the side of caution.
func DefaultLocalConfig() *Config {
	conf := DefaultLANConfig()
	conf.TCPTimeout = time.Second
	conf.IndirectChecks = 1
	conf.RetransmitMult = 2
	conf.SuspicionMult = 3
	conf.PushPullInterval = 15 * time.Second
	conf.ProbeTimeout = 200 * time.Millisecond
	conf.ProbeInterval = time.Second
	conf.GossipInterval = 100 * time.Millisecond
	conf.HandoffQueueDepth = 1024
	return conf
}

// Returns whether or not encryption is enabled
func (c *Config) EncryptionEnabled() bool {
	return c.Keyring != nil && len(c.Keyring.GetKeys()) > 0
}
<commit_msg>Remove unnecessary initialization of HandoffQueueDepth.<commit_after>package memberlist

import (
	"io"
	"log"
	"os"
	"time"
)

type Config struct {
	// The name of this node. This must be unique in the cluster.
	Name string

	// Configuration related to what address to bind to and ports to
	// listen on. The port is used for both UDP and TCP gossip.
	// It is assumed other nodes are running on this port, but they
	// do not need to.
	BindAddr string
	BindPort int

	// Configuration related to what address to advertise to other
	// cluster members. Used for nat traversal.
	AdvertiseAddr string
	AdvertisePort int

	// ProtocolVersion is the configured protocol version that we
	// will _speak_. This must be between ProtocolVersionMin and
	// ProtocolVersionMax.
	ProtocolVersion uint8

	// TCPTimeout is the timeout for establishing a TCP connection with
	// a remote node for a full state sync.
	TCPTimeout time.Duration

	// IndirectChecks is the number of nodes that will be asked to perform
	// an indirect probe of a node in the case a direct probe fails. Memberlist
	// waits for an ack from any single indirect node, so increasing this
	// number will increase the likelihood that an indirect probe will succeed
	// at the expense of bandwidth.
	IndirectChecks int

	// RetransmitMult is the multiplier for the number of retransmissions
	// that are attempted for messages broadcasted over gossip. The actual
	// count of retransmissions is calculated using the formula:
	//
	//   Retransmits = RetransmitMult * log(N+1)
	//
	// This allows the retransmits to scale properly with cluster size. The
	// higher the multiplier, the more likely a failed broadcast is to converge
	// at the expense of increased bandwidth.
	RetransmitMult int

	// SuspicionMult is the multiplier for determining the time an
	// inaccessible node is considered suspect before declaring it dead.
	// The actual timeout is calculated using the formula:
	//
	//   SuspicionTimeout = SuspicionMult * log(N+1) * ProbeInterval
	//
	// This allows the timeout to scale properly with expected propagation
	// delay with a larger cluster size. The higher the multiplier, the longer
	// an inaccessible node is considered part of the cluster before declaring
	// it dead, giving that suspect node more time to refute if it is indeed
	// still alive.
	SuspicionMult int

	// SuspicionMaxTimeoutMult is the multiplier applied to the
	// SuspicionTimeout used as an upper bound on detection time. This max
	// timeout is calculated using the formula:
	//
	// SuspicionMaxTimeout = SuspicionMaxTimeoutMult * SuspicionTimeout
	//
	// If everything is working properly, confirmations from other nodes will
	// accelerate suspicion timers in a manner which will cause the timeout
	// to reach the base SuspicionTimeout before that elapses, so this value
	// will typically only come into play if a node is experiencing issues
	// communicating with other nodes. It should be set to a something fairly
	// large so that a node having problems will have a lot of chances to
	// recover before falsely declaring other nodes as failed, but short
	// enough for a legitimately isolated node to still make progress marking
	// nodes failed in a reasonable amount of time.
	SuspicionMaxTimeoutMult int

	// PushPullInterval is the interval between complete state syncs.
	// Complete state syncs are done with a single node over TCP and are
	// quite expensive relative to standard gossiped messages. Setting this
	// to zero will disable state push/pull syncs completely.
	//
	// Setting this interval lower (more frequent) will increase convergence
	// speeds across larger clusters at the expense of increased bandwidth
	// usage.
	PushPullInterval time.Duration

	// ProbeInterval and ProbeTimeout are used to configure probing
	// behavior for memberlist.
	//
	// ProbeInterval is the interval between random node probes. Setting
	// this lower (more frequent) will cause the memberlist cluster to detect
	// failed nodes more quickly at the expense of increased bandwidth usage.
	//
	// ProbeTimeout is the timeout to wait for an ack from a probed node
	// before assuming it is unhealthy. This should be set to 99-percentile
	// of RTT (round-trip time) on your network.
	ProbeInterval time.Duration
	ProbeTimeout  time.Duration

	// DisableTcpPings will turn off the fallback TCP pings that are attempted
	// if the direct UDP ping fails. These get pipelined along with the
	// indirect UDP pings.
	DisableTcpPings bool

	// AwarenessMaxMultiplier will increase the probe interval if the node
	// becomes aware that it might be degraded and not meeting the soft real
	// time requirements to reliably probe other nodes.
	AwarenessMaxMultiplier int

	// GossipInterval and GossipNodes are used to configure the gossip
	// behavior of memberlist.
	//
	// GossipInterval is the interval between sending messages that need
	// to be gossiped that haven't been able to piggyback on probing messages.
	// If this is set to zero, non-piggyback gossip is disabled. By lowering
	// this value (more frequent) gossip messages are propagated across
	// the cluster more quickly at the expense of increased bandwidth.
	//
	// GossipNodes is the number of random nodes to send gossip messages to
	// per GossipInterval. Increasing this number causes the gossip messages
	// to propagate across the cluster more quickly at the expense of
	// increased bandwidth.
	GossipInterval time.Duration
	GossipNodes    int

	// EnableCompression is used to control message compression. This can
	// be used to reduce bandwidth usage at the cost of slightly more CPU
	// utilization. This is only available starting at protocol version 1.
	EnableCompression bool

	// SecretKey is used to initialize the primary encryption key in a keyring.
	// The primary encryption key is the only key used to encrypt messages and
	// the first key used while attempting to decrypt messages. Providing a
	// value for this primary key will enable message-level encryption and
	// verification, and automatically install the key onto the keyring.
	// The value should be either 16, 24, or 32 bytes to select AES-128,
	// AES-192, or AES-256.
	SecretKey []byte

	// The keyring holds all of the encryption keys used internally. It is
	// automatically initialized using the SecretKey and SecretKeys values.
	Keyring *Keyring

	// Delegate and Events are delegates for receiving and providing
	// data to memberlist via callback mechanisms. For Delegate, see
	// the Delegate interface. For Events, see the EventDelegate interface.
	//
	// The DelegateProtocolMin/Max are used to guarantee protocol-compatibility
	// for any custom messages that the delegate might do (broadcasts,
	// local/remote state, etc.). If you don't set these, then the protocol
	// versions will just be zero, and version compliance won't be done.
	Delegate                Delegate
	DelegateProtocolVersion uint8
	DelegateProtocolMin     uint8
	DelegateProtocolMax     uint8
	Events                  EventDelegate
	Conflict                ConflictDelegate
	Merge                   MergeDelegate
	Ping                    PingDelegate
	Alive                   AliveDelegate

	// DNSConfigPath points to the system's DNS config file, usually located
	// at /etc/resolv.conf. It can be overridden via config for easier testing.
	DNSConfigPath string

	// LogOutput is the writer where logs should be sent. If this is not
	// set, logging will go to stderr by default. You cannot specify both LogOutput
	// and Logger at the same time.
	LogOutput io.Writer

	// Logger is a custom logger which you provide. If Logger is set, it will use
	// this for the internal logger. If Logger is not set, it will fall back to the
	// behavior for using LogOutput. You cannot specify both LogOutput and Logger
	// at the same time.
	Logger *log.Logger

	// Size of Memberlist's internal channel which handles UDP messages. The
	// size of this determines the size of the queue which Memberlist will keep
	// while UDP messages are handled.
	HandoffQueueDepth int
}

// DefaultLANConfig returns a sane set of configurations for Memberlist.
// It uses the hostname as the node name, and otherwise sets very conservative
// values that are sane for most LAN environments. The default configuration
// errs on the side of caution, choosing values that are optimized
// for higher convergence at the cost of higher bandwidth usage. Regardless,
// these values are a good starting point when getting started with memberlist.
func DefaultLANConfig() *Config {
	hostname, _ := os.Hostname()
	return &Config{
		Name:                    hostname,
		BindAddr:                "0.0.0.0",
		BindPort:                7946,
		AdvertiseAddr:           "",
		AdvertisePort:           7946,
		ProtocolVersion:         ProtocolVersion2Compatible,
		TCPTimeout:              10 * time.Second,       // Timeout after 10 seconds
		IndirectChecks:          3,                      // Use 3 nodes for the indirect ping
		RetransmitMult:          4,                      // Retransmit a message 4 * log(N+1) nodes
		SuspicionMult:           5,                      // Suspect a node for 5 * log(N+1) * Interval
		SuspicionMaxTimeoutMult: 6,                      // For 10k nodes this will give a max timeout of 120 seconds
		PushPullInterval:        30 * time.Second,       // Low frequency
		ProbeTimeout:            500 * time.Millisecond, // Reasonable RTT time for LAN
		ProbeInterval:           1 * time.Second,        // Failure check every second
		DisableTcpPings:         false,                  // TCP pings are safe, even with mixed versions
		AwarenessMaxMultiplier:  8,                      // Probe interval backs off to 8 seconds

		GossipNodes:    3,                      // Gossip to 3 nodes
		GossipInterval: 200 * time.Millisecond, // Gossip more rapidly

		EnableCompression: true, // Enable compression by default

		SecretKey: nil,
		Keyring:   nil,

		DNSConfigPath: "/etc/resolv.conf",

		HandoffQueueDepth: 1024,
	}
}

// DefaultWANConfig works like DefaultConfig, however it returns a configuration
// that is optimized for most WAN environments. The default configuration is
// still very conservative and errs on the side of caution.
func DefaultWANConfig() *Config {
	conf := DefaultLANConfig()
	conf.TCPTimeout = 30 * time.Second
	conf.SuspicionMult = 6
	conf.PushPullInterval = 60 * time.Second
	conf.ProbeTimeout = 3 * time.Second
	conf.ProbeInterval = 5 * time.Second
	conf.GossipNodes = 4 // Gossip less frequently, but to an additional node
	conf.GossipInterval = 500 * time.Millisecond
	return conf
}

// DefaultLocalConfig works like DefaultConfig, however it returns a configuration
// that is optimized for a local loopback environments. The default configuration is
// still very conservative and errs on the side of caution.
func DefaultLocalConfig() *Config {
	conf := DefaultLANConfig()
	conf.TCPTimeout = time.Second
	conf.IndirectChecks = 1
	conf.RetransmitMult = 2
	conf.SuspicionMult = 3
	conf.PushPullInterval = 15 * time.Second
	conf.ProbeTimeout = 200 * time.Millisecond
	conf.ProbeInterval = time.Second
	conf.GossipInterval = 100 * time.Millisecond
	return conf
}

// Returns whether or not encryption is enabled
func (c *Config) EncryptionEnabled() bool {
	return c.Keyring != nil && len(c.Keyring.GetKeys()) > 0
}
<|endoftext|>


<commit_before>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package log

import (
	"context"
	"os"
	"sync"
	"sync/atomic"
)

var (
	defaultLogger    atomicLogger
	setDefaultLogger sync.Once

	fallback = New(os.Stderr, "", LstdFlags, nil)
)

func DefaultLogger() Logger {
	return &defaultLogger
}

func SetDefaultLogger(l Logger) {
	ok := false
	setDefaultLogger.Do(func() {
		if l == nil {
			panic("log.SetDefaultLogger(nil)")
		}
		defaultLogger.out.Store(l)
		ok = true
	})
	if !ok {
		panic("log.SetDefaultLogger called more than once")
	}
}

type atomicLogger struct {
	out atomic.Value
}

func (l *atomicLogger) Log(ctx context.Context, ent Entry) {
	l.logger().Log(ctx, ent)
}

func (l *atomicLogger) LogEnabled(ent Entry) bool {
	return l.logger().LogEnabled(ent)
}

func (l *atomicLogger) logger() Logger {
	out := l.out.Load().(Logger)
	if out == nil {
		return fallback
	}
	return out
}
<commit_msg>config: fix type assert on nil<commit_after>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package log

import (
	"context"
	"os"
	"sync"
	"sync/atomic"
)

var (
	defaultLogger    atomicLogger
	setDefaultLogger sync.Once

	fallback = New(os.Stderr, "", LstdFlags, nil)
)

func DefaultLogger() Logger {
	return &defaultLogger
}

func SetDefaultLogger(l Logger) {
	ok := false
	setDefaultLogger.Do(func() {
		if l == nil {
			panic("log.SetDefaultLogger(nil)")
		}
		defaultLogger.out.Store(l)
		ok = true
	})
	if !ok {
		panic("log.SetDefaultLogger called more than once")
	}
}

type atomicLogger struct {
	out atomic.Value
}

func (l *atomicLogger) Log(ctx context.Context, ent Entry) {
	l.logger().Log(ctx, ent)
}

func (l *atomicLogger) LogEnabled(ent Entry) bool {
	return l.logger().LogEnabled(ent)
}

func (l *atomicLogger) logger() Logger {
	out := l.out.Load()
	if out == nil {
		return fallback
	}
	return out.(Logger)
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"encoding/json"
	"net"
	"fmt"
	"log"
)

// load json configuration file into memory
func loadConfig(forceDebugMode bool) Configuration {
	file, _ := os.Open("config.json")
	decoder := json.NewDecoder(file)
	config  := Configuration{}
	err 	:= decoder.Decode(&config)
	log.Fatalf("loadConfigError: %s", err)
	panic(fmt.Sprintf("loadConfigError: %s", err))

	if config.Rabbit.ReconnectWaitTimeSec < 1 {
		config.Rabbit.ReconnectWaitTimeSec = 1
	}

	if forceDebugMode {
		config.DebugMode = true
	}

	return config
}
func checkAndCreateDirectory(dirpath string) {
	if _, err := os.Stat(dirpath); os.IsNotExist(err) {
		err := os.MkdirAll(dirpath, os.FileMode(int(0777)))
		if err != nil {
			failOnError(err, "Directory creation error, while creating : " + dirpath)
		}
	}
}
func checkSystem(config Configuration) {
	// Check if sendmail exists
	if(config.SendMailPath != "") {
		if _, err := os.Stat(config.SendMailPath); os.IsNotExist(err) {
			log.Fatalf("Incorrect sendmail path in config: %s", err)
			panic(fmt.Sprintf("Incorrect sendmail path in config: %s", err))
		}
	}

	port := config.SingularityPort
	ln, err := net.Listen("tcp", ":" + port)
	if err != nil {
		// Exit if port is already occupied
		failOnError(err, "Another process is already using port " + port + " - " + err.Error())
	}
	go ln.Accept()

	// Check if log directory exists, if not create it, In case of error fail
	checkAndCreateDirectory(config.Logging.GcmErr.RootPath)
	if config.Logging.GcmErr.LogSuccess == true {
		checkAndCreateDirectory(config.Logging.GcmErr.SuccessPath)
	}

	checkAndCreateDirectory(config.Logging.ApnErr.RootPath)
	if config.Logging.ApnErr.LogSuccess == true {
		checkAndCreateDirectory(config.Logging.ApnErr.SuccessPath)
	}
	checkAndCreateDirectory(config.Logging.DbErr.RootPath)

	// TODO: Check if app error file can be created, if not exit
	// Check if PEM files needed for APN exists
	for i:=0;i<len(config.ApnQueues);i++ {
		_, err := os.Stat(config.ApnQueues[i].PemPath)
		if err != nil {
			failOnError(err, "Specified PEM file does not exists : " + config.ApnQueues[i].PemPath)
		}
	}

	// TODO: Names of all the queues must be different

	if len(config.GcmQueues) < 1 {
		log.Fatalf("Config: Queues not found")
		panic(fmt.Sprintf("Config: Queues not found"))
	}
}

<commit_msg>Load Config Error Fix<commit_after>package main

import (
	"os"
	"encoding/json"
	"net"
	"fmt"
	"log"
)

// load json configuration file into memory
func loadConfig(forceDebugMode bool) Configuration {
	file, _ := os.Open("config.json")
	decoder := json.NewDecoder(file)
	config  := Configuration{}
	err 	:= decoder.Decode(&config)
	if err != nil {
		log.Fatalf("loadConfigError: %s", err)
		panic(fmt.Sprintf("loadConfigError: %s", err))
	}

	if config.Rabbit.ReconnectWaitTimeSec < 1 {
		config.Rabbit.ReconnectWaitTimeSec = 1
	}

	if forceDebugMode {
		config.DebugMode = true
	}

	return config
}
func checkAndCreateDirectory(dirpath string) {
	if _, err := os.Stat(dirpath); os.IsNotExist(err) {
		err := os.MkdirAll(dirpath, os.FileMode(int(0777)))
		if err != nil {
			failOnError(err, "Directory creation error, while creating : " + dirpath)
		}
	}
}
func checkSystem(config Configuration) {
	// Check if sendmail exists
	if(config.SendMailPath != "") {
		if _, err := os.Stat(config.SendMailPath); os.IsNotExist(err) {
			log.Fatalf("Incorrect sendmail path in config: %s", err)
			panic(fmt.Sprintf("Incorrect sendmail path in config: %s", err))
		}
	}

	port := config.SingularityPort
	ln, err := net.Listen("tcp", ":" + port)
	if err != nil {
		// Exit if port is already occupied
		failOnError(err, "Another process is already using port " + port + " - " + err.Error())
	}
	go ln.Accept()

	// Check if log directory exists, if not create it, In case of error fail
	checkAndCreateDirectory(config.Logging.GcmErr.RootPath)
	if config.Logging.GcmErr.LogSuccess == true {
		checkAndCreateDirectory(config.Logging.GcmErr.SuccessPath)
	}

	checkAndCreateDirectory(config.Logging.ApnErr.RootPath)
	if config.Logging.ApnErr.LogSuccess == true {
		checkAndCreateDirectory(config.Logging.ApnErr.SuccessPath)
	}
	checkAndCreateDirectory(config.Logging.DbErr.RootPath)

	// TODO: Check if app error file can be created, if not exit
	// Check if PEM files needed for APN exists
	for i:=0;i<len(config.ApnQueues);i++ {
		_, err := os.Stat(config.ApnQueues[i].PemPath)
		if err != nil {
			failOnError(err, "Specified PEM file does not exists : " + config.ApnQueues[i].PemPath)
		}
	}

	// TODO: Names of all the queues must be different

	if len(config.GcmQueues) < 1 {
		log.Fatalf("Config: Queues not found")
		panic(fmt.Sprintf("Config: Queues not found"))
	}
}

<|endoftext|>


<commit_before>package funnel

import (
	"errors"
	"log/syslog"
	"strconv"
	"strings"

	"github.com/fsnotify/fsnotify"
	"github.com/spf13/viper"
)

// XXX: Move it to constants.go if needed
const (
	// config keys
	LoggingDirectory         = "logging.directory"
	LoggingActiveFileName    = "logging.active_file_name"
	RotationMaxLines         = "rotation.max_lines"
	RotationMaxFileSizeBytes = "rotation.max_file_size_bytes"
	FlushingTimeIntervalSecs = "flushing.time_interval_secs"
	PrependValue             = "misc.prepend_value"
	FileRenamePolicy         = "rollup.file_rename_policy"
	MaxAge                   = "rollup.max_age"
	MaxCount                 = "rollup.max_count"
	Gzip                     = "rollup.gzip"
	Target                   = "target.name"
)

var (
	// ErrInvalidFileRenamePolicy is raised for invalid values to file rename policy
	ErrInvalidFileRenamePolicy = errors.New(FileRenamePolicy + " can only be timestamp or serial")
	// ErrInvalidMaxAge is raised for invalid value in max age - life bad suffixes or no integer value at all
	ErrInvalidMaxAge = errors.New(MaxAge + " must end with either d or h and start with a number")
)

// ConfigValueError holds the error value if a config key contains
// an invalid value
type ConfigValueError struct {
	Key string
}

func (e *ConfigValueError) Error() string {
	return "Invalid config value entered for - " + e.Key
}

// Config holds all the config settings
type Config struct {
	DirName        string
	ActiveFileName string

	RotationMaxLines int
	RotationMaxBytes uint64

	FlushingTimeIntervalSecs int

	PrependValue string

	FileRenamePolicy string
	MaxAge           int64
	MaxCount         int
	Gzip             bool

	Target string
}

// GetConfig returns the config struct which is then passed
// to the consumer
func GetConfig(v *viper.Viper, logger *syslog.Writer) (*Config, chan *Config, OutputWriter, error) {
	// Set default values. They are overridden by config file values, if provided
	setDefaults(v)
	// Create a chan to signal any config reload events
	reloadChan := make(chan *Config)

	// Find and read the config file
	err := v.ReadInConfig()
	// Return the error only if config file is present
	if err != nil && v.ConfigFileUsed() != "" {
		return nil, reloadChan, nil, err
	}

	// Read from env vars
	v.AutomaticEnv()
	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))

	// Validate
	if err := validateConfig(v); err != nil {
		return nil, reloadChan, nil, err
	}

	v.WatchConfig()
	v.OnConfigChange(func(e fsnotify.Event) {
		if e.Op == fsnotify.Write {
			if err := validateConfig(v); err != nil {
				logger.Err(err.Error())
				return
			}
			reloadChan <- getConfigStruct(v)
		}
	})
	// return output writer by passing the viper instance
	outputWriter, err := GetOutputWriter(v, logger)
	if err != nil {
		return nil, reloadChan, nil, err
	}

	// return struct
	return getConfigStruct(v), reloadChan, outputWriter, nil
}

func setDefaults(v *viper.Viper) {
	v.SetDefault(LoggingDirectory, "log")
	v.SetDefault(LoggingActiveFileName, "out.log")
	v.SetDefault(RotationMaxLines, 100)
	v.SetDefault(RotationMaxFileSizeBytes, 1000000)
	v.SetDefault(FlushingTimeIntervalSecs, 5)
	v.SetDefault(PrependValue, "")
	v.SetDefault(FileRenamePolicy, "timestamp")
	v.SetDefault(MaxAge, "30d")
	v.SetDefault(MaxCount, 100)
	v.SetDefault(Gzip, false)
	v.SetDefault(Target, "file")
}

func validateConfig(v *viper.Viper) error {
	// Validate strings
	for _, key := range []string{
		LoggingDirectory,
		LoggingActiveFileName,
		PrependValue,
		FileRenamePolicy,
		MaxAge,
		Target,
	} {
		// If a string value got successfully converted to integer,
		// then its incorrect
		if _, err := strconv.Atoi(v.GetString(key)); err == nil {
			return &ConfigValueError{key}
		}

		// File rename policy has to be either timestamp or serial
		if key == FileRenamePolicy &&
			(v.GetString(key) != "timestamp" && v.GetString(key) != "serial") {
			return ErrInvalidFileRenamePolicy
		}
	}

	// Validate integers
	for _, key := range []string{
		RotationMaxLines,
		RotationMaxFileSizeBytes,
		FlushingTimeIntervalSecs,
		MaxCount,
	} {
		// If an integer value was a string, it would come as zero,
		// hence its invalid
		if v.GetInt(key) == 0 {
			return &ConfigValueError{key}
		}
	}

	// Validate MaxAge
	maxAge := v.GetString(MaxAge)
	unit := maxAge[len(maxAge)-1:]
	_, err := strconv.Atoi(maxAge[0 : len(maxAge)-1])
	if err != nil {
		return ErrInvalidMaxAge
	}

	if unit != "d" && unit != "h" {
		return ErrInvalidMaxAge
	}

	return nil
}

func getConfigStruct(v *viper.Viper) *Config {
	return &Config{
		DirName:                  v.GetString(LoggingDirectory),
		ActiveFileName:           v.GetString(LoggingActiveFileName),
		RotationMaxLines:         v.GetInt(RotationMaxLines),
		RotationMaxBytes:         uint64(v.GetInt64(RotationMaxFileSizeBytes)),
		FlushingTimeIntervalSecs: v.GetInt(FlushingTimeIntervalSecs),
		PrependValue:             v.GetString(PrependValue),
		FileRenamePolicy:         v.GetString(FileRenamePolicy),
		MaxAge:                   getMaxAgeValue(v.GetString(MaxAge)),
		MaxCount:                 v.GetInt(MaxCount),
		Gzip:                     v.GetBool(Gzip),
		Target:                   v.GetString(Target),
	}
}

func getMaxAgeValue(maxAge string) int64 {
	unit := maxAge[len(maxAge)-1:]
	magnitude, _ := strconv.Atoi(maxAge[0 : len(maxAge)-1])

	if unit == "d" {
		return int64(magnitude) * 24 * 60 * 60
	}
	return int64(magnitude) * 60 * 60
}
<commit_msg>Set defaults<commit_after>package funnel

import (
	"errors"
	"log/syslog"
	"strconv"
	"strings"

	"github.com/fsnotify/fsnotify"
	"github.com/spf13/viper"
)

// XXX: Move it to constants.go if needed
const (
	// config keys
	LoggingDirectory         = "logging.directory"
	LoggingActiveFileName    = "logging.active_file_name"
	RotationMaxLines         = "rotation.max_lines"
	RotationMaxFileSizeBytes = "rotation.max_file_size_bytes"
	FlushingTimeIntervalSecs = "flushing.time_interval_secs"
	PrependValue             = "misc.prepend_value"
	FileRenamePolicy         = "rollup.file_rename_policy"
	MaxAge                   = "rollup.max_age"
	MaxCount                 = "rollup.max_count"
	Gzip                     = "rollup.gzip"
	Target                   = "target.name"
)

var (
	// ErrInvalidFileRenamePolicy is raised for invalid values to file rename policy
	ErrInvalidFileRenamePolicy = errors.New(FileRenamePolicy + " can only be timestamp or serial")
	// ErrInvalidMaxAge is raised for invalid value in max age - life bad suffixes or no integer value at all
	ErrInvalidMaxAge = errors.New(MaxAge + " must end with either d or h and start with a number")
)

// ConfigValueError holds the error value if a config key contains
// an invalid value
type ConfigValueError struct {
	Key string
}

func (e *ConfigValueError) Error() string {
	return "Invalid config value entered for - " + e.Key
}

// Config holds all the config settings
type Config struct {
	DirName        string
	ActiveFileName string

	RotationMaxLines int
	RotationMaxBytes uint64

	FlushingTimeIntervalSecs int

	PrependValue string

	FileRenamePolicy string
	MaxAge           int64
	MaxCount         int
	Gzip             bool

	Target string
}

// GetConfig returns the config struct which is then passed
// to the consumer
func GetConfig(v *viper.Viper, logger *syslog.Writer) (*Config, chan *Config, OutputWriter, error) {
	// Set default values. They are overridden by config file values, if provided
	setDefaults(v)
	// Create a chan to signal any config reload events
	reloadChan := make(chan *Config)

	// Find and read the config file
	err := v.ReadInConfig()
	// Return the error only if config file is present
	if err != nil && v.ConfigFileUsed() != "" {
		return nil, reloadChan, nil, err
	}

	// Read from env vars
	v.AutomaticEnv()
	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))

	// Validate
	if err := validateConfig(v); err != nil {
		return nil, reloadChan, nil, err
	}

	v.WatchConfig()
	v.OnConfigChange(func(e fsnotify.Event) {
		if e.Op == fsnotify.Write {
			if err := validateConfig(v); err != nil {
				logger.Err(err.Error())
				return
			}
			reloadChan <- getConfigStruct(v)
		}
	})
	// return output writer by passing the viper instance
	outputWriter, err := GetOutputWriter(v, logger)
	if err != nil {
		return nil, reloadChan, nil, err
	}

	// return struct
	return getConfigStruct(v), reloadChan, outputWriter, nil
}

func setDefaults(v *viper.Viper) {
	v.SetDefault(LoggingDirectory, "log")
	v.SetDefault(LoggingActiveFileName, "out.log")
	v.SetDefault(RotationMaxLines, 100000)
	v.SetDefault(RotationMaxFileSizeBytes, 5000000)
	v.SetDefault(FlushingTimeIntervalSecs, 5)
	v.SetDefault(PrependValue, "")
	v.SetDefault(FileRenamePolicy, "timestamp")
	v.SetDefault(MaxAge, "30d")
	v.SetDefault(MaxCount, 100)
	v.SetDefault(Gzip, false)
	v.SetDefault(Target, "file")
}

func validateConfig(v *viper.Viper) error {
	// Validate strings
	for _, key := range []string{
		LoggingDirectory,
		LoggingActiveFileName,
		PrependValue,
		FileRenamePolicy,
		MaxAge,
		Target,
	} {
		// If a string value got successfully converted to integer,
		// then its incorrect
		if _, err := strconv.Atoi(v.GetString(key)); err == nil {
			return &ConfigValueError{key}
		}

		// File rename policy has to be either timestamp or serial
		if key == FileRenamePolicy &&
			(v.GetString(key) != "timestamp" && v.GetString(key) != "serial") {
			return ErrInvalidFileRenamePolicy
		}
	}

	// Validate integers
	for _, key := range []string{
		RotationMaxLines,
		RotationMaxFileSizeBytes,
		FlushingTimeIntervalSecs,
		MaxCount,
	} {
		// If an integer value was a string, it would come as zero,
		// hence its invalid
		if v.GetInt(key) == 0 {
			return &ConfigValueError{key}
		}
	}

	// Validate MaxAge
	maxAge := v.GetString(MaxAge)
	unit := maxAge[len(maxAge)-1:]
	_, err := strconv.Atoi(maxAge[0 : len(maxAge)-1])
	if err != nil {
		return ErrInvalidMaxAge
	}

	if unit != "d" && unit != "h" {
		return ErrInvalidMaxAge
	}

	return nil
}

func getConfigStruct(v *viper.Viper) *Config {
	return &Config{
		DirName:                  v.GetString(LoggingDirectory),
		ActiveFileName:           v.GetString(LoggingActiveFileName),
		RotationMaxLines:         v.GetInt(RotationMaxLines),
		RotationMaxBytes:         uint64(v.GetInt64(RotationMaxFileSizeBytes)),
		FlushingTimeIntervalSecs: v.GetInt(FlushingTimeIntervalSecs),
		PrependValue:             v.GetString(PrependValue),
		FileRenamePolicy:         v.GetString(FileRenamePolicy),
		MaxAge:                   getMaxAgeValue(v.GetString(MaxAge)),
		MaxCount:                 v.GetInt(MaxCount),
		Gzip:                     v.GetBool(Gzip),
		Target:                   v.GetString(Target),
	}
}

func getMaxAgeValue(maxAge string) int64 {
	unit := maxAge[len(maxAge)-1:]
	magnitude, _ := strconv.Atoi(maxAge[0 : len(maxAge)-1])

	if unit == "d" {
		return int64(magnitude) * 24 * 60 * 60
	}
	return int64(magnitude) * 60 * 60
}
<|endoftext|>


<commit_before>/* {{{ Copyright (c) Paul R. Tagliamonte <paultag@debian.org>, 2015
 *
 * 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 main

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"sync"

	"github.com/golang/glog"

	"strings"

	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/cache"
)

const (
	maxTCPPort = 65535
)

type Config struct {
	Bind       Bind
	Metrics    Metrics
	Servers    []Server
	Kubernetes *Kubernetes
	proxy      *Proxy
	lock       sync.Mutex

	serviceController cache.Controller
	serviceStore      cache.Store
	ingressController cache.Controller
	ingressStore      cache.Store
}

// Valid returns an if the config is invalid
func (c *Config) Valid() error {
	if len(c.Servers) > 0 && c.Kubernetes != nil {
		return errors.New("Cannot set .Servers and .Kubernetes in config file")
	}

	if err := c.Metrics.Valid(); err != nil {
		return err
	}

	return nil
}

type Kubernetes struct {
	Kubeconfig   string
	Client       *kubernetes.Clientset
	IngressClass string
}

// Metrics contains the port & path for the
// prometheus endpoint
type Metrics struct {
	Host string
	Port int
	Path string
}

type Bind struct {
	Host string
	Port int
}

type Server struct {
	Default bool
	Regexp  bool
	Host    string
	Names   []string
	Port    int
}

// Valid returns an error if the metrics config is invalid
func (m Metrics) Valid() error {
	if m.Port > maxTCPPort {
		return fmt.Errorf("Configured metrics port is above %d: port=%d", maxTCPPort, m.Port)
	}

	return nil
}

func LoadConfig(path string) (*Config, error) {
	glog.V(5).Infof("Loading config from: %s", path)

	fd, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	config := Config{}
	err = json.NewDecoder(fd).Decode(&config)
	if err != nil {
		return nil, err
	}

	if err = config.Valid(); err != nil {
		return nil, err
	}

	config.setDefaultsIfUnset()

	glog.V(5).Infof("Read config: %v", config)

	return &config, err
}

func (c *Config) setDefaultsIfUnset() {
	if c.Bind.Port == 0 {
		c.Bind.Port = 8443
	}

	if c.Bind.Host == "" {
		glog.V(5).Infof("Bind host not set. Using default: 0.0.0.0")
		c.Bind.Host = "0.0.0.0"
	}

	if c.Metrics.Host == "" {
		glog.V(5).Infof("Metrics host not set. Using default: 0.0.0.0")
		c.Metrics.Host = c.Bind.Host
	}

	if c.Metrics.Port == 0 {
		glog.V(5).Infof("Metrics port not set. Using default: 9091")
		c.Metrics.Port = 9091
	}

	if c.Metrics.Path == "" {
		glog.V(5).Infof("Metrics path not set. Using default: /metrics")
		c.Metrics.Path = "/metrics"
	}
	if !strings.HasPrefix(c.Metrics.Path, "/") {
		c.Metrics.Path = "/" + c.Metrics.Path
	}

}
<commit_msg>goimports failed me<commit_after>/* {{{ Copyright (c) Paul R. Tagliamonte <paultag@debian.org>, 2015
 *
 * 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 main

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"strings"
	"sync"

	"github.com/golang/glog"

	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/cache"
)

const (
	maxTCPPort = 65535
)

type Config struct {
	Bind       Bind
	Metrics    Metrics
	Servers    []Server
	Kubernetes *Kubernetes
	proxy      *Proxy
	lock       sync.Mutex

	serviceController cache.Controller
	serviceStore      cache.Store
	ingressController cache.Controller
	ingressStore      cache.Store
}

// Valid returns an if the config is invalid
func (c *Config) Valid() error {
	if len(c.Servers) > 0 && c.Kubernetes != nil {
		return errors.New("Cannot set .Servers and .Kubernetes in config file")
	}

	if err := c.Metrics.Valid(); err != nil {
		return err
	}

	return nil
}

type Kubernetes struct {
	Kubeconfig   string
	Client       *kubernetes.Clientset
	IngressClass string
}

// Metrics contains the port & path for the
// prometheus endpoint
type Metrics struct {
	Host string
	Port int
	Path string
}

type Bind struct {
	Host string
	Port int
}

type Server struct {
	Default bool
	Regexp  bool
	Host    string
	Names   []string
	Port    int
}

// Valid returns an error if the metrics config is invalid
func (m Metrics) Valid() error {
	if m.Port > maxTCPPort {
		return fmt.Errorf("Configured metrics port is above %d: port=%d", maxTCPPort, m.Port)
	}

	return nil
}

func LoadConfig(path string) (*Config, error) {
	glog.V(5).Infof("Loading config from: %s", path)

	fd, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	config := Config{}
	err = json.NewDecoder(fd).Decode(&config)
	if err != nil {
		return nil, err
	}

	if err = config.Valid(); err != nil {
		return nil, err
	}

	config.setDefaultsIfUnset()

	glog.V(5).Infof("Read config: %v", config)

	return &config, err
}

func (c *Config) setDefaultsIfUnset() {
	if c.Bind.Port == 0 {
		c.Bind.Port = 8443
	}

	if c.Bind.Host == "" {
		glog.V(5).Infof("Bind host not set. Using default: 0.0.0.0")
		c.Bind.Host = "0.0.0.0"
	}

	if c.Metrics.Host == "" {
		glog.V(5).Infof("Metrics host not set. Using default: 0.0.0.0")
		c.Metrics.Host = c.Bind.Host
	}

	if c.Metrics.Port == 0 {
		glog.V(5).Infof("Metrics port not set. Using default: 9091")
		c.Metrics.Port = 9091
	}

	if c.Metrics.Path == "" {
		glog.V(5).Infof("Metrics path not set. Using default: /metrics")
		c.Metrics.Path = "/metrics"
	}
	if !strings.HasPrefix(c.Metrics.Path, "/") {
		c.Metrics.Path = "/" + c.Metrics.Path
	}

}
<|endoftext|>


<commit_before>package aws

import (
	"crypto/tls"
	"errors"
	"fmt"
	"log"
	"net/http"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/request"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/apigateway"
	"github.com/aws/aws-sdk-go/service/applicationautoscaling"
	"github.com/aws/aws-sdk-go/service/autoscaling"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/aws/aws-sdk-go/service/cloudfront"
	"github.com/aws/aws-sdk-go/service/cloudtrail"
	"github.com/aws/aws-sdk-go/service/cloudwatch"
	"github.com/aws/aws-sdk-go/service/cloudwatchevents"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
	"github.com/aws/aws-sdk-go/service/codecommit"
	"github.com/aws/aws-sdk-go/service/codedeploy"
	"github.com/aws/aws-sdk-go/service/directoryservice"
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/ecr"
	"github.com/aws/aws-sdk-go/service/ecs"
	"github.com/aws/aws-sdk-go/service/efs"
	"github.com/aws/aws-sdk-go/service/elasticache"
	"github.com/aws/aws-sdk-go/service/elasticbeanstalk"
	elasticsearch "github.com/aws/aws-sdk-go/service/elasticsearchservice"
	"github.com/aws/aws-sdk-go/service/elastictranscoder"
	"github.com/aws/aws-sdk-go/service/elb"
	"github.com/aws/aws-sdk-go/service/elbv2"
	"github.com/aws/aws-sdk-go/service/emr"
	"github.com/aws/aws-sdk-go/service/firehose"
	"github.com/aws/aws-sdk-go/service/glacier"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/aws/aws-sdk-go/service/kinesis"
	"github.com/aws/aws-sdk-go/service/kms"
	"github.com/aws/aws-sdk-go/service/lambda"
	"github.com/aws/aws-sdk-go/service/opsworks"
	"github.com/aws/aws-sdk-go/service/rds"
	"github.com/aws/aws-sdk-go/service/redshift"
	"github.com/aws/aws-sdk-go/service/route53"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/ses"
	"github.com/aws/aws-sdk-go/service/simpledb"
	"github.com/aws/aws-sdk-go/service/sns"
	"github.com/aws/aws-sdk-go/service/sqs"
	"github.com/aws/aws-sdk-go/service/ssm"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/go-cleanhttp"
	"github.com/hashicorp/terraform/helper/logging"
	"github.com/hashicorp/terraform/terraform"
)

type Config struct {
	AccessKey     string
	SecretKey     string
	CredsFilename string
	Profile       string
	Token         string
	Region        string
	MaxRetries    int

	AssumeRoleARN         string
	AssumeRoleExternalID  string
	AssumeRoleSessionName string

	AllowedAccountIds   []interface{}
	ForbiddenAccountIds []interface{}

	DynamoDBEndpoint string
	KinesisEndpoint  string
	Ec2Endpoint      string
	IamEndpoint      string
	ElbEndpoint      string
	S3Endpoint       string
	Insecure         bool

	SkipCredsValidation     bool
	SkipRequestingAccountId bool
	SkipMetadataApiCheck    bool
	S3ForcePathStyle        bool
}

type AWSClient struct {
	cfconn                *cloudformation.CloudFormation
	cloudfrontconn        *cloudfront.CloudFront
	cloudtrailconn        *cloudtrail.CloudTrail
	cloudwatchconn        *cloudwatch.CloudWatch
	cloudwatchlogsconn    *cloudwatchlogs.CloudWatchLogs
	cloudwatcheventsconn  *cloudwatchevents.CloudWatchEvents
	dsconn                *directoryservice.DirectoryService
	dynamodbconn          *dynamodb.DynamoDB
	ec2conn               *ec2.EC2
	ecrconn               *ecr.ECR
	ecsconn               *ecs.ECS
	efsconn               *efs.EFS
	elbconn               *elb.ELB
	elbv2conn             *elbv2.ELBV2
	emrconn               *emr.EMR
	esconn                *elasticsearch.ElasticsearchService
	apigateway            *apigateway.APIGateway
	appautoscalingconn    *applicationautoscaling.ApplicationAutoScaling
	autoscalingconn       *autoscaling.AutoScaling
	s3conn                *s3.S3
	sesConn               *ses.SES
	simpledbconn          *simpledb.SimpleDB
	sqsconn               *sqs.SQS
	snsconn               *sns.SNS
	stsconn               *sts.STS
	redshiftconn          *redshift.Redshift
	r53conn               *route53.Route53
	accountid             string
	region                string
	rdsconn               *rds.RDS
	iamconn               *iam.IAM
	kinesisconn           *kinesis.Kinesis
	kmsconn               *kms.KMS
	firehoseconn          *firehose.Firehose
	elasticacheconn       *elasticache.ElastiCache
	elasticbeanstalkconn  *elasticbeanstalk.ElasticBeanstalk
	elastictranscoderconn *elastictranscoder.ElasticTranscoder
	lambdaconn            *lambda.Lambda
	opsworksconn          *opsworks.OpsWorks
	glacierconn           *glacier.Glacier
	codedeployconn        *codedeploy.CodeDeploy
	codecommitconn        *codecommit.CodeCommit
	ssmconn               *ssm.SSM
}

// Client configures and returns a fully initialized AWSClient
func (c *Config) Client() (interface{}, error) {
	// Get the auth and region. This can fail if keys/regions were not
	// specified and we're attempting to use the environment.
	log.Println("[INFO] Building AWS region structure")
	err := c.ValidateRegion()
	if err != nil {
		return nil, err
	}

	var client AWSClient
	// store AWS region in client struct, for region specific operations such as
	// bucket storage in S3
	client.region = c.Region

	log.Println("[INFO] Building AWS auth structure")
	creds, err := GetCredentials(c)
	if err != nil {
		return nil, err
	}
	// Call Get to check for credential provider. If nothing found, we'll get an
	// error, and we can present it nicely to the user
	cp, err := creds.Get()
	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" {
			return nil, errors.New(`No valid credential sources found for AWS Provider.
  Please see https://terraform.io/docs/providers/aws/index.html for more information on
  providing credentials for the AWS Provider`)
		}

		return nil, fmt.Errorf("Error loading credentials for AWS Provider: %s", err)
	}

	log.Printf("[INFO] AWS Auth provider used: %q", cp.ProviderName)

	awsConfig := &aws.Config{
		Credentials:      creds,
		Region:           aws.String(c.Region),
		MaxRetries:       aws.Int(c.MaxRetries),
		HTTPClient:       cleanhttp.DefaultClient(),
		S3ForcePathStyle: aws.Bool(c.S3ForcePathStyle),
	}

	if logging.IsDebugOrHigher() {
		awsConfig.LogLevel = aws.LogLevel(aws.LogDebugWithHTTPBody)
		awsConfig.Logger = awsLogger{}
	}

	if c.Insecure {
		transport := awsConfig.HTTPClient.Transport.(*http.Transport)
		transport.TLSClientConfig = &tls.Config{
			InsecureSkipVerify: true,
		}
	}

	// Set up base session
	sess, err := session.NewSession(awsConfig)
	if err != nil {
		return nil, errwrap.Wrapf("Error creating AWS session: {{err}}", err)
	}
	sess.Handlers.Build.PushFrontNamed(addTerraformVersionToUserAgent)

	// Some services exist only in us-east-1, e.g. because they manage
	// resources that can span across multiple regions, or because
	// signature format v4 requires region to be us-east-1 for global
	// endpoints:
	// http://docs.aws.amazon.com/general/latest/gr/sigv4_changes.html
	usEast1Sess := sess.Copy(&aws.Config{Region: aws.String("us-east-1")})

	// Some services have user-configurable endpoints
	awsEc2Sess := sess.Copy(&aws.Config{Endpoint: aws.String(c.Ec2Endpoint)})
	awsElbSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.ElbEndpoint)})
	awsIamSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.IamEndpoint)})
	awsS3Sess := sess.Copy(&aws.Config{Endpoint: aws.String(c.S3Endpoint)})
	dynamoSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.DynamoDBEndpoint)})
	kinesisSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.KinesisEndpoint)})

	// These two services need to be set up early so we can check on AccountID
	client.iamconn = iam.New(awsIamSess)
	client.stsconn = sts.New(sess)

	if !c.SkipCredsValidation {
		err = c.ValidateCredentials(client.stsconn)
		if err != nil {
			return nil, err
		}
	}

	if !c.SkipRequestingAccountId {
		accountId, err := GetAccountId(client.iamconn, client.stsconn, cp.ProviderName)
		if err == nil {
			client.accountid = accountId
		}
	}

	authErr := c.ValidateAccountId(client.accountid)
	if authErr != nil {
		return nil, authErr
	}

	client.apigateway = apigateway.New(sess)
	client.appautoscalingconn = applicationautoscaling.New(sess)
	client.autoscalingconn = autoscaling.New(sess)
	client.cfconn = cloudformation.New(sess)
	client.cloudfrontconn = cloudfront.New(sess)
	client.cloudtrailconn = cloudtrail.New(sess)
	client.cloudwatchconn = cloudwatch.New(sess)
	client.cloudwatcheventsconn = cloudwatchevents.New(sess)
	client.cloudwatchlogsconn = cloudwatchlogs.New(sess)
	client.codecommitconn = codecommit.New(usEast1Sess)
	client.codedeployconn = codedeploy.New(sess)
	client.dsconn = directoryservice.New(sess)
	client.dynamodbconn = dynamodb.New(dynamoSess)
	client.ec2conn = ec2.New(awsEc2Sess)
	client.ecrconn = ecr.New(sess)
	client.ecsconn = ecs.New(sess)
	client.efsconn = efs.New(sess)
	client.elasticacheconn = elasticache.New(sess)
	client.elasticbeanstalkconn = elasticbeanstalk.New(sess)
	client.elastictranscoderconn = elastictranscoder.New(sess)
	client.elbconn = elb.New(awsElbSess)
	client.elbv2conn = elbv2.New(awsElbSess)
	client.emrconn = emr.New(sess)
	client.esconn = elasticsearch.New(sess)
	client.firehoseconn = firehose.New(sess)
	client.glacierconn = glacier.New(sess)
	client.kinesisconn = kinesis.New(kinesisSess)
	client.kmsconn = kms.New(sess)
	client.lambdaconn = lambda.New(sess)
	client.opsworksconn = opsworks.New(usEast1Sess)
	client.r53conn = route53.New(usEast1Sess)
	client.rdsconn = rds.New(sess)
	client.redshiftconn = redshift.New(sess)
	client.simpledbconn = simpledb.New(sess)
	client.s3conn = s3.New(awsS3Sess)
	client.sesConn = ses.New(sess)
	client.snsconn = sns.New(sess)
	client.sqsconn = sqs.New(sess)
	client.ssmconn = ssm.New(sess)

	return &client, nil
}

// ValidateRegion returns an error if the configured region is not a
// valid aws region and nil otherwise.
func (c *Config) ValidateRegion() error {
	var regions = [13]string{
		"ap-northeast-1",
		"ap-northeast-2",
		"ap-south-1",
		"ap-southeast-1",
		"ap-southeast-2",
		"cn-north-1",
		"eu-central-1",
		"eu-west-1",
		"sa-east-1",
		"us-east-1",
		"us-gov-west-1",
		"us-west-1",
		"us-west-2",
	}

	for _, valid := range regions {
		if c.Region == valid {
			return nil
		}
	}
	return fmt.Errorf("Not a valid region: %s", c.Region)
}

// Validate credentials early and fail before we do any graph walking.
func (c *Config) ValidateCredentials(stsconn *sts.STS) error {
	_, err := stsconn.GetCallerIdentity(&sts.GetCallerIdentityInput{})
	return err
}

// ValidateAccountId returns a context-specific error if the configured account
// id is explicitly forbidden or not authorised; and nil if it is authorised.
func (c *Config) ValidateAccountId(accountId string) error {
	if c.AllowedAccountIds == nil && c.ForbiddenAccountIds == nil {
		return nil
	}

	log.Println("[INFO] Validating account ID")

	if c.ForbiddenAccountIds != nil {
		for _, id := range c.ForbiddenAccountIds {
			if id == accountId {
				return fmt.Errorf("Forbidden account ID (%s)", id)
			}
		}
	}

	if c.AllowedAccountIds != nil {
		for _, id := range c.AllowedAccountIds {
			if id == accountId {
				return nil
			}
		}
		return fmt.Errorf("Account ID not allowed (%s)", accountId)
	}

	return nil
}

// addTerraformVersionToUserAgent is a named handler that will add Terraform's
// version information to requests made by the AWS SDK.
var addTerraformVersionToUserAgent = request.NamedHandler{
	Name: "terraform.TerraformVersionUserAgentHandler",
	Fn: request.MakeAddToUserAgentHandler(
		"terraform", terraform.VersionString()),
}

type awsLogger struct{}

func (l awsLogger) Log(args ...interface{}) {
	tokens := make([]string, 0, len(args))
	for _, arg := range args {
		if token, ok := arg.(string); ok {
			tokens = append(tokens, token)
		}
	}
	log.Printf("[DEBUG] [aws-sdk-go] %s", strings.Join(tokens, " "))
}
<commit_msg>Vendored WAF (#8785)<commit_after>package aws

import (
	"crypto/tls"
	"errors"
	"fmt"
	"log"
	"net/http"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/request"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/apigateway"
	"github.com/aws/aws-sdk-go/service/applicationautoscaling"
	"github.com/aws/aws-sdk-go/service/autoscaling"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/aws/aws-sdk-go/service/cloudfront"
	"github.com/aws/aws-sdk-go/service/cloudtrail"
	"github.com/aws/aws-sdk-go/service/cloudwatch"
	"github.com/aws/aws-sdk-go/service/cloudwatchevents"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
	"github.com/aws/aws-sdk-go/service/codecommit"
	"github.com/aws/aws-sdk-go/service/codedeploy"
	"github.com/aws/aws-sdk-go/service/directoryservice"
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/ecr"
	"github.com/aws/aws-sdk-go/service/ecs"
	"github.com/aws/aws-sdk-go/service/efs"
	"github.com/aws/aws-sdk-go/service/elasticache"
	"github.com/aws/aws-sdk-go/service/elasticbeanstalk"
	elasticsearch "github.com/aws/aws-sdk-go/service/elasticsearchservice"
	"github.com/aws/aws-sdk-go/service/elastictranscoder"
	"github.com/aws/aws-sdk-go/service/elb"
	"github.com/aws/aws-sdk-go/service/elbv2"
	"github.com/aws/aws-sdk-go/service/emr"
	"github.com/aws/aws-sdk-go/service/firehose"
	"github.com/aws/aws-sdk-go/service/glacier"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/aws/aws-sdk-go/service/kinesis"
	"github.com/aws/aws-sdk-go/service/kms"
	"github.com/aws/aws-sdk-go/service/lambda"
	"github.com/aws/aws-sdk-go/service/opsworks"
	"github.com/aws/aws-sdk-go/service/rds"
	"github.com/aws/aws-sdk-go/service/redshift"
	"github.com/aws/aws-sdk-go/service/route53"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/ses"
	"github.com/aws/aws-sdk-go/service/simpledb"
	"github.com/aws/aws-sdk-go/service/sns"
	"github.com/aws/aws-sdk-go/service/sqs"
	"github.com/aws/aws-sdk-go/service/ssm"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/aws/aws-sdk-go/service/waf"
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/go-cleanhttp"
	"github.com/hashicorp/terraform/helper/logging"
	"github.com/hashicorp/terraform/terraform"
)

type Config struct {
	AccessKey     string
	SecretKey     string
	CredsFilename string
	Profile       string
	Token         string
	Region        string
	MaxRetries    int

	AssumeRoleARN         string
	AssumeRoleExternalID  string
	AssumeRoleSessionName string

	AllowedAccountIds   []interface{}
	ForbiddenAccountIds []interface{}

	DynamoDBEndpoint string
	KinesisEndpoint  string
	Ec2Endpoint      string
	IamEndpoint      string
	ElbEndpoint      string
	S3Endpoint       string
	Insecure         bool

	SkipCredsValidation     bool
	SkipRequestingAccountId bool
	SkipMetadataApiCheck    bool
	S3ForcePathStyle        bool
}

type AWSClient struct {
	cfconn                *cloudformation.CloudFormation
	cloudfrontconn        *cloudfront.CloudFront
	cloudtrailconn        *cloudtrail.CloudTrail
	cloudwatchconn        *cloudwatch.CloudWatch
	cloudwatchlogsconn    *cloudwatchlogs.CloudWatchLogs
	cloudwatcheventsconn  *cloudwatchevents.CloudWatchEvents
	dsconn                *directoryservice.DirectoryService
	dynamodbconn          *dynamodb.DynamoDB
	ec2conn               *ec2.EC2
	ecrconn               *ecr.ECR
	ecsconn               *ecs.ECS
	efsconn               *efs.EFS
	elbconn               *elb.ELB
	elbv2conn             *elbv2.ELBV2
	emrconn               *emr.EMR
	esconn                *elasticsearch.ElasticsearchService
	apigateway            *apigateway.APIGateway
	appautoscalingconn    *applicationautoscaling.ApplicationAutoScaling
	autoscalingconn       *autoscaling.AutoScaling
	s3conn                *s3.S3
	sesConn               *ses.SES
	simpledbconn          *simpledb.SimpleDB
	sqsconn               *sqs.SQS
	snsconn               *sns.SNS
	stsconn               *sts.STS
	redshiftconn          *redshift.Redshift
	r53conn               *route53.Route53
	accountid             string
	region                string
	rdsconn               *rds.RDS
	iamconn               *iam.IAM
	kinesisconn           *kinesis.Kinesis
	kmsconn               *kms.KMS
	firehoseconn          *firehose.Firehose
	elasticacheconn       *elasticache.ElastiCache
	elasticbeanstalkconn  *elasticbeanstalk.ElasticBeanstalk
	elastictranscoderconn *elastictranscoder.ElasticTranscoder
	lambdaconn            *lambda.Lambda
	opsworksconn          *opsworks.OpsWorks
	glacierconn           *glacier.Glacier
	codedeployconn        *codedeploy.CodeDeploy
	codecommitconn        *codecommit.CodeCommit
	ssmconn               *ssm.SSM
	wafconn               *waf.WAF
}

// Client configures and returns a fully initialized AWSClient
func (c *Config) Client() (interface{}, error) {
	// Get the auth and region. This can fail if keys/regions were not
	// specified and we're attempting to use the environment.
	log.Println("[INFO] Building AWS region structure")
	err := c.ValidateRegion()
	if err != nil {
		return nil, err
	}

	var client AWSClient
	// store AWS region in client struct, for region specific operations such as
	// bucket storage in S3
	client.region = c.Region

	log.Println("[INFO] Building AWS auth structure")
	creds, err := GetCredentials(c)
	if err != nil {
		return nil, err
	}
	// Call Get to check for credential provider. If nothing found, we'll get an
	// error, and we can present it nicely to the user
	cp, err := creds.Get()
	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" {
			return nil, errors.New(`No valid credential sources found for AWS Provider.
  Please see https://terraform.io/docs/providers/aws/index.html for more information on
  providing credentials for the AWS Provider`)
		}

		return nil, fmt.Errorf("Error loading credentials for AWS Provider: %s", err)
	}

	log.Printf("[INFO] AWS Auth provider used: %q", cp.ProviderName)

	awsConfig := &aws.Config{
		Credentials:      creds,
		Region:           aws.String(c.Region),
		MaxRetries:       aws.Int(c.MaxRetries),
		HTTPClient:       cleanhttp.DefaultClient(),
		S3ForcePathStyle: aws.Bool(c.S3ForcePathStyle),
	}

	if logging.IsDebugOrHigher() {
		awsConfig.LogLevel = aws.LogLevel(aws.LogDebugWithHTTPBody)
		awsConfig.Logger = awsLogger{}
	}

	if c.Insecure {
		transport := awsConfig.HTTPClient.Transport.(*http.Transport)
		transport.TLSClientConfig = &tls.Config{
			InsecureSkipVerify: true,
		}
	}

	// Set up base session
	sess, err := session.NewSession(awsConfig)
	if err != nil {
		return nil, errwrap.Wrapf("Error creating AWS session: {{err}}", err)
	}
	sess.Handlers.Build.PushFrontNamed(addTerraformVersionToUserAgent)

	// Some services exist only in us-east-1, e.g. because they manage
	// resources that can span across multiple regions, or because
	// signature format v4 requires region to be us-east-1 for global
	// endpoints:
	// http://docs.aws.amazon.com/general/latest/gr/sigv4_changes.html
	usEast1Sess := sess.Copy(&aws.Config{Region: aws.String("us-east-1")})

	// Some services have user-configurable endpoints
	awsEc2Sess := sess.Copy(&aws.Config{Endpoint: aws.String(c.Ec2Endpoint)})
	awsElbSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.ElbEndpoint)})
	awsIamSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.IamEndpoint)})
	awsS3Sess := sess.Copy(&aws.Config{Endpoint: aws.String(c.S3Endpoint)})
	dynamoSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.DynamoDBEndpoint)})
	kinesisSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.KinesisEndpoint)})

	// These two services need to be set up early so we can check on AccountID
	client.iamconn = iam.New(awsIamSess)
	client.stsconn = sts.New(sess)

	if !c.SkipCredsValidation {
		err = c.ValidateCredentials(client.stsconn)
		if err != nil {
			return nil, err
		}
	}

	if !c.SkipRequestingAccountId {
		accountId, err := GetAccountId(client.iamconn, client.stsconn, cp.ProviderName)
		if err == nil {
			client.accountid = accountId
		}
	}

	authErr := c.ValidateAccountId(client.accountid)
	if authErr != nil {
		return nil, authErr
	}

	client.apigateway = apigateway.New(sess)
	client.appautoscalingconn = applicationautoscaling.New(sess)
	client.autoscalingconn = autoscaling.New(sess)
	client.cfconn = cloudformation.New(sess)
	client.cloudfrontconn = cloudfront.New(sess)
	client.cloudtrailconn = cloudtrail.New(sess)
	client.cloudwatchconn = cloudwatch.New(sess)
	client.cloudwatcheventsconn = cloudwatchevents.New(sess)
	client.cloudwatchlogsconn = cloudwatchlogs.New(sess)
	client.codecommitconn = codecommit.New(usEast1Sess)
	client.codedeployconn = codedeploy.New(sess)
	client.dsconn = directoryservice.New(sess)
	client.dynamodbconn = dynamodb.New(dynamoSess)
	client.ec2conn = ec2.New(awsEc2Sess)
	client.ecrconn = ecr.New(sess)
	client.ecsconn = ecs.New(sess)
	client.efsconn = efs.New(sess)
	client.elasticacheconn = elasticache.New(sess)
	client.elasticbeanstalkconn = elasticbeanstalk.New(sess)
	client.elastictranscoderconn = elastictranscoder.New(sess)
	client.elbconn = elb.New(awsElbSess)
	client.elbv2conn = elbv2.New(awsElbSess)
	client.emrconn = emr.New(sess)
	client.esconn = elasticsearch.New(sess)
	client.firehoseconn = firehose.New(sess)
	client.glacierconn = glacier.New(sess)
	client.kinesisconn = kinesis.New(kinesisSess)
	client.kmsconn = kms.New(sess)
	client.lambdaconn = lambda.New(sess)
	client.opsworksconn = opsworks.New(usEast1Sess)
	client.r53conn = route53.New(usEast1Sess)
	client.rdsconn = rds.New(sess)
	client.redshiftconn = redshift.New(sess)
	client.simpledbconn = simpledb.New(sess)
	client.s3conn = s3.New(awsS3Sess)
	client.sesConn = ses.New(sess)
	client.snsconn = sns.New(sess)
	client.sqsconn = sqs.New(sess)
	client.ssmconn = ssm.New(sess)
	client.wafconn = waf.New(sess)

	return &client, nil
}

// ValidateRegion returns an error if the configured region is not a
// valid aws region and nil otherwise.
func (c *Config) ValidateRegion() error {
	var regions = [13]string{
		"ap-northeast-1",
		"ap-northeast-2",
		"ap-south-1",
		"ap-southeast-1",
		"ap-southeast-2",
		"cn-north-1",
		"eu-central-1",
		"eu-west-1",
		"sa-east-1",
		"us-east-1",
		"us-gov-west-1",
		"us-west-1",
		"us-west-2",
	}

	for _, valid := range regions {
		if c.Region == valid {
			return nil
		}
	}
	return fmt.Errorf("Not a valid region: %s", c.Region)
}

// Validate credentials early and fail before we do any graph walking.
func (c *Config) ValidateCredentials(stsconn *sts.STS) error {
	_, err := stsconn.GetCallerIdentity(&sts.GetCallerIdentityInput{})
	return err
}

// ValidateAccountId returns a context-specific error if the configured account
// id is explicitly forbidden or not authorised; and nil if it is authorised.
func (c *Config) ValidateAccountId(accountId string) error {
	if c.AllowedAccountIds == nil && c.ForbiddenAccountIds == nil {
		return nil
	}

	log.Println("[INFO] Validating account ID")

	if c.ForbiddenAccountIds != nil {
		for _, id := range c.ForbiddenAccountIds {
			if id == accountId {
				return fmt.Errorf("Forbidden account ID (%s)", id)
			}
		}
	}

	if c.AllowedAccountIds != nil {
		for _, id := range c.AllowedAccountIds {
			if id == accountId {
				return nil
			}
		}
		return fmt.Errorf("Account ID not allowed (%s)", accountId)
	}

	return nil
}

// addTerraformVersionToUserAgent is a named handler that will add Terraform's
// version information to requests made by the AWS SDK.
var addTerraformVersionToUserAgent = request.NamedHandler{
	Name: "terraform.TerraformVersionUserAgentHandler",
	Fn: request.MakeAddToUserAgentHandler(
		"terraform", terraform.VersionString()),
}

type awsLogger struct{}

func (l awsLogger) Log(args ...interface{}) {
	tokens := make([]string, 0, len(args))
	for _, arg := range args {
		if token, ok := arg.(string); ok {
			tokens = append(tokens, token)
		}
	}
	log.Printf("[DEBUG] [aws-sdk-go] %s", strings.Join(tokens, " "))
}
<|endoftext|>


<commit_before>// Package config version v1.0.0
package config

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/user"
	"path/filepath"
	"reflect"
	"strings"

	"gopkg.in/yaml.v2"
)

// Namespace holds the pieces joined together to form the
// directory namespacing for config.
type Namespace struct {
	Organization string // optional additional namespace for orgs.
	System       string // Name of the system associated with this config.
}

// The Config interface implements config.
type Config interface {
	Load(src string, dst interface{}) error
}

// UserBase and SystemBase are the prefixes for the user and system config
// paths, respectively.
const (
	UserBase   string = "~/.config/"
	SystemBase string = "/etc/"
)

// Load reads the contents of the src URI and unmarshals into dst using
// go-yaml.
func Load(src string, dst interface{}) (err error) {
	var data []byte
	dstv := reflect.ValueOf(dst)

	if dstv.Kind() != reflect.Ptr {
		err = errors.New("config: not a pointer")
		return
	}

	uri, err := url.Parse(src)
	if err != nil {
		return err
	}

	switch {
	case uri.Scheme == "file" || uri.Scheme == "":
		path := ExpandUser(uri.Path)
		data, err = ioutil.ReadFile(path)
		if err != nil {
			return err
		}
	case uri.Scheme == "http" || uri.Scheme == "https":
		resp, err := http.Get(uri.String())
		if err != nil {
			return err
		}

		data, err = ioutil.ReadAll(resp.Body)

		resp.Body.Close()
		if err != nil {
			return err
		}
	}

	err = yaml.Unmarshal(data, dst)
	return
}

// ExpandUser acts kind of like os.path.expanduser in Python, except only
// supports expanding "~/" or "$HOME"
func ExpandUser(path string) (exPath string) {
	usr, _ := user.Current()

	dir := fmt.Sprintf("%s/", usr.HomeDir)

	exPath = path
	if len(path) > 2 && path[:2] == "~/" {
		exPath = strings.Replace(exPath, "~/", dir, 1)
	} else if len(path) > 5 && path[:5] == "$HOME" {
		exPath = strings.Replace(exPath, "$HOME", dir, 1)
	}

	exPath, _ = filepath.Abs(filepath.Clean(exPath))
	return
}

// Path returns path to config, chosen by hierarchy and checked for
// existence:
//
// 1. User config (~/.config/podhub/canary/config.yaml)
//
// 2. System config (/etc/podhub/canary/config.yaml)
func (c Namespace) Path() (path string) {
	systemPath, _ := c.systemPath()
	if _, err := os.Stat(systemPath); err == nil {
		path, _ = c.systemPath()
	}

	userPath, _ := c.userPath()
	if _, err := os.Stat(userPath); err == nil {
		path, _ = c.userPath()
	}
	return
}

func (c Namespace) systemPath() (path string, err error) {
	path = filepath.Join(SystemBase, c.Organization, c.System, "config.yaml")
	return
}

func (c Namespace) userPath() (path string, err error) {
	userBase := ExpandUser(UserBase)

	path = filepath.Join(userBase, c.Organization, c.System, "config.yaml")
	return
}

// EnvVar returns the name of the environment variable containing the URI
// of the config.
// Example: PODHUB_UUIDD_CONFIG_URI
func (c Namespace) EnvVar() (envvar string) {
	s := []string{c.Organization, c.System, "CONFIG", "URI"}
	envvar = strings.ToUpper(strings.Join(s, "_"))
	return
}

// Load is a convenience function registered to config.Namespace to
// implement Config.Load().
func (c Namespace) Load(dst interface{}) (err error) {
	cfgPath := c.Path()

	err = Load(cfgPath, dst)
	return
}
<commit_msg>use uri to construct path<commit_after>// Package config version v1.0.0
package config

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/user"
	"path/filepath"
	"reflect"
	"strings"

	"gopkg.in/yaml.v2"
)

// Namespace holds the pieces joined together to form the
// directory namespacing for config.
type Namespace struct {
	Organization string // optional additional namespace for orgs.
	System       string // Name of the system associated with this config.
}

// The Config interface implements config.
type Config interface {
	Load(src string, dst interface{}) error
}

// UserBase and SystemBase are the prefixes for the user and system config
// paths, respectively.
const (
	UserBase   string = "~/.config/"
	SystemBase string = "/etc/"
)

// Load reads the contents of the src URI and unmarshals into dst using
// go-yaml.
func Load(src string, dst interface{}) (err error) {
	var data []byte
	dstv := reflect.ValueOf(dst)

	if dstv.Kind() != reflect.Ptr {
		err = errors.New("config: not a pointer")
		return
	}

	uri, err := url.Parse(src)
	if err != nil {
		return err
	}

	switch {
	case uri.Scheme == "file" || uri.Scheme == "":
		path := ExpandUser(uri.Path)
		data, err = ioutil.ReadFile(path)
		if err != nil {
			return err
		}
	case uri.Scheme == "http" || uri.Scheme == "https":
		resp, err := http.Get(uri.String())
		if err != nil {
			return err
		}

		data, err = ioutil.ReadAll(resp.Body)

		resp.Body.Close()
		if err != nil {
			return err
		}
	}

	err = yaml.Unmarshal(data, dst)
	return
}

// ExpandUser acts kind of like os.path.expanduser in Python, except only
// supports expanding "~/" or "$HOME"
func ExpandUser(path string) (exPath string) {
	usr, _ := user.Current()

	dir := fmt.Sprintf("%s/", usr.HomeDir)

	exPath = path
	if len(path) > 2 && path[:2] == "~/" {
		exPath = strings.Replace(exPath, "~/", dir, 1)
	} else if len(path) > 5 && path[:5] == "$HOME" {
		exPath = strings.Replace(exPath, "$HOME", dir, 1)
	}

	exPath, _ = filepath.Abs(filepath.Clean(exPath))
	return
}

// Path returns path to config, chosen by hierarchy and checked for
// existence:
//
// 1. User config (~/.config/podhub/canary/config.yaml)
//
// 2. System config (/etc/podhub/canary/config.yaml)
func (c Namespace) Path() (path string) {
	systemPath, _ := c.systemPath()
	if _, err := os.Stat(systemPath); err == nil {
		path, _ = c.systemPath()
	}

	userPath, _ := c.userPath()
	if _, err := os.Stat(userPath); err == nil {
		path, _ = c.userPath()
	}
	return
}

func (c Namespace) systemPath() (uri string, err error) {
	path := filepath.Join(SystemBase, c.Organization, c.System, "config.yaml")
	_uri := url.URL{Path: path, Scheme: "file"}
	uri = _uri.String()
	return
}

func (c Namespace) userPath() (uri string, err error) {
	userBase := ExpandUser(UserBase)

	path := filepath.Join(userBase, c.Organization, c.System, "config.yaml")
	_uri := url.URL{Path: path, Scheme: "file"}
	uri = _uri.String()
	return
}

// EnvVar returns the name of the environment variable containing the URI
// of the config.
// Example: PODHUB_UUIDD_CONFIG_URI
func (c Namespace) EnvVar() (envvar string) {
	s := []string{c.Organization, c.System, "CONFIG", "URI"}
	envvar = strings.ToUpper(strings.Join(s, "_"))
	return
}

// Load is a convenience function registered to config.Namespace to
// implement Config.Load().
func (c Namespace) Load(dst interface{}) (err error) {
	cfgPath := c.Path()

	err = Load(cfgPath, dst)
	return
}
<|endoftext|>


<commit_before>package main

import (
	"reflect"
	"time"

	"gopkg.in/go-playground/validator.v8"

	"github.com/go-sql-driver/mysql"
	"github.com/spf13/viper"
)

type configuration struct {
	HTTP struct {
		Address string `validate:"required"`
		Mode    string `validate:"required,eq=release|eq=test|eq=debug"`
	}
	DB        db `validate:"required,dive"`
	AuthToken struct {
		Lifetime time.Duration `validate:"required"`
	}
	Mandrill struct {
		Key       string `validate:"required"`
		FromEmail string `validate:"required"`
		FromName  string `validate:"required"`
	}
	Cache struct {
		NumCachedIncomes int `validate:"required,gt=1"`
	}
	Template struct {
		EmailVerificationTemplate string `validate:"required"`
	}
}

type db struct {
	DataSourceName string `validate:"required,dsn"`
	MaxOpenConns   int    `validate:"required,min=1"`
	MaxIdleConns   int    `validate:"required,min=1,ltefield=MaxOpenConns"`
}

var config configuration

func initConfig() {
	// env config
	viper.SetEnvPrefix("solebtc") // will turn into uppercase, e.g. SOLEBTC_PORT
	viper.AutomaticEnv()

	// set default
	viper.SetDefault("mode", "debug")
	viper.SetDefault("address", "0.0.0.0:3000")
	viper.SetDefault("dsn", "/solebtc_dev?parseTime=true")
	viper.SetDefault("auth_token_lifetime", "720h")
	viper.SetDefault("mandrill_key", "SANDBOX_SUCCESS")
	viper.SetDefault("mandrill_from_email", "no_reply@solebtc.com")
	viper.SetDefault("mandrill_from_name", "SoleBTC")
	viper.SetDefault("max_open_conns", 2)
	viper.SetDefault("max_idle_conns", 2)
	viper.SetDefault("num_cached_incomes", 20)
	viper.SetDefault("email_verification_template", "./templates/email_verification_staging.html")

	// See Viper doc, config is get in the following order
	// override, flag, env, config file, key/value store, default

	config.HTTP.Mode = viper.GetString("mode")
	config.HTTP.Address = viper.GetString("address")

	config.DB.DataSourceName = viper.GetString("dsn")
	config.DB.MaxOpenConns = viper.GetInt("max_open_conns")
	config.DB.MaxIdleConns = viper.GetInt("max_idle_conns")

	config.AuthToken.Lifetime = must(time.ParseDuration(viper.GetString("auth_token_lifetime"))).(time.Duration)

	config.Mandrill.Key = viper.GetString("mandrill_key")
	config.Mandrill.FromEmail = viper.GetString("mandrill_from_email")
	config.Mandrill.FromName = viper.GetString("mandrill_from_name")

	config.Cache.NumCachedIncomes = viper.GetInt("num_cached_incomes")

	config.Template.EmailVerificationTemplate = viper.GetString("email_verification_template")

	// validate config
	must(nil, validateConfiguration(config))
}

func validateConfiguration(c configuration) error {
	validate := validator.New(&validator.Config{TagName: "validate"})
	must(nil, validate.RegisterValidation("dsn", dsnValidator))
	return validate.Struct(c)
}

func dsnValidator(v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	dsn, err := mysql.ParseDSN(field.String())
	return err == nil && dsn.ParseTime
}
<commit_msg>Update config validation<commit_after>package main

import (
	"reflect"
	"time"

	"gopkg.in/go-playground/validator.v8"

	"github.com/go-sql-driver/mysql"
	"github.com/spf13/viper"
)

type configuration struct {
	HTTP struct {
		Address string `validate:"required"`
		Mode    string `validate:"required,eq=release|eq=test|eq=debug"`
	} `validate:"required"`
	DB struct {
		DataSourceName string `validate:"required,dsn"`
		MaxOpenConns   int    `validate:"required,min=1"`
		MaxIdleConns   int    `validate:"required,min=1,ltefield=MaxOpenConns"`
	} `validate:"required"`
	AuthToken struct {
		Lifetime time.Duration `validate:"required"`
	} `validate:"required"`
	Mandrill struct {
		Key       string `validate:"required"`
		FromEmail string `validate:"required"`
		FromName  string `validate:"required"`
	} `validate:"required"`
	Cache struct {
		NumCachedIncomes int `validate:"required,gt=1"`
	} `validate:"required"`
	Template struct {
		EmailVerificationTemplate string `validate:"required"`
	} `validate:"required"`
}

var config configuration

func initConfig() {
	// env config
	viper.SetEnvPrefix("solebtc") // will turn into uppercase, e.g. SOLEBTC_PORT
	viper.AutomaticEnv()

	// set default
	viper.SetDefault("mode", "debug")
	viper.SetDefault("address", "0.0.0.0:3000")
	viper.SetDefault("dsn", "/solebtc_dev?parseTime=true")
	viper.SetDefault("auth_token_lifetime", "720h")
	viper.SetDefault("mandrill_key", "SANDBOX_SUCCESS")
	viper.SetDefault("mandrill_from_email", "no_reply@solebtc.com")
	viper.SetDefault("mandrill_from_name", "SoleBTC")
	viper.SetDefault("max_open_conns", 2)
	viper.SetDefault("max_idle_conns", 2)
	viper.SetDefault("num_cached_incomes", 20)
	viper.SetDefault("email_verification_template", "./templates/email_verification_staging.html")

	// See Viper doc, config is get in the following order
	// override, flag, env, config file, key/value store, default

	config.HTTP.Mode = viper.GetString("mode")
	config.HTTP.Address = viper.GetString("address")

	config.DB.DataSourceName = viper.GetString("dsn")
	config.DB.MaxOpenConns = viper.GetInt("max_open_conns")
	config.DB.MaxIdleConns = viper.GetInt("max_idle_conns")

	config.AuthToken.Lifetime = must(time.ParseDuration(viper.GetString("auth_token_lifetime"))).(time.Duration)

	config.Mandrill.Key = viper.GetString("mandrill_key")
	config.Mandrill.FromEmail = viper.GetString("mandrill_from_email")
	config.Mandrill.FromName = viper.GetString("mandrill_from_name")

	config.Cache.NumCachedIncomes = viper.GetInt("num_cached_incomes")

	config.Template.EmailVerificationTemplate = viper.GetString("email_verification_template")

	// validate config
	must(nil, validateConfiguration(config))
}

func validateConfiguration(c configuration) error {
	validate := validator.New(&validator.Config{TagName: "validate"})
	must(nil, validate.RegisterValidation("dsn", dsnValidator))
	return validate.Struct(c)
}

func dsnValidator(v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	dsn, err := mysql.ParseDSN(field.String())
	return err == nil && dsn.ParseTime
}
<|endoftext|>


<commit_before>package base62

import (
	"fmt"
	"math/big"
	"sort"
	"strconv"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

var result string

var testcases = []struct {
	num     int64
	encoded string
}{
	{1, "1"},
	{9, "9"},
	{10, "A"},
	{35, "Z"},
	{36, "a"},
	{61, "z"},
	{62, "10"},
	{99, "1b"},
	{3844, "100"},
	{3860, "10G"},
	{4815162342, "5Frvgk"},
	{9223372036854775807, "AzL8n0Y58m7"},
}

func TestEncodeInt64(t *testing.T) {
	for _, tc := range testcases {
		v := EncodeInt64(tc.num)
		t.Logf("Encoded %v as %s", tc.num, v)
		assert.Equal(t, tc.encoded, v)
	}
}

func TestDecodeToInt64(t *testing.T) {
	for _, tc := range testcases {
		v := DecodeToInt64(tc.encoded)
		t.Logf("Decoded %s to %v", tc.encoded, v)
		assert.Equal(t, tc.num, v)
	}
}

func BenchmarkEncodeInt64Medium(b *testing.B) {
	var id string
	for n := 0; n < b.N; n++ {
		id = EncodeInt64(4815162342)
	}
	result = id
}

func BenchmarkEncodeInt64Long(b *testing.B) {
	var id string
	for n := 0; n < b.N; n++ {
		id = EncodeInt64(9223372036854775807)
	}
	result = id
}

var bigTestcases = []struct {
	num     string
	encoded string
}{
	{"1", "1"},
	{"9", "9"},
	{"10", "A"},
	{"35", "Z"},
	{"36", "a"},
	{"61", "z"},
	{"62", "10"},
	{"99", "1b"},
	{"3844", "100"},
	{"3860", "10G"},
	{"4815162342", "5Frvgk"},

	{"9223372036854775807", "AzL8n0Y58m7"},       // max signed int64
	{"9223372036854775809", "AzL8n0Y58m9"},       // beyond int64
	{"9223372036854775861", "AzL8n0Y58mz"},       //
	{"18446744073709551615", "LygHa16AHYF"},      // max uint64
	{"571849066284996100034", "AzL8n0Y58m70"},    // max int64 * 62
	{"35454642109669758202168", "AzL8n0Y58m70y"}, // (max int64 * 62^2) + 60

	{"170141183460469231731687303715884105727", "3tX16dB2jpss4tZORYcqo3"}, // max signed 128bit int
	{"170141183460469231731687303715884105757", "3tX16dB2jpss4tZORYcqoX"}, // max signed 128bit int + 30
	{"340282366920938463463374607431768211455", "7n42DGM5Tflk9n8mt7Fhc7"}, // max unsigned 128bit int
}

func TestEncodeBigInt(t *testing.T) {
	for _, tc := range bigTestcases {
		var (
			n  *big.Int = new(big.Int)
			ok bool
		)

		n, ok = n.SetString(tc.num, 10)
		require.True(t, ok)

		v := EncodeBigInt(n)
		t.Logf("Encoded %v as %s", tc.num, v)
		assert.Equal(t, tc.encoded, v)
	}
}

func BenchmarkEncodeBigIntVeryLong(b *testing.B) {
	var (
		v *big.Int = new(big.Int)
		s string
	)
	v.SetString("340282366920938463463374607431768211455", 10)

	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		s = EncodeBigInt(v)
	}
	result = s
}

func TestDecodeToBigInt(t *testing.T) {
	for _, tc := range bigTestcases {
		v := DecodeToBigInt(tc.encoded)
		t.Logf("Decoded %v to %s", tc.encoded, v.String())
		assert.Equal(t, tc.num, v.String())
	}
}

// TestLexicalPaddedSort tests that numbers encoded as base62 strings
// are correctly lexically sorted with the original order preserved
// if these are left padded to the same length.
//
// An alternative sort method which could be used to avoid padding
// would be a Shortlex sort, which sorts by cardinality, then lexically.
func TestLexicalPaddedSort(t *testing.T) {

	var (
		lexicalOrder  sort.StringSlice = make([]string, 0)
		originalOrder                  = make([]string, 0)
	)

	// Generate lots of numbers, and encode them
	var i int64
	for i = 0; i < 100000; i++ {

		v := EncodeInt64(i)

		lexicalOrder = append(lexicalOrder, v)
		originalOrder = append(originalOrder, v)
	}

	// Find longest string & pad encoded strings to this length
	maxlen := len(originalOrder[len(originalOrder)-1])
	originalOrder = padStringArray(originalOrder, maxlen)
	lexicalOrder = padStringArray(lexicalOrder, maxlen)

	// Sort string array
	lexicalOrder.Sort()

	// Compare ordering with original
	var mismatch int64
	for i, v := range originalOrder {
		// t.Logf("%s %s", v, lexicalOrder[i])
		if lexicalOrder[i] != v {
			mismatch++
		}
	}
	assert.Equal(t, int64(0), mismatch, fmt.Sprintf("Expected zero mismatches, got %v", mismatch))
}

func padStringArray(s []string, maxlen int) []string {

	for i, v := range s {
		s[i] = pad(v, maxlen)
	}

	return s
}

func pad(s string, maxlen int) string {
	format := fmt.Sprint(`%0`, strconv.Itoa(maxlen), "s")
	return fmt.Sprintf(format, s)
}
<commit_msg>Add a modifier to lexical sort test to cover a much larger encoding space<commit_after>package base62

import (
	"fmt"
	"math/big"
	"sort"
	"strconv"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

var result string

var testcases = []struct {
	num     int64
	encoded string
}{
	{1, "1"},
	{9, "9"},
	{10, "A"},
	{35, "Z"},
	{36, "a"},
	{61, "z"},
	{62, "10"},
	{99, "1b"},
	{3844, "100"},
	{3860, "10G"},
	{4815162342, "5Frvgk"},
	{9223372036854775807, "AzL8n0Y58m7"},
}

func TestEncodeInt64(t *testing.T) {
	for _, tc := range testcases {
		v := EncodeInt64(tc.num)
		t.Logf("Encoded %v as %s", tc.num, v)
		assert.Equal(t, tc.encoded, v)
	}
}

func TestDecodeToInt64(t *testing.T) {
	for _, tc := range testcases {
		v := DecodeToInt64(tc.encoded)
		t.Logf("Decoded %s to %v", tc.encoded, v)
		assert.Equal(t, tc.num, v)
	}
}

func BenchmarkEncodeInt64Medium(b *testing.B) {
	var id string
	for n := 0; n < b.N; n++ {
		id = EncodeInt64(4815162342)
	}
	result = id
}

func BenchmarkEncodeInt64Long(b *testing.B) {
	var id string
	for n := 0; n < b.N; n++ {
		id = EncodeInt64(9223372036854775807)
	}
	result = id
}

var bigTestcases = []struct {
	num     string
	encoded string
}{
	{"1", "1"},
	{"9", "9"},
	{"10", "A"},
	{"35", "Z"},
	{"36", "a"},
	{"61", "z"},
	{"62", "10"},
	{"99", "1b"},
	{"3844", "100"},
	{"3860", "10G"},
	{"4815162342", "5Frvgk"},

	{"9223372036854775807", "AzL8n0Y58m7"},       // max signed int64
	{"9223372036854775809", "AzL8n0Y58m9"},       // beyond int64
	{"9223372036854775861", "AzL8n0Y58mz"},       //
	{"18446744073709551615", "LygHa16AHYF"},      // max uint64
	{"571849066284996100034", "AzL8n0Y58m70"},    // max int64 * 62
	{"35454642109669758202168", "AzL8n0Y58m70y"}, // (max int64 * 62^2) + 60

	{"170141183460469231731687303715884105727", "3tX16dB2jpss4tZORYcqo3"}, // max signed 128bit int
	{"170141183460469231731687303715884105757", "3tX16dB2jpss4tZORYcqoX"}, // max signed 128bit int + 30
	{"340282366920938463463374607431768211455", "7n42DGM5Tflk9n8mt7Fhc7"}, // max unsigned 128bit int
}

func TestEncodeBigInt(t *testing.T) {
	for _, tc := range bigTestcases {
		var (
			n  *big.Int = new(big.Int)
			ok bool
		)

		n, ok = n.SetString(tc.num, 10)
		require.True(t, ok)

		v := EncodeBigInt(n)
		t.Logf("Encoded %v as %s", tc.num, v)
		assert.Equal(t, tc.encoded, v)
	}
}

func BenchmarkEncodeBigIntVeryLong(b *testing.B) {
	var (
		v *big.Int = new(big.Int)
		s string
	)
	v.SetString("340282366920938463463374607431768211455", 10)

	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		s = EncodeBigInt(v)
	}
	result = s
}

func TestDecodeToBigInt(t *testing.T) {
	for _, tc := range bigTestcases {
		v := DecodeToBigInt(tc.encoded)
		t.Logf("Decoded %v to %s", tc.encoded, v.String())
		assert.Equal(t, tc.num, v.String())
	}
}

// TestLexicalPaddedSort tests that numbers encoded as base62 strings
// are correctly lexically sorted with the original order preserved
// if these are left padded to the same length.
//
// An alternative sort method which could be used to avoid padding
// would be a Shortlex sort, which sorts by cardinality, then lexically.
func TestLexicalPaddedSort(t *testing.T) {

	var (
		lexicalOrder  sort.StringSlice = make([]string, 0)
		originalOrder                  = make([]string, 0)
	)

	// Generate lots of numbers, and encode them
	var i int64
	var modifier int64 = 1
	for i = 0; i < 100000; i++ {
		if i%10000 == 0 {
			modifier = modifier * 30
		}

		v := EncodeInt64(i + modifier)

		lexicalOrder = append(lexicalOrder, v)
		originalOrder = append(originalOrder, v)
	}

	// Find longest string & pad encoded strings to this length
	maxlen := len(originalOrder[len(originalOrder)-1])
	originalOrder = padStringArray(originalOrder, maxlen)
	lexicalOrder = padStringArray(lexicalOrder, maxlen)

	// Sort string array
	lexicalOrder.Sort()

	// Compare ordering with original
	var mismatch int64
	for i, v := range originalOrder {
		// t.Logf("%s %s", v, lexicalOrder[i])
		if lexicalOrder[i] != v {
			mismatch++
		}
	}
	assert.Equal(t, int64(0), mismatch, fmt.Sprintf("Expected zero mismatches, got %v", mismatch))
}

func padStringArray(s []string, maxlen int) []string {

	for i, v := range s {
		s[i] = pad(v, maxlen)
	}

	return s
}

func pad(s string, maxlen int) string {
	format := fmt.Sprint(`%0`, strconv.Itoa(maxlen), "s")
	return fmt.Sprintf(format, s)
}
<|endoftext|>