<commit_before>package radius

import (
	"bytes"
	"crypto"
	"encoding/binary"
	"errors"
	"fmt"
	"net"
	"reflect"
	"strconv"
)

type AVP struct {
	Type  AttributeType
	Value []byte
}

func (a AVP) Copy() AVP {
	value := make([]byte, len(a.Value))
	copy(value, a.Value)
	return AVP{
		Type:  a.Type,
		Value: a.Value,
	}
}
func (a AVP) Encode(b []byte) (n int, err error) {
	fullLen := len(a.Value) + 2 //type and length
	if fullLen > 255 || fullLen < 2 {
		return 0, errors.New("value too big for attribute")
	}
	b[0] = uint8(a.Type)
	b[1] = uint8(fullLen)
	copy(b[2:], a.Value)
	return fullLen, err
}

func (a AVP) Decode(p *Packet) interface{} {
	return getAttributeTypeDesc(a.Type).dataType.Value(p, a)
}

func (a AVP) String() string {
	return "AVP type: " + a.Type.String() + " " + getAttributeTypeDesc(a.Type).dataType.String(nil, a)
}

func (a AVP) StringWithPacket(p *Packet) string {
	return "AVP type: " + a.Type.String() + " " + getAttributeTypeDesc(a.Type).dataType.String(p, a)
}

type avpDataType interface {
	Value(p *Packet, a AVP) interface{}
	String(p *Packet, a AVP) string
}

var avpString avpStringt

type avpStringt struct{}

func (s avpStringt) Value(p *Packet, a AVP) interface{} {
	return string(a.Value)
}
func (s avpStringt) String(p *Packet, a AVP) string {
	return string(a.Value)
}

var avpIP avpIPt

type avpIPt struct{}

func (s avpIPt) Value(p *Packet, a AVP) interface{} {
	return net.IP(a.Value)
}
func (s avpIPt) String(p *Packet, a AVP) string {
	return net.IP(a.Value).String()
}

var avpUint32 avpUint32t

type avpUint32t struct{}

func (s avpUint32t) Value(p *Packet, a AVP) interface{} {
	return uint32(binary.BigEndian.Uint32(a.Value))
}
func (s avpUint32t) String(p *Packet, a AVP) string {
	return strconv.Itoa(int(binary.BigEndian.Uint32(a.Value)))
}

var avpBinary avpBinaryt

type avpBinaryt struct{}

func (s avpBinaryt) Value(p *Packet, a AVP) interface{} {
	return a.Value
}
func (s avpBinaryt) String(p *Packet, a AVP) string {
	return fmt.Sprintf("%#v", a.Value)
}

var avpPassword avpPasswordt

type avpPasswordt struct{}

func (s avpPasswordt) Value(p *Packet, a AVP) interface{} {
	if p == nil {
		return ""
	}
	//Decode password. XOR against md5(p.server.secret+Authenticator)
	secAuth := append([]byte(nil), []byte(p.Secret)...)
	secAuth = append(secAuth, p.Authenticator[:]...)
	m := crypto.Hash(crypto.MD5).New()
	m.Write(secAuth)
	md := m.Sum(nil)
	pass := append([]byte(nil), a.Value...)
	if len(pass) == 16 {
		for i := 0; i < len(pass); i++ {
			pass[i] = pass[i] ^ md[i]
		}
		pass = bytes.TrimRight(pass, string([]rune{0}))
		return string(pass)
	}
	fmt.Println("[GetPassword] warning: not implemented for password > 16")
	return ""
}
func (s avpPasswordt) String(p *Packet, a AVP) string {
	return s.Value(p, a).(string)
}

type avpUint32EnumList []string

func (s avpUint32EnumList) Value(p *Packet, a AVP) interface{} {
	return uint32(binary.BigEndian.Uint32(a.Value))
}
func (s avpUint32EnumList) String(p *Packet, a AVP) string {
	number := int(binary.BigEndian.Uint32(a.Value))
	if number > len(s) {
		return "unknow " + strconv.Itoa(number)
	}
	out := s[number]
	if out == "" {
		return "unknow " + strconv.Itoa(number)
	}
	return out
}

type avpUint32Enum struct {
	t interface{} // t should from a uint32 type like AcctStatusTypeEnum
}

func (s avpUint32Enum) Value(p *Packet, a AVP) interface{} {
	value := reflect.New(reflect.TypeOf(s.t)).Elem()
	value.SetUint(uint64(binary.BigEndian.Uint32(a.Value)))
	return value.Interface()
}
func (s avpUint32Enum) String(p *Packet, a AVP) string {
	number := binary.BigEndian.Uint32(a.Value)
	value := reflect.New(reflect.TypeOf(s.t)).Elem()
	value.SetUint(uint64(number))
	method := value.MethodByName("String")
	if !method.IsValid() {
		return strconv.Itoa(int(number))
	}
	out := method.Call(nil)
	return out[0].Interface().(string)
}

var avpEapMessage avpEapMessaget

type avpEapMessaget struct{}

func (s avpEapMessaget) Value(p *Packet, a AVP) interface{} {
	eap, err := EapDecode(a.Value)
	if err != nil {
		//TODO error handle
		fmt.Println("EapDecode fail ", err)
		return nil
	}
	return eap

}
func (s avpEapMessaget) String(p *Packet, a AVP) string {
	eap := s.Value(p, a)
	if eap == nil {
		return "nil"
	}
	return eap.(*EapPacket).String()
}

type AcctStatusTypeEnum uint32

const (
	AcctStatusTypeEnumStart         AcctStatusTypeEnum = 1
	AcctStatusTypeEnumStop          AcctStatusTypeEnum = 2
	AcctStatusTypeEnumInterimUpdate AcctStatusTypeEnum = 3
	AcctStatusTypeEnumAccountingOn  AcctStatusTypeEnum = 7
	AcctStatusTypeEnumAccountingOff AcctStatusTypeEnum = 8
)

func (e AcctStatusTypeEnum) String() string {
	switch e {
	case AcctStatusTypeEnumStart:
		return "Start"
	case AcctStatusTypeEnumStop:
		return "Stop"
	case AcctStatusTypeEnumInterimUpdate:
		return "InterimUpdate"
	case AcctStatusTypeEnumAccountingOn:
		return "AccountingOn"
	case AcctStatusTypeEnumAccountingOff:
		return "AccountingOff"
	}
	return "unknow code " + strconv.Itoa(int(e))
}

type NASPortTypeEnum uint32

// TODO finish it
const (
	NASPortTypeEnumAsync            NASPortTypeEnum = 0
	NASPortTypeEnumSync             NASPortTypeEnum = 1
	NASPortTypeEnumISDNSync         NASPortTypeEnum = 2
	NASPortTypeEnumISDNSyncV120     NASPortTypeEnum = 3
	NASPortTypeEnumISDNSyncV110     NASPortTypeEnum = 4
	NASPortTypeEnumVirtual          NASPortTypeEnum = 5
	NASPortTypeEnumPIAFS            NASPortTypeEnum = 6
	NASPortTypeEnumHDLCClearChannel NASPortTypeEnum = 7
	NASPortTypeEnumEthernet         NASPortTypeEnum = 15
	NASPortTypeEnumCable            NASPortTypeEnum = 17
)

func (e NASPortTypeEnum) String() string {
	switch e {
	case NASPortTypeEnumAsync:
		return "Async"
	case NASPortTypeEnumSync:
		return "Sync"
	case NASPortTypeEnumISDNSync:
		return "ISDNSync"
	case NASPortTypeEnumISDNSyncV120:
		return "ISDNSyncV120"
	case NASPortTypeEnumISDNSyncV110:
		return "ISDNSyncV110"
	case NASPortTypeEnumVirtual:
		return "Virtual"
	case NASPortTypeEnumPIAFS:
		return "PIAFS"
	case NASPortTypeEnumHDLCClearChannel:
		return "HDLCClearChannel"
	case NASPortTypeEnumEthernet:
		return "Ethernet"
	case NASPortTypeEnumCable:
		return "Cable"
	}
	return "unknow code " + strconv.Itoa(int(e))
}

type ServiceTypeEnum uint32

// TODO finish it
const (
	ServiceTypeEnumLogin          ServiceTypeEnum = 1
	ServiceTypeEnumFramed         ServiceTypeEnum = 2
	ServiceTypeEnumCallbackLogin  ServiceTypeEnum = 3
	ServiceTypeEnumCallbackFramed ServiceTypeEnum = 4
	ServiceTypeEnumOutbound       ServiceTypeEnum = 5
)

func (e ServiceTypeEnum) String() string {
	switch e {
	case ServiceTypeEnumLogin:
		return "Login"
	case ServiceTypeEnumFramed:
		return "Framed"
	case ServiceTypeEnumCallbackLogin:
		return "CallbackLogin"
	case ServiceTypeEnumCallbackFramed:
		return "CallbackFramed"
	case ServiceTypeEnumOutbound:
		return "Outbound"
	}
	return "unknow code " + strconv.Itoa(int(e))
}

type AcctTerminateCauseEnum uint32

// TODO finish it
const (
	AcctTerminateCauseEnumUserRequest AcctTerminateCauseEnum = 1
	AcctTerminateCauseEnumLostCarrier AcctTerminateCauseEnum = 2
	AcctTerminateCauseEnumLostService AcctTerminateCauseEnum = 3
	AcctTerminateCauseEnumIdleTimeout AcctTerminateCauseEnum = 4
)

func (e AcctTerminateCauseEnum) String() string {
	switch e {
	case AcctTerminateCauseEnumUserRequest:
		return "UserRequest"
	case AcctTerminateCauseEnumLostCarrier:
		return "LostCarrier"
	case AcctTerminateCauseEnumLostService:
		return "LostService"
	case AcctTerminateCauseEnumIdleTimeout:
		return "IdleTimeout"
	}
	return "unknow code " + strconv.Itoa(int(e))
}
<commit_msg>Support passwords longer than 16 bytes<commit_after>package radius

import (
	"bytes"
	"crypto"
	"encoding/binary"
	"errors"
	"fmt"
	"log"
	"net"
	"reflect"
	"strconv"
)

type AVP struct {
	Type  AttributeType
	Value []byte
}

func (a AVP) Copy() AVP {
	value := make([]byte, len(a.Value))
	copy(value, a.Value)
	return AVP{
		Type:  a.Type,
		Value: a.Value,
	}
}
func (a AVP) Encode(b []byte) (n int, err error) {
	fullLen := len(a.Value) + 2 //type and length
	if fullLen > 255 || fullLen < 2 {
		return 0, errors.New("value too big for attribute")
	}
	b[0] = uint8(a.Type)
	b[1] = uint8(fullLen)
	copy(b[2:], a.Value)
	return fullLen, err
}

func (a AVP) Decode(p *Packet) interface{} {
	return getAttributeTypeDesc(a.Type).dataType.Value(p, a)
}

func (a AVP) String() string {
	return "AVP type: " + a.Type.String() + " " + getAttributeTypeDesc(a.Type).dataType.String(nil, a)
}

func (a AVP) StringWithPacket(p *Packet) string {
	return "AVP type: " + a.Type.String() + " " + getAttributeTypeDesc(a.Type).dataType.String(p, a)
}

type avpDataType interface {
	Value(p *Packet, a AVP) interface{}
	String(p *Packet, a AVP) string
}

var avpString avpStringt

type avpStringt struct{}

func (s avpStringt) Value(p *Packet, a AVP) interface{} {
	return string(a.Value)
}
func (s avpStringt) String(p *Packet, a AVP) string {
	return string(a.Value)
}

var avpIP avpIPt

type avpIPt struct{}

func (s avpIPt) Value(p *Packet, a AVP) interface{} {
	return net.IP(a.Value)
}
func (s avpIPt) String(p *Packet, a AVP) string {
	return net.IP(a.Value).String()
}

var avpUint32 avpUint32t

type avpUint32t struct{}

func (s avpUint32t) Value(p *Packet, a AVP) interface{} {
	return uint32(binary.BigEndian.Uint32(a.Value))
}
func (s avpUint32t) String(p *Packet, a AVP) string {
	return strconv.Itoa(int(binary.BigEndian.Uint32(a.Value)))
}

var avpBinary avpBinaryt

type avpBinaryt struct{}

func (s avpBinaryt) Value(p *Packet, a AVP) interface{} {
	return a.Value
}
func (s avpBinaryt) String(p *Packet, a AVP) string {
	return fmt.Sprintf("%#v", a.Value)
}

var avpPassword avpPasswordt

type avpPasswordt struct{}

func (s avpPasswordt) Value(p *Packet, a AVP) interface{} {
	if p == nil {
		return ""
	}
	log.Printf("value: %q [%d]\n", a.Value, len(a.Value))
	log.Printf("secret: %q\n", p.Secret)
	buff := a.Value
	pass := make([]byte, 0)
	last := make([]byte, 16)
	copy(last, p.Authenticator[:])

	for len(buff) > 0 {
		m := crypto.Hash(crypto.MD5).New()
		m.Write(append([]byte(p.Secret), last...))
		h := m.Sum(nil)
		for i := 0; i < 16; i++ {
			pass = append(pass, buff[i]^h[i])
		}
		last = buff[:16]
		buff = buff[16:]
	}

	pass = bytes.TrimRight(pass, string([]rune{0}))
	return string(pass)
}
func (s avpPasswordt) String(p *Packet, a AVP) string {
	return s.Value(p, a).(string)
}

type avpUint32EnumList []string

