<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"time"

	"github.com/99designs/aws-vault/keyring"
	"github.com/99designs/aws-vault/prompt"
	"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"
)

const (
	MaxSessionDuration    = time.Hour * 36
	MinSessionDuration    = time.Minute * 15
	MinAssumeRoleDuration = time.Minute * 15
	MaxAssumeRoleDuration = time.Hour

	DefaultSessionDuration    = time.Hour * 4
	DefaultAssumeRoleDuration = time.Minute * 15
)

type VaultOptions struct {
	SessionDuration    time.Duration
	AssumeRoleDuration time.Duration
	ExpiryWindow       time.Duration
	MfaToken           string
	MfaPrompt          prompt.PromptFunc
	NoSession          bool
	Profiles           profiles
}

func (o VaultOptions) Validate() error {
	if o.SessionDuration < MinSessionDuration {
		return errors.New("Minimum session duration is " + MinSessionDuration.String())
	} else if o.SessionDuration > MaxSessionDuration {
		return errors.New("Maximum session duration is " + MaxSessionDuration.String())
	}
	if o.AssumeRoleDuration < MinAssumeRoleDuration {
		return errors.New("Minimum duration for assumed roles is " + MinAssumeRoleDuration.String())
	} else if o.AssumeRoleDuration > MaxAssumeRoleDuration {
		return errors.New("Maximum duration for assumed roles is " + MaxAssumeRoleDuration.String())
	}

	return nil
}

func (o VaultOptions) ApplyDefaults() VaultOptions {
	if o.AssumeRoleDuration == 0 {
		o.AssumeRoleDuration = DefaultAssumeRoleDuration
	}
	if o.SessionDuration == 0 {
		o.SessionDuration = DefaultSessionDuration
	}
	return o
}

type VaultProvider struct {
	credentials.Expiry
	VaultOptions
	profile  string
	expires  time.Time
	keyring  keyring.Keyring
	sessions *KeyringSessions
	profiles profiles
	creds    map[string]credentials.Value
}

func NewVaultProvider(k keyring.Keyring, profile string, opts VaultOptions) (*VaultProvider, error) {
	opts = opts.ApplyDefaults()
	if err := opts.Validate(); err != nil {
		return nil, err
	}
	return &VaultProvider{
		VaultOptions: opts,
		keyring:      k,
		sessions:     &KeyringSessions{k, opts.Profiles},
		profile:      profile,
		profiles:     opts.Profiles,
		creds:        map[string]credentials.Value{},
	}, nil
}

func (p *VaultProvider) Retrieve() (credentials.Value, error) {
	creds, err := p.getMasterCreds()
	if err != nil {
		return credentials.Value{}, err
	}

	window := p.ExpiryWindow
	if window == 0 {
		window = time.Minute * 5
	}

	if p.NoSession {
		if role, ok := p.profiles[p.profile]["role_arn"]; ok {
			session, err := p.assumeRole(creds, role)
			if err != nil {
				return credentials.Value{}, err
			}

			log.Printf("Using role ****************%s, expires in %s",
				(*session.AccessKeyId)[len(*session.AccessKeyId)-4:],
				session.Expiration.Sub(time.Now()).String())

			p.SetExpiration(*session.Expiration, window)
			p.expires = *session.Expiration

			value := credentials.Value{
				AccessKeyID:     *session.AccessKeyId,
				SecretAccessKey: *session.SecretAccessKey,
				SessionToken:    *session.SessionToken,
			}

			return value, nil
		}

		// no role, exposes master credentials which don't expire
		return creds, nil
	}

	session, err := p.sessions.Retrieve(p.profile, p.SessionDuration)
	if err != nil {
		if err == keyring.ErrKeyNotFound {
			log.Println("Session not found in keyring")
		} else {
			log.Println(err)
		}

		session, err = p.getSessionToken(&creds)
		if err != nil {
			return credentials.Value{}, err
		}

		if err = p.sessions.Store(p.profile, session, p.SessionDuration); err != nil {
			return credentials.Value{}, err
		}
	}

	log.Printf("Using session ****************%s, expires in %s",
		(*session.AccessKeyId)[len(*session.AccessKeyId)-4:],
		session.Expiration.Sub(time.Now()).String())

	if role, ok := p.profiles[p.profile]["role_arn"]; ok {
		session, err = p.assumeRoleFromSession(session, role)
		if err != nil {
			return credentials.Value{}, err
		}

		log.Printf("Using role ****************%s (from session token), expires in %s",
			(*session.AccessKeyId)[len(*session.AccessKeyId)-4:],
			session.Expiration.Sub(time.Now()).String())
	}

	p.SetExpiration(*session.Expiration, window)
	p.expires = *session.Expiration

	value := credentials.Value{
		AccessKeyID:     *session.AccessKeyId,
		SecretAccessKey: *session.SecretAccessKey,
		SessionToken:    *session.SessionToken,
	}

	return value, nil
}

func (p *VaultProvider) getMasterCreds() (credentials.Value, error) {
	source := sourceProfile(p.profile, p.profiles)

	creds, ok := p.creds[source]
	if !ok {
		provider := credentials.NewChainCredentials([]credentials.Provider{
			&credentials.EnvProvider{},
			&credentials.SharedCredentialsProvider{Filename: "", Profile: p.profile},
			&KeyringProvider{Keyring: p.keyring, Profile: source},
		})

		var err error
		if creds, err = provider.Get(); err != nil {
			log.Printf("Failed to find credentials for profile %q in keyring", source)
			return creds, err
		}

		p.creds[source] = creds
	}

	return creds, nil
}

func (p *VaultProvider) getSessionToken(creds *credentials.Value) (sts.Credentials, error) {
	params := &sts.GetSessionTokenInput{
		DurationSeconds: aws.Int64(int64(p.SessionDuration.Seconds())),
	}

	if mfa, ok := p.profiles[p.profile]["mfa_serial"]; ok {
		params.SerialNumber = aws.String(mfa)
		if p.MfaToken == "" {
			token, err := p.MfaPrompt(fmt.Sprintf("Enter token for %s: ", mfa))
			if err != nil {
				return sts.Credentials{}, err
			}
			params.TokenCode = aws.String(token)
		} else {
			params.TokenCode = aws.String(p.MfaToken)
		}
	}

	client := sts.New(session.New(&aws.Config{
		Credentials: credentials.NewCredentials(&credentials.StaticProvider{
			Value: *creds,
		}),
	}))

	log.Printf("Getting new session token for profile %s", sourceProfile(p.profile, p.profiles))
	resp, err := client.GetSessionToken(params)
	if err != nil {
		return sts.Credentials{}, err
	}

	return *resp.Credentials, nil
}

func (p *VaultProvider) roleSessionName() string {
	if name := p.profiles[p.profile]["role_session_name"]; name != "" {
		return name
	}

	// Try to work out a role name that will hopefully end up unique.
	return fmt.Sprintf("%d", time.Now().UTC().UnixNano())
}

// assumeRoleFromSession takes a session created with GetSessionToken and uses that to assume a role
func (p *VaultProvider) assumeRoleFromSession(creds sts.Credentials, roleArn string) (sts.Credentials, error) {
	client := sts.New(session.New(&aws.Config{Credentials: credentials.NewStaticCredentials(
		*creds.AccessKeyId,
		*creds.SecretAccessKey,
		*creds.SessionToken,
	)}))

	input := &sts.AssumeRoleInput{
		RoleArn:         aws.String(roleArn),
		RoleSessionName: aws.String(p.roleSessionName()),
		DurationSeconds: aws.Int64(int64(p.AssumeRoleDuration.Seconds())),
	}

	log.Printf("Assuming role %s from session token", roleArn)
	resp, err := client.AssumeRole(input)
	if err != nil {
		return sts.Credentials{}, err
	}

	return *resp.Credentials, nil
}

// assumeRole uses IAM credentials to assume a role
func (p *VaultProvider) assumeRole(creds credentials.Value, roleArn string) (sts.Credentials, error) {
	client := sts.New(session.New(&aws.Config{
		Credentials: credentials.NewCredentials(&credentials.StaticProvider{Value: creds}),
	}))

	input := &sts.AssumeRoleInput{
		RoleArn:         aws.String(roleArn),
		RoleSessionName: aws.String(p.roleSessionName()),
		DurationSeconds: aws.Int64(int64(p.AssumeRoleDuration.Seconds())),
	}

	// if we don't have a session, we need to include MFA token in the AssumeRole call
	if mfa, ok := p.profiles[p.profile]["mfa_serial"]; ok {
		input.SerialNumber = aws.String(mfa)
		if p.MfaToken == "" {
			token, err := p.MfaPrompt(fmt.Sprintf("Enter token for %s: ", mfa))
			if err != nil {
				return sts.Credentials{}, err
			}
			input.TokenCode = aws.String(token)
		} else {
			input.TokenCode = aws.String(p.MfaToken)
		}
	}

	log.Printf("Assuming role %s with iam credentials", roleArn)
	resp, err := client.AssumeRole(input)
	if err != nil {
		return sts.Credentials{}, err
	}

	return *resp.Credentials, nil
}

type KeyringProvider struct {
	Keyring keyring.Keyring
	Profile string
}

func (p *KeyringProvider) IsExpired() bool {
	return false
}

func (p *KeyringProvider) Retrieve() (val credentials.Value, err error) {
	log.Printf("Looking up keyring for %s", p.Profile)
	item, err := p.Keyring.Get(p.Profile)
	if err != nil {
		log.Println("Error from keyring", err)
		return val, err
	}
	if err = json.Unmarshal(item.Data, &val); err != nil {
		return val, fmt.Errorf("Invalid data in keyring: %v", err)
	}
	return val, err
}

func (p *KeyringProvider) Store(val credentials.Value) error {
	bytes, err := json.Marshal(val)
	if err != nil {
		return err
	}

	return p.Keyring.Set(keyring.Item{
		Key:  p.Profile,
		Data: bytes,
	})
}

func (p *KeyringProvider) Delete() error {
	return p.Keyring.Remove(p.Profile)
}

type VaultCredentials struct {
	*credentials.Credentials
	provider *VaultProvider
}

func NewVaultCredentials(k keyring.Keyring, profile string, opts VaultOptions) (*VaultCredentials, error) {
	provider, err := NewVaultProvider(k, profile, opts)
	if err != nil {
		return nil, err
	}

	return &VaultCredentials{credentials.NewCredentials(provider), provider}, nil
}

func (v *VaultCredentials) Expires() time.Time {
	return v.provider.expires
}
<commit_msg>Drop support for the env and shared credentials provider<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"time"

	"github.com/99designs/aws-vault/keyring"
	"github.com/99designs/aws-vault/prompt"
	"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"
)

const (
	MaxSessionDuration    = time.Hour * 36
	MinSessionDuration    = time.Minute * 15
	MinAssumeRoleDuration = time.Minute * 15
	MaxAssumeRoleDuration = time.Hour

	DefaultSessionDuration    = time.Hour * 4
	DefaultAssumeRoleDuration = time.Minute * 15
)

type VaultOptions struct {
	SessionDuration    time.Duration
	AssumeRoleDuration time.Duration
	ExpiryWindow       time.Duration
	MfaToken           string
	MfaPrompt          prompt.PromptFunc
	NoSession          bool
	Profiles           profiles
}

