<commit_before>package envh

import (
	"regexp"
	"strings"
)

// EnvTree manages environment variables through a tree structure
// to store a config the same way as in a yaml file or whatever
// format allows to store a config hierarchically
type EnvTree struct {
	root *node
}

// NewEnvTree creates an environment variable tree.
// A delimiter is used to split key, reg is a regexp
// used to filter entries
func NewEnvTree(reg string, delimiter string) (EnvTree, error) {
	r, err := regexp.Compile(reg)

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

	t := createTreeFromDelimiterFilteringByRegexp(r, delimiter)

	return EnvTree{t}, nil
}

// FindString returns a string if key chain exists
// or an error otherwise
func (e EnvTree) FindString(keyChain ...string) (string, error) {
	return getString(getNodeValueByKeyChain(e.root, &keyChain))
}

// FindStringUnsecured is insecured version of FindString to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the variable is missing, it returns default zero string value.
// This function has to be used carefully
func (e EnvTree) FindStringUnsecured(keyChain ...string) string {
	if val, err := getString(getNodeValueByKeyChain(e.root, &keyChain)); err == nil {
		return val
	}

	return ""
}

// FindInt returns an integer if key chain exists
// or an error if value is not an integer or doesn't exist
func (e EnvTree) FindInt(keyChain ...string) (int, error) {
	return getInt(getNodeValueByKeyChain(e.root, &keyChain))
}

// FindIntUnsecured is insecured version of FindInt to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the variable is missing or not an int value, it returns default zero int value.
// This function has to be used carefully
func (e EnvTree) FindIntUnsecured(keyChain ...string) int {
	if val, err := getInt(getNodeValueByKeyChain(e.root, &keyChain)); err == nil {
		return val
	}

	return 0
}

// FindFloat returns a float if key chain exists
// or an error if value is not a float or doesn't exist
func (e EnvTree) FindFloat(keyChain ...string) (float32, error) {
	return getFloat(getNodeValueByKeyChain(e.root, &keyChain))
}

// FindFloatUnsecured is insecured version of FindFloat to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the variable is missing or not a floating value, it returns default zero floating value.
// This function has to be used carefully
func (e EnvTree) FindFloatUnsecured(keyChain ...string) float32 {
	if val, err := getFloat(getNodeValueByKeyChain(e.root, &keyChain)); err == nil {
		return val
	}

	return 0
}

// FindBool returns a boolean if key chain exists
// or an error if value is not a boolean or doesn't exist
func (e EnvTree) FindBool(keyChain ...string) (bool, error) {
	return getBool(getNodeValueByKeyChain(e.root, &keyChain))
}

// FindBoolUnsecured is insecured version of FindBool to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the variable is missing or not a boolean value, it returns default zero boolean value.
// This function has to be used carefully
func (e EnvTree) FindBoolUnsecured(keyChain ...string) bool {
	if val, err := getBool(getNodeValueByKeyChain(e.root, &keyChain)); err == nil {
		return val
	}

	return false
}

// IsExistingSubTree returns true if key chain has a tree associated or false if not
func (e EnvTree) IsExistingSubTree(keyChain ...string) bool {
	_, exists := e.root.findNodeByKeyChain(&keyChain)

	return exists
}

// HasSubTreeValue returns true if key chain has a value or false if not.
// If sub node doesn't exist, it returns an error ErrNodeNotFound
// as second value
func (e EnvTree) HasSubTreeValue(keyChain ...string) (bool, error) {
	n, exists := e.root.findNodeByKeyChain(&keyChain)

	if !exists {
		return false, NodeNotFoundError{keyChain}
	}

	return n.hasValue, nil
}

// HasSubTreeValueUnsecured is insecured version of HasSubTreeValue to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the node doesn't exist, it returns false.
// This function has to be used carefully
func (e EnvTree) HasSubTreeValueUnsecured(keyChain ...string) bool {
	n, exists := e.root.findNodeByKeyChain(&keyChain)

	if !exists {
		return false
	}

	return n.hasValue
}

// FindSubTree returns underlying tree from key chain,
// for instance given A -> B -> C -> D tree,
// "A" "B" "C" key chain will return C sub tree.
// If no node is found, it returns an error ErrNodeNotFound as
// second value
func (e EnvTree) FindSubTree(keyChain ...string) (EnvTree, error) {
	if n, exists := e.root.findNodeByKeyChain(&keyChain); exists {
		return EnvTree{n}, nil
	}

	return EnvTree{}, NodeNotFoundError{keyChain}
}

// FindSubTreeUnsecured is insecured version of FindSubTree to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the node doesn't exist, it returns empty EnvTree.
// This function has to be used carefully
func (e EnvTree) FindSubTreeUnsecured(keyChain ...string) EnvTree {
	if n, exists := e.root.findNodeByKeyChain(&keyChain); exists {
		return EnvTree{n}
	}

	return EnvTree{}
}

// FindChildrenKeys returns all children keys for a given key chain.
// If sub node doesn't exist, it returns an error ErrNodeNotFound
// as second value
func (e EnvTree) FindChildrenKeys(keyChain ...string) ([]string, error) {
	n, exists := e.root.findNodeByKeyChain(&keyChain)

	if !exists {
		return []string{}, NodeNotFoundError{keyChain}
	}

	keys := []string{}

	for _, c := range n.children {
		keys = append(keys, c.key)
	}

	return keys, nil
}

// FindChildrenKeysUnsecured is insecured version of FindChildrenKeys to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the node doesn't exist, it returns empty string slice.
// This function has to be used carefully
func (e EnvTree) FindChildrenKeysUnsecured(keyChain ...string) []string {
	n, exists := e.root.findNodeByKeyChain(&keyChain)

	if !exists {
		return []string{}
	}

	keys := []string{}

	for _, c := range n.children {
		keys = append(keys, c.key)
	}

	return keys
}

// GetChildrenKeys retrieves all current tree children node keys
func (e EnvTree) GetChildrenKeys() []string {
	keys := []string{}

	for _, c := range e.root.children {
		keys = append(keys, c.key)
	}

	return keys
}

// GetString returns current tree value as string if value exists
// or an error as second parameter
func (e EnvTree) GetString() (string, error) {
	return getString(e.getValue())
}

// GetStringUnsecured is insecured version of GetString to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the variable is missing, it returns default zero string value.
// This function has to be used carefully
func (e EnvTree) GetStringUnsecured() string {
	if val, err := getString(e.getValue()); err == nil {
		return val
	}

	return ""
}

// GetInt returns current tree value as int if value exists
// or an error if value is not an integer or doesn't exist
func (e EnvTree) GetInt() (int, error) {
	return getInt(e.getValue())
}

// GetIntUnsecured is insecured version of GetInt to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the variable is missing or not an int value, it returns default zero int value.
// This function has to be used carefully
func (e EnvTree) GetIntUnsecured() int {
	if val, err := getInt(e.getValue()); err == nil {
		return val
	}

	return 0
}

// GetFloat returns current tree value as float if value exists
// or an error if value is not a float or doesn't exist
func (e EnvTree) GetFloat() (float32, error) {
	return getFloat(e.getValue())
}

// GetFloatUnsecured is insecured version of GetFloat to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the variable is missing or not a floating value, it returns default zero floating value.
// This function has to be used carefully
func (e EnvTree) GetFloatUnsecured() float32 {
	if val, err := getFloat(e.getValue()); err == nil {
		return val
	}

	return 0
}

// GetBool returns current tree value as boolean if value exists
// or an error if value is not a boolean or doesn't exist
func (e EnvTree) GetBool() (bool, error) {
	return getBool(e.getValue())
}

// GetBoolUnsecured is insecured version of GetBool to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the variable is missing or not a boolean value, it returns default zero boolean value.
// This function has to be used carefully
func (e EnvTree) GetBoolUnsecured() bool {
	if val, err := getBool(e.getValue()); err == nil {
		return val
	}

	return false
}

// HasValue returns true if current tree has a value defined
// false otherwise
func (e EnvTree) HasValue() bool {
	return e.root.hasValue
}

// GetKey returns current tree key
func (e EnvTree) GetKey() string {
	return e.root.key
}

// PopulateStruct fills a structure with datas extracted.
// Missing values are ignored and only type errors are reported.
// It's possible to control the way struct fields are defined
// implementing StructWalker interface on structure,
// checkout StructWalker documentation for further examples.
func (e EnvTree) PopulateStruct(structure interface{}) error {
	return populateStructFromEnvTree(structure, &e, false)
}

// PopulateStructWithStrictMode fills a structure with datas extracted.
// A missing environment variable returns an error and type errors are reported.
// It's possible to control the way struct fields are defined
// implementing StructWalker interface on structure,
// checkout StructWalker documentation for further examples.
func (e EnvTree) PopulateStructWithStrictMode(structure interface{}) error {
	return populateStructFromEnvTree(structure, &e, true)
}

func (e EnvTree) getValue() func() (string, bool) {
	return func() (string, bool) {
		if e.root.hasValue {
			return e.root.value, true
		}

		return "", false
	}
}

func createTreeFromDelimiterFilteringByRegexp(reg *regexp.Regexp, delimiter string) *node {
	rootNode := newNode()

	for key, value := range *parseVars() {
		if reg.MatchString(key) {
			current := rootNode

			for _, component := range strings.Split(key, delimiter) {
				n, exists := current.findNodeByKey(component)

				if exists {
					current = n
				} else {
					child := newNode()
					child.key = component
					current.appendNode(child)
					current = child
				}
			}

			current.hasValue = true
			current.value = value
		}
	}

	return rootNode
}

func getNodeValueByKeyChain(node *node, keyChain *[]string) func() (string, bool) {
	return func() (string, bool) {
		n, exists := node.findNodeByKeyChain(keyChain)

		if !exists {
			return "", false
		}

		return n.value, true
	}
}
<commit_msg>Move private function out of structure<commit_after>package envh

import (
	"regexp"
	"strings"
)

// EnvTree manages environment variables through a tree structure
// to store a config the same way as in a yaml file or whatever
// format allows to store a config hierarchically
type EnvTree struct {
	root *node
}

// NewEnvTree creates an environment variable tree.
// A delimiter is used to split key, reg is a regexp
// used to filter entries
func NewEnvTree(reg string, delimiter string) (EnvTree, error) {
	r, err := regexp.Compile(reg)

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

	t := createTreeFromDelimiterFilteringByRegexp(r, delimiter)

	return EnvTree{t}, nil
}

// FindString returns a string if key chain exists
// or an error otherwise
func (e EnvTree) FindString(keyChain ...string) (string, error) {
	return getString(getNodeValueByKeyChain(e.root, &keyChain))
}

// FindStringUnsecured is insecured version of FindString to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the variable is missing, it returns default zero string value.
// This function has to be used carefully
func (e EnvTree) FindStringUnsecured(keyChain ...string) string {
	if val, err := getString(getNodeValueByKeyChain(e.root, &keyChain)); err == nil {
		return val
	}

	return ""
}

// FindInt returns an integer if key chain exists
// or an error if value is not an integer or doesn't exist
func (e EnvTree) FindInt(keyChain ...string) (int, error) {
	return getInt(getNodeValueByKeyChain(e.root, &keyChain))
}

// FindIntUnsecured is insecured version of FindInt to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the variable is missing or not an int value, it returns default zero int value.
// This function has to be used carefully
func (e EnvTree) FindIntUnsecured(keyChain ...string) int {
	if val, err := getInt(getNodeValueByKeyChain(e.root, &keyChain)); err == nil {
		return val
	}

	return 0
}

// FindFloat returns a float if key chain exists
// or an error if value is not a float or doesn't exist
func (e EnvTree) FindFloat(keyChain ...string) (float32, error) {
	return getFloat(getNodeValueByKeyChain(e.root, &keyChain))
}

// FindFloatUnsecured is insecured version of FindFloat to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the variable is missing or not a floating value, it returns default zero floating value.
// This function has to be used carefully
func (e EnvTree) FindFloatUnsecured(keyChain ...string) float32 {
	if val, err := getFloat(getNodeValueByKeyChain(e.root, &keyChain)); err == nil {
		return val
	}

	return 0
}

// FindBool returns a boolean if key chain exists
// or an error if value is not a boolean or doesn't exist
func (e EnvTree) FindBool(keyChain ...string) (bool, error) {
	return getBool(getNodeValueByKeyChain(e.root, &keyChain))
}

// FindBoolUnsecured is insecured version of FindBool to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the variable is missing or not a boolean value, it returns default zero boolean value.
// This function has to be used carefully
func (e EnvTree) FindBoolUnsecured(keyChain ...string) bool {
	if val, err := getBool(getNodeValueByKeyChain(e.root, &keyChain)); err == nil {
		return val
	}

	return false
}