func (s avpUint32EnumList) Value(p *Packet, a AVP) interface{} {
	return uint32(binary.BigEndian.Uint32(a.Value))
}
func (s avpUint32EnumList) String(p *Packet, a AVP) string {
	number := int(binary.BigEndian.Uint32(a.Value))
	if number > len(s) {
		return "unknow " + strconv.Itoa(number)
	}
	out := s[number]
	if out == "" {
		return "unknow " + strconv.Itoa(number)
	}
	return out
}

type avpUint32Enum struct {
	t interface{} // t should from a uint32 type like AcctStatusTypeEnum
}

func (s avpUint32Enum) Value(p *Packet, a AVP) interface{} {
	value := reflect.New(reflect.TypeOf(s.t)).Elem()
	value.SetUint(uint64(binary.BigEndian.Uint32(a.Value)))
	return value.Interface()
}
func (s avpUint32Enum) String(p *Packet, a AVP) string {
	number := binary.BigEndian.Uint32(a.Value)
	value := reflect.New(reflect.TypeOf(s.t)).Elem()
	value.SetUint(uint64(number))
	method := value.MethodByName("String")
	if !method.IsValid() {
		return strconv.Itoa(int(number))
	}
	out := method.Call(nil)
	return out[0].Interface().(string)
}

var avpEapMessage avpEapMessaget

type avpEapMessaget struct{}

func (s avpEapMessaget) Value(p *Packet, a AVP) interface{} {
	eap, err := EapDecode(a.Value)
	if err != nil {
		//TODO error handle
		fmt.Println("EapDecode fail ", err)
		return nil
	}
	return eap

}
func (s avpEapMessaget) String(p *Packet, a AVP) string {
	eap := s.Value(p, a)
	if eap == nil {
		return "nil"
	}
	return eap.(*EapPacket).String()
}

type AcctStatusTypeEnum uint32

const (
	AcctStatusTypeEnumStart         AcctStatusTypeEnum = 1
	AcctStatusTypeEnumStop          AcctStatusTypeEnum = 2
	AcctStatusTypeEnumInterimUpdate AcctStatusTypeEnum = 3
	AcctStatusTypeEnumAccountingOn  AcctStatusTypeEnum = 7
	AcctStatusTypeEnumAccountingOff AcctStatusTypeEnum = 8
)

func (e AcctStatusTypeEnum) String() string {
	switch e {
	case AcctStatusTypeEnumStart:
		return "Start"
	case AcctStatusTypeEnumStop:
		return "Stop"
	case AcctStatusTypeEnumInterimUpdate:
		return "InterimUpdate"
	case AcctStatusTypeEnumAccountingOn:
		return "AccountingOn"
	case AcctStatusTypeEnumAccountingOff:
		return "AccountingOff"
	}
	return "unknow code " + strconv.Itoa(int(e))
}

type NASPortTypeEnum uint32

// TODO finish it
const (
	NASPortTypeEnumAsync            NASPortTypeEnum = 0
	NASPortTypeEnumSync             NASPortTypeEnum = 1
	NASPortTypeEnumISDNSync         NASPortTypeEnum = 2
	NASPortTypeEnumISDNSyncV120     NASPortTypeEnum = 3
	NASPortTypeEnumISDNSyncV110     NASPortTypeEnum = 4
	NASPortTypeEnumVirtual          NASPortTypeEnum = 5
	NASPortTypeEnumPIAFS            NASPortTypeEnum = 6
	NASPortTypeEnumHDLCClearChannel NASPortTypeEnum = 7
	NASPortTypeEnumEthernet         NASPortTypeEnum = 15
	NASPortTypeEnumCable            NASPortTypeEnum = 17
)

func (e NASPortTypeEnum) String() string {
	switch e {
	case NASPortTypeEnumAsync:
		return "Async"
	case NASPortTypeEnumSync:
		return "Sync"
	case NASPortTypeEnumISDNSync:
		return "ISDNSync"
	case NASPortTypeEnumISDNSyncV120:
		return "ISDNSyncV120"
	case NASPortTypeEnumISDNSyncV110:
		return "ISDNSyncV110"
	case NASPortTypeEnumVirtual:
		return "Virtual"
	case NASPortTypeEnumPIAFS:
		return "PIAFS"
	case NASPortTypeEnumHDLCClearChannel:
		return "HDLCClearChannel"
	case NASPortTypeEnumEthernet:
		return "Ethernet"
	case NASPortTypeEnumCable:
		return "Cable"
	}
	return "unknow code " + strconv.Itoa(int(e))
}

type ServiceTypeEnum uint32

// TODO finish it
const (
	ServiceTypeEnumLogin          ServiceTypeEnum = 1
	ServiceTypeEnumFramed         ServiceTypeEnum = 2
	ServiceTypeEnumCallbackLogin  ServiceTypeEnum = 3
	ServiceTypeEnumCallbackFramed ServiceTypeEnum = 4
	ServiceTypeEnumOutbound       ServiceTypeEnum = 5
)

func (e ServiceTypeEnum) String() string {
	switch e {
	case ServiceTypeEnumLogin:
		return "Login"
	case ServiceTypeEnumFramed:
		return "Framed"
	case ServiceTypeEnumCallbackLogin:
		return "CallbackLogin"
	case ServiceTypeEnumCallbackFramed:
		return "CallbackFramed"
	case ServiceTypeEnumOutbound:
		return "Outbound"
	}
	return "unknow code " + strconv.Itoa(int(e))
}

type AcctTerminateCauseEnum uint32

// TODO finish it
const (
	AcctTerminateCauseEnumUserRequest AcctTerminateCauseEnum = 1
	AcctTerminateCauseEnumLostCarrier AcctTerminateCauseEnum = 2
	AcctTerminateCauseEnumLostService AcctTerminateCauseEnum = 3
	AcctTerminateCauseEnumIdleTimeout AcctTerminateCauseEnum = 4
)

func (e AcctTerminateCauseEnum) String() string {
	switch e {
	case AcctTerminateCauseEnumUserRequest:
		return "UserRequest"
	case AcctTerminateCauseEnumLostCarrier:
		return "LostCarrier"
	case AcctTerminateCauseEnumLostService:
		return "LostService"
	case AcctTerminateCauseEnumIdleTimeout:
		return "IdleTimeout"
	}
	return "unknow code " + strconv.Itoa(int(e))
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"fmt"
	"io"
	"mime"
	"os"
	"path/filepath"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/ryanuber/go-glob"
)

type AWS struct {
	client *s3.S3
	remote []string
	local  []string
	vargs  PluginArgs
}

func NewAWS(vargs PluginArgs) AWS {
	sess := session.New(&aws.Config{
		Credentials: credentials.NewStaticCredentials(vargs.Key, vargs.Secret, ""),
		Region:      aws.String(vargs.Region),
	})
	c := s3.New(sess)
	r := make([]string, 1, 1)
	l := make([]string, 1, 1)

	return AWS{c, r, l, vargs}
}

func (a *AWS) Upload(local, remote string) error {
	if local == "" {
		return nil
	}

	file, err := os.Open(local)
	if err != nil {
		return err
	}

	defer file.Close()

	access := ""
	if a.vargs.Access.IsString() {
		access = a.vargs.Access.String()
	} else if !a.vargs.Access.IsEmpty() {
		accessMap := a.vargs.Access.Map()
		for pattern := range accessMap {
			if match := glob.Glob(pattern, local); match == true {
				access = accessMap[pattern]
				break
			}
		}
	}

	if access == "" {
		access = "private"
	}

	fileExt := filepath.Ext(local)
	var contentType string
	if a.vargs.ContentType.IsString() {
		contentType = a.vargs.ContentType.String()
	} else if !a.vargs.ContentType.IsEmpty() {
		contentMap := a.vargs.ContentType.Map()
		for patternExt := range contentMap {
			if patternExt == fileExt {
				contentType = contentMap[patternExt]
				break
			}
		}
	}

	metadata := map[string]*string{}
	vmap := a.vargs.Metadata.Map()
	if len(vmap) > 0 {
		for pattern := range vmap {
			if match := glob.Glob(pattern, local); match == true {
				for k, v := range vmap[pattern] {
					metadata[k] = aws.String(v)
				}
				break
			}
		}
	}

	if contentType == "" {
		contentType = mime.TypeByExtension(fileExt)
	}

	head, err := a.client.HeadObject(&s3.HeadObjectInput{
		Bucket: aws.String(a.vargs.Bucket),
		Key:    aws.String(remote),
	})
	if err != nil && err.(awserr.Error).Code() != "404" {
		if err.(awserr.Error).Code() == "404" {
			return err
		}

		debug("Uploading \"%s\" with Content-Type \"%s\" and permissions \"%s\"", local, contentType, access)
		_, err = a.client.PutObject(&s3.PutObjectInput{
			Bucket:      aws.String(a.vargs.Bucket),
			Key:         aws.String(remote),
			Body:        file,
			ContentType: aws.String(contentType),
			ACL:         aws.String(access),
			Metadata:    metadata,
		})
		return err
	}

	hash := md5.New()
	io.Copy(hash, file)
	sum := fmt.Sprintf("\"%x\"", hash.Sum(nil))

	if sum == *head.ETag {
		shouldCopy := false

		if head.ContentType == nil && contentType != "" {
			debug("Content-Type has changed from unset to %s", contentType)
			shouldCopy = true
		}

		if !shouldCopy && head.ContentType != nil && contentType != *head.ContentType {
			debug("Content-Type has changed from %s to %s", *head.ContentType, contentType)
			shouldCopy = true
		}

		if !shouldCopy && len(head.Metadata) != len(metadata) {
			debug("Count of metadata values has changed for %s", local)
			shouldCopy = true
		}

		if !shouldCopy && len(metadata) > 0 {
			for k, v := range metadata {
				if hv, ok := head.Metadata[k]; ok {
					if *v != *hv {
						debug("Metadata values have changed for %s", local)
						shouldCopy = true
						break
					}
				}
			}
		}

		if !shouldCopy {
			grant, err := a.client.GetObjectAcl(&s3.GetObjectAclInput{
				Bucket: aws.String(a.vargs.Bucket),
				Key:    aws.String(remote),
			})
			if err != nil {
				return err
			}

			previousAccess := "private"
			for _, g := range grant.Grants {
				gt := *g.Grantee
				if gt.URI != nil {
					if *gt.URI == "http://acs.amazonaws.com/groups/global/AllUsers" {
						if *g.Permission == "READ" {
							previousAccess = "public-read"
						} else if *g.Permission == "WRITE" {
							previousAccess = "public-read-write"
						}
					} else if *gt.URI == "http://acs.amazonaws.com/groups/global/AllUsers" {
						if *g.Permission == "READ" {
							previousAccess = "authenticated-read"
						}
					}
				}
			}

			if previousAccess != access {
				debug("Permissions for \"%s\" have changed from \"%s\" to \"%s\"", remote, previousAccess, access)
				shouldCopy = true
			}
		}

		if !shouldCopy {
			debug("Skipping \"%s\" because hashes and metadata match", local)
			return nil
		}

		debug("Updating metadata for \"%s\" Content-Type: \"%s\", ACL: \"%s\"", local, contentType, access)
		_, err = a.client.CopyObject(&s3.CopyObjectInput{
			Bucket:            aws.String(a.vargs.Bucket),
			Key:               aws.String(remote),
			CopySource:        aws.String(fmt.Sprintf("%s/%s", a.vargs.Bucket, remote)),
			ACL:               aws.String(access),
			ContentType:       aws.String(contentType),
			Metadata:          metadata,
			MetadataDirective: aws.String("REPLACE"),
		})
		return err
	}

	return nil
}

func (a *AWS) Redirect(path, location string) error {
	debug("Adding redirect from \"%s\" to \"%s\"\n", path, location)
	_, err := a.client.PutObject(&s3.PutObjectInput{
		Bucket: aws.String(a.vargs.Bucket),
		Key:    aws.String(path),
		ACL:    aws.String("public-read"),
		WebsiteRedirectLocation: aws.String(location),
	})
	return err
}

func (a *AWS) Delete(remote string) error {
	debug("Removing remote file \"%s\"\n", remote)
	_, err := a.client.DeleteObject(&s3.DeleteObjectInput{
		Bucket: aws.String(a.vargs.Bucket),
		Key:    aws.String(remote),
	})
	return err
}

func (a *AWS) List(path string) ([]string, error) {
	remote := make([]string, 1, 1)
	resp, err := a.client.ListObjects(&s3.ListObjectsInput{
		Bucket: aws.String(a.vargs.Bucket),
		Prefix: aws.String(path),
	})
	if err != nil {
		return remote, err
	}

	for _, item := range resp.Contents {
		remote = append(remote, *item.Key)
	}

	for *resp.IsTruncated {
		resp, err = a.client.ListObjects(&s3.ListObjectsInput{
			Bucket: aws.String(a.vargs.Bucket),
			Prefix: aws.String(path),
			Marker: aws.String(remote[len(remote)-1]),
		})

		if err != nil {
			return remote, err
		}

		for _, item := range resp.Contents {
			remote = append(remote, *item.Key)
		}
	}

	return remote, nil
}
<commit_msg>remove some extraneous newlines<commit_after>package main

import (
	"crypto/md5"
	"fmt"
	"io"
	"mime"
	"os"
	"path/filepath"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/ryanuber/go-glob"
)

type AWS struct {
	client *s3.S3
	remote []string
	local  []string
	vargs  PluginArgs
}