func (o VaultOptions) Validate() error {
	if o.SessionDuration < MinSessionDuration {
		return errors.New("Minimum session duration is " + MinSessionDuration.String())
	} else if o.SessionDuration > MaxSessionDuration {
		return errors.New("Maximum session duration is " + MaxSessionDuration.String())
	}
	if o.AssumeRoleDuration < MinAssumeRoleDuration {
		return errors.New("Minimum duration for assumed roles is " + MinAssumeRoleDuration.String())
	} else if o.AssumeRoleDuration > MaxAssumeRoleDuration {
		return errors.New("Maximum duration for assumed roles is " + MaxAssumeRoleDuration.String())
	}

	return nil
}

func (o VaultOptions) ApplyDefaults() VaultOptions {
	if o.AssumeRoleDuration == 0 {
		o.AssumeRoleDuration = DefaultAssumeRoleDuration
	}
	if o.SessionDuration == 0 {
		o.SessionDuration = DefaultSessionDuration
	}
	return o
}

type VaultProvider struct {
	credentials.Expiry
	VaultOptions
	profile  string
	expires  time.Time
	keyring  keyring.Keyring
	sessions *KeyringSessions
	profiles profiles
	creds    map[string]credentials.Value
}

func NewVaultProvider(k keyring.Keyring, profile string, opts VaultOptions) (*VaultProvider, error) {
	opts = opts.ApplyDefaults()
	if err := opts.Validate(); err != nil {
		return nil, err
	}
	return &VaultProvider{
		VaultOptions: opts,
		keyring:      k,
		sessions:     &KeyringSessions{k, opts.Profiles},
		profile:      profile,
		profiles:     opts.Profiles,
		creds:        map[string]credentials.Value{},
	}, nil
}

func (p *VaultProvider) Retrieve() (credentials.Value, error) {
	creds, err := p.getMasterCreds()
	if err != nil {
		return credentials.Value{}, err
	}

	window := p.ExpiryWindow
	if window == 0 {
		window = time.Minute * 5
	}

	if p.NoSession {
		if role, ok := p.profiles[p.profile]["role_arn"]; ok {
			session, err := p.assumeRole(creds, role)
			if err != nil {
				return credentials.Value{}, err
			}

			log.Printf("Using role ****************%s, expires in %s",
				(*session.AccessKeyId)[len(*session.AccessKeyId)-4:],
				session.Expiration.Sub(time.Now()).String())

			p.SetExpiration(*session.Expiration, window)
			p.expires = *session.Expiration

			value := credentials.Value{
				AccessKeyID:     *session.AccessKeyId,
				SecretAccessKey: *session.SecretAccessKey,
				SessionToken:    *session.SessionToken,
			}

			return value, nil
		}

		// no role, exposes master credentials which don't expire
		return creds, nil
	}

	session, err := p.sessions.Retrieve(p.profile, p.SessionDuration)
	if err != nil {
		if err == keyring.ErrKeyNotFound {
			log.Println("Session not found in keyring")
		} else {
			log.Println(err)
		}

		session, err = p.getSessionToken(&creds)
		if err != nil {
			return credentials.Value{}, err
		}

		if err = p.sessions.Store(p.profile, session, p.SessionDuration); err != nil {
			return credentials.Value{}, err
		}
	}

	log.Printf("Using session ****************%s, expires in %s",
		(*session.AccessKeyId)[len(*session.AccessKeyId)-4:],
		session.Expiration.Sub(time.Now()).String())

	if role, ok := p.profiles[p.profile]["role_arn"]; ok {
		session, err = p.assumeRoleFromSession(session, role)
		if err != nil {
			return credentials.Value{}, err
		}

		log.Printf("Using role ****************%s (from session token), expires in %s",
			(*session.AccessKeyId)[len(*session.AccessKeyId)-4:],
			session.Expiration.Sub(time.Now()).String())
	}

	p.SetExpiration(*session.Expiration, window)
	p.expires = *session.Expiration

	value := credentials.Value{
		AccessKeyID:     *session.AccessKeyId,
		SecretAccessKey: *session.SecretAccessKey,
		SessionToken:    *session.SessionToken,
	}

	return value, nil
}

func (p *VaultProvider) getMasterCreds() (credentials.Value, error) {
	source := sourceProfile(p.profile, p.profiles)

	val, ok := p.creds[source]
	if !ok {
		creds := credentials.NewCredentials(&KeyringProvider{Keyring: p.keyring, Profile: source})

		var err error
		if val, err = creds.Get(); err != nil {
			log.Printf("Failed to find credentials for profile %q in keyring", source)
			return val, err
		}

		p.creds[source] = val
	}

	return val, nil
}

func (p *VaultProvider) getSessionToken(creds *credentials.Value) (sts.Credentials, error) {
	params := &sts.GetSessionTokenInput{
		DurationSeconds: aws.Int64(int64(p.SessionDuration.Seconds())),
	}

	if mfa, ok := p.profiles[p.profile]["mfa_serial"]; ok {
		params.SerialNumber = aws.String(mfa)
		if p.MfaToken == "" {
			token, err := p.MfaPrompt(fmt.Sprintf("Enter token for %s: ", mfa))
			if err != nil {
				return sts.Credentials{}, err
			}
			params.TokenCode = aws.String(token)
		} else {
			params.TokenCode = aws.String(p.MfaToken)
		}
	}

	client := sts.New(session.New(&aws.Config{
		Credentials: credentials.NewCredentials(&credentials.StaticProvider{
			Value: *creds,
		}),
	}))

	log.Printf("Getting new session token for profile %s", sourceProfile(p.profile, p.profiles))
	resp, err := client.GetSessionToken(params)
	if err != nil {
		return sts.Credentials{}, err
	}

	return *resp.Credentials, nil
}

func (p *VaultProvider) roleSessionName() string {
	if name := p.profiles[p.profile]["role_session_name"]; name != "" {
		return name
	}

	// Try to work out a role name that will hopefully end up unique.
	return fmt.Sprintf("%d", time.Now().UTC().UnixNano())
}

// assumeRoleFromSession takes a session created with GetSessionToken and uses that to assume a role
func (p *VaultProvider) assumeRoleFromSession(creds sts.Credentials, roleArn string) (sts.Credentials, error) {
	client := sts.New(session.New(&aws.Config{Credentials: credentials.NewStaticCredentials(
		*creds.AccessKeyId,
		*creds.SecretAccessKey,
		*creds.SessionToken,
	)}))

	input := &sts.AssumeRoleInput{
		RoleArn:         aws.String(roleArn),
		RoleSessionName: aws.String(p.roleSessionName()),
		DurationSeconds: aws.Int64(int64(p.AssumeRoleDuration.Seconds())),
	}

	log.Printf("Assuming role %s from session token", roleArn)
	resp, err := client.AssumeRole(input)
	if err != nil {
		return sts.Credentials{}, err
	}

	return *resp.Credentials, nil
}

// assumeRole uses IAM credentials to assume a role
func (p *VaultProvider) assumeRole(creds credentials.Value, roleArn string) (sts.Credentials, error) {
	client := sts.New(session.New(&aws.Config{
		Credentials: credentials.NewCredentials(&credentials.StaticProvider{Value: creds}),
	}))

	input := &sts.AssumeRoleInput{
		RoleArn:         aws.String(roleArn),
		RoleSessionName: aws.String(p.roleSessionName()),
		DurationSeconds: aws.Int64(int64(p.AssumeRoleDuration.Seconds())),
	}

	// if we don't have a session, we need to include MFA token in the AssumeRole call
	if mfa, ok := p.profiles[p.profile]["mfa_serial"]; ok {
		input.SerialNumber = aws.String(mfa)
		if p.MfaToken == "" {
			token, err := p.MfaPrompt(fmt.Sprintf("Enter token for %s: ", mfa))
			if err != nil {
				return sts.Credentials{}, err
			}
			input.TokenCode = aws.String(token)
		} else {
			input.TokenCode = aws.String(p.MfaToken)
		}
	}

	log.Printf("Assuming role %s with iam credentials", roleArn)
	resp, err := client.AssumeRole(input)
	if err != nil {
		return sts.Credentials{}, err
	}

	return *resp.Credentials, nil
}

type KeyringProvider struct {
	Keyring keyring.Keyring
	Profile string
}

func (p *KeyringProvider) IsExpired() bool {
	return false
}

func (p *KeyringProvider) Retrieve() (val credentials.Value, err error) {
	log.Printf("Looking up keyring for %s", p.Profile)
	item, err := p.Keyring.Get(p.Profile)
	if err != nil {
		log.Println("Error from keyring", err)
		return val, err
	}
	if err = json.Unmarshal(item.Data, &val); err != nil {
		return val, fmt.Errorf("Invalid data in keyring: %v", err)
	}
	return val, err
}

func (p *KeyringProvider) Store(val credentials.Value) error {
	bytes, err := json.Marshal(val)
	if err != nil {
		return err
	}

	return p.Keyring.Set(keyring.Item{
		Key:  p.Profile,
		Data: bytes,
	})
}

func (p *KeyringProvider) Delete() error {
	return p.Keyring.Remove(p.Profile)
}

type VaultCredentials struct {
	*credentials.Credentials
	provider *VaultProvider
}

func NewVaultCredentials(k keyring.Keyring, profile string, opts VaultOptions) (*VaultCredentials, error) {
	provider, err := NewVaultProvider(k, profile, opts)
	if err != nil {
		return nil, err
	}

	return &VaultCredentials{credentials.NewCredentials(provider), provider}, nil
}

func (v *VaultCredentials) Expires() time.Time {
	return v.provider.expires
}
<|endoftext|>


<commit_before>package inject

import (
	"fmt"
	"reflect"
)

type provider struct {
	constructor interface{}
	argPtrs     []interface{}
}

// NewProvider specifies how to construct a value given its constructor function and argument pointers
func NewProvider(constructor interface{}, argPtrs ...interface{}) Provider {
	fnValue := reflect.ValueOf(constructor)
	if fnValue.Kind() != reflect.Func {
		panic(fmt.Sprintf("constructor (%v) is not a function, found %v", fnValue, fnValue.Kind()))
	}

	fnType := reflect.TypeOf(constructor)
	if fnType.NumOut() != 1 {
		panic(fmt.Sprintf("constructor must have exactly 1 return value, found %v", fnType.NumOut()))
	}

	argCount := fnType.NumIn()
	if argCount != len(argPtrs) {
		panic(fmt.Sprintf("argPtrs (%d) must match constructor arguments (%d)", len(argPtrs), argCount))
	}

	for i, argPtr := range argPtrs {
		if reflect.TypeOf(argPtr).Kind() != reflect.Ptr {
			panic(fmt.Sprintf("argPtrs must all be pointers, found %v", reflect.TypeOf(argPtr)))
		}
		if reflect.ValueOf(argPtr).Elem().Kind() != fnType.In(i).Kind() {
			panic("argPtrs must match constructor argument types")
		}
	}

	return provider{
		constructor: constructor,
		argPtrs:     argPtrs,
	}
}

// Provide returns the result of executing the constructor with argument values resolved from a dependency graph
func (p provider) Provide(g Graph) reflect.Value {
	fnType := reflect.TypeOf(p.constructor)

	argCount := fnType.NumIn()
	args := make([]reflect.Value, argCount, argCount)
	for i := 0; i < argCount; i++ {
		arg := g.Resolve(p.argPtrs[i])
		argType := arg.Type()
		inType := fnType.In(i)
		if !argType.AssignableTo(inType) {
			if !argType.ConvertibleTo(inType) {
				panic(fmt.Sprintf(
					"arg %d of type %q cannot be assigned or converted to type %q for provider constructor (%s)",
					i, argType, inType, p.constructor,
				))
			}
			arg = arg.Convert(inType)
		}
		args[i] = arg
	}

	return reflect.ValueOf(p.constructor).Call(args)[0]
}