// IsExistingSubTree returns true if key chain has a tree associated or false if not
func (e EnvTree) IsExistingSubTree(keyChain ...string) bool {
	_, exists := e.root.findNodeByKeyChain(&keyChain)

	return exists
}

// HasSubTreeValue returns true if key chain has a value or false if not.
// If sub node doesn't exist, it returns an error ErrNodeNotFound
// as second value
func (e EnvTree) HasSubTreeValue(keyChain ...string) (bool, error) {
	n, exists := e.root.findNodeByKeyChain(&keyChain)

	if !exists {
		return false, NodeNotFoundError{keyChain}
	}

	return n.hasValue, nil
}

// HasSubTreeValueUnsecured is insecured version of HasSubTreeValue to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the node doesn't exist, it returns false.
// This function has to be used carefully
func (e EnvTree) HasSubTreeValueUnsecured(keyChain ...string) bool {
	n, exists := e.root.findNodeByKeyChain(&keyChain)

	if !exists {
		return false
	}

	return n.hasValue
}

// FindSubTree returns underlying tree from key chain,
// for instance given A -> B -> C -> D tree,
// "A" "B" "C" key chain will return C sub tree.
// If no node is found, it returns an error ErrNodeNotFound as
// second value
func (e EnvTree) FindSubTree(keyChain ...string) (EnvTree, error) {
	if n, exists := e.root.findNodeByKeyChain(&keyChain); exists {
		return EnvTree{n}, nil
	}

	return EnvTree{}, NodeNotFoundError{keyChain}
}

// FindSubTreeUnsecured is insecured version of FindSubTree to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the node doesn't exist, it returns empty EnvTree.
// This function has to be used carefully
func (e EnvTree) FindSubTreeUnsecured(keyChain ...string) EnvTree {
	if n, exists := e.root.findNodeByKeyChain(&keyChain); exists {
		return EnvTree{n}
	}

	return EnvTree{}
}

// FindChildrenKeys returns all children keys for a given key chain.
// If sub node doesn't exist, it returns an error ErrNodeNotFound
// as second value
func (e EnvTree) FindChildrenKeys(keyChain ...string) ([]string, error) {
	n, exists := e.root.findNodeByKeyChain(&keyChain)

	if !exists {
		return []string{}, NodeNotFoundError{keyChain}
	}

	keys := []string{}

	for _, c := range n.children {
		keys = append(keys, c.key)
	}

	return keys, nil
}

// FindChildrenKeysUnsecured is insecured version of FindChildrenKeys to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the node doesn't exist, it returns empty string slice.
// This function has to be used carefully
func (e EnvTree) FindChildrenKeysUnsecured(keyChain ...string) []string {
	n, exists := e.root.findNodeByKeyChain(&keyChain)

	if !exists {
		return []string{}
	}

	keys := []string{}

	for _, c := range n.children {
		keys = append(keys, c.key)
	}

	return keys
}

// GetChildrenKeys retrieves all current tree children node keys
func (e EnvTree) GetChildrenKeys() []string {
	keys := []string{}

	for _, c := range e.root.children {
		keys = append(keys, c.key)
	}

	return keys
}

// GetString returns current tree value as string if value exists
// or an error as second parameter
func (e EnvTree) GetString() (string, error) {
	return getString(getRootValue(e))
}

// GetStringUnsecured is insecured version of GetString to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the variable is missing, it returns default zero string value.
// This function has to be used carefully
func (e EnvTree) GetStringUnsecured() string {
	if val, err := getString(getRootValue(e)); err == nil {
		return val
	}

	return ""
}

// GetInt returns current tree value as int if value exists
// or an error if value is not an integer or doesn't exist
func (e EnvTree) GetInt() (int, error) {
	return getInt(getRootValue(e))
}

// GetIntUnsecured is insecured version of GetInt to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the variable is missing or not an int value, it returns default zero int value.
// This function has to be used carefully
func (e EnvTree) GetIntUnsecured() int {
	if val, err := getInt(getRootValue(e)); err == nil {
		return val
	}

	return 0
}

// GetFloat returns current tree value as float if value exists
// or an error if value is not a float or doesn't exist
func (e EnvTree) GetFloat() (float32, error) {
	return getFloat(getRootValue(e))
}

// GetFloatUnsecured is insecured version of GetFloat to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the variable is missing or not a floating value, it returns default zero floating value.
// This function has to be used carefully
func (e EnvTree) GetFloatUnsecured() float32 {
	if val, err := getFloat(getRootValue(e)); err == nil {
		return val
	}

	return 0
}

// GetBool returns current tree value as boolean if value exists
// or an error if value is not a boolean or doesn't exist
func (e EnvTree) GetBool() (bool, error) {
	return getBool(getRootValue(e))
}

// GetBoolUnsecured is insecured version of GetBool to avoid the burden
// of rechecking errors if it was done already. If any errors occurred cause
// the variable is missing or not a boolean value, it returns default zero boolean value.
// This function has to be used carefully
func (e EnvTree) GetBoolUnsecured() bool {
	if val, err := getBool(getRootValue(e)); err == nil {
		return val
	}

	return false
}

// HasValue returns true if current tree has a value defined
// false otherwise
func (e EnvTree) HasValue() bool {
	return e.root.hasValue
}

// GetKey returns current tree key
func (e EnvTree) GetKey() string {
	return e.root.key
}

// PopulateStruct fills a structure with datas extracted.
// Missing values are ignored and only type errors are reported.
// It's possible to control the way struct fields are defined
// implementing StructWalker interface on structure,
// checkout StructWalker documentation for further examples.
func (e EnvTree) PopulateStruct(structure interface{}) error {
	return populateStructFromEnvTree(structure, &e, false)
}

// PopulateStructWithStrictMode fills a structure with datas extracted.
// A missing environment variable returns an error and type errors are reported.
// It's possible to control the way struct fields are defined
// implementing StructWalker interface on structure,
// checkout StructWalker documentation for further examples.
func (e EnvTree) PopulateStructWithStrictMode(structure interface{}) error {
	return populateStructFromEnvTree(structure, &e, true)
}

func getRootValue(tree EnvTree) func() (string, bool) {
	return func() (string, bool) {
		if tree.root.hasValue {
			return tree.root.value, true
		}

		return "", false
	}
}

func createTreeFromDelimiterFilteringByRegexp(reg *regexp.Regexp, delimiter string) *node {
	rootNode := newNode()

	for key, value := range *parseVars() {
		if reg.MatchString(key) {
			current := rootNode

			for _, component := range strings.Split(key, delimiter) {
				n, exists := current.findNodeByKey(component)

				if exists {
					current = n
				} else {
					child := newNode()
					child.key = component
					current.appendNode(child)
					current = child
				}
			}

			current.hasValue = true
			current.value = value
		}
	}

	return rootNode
}

func getNodeValueByKeyChain(node *node, keyChain *[]string) func() (string, bool) {
	return func() (string, bool) {
		n, exists := node.findNodeByKeyChain(keyChain)

		if !exists {
			return "", false
		}

		return n.value, true
	}
}
<|endoftext|>


<commit_before>package secretservice