func NewAWS(vargs PluginArgs) AWS {
	sess := session.New(&aws.Config{
		Credentials: credentials.NewStaticCredentials(vargs.Key, vargs.Secret, ""),
		Region:      aws.String(vargs.Region),
	})
	c := s3.New(sess)
	r := make([]string, 1, 1)
	l := make([]string, 1, 1)

	return AWS{c, r, l, vargs}
}

func (a *AWS) Upload(local, remote string) error {
	if local == "" {
		return nil
	}

	file, err := os.Open(local)
	if err != nil {
		return err
	}

	defer file.Close()

	access := ""
	if a.vargs.Access.IsString() {
		access = a.vargs.Access.String()
	} else if !a.vargs.Access.IsEmpty() {
		accessMap := a.vargs.Access.Map()
		for pattern := range accessMap {
			if match := glob.Glob(pattern, local); match == true {
				access = accessMap[pattern]
				break
			}
		}
	}

	if access == "" {
		access = "private"
	}

	fileExt := filepath.Ext(local)
	var contentType string
	if a.vargs.ContentType.IsString() {
		contentType = a.vargs.ContentType.String()
	} else if !a.vargs.ContentType.IsEmpty() {
		contentMap := a.vargs.ContentType.Map()
		for patternExt := range contentMap {
			if patternExt == fileExt {
				contentType = contentMap[patternExt]
				break
			}
		}
	}

	metadata := map[string]*string{}
	vmap := a.vargs.Metadata.Map()
	if len(vmap) > 0 {
		for pattern := range vmap {
			if match := glob.Glob(pattern, local); match == true {
				for k, v := range vmap[pattern] {
					metadata[k] = aws.String(v)
				}
				break
			}
		}
	}

	if contentType == "" {
		contentType = mime.TypeByExtension(fileExt)
	}

	head, err := a.client.HeadObject(&s3.HeadObjectInput{
		Bucket: aws.String(a.vargs.Bucket),
		Key:    aws.String(remote),
	})
	if err != nil && err.(awserr.Error).Code() != "404" {
		if err.(awserr.Error).Code() == "404" {
			return err
		}

		debug("Uploading \"%s\" with Content-Type \"%s\" and permissions \"%s\"", local, contentType, access)
		_, err = a.client.PutObject(&s3.PutObjectInput{
			Bucket:      aws.String(a.vargs.Bucket),
			Key:         aws.String(remote),
			Body:        file,
			ContentType: aws.String(contentType),
			ACL:         aws.String(access),
			Metadata:    metadata,
		})
		return err
	}

	hash := md5.New()
	io.Copy(hash, file)
	sum := fmt.Sprintf("\"%x\"", hash.Sum(nil))

	if sum == *head.ETag {
		shouldCopy := false

		if head.ContentType == nil && contentType != "" {
			debug("Content-Type has changed from unset to %s", contentType)
			shouldCopy = true
		}

		if !shouldCopy && head.ContentType != nil && contentType != *head.ContentType {
			debug("Content-Type has changed from %s to %s", *head.ContentType, contentType)
			shouldCopy = true
		}

		if !shouldCopy && len(head.Metadata) != len(metadata) {
			debug("Count of metadata values has changed for %s", local)
			shouldCopy = true
		}

		if !shouldCopy && len(metadata) > 0 {
			for k, v := range metadata {
				if hv, ok := head.Metadata[k]; ok {
					if *v != *hv {
						debug("Metadata values have changed for %s", local)
						shouldCopy = true
						break
					}
				}
			}
		}

		if !shouldCopy {
			grant, err := a.client.GetObjectAcl(&s3.GetObjectAclInput{
				Bucket: aws.String(a.vargs.Bucket),
				Key:    aws.String(remote),
			})
			if err != nil {
				return err
			}

			previousAccess := "private"
			for _, g := range grant.Grants {
				gt := *g.Grantee
				if gt.URI != nil {
					if *gt.URI == "http://acs.amazonaws.com/groups/global/AllUsers" {
						if *g.Permission == "READ" {
							previousAccess = "public-read"
						} else if *g.Permission == "WRITE" {
							previousAccess = "public-read-write"
						}
					} else if *gt.URI == "http://acs.amazonaws.com/groups/global/AllUsers" {
						if *g.Permission == "READ" {
							previousAccess = "authenticated-read"
						}
					}
				}
			}

			if previousAccess != access {
				debug("Permissions for \"%s\" have changed from \"%s\" to \"%s\"", remote, previousAccess, access)
				shouldCopy = true
			}
		}

		if !shouldCopy {
			debug("Skipping \"%s\" because hashes and metadata match", local)
			return nil
		}

		debug("Updating metadata for \"%s\" Content-Type: \"%s\", ACL: \"%s\"", local, contentType, access)
		_, err = a.client.CopyObject(&s3.CopyObjectInput{
			Bucket:            aws.String(a.vargs.Bucket),
			Key:               aws.String(remote),
			CopySource:        aws.String(fmt.Sprintf("%s/%s", a.vargs.Bucket, remote)),
			ACL:               aws.String(access),
			ContentType:       aws.String(contentType),
			Metadata:          metadata,
			MetadataDirective: aws.String("REPLACE"),
		})
		return err
	}

	return nil
}

func (a *AWS) Redirect(path, location string) error {
	debug("Adding redirect from \"%s\" to \"%s\"", path, location)
	_, err := a.client.PutObject(&s3.PutObjectInput{
		Bucket: aws.String(a.vargs.Bucket),
		Key:    aws.String(path),
		ACL:    aws.String("public-read"),
		WebsiteRedirectLocation: aws.String(location),
	})
	return err
}

func (a *AWS) Delete(remote string) error {
	debug("Removing remote file \"%s\"", remote)
	_, err := a.client.DeleteObject(&s3.DeleteObjectInput{
		Bucket: aws.String(a.vargs.Bucket),
		Key:    aws.String(remote),
	})
	return err
}

func (a *AWS) List(path string) ([]string, error) {
	remote := make([]string, 1, 1)
	resp, err := a.client.ListObjects(&s3.ListObjectsInput{
		Bucket: aws.String(a.vargs.Bucket),
		Prefix: aws.String(path),
	})
	if err != nil {
		return remote, err
	}

	for _, item := range resp.Contents {
		remote = append(remote, *item.Key)
	}

	for *resp.IsTruncated {
		resp, err = a.client.ListObjects(&s3.ListObjectsInput{
			Bucket: aws.String(a.vargs.Bucket),
			Prefix: aws.String(path),
			Marker: aws.String(remote[len(remote)-1]),
		})

		if err != nil {
			return remote, err
		}

		for _, item := range resp.Contents {
			remote = append(remote, *item.Key)
		}
	}

	return remote, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/go-ini/ini"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

type Profile struct {
	RoleArn            string
	SourceProfile      string
	MfaSerial          string
	AwsAccessKeyId     string
	AwsSecretAccessKey string
	Region             string
	Token              string
	Name               string
}

var ErrNoAccessKeyGiven = errors.New("no access key given")
var ErrUnknownRegion = errors.New("unknown region given")

func getProfile(profiles []string, iniFile ini.File, hasPrefix bool) (profile Profile, err error) {
	found := false
	for _, p := range profiles {
		n := p
		if hasPrefix {
			n = "profile " + n
		}
		var section, err = iniFile.GetSection(n)
		if section != nil && err == nil {
			if section.HasKey("mfa_serial") {
				profile.MfaSerial = section.Key("mfa_serial").String()
			}
			if section.HasKey("source_profile") {
				profile.SourceProfile = section.Key("source_profile").String()
			}
			if section.HasKey("region") {
				profile.Region = section.Key("region").String()
			}
			if section.HasKey("role_arn") {
				profile.RoleArn = section.Key("role_arn").String()
			}
			if section.HasKey("aws_access_key_id") {
				profile.AwsAccessKeyId = section.Key("aws_access_key_id").String()
			}
			if section.HasKey("aws_secret_access_key") {
				profile.AwsSecretAccessKey = section.Key("aws_secret_access_key").String()
			}
			found = true
			break
		}
	}
	if found == false {
		err = errors.New(fmt.Sprintf("couldn't find any of the source profiles %s in aws credentials", strings.Join(profiles, ", ")))
	}
	return
}

func getProfileKeys(profileName string) (profiles []string) {
	profiles = append(profiles, profileName)
	lowerProjectName := strings.ToLower(profileName)
	profiles = append(profiles, strings.Replace(lowerProjectName, " ", "_", -1))
	profiles = append(profiles, strings.Replace(lowerProjectName, " ", "-", -1))
	return
}

func getAWSConf(projectName string) (sess *session.Session, err error) {
	var creds *credentials.Credentials
	hasPrefix := false
	confFn := os.Getenv("AWS_CONFIG_FILE")
	if confFn == "" {
		confFn = os.Getenv("HOME") + "/.aws/credentials"
		if _, err = os.Stat(confFn); os.IsNotExist(err) {
			confFn = os.Getenv("HOME") + "/.aws/config"
			hasPrefix = true
		}
	}
	if os.Getenv("AWS_ACCESS_KEY_ID") != "" && os.Getenv("AWS_SECRET_ACCESS_KEY") != "" && (os.Getenv("AWS_DEFAULT_REGION") != "" || os.Getenv("AWS_REGION") != "") {
		creds = credentials.NewEnvCredentials()
		region := os.Getenv("AWS_DEFAULT_REGION")
		sess = session.New(&aws.Config{Credentials: creds, Region: &region})
	} else {
		var iniFile *ini.File
		iniFile, err = ini.Load(confFn)
		if err != nil {
			log.Fatalf("Failed to load AWS credentials file  %s", confFn)
		}
		profileKeys := getProfileKeys(projectName)
		profile, _ := getProfile(profileKeys, *iniFile, hasPrefix)
		profile.Name = projectName
		if profile.SourceProfile != "" {
			profileKeys = getProfileKeys(profile.SourceProfile)
			source_profile, err := getProfile(profileKeys, *iniFile, hasPrefix)
			if err == nil {
				profile.AwsAccessKeyId = source_profile.AwsAccessKeyId
				profile.AwsSecretAccessKey = source_profile.AwsSecretAccessKey
				profile.Region = source_profile.Region
			} else {
				log.Fatal(err)
			}
		}
		creds = loadCachedCreds(profile)
		if creds == nil {
			if profile.RoleArn != "" {
				if profile.MfaSerial != "" {
					profile.Token = readToken()
				}
				creds = getStsCredentials(profile)
			} else {
				creds = credentials.NewStaticCredentials(profile.AwsAccessKeyId, profile.AwsSecretAccessKey, "")
				creds.Get()
			}
		}
		sess = session.New(&aws.Config{Credentials: creds, Region: &profile.Region})
	}

	return
}

func getStsCredentials(profile Profile) (creds *credentials.Credentials) {
	staticCreds := credentials.NewStaticCredentials(profile.AwsAccessKeyId, profile.AwsSecretAccessKey, "")
	staticCreds.Get()
	client := sts.New(session.New(&aws.Config{Credentials: staticCreds, Region: &profile.Region}))

	sessionName := "AWS-Profile-session-" + strconv.Itoa(int(time.Now().Unix()))
	input := sts.AssumeRoleInput{
		RoleArn:         &profile.RoleArn,
		SerialNumber:    &profile.MfaSerial,
		RoleSessionName: &sessionName,
		TokenCode:       &profile.Token,
	}

	output, err := client.AssumeRole(&input)
	if err != nil {
		log.Fatal(err)
	}
	saveCachedCreds(profile, output)

	creds = credentials.NewStaticCredentials(*output.Credentials.AccessKeyId, *output.Credentials.SecretAccessKey, *output.Credentials.SessionToken)
	creds.Get()
	return
}

func readToken() (token string) {
	var err error
	for {
		fmt.Print("Enter MFA code: ")
		_, err = fmt.Scanln(&token)
		if err != nil {
			fmt.Println("There was a problem reading from stdin")
			continue
		}
		if len(token) != 6 {
			fmt.Println("Please make sure your token length is 6")
			continue
		}
		_, err = strconv.Atoi(token)
		if err != nil {
			fmt.Println("Please make sure your token is an integer")
			continue
		}
		return
	}
}

func getCachePath(profile Profile) (path string) {
	path = strings.Replace(profile.RoleArn, ":", "_", -1)
	path = strings.Replace(path, "/", "-", -1)
	path = profile.Name + "--" + path + ".json"
	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}
	path = filepath.Join(usr.HomeDir, ".aws/cli/cache/", path)
	return
}

func loadCachedCreds(profile Profile) (creds *credentials.Credentials) {
	path := getCachePath(profile)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		return
	}

	b, err := ioutil.ReadFile(path)

	if err != nil {
		log.Fatalf("Failed to read cache path %s", path)
	}
	assumeRole := new(sts.AssumeRoleOutput)
	err = json.Unmarshal(b, &assumeRole)
	if err != nil {
		log.Fatal(err)
	}
	now := time.Now()
	if now.Unix() > assumeRole.Credentials.Expiration.Unix() {
		return
	}

	creds = credentials.NewStaticCredentials(*assumeRole.Credentials.AccessKeyId, *assumeRole.Credentials.SecretAccessKey, *assumeRole.Credentials.SessionToken)
	creds.Get()
	return
}

func saveCachedCreds(profile Profile, assumeRoleOutput *sts.AssumeRoleOutput) {
	path := getCachePath(profile)
	b, err := json.Marshal(assumeRoleOutput)
	if err != nil {
		log.Fatal(err)
	}
	err = ioutil.WriteFile(path, b, 0600)
	if err != nil {
		log.Fatalf("Failed to write to cache path %s", path)
	}
}
<commit_msg>Fixed bug with no creating the MFA cache path first<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/go-ini/ini"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