// Type returns the type of value to expect from Provide
func (p provider) ReturnType() reflect.Type {
	return reflect.TypeOf(p.constructor).Out(0)
}

// String returns a multiline string representation of the provider
func (p provider) String() string {
	return fmt.Sprintf("&provider{\n%s,\n%s\n}",
		indent(fmt.Sprintf("constructor: %s", reflect.TypeOf(p.constructor)), 1),
		indent(fmt.Sprintf("argPtrs: %s", p.fmtArgPtrs()), 1),
	)
}

func (p provider) fmtArgPtrs() string {
	b := make([]string, len(p.argPtrs), len(p.argPtrs))
	for i, argPtr := range p.argPtrs {
		b[i] = ptrString(argPtr)
	}
	return arrayString(b)
}
<commit_msg>Add support for variadic arguments on provider constructor<commit_after>package inject

import (
	"fmt"
	"reflect"
)

type provider struct {
	constructor interface{}
	argPtrs     []interface{}
}

// NewProvider specifies how to construct a value given its constructor function and argument pointers
func NewProvider(constructor interface{}, argPtrs ...interface{}) Provider {
	fnValue := reflect.ValueOf(constructor)
	if fnValue.Kind() != reflect.Func {
		panic(fmt.Sprintf("constructor (%v) is not a function, found %v", fnValue, fnValue.Kind()))
	}

	fnType := reflect.TypeOf(constructor)
	if fnType.NumOut() != 1 {
		panic(fmt.Sprintf("constructor must have exactly 1 return value, found %v", fnType.NumOut()))
	}

	argCount := fnType.NumIn()
	if !fnValue.Type().IsVariadic() && argCount != len(argPtrs) {
		panic(fmt.Sprintf("argPtrs (%d) must match constructor arguments (%d)", len(argPtrs), argCount))
	}

	var kind reflect.Kind
	for i, argPtr := range argPtrs {
		isVariadic := fnValue.Type().IsVariadic() && (fnType.NumIn() == 1 || i >= fnType.NumIn())

		if i < fnType.NumIn() {
			kind = fnType.In(i).Kind()
		} else {
			kind = fnType.In(fnType.NumIn() - 1).Kind()
		}

		if reflect.TypeOf(argPtr).Kind() != reflect.Ptr {
			panic(fmt.Sprintf("argPtrs must all be pointers, found %v", reflect.TypeOf(argPtr)))
		}

		if !isVariadic && reflect.ValueOf(argPtr).Elem().Kind() != kind {
			panic("argPtrs must match constructor argument types")
		}
	}

	return provider{
		constructor: constructor,
		argPtrs:     argPtrs,
	}
}

// Provide returns the result of executing the constructor with argument values resolved from a dependency graph
func (p provider) Provide(g Graph) reflect.Value {
	fnType := reflect.TypeOf(p.constructor)

	argCount := fnType.NumIn()
	if fnType.IsVariadic() {
		argCount = len(p.argPtrs)
	}

	args := make([]reflect.Value, argCount, argCount)
	var inType reflect.Type
	for i := 0; i < argCount; i++ {
		arg := g.Resolve(p.argPtrs[i])
		argType := arg.Type()

		if i < fnType.NumIn() {
			inType = fnType.In(i)
		} else {
			inType = fnType.In(fnType.NumIn() - 1)
		}

		if inType.Kind() == reflect.Slice {
			inType = inType.Elem()
		}

		if !argType.AssignableTo(inType) {
			if !argType.ConvertibleTo(inType) {
				panic(fmt.Sprintf(
					"arg %d of type %q cannot be assigned or converted to type %q for provider constructor (%s)",
					i, argType, inType, p.constructor,
				))
			}
			arg = arg.Convert(inType)
		}
		args[i] = arg
	}

	return reflect.ValueOf(p.constructor).Call(args)[0]
}

// Type returns the type of value to expect from Provide
func (p provider) ReturnType() reflect.Type {
	return reflect.TypeOf(p.constructor).Out(0)
}

// String returns a multiline string representation of the provider
func (p provider) String() string {
	return fmt.Sprintf("&provider{\n%s,\n%s\n}",
		indent(fmt.Sprintf("constructor: %s", reflect.TypeOf(p.constructor)), 1),
		indent(fmt.Sprintf("argPtrs: %s", p.fmtArgPtrs()), 1),
	)
}

func (p provider) fmtArgPtrs() string {
	b := make([]string, len(p.argPtrs), len(p.argPtrs))
	for i, argPtr := range p.argPtrs {
		b[i] = ptrString(argPtr)
	}
	return arrayString(b)
}
<|endoftext|>


<commit_before>package wsrpcpool

// The RPC provider module

import (
	"crypto/tls"
	"crypto/x509"
	"golang.org/x/net/websocket"
	"net/rpc"
	"net/rpc/jsonrpc"
	"time"
)

var (
	/* DefaultDelay is the default delay between pool
	server re-connections. The default value is 100s.*/
	DefaultDelay = 100 * time.Millisecond
)

/* Provider is intended to connect to a PoolServer and handle RPC calls. */
type Provider struct {
	Config *websocket.Config
	// Maximum number of connection attempts. Default is unlimited.*/
	MaxAttempts int
	// The delay between reconnections. The default is DefaultReconnectDelay (100ms).
	Delay time.Duration
}

/* PoolConn represents a signle pool connection that handles
RPC requests coming from a pool server. */
type PoolConn struct {
	// Error channel
	Error <-chan error
	// Connected signal channel
	Connected <-chan struct{}
	// Closed signal channel
	Closed <-chan struct{}
	// Used to close the connection
	stop chan struct{}
}

/* PoolCallerConn represents a signle connection that is
used to send RPC calls *to* the pool. */
type PoolCallerConn struct {
	*PoolConn
	// Call channel
	Calls chan *rpc.Call
}

/* NewProvider returns a new Provider instance tuned to the given pool URL
with no SSL client-side certificate authentication. The optional set of
root CAs, if any, are used to validate the pool server certificate in the
case wss:// URL is used. */
func NewProvider(url string, rootCAs ...string) (*Provider, error) {
	conf, err := websocket.NewConfig(url, "http://localhost/")
	if err != nil {
		return nil, err
	} else {
		p := &Provider{Config: conf}
		if err := p.AppendRootCAs(rootCAs...); err != nil {
			return nil, err
		}
		return p, nil
	}
}

/* NewProviderTLS returns a Provider instance equipped with the given
SSL certificate to authenticate itself with the pool server and an optional
set of root CAs to validate the pool server certificate. */
func NewProviderTLSAuth(url, certfile, keyfile string, rootCAs ...string) (*Provider, error) {
	p, err := NewProvider(url)
	if err == nil {
		if err := p.AppendCertificate(certfile, keyfile); err != nil {
			return nil, err
		}
		if err := p.AppendRootCAs(rootCAs...); err != nil {
			return nil, err
		}
		return p, nil
	} else {
		return nil, err
	}
}

/* AppendCertificate appends an SSL certificate to the set of provider
certificates loading it from the pair of public certificate and private
key files. */
func (p *Provider) AppendCertificate(certfile, keyfile string) error {
	if p.Config.TlsConfig == nil {
		p.Config.TlsConfig = &tls.Config{}
	}
	return appendCertificate(p.Config.TlsConfig, certfile, keyfile)
}

/* AppendRootCAs appends the given SSL root CA certificate files to the
set of ones that are used to validate the pool server certificate. */
func (p *Provider) AppendRootCAs(rootCAs ...string) error {
	if len(rootCAs) == 0 {
		return nil
	}
	if p.Config.TlsConfig == nil {
		p.Config.TlsConfig = &tls.Config{}
	}
	if p.Config.TlsConfig.RootCAs == nil {
		p.Config.TlsConfig.RootCAs = x509.NewCertPool()
	}
	return appendCAs(p.Config.TlsConfig.RootCAs, rootCAs...)
}

/*
ConnectAndServe connects to the configured server pool URL
and asynchronously handles incoming JSON-RPC messages.
The connection is automatically re-established when broken
until MaxAttempts is exceeded.
Don't forget to call rpc.Register() with your exported objects.
*/
func (p *Provider) ConnectAndServe() *PoolConn {
	return p.connect(nil)
}

/*
ConnectAndUse connects to the configured server pool URL
to send RPC calls *to* the pool with JSON-RPC messages.
The connection is automatically re-established when broken
until MaxAttempts is exceeded. See PoolServer.BindIn()
method for more information.
*/
func (p *Provider) ConnectAndUse() *PoolCallerConn {
	calls := make(chan *rpc.Call)
	pc := p.connect(calls)
	return &PoolCallerConn{pc, calls}
}

func (p *Provider) connect(callIn <-chan *rpc.Call) *PoolConn {
	var (
		ws       *websocket.Conn
		pc       PoolConn
		err      error
		attempts int
	)

	pc.stop = make(chan struct{})
	closed := make(chan struct{})
	pc.Closed = closed
	errc := make(chan error, 1)
	pc.Error = errc
	connected := make(chan struct{}, 1)
	pc.Connected = connected

	if p.MaxAttempts < 0 {
		close(pc.stop)
		errc <- nil
		close(errc)
		return &pc
	}

	go func() {
	loop:
		for {
			ws, err = websocket.DialConfig(p.Config)
			attempts++

			if err == nil {
				select {
				case connected <- struct{}{}:
				default:
				}

				if callIn != nil {
					errOut := make(chan error)
					handle(callIn, errOut)(ws)
					if err := <- errOut; err == nil {
						break loop // callIn is closed
					}
				} else {
					done := make(chan struct{})
					go func() {
						select {
						case <-done:
						case <-pc.stop:
						}
						ws.Close()
					}()
					jsonrpc.ServeConn(ws)
					close(done)
				}
			}

			if attempts < p.MaxAttempts || p.MaxAttempts == 0 {
				delay := p.Delay
				if delay == 0 {
					delay = DefaultDelay
				}
				if delay > 0 {
					time.Sleep(delay)
				}
			} else {
				break loop
			}
		}

		errc <- err
		close(errc)
		close(closed)
	}()

	return &pc
}

/* ConnectAndServeMulti asynchronously runs the given number of
simultaneous connections using ConnectAndServe(). */
func (p *Provider) ConnectAndServeMulti(count int) []*PoolConn {
	cons := make([]*PoolConn, count, count)
	for n := 0; n < count; n++ {
		cons = append(cons, p.ConnectAndServe())
	}
	return cons
}

/* Close closes the connection and returns the last unread error
from pc.Error. */
func (pc *PoolConn) Close() error {
	close(pc.stop)
	return <-pc.Error
}

/* Go invokes the given remote function *on the pool server* asynchronously.
The name of the function should be "Service.Method" same, as in the package
net/rpc. If "done" is nil, a new channel is allocated and passed in the return
value. See net/rpc package for details. */
func (pcc *PoolCallerConn) Go(serviceMethod string, args interface{}, reply interface{}, done chan *rpc.Call) (*rpc.Call, error) {
	call := &rpc.Call{
		ServiceMethod: serviceMethod,
		Args:          args,
		Reply:         reply,
	}
	if done == nil {
		done = make(chan *rpc.Call, 1)
	}
	call.Done = done

	pcc.Calls <- call
	return call, nil
}