import (
	"testing"

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

func TestNewKeypair(t *testing.T) {
	group := rfc2409SecondOakleyGroup()
	private, public, err := group.NewKeypair()
	require.NoError(t, err)
	require.NotNil(t, private)
	require.NotNil(t, public)
	private2, public2, err := group.NewKeypair()
	require.NotEqual(t, private.Cmp(private2), 0, "should get different private key with every keygen")
	require.NotEqual(t, public.Cmp(public2), 0, "should get different public key with every keygen")
}

func TestKeygen(t *testing.T) {
	group := rfc2409SecondOakleyGroup()
	myPrivate, myPublic, err := group.NewKeypair()
	require.NoError(t, err)
	theirPrivate, theirPublic, err := group.NewKeypair()
	require.NoError(t, err)

	myKey, err := group.keygenHKDFSHA256AES128(theirPublic, myPrivate)
	theirKey, err := group.keygenHKDFSHA256AES128(myPublic, theirPrivate)
	require.Equal(t, myKey, theirKey)
}

func TestEncryption(t *testing.T) {
	key := []byte("YELLOW SUBMARINE")
	plaintext := []byte("hello world")
	iv, ciphertext, err := unauthenticatedAESCBCEncrypt(plaintext, key)
	require.NoError(t, err)
	gotPlaintext, err := unauthenticatedAESCBCDecrypt(iv, ciphertext, key)
	require.NoError(t, err)
	require.Equal(t, plaintext, gotPlaintext)
}

var pkcs7tests = []struct {
	in  []byte
	out []byte
}{
	{[]byte{}, []byte{4, 4, 4, 4}},
	{[]byte{1, 2}, []byte{1, 2, 2, 2}},
	{[]byte{1, 2, 3}, []byte{1, 2, 3, 1}},
	{[]byte{1, 2, 3, 4}, []byte{1, 2, 3, 4, 4, 4, 4, 4}},
	{[]byte{1, 2, 3, 4, 5}, []byte{1, 2, 3, 4, 5, 3, 3, 3}},
	{[]byte{1, 2, 3, 4, 1, 1, 1}, []byte{1, 2, 3, 4, 1, 1, 1, 1}},
}

func TestPKCS7(t *testing.T) {
	for _, testCase := range pkcs7tests {
		require.Equal(t, padPKCS7(testCase.in, 4), testCase.out)
		preimage, err := unpadPKCS7(testCase.out, 4)
		require.NoError(t, err)
		require.Equal(t, preimage, testCase.in)
	}

	_, err := unpadPKCS7([]byte{}, 4)
	require.Error(t, err)
	_, err = unpadPKCS7([]byte{1, 2, 3, 4}, 4)
	require.Error(t, err)
	_, err = unpadPKCS7([]byte{1, 2, 3, 3}, 4)
	require.Error(t, err)
	_, err = unpadPKCS7([]byte{1, 2, 3, 4, 1, 1, 1, 2}, 4)
	require.Error(t, err)
}
<commit_msg>Add test for random iv<commit_after>package secretservice

import (
	"testing"

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

func TestNewKeypair(t *testing.T) {
	group := rfc2409SecondOakleyGroup()
	private, public, err := group.NewKeypair()
	require.NoError(t, err)
	require.NotNil(t, private)
	require.NotNil(t, public)
	private2, public2, err := group.NewKeypair()
	require.NotEqual(t, private.Cmp(private2), 0, "should get different private key with every keygen")
	require.NotEqual(t, public.Cmp(public2), 0, "should get different public key with every keygen")
}

func TestKeygen(t *testing.T) {
	group := rfc2409SecondOakleyGroup()
	myPrivate, myPublic, err := group.NewKeypair()
	require.NoError(t, err)
	theirPrivate, theirPublic, err := group.NewKeypair()
	require.NoError(t, err)

	myKey, err := group.keygenHKDFSHA256AES128(theirPublic, myPrivate)
	theirKey, err := group.keygenHKDFSHA256AES128(myPublic, theirPrivate)
	require.Equal(t, myKey, theirKey)
}

func TestEncryption(t *testing.T) {
	key := []byte("YELLOW SUBMARINE")
	plaintext := []byte("hello world")
	iv, ciphertext, err := unauthenticatedAESCBCEncrypt(plaintext, key)
	require.NoError(t, err)
	gotPlaintext, err := unauthenticatedAESCBCDecrypt(iv, ciphertext, key)
	require.NoError(t, err)
	require.Equal(t, plaintext, gotPlaintext)
}

func TestEncryptionRng(t *testing.T) {
	key := []byte("YELLOW SUBMARINE")
	plaintext := []byte("hello world")
	iv1, ciphertext1, err := unauthenticatedAESCBCEncrypt(plaintext, key)
	require.NoError(t, err)
	iv2, ciphertext2, err := unauthenticatedAESCBCEncrypt(plaintext, key)
	require.NoError(t, err)
	require.NotEqual(t, iv1, iv2)
	require.NotEqual(t, ciphertext1, ciphertext2)
}

var pkcs7tests = []struct {
	in  []byte
	out []byte
}{
	{[]byte{}, []byte{4, 4, 4, 4}},
	{[]byte{1, 2}, []byte{1, 2, 2, 2}},
	{[]byte{1, 2, 3}, []byte{1, 2, 3, 1}},
	{[]byte{1, 2, 3, 4}, []byte{1, 2, 3, 4, 4, 4, 4, 4}},
	{[]byte{1, 2, 3, 4, 5}, []byte{1, 2, 3, 4, 5, 3, 3, 3}},
	{[]byte{1, 2, 3, 4, 1, 1, 1}, []byte{1, 2, 3, 4, 1, 1, 1, 1}},
}

func TestPKCS7(t *testing.T) {
	for _, testCase := range pkcs7tests {
		require.Equal(t, padPKCS7(testCase.in, 4), testCase.out)
		preimage, err := unpadPKCS7(testCase.out, 4)
		require.NoError(t, err)
		require.Equal(t, preimage, testCase.in)
	}

	_, err := unpadPKCS7([]byte{}, 4)
	require.Error(t, err)
	_, err = unpadPKCS7([]byte{1, 2, 3, 4}, 4)
	require.Error(t, err)
	_, err = unpadPKCS7([]byte{1, 2, 3, 3}, 4)
	require.Error(t, err)
	_, err = unpadPKCS7([]byte{1, 2, 3, 4, 1, 1, 1, 2}, 4)
	require.Error(t, err)
}
<|endoftext|>


<commit_before>/*
 * Bitcoin RPC return types.
 *
 * (c) 2011-2013 Bernd Fix   >Y<
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package rpc

///////////////////////////////////////////////////////////////////////
// public types

//---------------------------------------------------------------------
/*
 * Generic infomation about running Bitcoin server.
 */
type Info struct {
	Version         int
	ProtocolVersion int
	WalletVersion   int
	Proxy           string
	TestNet         bool
	Connections     int
	KeyPoolSize     int
	TimeOffset      int
	KeyPoolOldest   int
	Balance         float64
	Errors          string
	PayTxFee        float64
	Difficulty      float64
	Blocks          int
}

//---------------------------------------------------------------------
/*
 * Block (element of the Bitcoin blockchain)
 */
type Block struct {
	IdList            []string
	Time              int
	Height            int
	Nonce             int
	Confirmations     int
	Hash              string
	PreviousBlockHash string
	NextBlockHash     string
	Bits              string
	Difficulty        int
	MerkleRoot        string
	Version           int
	Size              int
}

//---------------------------------------------------------------------
/*
 * Bitcoin transaction
 */
type Transaction struct {
	Amount        float64
	Fee           float64
	BlockIndex    int
	Confirmations int
	Id            string
	BlockHash     string
	Time          int
	BlockTime     int
	TimeReceived  int
}

//---------------------------------------------------------------------
/*
 * Raw transaction input slot
 */
type Vinput struct {
	Id        string
	Vout      int
	ScriptSig string
	Sequence  int
}

//---------------------------------------------------------------------
/*
 * Raw transaction output slot
 */
type Voutput struct {
	Value        float64
	N            int
	ScriptPubkey string
	ReqSigs      int
	Type         string
	Addresses    []string
}

//---------------------------------------------------------------------
/*
 * Bitcoin transaction
 */
type RawTransaction struct {
	Id       string
	Version  int
	LockTime int
	Vin      []Vinput
	Vout     []Voutput
}

//---------------------------------------------------------------------
/*
 * Transaction output
 */
type Output struct {
	Id   string
	Vout int
}

//---------------------------------------------------------------------
/*
 * Unspent transactions for accounts
 */
type Unspent struct {
	Output
	ScriptPubkey  string
	Amount        float64
	Confirmations int
}

//---------------------------------------------------------------------
/*
 * Received transactions for account/address (accumulated)
 */
type Received struct {
	Account       string
	Label         string
	Address       string
	Amount        float64
	Confirmations int
}

//---------------------------------------------------------------------
/*
 * Validity check on address
 */
type Validity struct {
	Address      string
	IsCompressed bool
	Account      string
	PubKey       string
	IsMine       bool
	IsValid      bool
}

//---------------------------------------------------------------------
/*
 * Balance of Bitcoin address (used for outgoing transactions as well)
 */
type Balance struct {
	Address string
	Amount  float64
}
<commit_msg>Handle scripts (pubKey, redeeem) in transactions.<commit_after>/*
 * Bitcoin RPC return types.
 *
 * (c) 2011-2013 Bernd Fix   >Y<
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package rpc

///////////////////////////////////////////////////////////////////////
// public types

//---------------------------------------------------------------------
/*
 * Generic infomation about running Bitcoin server.
 */
type Info struct {
	Version         int
	ProtocolVersion int
	WalletVersion   int
	Proxy           string
	TestNet         bool
	Connections     int
	KeyPoolSize     int
	TimeOffset      int
	KeyPoolOldest   int
	Balance         float64
	Errors          string
	PayTxFee        float64
	Difficulty      float64
	Blocks          int
}

//---------------------------------------------------------------------
/*
 * Block (element of the Bitcoin blockchain)
 */
type Block struct {
	IdList            []string
	Time              int
	Height            int
	Nonce             int
	Confirmations     int
	Hash              string
	PreviousBlockHash string
	NextBlockHash     string
	Bits              string
	Difficulty        int
	MerkleRoot        string
	Version           int
	Size              int
}

//---------------------------------------------------------------------
/*
 * Bitcoin transaction
 */
type Transaction struct {
	Amount        float64
	Fee           float64
	BlockIndex    int
	Confirmations int
	Id            string
	BlockHash     string
	Time          int
	BlockTime     int
	TimeReceived  int
}

//---------------------------------------------------------------------
/*
 * Raw transaction input slot
 */
type Vinput struct {
	Id        string
	Vout      int
	ScriptSig string
	Sequence  int
}

//---------------------------------------------------------------------
/*
 * Raw transaction output slot
 */
type Voutput struct {
	Value        float64
	N            int
	ScriptPubKey string
	ReqSigs      int
	Type         string
	Addresses    []string
}

//---------------------------------------------------------------------
/*
 * Bitcoin transaction
 */
type RawTransaction struct {
	Id       string
	Version  int
	LockTime int
	Vin      []Vinput
	Vout     []Voutput
}

//---------------------------------------------------------------------
/*
 * Transaction output
 */
type Output struct {
	Id           string
	Vout         int
	ScriptPubKey string
	RedeemScript string
}

//---------------------------------------------------------------------
/*
 * Unspent transactions for accounts
 */
type Unspent struct {
	Output
	Amount        float64
	Confirmations int
}

//---------------------------------------------------------------------
/*
 * Received transactions for account/address (accumulated)
 */
type Received struct {
	Account       string
	Label         string
	Address       string
	Amount        float64
	Confirmations int
}

//---------------------------------------------------------------------
/*
 * Validity check on address
 */
type Validity struct {
	Address      string
	IsCompressed bool
	Account      string
	PubKey       string
	IsMine       bool
	IsValid      bool
}

//---------------------------------------------------------------------
/*
 * Balance of Bitcoin address (used for outgoing transactions as well)
 */
type Balance struct {
	Address string
	Amount  float64
}
<|endoftext|>


<commit_before>package oauth2

import (
	"encoding/json"
	"net/http"

	"github.com/influxdata/chronograf"

	"golang.org/x/oauth2"
	hrk "golang.org/x/oauth2/heroku"
)

// Ensure that Heroku is an oauth2.Provider
var _ Provider = &Heroku{}

const (
	// HerokuAccountRoute is required for interacting with Heroku API
	HerokuAccountRoute string = "https://api.heroku.com/account"
)

// Heroku is an OAuth2 Provider allowing users to authenticate with Heroku to
// gain access to Chronograf
type Heroku struct {
	// OAuth2 Secrets
	ClientID     string
	ClientSecret string

	Organizations []string // set of organizations permitted to access the protected resource. Empty means "all"

	Logger chronograf.Logger
}

// Config returns the OAuth2 exchange information and endpoints
func (h *Heroku) Config() *oauth2.Config {
	return &oauth2.Config{
		ClientID:     h.ID(),
		ClientSecret: h.Secret(),
		Scopes:       h.Scopes(),
		Endpoint:     hrk.Endpoint,
	}
}

// ID returns the Heroku application client ID
func (h *Heroku) ID() string {
	return h.ClientID
}

// Name returns the name of this provider (heroku)
func (h *Heroku) Name() string {
	return "heroku"
}

// PrincipalID returns the Heroku email address of the user.
func (h *Heroku) PrincipalID(provider *http.Client) (string, error) {
	type DefaultOrg struct {
		ID   string `json:"id"`
		Name string `json:"name"`
	}
	type Account struct {
		Email               string     `json:"email"`
		DefaultOrganization DefaultOrg `json:"default_organization"`
	}

	resp, err := provider.Get(HerokuAccountRoute)
	if err != nil {
		h.Logger.Error("Unable to communicate with Heroku. err:", err)
		return "", err
	}
	defer resp.Body.Close()
	d := json.NewDecoder(resp.Body)

	var account Account
	if err := d.Decode(&account); err != nil {
		h.Logger.Error("Unable to decode response from Heroku. err:", err)
		return "", err
	}

	// check if member of org
	if len(h.Organizations) > 0 {
		for _, org := range h.Organizations {
			if account.DefaultOrganization.Name == org {
				return account.Email, nil
			}
		}
		h.Logger.Error(ErrOrgMembership)
		return "", ErrOrgMembership
	}
	return account.Email, nil
}

// Group returns the Heroku organization that user belongs to.
func (h *Heroku) Group(provider *http.Client) (string, error) {
	type DefaultOrg struct {
		ID   string `json:"id"`
		Name string `json:"name"`
	}
	type Account struct {
		Email               string     `json:"email"`
		DefaultOrganization DefaultOrg `json:"default_organization"`
	}

	resp, err := provider.Get(HerokuAccountRoute)
	if err != nil {
		h.Logger.Error("Unable to communicate with Heroku. err:", err)
		return "", err
	}
	defer resp.Body.Close()
	d := json.NewDecoder(resp.Body)

	var account Account
	if err := d.Decode(&account); err != nil {
		h.Logger.Error("Unable to decode response from Heroku. err:", err)
		return "", err
	}

	return account.DefaultOrganization.Name, nil
}

// Scopes for heroku is "identity" which grants access to user account
// information. This will grant us access to the user's email address which is
// used as the Principal's identifier.
func (h *Heroku) Scopes() []string {
	return []string{"identity"}
}

// Secret returns the Heroku application client secret
func (h *Heroku) Secret() string {
	return h.ClientSecret
}
<commit_msg>Fix Heroku OAuth by adding required HTTP req header to API GET<commit_after>package oauth2

import (
	"encoding/json"
	"net/http"

	"github.com/influxdata/chronograf"

	"golang.org/x/oauth2"
	hrk "golang.org/x/oauth2/heroku"
)

// Ensure that Heroku is an oauth2.Provider
var _ Provider = &Heroku{}

const (
	// HerokuAccountRoute is required for interacting with Heroku API
	HerokuAccountRoute string = "https://api.heroku.com/account"
)

// Heroku is an OAuth2 Provider allowing users to authenticate with Heroku to
// gain access to Chronograf
type Heroku struct {
	// OAuth2 Secrets
	ClientID     string
	ClientSecret string

	Organizations []string // set of organizations permitted to access the protected resource. Empty means "all"

	Logger chronograf.Logger
}

// Config returns the OAuth2 exchange information and endpoints
func (h *Heroku) Config() *oauth2.Config {
	return &oauth2.Config{
		ClientID:     h.ID(),
		ClientSecret: h.Secret(),
		Scopes:       h.Scopes(),
		Endpoint:     hrk.Endpoint,
	}
}

// ID returns the Heroku application client ID
func (h *Heroku) ID() string {
	return h.ClientID
}

// Name returns the name of this provider (heroku)
func (h *Heroku) Name() string {
	return "heroku"
}

// PrincipalID returns the Heroku email address of the user.
func (h *Heroku) PrincipalID(provider *http.Client) (string, error) {
	type DefaultOrg struct {
		ID   string `json:"id"`
		Name string `json:"name"`
	}
	type Account struct {
		Email               string     `json:"email"`
		DefaultOrganization DefaultOrg `json:"default_organization"`
	}

	req, err := http.NewRequest("GET", HerokuAccountRoute, nil)
	// Requests fail to Heroku unless this Accept header is set.
	req.Header.Set("Accept", "application/vnd.heroku+json; version=3")
	resp, err := provider.Do(req)
	if err != nil {
		h.Logger.Error("Unable to communicate with Heroku. err:", err)
		return "", err
	}
	defer resp.Body.Close()
	d := json.NewDecoder(resp.Body)

	var account Account
	if err := d.Decode(&account); err != nil {
		h.Logger.Error("Unable to decode response from Heroku. err:", err)
		return "", err
	}

	// check if member of org
	if len(h.Organizations) > 0 {
		for _, org := range h.Organizations {
			if account.DefaultOrganization.Name == org {
				return account.Email, nil
			}
		}
		h.Logger.Error(ErrOrgMembership)
		return "", ErrOrgMembership
	}
	return account.Email, nil
}

// Group returns the Heroku organization that user belongs to.
func (h *Heroku) Group(provider *http.Client) (string, error) {
	type DefaultOrg struct {
		ID   string `json:"id"`
		Name string `json:"name"`
	}
	type Account struct {
		Email               string     `json:"email"`
		DefaultOrganization DefaultOrg `json:"default_organization"`
	}

	resp, err := provider.Get(HerokuAccountRoute)
	if err != nil {
		h.Logger.Error("Unable to communicate with Heroku. err:", err)
		return "", err
	}
	defer resp.Body.Close()
	d := json.NewDecoder(resp.Body)

	var account Account
	if err := d.Decode(&account); err != nil {
		h.Logger.Error("Unable to decode response from Heroku. err:", err)
		return "", err
	}

	return account.DefaultOrganization.Name, nil
}

// Scopes for heroku is "identity" which grants access to user account
// information. This will grant us access to the user's email address which is
// used as the Principal's identifier.
func (h *Heroku) Scopes() []string {
	return []string{"identity"}
}

// Secret returns the Heroku application client secret
func (h *Heroku) Secret() string {
	return h.ClientSecret
}
<|endoftext|>


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

package openweathermap

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
)

// IconData holds the relevant info for linking icons to conditions.
type IconData struct {
	Condition string
	Day       string
	Night     string
}

// ConditionData holds data structure for weather conditions information.
type ConditionData struct {
	ID      int
	Meaning string
	Icon1   string
	Icon2   string
}

// RetrieveIcon will get the specified icon from the API.
func RetrieveIcon(destination, iconFile string) (int64, error) {
	fullFilePath := fmt.Sprintf("%s/%s", destination, iconFile)

	// Check to see if we've already gotten that icon file.  If so, use it rather
	// than getting it again.
	if _, err := os.Stat(fullFilePath); err != nil {
		response, err := http.Get(fmt.Sprintf(iconURL, iconFile))
		if err != nil {
			return 0, err
		}
		defer response.Body.Close()

		// Create the icon file
		out, err := os.Create(fullFilePath)
		if err != nil {
			return 0, err
		}
		defer out.Close()

		// Fill the empty file with the actual content
		n, err := io.Copy(out, response.Body)
		if err != nil {
			return 0, err
		}
		return n, nil
	}
	return 0, nil
}

// IconList is a slice of IconData pointers
var IconList = []*IconData{
	&IconData{Condition: "clear sky", Day: "01d.png", Night: "01n.png"},
	&IconData{Condition: "few clouds", Day: "02d.png", Night: "02n.png"},
	&IconData{Condition: "scattered clouds", Day: "03d.png", Night: "03n.png"},
	&IconData{Condition: "broken clouds", Day: "04d.png", Night: "04n.png"},
	&IconData{Condition: "shower rain", Day: "09d.png", Night: "09n.png"},
	&IconData{Condition: "rain", Day: "10d.png", Night: "10n.png"},
	&IconData{Condition: "thunderstorm", Day: "11d.png", Night: "11n.png"},
	&IconData{Condition: "snow", Day: "13d.png", Night: "13n.png"},
	&IconData{Condition: "mist", Day: "50d.png", Night: "50n.png"},
}

// ThunderstormConditions is a slice of ConditionData pointers
var ThunderstormConditions = []*ConditionData{
	&ConditionData{ID: 200, Meaning: "thunderstorm with light rain", Icon1: "11d.png"},
	&ConditionData{ID: 201, Meaning: "thunderstorm with rain", Icon1: "11d.png"},
	&ConditionData{ID: 202, Meaning: "thunderstorm with heavy rain", Icon1: "11d.png"},
	&ConditionData{ID: 210, Meaning: "light thunderstorm", Icon1: "11d.png"},
	&ConditionData{ID: 211, Meaning: "thunderstorm", Icon1: "11d.png"},
	&ConditionData{ID: 212, Meaning: "heavy thunderstorm", Icon1: "11d.png"},
	&ConditionData{ID: 221, Meaning: "ragged thunderstorm", Icon1: "11d.png"},
	&ConditionData{ID: 230, Meaning: "thunderstorm with light drizzle", Icon1: "11d.png"},
	&ConditionData{ID: 231, Meaning: "thunderstorm with drizzle", Icon1: "11d.png"},
	&ConditionData{ID: 232, Meaning: "thunderstorm with heavy drizzle", Icon1: "11d.png"},
}

// DrizzleConditions is a slice of ConditionData pointers
var DrizzleConditions = []*ConditionData{
	&ConditionData{ID: 300, Meaning: "light intensity drizzle", Icon1: "09d.png"},
	&ConditionData{ID: 301, Meaning: "drizzle", Icon1: "09d.png"},
	&ConditionData{ID: 302, Meaning: "heavy intensity drizzle", Icon1: "09d.png"},
	&ConditionData{ID: 310, Meaning: "light intensity drizzle rain", Icon1: "09d.png"},
	&ConditionData{ID: 311, Meaning: "drizzle rain", Icon1: "09d.png"},
	&ConditionData{ID: 312, Meaning: "heavy intensity drizzle rain", Icon1: "09d.png"},
	&ConditionData{ID: 313, Meaning: "shower rain and drizzle", Icon1: "09d.png"},
	&ConditionData{ID: 314, Meaning: "heavy shower rain and drizzle", Icon1: "09d.png"},
	&ConditionData{ID: 321, Meaning: "shower drizzle", Icon1: "09d.png"},
}

// RainConditions is a slice of ConditionData pointers
var RainConditions = []*ConditionData{
	&ConditionData{ID: 500, Meaning: "light rain", Icon1: "09d.png"},
	&ConditionData{ID: 501, Meaning: "moderate rain", Icon1: "09d.png"},
	&ConditionData{ID: 502, Meaning: "heavy intensity rain", Icon1: "09d.png"},
	&ConditionData{ID: 503, Meaning: "very heavy rain", Icon1: "09d.png"},
	&ConditionData{ID: 504, Meaning: "extreme rain", Icon1: "09d.png"},
	&ConditionData{ID: 511, Meaning: "freezing rain", Icon1: "13d.png"},
	&ConditionData{ID: 520, Meaning: "light intensity shower rain", Icon1: "09d.png"},
	&ConditionData{ID: 521, Meaning: "shower rain", Icon1: "09d.png"},
	&ConditionData{ID: 522, Meaning: "heavy intensity shower rain", Icon1: "09d.png"},
	&ConditionData{ID: 531, Meaning: "ragged shower rain", Icon1: "09d.png"},
}

// SnowConditions is a slice of ConditionData pointers
var SnowConditions = []*ConditionData{
	&ConditionData{ID: 600, Meaning: "light snow", Icon1: "13d.png"},
	&ConditionData{ID: 601, Meaning: "snow", Icon1: "13d.png"},
	&ConditionData{ID: 602, Meaning: "heavy snow", Icon1: "13d.png"},
	&ConditionData{ID: 611, Meaning: "sleet", Icon1: "13d.png"},
	&ConditionData{ID: 612, Meaning: "shower sleet", Icon1: "13d.png"},
	&ConditionData{ID: 615, Meaning: "light rain and snow", Icon1: "13d.png"},
	&ConditionData{ID: 616, Meaning: "rain and snow", Icon1: "13d.png"},
	&ConditionData{ID: 620, Meaning: "light shower snow", Icon1: "13d.png"},
	&ConditionData{ID: 621, Meaning: "shower snow", Icon1: "13d.png"},
	&ConditionData{ID: 622, Meaning: "heavy shower snow", Icon1: "13d.png"},
}

// AtmosphereConditions is a slice of ConditionData pointers
var AtmosphereConditions = []*ConditionData{
	&ConditionData{ID: 701, Meaning: "mist", Icon1: "50d.png"},
	&ConditionData{ID: 711, Meaning: "smoke", Icon1: "50d.png"},
	&ConditionData{ID: 721, Meaning: "haze", Icon1: "50d.png"},
	&ConditionData{ID: 731, Meaning: "sand, dust whirls", Icon1: "50d.png"},
	&ConditionData{ID: 741, Meaning: "fog", Icon1: "50d.png"},
	&ConditionData{ID: 751, Meaning: "sand", Icon1: "50d.png"},
	&ConditionData{ID: 761, Meaning: "dust", Icon1: "50d.png"},
	&ConditionData{ID: 762, Meaning: "volcanic ash", Icon1: "50d.png"},
	&ConditionData{ID: 771, Meaning: "squalls", Icon1: "50d.png"},
	&ConditionData{ID: 781, Meaning: "tornado", Icon1: "50d.png"},
}

// CloudConditions is a slice of ConditionData pointers
var CloudConditions = []*ConditionData{
	&ConditionData{ID: 800, Meaning: "clear sky", Icon1: "01d.png", Icon2: "01n.png"},
	&ConditionData{ID: 801, Meaning: "few clouds", Icon1: "02d.png", Icon2: " 02n.png"},
	&ConditionData{ID: 802, Meaning: "scattered clouds", Icon1: "03d.png", Icon2: "03d.png"},
	&ConditionData{ID: 803, Meaning: "broken clouds", Icon1: "04d.png", Icon2: "03d.png"},
	&ConditionData{ID: 804, Meaning: "overcast clouds", Icon1: "04d.png", Icon2: "04d.png"},
}

// ExtremeConditions is a slice of ConditionData pointers
var ExtremeConditions = []*ConditionData{
	&ConditionData{ID: 900, Meaning: "tornado", Icon1: ""},
	&ConditionData{ID: 901, Meaning: "tropical storm", Icon1: ""},
	&ConditionData{ID: 902, Meaning: "hurricane", Icon1: ""},
	&ConditionData{ID: 903, Meaning: "cold", Icon1: ""},
	&ConditionData{ID: 904, Meaning: "hot", Icon1: ""},
	&ConditionData{ID: 905, Meaning: "windy", Icon1: ""},
	&ConditionData{ID: 906, Meaning: "hail", Icon1: ""},
}

// AdditionalConditions is a slive of ConditionData pointers
var AdditionalConditions = []*ConditionData{
	&ConditionData{ID: 951, Meaning: "calm", Icon1: ""},
	&ConditionData{ID: 952, Meaning: "light breeze", Icon1: ""},
	&ConditionData{ID: 953, Meaning: "gentle breeze", Icon1: ""},
	&ConditionData{ID: 954, Meaning: "moderate breeze", Icon1: ""},
	&ConditionData{ID: 955, Meaning: "fresh breeze", Icon1: ""},
	&ConditionData{ID: 956, Meaning: "strong breeze", Icon1: ""},
	&ConditionData{ID: 957, Meaning: "high wind, near gale", Icon1: ""},
	&ConditionData{ID: 958, Meaning: "gale", Icon1: ""},
	&ConditionData{ID: 959, Meaning: "severe gale", Icon1: ""},
	&ConditionData{ID: 960, Meaning: "storm", Icon1: ""},
	&ConditionData{ID: 961, Meaning: "violent storm", Icon1: ""},
	&ConditionData{ID: 962, Meaning: "hurricane", Icon1: ""},
}
<commit_msg>commit adjustment<commit_after>// Copyright 2014 Brian J. Downs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package openweathermap

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
)