type Profile struct {
	RoleArn            string
	SourceProfile      string
	MfaSerial          string
	AwsAccessKeyId     string
	AwsSecretAccessKey string
	Region             string
	Token              string
	Name               string
}

var ErrNoAccessKeyGiven = errors.New("no access key given")
var ErrUnknownRegion = errors.New("unknown region given")

func getProfile(profiles []string, iniFile ini.File, hasPrefix bool) (profile Profile, err error) {
	found := false
	for _, p := range profiles {
		n := p
		if hasPrefix {
			n = "profile " + n
		}
		var section, err = iniFile.GetSection(n)
		if section != nil && err == nil {
			if section.HasKey("mfa_serial") {
				profile.MfaSerial = section.Key("mfa_serial").String()
			}
			if section.HasKey("source_profile") {
				profile.SourceProfile = section.Key("source_profile").String()
			}
			if section.HasKey("region") {
				profile.Region = section.Key("region").String()
			}
			if section.HasKey("role_arn") {
				profile.RoleArn = section.Key("role_arn").String()
			}
			if section.HasKey("aws_access_key_id") {
				profile.AwsAccessKeyId = section.Key("aws_access_key_id").String()
			}
			if section.HasKey("aws_secret_access_key") {
				profile.AwsSecretAccessKey = section.Key("aws_secret_access_key").String()
			}
			found = true
			break
		}
	}
	if found == false {
		err = errors.New(fmt.Sprintf("couldn't find any of the source profiles %s in aws credentials", strings.Join(profiles, ", ")))
		log.Fatal(err)
	}
	return
}

func getProfileKeys(profileName string) (profiles []string) {
	profiles = append(profiles, profileName)
	lowerProjectName := strings.ToLower(profileName)
	profiles = append(profiles, strings.Replace(lowerProjectName, " ", "_", -1))
	profiles = append(profiles, strings.Replace(lowerProjectName, " ", "-", -1))
	return
}

func getAWSConf(projectName string) (sess *session.Session, err error) {
	var creds *credentials.Credentials
	hasPrefix := false
	confFn := os.Getenv("AWS_CONFIG_FILE")
	if confFn == "" {
		confFn = os.Getenv("HOME") + "/.aws/credentials"
		if _, err = os.Stat(confFn); os.IsNotExist(err) {
			confFn = os.Getenv("HOME") + "/.aws/config"
			hasPrefix = true
		}
	}
	if os.Getenv("AWS_ACCESS_KEY_ID") != "" && os.Getenv("AWS_SECRET_ACCESS_KEY") != "" && (os.Getenv("AWS_DEFAULT_REGION") != "" || os.Getenv("AWS_REGION") != "") {
		creds = credentials.NewEnvCredentials()
		region := os.Getenv("AWS_DEFAULT_REGION")
		sess = session.New(&aws.Config{Credentials: creds, Region: &region})
	} else {
		var iniFile *ini.File
		iniFile, err = ini.Load(confFn)
		if err != nil {
			log.Fatalf("Failed to load AWS credentials file  %s", confFn)
		}
		profileKeys := getProfileKeys(projectName)
		profile, _ := getProfile(profileKeys, *iniFile, hasPrefix)
		profile.Name = projectName
		if profile.SourceProfile != "" {
			profileKeys = getProfileKeys(profile.SourceProfile)
			source_profile, err := getProfile(profileKeys, *iniFile, hasPrefix)
			if err == nil {
				profile.AwsAccessKeyId = source_profile.AwsAccessKeyId
				profile.AwsSecretAccessKey = source_profile.AwsSecretAccessKey
				profile.Region = source_profile.Region
			} else {
				log.Fatal(err)
			}
		}
		creds = loadCachedCreds(profile)
		if creds == nil {
			if profile.RoleArn != "" {
				if profile.MfaSerial != "" {
					profile.Token = readToken()
				}
				creds = getStsCredentials(profile)
			} else {
				creds = credentials.NewStaticCredentials(profile.AwsAccessKeyId, profile.AwsSecretAccessKey, "")
				creds.Get()
			}
		}
		sess = session.New(&aws.Config{Credentials: creds, Region: &profile.Region})
	}

	return
}

func getStsCredentials(profile Profile) (creds *credentials.Credentials) {
	staticCreds := credentials.NewStaticCredentials(profile.AwsAccessKeyId, profile.AwsSecretAccessKey, "")
	staticCreds.Get()
	client := sts.New(session.New(&aws.Config{Credentials: staticCreds, Region: &profile.Region}))

	sessionName := "AWS-Profile-session-" + strconv.Itoa(int(time.Now().Unix()))
	input := sts.AssumeRoleInput{
		RoleArn:         &profile.RoleArn,
		SerialNumber:    &profile.MfaSerial,
		RoleSessionName: &sessionName,
		TokenCode:       &profile.Token,
	}

	output, err := client.AssumeRole(&input)
	if err != nil {
		log.Fatal(err)
	}
	saveCachedCreds(profile, output)

	creds = credentials.NewStaticCredentials(*output.Credentials.AccessKeyId, *output.Credentials.SecretAccessKey, *output.Credentials.SessionToken)
	creds.Get()
	return
}

func readToken() (token string) {
	var err error
	for {
		fmt.Print("Enter MFA code: ")
		_, err = fmt.Scanln(&token)
		if err != nil {
			fmt.Println("There was a problem reading from stdin")
			continue
		}
		if len(token) != 6 {
			fmt.Println("Please make sure your token length is 6")
			continue
		}
		_, err = strconv.Atoi(token)
		if err != nil {
			fmt.Println("Please make sure your token is an integer")
			continue
		}
		return
	}
}

func getCachePath(profile Profile) (path string) {
	path = strings.Replace(profile.RoleArn, ":", "_", -1)
	path = strings.Replace(path, "/", "-", -1)
	path = profile.Name + "--" + path + ".json"
	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}
	base := filepath.Join(usr.HomeDir, ".aws/cli/cache/")
	if _, err = os.Stat(base); os.IsNotExist(err) {
		os.MkdirAll(base, 0700)
	}
	path = filepath.Join(base, path)

	return
}

func loadCachedCreds(profile Profile) (creds *credentials.Credentials) {
	path := getCachePath(profile)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		return
	}

	b, err := ioutil.ReadFile(path)

	if err != nil {
		log.Fatalf("Failed to read cache path %s", path)
	}
	assumeRole := new(sts.AssumeRoleOutput)
	err = json.Unmarshal(b, &assumeRole)
	if err != nil {
		log.Fatal(err)
	}
	now := time.Now()
	if now.Unix() > assumeRole.Credentials.Expiration.Unix() {
		return
	}

	creds = credentials.NewStaticCredentials(*assumeRole.Credentials.AccessKeyId, *assumeRole.Credentials.SecretAccessKey, *assumeRole.Credentials.SessionToken)
	creds.Get()
	return
}