/* Call invokes the given remote function *on the pool server* and waits for it to
complete, returning its error status. */
func (pcc *PoolCallerConn) Call(serviceMethod string, args interface{}, reply interface{}) error {
	if call, err := pcc.Go(serviceMethod, args, reply, nil); err == nil {
		call = <-call.Done
		return call.Error
	} else {
		return err
	}
}

/* Close closes the caller connection and returns the last unread
error from pcc.Error. */
func (pcc *PoolCallerConn) Close() error {
	close(pcc.Calls)
	return pcc.PoolConn.Close()
}
<commit_msg>PollConn: Add "disconnect" signal<commit_after>package wsrpcpool

// The RPC provider module

import (
	"crypto/tls"
	"crypto/x509"
	"golang.org/x/net/websocket"
	"net/rpc"
	"net/rpc/jsonrpc"
	"time"
)

var (
	/* DefaultDelay is the default delay between pool
	server re-connections. The default value is 100s.*/
	DefaultDelay = 100 * time.Millisecond
)

/* Provider is intended to connect to a PoolServer and handle RPC calls. */
type Provider struct {
	Config *websocket.Config
	// Maximum number of connection attempts. Default is unlimited.*/
	MaxAttempts int
	// The delay between reconnections. The default is DefaultReconnectDelay (100ms).
	Delay time.Duration
}

/* PoolConn represents a signle pool connection that handles
RPC requests coming from a pool server. */
type PoolConn struct {
	// Error channel
	Error <-chan error
	// "Connected" signal channel
	Connected <-chan struct{}
	// "Disconnected" signal channel
	Disconnected <-chan struct{}
	// Closed signal channel
	Closed <-chan struct{}
	// Used to close the connection
	stop chan struct{}
}

/* PoolCallerConn represents a signle connection that is
used to send RPC calls *to* the pool. */
type PoolCallerConn struct {
	*PoolConn
	// Call channel
	Calls chan *rpc.Call
}

/* NewProvider returns a new Provider instance tuned to the given pool URL
with no SSL client-side certificate authentication. The optional set of
root CAs, if any, are used to validate the pool server certificate in the
case wss:// URL is used. */
func NewProvider(url string, rootCAs ...string) (*Provider, error) {
	conf, err := websocket.NewConfig(url, "http://localhost/")
	if err != nil {
		return nil, err
	} else {
		p := &Provider{Config: conf}
		if err := p.AppendRootCAs(rootCAs...); err != nil {
			return nil, err
		}
		return p, nil
	}
}

/* NewProviderTLS returns a Provider instance equipped with the given
SSL certificate to authenticate itself with the pool server and an optional
set of root CAs to validate the pool server certificate. */
func NewProviderTLSAuth(url, certfile, keyfile string, rootCAs ...string) (*Provider, error) {
	p, err := NewProvider(url)
	if err == nil {
		if err := p.AppendCertificate(certfile, keyfile); err != nil {
			return nil, err
		}
		if err := p.AppendRootCAs(rootCAs...); err != nil {
			return nil, err
		}
		return p, nil
	} else {
		return nil, err
	}
}

/* AppendCertificate appends an SSL certificate to the set of provider
certificates loading it from the pair of public certificate and private
key files. */
func (p *Provider) AppendCertificate(certfile, keyfile string) error {
	if p.Config.TlsConfig == nil {
		p.Config.TlsConfig = &tls.Config{}
	}
	return appendCertificate(p.Config.TlsConfig, certfile, keyfile)
}

/* AppendRootCAs appends the given SSL root CA certificate files to the
set of ones that are used to validate the pool server certificate. */
func (p *Provider) AppendRootCAs(rootCAs ...string) error {
	if len(rootCAs) == 0 {
		return nil
	}
	if p.Config.TlsConfig == nil {
		p.Config.TlsConfig = &tls.Config{}
	}
	if p.Config.TlsConfig.RootCAs == nil {
		p.Config.TlsConfig.RootCAs = x509.NewCertPool()
	}
	return appendCAs(p.Config.TlsConfig.RootCAs, rootCAs...)
}

/*
ConnectAndServe connects to the configured server pool URL
and asynchronously handles incoming JSON-RPC messages.
The connection is automatically re-established when broken
until MaxAttempts is exceeded.
Don't forget to call rpc.Register() with your exported objects.
*/
func (p *Provider) ConnectAndServe() *PoolConn {
	return p.connect(nil)
}

/*
ConnectAndUse connects to the configured server pool URL
to send RPC calls *to* the pool with JSON-RPC messages.
The connection is automatically re-established when broken
until MaxAttempts is exceeded. See PoolServer.BindIn()
method for more information.
*/
func (p *Provider) ConnectAndUse() *PoolCallerConn {
	calls := make(chan *rpc.Call)
	pc := p.connect(calls)
	return &PoolCallerConn{pc, calls}
}

func (p *Provider) connect(callIn <-chan *rpc.Call) *PoolConn {
	var (
		ws       *websocket.Conn
		pc       PoolConn
		err      error
		attempts int
	)

	pc.stop = make(chan struct{})
	closed := make(chan struct{})
	pc.Closed = closed
	errc := make(chan error, 1)
	pc.Error = errc
	connected := make(chan struct{}, 1)
	pc.Connected = connected
	disconnected := make(chan struct{}, 1)
	pc.Disconnected = disconnected

	if p.MaxAttempts < 0 {
		close(pc.stop)
		errc <- nil
		close(errc)
		return &pc
	}

	go func() {
	loop:
		for {
			ws, err = websocket.DialConfig(p.Config)
			attempts++

			var _break bool
			if err == nil {
				select {
				case connected <- struct{}{}:
				default:
				}
				if callIn != nil {
					errOut := make(chan error)
					handle(callIn, errOut)(ws)
					if err := <- errOut; err == nil {
						_break = true // callIn is closed
					}
				} else {
					done := make(chan struct{})
					go func() {
						select {
						case <-done:
						case <-pc.stop:
							ws.Close()
						}
					}()
					jsonrpc.ServeConn(ws)
					close(done)
				}
			}

			select {
			case disconnected <- struct{}{}:
			default:
			}

			if _break {
				break loop
			}

			if attempts < p.MaxAttempts || p.MaxAttempts == 0 {
				delay := p.Delay
				if delay == 0 {
					delay = DefaultDelay
				}
				if delay > 0 {
					time.Sleep(delay)
				}
			} else {
				break loop
			}
		}

		errc <- err
		close(errc)
		close(closed)
	}()

	return &pc
}

/* ConnectAndServeMulti asynchronously runs the given number of
simultaneous connections using ConnectAndServe(). */
func (p *Provider) ConnectAndServeMulti(count int) []*PoolConn {
	cons := make([]*PoolConn, count, count)
	for n := 0; n < count; n++ {
		cons = append(cons, p.ConnectAndServe())
	}
	return cons
}

/* Close closes the connection and returns the last unread error
from pc.Error. */
func (pc *PoolConn) Close() error {
	close(pc.stop)
	return <-pc.Error
}

/* Go invokes the given remote function *on the pool server* asynchronously.
The name of the function should be "Service.Method" same, as in the package
net/rpc. If "done" is nil, a new channel is allocated and passed in the return
value. See net/rpc package for details. */
func (pcc *PoolCallerConn) Go(serviceMethod string, args interface{}, reply interface{}, done chan *rpc.Call) (*rpc.Call, error) {
	call := &rpc.Call{
		ServiceMethod: serviceMethod,
		Args:          args,
		Reply:         reply,
	}
	if done == nil {
		done = make(chan *rpc.Call, 1)
	}
	call.Done = done

	pcc.Calls <- call
	return call, nil
}

/* Call invokes the given remote function *on the pool server* and waits for it to
complete, returning its error status. */
func (pcc *PoolCallerConn) Call(serviceMethod string, args interface{}, reply interface{}) error {
	if call, err := pcc.Go(serviceMethod, args, reply, nil); err == nil {
		call = <-call.Done
		return call.Error
	} else {
		return err
	}
}

/* Close closes the caller connection and returns the last unread
error from pcc.Error. */
func (pcc *PoolCallerConn) Close() error {
	close(pcc.Calls)
	return pcc.PoolConn.Close()
}
<|endoftext|>


<commit_before>package neutron

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

const X_AUTH_TOKEN_HEADER = "X-Auth-Token"

type Client struct {
	URL   string
	token string
}

func NewClient(url, token string) (*Client, error) {
	if url == "" {
		return nil, fmt.Errorf("missing URL")
	}
	if token == "" {
		return nil, fmt.Errorf("missing token")
	}
	return &Client{URL: url, token: token}, nil
}

type request struct {
	URL          string
	Method       string
	Body         []byte
	OkStatusCode int
}

type response struct {
	Body       []byte
	StatusCode int
}

func (c *Client) doRequest(r request) (response, error) {
	client := &http.Client{}

	req, err := http.NewRequest(r.Method, r.URL, bytes.NewBuffer(r.Body))
	req.Header.Add(X_AUTH_TOKEN_HEADER, c.token)
	req.Header.Set("Content-Type", "application/json")

	resp, err := client.Do(req)
	if err != nil {
		return response{}, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return response{}, err
	}

	// fmt.Printf("resp: %s\n", body)

	if resp.StatusCode != r.OkStatusCode {
		return response{}, fmt.Errorf("Error: %s\n", resp.Status)
	}
	return response{Body: body, StatusCode: resp.StatusCode}, nil
}

func (c *Client) CreateNetwork(net Network) (Network, error) {
	jsonStr, err := json.Marshal(SingleNetwork{Network: net})
	if err != nil {
		return Network{}, fmt.Errorf("invalid network: ", err)
	}

	resp, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/networks", c.URL),
		Method:       "POST",
		Body:         jsonStr,
		OkStatusCode: http.StatusCreated,
	})
	if err != nil {
		return Network{}, err
	}

	var r SingleNetwork
	err = json.Unmarshal(resp.Body, &r)
	if err != nil {
		return Network{}, err
	}

	return r.Network, nil
}

func (c *Client) DeleteNetwork(id string) error {
	if id == "" {
		return fmt.Errorf("empty 'id' parameter")
	}
	_, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/networks/%s", c.URL, id),
		Method:       "DELETE",
		OkStatusCode: http.StatusNoContent,
	})
	if err != nil {
		return err
	}
	return nil
}

func (c *Client) Networks() ([]Network, error) {
	resp, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/networks", c.URL),
		Method:       "GET",
		OkStatusCode: http.StatusOK,
	})
	if err != nil {
		return nil, err
	}

	var r GetNetworks
	err = json.Unmarshal(resp.Body, &r)
	if err != nil {
		return nil, err
	}
	return r.Networks, nil
}

func (c *Client) NetworksByName(name string) ([]Network, error) {
	if name == "" {
		return nil, fmt.Errorf("empty 'name' parameter")
	}

	resp, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/networks?name=%s", c.URL, name),
		Method:       "GET",
		OkStatusCode: http.StatusOK,
	})
	if err != nil {
		return nil, err
	}

	var r GetNetworks
	err = json.Unmarshal(resp.Body, &r)
	if err != nil {
		return nil, err
	}
	return r.Networks, nil
}