// IconData holds the relevant info for linking icons to conditions.
type IconData struct {
	Condition string
	Day       string
	Night     string
}

// ConditionData holds data structure for weather conditions information.
type ConditionData struct {
	ID      int
	Meaning string
	Icon1   string
	Icon2   string
}

// RetrieveIcon will get the specified icon from the API.
func RetrieveIcon(destination, iconFile string) (int64, error) {
	fullFilePath := fmt.Sprintf("%s/%s", destination, iconFile)

	// Check to see if we've already gotten that icon file.  If so, use it
	// rather than getting it again.
	if _, err := os.Stat(fullFilePath); err != nil {
		response, err := http.Get(fmt.Sprintf(iconURL, iconFile))
		if err != nil {
			return 0, err
		}
		defer response.Body.Close()

		// Create the icon file
		out, err := os.Create(fullFilePath)
		if err != nil {
			return 0, err
		}
		defer out.Close()

		// Fill the empty file with the actual content
		n, err := io.Copy(out, response.Body)
		if err != nil {
			return 0, err
		}
		return n, nil
	}
	return 0, nil
}

// IconList is a slice of IconData pointers
var IconList = []*IconData{
	&IconData{Condition: "clear sky", Day: "01d.png", Night: "01n.png"},
	&IconData{Condition: "few clouds", Day: "02d.png", Night: "02n.png"},
	&IconData{Condition: "scattered clouds", Day: "03d.png", Night: "03n.png"},
	&IconData{Condition: "broken clouds", Day: "04d.png", Night: "04n.png"},
	&IconData{Condition: "shower rain", Day: "09d.png", Night: "09n.png"},
	&IconData{Condition: "rain", Day: "10d.png", Night: "10n.png"},
	&IconData{Condition: "thunderstorm", Day: "11d.png", Night: "11n.png"},
	&IconData{Condition: "snow", Day: "13d.png", Night: "13n.png"},
	&IconData{Condition: "mist", Day: "50d.png", Night: "50n.png"},
}