func saveCachedCreds(profile Profile, assumeRoleOutput *sts.AssumeRoleOutput) {
	path := getCachePath(profile)
	b, err := json.Marshal(assumeRoleOutput)
	if err != nil {
		log.Fatal(err)
	}
	err = ioutil.WriteFile(path, b, 0600)
	if err != nil {
		log.Fatalf("Failed to write to cache path %s", path)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/jingweno/conf"
	"os"
)

func main() {
	os.Setenv("GO_ENV", "development")
	conf, err := conf.NewLoader().
		Env().
		File("./config.json").
		Defaults(
		map[string]interface{}{
			"DATABASE_HOST": "127.0.0.1",
			"DATABASE_PORT": "1234",
		}).
		Load()
	if err != nil {
		fmt.Printf("err: %s\n", err)
		return
	}

	printConf(conf, "GO_ENV")
	printConf(conf, "DATABASE")
	printConf(conf, "DATABASE_HOST")
	printConf(conf, "DATABASE_PORT")
}

func printConf(conf *conf.Conf, key string) {
	fmt.Printf("%s: %v\n", key, conf.Get(key))
}
<commit_msg>Add space to example<commit_after>package main

import (
	"fmt"
	"github.com/jingweno/conf"
	"os"
)

func main() {
	os.Setenv("GO_ENV", "development")
	conf, err := conf.NewLoader().
		Env().
		File("./config.json").
		Defaults(
		map[string]interface{}{
			"DATABASE_HOST": "127.0.0.1",
			"DATABASE_PORT": "1234",
		}).
		Load()

	if err != nil {
		fmt.Printf("err: %s\n", err)
		return
	}

	printConf(conf, "GO_ENV")
	printConf(conf, "DATABASE")
	printConf(conf, "DATABASE_HOST")
	printConf(conf, "DATABASE_PORT")
}

func printConf(conf *conf.Conf, key string) {
	fmt.Printf("%s: %v\n", key, conf.Get(key))
}
<|endoftext|>


<commit_before>/**
 * Copyright 2017 IBM Corp.
 *
 * 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 volume

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"strings"

	"github.com/IBM/ubiquity/resources"
	"github.com/kubernetes-incubator/external-storage/lib/controller"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/uuid"
	"k8s.io/client-go/pkg/api/v1"
)

const (

	// are we allowed to set this? else make up our own
	annCreatedBy = "kubernetes.io/createdby"
	createdBy    = "ubiquity-provisioner"

	// Name of the file where an nfsProvisioner will store its identity
	identityFile = "ubiquity-provisioner.identity"

	// VolumeGidAnnotationKey is the key of the annotation on the PersistentVolume
	// object that specifies a supplemental GID.
	VolumeGidAnnotationKey = "pv.beta.kubernetes.io/gid"

	// A PV annotation for the identity of the flexProvisioner that provisioned it
	annProvisionerId = "Provisioner_Id"

	podIPEnv     = "POD_IP"
	serviceEnv   = "SERVICE_NAME"
	namespaceEnv = "POD_NAMESPACE"
	nodeEnv      = "NODE_NAME"
)

func NewFlexProvisioner(logger *log.Logger, ubiquityClient resources.StorageClient, config resources.UbiquityPluginConfig) (controller.Provisioner, error) {
	return newFlexProvisionerInternal(logger, ubiquityClient, config)
}

func newFlexProvisionerInternal(logger *log.Logger, ubiquityClient resources.StorageClient, config resources.UbiquityPluginConfig) (*flexProvisioner, error) {
	var identity types.UID
	identityPath := path.Join(config.LogPath, identityFile)
	if _, err := os.Stat(identityPath); os.IsNotExist(err) {
		identity = uuid.NewUUID()
		err := ioutil.WriteFile(identityPath, []byte(identity), 0600)
		if err != nil {
			logger.Printf("Error writing identity file %s! %v", identityPath, err)
		}
	} else {
		read, err := ioutil.ReadFile(identityPath)
		if err != nil {
			logger.Printf("Error reading identity file %s! %v", config.LogPath, err)
		}
		identity = types.UID(strings.TrimSpace(string(read)))
	}
	provisioner := &flexProvisioner{
		logger:         logger,
		identity:       identity,
		ubiquityClient: ubiquityClient,
		ubiquityConfig: config,
		podIPEnv:       podIPEnv,
		serviceEnv:     serviceEnv,
		namespaceEnv:   namespaceEnv,
		nodeEnv:        nodeEnv,
	}

	activateRequest := resources.ActivateRequest{Backends: config.Backends}
	err := provisioner.ubiquityClient.Activate(activateRequest)

	return provisioner, err
}

type flexProvisioner struct {
	logger   *log.Logger
	identity types.UID
	// Whether the provisioner is running out of cluster and so cannot rely on
	// the existence of any of the pod, service, namespace, node env variables.
	outOfCluster bool

	ubiquityClient resources.StorageClient
	ubiquityConfig resources.UbiquityPluginConfig

	// Environment variables the provisioner pod needs valid values for in order to
	// put a service cluster IP as the server of provisioned NFS PVs, passed in
	// via downward API. If serviceEnv is set, namespaceEnv must be too.
	podIPEnv     string
	serviceEnv   string
	namespaceEnv string
	nodeEnv      string
}

// Provision creates a volume i.e. the storage asset and returns a PV object for
// the volume.
func (p *flexProvisioner) Provision(options controller.VolumeOptions) (*v1.PersistentVolume, error) {
	if options.PVC == nil {
		return nil, fmt.Errorf("options missing PVC %#v", options)
	}
	capacity, exists := options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
	if !exists {
		return nil, fmt.Errorf("options.PVC.Spec.Resources.Requests does not contain capacity")
	}
	fmt.Printf("PVC with capacity %d", capacity.Value())
	capacityMB := capacity.Value() / (1024 * 1024)

	volume_details, err := p.createVolume(options, capacityMB)
	if err != nil {
		return nil, err
	}

	annotations := make(map[string]string)
	annotations[annCreatedBy] = createdBy
	annotations[annProvisionerId] = "ubiquity-provisioner"

	pv := &v1.PersistentVolume{
		ObjectMeta: v1.ObjectMeta{
			Name:        options.PVName,
			Labels:      map[string]string{},
			Annotations: annotations,
		},
		Spec: v1.PersistentVolumeSpec{
			PersistentVolumeReclaimPolicy: options.PersistentVolumeReclaimPolicy,
			AccessModes:                   options.PVC.Spec.AccessModes,
			Capacity: v1.ResourceList{
				v1.ResourceName(v1.ResourceStorage): options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)],
			},
			PersistentVolumeSource: v1.PersistentVolumeSource{
				FlexVolume: &v1.FlexVolumeSource{
					Driver:    "ibm/ubiquity",
					FSType:    "",
					SecretRef: nil,
					ReadOnly:  false,
					Options:   volume_details,
				},
			},
		},
	}

	return pv, nil
}

// Delete removes the directory that was created by Provision backing the given
// PV.
func (p *flexProvisioner) Delete(volume *v1.PersistentVolume) error {
	if volume.Name == "" {
		return fmt.Errorf("volume name cannot be empty %#v", volume)
	}

	if volume.Spec.PersistentVolumeReclaimPolicy != v1.PersistentVolumeReclaimRetain {
		getVolumeRequest := resources.GetVolumeRequest{Name: volume.Name}
		volume, err := p.ubiquityClient.GetVolume(getVolumeRequest)
		if err != nil {
			fmt.Printf("error-retrieving-volume-info")
			return err
		}
		removeVolumeRequest := resources.RemoveVolumeRequest{Name: volume.Name}
		err = p.ubiquityClient.RemoveVolume(removeVolumeRequest)
		if err != nil {
			return err
		}
		return nil

	}

	return nil
}

func (p *flexProvisioner) createVolume(options controller.VolumeOptions, capacity int64) (map[string]string, error) {
	ubiquityParams := make(map[string]interface{})
	if capacity != 0 {
		ubiquityParams["quota"] = fmt.Sprintf("%dM", capacity)
	}
	for key, value := range options.Parameters {
		ubiquityParams[key] = value
	}
	backendName, exists := ubiquityParams["backend"]
	if !exists {
		return nil, fmt.Errorf("backend is not specified")
	}
	b := backendName.(string)
	createVolumeRequest := resources.CreateVolumeRequest{Name: options.PVName, Backend: b, Opts: ubiquityParams}
	err := p.ubiquityClient.CreateVolume(createVolumeRequest)
	if err != nil {
		return nil, fmt.Errorf("error creating volume: %v", err)
	}

	getVolumeConfigRequest := resources.GetVolumeConfigRequest{Name: options.PVName}
	volumeConfig, err := p.ubiquityClient.GetVolumeConfig(getVolumeConfigRequest)
	if err != nil {
		return nil, fmt.Errorf("error getting volume config details: %v", err)
	}

	flexVolumeConfig := make(map[string]string)
	flexVolumeConfig["volumeName"] = options.PVName
	for key, value := range volumeConfig {
		flexVolumeConfig[key] = fmt.Sprintf("%v", value)
	}

	return flexVolumeConfig, nil
}
<commit_msg>#34 : add size for scbe in GB<commit_after>/**
 * Copyright 2017 IBM Corp.
 *
 * 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 volume

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"strings"

	"github.com/IBM/ubiquity/resources"
	"github.com/kubernetes-incubator/external-storage/lib/controller"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/uuid"
	"k8s.io/client-go/pkg/api/v1"
)

const (

	// are we allowed to set this? else make up our own
	annCreatedBy = "kubernetes.io/createdby"
	createdBy    = "ubiquity-provisioner"

	// Name of the file where an nfsProvisioner will store its identity
	identityFile = "ubiquity-provisioner.identity"

	// VolumeGidAnnotationKey is the key of the annotation on the PersistentVolume
	// object that specifies a supplemental GID.
	VolumeGidAnnotationKey = "pv.beta.kubernetes.io/gid"

	// A PV annotation for the identity of the flexProvisioner that provisioned it
	annProvisionerId = "Provisioner_Id"

	podIPEnv     = "POD_IP"
	serviceEnv   = "SERVICE_NAME"
	namespaceEnv = "POD_NAMESPACE"
	nodeEnv      = "NODE_NAME"
)

func NewFlexProvisioner(logger *log.Logger, ubiquityClient resources.StorageClient, config resources.UbiquityPluginConfig) (controller.Provisioner, error) {
	return newFlexProvisionerInternal(logger, ubiquityClient, config)
}

func newFlexProvisionerInternal(logger *log.Logger, ubiquityClient resources.StorageClient, config resources.UbiquityPluginConfig) (*flexProvisioner, error) {
	var identity types.UID
	identityPath := path.Join(config.LogPath, identityFile)
	if _, err := os.Stat(identityPath); os.IsNotExist(err) {
		identity = uuid.NewUUID()
		err := ioutil.WriteFile(identityPath, []byte(identity), 0600)
		if err != nil {
			logger.Printf("Error writing identity file %s! %v", identityPath, err)
		}
	} else {
		read, err := ioutil.ReadFile(identityPath)
		if err != nil {
			logger.Printf("Error reading identity file %s! %v", config.LogPath, err)
		}
		identity = types.UID(strings.TrimSpace(string(read)))
	}
	provisioner := &flexProvisioner{
		logger:         logger,
		identity:       identity,
		ubiquityClient: ubiquityClient,
		ubiquityConfig: config,
		podIPEnv:       podIPEnv,
		serviceEnv:     serviceEnv,
		namespaceEnv:   namespaceEnv,
		nodeEnv:        nodeEnv,
	}

	activateRequest := resources.ActivateRequest{Backends: config.Backends}
	err := provisioner.ubiquityClient.Activate(activateRequest)

	return provisioner, err
}

type flexProvisioner struct {
	logger   *log.Logger
	identity types.UID
	// Whether the provisioner is running out of cluster and so cannot rely on
	// the existence of any of the pod, service, namespace, node env variables.
	outOfCluster bool

	ubiquityClient resources.StorageClient
	ubiquityConfig resources.UbiquityPluginConfig

	// Environment variables the provisioner pod needs valid values for in order to
	// put a service cluster IP as the server of provisioned NFS PVs, passed in
	// via downward API. If serviceEnv is set, namespaceEnv must be too.
	podIPEnv     string
	serviceEnv   string
	namespaceEnv string
	nodeEnv      string
}

// Provision creates a volume i.e. the storage asset and returns a PV object for
// the volume.
func (p *flexProvisioner) Provision(options controller.VolumeOptions) (*v1.PersistentVolume, error) {
	if options.PVC == nil {
		return nil, fmt.Errorf("options missing PVC %#v", options)
	}
	capacity, exists := options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
	if !exists {
		return nil, fmt.Errorf("options.PVC.Spec.Resources.Requests does not contain capacity")
	}
	fmt.Printf("PVC with capacity %d", capacity.Value())
	capacityMB := capacity.Value() / (1024 * 1024)

	volume_details, err := p.createVolume(options, capacityMB)
	if err != nil {
		return nil, err
	}

	annotations := make(map[string]string)
	annotations[annCreatedBy] = createdBy
	annotations[annProvisionerId] = "ubiquity-provisioner"

	pv := &v1.PersistentVolume{
		ObjectMeta: v1.ObjectMeta{
			Name:        options.PVName,
			Labels:      map[string]string{},
			Annotations: annotations,
		},
		Spec: v1.PersistentVolumeSpec{
			PersistentVolumeReclaimPolicy: options.PersistentVolumeReclaimPolicy,
			AccessModes:                   options.PVC.Spec.AccessModes,
			Capacity: v1.ResourceList{
				v1.ResourceName(v1.ResourceStorage): options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)],
			},
			PersistentVolumeSource: v1.PersistentVolumeSource{
				FlexVolume: &v1.FlexVolumeSource{
					Driver:    "ibm/ubiquity",
					FSType:    "",
					SecretRef: nil,
					ReadOnly:  false,
					Options:   volume_details,
				},
			},
		},
	}

	return pv, nil
}

// Delete removes the directory that was created by Provision backing the given
// PV.
func (p *flexProvisioner) Delete(volume *v1.PersistentVolume) error {
	if volume.Name == "" {
		return fmt.Errorf("volume name cannot be empty %#v", volume)
	}

	if volume.Spec.PersistentVolumeReclaimPolicy != v1.PersistentVolumeReclaimRetain {
		getVolumeRequest := resources.GetVolumeRequest{Name: volume.Name}
		volume, err := p.ubiquityClient.GetVolume(getVolumeRequest)
		if err != nil {
			fmt.Printf("error-retrieving-volume-info")
			return err
		}
		removeVolumeRequest := resources.RemoveVolumeRequest{Name: volume.Name}
		err = p.ubiquityClient.RemoveVolume(removeVolumeRequest)
		if err != nil {
			return err
		}
		return nil

	}

	return nil
}

func (p *flexProvisioner) createVolume(options controller.VolumeOptions, capacity int64) (map[string]string, error) {
	ubiquityParams := make(map[string]interface{})
	if capacity != 0 {
		ubiquityParams["quota"] = fmt.Sprintf("%dM", capacity)    // SSc backend expect quota option
		ubiquityParams["size"] = fmt.Sprintf("%d", capacity*1024) // SCBE backend expect size option
	}
	for key, value := range options.Parameters {
		ubiquityParams[key] = value
	}
	backendName, exists := ubiquityParams["backend"]
	if !exists {
		return nil, fmt.Errorf("backend is not specified")
	}
	b := backendName.(string)
	createVolumeRequest := resources.CreateVolumeRequest{Name: options.PVName, Backend: b, Opts: ubiquityParams}
	err := p.ubiquityClient.CreateVolume(createVolumeRequest)
	if err != nil {
		return nil, fmt.Errorf("error creating volume: %v", err)
	}

	getVolumeConfigRequest := resources.GetVolumeConfigRequest{Name: options.PVName}
	volumeConfig, err := p.ubiquityClient.GetVolumeConfig(getVolumeConfigRequest)
	if err != nil {
		return nil, fmt.Errorf("error getting volume config details: %v", err)
	}

	flexVolumeConfig := make(map[string]string)
	flexVolumeConfig["volumeName"] = options.PVName
	for key, value := range volumeConfig {
		flexVolumeConfig[key] = fmt.Sprintf("%v", value)
	}

	return flexVolumeConfig, nil
}
<|endoftext|>


<commit_before>package drivers

import (
	"errors"
	"os"
	"path"

	"github.com/golang/glog"
)

// OnDiskStorageController is a way of testing the StorageController backend to ensure that all of the logic is correct.
type OnDiskStorageController struct {
	FSPath string
}

// NewOnDiskStorageController creates a new OnDiskStorageController
func NewOnDiskStorageController(path string) OnDiskStorageController {

	warning := `
********************************************************************************
*                                                                              *
*                                WARNING!!!                                    *
*             DO NOT USE the "On-Disk Controller" IN PRODUCTION!               *
*                                                                              *
*        You are currently using the On-Disk Storage Controller, it            *
*        operates on the host's disk with out the benifits of rdma and         *
*        should only be used for testing purposes. All data saved in           *
*        the volumes will still be available by manually accessing the         *
*        data in the host's filesystem.                                        *
*                                                                              *
********************************************************************************
	`

	glog.Warning(warning)

	if path == "" {
		path = "/etc/docker/mounts/"
	}

	_, err := os.Open(path)
	if err != nil {
		os.MkdirAll(path, 0755)
		_, err = os.Open(path)
		if err != nil {
			glog.Fatal("Unable to create folder ", path, ". ", err)
		}
	}

	return OnDiskStorageController{path}
}

// Connect is a NOOP
func (d OnDiskStorageController) Connect() error {
	glog.Info("Connect function called, no action taken.")
	return nil
}

// Disconnect is a NOOP
func (d OnDiskStorageController) Disconnect() error {
	glog.Info("Disconnect function called, no action taken.")
	return nil
}

// Mount a particular volume
func (d OnDiskStorageController) Mount(volumeName string) (string, error) {
	pathMounted := path.Join(d.FSPath, volumeName)
	pathUnmounted := path.Join(path.Dir(pathMounted), path.Base(pathMounted)+".unmounted")

	// If there is an unmounted volume, return it.
	_, err := os.Open(pathUnmounted)
	if err == nil {
		glog.Info("Renaming: ", pathMounted, " to ", pathUnmounted)
		return pathMounted, os.Rename(pathUnmounted, pathMounted)
	}

	_, err = os.Open(pathMounted)
	if err != nil {
		glog.Info("Creating: ", pathMounted)
		os.MkdirAll(pathMounted, 0755)
		_, err = os.Open(pathMounted)
		if err != nil {
			return "", err
		}
	}

	return pathMounted, nil
}

// Unmount a particular volume
func (d OnDiskStorageController) Unmount(volumeName string) error {
	pathMounted := path.Join(d.FSPath, volumeName)
	pathUnmounted := path.Join(path.Dir(pathMounted), path.Base(pathMounted)+".unmounted")

	glog.Info(pathMounted)

	// If there is an unmounted volume, return it.
	_, err := os.Open(pathMounted)
	if err == nil {
		glog.Info("Renaming: ", pathMounted, " to ", pathUnmounted)
		return os.Rename(pathMounted, pathUnmounted)
	}

	_, err = os.Open(pathUnmounted)
	if err != nil {
		return err
	}

	return errors.New("Already unmounted.")
}

// Delete a particular volume
func (d OnDiskStorageController) Delete(volumeName string) error {
	pathMounted := path.Join(d.FSPath, volumeName)
	pathUnmounted := path.Join(path.Dir(pathMounted), path.Base(pathMounted)+".unmounted")

	// If there is an unmounted volume, return it.
	_, err := os.Open(pathUnmounted)
	if err == nil {
		return os.Remove(pathUnmounted)
	}

	_, err = os.Open(pathMounted)
	if err != nil {
		return err
	}

	return os.Remove(pathMounted)
}
<commit_msg>Fix bug where on-disk driver would return an error if volume was removed and never mounted<commit_after>package drivers

import (
	"errors"
	"os"
	"path"

	"github.com/golang/glog"
)

// OnDiskStorageController is a way of testing the StorageController backend to ensure that all of the logic is correct.
type OnDiskStorageController struct {
	FSPath string
}

// NewOnDiskStorageController creates a new OnDiskStorageController
func NewOnDiskStorageController(path string) OnDiskStorageController {

	warning := `
********************************************************************************
*                                                                              *
*                                WARNING!!!                                    *
*             DO NOT USE the "On-Disk Controller" IN PRODUCTION!               *
*                                                                              *
*        You are currently using the On-Disk Storage Controller, it            *
*        operates on the host's disk with out the benifits of rdma and         *
*        should only be used for testing purposes. All data saved in           *
*        the volumes will still be available by manually accessing the         *
*        data in the host's filesystem.                                        *
*                                                                              *
********************************************************************************
	`

	glog.Warning(warning)

	if path == "" {
		path = "/etc/docker/mounts/"
	}

	_, err := os.Open(path)
	if err != nil {
		os.MkdirAll(path, 0755)
		_, err = os.Open(path)
		if err != nil {
			glog.Fatal("Unable to create folder ", path, ". ", err)
		}
	}

	return OnDiskStorageController{path}
}

// Connect is a NOOP
func (d OnDiskStorageController) Connect() error {
	glog.Info("Connect function called, no action taken.")
	return nil
}

// Disconnect is a NOOP
func (d OnDiskStorageController) Disconnect() error {
	glog.Info("Disconnect function called, no action taken.")
	return nil
}

// Mount a particular volume
func (d OnDiskStorageController) Mount(volumeName string) (string, error) {
	pathMounted := path.Join(d.FSPath, volumeName)
	pathUnmounted := path.Join(path.Dir(pathMounted), path.Base(pathMounted)+".unmounted")

	// If there is an unmounted volume, return it.
	_, err := os.Open(pathUnmounted)
	if err == nil {
		glog.Info("Renaming: ", pathMounted, " to ", pathUnmounted)
		return pathMounted, os.Rename(pathUnmounted, pathMounted)
	}

	_, err = os.Open(pathMounted)
	if err != nil {
		glog.Info("Creating: ", pathMounted)
		os.MkdirAll(pathMounted, 0755)
		_, err = os.Open(pathMounted)
		if err != nil {
			return "", err
		}
	}

	return pathMounted, nil
}

// Unmount a particular volume
func (d OnDiskStorageController) Unmount(volumeName string) error {
	pathMounted := path.Join(d.FSPath, volumeName)
	pathUnmounted := path.Join(path.Dir(pathMounted), path.Base(pathMounted)+".unmounted")

	glog.Info(pathMounted)

	// If there is an unmounted volume, return it.
	_, err := os.Open(pathMounted)
	if err == nil {
		glog.Info("Renaming: ", pathMounted, " to ", pathUnmounted)
		return os.Rename(pathMounted, pathUnmounted)
	}

	_, err = os.Open(pathUnmounted)
	if err != nil {
		return err
	}

	return errors.New("Already unmounted.")
}

// Delete a particular volume
func (d OnDiskStorageController) Delete(volumeName string) error {
	pathMounted := path.Join(d.FSPath, volumeName)
	pathUnmounted := path.Join(path.Dir(pathMounted), path.Base(pathMounted)+".unmounted")

	// If there is an unmounted volume, return it.
	_, err := os.Open(pathUnmounted)
	if err == nil {
		return os.Remove(pathUnmounted)
	}

	_, err = os.Open(pathMounted)
	if err == nil {
		return os.Remove(pathMounted)
	}

	return nil
}
<|endoftext|>


<commit_before>// The chroot package is able to create an Amazon AMI without requiring
// the launch of a new instance for every build. It does this by attaching
// and mounting the root volume of another AMI and chrooting into that
// directory. It then creates an AMI from that attached drive.
package chroot

import (
	"errors"
	"github.com/mitchellh/goamz/ec2"
	"github.com/mitchellh/multistep"
	awscommon "github.com/mitchellh/packer/builder/amazon/common"
	"github.com/mitchellh/packer/builder/common"
	"github.com/mitchellh/packer/packer"
	"log"
	"runtime"
)

// The unique ID for this builder
const BuilderId = "mitchellh.amazon.chroot"

// Config is the configuration that is chained through the steps and
// settable from the template.
type Config struct {
	common.PackerConfig    `mapstructure:",squash"`
	awscommon.AccessConfig `mapstructure:",squash"`

	AttachedDevicePath string     `mapstructure:"attached_device_path"`
	ChrootMounts       [][]string `mapstructure:"chroot_mounts"`
	DevicePath         string     `mapstructure:"device_path"`
	MountCommand       string     `mapstructure:"mount_command"`
	MountPath          string     `mapstructure:"mount_path"`
	SourceAmi          string     `mapstructure:"source_ami"`
	UnmountCommand     string     `mapstructure:"unmount_command"`
}

type Builder struct {
	config Config
	runner multistep.Runner
}

func (b *Builder) Prepare(raws ...interface{}) error {
	md, err := common.DecodeConfig(&b.config, raws...)
	if err != nil {
		return err
	}

	// Defaults
	if b.config.ChrootMounts == nil {
		b.config.ChrootMounts = make([][]string, 0)
	}

	if len(b.config.ChrootMounts) == 0 {
		b.config.ChrootMounts = [][]string{
			[]string{"proc", "proc", "/proc"},
			[]string{"sysfs", "sysfs", "/sys"},
			[]string{"bind", "/dev", "/dev"},
			[]string{"devpts", "devpts", "/dev/pts"},
			[]string{"binfmt_misc", "binfmt_misc", "/proc/sys/fs/binfmt_misc"},
		}
	}

	if b.config.DevicePath == "" {
		b.config.DevicePath = "/dev/sdh"
	}

	if b.config.AttachedDevicePath == "" {
		b.config.AttachedDevicePath = "/dev/xvdh"
	}

	if b.config.MountCommand == "" {
		b.config.MountCommand = "mount"
	}

	if b.config.MountPath == "" {
		b.config.MountPath = "/var/packer-amazon-chroot/volumes/{{.Device}}"
	}

	if b.config.UnmountCommand == "" {
		b.config.UnmountCommand = "umount"
	}

	// Accumulate any errors
	errs := common.CheckUnusedConfig(md)
	errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare()...)

	if b.config.SourceAmi == "" {
		errs = packer.MultiErrorAppend(errs, errors.New("source_ami is required."))
	}

	if errs != nil && len(errs.Errors) > 0 {
		return errs
	}

	log.Printf("Config: %+v", b.config)
	return nil
}

func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
	if runtime.GOOS != "linux" {
		return nil, errors.New("The amazon-chroot builder only works on Linux environments.")
	}

	region, err := b.config.Region()
	if err != nil {
		return nil, err
	}

	auth, err := b.config.AccessConfig.Auth()
	if err != nil {
		return nil, err
	}

	ec2conn := ec2.New(auth, region)

	// Setup the state bag and initial state for the steps
	state := make(map[string]interface{})
	state["config"] = &b.config
	state["ec2"] = ec2conn
	state["hook"] = hook
	state["ui"] = ui

	// Build the steps
	steps := []multistep.Step{
		&StepInstanceInfo{},
		&StepSourceAMIInfo{},
		&StepCreateVolume{},
		&StepAttachVolume{},
		&StepMountDevice{},
		&StepMountExtra{},
	}

	// Run!
	if b.config.PackerDebug {
		b.runner = &multistep.DebugRunner{
			Steps:   steps,
			PauseFn: common.MultistepDebugFn(ui),
		}
	} else {
		b.runner = &multistep.BasicRunner{Steps: steps}
	}

	b.runner.Run(state)

	// If there was an error, return that
	if rawErr, ok := state["error"]; ok {
		return nil, rawErr.(error)
	}

	// If there are no AMIs, then just return
	if _, ok := state["amis"]; !ok {
		return nil, nil
	}

	// Build the artifact and return it
	artifact := &awscommon.Artifact{
		Amis:           state["amis"].(map[string]string),
		BuilderIdValue: BuilderId,
		Conn:           ec2conn,
	}

	return artifact, nil
}

func (b *Builder) Cancel() {
	if b.runner != nil {
		log.Println("Cancelling the step runner...")
		b.runner.Cancel()
	}
}
<commit_msg>builder/amazon/chroot: enable the chroot provisioner<commit_after>// The chroot package is able to create an Amazon AMI without requiring
// the launch of a new instance for every build. It does this by attaching
// and mounting the root volume of another AMI and chrooting into that
// directory. It then creates an AMI from that attached drive.
package chroot

import (
	"errors"
	"github.com/mitchellh/goamz/ec2"
	"github.com/mitchellh/multistep"
	awscommon "github.com/mitchellh/packer/builder/amazon/common"
	"github.com/mitchellh/packer/builder/common"
	"github.com/mitchellh/packer/packer"
	"log"
	"runtime"
)

// The unique ID for this builder
const BuilderId = "mitchellh.amazon.chroot"

// Config is the configuration that is chained through the steps and
// settable from the template.
type Config struct {
	common.PackerConfig    `mapstructure:",squash"`
	awscommon.AccessConfig `mapstructure:",squash"`

	AttachedDevicePath string     `mapstructure:"attached_device_path"`
	ChrootMounts       [][]string `mapstructure:"chroot_mounts"`
	DevicePath         string     `mapstructure:"device_path"`
	MountCommand       string     `mapstructure:"mount_command"`
	MountPath          string     `mapstructure:"mount_path"`
	SourceAmi          string     `mapstructure:"source_ami"`
	UnmountCommand     string     `mapstructure:"unmount_command"`
}

type Builder struct {
	config Config
	runner multistep.Runner
}

func (b *Builder) Prepare(raws ...interface{}) error {
	md, err := common.DecodeConfig(&b.config, raws...)
	if err != nil {
		return err
	}

	// Defaults
	if b.config.ChrootMounts == nil {
		b.config.ChrootMounts = make([][]string, 0)
	}

	if len(b.config.ChrootMounts) == 0 {
		b.config.ChrootMounts = [][]string{
			[]string{"proc", "proc", "/proc"},
			[]string{"sysfs", "sysfs", "/sys"},
			[]string{"bind", "/dev", "/dev"},
			[]string{"devpts", "devpts", "/dev/pts"},
			[]string{"binfmt_misc", "binfmt_misc", "/proc/sys/fs/binfmt_misc"},
		}
	}

	if b.config.DevicePath == "" {
		b.config.DevicePath = "/dev/sdh"
	}

	if b.config.AttachedDevicePath == "" {
		b.config.AttachedDevicePath = "/dev/xvdh"
	}

	if b.config.MountCommand == "" {
		b.config.MountCommand = "mount"
	}

	if b.config.MountPath == "" {
		b.config.MountPath = "/var/packer-amazon-chroot/volumes/{{.Device}}"
	}

	if b.config.UnmountCommand == "" {
		b.config.UnmountCommand = "umount"
	}

	// Accumulate any errors
	errs := common.CheckUnusedConfig(md)
	errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare()...)

	if b.config.SourceAmi == "" {
		errs = packer.MultiErrorAppend(errs, errors.New("source_ami is required."))
	}

	if errs != nil && len(errs.Errors) > 0 {
		return errs
	}

	log.Printf("Config: %+v", b.config)
	return nil
}

func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
	if runtime.GOOS != "linux" {
		return nil, errors.New("The amazon-chroot builder only works on Linux environments.")
	}

	region, err := b.config.Region()
	if err != nil {
		return nil, err
	}

	auth, err := b.config.AccessConfig.Auth()
	if err != nil {
		return nil, err
	}

	ec2conn := ec2.New(auth, region)

	// Setup the state bag and initial state for the steps
	state := make(map[string]interface{})
	state["config"] = &b.config
	state["ec2"] = ec2conn
	state["hook"] = hook
	state["ui"] = ui

	// Build the steps
	steps := []multistep.Step{
		&StepInstanceInfo{},
		&StepSourceAMIInfo{},
		&StepCreateVolume{},
		&StepAttachVolume{},
		&StepMountDevice{},
		&StepMountExtra{},
		&StepChrootProvision{},
	}

	// Run!
	if b.config.PackerDebug {
		b.runner = &multistep.DebugRunner{
			Steps:   steps,
			PauseFn: common.MultistepDebugFn(ui),
		}
	} else {
		b.runner = &multistep.BasicRunner{Steps: steps}
	}

	b.runner.Run(state)

	// If there was an error, return that
	if rawErr, ok := state["error"]; ok {
		return nil, rawErr.(error)
	}

	// If there are no AMIs, then just return
	if _, ok := state["amis"]; !ok {
		return nil, nil
	}

	// Build the artifact and return it
	artifact := &awscommon.Artifact{
		Amis:           state["amis"].(map[string]string),
		BuilderIdValue: BuilderId,
		Conn:           ec2conn,
	}

	return artifact, nil
}

func (b *Builder) Cancel() {
	if b.runner != nil {
		log.Println("Cancelling the step runner...")
		b.runner.Cancel()
	}
}
<|endoftext|>


<commit_before>package github

import (
	"fmt"
	"os"
	"strings"

	"github.com/hashicorp/vault/api"
)

type CLIHandler struct{}

func (h *CLIHandler) Auth(c *api.Client, m map[string]string) (*api.Secret, error) {
	mount, ok := m["mount"]
	if !ok {
		mount = "github"
	}

	token, ok := m["token"]
	if !ok {
		if token = os.Getenv("VAULT_AUTH_GITHUB_TOKEN"); token == "" {
			return nil, fmt.Errorf("GitHub token should be provided either as 'value' for 'token' key,\nor via an env var VAULT_AUTH_GITHUB_TOKEN")
		}
	}

	path := fmt.Sprintf("auth/%s/login", mount)
	secret, err := c.Logical().Write(path, map[string]interface{}{
		"token": token,
	})
	if err != nil {
		return nil, err
	}
	if secret == nil {
		return nil, fmt.Errorf("empty response from credential provider")
	}

	return secret, nil
}

func (h *CLIHandler) Help() string {
	help := `
Usage: vault login -method=github [CONFIG K=V...]

  The GitHub auth method allows users to authenticate using a GitHub
  personal access token. Users can generate a personal access token from the
  settings page on their GitHub account.

  Authenticate using a GitHub token:

      $ vault login -method=github token=abcd1234

Configuration:

  mount=<string>
      Path where the GitHub credential method is mounted. This is usually
      provided via the -path flag in the "vault login" command, but it can be
      specified here as well. If specified here, it takes precedence over the
      value for -path. The default value is "github".

  token=<string>
      GitHub personal access token to use for authentication.
`

	return strings.TrimSpace(help)
}
<commit_msg>Prompt for GitHub token if not provided<commit_after>package github

import (
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/hashicorp/vault/api"
	"github.com/hashicorp/vault/helper/password"
)

type CLIHandler struct {
	// for tests
	testStdout io.Writer
}

func (h *CLIHandler) Auth(c *api.Client, m map[string]string) (*api.Secret, error) {
	mount, ok := m["mount"]
	if !ok {
		mount = "github"
	}

	// Extract or prompt for token
	token := m["token"]
	if token == "" {
		token = os.Getenv("VAULT_AUTH_GITHUB_TOKEN")
	}
	if token == "" {
		// Override the output
		stdout := h.testStdout
		if stdout == nil {
			stdout = os.Stdout
		}

		var err error
		fmt.Fprintf(stdout, "GitHub Personal Access Token (will be hidden): ")
		token, err = password.Read(os.Stdin)
		fmt.Fprintf(stdout, "\n")
		if err != nil {
			if err == password.ErrInterrupted {
				return nil, fmt.Errorf("user interrupted")
			}

			return nil, fmt.Errorf("An error occurred attempting to "+
				"ask for a token. The raw error message is shown below, but usually "+
				"this is because you attempted to pipe a value into the command or "+
				"you are executing outside of a terminal (tty). If you want to pipe "+
				"the value, pass \"-\" as the argument to read from stdin. The raw "+
				"error was: %s", err)
		}
	}

	path := fmt.Sprintf("auth/%s/login", mount)
	secret, err := c.Logical().Write(path, map[string]interface{}{
		"token": strings.TrimSpace(token),
	})
	if err != nil {
		return nil, err
	}
	if secret == nil {
		return nil, fmt.Errorf("empty response from credential provider")
	}

	return secret, nil
}

func (h *CLIHandler) Help() string {
	help := `
Usage: vault login -method=github [CONFIG K=V...]

  The GitHub auth method allows users to authenticate using a GitHub
  personal access token. Users can generate a personal access token from the
  settings page on their GitHub account.

  Authenticate using a GitHub token:

      $ vault login -method=github token=abcd1234

Configuration:

  mount=<string>
      Path where the GitHub credential method is mounted. This is usually
      provided via the -path flag in the "vault login" command, but it can be
      specified here as well. If specified here, it takes precedence over the
      value for -path. The default value is "github".

  token=<string>
      GitHub personal access token to use for authentication. If not provided,
      Vault will prompt for the value.
`

	return strings.TrimSpace(help)
}
<|endoftext|>


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

package pathutil_test

import (
	"fmt"
	"go/build"
	"nvim-go/pathutil"
	"os"
	"path/filepath"
	"testing"
)

func TestIsGb(t *testing.T) {
	var (
		cwd, _ = os.Getwd()
		gopath = os.Getenv("GOPATH")
		gbroot = filepath.Dir(filepath.Dir(filepath.Dir(cwd)))
	)

	type args struct {
		dir string
	}
	tests := []struct {
		name  string
		tool  string
		args  args
		want  string
		want1 bool
	}{
		{
			name:  "go github.com/constabulary/gb",
			tool:  "go",
			args:  args{dir: filepath.Join(gopath, "/src/github.com/constabulary/gb")}, // On the $GOPATH package
			want:  "",
			want1: false,
		},
		{
			name:  "go github.com/constabulary/gb/cmd/gb",
			tool:  "go",
			args:  args{dir: filepath.Join(gopath, "/src/github.com/constabulary/gb/cmd/gb")}, // On the $GOPATH package
			want:  "",
			want1: false,
		},
		{
			name:  "gb (nvim-go root)",
			tool:  "gb",
			args:  args{dir: gbroot}, //gb procject root directory
			want:  gbroot,            // nvim-go/src/nvim-go/context
			want1: true,
		},
		{
			name:  "gb (nvim-go/src/nvim-go)",
			tool:  "gb",
			args:  args{dir: filepath.Join(gbroot, "/src/nvim-go")}, // gb source root directory
			want:  gbroot,
			want1: true,
		},
		{
			name:  "gb (nvim-go/src/nvim-go/vendor)",
			tool:  "gb",
			args:  args{dir: filepath.Join(gbroot, "src", "nvim-go", "vendor")}, // gb vendor directory
			want:  gbroot,
			want1: true,
		},
		{
			name:  "gb (nvim-go/src/nvim-go/commands)",
			tool:  "gb",
			args:  args{dir: filepath.Join(gbroot, "/src/nvim-go/src/nvim-go/commands")}, // commands directory
			want:  gbroot,
			want1: true,
		},
		{
			name:  "gb (nvim-go/src/nvim-go/commands/guru)",
			tool:  "gb",
			args:  args{dir: filepath.Join(gbroot, "/src/nvim-go/src/nvim-go/internel/guru")}, // internal directory
			want:  gbroot,
			want1: true,
		},
		{
			name:  "wrong path",
			tool:  "gb",
			args:  args{dir: "a/b/c"}, // internal directory
			want:  "",
			want1: false,
		},
	}
	for _, tt := range tests {
		switch tt.tool {
		case "go":
			build.Default.GOPATH = gopath
		case "gb":
			build.Default.GOPATH = fmt.Sprintf("%s:%s/vendor", projectRoot, projectRoot)
		}

		t.Run(tt.name, func(t *testing.T) {
			got, got1 := pathutil.IsGb(tt.args.dir)
			if got != tt.want {
				t.Errorf("IsGb(%v) got = %v, want %v", tt.args.dir, got, tt.want)
			}
			if got1 != tt.want1 {
				t.Errorf("IsGb(%v) got1 = %v, want %v", tt.args.dir, got1, tt.want1)
			}
		})
	}
}

func TestFindGbProjectRoot(t *testing.T) {
	type args struct {
		path string
	}
	tests := []struct {
		name    string
		args    args
		want    string
		wantErr bool
	}{
		{
			name:    "nvim-go root",
			args:    args{path: projectRoot},
			want:    projectRoot,
			wantErr: false,
		},
		{
			name:    "nvim-go with /src/commands",
			args:    args{path: filepath.Join(projectRoot, "src", "commands")},
			want:    projectRoot,
			wantErr: false,
		},
		{
			name:    "gsftp",
			args:    args{path: filepath.Join(testGbPath, "gsftp", "src", "cmd", "gsftp")},
			want:    filepath.Join(testGbPath, "gsftp"),
			wantErr: false,
		},
		{
			name:    "empty path",
			args:    args{path: ""},
			want:    "",
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := pathutil.FindGbProjectRoot(tt.args.path)
			if (err != nil) != tt.wantErr {
				t.Errorf("FindGbProjectRoot(%v) error = %v, wantErr %v", tt.args.path, err, tt.wantErr)
				return
			}
			if got != tt.want {
				t.Errorf("FindGbProjectRoot(%v) = %v, want %v", tt.args.path, got, tt.want)
			}
		})
	}
}

func TestGbProjectName(t *testing.T) {
	type args struct {
		projectRoot string
	}
	tests := []struct {
		name string
		args args
		want string
	}{
		{
			name: "nvim-go",
			args: args{projectRoot: projectRoot},
			want: "nvim-go",
		},
		{
			name: "gsftp",
			args: args{projectRoot: filepath.Join(testGbPath, "gsftp")},
			want: "gsftp",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := pathutil.GbProjectName(tt.args.projectRoot); got != tt.want {
				t.Errorf("GbProjectName(%v) = %v, want %v", tt.args.projectRoot, got, tt.want)
			}
		})
	}
}
<commit_msg>test/pathutil: Add gb vendor testcase and fix test name<commit_after>// Copyright 2016 The nvim-go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package pathutil_test

import (
	"fmt"
	"go/build"
	"nvim-go/pathutil"
	"os"
	"path/filepath"
	"testing"
)

func TestIsGb(t *testing.T) {
	var (
		cwd, _ = os.Getwd()
		gopath = os.Getenv("GOPATH")
		gbroot = filepath.Dir(filepath.Dir(filepath.Dir(cwd)))
	)

	type args struct {
		dir string
	}
	tests := []struct {
		name  string
		tool  string
		args  args
		want  string
		want1 bool
	}{
		{
			name:  "go (On the $GOPATH package)",
			tool:  "go",
			args:  args{dir: filepath.Join(gopath, "/src/github.com/constabulary/gb")},
			want:  "",
			want1: false,
		},
		{
			name:  "go (On the $GOPATH package with cmd/gb directory)",
			tool:  "go",
			args:  args{dir: filepath.Join(gopath, "/src/github.com/constabulary/gb/cmd/gb")},
			want:  "",
			want1: false,
		},
		{
			name:  "gb (nvim-go root)",
			tool:  "gb",
			args:  args{dir: projectRoot}, // gb procject root directory
			want:  projectRoot,            // nvim-go/src/nvim-go/context
			want1: true,
		},
		{
			name:  "gb (gb source root directory)",
			tool:  "gb",
			args:  args{dir: filepath.Join(projectRoot, "src", "nvim-go")},
			want:  projectRoot,
			want1: true,
		},
		{
			name:  "gb (gb vendor directory)",
			tool:  "gb",
			args:  args{dir: filepath.Join(projectRoot, "src", "nvim-go", "vendor")},
			want:  projectRoot,
			want1: true,
		},
		{
			name:  "gb (On the gb vendor directory)",
			tool:  "gb",
			args:  args{dir: filepath.Join(projectRoot, "vendor", "src", "github.com", "neovim", "go-client", "nvim")},
			want:  projectRoot,
			want1: true,
		},
		{
			name:  "gb (nvim-go commands directory)",
			tool:  "gb",
			args:  args{dir: filepath.Join(projectRoot, "src", "nvim-go", "src", "nvim-go", "commands")},
			want:  gbroot,
			want1: true,
		},
		{
			name:  "gb (nvim-go internal directory)",
			tool:  "gb",
			args:  args{dir: filepath.Join(gbroot, "src", "nvim-go", "src", "nvim-go", "internel", "guru")},
			want:  gbroot,
			want1: true,
		},
		{
			name:  "wrong path",
			tool:  "gb",
			args:  args{dir: "a/b/c"}, // internal directory
			want:  "",
			want1: false,
		},
	}
	for _, tt := range tests {
		switch tt.tool {
		case "go":
			build.Default.GOPATH = gopath
		case "gb":
			build.Default.GOPATH = fmt.Sprintf("%s:%s/vendor", projectRoot, projectRoot)
		}

		t.Run(tt.name, func(t *testing.T) {
			got, got1 := pathutil.IsGb(tt.args.dir)
			if got != tt.want {
				t.Errorf("IsGb(%v) got = %v, want %v", tt.args.dir, got, tt.want)
			}
			if got1 != tt.want1 {
				t.Errorf("IsGb(%v) got1 = %v, want %v", tt.args.dir, got1, tt.want1)
			}
		})
	}
}

func TestFindGbProjectRoot(t *testing.T) {
	type args struct {
		path string
	}
	tests := []struct {
		name    string
		args    args
		want    string
		wantErr bool
	}{
		{
			name:    "nvim-go root",
			args:    args{path: projectRoot},
			want:    projectRoot,
			wantErr: false,
		},
		{
			name:    "nvim-go with /src/commands",
			args:    args{path: filepath.Join(projectRoot, "src", "commands")},
			want:    projectRoot,
			wantErr: false,
		},
		{
			name:    "gb vendor directory (return .../vendor)",
			args:    args{path: filepath.Join(projectRoot, "vendor", "src", "github.com", "neovim", "go-client", "nvim")},
			want:    filepath.Join(projectRoot, "vendor"),
			wantErr: false,
		},
		{
			name:    "gsftp",
			args:    args{path: filepath.Join(testGbPath, "gsftp", "src", "cmd", "gsftp")},
			want:    filepath.Join(testGbPath, "gsftp"),
			wantErr: false,
		},
		{
			name:    "empty path",
			args:    args{path: ""},
			want:    "",
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := pathutil.FindGbProjectRoot(tt.args.path)
			if (err != nil) != tt.wantErr {
				t.Errorf("FindGbProjectRoot(%v) error = %v, wantErr %v", tt.args.path, err, tt.wantErr)
				return
			}
			if got != tt.want {
				t.Errorf("FindGbProjectRoot(%v) = %v, want %v", tt.args.path, got, tt.want)
			}
		})
	}
}

func TestGbProjectName(t *testing.T) {
	type args struct {
		projectRoot string
	}
	tests := []struct {
		name string
		args args
		want string
	}{
		{
			name: "nvim-go",
			args: args{projectRoot: projectRoot},
			want: "nvim-go",
		},
		{
			name: "gsftp",
			args: args{projectRoot: filepath.Join(testGbPath, "gsftp")},
			want: "gsftp",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := pathutil.GbProjectName(tt.args.projectRoot); got != tt.want {
				t.Errorf("GbProjectName(%v) = %v, want %v", tt.args.projectRoot, got, tt.want)
			}
		})
	}
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

const (
	// File is the default name of the JSON file where the config written.
	// The user can pass an alternate filename when using the CLI.
	File = ".exercism.json"
	// LegacyFile is the name of the original config file.
	// It is a misnomer, since the config was in json, not go.
	LegacyFile = ".exercism.go"

	// hostAPI is the endpoint to submit solutions to, and to get personalized data
	hostAPI = "http://exercism.io"
	// hostXAPI is the endpoint to fetch problems from
	hostXAPI = "http://x.exercism.io"

	// DirExercises is the default name of the directory for active users.
	// Make this non-exported when handlers.Login is deleted.
	DirExercises = "exercism"
)

var (
	errHomeNotFound = errors.New("unable to locate home directory")
)

// Config represents the settings for particular user.
// This defines both the auth for talking to the API, as well as
// where to put problems that get downloaded.
type Config struct {
	APIKey string `json:"apiKey"`
	Dir    string `json:"dir"`
	API    string `json:"api"`
	XAPI   string `json:"xapi"`
	home   string // cache user's home directory
	file   string // full path to config file

	// deprecated, get rid of them when nobody uses 1.7.0 anymore
	ExercismDirectory string `json:"exercismDirectory,omitempty"`
	Hostname          string `json:"hostname,omitempty"`
	ProblemsHost      string `json:"problemsHost,omitempty"`
}

// Home returns the user's canonical home directory.
// See: http://stackoverflow.com/questions/7922270/obtain-users-home-directory
// we can't cross compile using cgo and use user.Current()
func Home() (string, error) {
	var dir string
	if runtime.GOOS == "windows" {
		dir = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
		if dir == "" {
			dir = os.Getenv("USERPROFILE")
		}
	} else {
		dir = os.Getenv("HOME")
	}

	if dir == "" {
		return dir, errHomeNotFound
	}
	return dir, nil
}

// Read loads the config from the stored JSON file.
func Read(file string) (*Config, error) {
	c := &Config{}
	err := c.Read(file)
	return c, err
}

// New returns a new config.
// It will attempt to set defaults where no value is passed in.
func New(key, host, dir string) (*Config, error) {
	c := &Config{
		APIKey: key,
		API:    host,
		Dir:    dir,
	}
	return c.configure()
}

func (c *Config) Update(key, host, dir string) {
	if key != "" {
		c.APIKey = key
	}

	if host != "" {
		c.API = host
	}

	if dir != "" {
		c.Dir = dir
	}
	c.configure()
}

// Read loads the config from the stored JSON file.
func (c *Config) Read(file string) error {
	renameLegacy()

	if file == "" {
		home, err := c.homeDir()
		if err != nil {
			return err
		}
		file = filepath.Join(home, File)
	}

	if _, err := os.Stat(file); err != nil {
		if os.IsNotExist(err) {
			c.configure()
			return nil
		}
		return err
	}

	f, err := os.Open(file)
	if err != nil {
		return err
	}
	defer f.Close()

	d := json.NewDecoder(f)
	err = d.Decode(&c)
	if err != nil {
		return err
	}
	c.SavePath(file)
	c.configure()
	return nil
}

// SavePath allows the user to customize the location of the JSON file.
func (c *Config) SavePath(file string) {
	if file != "" {
		c.file = file
	}
}

// File represents the path to the config file.
func (c *Config) File() string {
	return c.file
}

// Write() saves the config as JSON.
func (c *Config) Write() error {
	renameLegacy()
	c.ExercismDirectory = ""
	c.Hostname = ""
	c.ProblemsHost = ""

	// truncates existing file if it exists
	f, err := os.Create(c.file)
	if err != nil {
		return err
	}
	defer f.Close()

	e := json.NewEncoder(f)
	return e.Encode(c)
}

func (c *Config) configure() (*Config, error) {
	c.sanitize()

	if c.Hostname != "" {
		c.API = c.Hostname
	}

	if c.API == "" {
		c.API = hostAPI
	}

	if c.ProblemsHost != "" {
		c.XAPI = c.ProblemsHost
	}

	if c.XAPI == "" {
		c.XAPI = hostXAPI
	}

	dir, err := c.homeDir()
	if err != nil {
		return c, err
	}
	c.file = filepath.Join(dir, File)

	// use legacy value, if it exists
	if c.ExercismDirectory != "" {
		c.Dir = c.ExercismDirectory
	}

	// fall back to default value
	if c.Dir == "" {
		c.Dir = filepath.Join(dir, DirExercises)
	}

	err = c.setDir(c.Dir)
	if err != nil {
		return c, err
	}

	return c, nil
}

func (c *Config) setDir(dir string) error {
	homeDir, err := c.homeDir()
	if err != nil {
		return err
	}

	c.Dir = strings.Replace(dir, "~/", fmt.Sprintf("%s/", homeDir), 1)

	return nil
}

// FilePath returns the path to the config file.
func FilePath(file string) (string, error) {
	if file != "" {
		return file, nil
	}

	dir, err := Home()
	if err != nil {
		return "", err
	}
	return filepath.Join(dir, File), nil
}

// IsAuthenticated returns true if the config contains an API key.
// This does not check whether or not that key is valid.
func (c *Config) IsAuthenticated() bool {
	return c.APIKey != ""
}

// See: http://stackoverflow.com/questions/7922270/obtain-users-home-directory
// we can't cross compile using cgo and use user.Current()
func (c *Config) homeDir() (string, error) {
	if c.home != "" {
		return c.home, nil
	}
	return Home()
}

func (c *Config) sanitize() {
	c.APIKey = strings.TrimSpace(c.APIKey)
	c.Dir = strings.TrimSpace(c.Dir)
	c.API = strings.TrimSpace(c.API)
	c.XAPI = strings.TrimSpace(c.XAPI)
	c.Hostname = strings.TrimSpace(c.Hostname)
	c.ProblemsHost = strings.TrimSpace(c.ProblemsHost)
}

// renameLegacy normalizes the default config file name.
// This function will bail silently if any error occurs.
func renameLegacy() {
	dir, err := Home()
	if err != nil {
		return
	}

	legacyPath := filepath.Join(dir, LegacyFile)
	if _, err = os.Stat(legacyPath); err != nil {
		return
	}

	correctPath := filepath.Join(dir, File)
	os.Rename(legacyPath, correctPath)
	return
}
<commit_msg>Fix home dir for windows<commit_after>package config

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

const (
	// File is the default name of the JSON file where the config written.
	// The user can pass an alternate filename when using the CLI.
	File = ".exercism.json"
	// LegacyFile is the name of the original config file.
	// It is a misnomer, since the config was in json, not go.
	LegacyFile = ".exercism.go"

	// hostAPI is the endpoint to submit solutions to, and to get personalized data
	hostAPI = "http://exercism.io"
	// hostXAPI is the endpoint to fetch problems from
	hostXAPI = "http://x.exercism.io"

	// DirExercises is the default name of the directory for active users.
	// Make this non-exported when handlers.Login is deleted.
	DirExercises = "exercism"
)

var (
	errHomeNotFound = errors.New("unable to locate home directory")
)

// Config represents the settings for particular user.
// This defines both the auth for talking to the API, as well as
// where to put problems that get downloaded.
type Config struct {
	APIKey string `json:"apiKey"`
	Dir    string `json:"dir"`
	API    string `json:"api"`
	XAPI   string `json:"xapi"`
	home   string // cache user's home directory
	file   string // full path to config file

	// deprecated, get rid of them when nobody uses 1.7.0 anymore
	ExercismDirectory string `json:"exercismDirectory,omitempty"`
	Hostname          string `json:"hostname,omitempty"`
	ProblemsHost      string `json:"problemsHost,omitempty"`
}

// Home returns the user's canonical home directory.
// See: http://stackoverflow.com/questions/7922270/obtain-users-home-directory
// we can't cross compile using cgo and use user.Current()
func Home() (string, error) {
	var dir string
	if runtime.GOOS == "windows" {
		dir = os.Getenv("USERPROFILE")
		if dir == "" {
			dir = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
		}
	} else {
		dir = os.Getenv("HOME")
	}

	if dir == "" {
		return dir, errHomeNotFound
	}
	return dir, nil
}

// Read loads the config from the stored JSON file.
func Read(file string) (*Config, error) {
	c := &Config{}
	err := c.Read(file)
	return c, err
}

// New returns a new config.
// It will attempt to set defaults where no value is passed in.
func New(key, host, dir string) (*Config, error) {
	c := &Config{
		APIKey: key,
		API:    host,
		Dir:    dir,
	}
	return c.configure()
}

func (c *Config) Update(key, host, dir string) {
	if key != "" {
		c.APIKey = key
	}

	if host != "" {
		c.API = host
	}

	if dir != "" {
		c.Dir = dir
	}
	c.configure()
}

// Read loads the config from the stored JSON file.
func (c *Config) Read(file string) error {
	renameLegacy()

	if file == "" {
		home, err := c.homeDir()
		if err != nil {
			return err
		}
		file = filepath.Join(home, File)
	}

	if _, err := os.Stat(file); err != nil {
		if os.IsNotExist(err) {
			c.configure()
			return nil
		}
		return err
	}

	f, err := os.Open(file)
	if err != nil {
		return err
	}
	defer f.Close()

	d := json.NewDecoder(f)
	err = d.Decode(&c)
	if err != nil {
		return err
	}
	c.SavePath(file)
	c.configure()
	return nil
}

// SavePath allows the user to customize the location of the JSON file.
func (c *Config) SavePath(file string) {
	if file != "" {
		c.file = file
	}
}

// File represents the path to the config file.
func (c *Config) File() string {
	return c.file
}

// Write() saves the config as JSON.
func (c *Config) Write() error {
	renameLegacy()
	c.ExercismDirectory = ""
	c.Hostname = ""
	c.ProblemsHost = ""

	// truncates existing file if it exists
	f, err := os.Create(c.file)
	if err != nil {
		return err
	}
	defer f.Close()

	e := json.NewEncoder(f)
	return e.Encode(c)
}

func (c *Config) configure() (*Config, error) {
	c.sanitize()

	if c.Hostname != "" {
		c.API = c.Hostname
	}

	if c.API == "" {
		c.API = hostAPI
	}

	if c.ProblemsHost != "" {
		c.XAPI = c.ProblemsHost
	}

	if c.XAPI == "" {
		c.XAPI = hostXAPI
	}

	dir, err := c.homeDir()
	if err != nil {
		return c, err
	}
	c.file = filepath.Join(dir, File)

	// use legacy value, if it exists
	if c.ExercismDirectory != "" {
		c.Dir = c.ExercismDirectory
	}

	// fall back to default value
	if c.Dir == "" {
		c.Dir = filepath.Join(dir, DirExercises)
	}

	err = c.setDir(c.Dir)
	if err != nil {
		return c, err
	}

	return c, nil
}

func (c *Config) setDir(dir string) error {
	homeDir, err := c.homeDir()
	if err != nil {
		return err
	}

	c.Dir = strings.Replace(dir, "~/", fmt.Sprintf("%s/", homeDir), 1)

	return nil
}

// FilePath returns the path to the config file.
func FilePath(file string) (string, error) {
	if file != "" {
		return file, nil
	}

	dir, err := Home()
	if err != nil {
		return "", err
	}
	return filepath.Join(dir, File), nil
}

// IsAuthenticated returns true if the config contains an API key.
// This does not check whether or not that key is valid.
func (c *Config) IsAuthenticated() bool {
	return c.APIKey != ""
}

// See: http://stackoverflow.com/questions/7922270/obtain-users-home-directory
// we can't cross compile using cgo and use user.Current()
func (c *Config) homeDir() (string, error) {
	if c.home != "" {
		return c.home, nil
	}
	return Home()
}

func (c *Config) sanitize() {
	c.APIKey = strings.TrimSpace(c.APIKey)
	c.Dir = strings.TrimSpace(c.Dir)
	c.API = strings.TrimSpace(c.API)
	c.XAPI = strings.TrimSpace(c.XAPI)
	c.Hostname = strings.TrimSpace(c.Hostname)
	c.ProblemsHost = strings.TrimSpace(c.ProblemsHost)
}

// renameLegacy normalizes the default config file name.
// This function will bail silently if any error occurs.
func renameLegacy() {
	dir, err := Home()
	if err != nil {
		return
	}

	legacyPath := filepath.Join(dir, LegacyFile)
	if _, err = os.Stat(legacyPath); err != nil {
		return
	}

	correctPath := filepath.Join(dir, File)
	os.Rename(legacyPath, correctPath)
	return
}
<|endoftext|>