func (c *Client) Subnets() ([]Subnet, error) {
	resp, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/subnets", c.URL),
		Method:       "GET",
		OkStatusCode: http.StatusOK,
	})
	if err != nil {
		return nil, err
	}

	var r GetSubnets
	err = json.Unmarshal(resp.Body, &r)
	if err != nil {
		return nil, err
	}
	return r.Subnets, nil
}

func (c *Client) SubnetsByName(name string) ([]Subnet, error) {
	if name == "" {
		return nil, fmt.Errorf("empty 'name' parameter")
	}

	resp, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/subnets?name=%s", c.URL, name),
		Method:       "GET",
		OkStatusCode: http.StatusOK,
	})
	if err != nil {
		return nil, err
	}

	var r GetSubnets
	err = json.Unmarshal(resp.Body, &r)
	if err != nil {
		return nil, err
	}
	return r.Subnets, nil
}

func (c *Client) CreateSubnet(s Subnet) (Subnet, error) {
	jsonStr, err := json.Marshal(SingleSubnet{Subnet: s})
	if err != nil {
		return Subnet{}, fmt.Errorf("invalid subnet: ", err)
	}

	resp, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/subnets", c.URL),
		Method:       "POST",
		Body:         jsonStr,
		OkStatusCode: http.StatusCreated,
	})

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

	var r SingleSubnet
	err = json.Unmarshal(resp.Body, &r)
	if err != nil {
		return Subnet{}, err
	}

	return r.Subnet, nil
}

func (c *Client) CreatePort(p Port) (Port, error) {
	jsonStr, err := json.Marshal(SinglePort{Port: p})
	if err != nil {
		return Port{}, fmt.Errorf("invalid port: ", err)
	}

	resp, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/ports", c.URL),
		Method:       "POST",
		Body:         jsonStr,
		OkStatusCode: http.StatusCreated,
	})

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

	var r SinglePort
	err = json.Unmarshal(resp.Body, &r)
	if err != nil {
		return Port{}, err
	}

	return r.Port, nil
}

func (c *Client) DeletePort(id string) error {
	if id == "" {
		return fmt.Errorf("empty 'id' parameter")
	}
	_, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/ports/%s", c.URL, id),
		Method:       "DELETE",
		OkStatusCode: http.StatusNoContent,
	})
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>Add missing err check<commit_after>package neutron

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

const X_AUTH_TOKEN_HEADER = "X-Auth-Token"

type Client struct {
	URL   string
	token string
}

func NewClient(url, token string) (*Client, error) {
	if url == "" {
		return nil, fmt.Errorf("missing URL")
	}
	if token == "" {
		return nil, fmt.Errorf("missing token")
	}
	return &Client{URL: url, token: token}, nil
}

type request struct {
	URL          string
	Method       string
	Body         []byte
	OkStatusCode int
}

type response struct {
	Body       []byte
	StatusCode int
}

func (c *Client) doRequest(r request) (response, error) {
	client := &http.Client{}

	req, err := http.NewRequest(r.Method, r.URL, bytes.NewBuffer(r.Body))
	if err != nil {
		return response{}, err
	}

	req.Header.Add(X_AUTH_TOKEN_HEADER, c.token)
	req.Header.Set("Content-Type", "application/json")

	resp, err := client.Do(req)
	if err != nil {
		return response{}, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return response{}, err
	}

	// fmt.Printf("resp: %s\n", body)

	if resp.StatusCode != r.OkStatusCode {
		return response{}, fmt.Errorf("Error: %s\n", resp.Status)
	}
	return response{Body: body, StatusCode: resp.StatusCode}, nil
}

func (c *Client) CreateNetwork(net Network) (Network, error) {
	jsonStr, err := json.Marshal(SingleNetwork{Network: net})
	if err != nil {
		return Network{}, fmt.Errorf("invalid network: ", err)
	}

	resp, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/networks", c.URL),
		Method:       "POST",
		Body:         jsonStr,
		OkStatusCode: http.StatusCreated,
	})
	if err != nil {
		return Network{}, err
	}

	var r SingleNetwork
	err = json.Unmarshal(resp.Body, &r)
	if err != nil {
		return Network{}, err
	}

	return r.Network, nil
}

func (c *Client) DeleteNetwork(id string) error {
	if id == "" {
		return fmt.Errorf("empty 'id' parameter")
	}
	_, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/networks/%s", c.URL, id),
		Method:       "DELETE",
		OkStatusCode: http.StatusNoContent,
	})
	if err != nil {
		return err
	}
	return nil
}

func (c *Client) Networks() ([]Network, error) {
	resp, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/networks", c.URL),
		Method:       "GET",
		OkStatusCode: http.StatusOK,
	})
	if err != nil {
		return nil, err
	}

	var r GetNetworks
	err = json.Unmarshal(resp.Body, &r)
	if err != nil {
		return nil, err
	}
	return r.Networks, nil
}

func (c *Client) NetworksByName(name string) ([]Network, error) {
	if name == "" {
		return nil, fmt.Errorf("empty 'name' parameter")
	}

	resp, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/networks?name=%s", c.URL, name),
		Method:       "GET",
		OkStatusCode: http.StatusOK,
	})
	if err != nil {
		return nil, err
	}

	var r GetNetworks
	err = json.Unmarshal(resp.Body, &r)
	if err != nil {
		return nil, err
	}
	return r.Networks, nil
}

func (c *Client) Subnets() ([]Subnet, error) {
	resp, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/subnets", c.URL),
		Method:       "GET",
		OkStatusCode: http.StatusOK,
	})
	if err != nil {
		return nil, err
	}

	var r GetSubnets
	err = json.Unmarshal(resp.Body, &r)
	if err != nil {
		return nil, err
	}
	return r.Subnets, nil
}

func (c *Client) SubnetsByName(name string) ([]Subnet, error) {
	if name == "" {
		return nil, fmt.Errorf("empty 'name' parameter")
	}

	resp, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/subnets?name=%s", c.URL, name),
		Method:       "GET",
		OkStatusCode: http.StatusOK,
	})
	if err != nil {
		return nil, err
	}

	var r GetSubnets
	err = json.Unmarshal(resp.Body, &r)
	if err != nil {
		return nil, err
	}
	return r.Subnets, nil
}

func (c *Client) CreateSubnet(s Subnet) (Subnet, error) {
	jsonStr, err := json.Marshal(SingleSubnet{Subnet: s})
	if err != nil {
		return Subnet{}, fmt.Errorf("invalid subnet: ", err)
	}

	resp, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/subnets", c.URL),
		Method:       "POST",
		Body:         jsonStr,
		OkStatusCode: http.StatusCreated,
	})

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

	var r SingleSubnet
	err = json.Unmarshal(resp.Body, &r)
	if err != nil {
		return Subnet{}, err
	}

	return r.Subnet, nil
}

func (c *Client) CreatePort(p Port) (Port, error) {
	jsonStr, err := json.Marshal(SinglePort{Port: p})
	if err != nil {
		return Port{}, fmt.Errorf("invalid port: ", err)
	}

	resp, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/ports", c.URL),
		Method:       "POST",
		Body:         jsonStr,
		OkStatusCode: http.StatusCreated,
	})

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

	var r SinglePort
	err = json.Unmarshal(resp.Body, &r)
	if err != nil {
		return Port{}, err
	}

	return r.Port, nil
}

func (c *Client) DeletePort(id string) error {
	if id == "" {
		return fmt.Errorf("empty 'id' parameter")
	}
	_, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/ports/%s", c.URL, id),
		Method:       "DELETE",
		OkStatusCode: http.StatusNoContent,
	})
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"time"

	"github.com/99designs/aws-vault/keyring"
	"github.com/99designs/aws-vault/prompt"
	"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"
)

const (
	MaxSessionDuration    = time.Hour * 36
	MinSessionDuration    = time.Minute * 15
	MinAssumeRoleDuration = time.Minute * 15
	MaxAssumeRoleDuration = time.Hour

	DefaultSessionDuration    = time.Hour * 4
	DefaultAssumeRoleDuration = time.Minute * 15
)

type VaultOptions struct {
	SessionDuration    time.Duration
	AssumeRoleDuration time.Duration
	ExpiryWindow       time.Duration
	MfaToken           string
	MfaPrompt          prompt.PromptFunc
	NoSession          bool
}

func (o VaultOptions) Validate() error {
	if o.SessionDuration < MinSessionDuration {
		return errors.New("Minimum session duration is " + MinSessionDuration.String())
	} else if o.SessionDuration > MaxSessionDuration {
		return errors.New("Maximum session duration is " + MaxSessionDuration.String())
	}
	if o.AssumeRoleDuration < MinAssumeRoleDuration {
		return errors.New("Minimum duration for assumed roles is " + MinAssumeRoleDuration.String())
	} else if o.AssumeRoleDuration > MaxAssumeRoleDuration {
		return errors.New("Maximum duration for assumed roles is " + MaxAssumeRoleDuration.String())
	}

	return nil
}

func (o VaultOptions) ApplyDefaults() VaultOptions {
	if o.AssumeRoleDuration == 0 {
		o.AssumeRoleDuration = DefaultAssumeRoleDuration
	}
	if o.SessionDuration == 0 {
		o.SessionDuration = DefaultSessionDuration
	}
	return o
}

type VaultProvider struct {
	credentials.Expiry
	VaultOptions
	profile  string
	expires  time.Time
	keyring  keyring.Keyring
	sessions *KeyringSessions
	profiles profiles
	creds    map[string]credentials.Value
}

func NewVaultProvider(k keyring.Keyring, profile string, opts VaultOptions) (*VaultProvider, error) {
	opts = opts.ApplyDefaults()
	if err := opts.Validate(); err != nil {
		return nil, err
	}
	profiles, err := parseProfiles()
	if err != nil {
		return nil, err
	}
	return &VaultProvider{
		VaultOptions: opts,
		keyring:      k,
		sessions:     &KeyringSessions{k, profiles},
		profile:      profile,
		profiles:     profiles,
		creds:        map[string]credentials.Value{},
	}, nil
}

func (p *VaultProvider) Retrieve() (credentials.Value, error) {
	creds, err := p.getMasterCreds()
	if err != nil {
		return credentials.Value{}, err
	}

	window := p.ExpiryWindow
	if window == 0 {
		window = time.Minute * 5
	}

	if p.NoSession {
		if role, ok := p.profiles[p.profile]["role_arn"]; ok {
			session, err := p.assumeRole(creds, role)
			if err != nil {
				return credentials.Value{}, err
			}

			log.Printf("Using role ****************%s, expires in %s",
				(*session.AccessKeyId)[len(*session.AccessKeyId)-4:],
				session.Expiration.Sub(time.Now()).String())

			p.SetExpiration(*session.Expiration, window)
			p.expires = *session.Expiration

			value := credentials.Value{
				AccessKeyID:     *session.AccessKeyId,
				SecretAccessKey: *session.SecretAccessKey,
				SessionToken:    *session.SessionToken,
			}

			return value, nil
		}

		// no role, exposes master credentials which don't expire
		return creds, nil
	}

	session, err := p.sessions.Retrieve(p.profile, p.SessionDuration)
	if err != nil {
		if err == keyring.ErrKeyNotFound {
			log.Println("Session not found in keyring")
		} else {
			log.Println(err)
		}

		session, err = p.getSessionToken(&creds)
		if err != nil {
			return credentials.Value{}, err
		}

		if err = p.sessions.Store(p.profile, session, p.SessionDuration); err != nil {
			return credentials.Value{}, err
		}
	}

	log.Printf("Using session ****************%s, expires in %s",
		(*session.AccessKeyId)[len(*session.AccessKeyId)-4:],
		session.Expiration.Sub(time.Now()).String())

	if role, ok := p.profiles[p.profile]["role_arn"]; ok {
		session, err = p.assumeRoleFromSession(session, role)
		if err != nil {
			return credentials.Value{}, err
		}

		log.Printf("Using role ****************%s (from session token), expires in %s",
			(*session.AccessKeyId)[len(*session.AccessKeyId)-4:],
			session.Expiration.Sub(time.Now()).String())
	}

	p.SetExpiration(*session.Expiration, window)
	p.expires = *session.Expiration

	value := credentials.Value{
		AccessKeyID:     *session.AccessKeyId,
		SecretAccessKey: *session.SecretAccessKey,
		SessionToken:    *session.SessionToken,
	}

	return value, nil
}