// ThunderstormConditions is a slice of ConditionData pointers
var ThunderstormConditions = []*ConditionData{
	&ConditionData{ID: 200, Meaning: "thunderstorm with light rain", Icon1: "11d.png"},
	&ConditionData{ID: 201, Meaning: "thunderstorm with rain", Icon1: "11d.png"},
	&ConditionData{ID: 202, Meaning: "thunderstorm with heavy rain", Icon1: "11d.png"},
	&ConditionData{ID: 210, Meaning: "light thunderstorm", Icon1: "11d.png"},
	&ConditionData{ID: 211, Meaning: "thunderstorm", Icon1: "11d.png"},
	&ConditionData{ID: 212, Meaning: "heavy thunderstorm", Icon1: "11d.png"},
	&ConditionData{ID: 221, Meaning: "ragged thunderstorm", Icon1: "11d.png"},
	&ConditionData{ID: 230, Meaning: "thunderstorm with light drizzle", Icon1: "11d.png"},
	&ConditionData{ID: 231, Meaning: "thunderstorm with drizzle", Icon1: "11d.png"},
	&ConditionData{ID: 232, Meaning: "thunderstorm with heavy drizzle", Icon1: "11d.png"},
}

// DrizzleConditions is a slice of ConditionData pointers
var DrizzleConditions = []*ConditionData{
	&ConditionData{ID: 300, Meaning: "light intensity drizzle", Icon1: "09d.png"},
	&ConditionData{ID: 301, Meaning: "drizzle", Icon1: "09d.png"},
	&ConditionData{ID: 302, Meaning: "heavy intensity drizzle", Icon1: "09d.png"},
	&ConditionData{ID: 310, Meaning: "light intensity drizzle rain", Icon1: "09d.png"},
	&ConditionData{ID: 311, Meaning: "drizzle rain", Icon1: "09d.png"},
	&ConditionData{ID: 312, Meaning: "heavy intensity drizzle rain", Icon1: "09d.png"},
	&ConditionData{ID: 313, Meaning: "shower rain and drizzle", Icon1: "09d.png"},
	&ConditionData{ID: 314, Meaning: "heavy shower rain and drizzle", Icon1: "09d.png"},
	&ConditionData{ID: 321, Meaning: "shower drizzle", Icon1: "09d.png"},
}

// RainConditions is a slice of ConditionData pointers
var RainConditions = []*ConditionData{
	&ConditionData{ID: 500, Meaning: "light rain", Icon1: "09d.png"},
	&ConditionData{ID: 501, Meaning: "moderate rain", Icon1: "09d.png"},
	&ConditionData{ID: 502, Meaning: "heavy intensity rain", Icon1: "09d.png"},
	&ConditionData{ID: 503, Meaning: "very heavy rain", Icon1: "09d.png"},
	&ConditionData{ID: 504, Meaning: "extreme rain", Icon1: "09d.png"},
	&ConditionData{ID: 511, Meaning: "freezing rain", Icon1: "13d.png"},
	&ConditionData{ID: 520, Meaning: "light intensity shower rain", Icon1: "09d.png"},
	&ConditionData{ID: 521, Meaning: "shower rain", Icon1: "09d.png"},
	&ConditionData{ID: 522, Meaning: "heavy intensity shower rain", Icon1: "09d.png"},
	&ConditionData{ID: 531, Meaning: "ragged shower rain", Icon1: "09d.png"},
}

// SnowConditions is a slice of ConditionData pointers
var SnowConditions = []*ConditionData{
	&ConditionData{ID: 600, Meaning: "light snow", Icon1: "13d.png"},
	&ConditionData{ID: 601, Meaning: "snow", Icon1: "13d.png"},
	&ConditionData{ID: 602, Meaning: "heavy snow", Icon1: "13d.png"},
	&ConditionData{ID: 611, Meaning: "sleet", Icon1: "13d.png"},
	&ConditionData{ID: 612, Meaning: "shower sleet", Icon1: "13d.png"},
	&ConditionData{ID: 615, Meaning: "light rain and snow", Icon1: "13d.png"},
	&ConditionData{ID: 616, Meaning: "rain and snow", Icon1: "13d.png"},
	&ConditionData{ID: 620, Meaning: "light shower snow", Icon1: "13d.png"},
	&ConditionData{ID: 621, Meaning: "shower snow", Icon1: "13d.png"},
	&ConditionData{ID: 622, Meaning: "heavy shower snow", Icon1: "13d.png"},
}

// AtmosphereConditions is a slice of ConditionData pointers
var AtmosphereConditions = []*ConditionData{
	&ConditionData{ID: 701, Meaning: "mist", Icon1: "50d.png"},
	&ConditionData{ID: 711, Meaning: "smoke", Icon1: "50d.png"},
	&ConditionData{ID: 721, Meaning: "haze", Icon1: "50d.png"},
	&ConditionData{ID: 731, Meaning: "sand, dust whirls", Icon1: "50d.png"},
	&ConditionData{ID: 741, Meaning: "fog", Icon1: "50d.png"},
	&ConditionData{ID: 751, Meaning: "sand", Icon1: "50d.png"},
	&ConditionData{ID: 761, Meaning: "dust", Icon1: "50d.png"},
	&ConditionData{ID: 762, Meaning: "volcanic ash", Icon1: "50d.png"},
	&ConditionData{ID: 771, Meaning: "squalls", Icon1: "50d.png"},
	&ConditionData{ID: 781, Meaning: "tornado", Icon1: "50d.png"},
}

// CloudConditions is a slice of ConditionData pointers
var CloudConditions = []*ConditionData{
	&ConditionData{ID: 800, Meaning: "clear sky", Icon1: "01d.png", Icon2: "01n.png"},
	&ConditionData{ID: 801, Meaning: "few clouds", Icon1: "02d.png", Icon2: " 02n.png"},
	&ConditionData{ID: 802, Meaning: "scattered clouds", Icon1: "03d.png", Icon2: "03d.png"},
	&ConditionData{ID: 803, Meaning: "broken clouds", Icon1: "04d.png", Icon2: "03d.png"},
	&ConditionData{ID: 804, Meaning: "overcast clouds", Icon1: "04d.png", Icon2: "04d.png"},
}

// ExtremeConditions is a slice of ConditionData pointers
var ExtremeConditions = []*ConditionData{
	&ConditionData{ID: 900, Meaning: "tornado", Icon1: ""},
	&ConditionData{ID: 901, Meaning: "tropical storm", Icon1: ""},
	&ConditionData{ID: 902, Meaning: "hurricane", Icon1: ""},
	&ConditionData{ID: 903, Meaning: "cold", Icon1: ""},
	&ConditionData{ID: 904, Meaning: "hot", Icon1: ""},
	&ConditionData{ID: 905, Meaning: "windy", Icon1: ""},
	&ConditionData{ID: 906, Meaning: "hail", Icon1: ""},
}

// AdditionalConditions is a slive of ConditionData pointers
var AdditionalConditions = []*ConditionData{
	&ConditionData{ID: 951, Meaning: "calm", Icon1: ""},
	&ConditionData{ID: 952, Meaning: "light breeze", Icon1: ""},
	&ConditionData{ID: 953, Meaning: "gentle breeze", Icon1: ""},
	&ConditionData{ID: 954, Meaning: "moderate breeze", Icon1: ""},
	&ConditionData{ID: 955, Meaning: "fresh breeze", Icon1: ""},
	&ConditionData{ID: 956, Meaning: "strong breeze", Icon1: ""},
	&ConditionData{ID: 957, Meaning: "high wind, near gale", Icon1: ""},
	&ConditionData{ID: 958, Meaning: "gale", Icon1: ""},
	&ConditionData{ID: 959, Meaning: "severe gale", Icon1: ""},
	&ConditionData{ID: 960, Meaning: "storm", Icon1: ""},
	&ConditionData{ID: 961, Meaning: "violent storm", Icon1: ""},
	&ConditionData{ID: 962, Meaning: "hurricane", Icon1: ""},
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.

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

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

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

package object

import (
	"context"
	"reflect"

	"github.com/vmware/govmomi/vim25/soap"
	"github.com/vmware/govmomi/vim25/types"
)

func init() {
	types.Add("ArrayOfVirtualDiskInfo", reflect.TypeOf((*arrayOfVirtualDiskInfo)(nil)).Elem())

	types.Add("VirtualDiskInfo", reflect.TypeOf((*VirtualDiskInfo)(nil)).Elem())
}

type arrayOfVirtualDiskInfo struct {
	VirtualDiskInfo []VirtualDiskInfo `xml:"VirtualDiskInfo,omitempty"`
}

type queryVirtualDiskInfoTaskRequest struct {
	This           types.ManagedObjectReference  `xml:"_this"`
	Name           string                        `xml:"name"`
	Datacenter     *types.ManagedObjectReference `xml:"datacenter,omitempty"`
	IncludeParents bool                          `xml:"includeParents"`
}

type queryVirtualDiskInfoTaskResponse struct {
	Returnval types.ManagedObjectReference `xml:"returnval"`
}

type queryVirtualDiskInfoTaskBody struct {
	Req *queryVirtualDiskInfoTaskRequest  `xml:"urn:internalvim25 QueryVirtualDiskInfo_Task,omitempty"`
	Res *queryVirtualDiskInfoTaskResponse `xml:"urn:vim25 QueryVirtualDiskInfo_TaskResponse,omitempty"`
	Err *soap.Fault                       `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
}

func (b *queryVirtualDiskInfoTaskBody) Fault() *soap.Fault { return b.Err }

func queryVirtualDiskInfoTask(ctx context.Context, r soap.RoundTripper, req *queryVirtualDiskInfoTaskRequest) (*queryVirtualDiskInfoTaskResponse, error) {
	var reqBody, resBody queryVirtualDiskInfoTaskBody

	reqBody.Req = req

	if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
		return nil, err
	}

	return resBody.Res, nil
}

type VirtualDiskInfo struct {
	Name     string `xml:"unit>name"`
	DiskType string `xml:"diskType"`
	Parent   string `xml:"parent,omitempty"`
}

func (m VirtualDiskManager) QueryVirtualDiskInfo(ctx context.Context, name string, dc *Datacenter, includeParents bool) ([]VirtualDiskInfo, error) {
	req := queryVirtualDiskInfoTaskRequest{
		This:           m.Reference(),
		Name:           name,
		IncludeParents: includeParents,
	}

	if dc != nil {
		ref := dc.Reference()
		req.Datacenter = &ref
	}

	res, err := queryVirtualDiskInfoTask(ctx, m.Client(), &req)
	if err != nil {
		return nil, err
	}

	info, err := NewTask(m.Client(), res.Returnval).WaitForResult(ctx, nil)
	if err != nil {
		return nil, err
	}

	return info.Result.(arrayOfVirtualDiskInfo).VirtualDiskInfo, nil
}

type createChildDiskTaskRequest struct {
	This             types.ManagedObjectReference  `xml:"_this"`
	ChildName        string                        `xml:"childName"`
	ChildDatacenter  *types.ManagedObjectReference `xml:"childDatacenter,omitempty"`
	ParentName       string                        `xml:"parentName"`
	ParentDatacenter *types.ManagedObjectReference `xml:"parentDatacenter,omitempty"`
	IsLinkedClone    bool                          `xml:"isLinkedClone"`
}

type createChildDiskTaskResponse struct {
	Returnval types.ManagedObjectReference `xml:"returnval"`
}

type createChildDiskTaskBody struct {
	Req         *createChildDiskTaskRequest  `xml:"urn:internalvim25 CreateChildDisk_Task,omitempty"`
	Res         *createChildDiskTaskResponse `xml:"urn:vim25 CreateChildDisk_TaskResponse,omitempty"`
	InternalRes *createChildDiskTaskResponse `xml:"urn:internalvim25 CreateChildDisk_TaskResponse,omitempty"`
	Err         *soap.Fault                  `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
}

func (b *createChildDiskTaskBody) Fault() *soap.Fault { return b.Err }

func createChildDiskTask(ctx context.Context, r soap.RoundTripper, req *createChildDiskTaskRequest) (*createChildDiskTaskResponse, error) {
	var reqBody, resBody createChildDiskTaskBody

	reqBody.Req = req

	if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
		return nil, err
	}

	if resBody.Res != nil {
		return resBody.Res, nil // vim-version <= 6.5
	}

	return resBody.InternalRes, nil // vim-version >= 6.7
}