func (p *VaultProvider) getMasterCreds() (credentials.Value, error) {
	source := p.profiles.sourceProfile(p.profile)

	creds, ok := p.creds[source]
	if !ok {
		provider := credentials.NewChainCredentials([]credentials.Provider{
			&credentials.EnvProvider{},
			&credentials.SharedCredentialsProvider{Filename: "", Profile: p.profile},
			&KeyringProvider{Keyring: p.keyring, Profile: source},
		})

		var err error
		if creds, err = provider.Get(); err != nil {
			return creds, err
		}

		p.creds[source] = creds
	}

	return creds, nil
}

func (p *VaultProvider) getSessionToken(creds *credentials.Value) (sts.Credentials, error) {
	params := &sts.GetSessionTokenInput{
		DurationSeconds: aws.Int64(int64(p.SessionDuration.Seconds())),
	}

	if mfa, ok := p.profiles[p.profile]["mfa_serial"]; ok {
		params.SerialNumber = aws.String(mfa)
		if p.MfaToken == "" {
			token, err := p.MfaPrompt(fmt.Sprintf("Enter token for %s: ", mfa))
			if err != nil {
				return sts.Credentials{}, err
			}
			params.TokenCode = aws.String(token)
		} else {
			params.TokenCode = aws.String(p.MfaToken)
		}
	}

	client := sts.New(session.New(&aws.Config{
		Credentials: credentials.NewCredentials(&credentials.StaticProvider{
			Value: *creds,
		}),
	}))

	log.Printf("Getting new session token for profile %s", p.profiles.sourceProfile(p.profile))
	resp, err := client.GetSessionToken(params)
	if err != nil {
		return sts.Credentials{}, err
	}

	return *resp.Credentials, nil
}

func (p *VaultProvider) roleSessionName() string {
	if name := p.profiles[p.profile]["role_session_name"]; name != "" {
		return name
	}

	// Try to work out a role name that will hopefully end up unique.
	return fmt.Sprintf("%d", time.Now().UTC().UnixNano())
}

// assumeRoleFromSession takes a session created with GetSessionToken and uses that to assume a role
func (p *VaultProvider) assumeRoleFromSession(creds sts.Credentials, roleArn string) (sts.Credentials, error) {
	client := sts.New(session.New(&aws.Config{Credentials: credentials.NewStaticCredentials(
		*creds.AccessKeyId,
		*creds.SecretAccessKey,
		*creds.SessionToken,
	)}))

	input := &sts.AssumeRoleInput{
		RoleArn:         aws.String(roleArn),
		RoleSessionName: aws.String(p.roleSessionName()),
		DurationSeconds: aws.Int64(int64(p.AssumeRoleDuration.Seconds())),
	}

	log.Printf("Assuming role %s from session token", roleArn)
	resp, err := client.AssumeRole(input)
	if err != nil {
		return sts.Credentials{}, err
	}

	return *resp.Credentials, nil
}

// assumeRole uses IAM credentials to assume a role
func (p *VaultProvider) assumeRole(creds credentials.Value, roleArn string) (sts.Credentials, error) {
	client := sts.New(session.New(&aws.Config{
		Credentials: credentials.NewCredentials(&credentials.StaticProvider{Value: creds}),
	}))

	input := &sts.AssumeRoleInput{
		RoleArn:         aws.String(roleArn),
		RoleSessionName: aws.String(p.roleSessionName()),
		DurationSeconds: aws.Int64(int64(p.AssumeRoleDuration.Seconds())),
	}

	// if we don't have a session, we need to include MFA token in the AssumeRole call
	if mfa, ok := p.profiles[p.profile]["mfa_serial"]; ok {
		input.SerialNumber = aws.String(mfa)
		if p.MfaToken == "" {
			token, err := p.MfaPrompt(fmt.Sprintf("Enter token for %s: ", mfa))
			if err != nil {
				return sts.Credentials{}, err
			}
			input.TokenCode = aws.String(token)
		} else {
			input.TokenCode = aws.String(p.MfaToken)
		}
	}

	log.Printf("Assuming role %s with iam credentials", roleArn)
	resp, err := client.AssumeRole(input)
	if err != nil {
		return sts.Credentials{}, err
	}

	return *resp.Credentials, nil
}

type KeyringProvider struct {
	Keyring keyring.Keyring
	Profile string
}

func (p *KeyringProvider) IsExpired() bool {
	return false
}

func (p *KeyringProvider) Retrieve() (val credentials.Value, err error) {
	log.Printf("Looking up keyring for %s", p.Profile)
	item, err := p.Keyring.Get(p.Profile)
	if err != nil {
		log.Println("Error from keyring", err)
		return val, err
	}
	if err = json.Unmarshal(item.Data, &val); err != nil {
		return val, fmt.Errorf("Invalid data in keyring: %v", err)
	}
	return val, err
}

func (p *KeyringProvider) Store(val credentials.Value) error {
	bytes, err := json.Marshal(val)
	if err != nil {
		return err
	}

	return p.Keyring.Set(keyring.Item{
		Key:  p.Profile,
		Data: bytes,
	})
}

func (p *KeyringProvider) Delete() error {
	return p.Keyring.Remove(p.Profile)
}

type VaultCredentials struct {
	*credentials.Credentials
	provider *VaultProvider
}

func NewVaultCredentials(k keyring.Keyring, profile string, opts VaultOptions) (*VaultCredentials, error) {
	provider, err := NewVaultProvider(k, profile, opts)
	if err != nil {
		return nil, err
	}

	return &VaultCredentials{credentials.NewCredentials(provider), provider}, nil
}

func (v *VaultCredentials) Expires() time.Time {
	return v.provider.expires
}
<commit_msg>Add debugging for failing test<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"time"

	"github.com/99designs/aws-vault/keyring"
	"github.com/99designs/aws-vault/prompt"
	"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"
)

const (
	MaxSessionDuration    = time.Hour * 36
	MinSessionDuration    = time.Minute * 15
	MinAssumeRoleDuration = time.Minute * 15
	MaxAssumeRoleDuration = time.Hour

	DefaultSessionDuration    = time.Hour * 4
	DefaultAssumeRoleDuration = time.Minute * 15
)

type VaultOptions struct {
	SessionDuration    time.Duration
	AssumeRoleDuration time.Duration
	ExpiryWindow       time.Duration
	MfaToken           string
	MfaPrompt          prompt.PromptFunc
	NoSession          bool
}

func (o VaultOptions) Validate() error {
	if o.SessionDuration < MinSessionDuration {
		return errors.New("Minimum session duration is " + MinSessionDuration.String())
	} else if o.SessionDuration > MaxSessionDuration {
		return errors.New("Maximum session duration is " + MaxSessionDuration.String())
	}
	if o.AssumeRoleDuration < MinAssumeRoleDuration {
		return errors.New("Minimum duration for assumed roles is " + MinAssumeRoleDuration.String())
	} else if o.AssumeRoleDuration > MaxAssumeRoleDuration {
		return errors.New("Maximum duration for assumed roles is " + MaxAssumeRoleDuration.String())
	}

	return nil
}

func (o VaultOptions) ApplyDefaults() VaultOptions {
	if o.AssumeRoleDuration == 0 {
		o.AssumeRoleDuration = DefaultAssumeRoleDuration
	}
	if o.SessionDuration == 0 {
		o.SessionDuration = DefaultSessionDuration
	}
	return o
}

type VaultProvider struct {
	credentials.Expiry
	VaultOptions
	profile  string
	expires  time.Time
	keyring  keyring.Keyring
	sessions *KeyringSessions
	profiles profiles
	creds    map[string]credentials.Value
}

func NewVaultProvider(k keyring.Keyring, profile string, opts VaultOptions) (*VaultProvider, error) {
	opts = opts.ApplyDefaults()
	if err := opts.Validate(); err != nil {
		return nil, err
	}
	profiles, err := parseProfiles()
	if err != nil {
		return nil, err
	}
	return &VaultProvider{
		VaultOptions: opts,
		keyring:      k,
		sessions:     &KeyringSessions{k, profiles},
		profile:      profile,
		profiles:     profiles,
		creds:        map[string]credentials.Value{},
	}, nil
}

func (p *VaultProvider) Retrieve() (credentials.Value, error) {
	creds, err := p.getMasterCreds()
	if err != nil {
		return credentials.Value{}, err
	}

	window := p.ExpiryWindow
	if window == 0 {
		window = time.Minute * 5
	}

	if p.NoSession {
		if role, ok := p.profiles[p.profile]["role_arn"]; ok {
			session, err := p.assumeRole(creds, role)
			if err != nil {
				return credentials.Value{}, err
			}

			log.Printf("Using role ****************%s, expires in %s",
				(*session.AccessKeyId)[len(*session.AccessKeyId)-4:],
				session.Expiration.Sub(time.Now()).String())

			p.SetExpiration(*session.Expiration, window)
			p.expires = *session.Expiration

			value := credentials.Value{
				AccessKeyID:     *session.AccessKeyId,
				SecretAccessKey: *session.SecretAccessKey,
				SessionToken:    *session.SessionToken,
			}

			return value, nil
		}

		// no role, exposes master credentials which don't expire
		return creds, nil
	}

	session, err := p.sessions.Retrieve(p.profile, p.SessionDuration)
	if err != nil {
		if err == keyring.ErrKeyNotFound {
			log.Println("Session not found in keyring")
		} else {
			log.Println(err)
		}

		session, err = p.getSessionToken(&creds)
		if err != nil {
			return credentials.Value{}, err
		}

		if err = p.sessions.Store(p.profile, session, p.SessionDuration); err != nil {
			return credentials.Value{}, err
		}
	}

	log.Printf("Using session ****************%s, expires in %s",
		(*session.AccessKeyId)[len(*session.AccessKeyId)-4:],
		session.Expiration.Sub(time.Now()).String())

	if role, ok := p.profiles[p.profile]["role_arn"]; ok {
		session, err = p.assumeRoleFromSession(session, role)
		if err != nil {
			return credentials.Value{}, err
		}

		log.Printf("Using role ****************%s (from session token), expires in %s",
			(*session.AccessKeyId)[len(*session.AccessKeyId)-4:],
			session.Expiration.Sub(time.Now()).String())
	}

	p.SetExpiration(*session.Expiration, window)
	p.expires = *session.Expiration

	value := credentials.Value{
		AccessKeyID:     *session.AccessKeyId,
		SecretAccessKey: *session.SecretAccessKey,
		SessionToken:    *session.SessionToken,
	}

	return value, nil
}

func (p *VaultProvider) getMasterCreds() (credentials.Value, error) {
	source := p.profiles.sourceProfile(p.profile)

	creds, ok := p.creds[source]
	if !ok {
		provider := credentials.NewChainCredentials([]credentials.Provider{
			&credentials.EnvProvider{},
			&credentials.SharedCredentialsProvider{Filename: "", Profile: p.profile},
			&KeyringProvider{Keyring: p.keyring, Profile: source},
		})

		var err error
		if creds, err = provider.Get(); err != nil {
			log.Printf("Failed to find credentials for profile %q in keyring", source)
			return creds, err
		}

		p.creds[source] = creds
	}

	return creds, nil
}

func (p *VaultProvider) getSessionToken(creds *credentials.Value) (sts.Credentials, error) {
	params := &sts.GetSessionTokenInput{
		DurationSeconds: aws.Int64(int64(p.SessionDuration.Seconds())),
	}

	if mfa, ok := p.profiles[p.profile]["mfa_serial"]; ok {
		params.SerialNumber = aws.String(mfa)
		if p.MfaToken == "" {
			token, err := p.MfaPrompt(fmt.Sprintf("Enter token for %s: ", mfa))
			if err != nil {
				return sts.Credentials{}, err
			}
			params.TokenCode = aws.String(token)
		} else {
			params.TokenCode = aws.String(p.MfaToken)
		}
	}

	client := sts.New(session.New(&aws.Config{
		Credentials: credentials.NewCredentials(&credentials.StaticProvider{
			Value: *creds,
		}),
	}))

	log.Printf("Getting new session token for profile %s", p.profiles.sourceProfile(p.profile))
	resp, err := client.GetSessionToken(params)
	if err != nil {
		return sts.Credentials{}, err
	}

	return *resp.Credentials, nil
}

func (p *VaultProvider) roleSessionName() string {
	if name := p.profiles[p.profile]["role_session_name"]; name != "" {
		return name
	}

	// Try to work out a role name that will hopefully end up unique.
	return fmt.Sprintf("%d", time.Now().UTC().UnixNano())
}

// assumeRoleFromSession takes a session created with GetSessionToken and uses that to assume a role
func (p *VaultProvider) assumeRoleFromSession(creds sts.Credentials, roleArn string) (sts.Credentials, error) {
	client := sts.New(session.New(&aws.Config{Credentials: credentials.NewStaticCredentials(
		*creds.AccessKeyId,
		*creds.SecretAccessKey,
		*creds.SessionToken,
	)}))

	input := &sts.AssumeRoleInput{
		RoleArn:         aws.String(roleArn),
		RoleSessionName: aws.String(p.roleSessionName()),
		DurationSeconds: aws.Int64(int64(p.AssumeRoleDuration.Seconds())),
	}

	log.Printf("Assuming role %s from session token", roleArn)
	resp, err := client.AssumeRole(input)
	if err != nil {
		return sts.Credentials{}, err
	}

	return *resp.Credentials, nil
}

// assumeRole uses IAM credentials to assume a role
func (p *VaultProvider) assumeRole(creds credentials.Value, roleArn string) (sts.Credentials, error) {
	client := sts.New(session.New(&aws.Config{
		Credentials: credentials.NewCredentials(&credentials.StaticProvider{Value: creds}),
	}))

	input := &sts.AssumeRoleInput{
		RoleArn:         aws.String(roleArn),
		RoleSessionName: aws.String(p.roleSessionName()),
		DurationSeconds: aws.Int64(int64(p.AssumeRoleDuration.Seconds())),
	}

	// if we don't have a session, we need to include MFA token in the AssumeRole call
	if mfa, ok := p.profiles[p.profile]["mfa_serial"]; ok {
		input.SerialNumber = aws.String(mfa)
		if p.MfaToken == "" {
			token, err := p.MfaPrompt(fmt.Sprintf("Enter token for %s: ", mfa))
			if err != nil {
				return sts.Credentials{}, err
			}
			input.TokenCode = aws.String(token)
		} else {
			input.TokenCode = aws.String(p.MfaToken)
		}
	}

	log.Printf("Assuming role %s with iam credentials", roleArn)
	resp, err := client.AssumeRole(input)
	if err != nil {
		return sts.Credentials{}, err
	}

	return *resp.Credentials, nil
}

type KeyringProvider struct {
	Keyring keyring.Keyring
	Profile string
}

func (p *KeyringProvider) IsExpired() bool {
	return false
}

func (p *KeyringProvider) Retrieve() (val credentials.Value, err error) {
	log.Printf("Looking up keyring for %s", p.Profile)
	item, err := p.Keyring.Get(p.Profile)
	if err != nil {
		log.Println("Error from keyring", err)
		return val, err
	}
	if err = json.Unmarshal(item.Data, &val); err != nil {
		return val, fmt.Errorf("Invalid data in keyring: %v", err)
	}
	return val, err
}

func (p *KeyringProvider) Store(val credentials.Value) error {
	bytes, err := json.Marshal(val)
	if err != nil {
		return err
	}

	return p.Keyring.Set(keyring.Item{
		Key:  p.Profile,
		Data: bytes,
	})
}

func (p *KeyringProvider) Delete() error {
	return p.Keyring.Remove(p.Profile)
}

type VaultCredentials struct {
	*credentials.Credentials
	provider *VaultProvider
}

func NewVaultCredentials(k keyring.Keyring, profile string, opts VaultOptions) (*VaultCredentials, error) {
	provider, err := NewVaultProvider(k, profile, opts)
	if err != nil {
		return nil, err
	}

	return &VaultCredentials{credentials.NewCredentials(provider), provider}, nil
}

func (v *VaultCredentials) Expires() time.Time {
	return v.provider.expires
}
<|endoftext|>


<commit_before>package nodes

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"time"

	"github.com/tutumcloud/go-tutum/tutum"
)

var (
	Tutum_Node_Api_Uri   = os.Getenv("TUTUM_NODE_API_URI")
	Tutum_Node_Public_Ip = ""
	peer_ips             = []string{""}
)

func DiscoverPeers(ch chan string) {
	tries := 0
	for {
		node_ips := []string{}
		nodeList, err := tutum.ListNodes()
		if err != nil {
			tries++
			time.Sleep(1)
			if tries > 3 {
				log.Fatal(err)
			}
		}

		for i := range nodeList.Objects {
			state := nodeList.Objects[i].State

			if state == "Deployed" { //or Unreachable
				if nodeList.Objects[i].Public_ip != Tutum_Node_Public_Ip {
					node_ips = append(node_ips, nodeList.Objects[i].Public_ip)
					//ch <- fmt.Sprintln(node_ips)
				}
			}
		}

		//ch <- fmt.Sprintf("Discovering peers")
		var diff1 []string
		for _, s1 := range node_ips {
			found := false
			for _, s2 := range peer_ips {
				if s1 == s2 {
					found = true
					break
				}
			}
			if !found {
				diff1 = append(diff1, s1)
			}
			//ch <- fmt.Sprintln(diff1)
			for _, i := range diff1 {
				log.Println(i)
				connectToPeers(i)
			}
		}
		//ch <- fmt.Sprintln(node_ips)
		//ch <- fmt.Sprintln(peer_ips)

		//ch <- fmt.Sprintf("Forgetting peers")
		var diff2 []string
		for _, s1 := range peer_ips {
			found := false
			for _, s2 := range node_ips {
				if s1 == s2 {
					found = true
					break
				}
			}
			if !found {
				diff2 = append(diff2, s1)
			}
			for _, i := range diff2 {
				forgetPeers(i)
			}
		}
		/*for _, ip := range peer_ips {
			for _, i := range node_ips {
				if ip != i {
					forgetPeers(ip)
				}
			}
		}*/
		peer_ips = node_ips
		break
	}
	ch <- fmt.Sprint("STOP DISCOVER FUNCTION")
}

func connectToPeers(node_ip string) {
	tries := 0
	for {

		log.Printf("connecting to newly discovered peer: %s", node_ip)
		cmd := exec.Command("/weave", "--local", "connect", node_ip)
		stdout, err := cmd.StdoutPipe()
		if err != nil {
			log.Fatal(err)
		}

		stderr, err := cmd.StderrPipe()
		if err != nil {
			log.Fatal(err)
		}

		if err := cmd.Start(); err != nil {
			log.Fatal(err)
		}

		if err := cmd.Wait(); err != nil {
			log.Printf("%s: %s %s", node_ip, stdout, stderr)
			tries++
			if tries > 3 {
				log.Printf("Unable to 'weave connect: %s %s", stdout, stderr)
			}
		} else {
			break
		}
	}
	fmt.Sprint("STOP CONNECT FUNCTION")
}

func forgetPeers(node_ip string) {
	tries := 0
	for {
		log.Printf("forgetting peer: %s", node_ip)
		cmd := exec.Command("/weave", "--local", "forget", node_ip)
		stdout, err := cmd.StdoutPipe()
		if err != nil {
			log.Fatal(err)
		}

		stderr, err := cmd.StderrPipe()
		if err != nil {
			log.Fatal(err)
		}

		if err := cmd.Start(); err != nil {
			log.Fatal(err)
		}

		if err := cmd.Wait(); err != nil {
			log.Printf("%s: %s %s", node_ip, stdout, stderr)
			tries++
			if tries > 3 {
				log.Printf("Unable to 'weave forget: %s %s", stdout, stderr)
			}
			time.Sleep(1)
		} else {
			break
		}
	}
}

func EventHandler(event tutum.Event) {
	ch := make(chan string)
	if event.Type == "node" && (event.State == "Deployed" || event.State == "Terminated") {
		DiscoverPeers(ch)
	}
}
<commit_msg>add additionnal debug flags<commit_after>package nodes

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"time"

	"github.com/tutumcloud/go-tutum/tutum"
)

var (
	Tutum_Node_Api_Uri   = os.Getenv("TUTUM_NODE_API_URI")
	Tutum_Node_Public_Ip = ""
	peer_ips             = []string{""}
)

func DiscoverPeers(ch chan string) {
	tries := 0
	for {
		node_ips := []string{}
		nodeList, err := tutum.ListNodes()
		if err != nil {
			tries++
			time.Sleep(1)
			if tries > 3 {
				log.Fatal(err)
			}
		}

		for i := range nodeList.Objects {
			state := nodeList.Objects[i].State

			if state == "Deployed" { //or Unreachable
				if nodeList.Objects[i].Public_ip != Tutum_Node_Public_Ip {
					node_ips = append(node_ips, nodeList.Objects[i].Public_ip)
					//ch <- fmt.Sprintln(node_ips)
				}
			}
		}

		//ch <- fmt.Sprintf("Discovering peers")
		var diff1 []string
		for _, s1 := range node_ips {
			found := false
			for _, s2 := range peer_ips {
				if s1 == s2 {
					found = true
					break
				}
			}
			if !found {
				diff1 = append(diff1, s1)
			}
			//ch <- fmt.Sprintln(diff1)
			for _, i := range diff1 {
				log.Println(i)
				connectToPeers(i)
			}
		}
		//ch <- fmt.Sprintln(node_ips)
		//ch <- fmt.Sprintln(peer_ips)

		//ch <- fmt.Sprintf("Forgetting peers")
		var diff2 []string
		for _, s1 := range peer_ips {
			found := false
			for _, s2 := range node_ips {
				if s1 == s2 {
					found = true
					break
				}
			}
			if !found {
				diff2 = append(diff2, s1)
			}
			for _, i := range diff2 {
				forgetPeers(i)
			}
		}
		/*for _, ip := range peer_ips {
			for _, i := range node_ips {
				if ip != i {
					forgetPeers(ip)
				}
			}
		}*/
		peer_ips = node_ips
		break
	}
	ch <- fmt.Sprint("STOP DISCOVER FUNCTION")
}