func (m VirtualDiskManager) CreateChildDisk(ctx context.Context, parent string, pdc *Datacenter, name string, dc *Datacenter, linked bool) (*Task, error) {
	req := createChildDiskTaskRequest{
		This:          m.Reference(),
		ChildName:     name,
		ParentName:    parent,
		IsLinkedClone: linked,
	}

	if dc != nil {
		ref := dc.Reference()
		req.ChildDatacenter = &ref
	}

	if pdc != nil {
		ref := pdc.Reference()
		req.ParentDatacenter = &ref
	}

	res, err := createChildDiskTask(ctx, m.Client(), &req)
	if err != nil {
		return nil, err
	}

	return NewTask(m.Client(), res.Returnval), nil
}
<commit_msg>Avoid possible panic in QueryVirtualDiskInfo<commit_after>/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.

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

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

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

package object

import (
	"context"
	"reflect"

	"github.com/vmware/govmomi/vim25/soap"
	"github.com/vmware/govmomi/vim25/types"
)

func init() {
	types.Add("ArrayOfVirtualDiskInfo", reflect.TypeOf((*arrayOfVirtualDiskInfo)(nil)).Elem())

	types.Add("VirtualDiskInfo", reflect.TypeOf((*VirtualDiskInfo)(nil)).Elem())
}

type arrayOfVirtualDiskInfo struct {
	VirtualDiskInfo []VirtualDiskInfo `xml:"VirtualDiskInfo,omitempty"`
}

type queryVirtualDiskInfoTaskRequest struct {
	This           types.ManagedObjectReference  `xml:"_this"`
	Name           string                        `xml:"name"`
	Datacenter     *types.ManagedObjectReference `xml:"datacenter,omitempty"`
	IncludeParents bool                          `xml:"includeParents"`
}

type queryVirtualDiskInfoTaskResponse struct {
	Returnval types.ManagedObjectReference `xml:"returnval"`
}

type queryVirtualDiskInfoTaskBody struct {
	Req         *queryVirtualDiskInfoTaskRequest  `xml:"urn:internalvim25 QueryVirtualDiskInfo_Task,omitempty"`
	Res         *queryVirtualDiskInfoTaskResponse `xml:"urn:vim25 QueryVirtualDiskInfo_TaskResponse,omitempty"`
	InternalRes *queryVirtualDiskInfoTaskResponse `xml:"urn:internalvim25 QueryVirtualDiskInfo_TaskResponse,omitempty"`
	Err         *soap.Fault                       `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
}

func (b *queryVirtualDiskInfoTaskBody) Fault() *soap.Fault { return b.Err }

func queryVirtualDiskInfoTask(ctx context.Context, r soap.RoundTripper, req *queryVirtualDiskInfoTaskRequest) (*queryVirtualDiskInfoTaskResponse, error) {
	var reqBody, resBody queryVirtualDiskInfoTaskBody

	reqBody.Req = req

	if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
		return nil, err
	}

	if resBody.Res != nil {
		return resBody.Res, nil
	}

	return resBody.InternalRes, nil
}

type VirtualDiskInfo struct {
	Name     string `xml:"unit>name"`
	DiskType string `xml:"diskType"`
	Parent   string `xml:"parent,omitempty"`
}

func (m VirtualDiskManager) QueryVirtualDiskInfo(ctx context.Context, name string, dc *Datacenter, includeParents bool) ([]VirtualDiskInfo, error) {
	req := queryVirtualDiskInfoTaskRequest{
		This:           m.Reference(),
		Name:           name,
		IncludeParents: includeParents,
	}

	if dc != nil {
		ref := dc.Reference()
		req.Datacenter = &ref
	}

	res, err := queryVirtualDiskInfoTask(ctx, m.Client(), &req)
	if err != nil {
		return nil, err
	}

	info, err := NewTask(m.Client(), res.Returnval).WaitForResult(ctx, nil)
	if err != nil {
		return nil, err
	}

	return info.Result.(arrayOfVirtualDiskInfo).VirtualDiskInfo, nil
}

type createChildDiskTaskRequest struct {
	This             types.ManagedObjectReference  `xml:"_this"`
	ChildName        string                        `xml:"childName"`
	ChildDatacenter  *types.ManagedObjectReference `xml:"childDatacenter,omitempty"`
	ParentName       string                        `xml:"parentName"`
	ParentDatacenter *types.ManagedObjectReference `xml:"parentDatacenter,omitempty"`
	IsLinkedClone    bool                          `xml:"isLinkedClone"`
}

type createChildDiskTaskResponse struct {
	Returnval types.ManagedObjectReference `xml:"returnval"`
}

type createChildDiskTaskBody struct {
	Req         *createChildDiskTaskRequest  `xml:"urn:internalvim25 CreateChildDisk_Task,omitempty"`
	Res         *createChildDiskTaskResponse `xml:"urn:vim25 CreateChildDisk_TaskResponse,omitempty"`
	InternalRes *createChildDiskTaskResponse `xml:"urn:internalvim25 CreateChildDisk_TaskResponse,omitempty"`
	Err         *soap.Fault                  `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
}

func (b *createChildDiskTaskBody) Fault() *soap.Fault { return b.Err }

func createChildDiskTask(ctx context.Context, r soap.RoundTripper, req *createChildDiskTaskRequest) (*createChildDiskTaskResponse, error) {
	var reqBody, resBody createChildDiskTaskBody

	reqBody.Req = req

	if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
		return nil, err
	}

	if resBody.Res != nil {
		return resBody.Res, nil // vim-version <= 6.5
	}

	return resBody.InternalRes, nil // vim-version >= 6.7
}

func (m VirtualDiskManager) CreateChildDisk(ctx context.Context, parent string, pdc *Datacenter, name string, dc *Datacenter, linked bool) (*Task, error) {
	req := createChildDiskTaskRequest{
		This:          m.Reference(),
		ChildName:     name,
		ParentName:    parent,
		IsLinkedClone: linked,
	}

	if dc != nil {
		ref := dc.Reference()
		req.ChildDatacenter = &ref
	}

	if pdc != nil {
		ref := pdc.Reference()
		req.ParentDatacenter = &ref
	}

	res, err := createChildDiskTask(ctx, m.Client(), &req)
	if err != nil {
		return nil, err
	}

	return NewTask(m.Client(), res.Returnval), nil
}
<|endoftext|>


<commit_before>package mainline

import (
	"net"

	"github.com/anacrolix/torrent/bencode"
	sockaddr "github.com/libp2p/go-sockaddr/net"
	"go.uber.org/zap"
	"golang.org/x/sys/unix"
)

type Transport struct {
	fd      int
	laddr   *net.UDPAddr
	started bool
	buffer  []byte

	// OnMessage is the function that will be called when Transport receives a packet that is
	// successfully unmarshalled as a syntactically correct Message (but -of course- the checking
	// the semantic correctness of the Message is left to Protocol).
	onMessage func(*Message, *net.UDPAddr)
	// OnCongestion
	onCongestion func()
}

func NewTransport(laddr string, onMessage func(*Message, *net.UDPAddr), onCongestion func()) *Transport {
	t := new(Transport)
	/*   The field size sets a theoretical limit of 65,535 bytes (8 byte header + 65,527 bytes of
	 * data) for a UDP datagram. However the actual limit for the data length, which is imposed by
	 * the underlying IPv4 protocol, is 65,507 bytes (65,535 − 8 byte UDP header − 20 byte IP
	 * header).
	 *
	 *   In IPv6 jumbograms it is possible to have UDP packets of size greater than 65,535 bytes.
	 * RFC 2675 specifies that the length field is set to zero if the length of the UDP header plus
	 * UDP data is greater than 65,535.
	 *
	 * https://en.wikipedia.org/wiki/User_Datagram_Protocol
	 */
	t.buffer = make([]byte, 65507)
	t.onMessage = onMessage
	t.onCongestion = onCongestion

	var err error
	t.laddr, err = net.ResolveUDPAddr("udp", laddr)
	if err != nil {
		zap.L().Panic("Could not resolve the UDP address for the trawler!", zap.Error(err))
	}
	if t.laddr.IP.To4() == nil {
		zap.L().Panic("IP address is not IPv4!")
	}

	return t
}

func (t *Transport) Start() {
	// Why check whether the Transport `t` started or not, here and not -for instance- in
	// t.Terminate()?
	// Because in t.Terminate() the programmer (i.e. you & me) would stumble upon an error while
	// trying close an uninitialised net.UDPConn or something like that: it's mostly harmless
	// because its effects are immediate. But if you try to start a Transport `t` for the second
	// (or the third, 4th, ...) time, it will keep spawning goroutines and any small mistake may
	// end up in a debugging horror.
	//                                                                   Here ends my justification.
	if t.started {
		zap.L().Panic("Attempting to Start() a mainline/Transport that has been already started! (Programmer error.)")
	}
	t.started = true

	var err error
	t.fd, err = unix.Socket(unix.SOCK_DGRAM, unix.AF_INET, 0)
	if err != nil {
		zap.L().Fatal("Could NOT create a UDP socket!", zap.Error(err))
	}

	var ip [4]byte
	copy(ip[:], t.laddr.IP.To4())
	err = unix.Bind(t.fd, &unix.SockaddrInet4{Addr: ip, Port: t.laddr.Port})
	if err != nil {
		zap.L().Fatal("Could NOT bind the socket!", zap.Error(err))
	}

	go t.readMessages()
}

func (t *Transport) Terminate() {
	unix.Close(t.fd)
}

// readMessages is a goroutine!
func (t *Transport) readMessages() {
	for {
		n, fromSA, err := unix.Recvfrom(t.fd, t.buffer, 0)
		if err == unix.EPERM || err == unix.ENOBUFS { // todo: are these errors possible for recvfrom?
			zap.L().Warn("READ CONGESTION!", zap.Error(err))
			t.onCongestion()
		} else if err != nil {
			zap.L().Warn("Could NOT read an UDP packet!", zap.Error(err))
		}

		if n == 0 {
			/* Datagram sockets in various domains  (e.g., the UNIX and Internet domains) permit
			 * zero-length datagrams. When such a datagram is received, the return value (n) is 0.
			 */
			continue
		}

		from := sockaddr.SockaddrToUDPAddr(fromSA)
		if from == nil {
			zap.L().Panic("dht mainline transport SockaddrToUDPAddr: nil")
		}

		var msg Message
		err = bencode.Unmarshal(t.buffer[:n], &msg)
		if err != nil {
			// couldn't unmarshal packet data
			continue
		}

		t.onMessage(&msg, from)
	}
}

func (t *Transport) WriteMessages(msg *Message, addr *net.UDPAddr) {
	data, err := bencode.Marshal(msg)
	if err != nil {
		zap.L().Panic("Could NOT marshal an outgoing message! (Programmer error.)")
	}

	addrSA := sockaddr.NetAddrToSockaddr(addr)
	if addrSA == nil {
		zap.L().Debug("Wrong net address for the remote peer!",
			zap.String("addr", addr.String()))
		return
	}

	err = unix.Sendto(t.fd, data, 0, addrSA)
	if err == unix.EPERM || err == unix.ENOBUFS {
		/*   EPERM (errno: 1) is kernel's way of saying that "you are far too fast, chill". It is
		 * also likely that we have received a ICMP source quench packet (meaning, that we *really*
		 * need to slow down.
		 *
		 * Read more here: http://www.archivum.info/comp.protocols.tcp-ip/2009-05/00088/UDP-socket-amp-amp-sendto-amp-amp-EPERM.html
		 *
		 * >   Note On BSD systems (OS X, FreeBSD, etc.) flow control is not supported for
		 * > DatagramProtocol, because send failures caused by writing too many packets cannot be
		 * > detected easily. The socket always appears ‘ready’ and excess packets are dropped; an
		 * > OSError with errno set to errno.ENOBUFS may or may not be raised; if it is raised, it
		 * > will be reported to DatagramProtocol.error_received() but otherwise ignored.
		 *
		 * Source: https://docs.python.org/3/library/asyncio-protocol.html#flow-control-callbacks
		 */
		zap.L().Warn("WRITE CONGESTION!", zap.Error(err))
		if t.onCongestion != nil {
			t.onCongestion()
		}
	} else if err != nil {
		zap.L().Warn("Could NOT write an UDP packet!", zap.Error(err))
	}
}
<commit_msg>[magneticod] detect UDP socket closure reliably<commit_after>package mainline

import (
	"net"

	"github.com/anacrolix/torrent/bencode"
	sockaddr "github.com/libp2p/go-sockaddr/net"
	"go.uber.org/zap"
	"golang.org/x/sys/unix"
)

type Transport struct {
	fd      int
	laddr   *net.UDPAddr
	started bool
	buffer  []byte

	// OnMessage is the function that will be called when Transport receives a packet that is
	// successfully unmarshalled as a syntactically correct Message (but -of course- the checking
	// the semantic correctness of the Message is left to Protocol).
	onMessage func(*Message, *net.UDPAddr)
	// OnCongestion
	onCongestion func()
}