func connectToPeers(node_ip string) {
	tries := 0
	for {

		log.Printf("connecting to newly discovered peer: %s", node_ip)
		cmd := exec.Command("/weave", "--local", "connect", node_ip)
		stdout, err := cmd.StdoutPipe()
		if err != nil {
			log.Fatal(err)
		}

		stderr, err := cmd.StderrPipe()
		if err != nil {
			log.Fatal(err)
		}

		if err := cmd.Start(); err != nil {
			log.Fatal(err)
		}

		if err := cmd.Wait(); err != nil {
			log.Printf("%s: %s %s", node_ip, stdout, stderr)
			tries++
			if tries > 3 {
				log.Printf("Unable to 'weave connect: %s %s", stdout, stderr)
			}
		} else {
			break
		}
	}
	log.Println("STOP CONNECT FUNCTION")
}

func forgetPeers(node_ip string) {
	tries := 0
	for {
		log.Printf("forgetting peer: %s", node_ip)
		cmd := exec.Command("/weave", "--local", "forget", node_ip)
		stdout, err := cmd.StdoutPipe()
		if err != nil {
			log.Fatal(err)
		}

		stderr, err := cmd.StderrPipe()
		if err != nil {
			log.Fatal(err)
		}

		if err := cmd.Start(); err != nil {
			log.Fatal(err)
		}

		if err := cmd.Wait(); err != nil {
			log.Printf("%s: %s %s", node_ip, stdout, stderr)
			tries++
			if tries > 3 {
				log.Printf("Unable to 'weave forget: %s %s", stdout, stderr)
			}
			time.Sleep(1)
		} else {
			break
		}
	}
	log.Println("STOP FORGET FUNCTION")
}

func EventHandler(event tutum.Event) {
	ch := make(chan string)
	if event.Type == "node" && (event.State == "Deployed" || event.State == "Terminated") {
		DiscoverPeers(ch)
	}
}
<|endoftext|>


<commit_before>package linenoise

// -windows

// #include <stdlib.h>
// #include "linenoise.h"
// #include "linenoiseCompletionCallbackHook.h"
import "C"

import (
	"errors"
	"unsafe"
)

// KillSignalError is returned returned by Line() when a user quits from prompt.
// This occurs when the user enters ctrl+C or ctrl+D.
var KillSignalError = errors.New("prompt was quited with a killsignal")

func init() {
	C.linenoiseSetupCompletionCallbackHook()
}

// Line displays given string and returns line from user input.
func Line(prompt string) (string, error) { // char *linenoise(const char *prompt);
	promptCString := C.CString(prompt)
	resultCString := C.linenoise(promptCString)
	C.free(unsafe.Pointer(promptCString))
	defer C.free(unsafe.Pointer(resultCString))

	if resultCString == nil {
		return "", KillSignalError
	}

	result := C.GoString(resultCString)

	return result, nil
}

// AddHistory adds a line to history. Returns non-nil error on fail.
func AddHistory(line string) error { // int linenoiseHistoryAdd(const char *line);
	lineCString := C.CString(line)
	res := C.linenoiseHistoryAdd(lineCString)
	C.free(unsafe.Pointer(lineCString))
	if res != 1 {
		return errors.New("Could not add line to history.")
	}
	return nil
}

// SetHistoryCapacity changes the maximum length of history. Returns non-nil error on fail.
func SetHistoryCapacity(capacity int) error { // int linenoiseHistorySetMaxLen(int len);
	res := C.linenoiseHistorySetMaxLen(C.int(capacity))
	if res != 1 {
		return errors.New("Could not set history max len.")
	}
	return nil
}

// SaveHistory saves from file with given filename. Returns non-nil error on fail.
func SaveHistory(filename string) error { // int linenoiseHistorySave(char *filename);
	filenameCString := C.CString(filename)
	res := C.linenoiseHistorySave(filenameCString)
	C.free(unsafe.Pointer(filenameCString))
	if res != 0 {
		return errors.New("Could not save history to file.")
	}
	return nil
}

// LoadHistory loads from file with given filename. Returns non-nil error on fail.
func LoadHistory(filename string) error { // int linenoiseHistoryLoad(char *filename);
	filenameCString := C.CString(filename)
	res := C.linenoiseHistoryLoad(filenameCString)
	C.free(unsafe.Pointer(filenameCString))
	if res != 0 {
		return errors.New("Could not load history from file.")
	}
	return nil
}

// Clear clears the screen.
func Clear() { // void linenoiseClearScreen(void);
	C.linenoiseClearScreen()
}

// SetMultiline sets linenoise to multiline or single line.
// In multiline mode the user input will be wrapped to a new line when the length exceeds the amount of available rows in the terminal.
func SetMultiline(ml bool) { // void linenoiseSetMultiLine(int ml);
	if ml {
		C.linenoiseSetMultiLine(1)
	} else {
		C.linenoiseSetMultiLine(0)
	}
}

// CompletionHandler provides possible completions for given input
type CompletionHandler func(input string) []string

// DefaultCompletionHandler simply returns an empty slice.
var DefaultCompletionHandler = func(input string) []string {
	return make([]string, 0)
}

var complHandler = DefaultCompletionHandler

// SetCompletionHandler sets the CompletionHandler to be used for completion
func SetCompletionHandler(c CompletionHandler) {
	complHandler = c
}

// typedef struct linenoiseCompletions {
//   size_t len;
//   char **cvec;
// } linenoiseCompletions;
// typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
// void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
// void linenoiseAddCompletion(linenoiseCompletions *, char *);

//export linenoiseGoCompletionCallbackHook
func linenoiseGoCompletionCallbackHook(input *C.char, completions *C.linenoiseCompletions) {
	completionsSlice := complHandler(C.GoString(input))

	completionsLen := len(completionsSlice)
	completions.len = C.size_t(completionsLen)

	if completionsLen > 0 {
		cvec := C.malloc(C.size_t(int(unsafe.Sizeof(*(**C.char)(nil))) * completionsLen))
		cvecSlice := (*(*[999999]*C.char)(cvec))[:completionsLen]

		for i, str := range completionsSlice {
			cvecSlice[i] = C.CString(str)
		}
		completions.cvec = (**C.char)(cvec)
	}
}

// PrintKeyCodes puts linenoise in key codes debugging mode.
// Press keys to see scan codes. Type 'quit' at any time to exit.
// PrintKeyCodes blocks until user enters 'quit'.
func PrintKeyCodes() { // void linenoisePrintKeyCodes(void);
	C.linenoisePrintKeyCodes()
}
<commit_msg>Update PrintKeyCodes description<commit_after>package linenoise

// -windows

// #include <stdlib.h>
// #include "linenoise.h"
// #include "linenoiseCompletionCallbackHook.h"
import "C"

import (
	"errors"
	"unsafe"
)

// KillSignalError is returned returned by Line() when a user quits from prompt.
// This occurs when the user enters ctrl+C or ctrl+D.
var KillSignalError = errors.New("prompt was quited with a killsignal")

func init() {
	C.linenoiseSetupCompletionCallbackHook()
}

// Line displays given string and returns line from user input.
func Line(prompt string) (string, error) { // char *linenoise(const char *prompt);
	promptCString := C.CString(prompt)
	resultCString := C.linenoise(promptCString)
	C.free(unsafe.Pointer(promptCString))
	defer C.free(unsafe.Pointer(resultCString))

	if resultCString == nil {
		return "", KillSignalError
	}

	result := C.GoString(resultCString)

	return result, nil
}

// AddHistory adds a line to history. Returns non-nil error on fail.
func AddHistory(line string) error { // int linenoiseHistoryAdd(const char *line);
	lineCString := C.CString(line)
	res := C.linenoiseHistoryAdd(lineCString)
	C.free(unsafe.Pointer(lineCString))
	if res != 1 {
		return errors.New("Could not add line to history.")
	}
	return nil
}

// SetHistoryCapacity changes the maximum length of history. Returns non-nil error on fail.
func SetHistoryCapacity(capacity int) error { // int linenoiseHistorySetMaxLen(int len);
	res := C.linenoiseHistorySetMaxLen(C.int(capacity))
	if res != 1 {
		return errors.New("Could not set history max len.")
	}
	return nil
}

// SaveHistory saves from file with given filename. Returns non-nil error on fail.
func SaveHistory(filename string) error { // int linenoiseHistorySave(char *filename);
	filenameCString := C.CString(filename)
	res := C.linenoiseHistorySave(filenameCString)
	C.free(unsafe.Pointer(filenameCString))
	if res != 0 {
		return errors.New("Could not save history to file.")
	}
	return nil
}

// LoadHistory loads from file with given filename. Returns non-nil error on fail.
func LoadHistory(filename string) error { // int linenoiseHistoryLoad(char *filename);
	filenameCString := C.CString(filename)
	res := C.linenoiseHistoryLoad(filenameCString)
	C.free(unsafe.Pointer(filenameCString))
	if res != 0 {
		return errors.New("Could not load history from file.")
	}
	return nil
}

// Clear clears the screen.
func Clear() { // void linenoiseClearScreen(void);
	C.linenoiseClearScreen()
}

// SetMultiline sets linenoise to multiline or single line.
// In multiline mode the user input will be wrapped to a new line when the length exceeds the amount of available rows in the terminal.
func SetMultiline(ml bool) { // void linenoiseSetMultiLine(int ml);
	if ml {
		C.linenoiseSetMultiLine(1)
	} else {
		C.linenoiseSetMultiLine(0)
	}
}

// CompletionHandler provides possible completions for given input
type CompletionHandler func(input string) []string

// DefaultCompletionHandler simply returns an empty slice.
var DefaultCompletionHandler = func(input string) []string {
	return make([]string, 0)
}

var complHandler = DefaultCompletionHandler

// SetCompletionHandler sets the CompletionHandler to be used for completion
func SetCompletionHandler(c CompletionHandler) {
	complHandler = c
}

// typedef struct linenoiseCompletions {
//   size_t len;
//   char **cvec;
// } linenoiseCompletions;
// typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
// void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
// void linenoiseAddCompletion(linenoiseCompletions *, char *);

//export linenoiseGoCompletionCallbackHook
func linenoiseGoCompletionCallbackHook(input *C.char, completions *C.linenoiseCompletions) {
	completionsSlice := complHandler(C.GoString(input))

	completionsLen := len(completionsSlice)
	completions.len = C.size_t(completionsLen)

	if completionsLen > 0 {
		cvec := C.malloc(C.size_t(int(unsafe.Sizeof(*(**C.char)(nil))) * completionsLen))
		cvecSlice := (*(*[999999]*C.char)(cvec))[:completionsLen]

		for i, str := range completionsSlice {
			cvecSlice[i] = C.CString(str)
		}
		completions.cvec = (**C.char)(cvec)
	}
}

// PrintKeyCodes puts linenoise in key codes debugging mode.
// Press keys and key combinations to see key codes. Type 'quit' at any time to exit.
// PrintKeyCodes blocks until user enters 'quit'.
func PrintKeyCodes() { // void linenoisePrintKeyCodes(void);
	C.linenoisePrintKeyCodes()
}
<|endoftext|>