func NewTransport(laddr string, onMessage func(*Message, *net.UDPAddr), onCongestion func()) *Transport {
	t := new(Transport)
	/*   The field size sets a theoretical limit of 65,535 bytes (8 byte header + 65,527 bytes of
	 * data) for a UDP datagram. However the actual limit for the data length, which is imposed by
	 * the underlying IPv4 protocol, is 65,507 bytes (65,535 − 8 byte UDP header − 20 byte IP
	 * header).
	 *
	 *   In IPv6 jumbograms it is possible to have UDP packets of size greater than 65,535 bytes.
	 * RFC 2675 specifies that the length field is set to zero if the length of the UDP header plus
	 * UDP data is greater than 65,535.
	 *
	 * https://en.wikipedia.org/wiki/User_Datagram_Protocol
	 */
	t.buffer = make([]byte, 65507)
	t.onMessage = onMessage
	t.onCongestion = onCongestion

	var err error
	t.laddr, err = net.ResolveUDPAddr("udp", laddr)
	if err != nil {
		zap.L().Panic("Could not resolve the UDP address for the trawler!", zap.Error(err))
	}
	if t.laddr.IP.To4() == nil {
		zap.L().Panic("IP address is not IPv4!")
	}

	return t
}

func (t *Transport) Start() {
	// Why check whether the Transport `t` started or not, here and not -for instance- in
	// t.Terminate()?
	// Because in t.Terminate() the programmer (i.e. you & me) would stumble upon an error while
	// trying close an uninitialised net.UDPConn or something like that: it's mostly harmless
	// because its effects are immediate. But if you try to start a Transport `t` for the second
	// (or the third, 4th, ...) time, it will keep spawning goroutines and any small mistake may
	// end up in a debugging horror.
	//                                                                   Here ends my justification.
	if t.started {
		zap.L().Panic("Attempting to Start() a mainline/Transport that has been already started! (Programmer error.)")
	}
	t.started = true

	var err error
	t.fd, err = unix.Socket(unix.SOCK_DGRAM, unix.AF_INET, 0)
	if err != nil {
		zap.L().Fatal("Could NOT create a UDP socket!", zap.Error(err))
	}

	var ip [4]byte
	copy(ip[:], t.laddr.IP.To4())
	err = unix.Bind(t.fd, &unix.SockaddrInet4{Addr: ip, Port: t.laddr.Port})
	if err != nil {
		zap.L().Fatal("Could NOT bind the socket!", zap.Error(err))
	}

	go t.readMessages()
}

func (t *Transport) Terminate() {
	unix.Close(t.fd)
}

// readMessages is a goroutine!
func (t *Transport) readMessages() {
	for {
		n, fromSA, err := unix.Recvfrom(t.fd, t.buffer, 0)
		if err == unix.EPERM || err == unix.ENOBUFS { // todo: are these errors possible for recvfrom?
			zap.L().Warn("READ CONGESTION!", zap.Error(err))
			t.onCongestion()
		} else if err != nil {
			// Socket is probably closed
			break
		}

		if n == 0 {
			/* Datagram sockets in various domains  (e.g., the UNIX and Internet domains) permit
			 * zero-length datagrams. When such a datagram is received, the return value (n) is 0.
			 */
			continue
		}

		from := sockaddr.SockaddrToUDPAddr(fromSA)
		if from == nil {
			zap.L().Panic("dht mainline transport SockaddrToUDPAddr: nil")
		}

		var msg Message
		err = bencode.Unmarshal(t.buffer[:n], &msg)
		if err != nil {
			// couldn't unmarshal packet data
			continue
		}

		t.onMessage(&msg, from)
	}
}

func (t *Transport) WriteMessages(msg *Message, addr *net.UDPAddr) {
	data, err := bencode.Marshal(msg)
	if err != nil {
		zap.L().Panic("Could NOT marshal an outgoing message! (Programmer error.)")
	}

	addrSA := sockaddr.NetAddrToSockaddr(addr)
	if addrSA == nil {
		zap.L().Debug("Wrong net address for the remote peer!",
			zap.String("addr", addr.String()))
		return
	}

	err = unix.Sendto(t.fd, data, 0, addrSA)
	if err == unix.EPERM || err == unix.ENOBUFS {
		/*   EPERM (errno: 1) is kernel's way of saying that "you are far too fast, chill". It is
		 * also likely that we have received a ICMP source quench packet (meaning, that we *really*
		 * need to slow down.
		 *
		 * Read more here: http://www.archivum.info/comp.protocols.tcp-ip/2009-05/00088/UDP-socket-amp-amp-sendto-amp-amp-EPERM.html
		 *
		 * >   Note On BSD systems (OS X, FreeBSD, etc.) flow control is not supported for
		 * > DatagramProtocol, because send failures caused by writing too many packets cannot be
		 * > detected easily. The socket always appears ‘ready’ and excess packets are dropped; an
		 * > OSError with errno set to errno.ENOBUFS may or may not be raised; if it is raised, it
		 * > will be reported to DatagramProtocol.error_received() but otherwise ignored.
		 *
		 * Source: https://docs.python.org/3/library/asyncio-protocol.html#flow-control-callbacks
		 */
		zap.L().Warn("WRITE CONGESTION!", zap.Error(err))
		if t.onCongestion != nil {
			t.onCongestion()
		}
	} else if err != nil {
		zap.L().Warn("Could NOT write an UDP packet!", zap.Error(err))
	}
}
<|endoftext|>


<commit_before>package daemon

import (
	"bufio"
	"encoding/json"
	"errors"
	"io"
	"os"

	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net"

	u "github.com/jbenet/go-ipfs/util"
)

// ErrDaemonNotRunning is returned when attempting to retrieve the daemon's
// address and the daemon is not actually running.
var ErrDaemonNotRunning = errors.New("daemon not running")

func getDaemonAddr(confdir string) (string, error) {
	var err error
	confdir, err = u.TildeExpansion(confdir)
	if err != nil {
		return "", err
	}
	fi, err := os.Open(confdir + "/rpcaddress")
	if err != nil {
		log.Debug("getDaemonAddr failed: %s", err)
		if err == os.ErrNotExist {
			return "", ErrDaemonNotRunning
		}
		return "", err
	}

	read := bufio.NewReader(fi)

	// TODO: operating system agostic line delim
	line, err := read.ReadBytes('\n')
	if err != nil && err != io.EOF {
		return "", err
	}
	return string(line), nil
}

// SendCommand attempts to run the command over a currently-running daemon.
// If there is no running daemon, returns ErrDaemonNotRunning. This is done
// over network RPC API. The address of the daemon is retrieved from the config
// directory, where live daemons write their addresses to special files.
func SendCommand(command *Command, confdir string) error {
	//check if daemon is running
	log.Info("Checking if daemon is running...")
	var err error
	confdir, err = u.TildeExpansion(confdir)
	if err != nil {
		return err
	}
	lk, err := daemonLock(confdir)
	if err == nil {
		lk.Close()
		return ErrDaemonNotRunning
	}

	log.Info("Daemon is running! [reason = %s]", err)

	server, err := getDaemonAddr(confdir)
	if err != nil {
		return err
	}

	log.Info("Daemon address: %s", server)
	maddr, err := ma.NewMultiaddr(server)
	if err != nil {
		return err
	}

	conn, err := manet.Dial(maddr)
	if err != nil {
		return err
	}

	enc := json.NewEncoder(conn)
	err = enc.Encode(command)
	if err != nil {
		return err
	}

	io.Copy(os.Stdout, conn)

	return nil
}
<commit_msg>IPFS_ADDRESS_RPC env var for changing rpc target<commit_after>package daemon

import (
	"bufio"
	"encoding/json"
	"errors"
	"io"
	"os"

	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net"

	u "github.com/jbenet/go-ipfs/util"
)

// ErrDaemonNotRunning is returned when attempting to retrieve the daemon's
// address and the daemon is not actually running.
var ErrDaemonNotRunning = errors.New("daemon not running")

func getDaemonAddr(confdir string) (string, error) {
	var err error
	confdir, err = u.TildeExpansion(confdir)
	if err != nil {
		return "", err
	}
	fi, err := os.Open(confdir + "/rpcaddress")
	if err != nil {
		log.Debug("getDaemonAddr failed: %s", err)
		if err == os.ErrNotExist {
			return "", ErrDaemonNotRunning
		}
		return "", err
	}

	read := bufio.NewReader(fi)

	// TODO: operating system agostic line delim
	line, err := read.ReadBytes('\n')
	if err != nil && err != io.EOF {
		return "", err
	}
	return string(line), nil
}

// SendCommand attempts to run the command over a currently-running daemon.
// If there is no running daemon, returns ErrDaemonNotRunning. This is done
// over network RPC API. The address of the daemon is retrieved from the config
// directory, where live daemons write their addresses to special files.
func SendCommand(command *Command, confdir string) error {
	server := os.Getenv("IPFS_ADDRESS_RPC")

	if server == "" {
		//check if daemon is running
		log.Info("Checking if daemon is running...")
		if !serverIsRunning(confdir) {
			return ErrDaemonNotRunning
		}

		log.Info("Daemon is running!")

		var err error
		server, err = getDaemonAddr(confdir)
		if err != nil {
			return err
		}
	}

	return serverComm(server, command)
}

func serverIsRunning(confdir string) bool {
	var err error
	confdir, err = u.TildeExpansion(confdir)
	if err != nil {
		log.Error("Tilde Expansion Failed: %s", err)
		return false
	}
	lk, err := daemonLock(confdir)
	if err == nil {
		lk.Close()
		return false
	}
	return true
}

func serverComm(server string, command *Command) error {
	log.Info("Daemon address: %s", server)
	maddr, err := ma.NewMultiaddr(server)
	if err != nil {
		return err
	}

	conn, err := manet.Dial(maddr)
	if err != nil {
		return err
	}

	enc := json.NewEncoder(conn)
	err = enc.Encode(command)
	if err != nil {
		return err
	}

	io.Copy(os.Stdout, conn)

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"strings"
	"text/tabwriter"
	"time"
	"unicode"

	"github.com/docker/go-units"
	cfg "github.com/flynn/flynn/cli/config"
	controller "github.com/flynn/flynn/controller/client"
	"github.com/flynn/flynn/pkg/shutdown"
	"github.com/flynn/flynn/pkg/version"
	"github.com/flynn/go-docopt"
)

var (
	flagCluster = os.Getenv("FLYNN_CLUSTER")
	flagApp     string
)

func main() {
	defer shutdown.Exit()

	log.SetFlags(0)

	usage := `
usage: flynn [-a <app>] [-c <cluster>] <command> [<args>...]

Options:
	-a <app>
	-c <cluster>
	-h, --help

Commands:
	help        show usage for a specific command
	cluster     manage clusters
	create      create an app
	delete      delete an app
	apps        list apps
	info        show app information
	ps          list jobs
	kill        kill jobs
	log         get app log
	scale       change formation
	run         run a job
	env         manage env variables
	limit       manage resource limits
	meta        manage app metadata
	route       manage routes
	pg          manage postgres database
	mysql       manage mysql database
	mongodb     manage mongodb database
	redis       manage redis database
	provider    manage resource providers
	docker      deploy Docker images to a Flynn cluster
	remote      manage git remotes
	resource    provision a new resource
	release     manage app releases
	deployment  list deployments
	volume      manage volumes
	export      export app data
	import      create app from exported data
	version     show flynn version

See 'flynn help <command>' for more information on a specific command.
`[1:]
	args, _ := docopt.Parse(usage, nil, true, version.String(), true)

	cmd := args.String["<command>"]
	cmdArgs := args.All["<args>"].([]string)

	if cmd == "help" {
		if len(cmdArgs) == 0 { // `flynn help`
			fmt.Println(usage)
			return
		} else if cmdArgs[0] == "--json" {
			cmds := make(map[string]string)
			for name, cmd := range commands {
				cmds[name] = cmd.usage
			}
			out, err := json.MarshalIndent(cmds, "", "\t")
			if err != nil {
				shutdown.Fatal(err)
			}
			fmt.Println(string(out))
			return
		} else { // `flynn help <command>`
			cmd = cmdArgs[0]
			cmdArgs = make([]string, 1)
			cmdArgs[0] = "--help"
		}
	}
	// Run the update command as early as possible to avoid the possibility of
	// installations being stranded without updates due to errors in other code
	if cmd == "update" {
		if err := runUpdate(); err != nil {
			shutdown.Fatal(err)
		}
		return
	} else {
		defer updater.backgroundRun() // doesn't run if os.Exit is called
	}

	// Set the cluster config name
	if args.String["-c"] != "" {
		flagCluster = args.String["-c"]
	}

	flagApp = args.String["-a"]
	if flagApp != "" {
		if err := readConfig(); err != nil {
			shutdown.Fatal(err)
		}

		if ra, err := appFromGitRemote(flagApp); err == nil {
			clusterConf = ra.Cluster
			flagApp = ra.Name
		}
	}

	if err := runCommand(cmd, cmdArgs); err != nil {
		log.Println(err)
		if strings.Contains(err.Error(), "invalid_grant") {
			log.Println("Reauthentication required. Run `flynn login` to get new credentials.")
		}
		shutdown.ExitWithCode(1)
		return
	}
}

type command struct {
	usage     string
	f         interface{}
	optsFirst bool
}

var commands = make(map[string]*command)

func register(cmd string, f interface{}, usage string) *command {
	switch f.(type) {
	case func(*docopt.Args, controller.Client) error, func(*docopt.Args) error, func() error, func():
	default:
		panic(fmt.Sprintf("invalid command function %s '%T'", cmd, f))
	}
	c := &command{usage: strings.TrimLeftFunc(usage, unicode.IsSpace), f: f}
	commands[cmd] = c
	return c
}

func runCommand(name string, args []string) (err error) {
	argv := make([]string, 1, 1+len(args))
	argv[0] = name
	argv = append(argv, args...)

	cmd, ok := commands[name]
	if !ok {
		return fmt.Errorf("%s is not a flynn command. See 'flynn help'", name)
	}
	parsedArgs, err := docopt.Parse(cmd.usage, argv, true, "", cmd.optsFirst)
	if err != nil {
		return err
	}

	switch f := cmd.f.(type) {
	case func(*docopt.Args, controller.Client) error:
		// create client and run command
		client, err := getClusterClient()
		if err != nil {
			shutdown.Fatal(err)
		}

		return f(parsedArgs, client)
	case func(*docopt.Args) error:
		return f(parsedArgs)
	case func() error:
		return f()
	case func():
		f()
		return nil
	}

	return fmt.Errorf("unexpected command type %T", cmd.f)
}

var config *cfg.Config
var clusterConf *cfg.Cluster

func configPath() string {
	return cfg.DefaultPath()
}

func readConfig() (err error) {
	if config != nil {
		return nil
	}
	config, err = cfg.ReadFile(configPath())
	if os.IsNotExist(err) {
		err = nil
	}
	if config.Upgrade() {
		if err := config.SaveTo(configPath()); err != nil {
			return fmt.Errorf("Error saving upgraded config: %s", err)
		}
	}
	return
}

func getClusterClient() (controller.Client, error) {
	cluster, err := getCluster()
	if err != nil {
		return nil, err
	}
	return cluster.Client()
}

var ErrNoClusters = errors.New("no clusters configured")

func getCluster() (*cfg.Cluster, error) {
	app() // try to look up and cache app/cluster from git remotes
	if clusterConf != nil {
		return clusterConf, nil
	}
	if err := readConfig(); err != nil {
		return nil, err
	}
	if len(config.Clusters) == 0 {
		return nil, ErrNoClusters
	}
	name := flagCluster
	// Get the default cluster
	if name == "" {
		name = config.Default
	}
	// Default cluster not set, pick the first one
	if name == "" {
		clusterConf = config.Clusters[0]
		return clusterConf, nil
	}
	for _, s := range config.Clusters {
		if s.Name == name {
			clusterConf = s
			return s, nil
		}
	}
	return nil, fmt.Errorf("unknown cluster %q", name)
}

func app() (string, error) {
	if flagApp != "" {
		return flagApp, nil
	}
	if app := os.Getenv("FLYNN_APP"); app != "" {
		flagApp = app
		return app, nil
	}
	if err := readConfig(); err != nil {
		return "", err
	}

	ra, err := appFromGitRemote(remoteFromGitConfig())
	if err != nil {
		return "", err
	}
	if ra == nil {
		return "", errors.New("no app found, run from a repo with a flynn remote or specify one with -a")
	}
	clusterConf = ra.Cluster
	flagApp = ra.Name
	return ra.Name, nil
}

func mustApp() string {
	name, err := app()
	if err != nil {
		log.Println(err)
		shutdown.ExitWithCode(1)
	}
	return name
}

func tabWriter() *tabwriter.Writer {
	return tabwriter.NewWriter(os.Stdout, 1, 2, 2, ' ', 0)
}

func humanTime(ts *time.Time) string {
	if ts == nil || ts.IsZero() {
		return ""
	}
	return units.HumanDuration(time.Now().UTC().Sub(*ts)) + " ago"
}

func listRec(w io.Writer, a ...interface{}) {
	for i, x := range a {
		fmt.Fprint(w, x)
		if i+1 < len(a) {
			w.Write([]byte{'\t'})
		} else {
			w.Write([]byte{'\n'})
		}
	}
}

func compatCheck(client controller.Client, minVersion string) (bool, error) {
	status, err := client.Status()
	if err != nil {
		return false, err
	}
	v := version.Parse(status.Version)
	return v.Dev || !v.Before(version.Parse(minVersion)), nil
}
<commit_msg>cli: Add warning about maintenance status<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"strings"
	"text/tabwriter"
	"time"
	"unicode"

	"github.com/docker/go-units"
	cfg "github.com/flynn/flynn/cli/config"
	controller "github.com/flynn/flynn/controller/client"
	"github.com/flynn/flynn/pkg/shutdown"
	"github.com/flynn/flynn/pkg/version"
	"github.com/flynn/go-docopt"
)

var (
	flagCluster = os.Getenv("FLYNN_CLUSTER")
	flagApp     string
)

func main() {
	defer shutdown.Exit()

	log.SetFlags(0)

	usage := `
usage: flynn [-a <app>] [-c <cluster>] <command> [<args>...]

Options:
	-a <app>
	-c <cluster>
	-h, --help

Commands:
	help        show usage for a specific command
	cluster     manage clusters
	create      create an app
	delete      delete an app
	apps        list apps
	info        show app information
	ps          list jobs
	kill        kill jobs
	log         get app log
	scale       change formation
	run         run a job
	env         manage env variables
	limit       manage resource limits
	meta        manage app metadata
	route       manage routes
	pg          manage postgres database
	mysql       manage mysql database
	mongodb     manage mongodb database
	redis       manage redis database
	provider    manage resource providers
	docker      deploy Docker images to a Flynn cluster
	remote      manage git remotes
	resource    provision a new resource
	release     manage app releases
	deployment  list deployments
	volume      manage volumes
	export      export app data
	import      create app from exported data
	version     show flynn version

See 'flynn help <command>' for more information on a specific command.
`[1:]
	args, _ := docopt.Parse(usage, nil, true, version.String(), true)

	cmd := args.String["<command>"]
	cmdArgs := args.All["<args>"].([]string)

	if cmd == "help" {
		if len(cmdArgs) == 0 { // `flynn help`
			fmt.Println(usage)
			return
		} else if cmdArgs[0] == "--json" {
			cmds := make(map[string]string)
			for name, cmd := range commands {
				cmds[name] = cmd.usage
			}
			out, err := json.MarshalIndent(cmds, "", "\t")
			if err != nil {
				shutdown.Fatal(err)
			}
			fmt.Println(string(out))
			return
		} else { // `flynn help <command>`
			cmd = cmdArgs[0]
			cmdArgs = make([]string, 1)
			cmdArgs[0] = "--help"
		}
	}
	// Run the update command as early as possible to avoid the possibility of
	// installations being stranded without updates due to errors in other code
	if cmd == "update" {
		if err := runUpdate(); err != nil {
			shutdown.Fatal(err)
		}
		return
	} else {
		defer updater.backgroundRun() // doesn't run if os.Exit is called
	}

	// Set the cluster config name
	if args.String["-c"] != "" {
		flagCluster = args.String["-c"]
	}

	flagApp = args.String["-a"]
	if flagApp != "" {
		if err := readConfig(); err != nil {
			shutdown.Fatal(err)
		}

		if ra, err := appFromGitRemote(flagApp); err == nil {
			clusterConf = ra.Cluster
			flagApp = ra.Name
		}
	}

	if err := runCommand(cmd, cmdArgs); err != nil {
		log.Println(err)
		if strings.Contains(err.Error(), "invalid_grant") {
			log.Println("Reauthentication required. Run `flynn login` to get new credentials.")
		}
		shutdown.ExitWithCode(1)
		return
	}

	fmt.Fprintln(os.Stderr, "WARNING: Flynn is unmaintained and new installs will fail on June 1. See: https://github.com/flynn/flynn")
}

type command struct {
	usage     string
	f         interface{}
	optsFirst bool
}

var commands = make(map[string]*command)

func register(cmd string, f interface{}, usage string) *command {
	switch f.(type) {
	case func(*docopt.Args, controller.Client) error, func(*docopt.Args) error, func() error, func():
	default:
		panic(fmt.Sprintf("invalid command function %s '%T'", cmd, f))
	}
	c := &command{usage: strings.TrimLeftFunc(usage, unicode.IsSpace), f: f}
	commands[cmd] = c
	return c
}

func runCommand(name string, args []string) (err error) {
	argv := make([]string, 1, 1+len(args))
	argv[0] = name
	argv = append(argv, args...)

	cmd, ok := commands[name]
	if !ok {
		return fmt.Errorf("%s is not a flynn command. See 'flynn help'", name)
	}
	parsedArgs, err := docopt.Parse(cmd.usage, argv, true, "", cmd.optsFirst)
	if err != nil {
		return err
	}

	switch f := cmd.f.(type) {
	case func(*docopt.Args, controller.Client) error:
		// create client and run command
		client, err := getClusterClient()
		if err != nil {
			shutdown.Fatal(err)
		}

		return f(parsedArgs, client)
	case func(*docopt.Args) error:
		return f(parsedArgs)
	case func() error:
		return f()
	case func():
		f()
		return nil
	}

	return fmt.Errorf("unexpected command type %T", cmd.f)
}

var config *cfg.Config
var clusterConf *cfg.Cluster

func configPath() string {
	return cfg.DefaultPath()
}

func readConfig() (err error) {
	if config != nil {
		return nil
	}
	config, err = cfg.ReadFile(configPath())
	if os.IsNotExist(err) {
		err = nil
	}
	if config.Upgrade() {
		if err := config.SaveTo(configPath()); err != nil {
			return fmt.Errorf("Error saving upgraded config: %s", err)
		}
	}
	return
}

func getClusterClient() (controller.Client, error) {
	cluster, err := getCluster()
	if err != nil {
		return nil, err
	}
	return cluster.Client()
}

var ErrNoClusters = errors.New("no clusters configured")

func getCluster() (*cfg.Cluster, error) {
	app() // try to look up and cache app/cluster from git remotes
	if clusterConf != nil {
		return clusterConf, nil
	}
	if err := readConfig(); err != nil {
		return nil, err
	}
	if len(config.Clusters) == 0 {
		return nil, ErrNoClusters
	}
	name := flagCluster
	// Get the default cluster
	if name == "" {
		name = config.Default
	}
	// Default cluster not set, pick the first one
	if name == "" {
		clusterConf = config.Clusters[0]
		return clusterConf, nil
	}
	for _, s := range config.Clusters {
		if s.Name == name {
			clusterConf = s
			return s, nil
		}
	}
	return nil, fmt.Errorf("unknown cluster %q", name)
}

func app() (string, error) {
	if flagApp != "" {
		return flagApp, nil
	}
	if app := os.Getenv("FLYNN_APP"); app != "" {
		flagApp = app
		return app, nil
	}
	if err := readConfig(); err != nil {
		return "", err
	}

	ra, err := appFromGitRemote(remoteFromGitConfig())
	if err != nil {
		return "", err
	}
	if ra == nil {
		return "", errors.New("no app found, run from a repo with a flynn remote or specify one with -a")
	}
	clusterConf = ra.Cluster
	flagApp = ra.Name
	return ra.Name, nil
}

func mustApp() string {
	name, err := app()
	if err != nil {
		log.Println(err)
		shutdown.ExitWithCode(1)
	}
	return name
}

func tabWriter() *tabwriter.Writer {
	return tabwriter.NewWriter(os.Stdout, 1, 2, 2, ' ', 0)
}

func humanTime(ts *time.Time) string {
	if ts == nil || ts.IsZero() {
		return ""
	}
	return units.HumanDuration(time.Now().UTC().Sub(*ts)) + " ago"
}

func listRec(w io.Writer, a ...interface{}) {
	for i, x := range a {
		fmt.Fprint(w, x)
		if i+1 < len(a) {
			w.Write([]byte{'\t'})
		} else {
			w.Write([]byte{'\n'})
		}
	}
}

func compatCheck(client controller.Client, minVersion string) (bool, error) {
	status, err := client.Status()
	if err != nil {
		return false, err
	}
	v := version.Parse(status.Version)
	return v.Dev || !v.Before(version.Parse(minVersion)), nil
}
<|endoftext|>