<commit_before>package iamy

import (
	"errors"
	"regexp"
	"strings"

	"github.com/99designs/iamy/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws"
	"github.com/99designs/iamy/Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/ec2"
	"github.com/99designs/iamy/Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/iam"
	"github.com/99designs/iamy/Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/iam/iamiface"
)

var cfnResourceRegexp = regexp.MustCompile(`-[A-Z0-9]{10,20}$`)

var Aws = awsIamFetcher{
	client: iam.New(nil),
}

type awsIamFetcher struct {
	client  iamiface.IAMAPI
	account *Account
}

func (a *awsIamFetcher) Fetch() (*AccountData, error) {
	logPrintln("Fetching AWS IAM data")
	var err error
	data := AccountData{}

	if data.Account, err = a.getAccount(); err != nil {
		return nil, err
	}
	a.account = data.Account

	if data.Users, err = a.loadUsers(); err != nil {
		return nil, err
	}

	if data.Policies, err = a.loadPolicies(); err != nil {
		return nil, err
	}

	if data.Groups, err = a.loadGroups(); err != nil {
		return nil, err
	}

	if data.Roles, err = a.loadRoles(); err != nil {
		return nil, err
	}

	return &data, nil
}

func (a *awsIamFetcher) getAccount() (*Account, error) {
	var err error
	acct := Account{}

	acct.Id, err = a.determineAccountId()
	if err != nil {
		return nil, err
	}

	aliasResp, err := a.client.ListAccountAliases(&iam.ListAccountAliasesInput{})
	if err != nil {
		return nil, err
	}
	if len(aliasResp.AccountAliases) > 0 {
		acct.Alias = *aliasResp.AccountAliases[0]
	}

	return &acct, nil
}

func (a *awsIamFetcher) determineAccountId() (string, error) {
	accountid, err := a.determineAccountIdViaGetUser()
	if err == nil {
		return accountid, nil
	}

	accountid, err = a.determineAccountIdViaListUsers()
	if err == nil {
		return accountid, nil
	}

	accountid, err = determineAccountIdViaDefaultSecurityGroup()
	if err == nil {
		return accountid, nil
	}
	if err == aws.ErrMissingRegion {
		return "", errors.New("Error determining the AWS account id - check the AWS_REGION environment variable is set")
	}

	return "", errors.New("Can't determine the AWS account id")
}

func getAccountIdFromArn(arn string) string {
	s := strings.Split(arn, ":")
	return s[4]
}

// see http://stackoverflow.com/a/18124234
func (a *awsIamFetcher) determineAccountIdViaGetUser() (string, error) {
	getUserResp, err := a.client.GetUser(&iam.GetUserInput{})
	if err != nil {
		return "", err
	}

	return getAccountIdFromArn(*getUserResp.User.Arn), nil
}

func (a *awsIamFetcher) determineAccountIdViaListUsers() (string, error) {
	listUsersResp, err := a.client.ListUsers(&iam.ListUsersInput{})
	if err != nil {
		return "", err
	}
	if len(listUsersResp.Users) == 0 {
		return "", errors.New("No users found")
	}

	return getAccountIdFromArn(*listUsersResp.Users[0].Arn), nil
}

// see http://stackoverflow.com/a/30578645
func determineAccountIdViaDefaultSecurityGroup() (string, error) {
	ec2Client := ec2.New(nil)

	sg, err := ec2Client.DescribeSecurityGroups(&ec2.DescribeSecurityGroupsInput{
		GroupNames: []*string{
			aws.String("default"),
		},
	})
	if err != nil {
		return "", err
	}
	if len(sg.SecurityGroups) == 0 {
		return "", errors.New("No security groups found")
	}

	return *sg.SecurityGroups[0].OwnerId, nil
}

func (a *awsIamFetcher) loadUsers() ([]User, error) {
	logPrintln("Fetching AWS IAM users")

	resp, err := a.client.ListUsers(&iam.ListUsersInput{})
	if err != nil {
		return nil, err
	}

	users := []User{}

	for _, user := range resp.Users {
		if cfnResourceRegexp.MatchString(*user.UserName) {
			logPrintf("Skipping CloudFormation generated user %s", *user.UserName)
			continue
		}

		logPrintf("Fetching %s", *user.Arn)

		u := User{
			Name: *user.UserName,
			Path: *user.Path,
		}

		if err = a.populateUserGroups(&u); err != nil {
			return nil, err
		}

		if err = a.populateUserPolicies(&u); err != nil {
			return nil, err
		}

		users = append(users, u)
	}

	return users, nil
}

func (a *awsIamFetcher) populateUserGroups(user *User) error {
	params := &iam.ListGroupsForUserInput{
		UserName: aws.String(user.Name), // Required
	}

	user.Groups = []string{}
	resp, err := a.client.ListGroupsForUser(params)
	if err != nil {
		return err
	}

	for _, group := range resp.Groups {
		user.Groups = append(user.Groups, *group.GroupName)
	}

	return nil
}

func (a *awsIamFetcher) populateUserPolicies(user *User) error {
	params := &iam.ListUserPoliciesInput{
		UserName: aws.String(user.Name), // Required
	}

	user.InlinePolicies = []InlinePolicy{}
	resp, err := a.client.ListUserPolicies(params)
	if err != nil {
		return err
	}

	for _, policyName := range resp.PolicyNames {
		policyResp, err := a.client.GetUserPolicy(&iam.GetUserPolicyInput{
			PolicyName: policyName,
			UserName:   aws.String(user.Name),
		})
		if err != nil {
			return err
		}

		doc, err := NewPolicyDocumentFromEncodedJson(*policyResp.PolicyDocument)
		if err != nil {
			return err
		}

		user.InlinePolicies = append(user.InlinePolicies, InlinePolicy{
			Name:   *policyName,
			Policy: doc,
		})
	}

	user.Policies = []string{}
	attachedResp, err := a.client.ListAttachedUserPolicies(&iam.ListAttachedUserPoliciesInput{
		UserName: aws.String(user.Name),
	})

	for _, policyResp := range attachedResp.AttachedPolicies {
		user.Policies = append(user.Policies, a.account.normalisePolicyArn(*policyResp.PolicyArn))
	}

	return nil
}

func (a *awsIamFetcher) loadPolicies() ([]Policy, error) {
	logPrintln("Fetching AWS IAM policies")

	resp, err := a.client.ListPolicies(&iam.ListPoliciesInput{
		Scope:        aws.String(iam.PolicyScopeTypeLocal),
		OnlyAttached: aws.Bool(false),
	})
	if err != nil {
		return nil, err
	}

	policies := []Policy{}

	for _, respPolicy := range resp.Policies {
		if cfnResourceRegexp.MatchString(*respPolicy.PolicyName) {
			logPrintf("Skipping CloudFormation generated policy %s", *respPolicy.PolicyName)
			continue
		}

		logPrintf("Fetching policy %s", *respPolicy.Arn)

		respVersions, err := a.client.ListPolicyVersions(&iam.ListPolicyVersionsInput{
			PolicyArn: respPolicy.Arn,
		})
		if err != nil {
			return nil, err
		}

		for _, version := range respVersions.Versions {
			if *version.IsDefaultVersion {
				respPolicyVersion, err := a.client.GetPolicyVersion(&iam.GetPolicyVersionInput{
					PolicyArn: respPolicy.Arn,
					VersionId: version.VersionId,
				})
				if err != nil {
					return nil, err
				}
				doc, err := NewPolicyDocumentFromEncodedJson(*respPolicyVersion.PolicyVersion.Document)
				if err != nil {
					return nil, err
				}
				policy := Policy{
					Name:   *respPolicy.PolicyName,
					Path:   *respPolicy.Path,
					Policy: doc,
				}

				policies = append(policies, policy)
			}
		}
	}

	return policies, nil
}

func (a *awsIamFetcher) loadGroups() ([]Group, error) {
	logPrintln("Fetching AWS IAM groups")

	params := &iam.ListGroupsInput{}
	resp, err := a.client.ListGroups(params)
	if err != nil {
		return nil, err
	}

	groups := []Group{}

	for _, groupResp := range resp.Groups {
		if cfnResourceRegexp.MatchString(*groupResp.GroupName) {
			logPrintf("Skipping CloudFormation generated group %s", *groupResp.GroupName)
			continue
		}

		logPrintf("Fetching group %s", *groupResp.Arn)
		group := Group{
			Name: *groupResp.GroupName,
			Path: *groupResp.Path,
		}

		if err = a.populateGroupPolicies(&group); err != nil {
			return nil, err
		}

		groups = append(groups, group)
	}

	return groups, nil
}

func (a *awsIamFetcher) populateGroupPolicies(group *Group) error {
	params := &iam.ListGroupPoliciesInput{
		GroupName: aws.String(group.Name),
	}

	group.InlinePolicies = []InlinePolicy{}
	resp, err := a.client.ListGroupPolicies(params)
	if err != nil {
		return err
	}

	for _, policyName := range resp.PolicyNames {
		policyResp, err := a.client.GetGroupPolicy(&iam.GetGroupPolicyInput{
			PolicyName: policyName,
			GroupName:  aws.String(group.Name),
		})
		if err != nil {
			return err
		}

		doc, err := NewPolicyDocumentFromEncodedJson(*policyResp.PolicyDocument)
		if err != nil {
			return err
		}

		group.InlinePolicies = append(group.InlinePolicies, InlinePolicy{
			Name:   *policyName,
			Policy: doc,
		})
	}

	group.Policies = []string{}
	attachedResp, err := a.client.ListAttachedGroupPolicies(&iam.ListAttachedGroupPoliciesInput{
		GroupName: aws.String(group.Name),
	})

	for _, policyResp := range attachedResp.AttachedPolicies {
		group.Policies = append(group.Policies, a.account.normalisePolicyArn(*policyResp.PolicyArn))
	}

	return nil
}

func (a *awsIamFetcher) loadRoles() ([]Role, error) {
	logPrintln("Fetching AWS IAM Roles")

	resp, err := a.client.ListRoles(&iam.ListRolesInput{})
	if err != nil {
		return nil, err
	}

	roles := []Role{}

	for _, roleResp := range resp.Roles {
		if cfnResourceRegexp.MatchString(*roleResp.RoleName) {
			logPrintf("Skipping CloudFormation generated role %s", *roleResp.RoleName)
			continue
		}

		logPrintf("Fetching role %s", *roleResp.Arn)

		doc, err := NewPolicyDocumentFromEncodedJson(*roleResp.AssumeRolePolicyDocument)
		if err != nil {
			return nil, err
		}

		role := Role{
			Name: *roleResp.RoleName,
			Path: *roleResp.Path,
			AssumeRolePolicyDocument: doc,
		}

		if err = a.populateRolePolicies(&role); err != nil {
			return nil, err
		}

		roles = append(roles, role)
	}

	return roles, nil
}

func (a *awsIamFetcher) populateRolePolicies(role *Role) error {
	params := &iam.ListRolePoliciesInput{
		RoleName: aws.String(role.Name),
	}

	role.InlinePolicies = []InlinePolicy{}
	resp, err := a.client.ListRolePolicies(params)
	if err != nil {
		return err
	}

	for _, policyName := range resp.PolicyNames {
		policyResp, err := a.client.GetRolePolicy(&iam.GetRolePolicyInput{
			PolicyName: policyName,
			RoleName:   aws.String(role.Name),
		})
		if err != nil {
			return err
		}

		doc, err := NewPolicyDocumentFromEncodedJson(*policyResp.PolicyDocument)
		if err != nil {
			return err
		}

		role.InlinePolicies = append(role.InlinePolicies, InlinePolicy{
			Name:   *policyName,
			Policy: doc,
		})
	}

	role.Policies = []string{}
	attachedResp, err := a.client.ListAttachedRolePolicies(&iam.ListAttachedRolePoliciesInput{
		RoleName: aws.String(role.Name),
	})

	for _, policyResp := range attachedResp.AttachedPolicies {
		role.Policies = append(role.Policies, a.account.normalisePolicyArn(*policyResp.PolicyArn))
	}

	return nil
}
<commit_msg>Simplify aws api calls to use GetAccountAuthorizationDetails<commit_after>package iamy

import (
	"errors"
	"regexp"
	"strings"

	"github.com/99designs/iamy/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws"
	"github.com/99designs/iamy/Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/ec2"
	"github.com/99designs/iamy/Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/iam"
	"github.com/99designs/iamy/Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/iam/iamiface"
)

var cfnResourceRegexp = regexp.MustCompile(`-[A-Z0-9]{10,20}$`)

var Aws = awsIamFetcher{
	client: iam.New(nil),
}

type awsIamFetcher struct {
	client  iamiface.IAMAPI
	account *Account
}

func (a *awsIamFetcher) Fetch() (*AccountData, error) {
	logPrintln("Fetching AWS IAM data")
	var err error
	data := AccountData{}

	if data.Account, err = a.getAccount(); err != nil {
		return nil, err
	}
	a.account = data.Account

	complete := false
	var marker *string = nil
	for !complete {
		resp, err := a.client.GetAccountAuthorizationDetails(&iam.GetAccountAuthorizationDetailsInput{
			MaxItems: aws.Int64(1000),
			Marker:   marker,
			Filter: aws.StringSlice([]string{
				iam.EntityTypeUser,
				iam.EntityTypeGroup,
				iam.EntityTypeRole,
				iam.EntityTypeLocalManagedPolicy,
			}),
		})
		if err != nil {
			return nil, err
		}

		err = a.populateData(resp, &data)
		if err != nil {
			return nil, err
		}

		if *resp.IsTruncated {
			marker = resp.Marker
		} else {
			complete = true
		}
	}

	return &data, nil
}

func (a *awsIamFetcher) populateInlinePolicies(source []*iam.PolicyDetail, target *[]InlinePolicy) error {
	for _, ip := range source {
		doc, err := NewPolicyDocumentFromEncodedJson(*ip.PolicyDocument)
		if err != nil {
			return err
		}
		*target = append(*target, InlinePolicy{
			Name:   *ip.PolicyName,
			Policy: doc,
		})
	}

	return nil
}

func (a *awsIamFetcher) populateData(resp *iam.GetAccountAuthorizationDetailsOutput, data *AccountData) error {
	for _, userResp := range resp.UserDetailList {
		if cfnResourceRegexp.MatchString(*userResp.UserName) {
			logPrintf("Skipping CloudFormation generated user %s", *userResp.UserName)
			continue
		}

		user := User{
			Name: *userResp.UserName,
			Path: *userResp.Path,
		}

		for _, g := range userResp.GroupList {
			user.Groups = append(user.Groups, *g)
		}
		for _, p := range userResp.AttachedManagedPolicies {
			user.Policies = append(user.Policies, a.account.normalisePolicyArn(*p.PolicyArn))
		}
		if err := a.populateInlinePolicies(userResp.UserPolicyList, &user.InlinePolicies); err != nil {
			return err
		}

		data.Users = append(data.Users, user)
	}

	for _, groupResp := range resp.GroupDetailList {
		if cfnResourceRegexp.MatchString(*groupResp.GroupName) {
			logPrintf("Skipping CloudFormation generated group %s", *groupResp.GroupName)
			continue
		}

		group := Group{
			Name: *groupResp.GroupName,
			Path: *groupResp.Path,
		}

		for _, p := range groupResp.AttachedManagedPolicies {
			group.Policies = append(group.Policies, a.account.normalisePolicyArn(*p.PolicyArn))
		}
		if err := a.populateInlinePolicies(groupResp.GroupPolicyList, &group.InlinePolicies); err != nil {
			return err
		}

		data.Groups = append(data.Groups, group)
	}

	for _, roleResp := range resp.RoleDetailList {
		if cfnResourceRegexp.MatchString(*roleResp.RoleName) {
			logPrintf("Skipping CloudFormation generated role %s", *roleResp.RoleName)
			continue
		}

		role := Role{
			Name: *roleResp.RoleName,
			Path: *roleResp.Path,
		}

		var err error
		role.AssumeRolePolicyDocument, err = NewPolicyDocumentFromEncodedJson(*roleResp.AssumeRolePolicyDocument)
		if err != nil {
			return err
		}
		for _, p := range roleResp.AttachedManagedPolicies {
			role.Policies = append(role.Policies, a.account.normalisePolicyArn(*p.PolicyArn))
		}
		if err := a.populateInlinePolicies(roleResp.RolePolicyList, &role.InlinePolicies); err != nil {
			return err
		}

		data.Roles = append(data.Roles, role)
	}

	for _, policyResp := range resp.Policies {
		if cfnResourceRegexp.MatchString(*policyResp.PolicyName) {
			logPrintf("Skipping CloudFormation generated policy %s", *policyResp.PolicyName)
			continue
		}

		for _, version := range policyResp.PolicyVersionList {
			if *version.IsDefaultVersion {
				doc, err := NewPolicyDocumentFromEncodedJson(*version.Document)
				if err != nil {
					return err
				}

				data.Policies = append(data.Policies, Policy{
					Name:   *policyResp.PolicyName,
					Path:   *policyResp.Path,
					Policy: doc,
				})
			}
		}
	}

	return nil
}

func (a *awsIamFetcher) getAccount() (*Account, error) {
	var err error
	acct := Account{}

	acct.Id, err = a.determineAccountId()
	if err != nil {
		return nil, err
	}

	aliasResp, err := a.client.ListAccountAliases(&iam.ListAccountAliasesInput{})
	if err != nil {
		return nil, err
	}
	if len(aliasResp.AccountAliases) > 0 {
		acct.Alias = *aliasResp.AccountAliases[0]
	}

	return &acct, nil
}

func (a *awsIamFetcher) determineAccountId() (string, error) {
	accountid, err := a.determineAccountIdViaGetUser()
	if err == nil {
		return accountid, nil
	}

	accountid, err = a.determineAccountIdViaListUsers()
	if err == nil {
		return accountid, nil
	}

	accountid, err = determineAccountIdViaDefaultSecurityGroup()
	if err == nil {
		return accountid, nil
	}
	if err == aws.ErrMissingRegion {
		return "", errors.New("Error determining the AWS account id - check the AWS_REGION environment variable is set")
	}

	return "", errors.New("Can't determine the AWS account id")
}

func getAccountIdFromArn(arn string) string {
	s := strings.Split(arn, ":")
	return s[4]
}

// see http://stackoverflow.com/a/18124234
func (a *awsIamFetcher) determineAccountIdViaGetUser() (string, error) {
	getUserResp, err := a.client.GetUser(&iam.GetUserInput{})
	if err != nil {
		return "", err
	}

	return getAccountIdFromArn(*getUserResp.User.Arn), nil
}

func (a *awsIamFetcher) determineAccountIdViaListUsers() (string, error) {
	listUsersResp, err := a.client.ListUsers(&iam.ListUsersInput{})
	if err != nil {
		return "", err
	}
	if len(listUsersResp.Users) == 0 {
		return "", errors.New("No users found")
	}

	return getAccountIdFromArn(*listUsersResp.Users[0].Arn), nil
}

// see http://stackoverflow.com/a/30578645
func determineAccountIdViaDefaultSecurityGroup() (string, error) {
	ec2Client := ec2.New(nil)

	sg, err := ec2Client.DescribeSecurityGroups(&ec2.DescribeSecurityGroupsInput{
		GroupNames: []*string{
			aws.String("default"),
		},
	})
	if err != nil {
		return "", err
	}
	if len(sg.SecurityGroups) == 0 {
		return "", errors.New("No security groups found")
	}

	return *sg.SecurityGroups[0].OwnerId, nil
}
<|endoftext|>


<commit_before>package container

import (
	"bytes"
	"io/ioutil"
	"testing"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/fsouza/go-dockerclient"
	mesos "github.com/mesos/mesos-go/mesosproto"
	. "github.com/smartystreets/goconvey/convey"
)

const (
	prelude = "LO, praise of the prowess of people-kings of spear-armed Danes, in days long sped"
	ending  = "Thus made their mourning the men of Geatland, for their hero's passing his hearth-companions"
)

func init() {
	log.SetOutput(ioutil.Discard)
}

func Test_PullImage(t *testing.T) {
	Convey("PullImage()", t, func() {

		image := "foo/foo:foo"
		taskInfo := &mesos.TaskInfo{
			Container: &mesos.ContainerInfo{
				Docker: &mesos.ContainerInfo_DockerInfo{
					Image: &image,
				},
			},
		}

		dockerClient := &MockDockerClient{}

		Convey("passes the right params", func() {
			err := PullImage(dockerClient, taskInfo, &docker.AuthConfiguration{})

			So(dockerClient.validOptions, ShouldBeTrue)
			So(err, ShouldBeNil)
		})

		Convey("bubbles up errors", func() {
			dockerClient.PullImageShouldError = true
			err := PullImage(dockerClient, taskInfo, &docker.AuthConfiguration{})

			So(err, ShouldNotBeNil)
			So(err.Error(), ShouldContainSubstring, "Something went wrong")
		})
	})
}

func Test_CheckImage(t *testing.T) {
	Convey("CheckImage()", t, func() {
		image := "gonitro/sidecar:1.0.0"
		images := []docker.APIImages{
			{
				RepoTags: []string{image, "sidecar", "sidecar:latest"},
			},
		}

		taskInfo := &mesos.TaskInfo{
			Container: &mesos.ContainerInfo{
				Docker: &mesos.ContainerInfo_DockerInfo{
					Image: &image,
				},
			},
		}

		dockerClient := &MockDockerClient{Images: images}

		Convey("handles errors", func() {
			dockerClient.ListImagesShouldError = true
			So(CheckImage(dockerClient, taskInfo), ShouldBeFalse)
		})

		Convey("matches the image", func() {
			So(CheckImage(dockerClient, taskInfo), ShouldBeTrue)
		})

		Convey("handles missing images", func() {
			wrong := "wrong"
			taskInfo.Container.Docker.Image = &wrong
			So(CheckImage(dockerClient, taskInfo), ShouldBeFalse)
		})

	})
}

func Test_StopContainer(t *testing.T) {
	Convey("When stopping containers", t, func() {
		dockerClient := &MockDockerClient{
			StopContainerShouldError: true,
			StopContainerMaxFails:    1,
			Container: &docker.Container{
				State: docker.State{
					Status: "running",
				},
			},
		}

		Convey("retries stopping the container", func() {
			err := StopContainer(dockerClient, "someid", 0)
			So(err, ShouldNotBeNil)
			So(err.Error(), ShouldNotContainSubstring, "Unable to kill")
			So(dockerClient.stopContainerFails, ShouldEqual, 2)
		})

		Convey("returns an error when it really won't stop", func() {
			dockerClient.StopContainerMaxFails = 2
			err := StopContainer(dockerClient, "someid", 0)
			So(err, ShouldNotBeNil)
			So(err.Error(), ShouldContainSubstring, "Unable to kill")
			So(dockerClient.stopContainerFails, ShouldEqual, 2)

		})
	})
}

func Test_GetLogs(t *testing.T) {
	Convey("Fetches the logs from a task", t, func() {
		containerId := "mesos-nginx-2392676-1479746266455-1-dev_singularity_sick_sing-DEFAULT"
		dockerClient := &MockDockerClient{
			LogOutputString: prelude,
			LogErrorString:  ending,
		}

		stdout := bytes.NewBuffer(make([]byte, 0, 256))
		stderr := bytes.NewBuffer(make([]byte, 0, 256))

		GetLogs(dockerClient, containerId, time.Now().UTC().Unix(), stdout, stderr)

		time.Sleep(1 * time.Millisecond) // Nasty, but lets buffer flush

		So(string(stdout.Bytes()), ShouldResemble, prelude)
		So(string(stderr.Bytes()), ShouldResemble, ending)

		So(dockerClient.logOpts.Stdout, ShouldBeTrue)
		So(dockerClient.logOpts.OutputStream, ShouldNotBeNil)
		So(dockerClient.logOpts.ErrorStream, ShouldNotBeNil)
	})
}

func Test_ConfigGeneration(t *testing.T) {
	Convey("Generating the Docker config from a Mesos Task", t, func() {

		// The whole structure is full of pointers, so we have to define
		// a bunch of things so we can take their address.
		taskId := "nginx-2392676-1479746266455-1-dev_singularity_sick_sing-DEFAULT"
		image := "foo/foo:1.0.0"
		cpus := "cpus"
		cpusValue := float64(0.5)
		memory := "mem"
		memoryValue := float64(128)

		env := "env"
		envValue := "SOMETHING=123=123"
		label := "label"
		labelValue := "ANYTHING=123=123"
		capAdd := "cap-add"
		capAddValue := "NET_ADMIN"
		capDrop := "cap-drop"
		capDropValue := "NET_ADMIN"

		svcName := "dev-test-app"
		svcNameLabel := "ServiceName=" + svcName
		envName := "dev"
		envNameLabel := "EnvironmentName=" + envName

		host := mesos.ContainerInfo_DockerInfo_HOST

		port := uint32(8080)
		port2 := uint32(443)
		port2_hp := uint32(10270)
		port3 := uint32(9090)
		port3_hp := uint32(10271)
		port3Proto := "tcp,udp"

		v1_cp := "/tmp/somewhere"
		v1_hp := "/tmp/elsewhere"
		v2_cp := "/tmp/foo"
		v2_hp := "/tmp/bar"
		mode := mesos.Volume_RO

		hostname := "beowulf.example.com"
		hostKey := "TASK_HOST"

		taskInfo := &mesos.TaskInfo{
			TaskId: &mesos.TaskID{Value: &taskId},
			Container: &mesos.ContainerInfo{
				Docker: &mesos.ContainerInfo_DockerInfo{
					Image:   &image,
					Network: &host,
					Parameters: []*mesos.Parameter{
						{
							Key:   &env,
							Value: &envValue,
						},
						{
							Key:   &label,
							Value: &labelValue,
						},
						{
							Key:   &label,
							Value: &svcNameLabel,
						},
						{
							Key:   &label,
							Value: &envNameLabel,
						},
						{
							Key:   &capAdd,
							Value: &capAddValue,
						},
						{
							Key:   &capDrop,
							Value: &capDropValue,
						},
					},
					PortMappings: []*mesos.ContainerInfo_DockerInfo_PortMapping{
						{
							ContainerPort: &port,
						},
						{
							ContainerPort: &port2,
							HostPort:      &port2_hp,
						},
						{
							ContainerPort: &port3,
							HostPort:      &port3_hp,
							Protocol:      &port3Proto,
						},
					},
				},
				Volumes: []*mesos.Volume{
					{
						Mode:          &mode,
						ContainerPath: &v1_cp,
						HostPath:      &v1_hp,
					},
					{
						ContainerPath: &v2_cp,
						HostPath:      &v2_hp,
					},
				},
			},
			Resources: []*mesos.Resource{
				{
					Name:   &cpus,
					Scalar: &mesos.Value_Scalar{Value: &cpusValue},
				},
				{
					Name:   &memory,
					Scalar: &mesos.Value_Scalar{Value: &memoryValue},
				},
			},
			Executor: &mesos.ExecutorInfo{
				Command: &mesos.CommandInfo{
					Environment: &mesos.Environment{
						Variables: []*mesos.Environment_Variable{
							{
								Name:  &hostKey,
								Value: &hostname,
							},
						},
					},
				},
			},
		}

		opts := ConfigForTask(taskInfo, false, false)
		optsForced := ConfigForTask(taskInfo, true, true)

		Convey("gets the name from the task ID", func() {
			So(opts.Name, ShouldEqual, "mesos-"+taskId)
		})

		Convey("properly calculates the CPU limit", func() {
			So(optsForced.HostConfig.CPUPeriod, ShouldEqual, float64(50000))
			So(optsForced.HostConfig.CPUQuota, ShouldEqual, float64(25000))

			So(opts.HostConfig.CPUPeriod, ShouldEqual, float64(0))
			So(opts.HostConfig.CPUQuota, ShouldEqual, float64(0))
		})

		Convey("properly calculates the memory limit", func() {
			So(optsForced.HostConfig.Memory, ShouldEqual, float64(128*1024*1024))
			So(opts.HostConfig.Memory, ShouldEqual, float64(0))
		})

		Convey("populates the environment", func() {
			So(len(opts.Config.Env), ShouldBeGreaterThan, 1)
			So(opts.Config.Env[0], ShouldEqual, "SOMETHING=123=123")
		})

		Convey("maps ports into the environment", func() {
			// We index backward to find the vars we just set
			So(opts.Config.Env[len(opts.Config.Env)-6], ShouldEqual, "MESOS_PORT_443=10270")
		})

		Convey("maps the hostname into the environment", func() {
			So(opts.Config.Env[len(opts.Config.Env)-4], ShouldEqual, "MESOS_HOSTNAME="+hostname)
		})

		Convey("maps the ServiceName into the environment", func() {
			So(opts.Config.Env[len(opts.Config.Env)-3], ShouldEqual, "SERVICE_NAME="+svcName)
		})

		Convey("maps the EnvironmentName into the environment", func() {
			So(opts.Config.Env[len(opts.Config.Env)-2], ShouldEqual, "ENVIRONMENT_NAME="+envName)
		})

		Convey("maps the version into the environment", func() {
			So(opts.Config.Env[len(opts.Config.Env)-1], ShouldEqual, "SERVICE_VERSION=1.0.0")
		})

		Convey("fills in the exposed ports", func() {
			So(len(opts.Config.ExposedPorts), ShouldEqual, 4)
			So(opts.Config.ExposedPorts, ShouldContainKey, docker.Port("8080/tcp"))
			So(opts.Config.ExposedPorts, ShouldContainKey, docker.Port("9090/tcp"))
			So(opts.Config.ExposedPorts, ShouldContainKey, docker.Port("9090/udp"))
		})

		Convey("has the right image name", func() {
			So(opts.Config.Image, ShouldEqual, image)
		})

		Convey("gets the labels", func() {
			So(len(opts.Config.Labels), ShouldBeGreaterThanOrEqualTo, 1)
			So(opts.Config.Labels["ANYTHING"], ShouldEqual, "123=123")
		})

		Convey("gets the cap-adds", func() {
			So(len(opts.HostConfig.CapAdd), ShouldEqual, 1)
			So(opts.HostConfig.CapAdd[0], ShouldEqual, "NET_ADMIN")
		})

		Convey("gets the cap-drops", func() {
			So(len(opts.HostConfig.CapDrop), ShouldEqual, 1)
			So(opts.HostConfig.CapDrop[0], ShouldEqual, "NET_ADMIN")
		})

		Convey("grabs and formats volume binds properly", func() {
			So(len(opts.HostConfig.Binds), ShouldEqual, 2)
			So(opts.HostConfig.Binds[0], ShouldEqual, "/tmp/elsewhere:/tmp/somewhere:ro")
			So(opts.HostConfig.Binds[1], ShouldEqual, "/tmp/bar:/tmp/foo")
		})

		Convey("handles port bindings", func() {
			So(len(opts.HostConfig.PortBindings), ShouldEqual, 3)
			So(opts.HostConfig.PortBindings["443/tcp"][0].HostPort, ShouldEqual, "10270")
			So(opts.HostConfig.PortBindings["9090/tcp"][0].HostPort, ShouldEqual, "10271")
			So(opts.HostConfig.PortBindings["9090/udp"][0].HostPort, ShouldEqual, "10271")
		})

		Convey("uses the right network mode when it's set", func() {
			So(opts.HostConfig.NetworkMode, ShouldEqual, "host")
		})

		Convey("defaults to correct network mode", func() {
			none := mesos.ContainerInfo_DockerInfo_NONE
			taskInfo.Container.Docker.Network = &none
			opts := ConfigForTask(taskInfo, false, false)
			So(opts.HostConfig.NetworkMode, ShouldEqual, "none")
		})
	})
}
<commit_msg>Fix tests<commit_after>package container

import (
	"bytes"
	"io/ioutil"
	"testing"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/fsouza/go-dockerclient"
	mesos "github.com/mesos/mesos-go/mesosproto"
	. "github.com/smartystreets/goconvey/convey"
)

const (
	prelude = "LO, praise of the prowess of people-kings of spear-armed Danes, in days long sped"
	ending  = "Thus made their mourning the men of Geatland, for their hero's passing his hearth-companions"
)

func init() {
	log.SetOutput(ioutil.Discard)
}

func Test_PullImage(t *testing.T) {
	Convey("PullImage()", t, func() {

		image := "foo/foo:foo"
		taskInfo := &mesos.TaskInfo{
			Container: &mesos.ContainerInfo{
				Docker: &mesos.ContainerInfo_DockerInfo{
					Image: &image,
				},
			},
		}

		dockerClient := &MockDockerClient{}

		Convey("passes the right params", func() {
			err := PullImage(dockerClient, taskInfo, &docker.AuthConfiguration{})

			So(dockerClient.validOptions, ShouldBeTrue)
			So(err, ShouldBeNil)
		})

		Convey("bubbles up errors", func() {
			dockerClient.PullImageShouldError = true
			err := PullImage(dockerClient, taskInfo, &docker.AuthConfiguration{})

			So(err, ShouldNotBeNil)
			So(err.Error(), ShouldContainSubstring, "Something went wrong")
		})
	})
}

func Test_CheckImage(t *testing.T) {
	Convey("CheckImage()", t, func() {
		image := "gonitro/sidecar:1.0.0"
		images := []docker.APIImages{
			{
				RepoTags: []string{image, "sidecar", "sidecar:latest"},
			},
		}

		taskInfo := &mesos.TaskInfo{
			Container: &mesos.ContainerInfo{
				Docker: &mesos.ContainerInfo_DockerInfo{
					Image: &image,
				},
			},
		}

		dockerClient := &MockDockerClient{Images: images}

		Convey("handles errors", func() {
			dockerClient.ListImagesShouldError = true
			So(CheckImage(dockerClient, taskInfo), ShouldBeFalse)
		})

		Convey("matches the image", func() {
			So(CheckImage(dockerClient, taskInfo), ShouldBeTrue)
		})

		Convey("handles missing images", func() {
			wrong := "wrong"
			taskInfo.Container.Docker.Image = &wrong
			So(CheckImage(dockerClient, taskInfo), ShouldBeFalse)
		})

	})
}

func Test_StopContainer(t *testing.T) {
	Convey("When stopping containers", t, func() {
		dockerClient := &MockDockerClient{
			StopContainerShouldError: true,
			StopContainerMaxFails:    1,
			Container: &docker.Container{
				State: docker.State{
					Status: "running",
				},
			},
		}

		Convey("retries stopping the container", func() {
			err := StopContainer(dockerClient, "someid", 0)
			So(err, ShouldNotBeNil)
			So(err.Error(), ShouldNotContainSubstring, "Unable to kill")
			So(dockerClient.stopContainerFails, ShouldEqual, 2)
		})

		Convey("returns an error when it really won't stop", func() {
			dockerClient.StopContainerMaxFails = 2
			err := StopContainer(dockerClient, "someid", 0)
			So(err, ShouldNotBeNil)
			So(err.Error(), ShouldContainSubstring, "Unable to kill")
			So(dockerClient.stopContainerFails, ShouldEqual, 2)

		})
	})
}

func Test_GetLogs(t *testing.T) {
	Convey("Fetches the logs from a task", t, func() {
		containerId := "mesos-nginx-2392676-1479746266455-1-dev_singularity_sick_sing-DEFAULT"
		dockerClient := &MockDockerClient{
			LogOutputString: prelude,
			LogErrorString:  ending,
		}

		stdout := bytes.NewBuffer(make([]byte, 0, 256))
		stderr := bytes.NewBuffer(make([]byte, 0, 256))

		GetLogs(dockerClient, containerId, time.Now().UTC().Unix(), stdout, stderr)

		time.Sleep(1 * time.Millisecond) // Nasty, but lets buffer flush

		So(string(stdout.Bytes()), ShouldResemble, prelude)
		So(string(stderr.Bytes()), ShouldResemble, ending)

		So(dockerClient.logOpts.Stdout, ShouldBeTrue)
		So(dockerClient.logOpts.OutputStream, ShouldNotBeNil)
		So(dockerClient.logOpts.ErrorStream, ShouldNotBeNil)
	})
}

func Test_ConfigGeneration(t *testing.T) {
	Convey("Generating the Docker config from a Mesos Task", t, func() {

		// The whole structure is full of pointers, so we have to define
		// a bunch of things so we can take their address.
		taskId := "nginx-2392676-1479746266455-1-dev_singularity_sick_sing-DEFAULT"
		image := "foo/foo:1.0.0"
		cpus := "cpus"
		cpusValue := float64(0.5)
		memory := "mem"
		memoryValue := float64(128)

		env := "env"
		envValue := "SOMETHING=123=123"
		label := "label"
		labelValue := "ANYTHING=123=123"
		capAdd := "cap-add"
		capAddValue := "NET_ADMIN"
		capDrop := "cap-drop"
		capDropValue := "NET_ADMIN"

		svcName := "dev-test-app"
		svcNameLabel := "ServiceName=" + svcName
		envName := "dev"
		envNameLabel := "EnvironmentName=" + envName

		host := mesos.ContainerInfo_DockerInfo_HOST

		port := uint32(8080)
		port2 := uint32(443)
		port2_hp := uint32(10270)
		port3 := uint32(9090)
		port3_hp := uint32(10271)
		port3Proto := "tcp,udp"

		v1_cp := "/tmp/somewhere"
		v1_hp := "/tmp/elsewhere"
		v2_cp := "/tmp/foo"
		v2_hp := "/tmp/bar"
		mode := mesos.Volume_RO

		hostname := "beowulf.example.com"
		hostKey := "TASK_HOST"

		taskInfo := &mesos.TaskInfo{
			TaskId: &mesos.TaskID{Value: &taskId},
			Container: &mesos.ContainerInfo{
				Docker: &mesos.ContainerInfo_DockerInfo{
					Image:   &image,
					Network: &host,
					Parameters: []*mesos.Parameter{
						{
							Key:   &env,
							Value: &envValue,
						},
						{
							Key:   &label,
							Value: &labelValue,
						},
						{
							Key:   &label,
							Value: &svcNameLabel,
						},
						{
							Key:   &label,
							Value: &envNameLabel,
						},
						{
							Key:   &capAdd,
							Value: &capAddValue,
						},
						{
							Key:   &capDrop,
							Value: &capDropValue,
						},
					},
					PortMappings: []*mesos.ContainerInfo_DockerInfo_PortMapping{
						{
							ContainerPort: &port,
						},
						{
							ContainerPort: &port2,
							HostPort:      &port2_hp,
						},
						{
							ContainerPort: &port3,
							HostPort:      &port3_hp,
							Protocol:      &port3Proto,
						},
					},
				},
				Volumes: []*mesos.Volume{
					{
						Mode:          &mode,
						ContainerPath: &v1_cp,
						HostPath:      &v1_hp,
					},
					{
						ContainerPath: &v2_cp,
						HostPath:      &v2_hp,
					},
				},
			},
			Resources: []*mesos.Resource{
				{
					Name:   &cpus,
					Scalar: &mesos.Value_Scalar{Value: &cpusValue},
				},
				{
					Name:   &memory,
					Scalar: &mesos.Value_Scalar{Value: &memoryValue},
				},
			},
			Executor: &mesos.ExecutorInfo{
				Command: &mesos.CommandInfo{
					Environment: &mesos.Environment{
						Variables: []*mesos.Environment_Variable{
							{
								Name:  &hostKey,
								Value: &hostname,
							},
						},
					},
				},
			},
		}

		opts := ConfigForTask(taskInfo, false, false, []string{})
		optsForced := ConfigForTask(taskInfo, true, true, []string{})

		Convey("gets the name from the task ID", func() {
			So(opts.Name, ShouldEqual, "mesos-"+taskId)
		})

		Convey("properly calculates the CPU limit", func() {
			So(optsForced.HostConfig.CPUPeriod, ShouldEqual, float64(50000))
			So(optsForced.HostConfig.CPUQuota, ShouldEqual, float64(25000))

			So(opts.HostConfig.CPUPeriod, ShouldEqual, float64(0))
			So(opts.HostConfig.CPUQuota, ShouldEqual, float64(0))
		})

		Convey("properly calculates the memory limit", func() {
			So(optsForced.HostConfig.Memory, ShouldEqual, float64(128*1024*1024))
			So(opts.HostConfig.Memory, ShouldEqual, float64(0))
		})

		Convey("populates the environment", func() {
			So(len(opts.Config.Env), ShouldBeGreaterThan, 1)
			So(opts.Config.Env[0], ShouldEqual, "SOMETHING=123=123")
		})

		Convey("maps ports into the environment", func() {
			// We index backward to find the vars we just set
			So(opts.Config.Env[len(opts.Config.Env)-6], ShouldEqual, "MESOS_PORT_443=10270")
		})

		Convey("maps the hostname into the environment", func() {
			So(opts.Config.Env[len(opts.Config.Env)-4], ShouldEqual, "MESOS_HOSTNAME="+hostname)
		})

		Convey("maps the ServiceName into the environment", func() {
			So(opts.Config.Env[len(opts.Config.Env)-3], ShouldEqual, "SERVICE_NAME="+svcName)
		})

		Convey("maps the EnvironmentName into the environment", func() {
			So(opts.Config.Env[len(opts.Config.Env)-2], ShouldEqual, "ENVIRONMENT_NAME="+envName)
		})

		Convey("maps the version into the environment", func() {
			So(opts.Config.Env[len(opts.Config.Env)-1], ShouldEqual, "SERVICE_VERSION=1.0.0")
		})

		Convey("fills in the exposed ports", func() {
			So(len(opts.Config.ExposedPorts), ShouldEqual, 4)
			So(opts.Config.ExposedPorts, ShouldContainKey, docker.Port("8080/tcp"))
			So(opts.Config.ExposedPorts, ShouldContainKey, docker.Port("9090/tcp"))
			So(opts.Config.ExposedPorts, ShouldContainKey, docker.Port("9090/udp"))
		})

		Convey("has the right image name", func() {
			So(opts.Config.Image, ShouldEqual, image)
		})

		Convey("gets the labels", func() {
			So(len(opts.Config.Labels), ShouldBeGreaterThanOrEqualTo, 1)
			So(opts.Config.Labels["ANYTHING"], ShouldEqual, "123=123")
		})

		Convey("gets the cap-adds", func() {
			So(len(opts.HostConfig.CapAdd), ShouldEqual, 1)
			So(opts.HostConfig.CapAdd[0], ShouldEqual, "NET_ADMIN")
		})

		Convey("gets the cap-drops", func() {
			So(len(opts.HostConfig.CapDrop), ShouldEqual, 1)
			So(opts.HostConfig.CapDrop[0], ShouldEqual, "NET_ADMIN")
		})

		Convey("grabs and formats volume binds properly", func() {
			So(len(opts.HostConfig.Binds), ShouldEqual, 2)
			So(opts.HostConfig.Binds[0], ShouldEqual, "/tmp/elsewhere:/tmp/somewhere:ro")
			So(opts.HostConfig.Binds[1], ShouldEqual, "/tmp/bar:/tmp/foo")
		})

		Convey("handles port bindings", func() {
			So(len(opts.HostConfig.PortBindings), ShouldEqual, 3)
			So(opts.HostConfig.PortBindings["443/tcp"][0].HostPort, ShouldEqual, "10270")
			So(opts.HostConfig.PortBindings["9090/tcp"][0].HostPort, ShouldEqual, "10271")
			So(opts.HostConfig.PortBindings["9090/udp"][0].HostPort, ShouldEqual, "10271")
		})

		Convey("uses the right network mode when it's set", func() {
			So(opts.HostConfig.NetworkMode, ShouldEqual, "host")
		})

		Convey("defaults to correct network mode", func() {
			none := mesos.ContainerInfo_DockerInfo_NONE
			taskInfo.Container.Docker.Network = &none
			opts := ConfigForTask(taskInfo, false, false, []string{})
			So(opts.HostConfig.NetworkMode, ShouldEqual, "none")
		})
	})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"
	"time"

	"github.com/docker/docker/integration-cli/checker"
	"github.com/docker/docker/integration-cli/cli"
	"github.com/go-check/check"
	"gotest.tools/icmd"
)

// Regression test for https://github.com/docker/docker/issues/7843
func (s *DockerSuite) TestStartAttachReturnsOnError(c *check.C) {
	// Windows does not support link
	testRequires(c, DaemonIsLinux)
	dockerCmd(c, "run", "--name", "test", "busybox")

	// Expect this to fail because the above container is stopped, this is what we want
	out, _, err := dockerCmdWithError("run", "--name", "test2", "--link", "test:test", "busybox")
	// err shouldn't be nil because container test2 try to link to stopped container
	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))

	ch := make(chan error)
	go func() {
		// Attempt to start attached to the container that won't start
		// This should return an error immediately since the container can't be started
		if out, _, err := dockerCmdWithError("start", "-a", "test2"); err == nil {
			ch <- fmt.Errorf("Expected error but got none:\n%s", out)
		}
		close(ch)
	}()

	select {
	case err := <-ch:
		c.Assert(err, check.IsNil)
	case <-time.After(5 * time.Second):
		c.Fatalf("Attach did not exit properly")
	}
}

// gh#8555: Exit code should be passed through when using start -a
func (s *DockerSuite) TestStartAttachCorrectExitCode(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out := cli.DockerCmd(c, "run", "-d", "busybox", "sh", "-c", "sleep 2; exit 1").Stdout()
	out = strings.TrimSpace(out)

	// make sure the container has exited before trying the "start -a"
	cli.DockerCmd(c, "wait", out)

	cli.Docker(cli.Args("start", "-a", out)).Assert(c, icmd.Expected{
		ExitCode: 1,
	})
}

func (s *DockerSuite) TestStartAttachSilent(c *check.C) {
	name := "teststartattachcorrectexitcode"
	dockerCmd(c, "run", "--name", name, "busybox", "echo", "test")

	// make sure the container has exited before trying the "start -a"
	dockerCmd(c, "wait", name)

	startOut, _ := dockerCmd(c, "start", "-a", name)
	// start -a produced unexpected output
	c.Assert(startOut, checker.Equals, "test\n")
}

func (s *DockerSuite) TestStartRecordError(c *check.C) {
	// TODO Windows CI: Requires further porting work. Should be possible.
	testRequires(c, DaemonIsLinux)
	// when container runs successfully, we should not have state.Error
	dockerCmd(c, "run", "-d", "-p", "9999:9999", "--name", "test", "busybox", "top")
	stateErr := inspectField(c, "test", "State.Error")
	// Expected to not have state error
	c.Assert(stateErr, checker.Equals, "")

	// Expect this to fail and records error because of ports conflict
	out, _, err := dockerCmdWithError("run", "-d", "--name", "test2", "-p", "9999:9999", "busybox", "top")
	// err shouldn't be nil because docker run will fail
	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))

	stateErr = inspectField(c, "test2", "State.Error")
	c.Assert(stateErr, checker.Contains, "port is already allocated")

	// Expect the conflict to be resolved when we stop the initial container
	dockerCmd(c, "stop", "test")
	dockerCmd(c, "start", "test2")
	stateErr = inspectField(c, "test2", "State.Error")
	// Expected to not have state error but got one
	c.Assert(stateErr, checker.Equals, "")
}

func (s *DockerSuite) TestStartPausedContainer(c *check.C) {
	// Windows does not support pausing containers
	testRequires(c, IsPausable)

	runSleepingContainer(c, "-d", "--name", "testing")

	dockerCmd(c, "pause", "testing")

	out, _, err := dockerCmdWithError("start", "testing")
	// an error should have been shown that you cannot start paused container
	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
	// an error should have been shown that you cannot start paused container
	c.Assert(strings.ToLower(out), checker.Contains, "cannot start a paused container, try unpause instead")
}

func (s *DockerSuite) TestStartMultipleContainers(c *check.C) {
	// Windows does not support --link
	testRequires(c, DaemonIsLinux)
	// run a container named 'parent' and create two container link to `parent`
	dockerCmd(c, "run", "-d", "--name", "parent", "busybox", "top")

	for _, container := range []string{"child_first", "child_second"} {
		dockerCmd(c, "create", "--name", container, "--link", "parent:parent", "busybox", "top")
	}

	// stop 'parent' container
	dockerCmd(c, "stop", "parent")

	out := inspectField(c, "parent", "State.Running")
	// Container should be stopped
	c.Assert(out, checker.Equals, "false")

	// start all the three containers, container `child_first` start first which should be failed
	// container 'parent' start second and then start container 'child_second'
	expOut := "Cannot link to a non running container"
	expErr := "failed to start containers: [child_first]"
	out, _, err := dockerCmdWithError("start", "child_first", "parent", "child_second")
	// err shouldn't be nil because start will fail
	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
	// output does not correspond to what was expected
	if !(strings.Contains(out, expOut) || strings.Contains(err.Error(), expErr)) {
		c.Fatalf("Expected out: %v with err: %v  but got out: %v with err: %v", expOut, expErr, out, err)
	}

	for container, expected := range map[string]string{"parent": "true", "child_first": "false", "child_second": "true"} {
		out := inspectField(c, container, "State.Running")
		// Container running state wrong
		c.Assert(out, checker.Equals, expected)
	}
}

func (s *DockerSuite) TestStartAttachMultipleContainers(c *check.C) {
	// run  multiple containers to test
	for _, container := range []string{"test1", "test2", "test3"} {
		runSleepingContainer(c, "--name", container)
	}

	// stop all the containers
	for _, container := range []string{"test1", "test2", "test3"} {
		dockerCmd(c, "stop", container)
	}

	// test start and attach multiple containers at once, expected error
	for _, option := range []string{"-a", "-i", "-ai"} {
		out, _, err := dockerCmdWithError("start", option, "test1", "test2", "test3")
		// err shouldn't be nil because start will fail
		c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
		// output does not correspond to what was expected
		c.Assert(out, checker.Contains, "you cannot start and attach multiple containers at once")
	}

	// confirm the state of all the containers be stopped
	for container, expected := range map[string]string{"test1": "false", "test2": "false", "test3": "false"} {
		out := inspectField(c, container, "State.Running")
		// Container running state wrong
		c.Assert(out, checker.Equals, expected)
	}
}

// Test case for #23716
func (s *DockerSuite) TestStartAttachWithRename(c *check.C) {
	testRequires(c, DaemonIsLinux)
	cli.DockerCmd(c, "create", "-t", "--name", "before", "busybox")
	go func() {
		cli.WaitRun(c, "before")
		cli.DockerCmd(c, "rename", "before", "after")
		cli.DockerCmd(c, "stop", "--time=2", "after")
	}()
	// FIXME(vdemeester) the intent is not clear and potentially racey
	result := cli.Docker(cli.Args("start", "-a", "before")).Assert(c, icmd.Expected{
		ExitCode: 137,
	})
	c.Assert(result.Stderr(), checker.Not(checker.Contains), "No such container")
}

func (s *DockerSuite) TestStartReturnCorrectExitCode(c *check.C) {
	dockerCmd(c, "create", "--restart=on-failure:2", "--name", "withRestart", "busybox", "sh", "-c", "exit 11")
	dockerCmd(c, "create", "--rm", "--name", "withRm", "busybox", "sh", "-c", "exit 12")

	_, exitCode, err := dockerCmdWithError("start", "-a", "withRestart")
	c.Assert(err, checker.NotNil)
	c.Assert(exitCode, checker.Equals, 11)
	_, exitCode, err = dockerCmdWithError("start", "-a", "withRm")
	c.Assert(err, checker.NotNil)
	c.Assert(exitCode, checker.Equals, 12)
}
<commit_msg>TestStartReturnCorrectExitCode: show error<commit_after>package main

import (
	"fmt"
	"strings"
	"time"

	"github.com/docker/docker/integration-cli/checker"
	"github.com/docker/docker/integration-cli/cli"
	"github.com/go-check/check"
	"gotest.tools/icmd"
)

// Regression test for https://github.com/docker/docker/issues/7843
func (s *DockerSuite) TestStartAttachReturnsOnError(c *check.C) {
	// Windows does not support link
	testRequires(c, DaemonIsLinux)
	dockerCmd(c, "run", "--name", "test", "busybox")

	// Expect this to fail because the above container is stopped, this is what we want
	out, _, err := dockerCmdWithError("run", "--name", "test2", "--link", "test:test", "busybox")
	// err shouldn't be nil because container test2 try to link to stopped container
	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))

	ch := make(chan error)
	go func() {
		// Attempt to start attached to the container that won't start
		// This should return an error immediately since the container can't be started
		if out, _, err := dockerCmdWithError("start", "-a", "test2"); err == nil {
			ch <- fmt.Errorf("Expected error but got none:\n%s", out)
		}
		close(ch)
	}()

	select {
	case err := <-ch:
		c.Assert(err, check.IsNil)
	case <-time.After(5 * time.Second):
		c.Fatalf("Attach did not exit properly")
	}
}

// gh#8555: Exit code should be passed through when using start -a
func (s *DockerSuite) TestStartAttachCorrectExitCode(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out := cli.DockerCmd(c, "run", "-d", "busybox", "sh", "-c", "sleep 2; exit 1").Stdout()
	out = strings.TrimSpace(out)

	// make sure the container has exited before trying the "start -a"
	cli.DockerCmd(c, "wait", out)

	cli.Docker(cli.Args("start", "-a", out)).Assert(c, icmd.Expected{
		ExitCode: 1,
	})
}

func (s *DockerSuite) TestStartAttachSilent(c *check.C) {
	name := "teststartattachcorrectexitcode"
	dockerCmd(c, "run", "--name", name, "busybox", "echo", "test")

	// make sure the container has exited before trying the "start -a"
	dockerCmd(c, "wait", name)

	startOut, _ := dockerCmd(c, "start", "-a", name)
	// start -a produced unexpected output
	c.Assert(startOut, checker.Equals, "test\n")
}

func (s *DockerSuite) TestStartRecordError(c *check.C) {
	// TODO Windows CI: Requires further porting work. Should be possible.
	testRequires(c, DaemonIsLinux)
	// when container runs successfully, we should not have state.Error
	dockerCmd(c, "run", "-d", "-p", "9999:9999", "--name", "test", "busybox", "top")
	stateErr := inspectField(c, "test", "State.Error")
	// Expected to not have state error
	c.Assert(stateErr, checker.Equals, "")

	// Expect this to fail and records error because of ports conflict
	out, _, err := dockerCmdWithError("run", "-d", "--name", "test2", "-p", "9999:9999", "busybox", "top")
	// err shouldn't be nil because docker run will fail
	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))

	stateErr = inspectField(c, "test2", "State.Error")
	c.Assert(stateErr, checker.Contains, "port is already allocated")

	// Expect the conflict to be resolved when we stop the initial container
	dockerCmd(c, "stop", "test")
	dockerCmd(c, "start", "test2")
	stateErr = inspectField(c, "test2", "State.Error")
	// Expected to not have state error but got one
	c.Assert(stateErr, checker.Equals, "")
}

func (s *DockerSuite) TestStartPausedContainer(c *check.C) {
	// Windows does not support pausing containers
	testRequires(c, IsPausable)

	runSleepingContainer(c, "-d", "--name", "testing")

	dockerCmd(c, "pause", "testing")

	out, _, err := dockerCmdWithError("start", "testing")
	// an error should have been shown that you cannot start paused container
	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
	// an error should have been shown that you cannot start paused container
	c.Assert(strings.ToLower(out), checker.Contains, "cannot start a paused container, try unpause instead")
}

func (s *DockerSuite) TestStartMultipleContainers(c *check.C) {
	// Windows does not support --link
	testRequires(c, DaemonIsLinux)
	// run a container named 'parent' and create two container link to `parent`
	dockerCmd(c, "run", "-d", "--name", "parent", "busybox", "top")

	for _, container := range []string{"child_first", "child_second"} {
		dockerCmd(c, "create", "--name", container, "--link", "parent:parent", "busybox", "top")
	}

	// stop 'parent' container
	dockerCmd(c, "stop", "parent")

	out := inspectField(c, "parent", "State.Running")
	// Container should be stopped
	c.Assert(out, checker.Equals, "false")

	// start all the three containers, container `child_first` start first which should be failed
	// container 'parent' start second and then start container 'child_second'
	expOut := "Cannot link to a non running container"
	expErr := "failed to start containers: [child_first]"
	out, _, err := dockerCmdWithError("start", "child_first", "parent", "child_second")
	// err shouldn't be nil because start will fail
	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
	// output does not correspond to what was expected
	if !(strings.Contains(out, expOut) || strings.Contains(err.Error(), expErr)) {
		c.Fatalf("Expected out: %v with err: %v  but got out: %v with err: %v", expOut, expErr, out, err)
	}

	for container, expected := range map[string]string{"parent": "true", "child_first": "false", "child_second": "true"} {
		out := inspectField(c, container, "State.Running")
		// Container running state wrong
		c.Assert(out, checker.Equals, expected)
	}
}

func (s *DockerSuite) TestStartAttachMultipleContainers(c *check.C) {
	// run  multiple containers to test
	for _, container := range []string{"test1", "test2", "test3"} {
		runSleepingContainer(c, "--name", container)
	}

	// stop all the containers
	for _, container := range []string{"test1", "test2", "test3"} {
		dockerCmd(c, "stop", container)
	}

	// test start and attach multiple containers at once, expected error
	for _, option := range []string{"-a", "-i", "-ai"} {
		out, _, err := dockerCmdWithError("start", option, "test1", "test2", "test3")
		// err shouldn't be nil because start will fail
		c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
		// output does not correspond to what was expected
		c.Assert(out, checker.Contains, "you cannot start and attach multiple containers at once")
	}

	// confirm the state of all the containers be stopped
	for container, expected := range map[string]string{"test1": "false", "test2": "false", "test3": "false"} {
		out := inspectField(c, container, "State.Running")
		// Container running state wrong
		c.Assert(out, checker.Equals, expected)
	}
}

// Test case for #23716
func (s *DockerSuite) TestStartAttachWithRename(c *check.C) {
	testRequires(c, DaemonIsLinux)
	cli.DockerCmd(c, "create", "-t", "--name", "before", "busybox")
	go func() {
		cli.WaitRun(c, "before")
		cli.DockerCmd(c, "rename", "before", "after")
		cli.DockerCmd(c, "stop", "--time=2", "after")
	}()
	// FIXME(vdemeester) the intent is not clear and potentially racey
	result := cli.Docker(cli.Args("start", "-a", "before")).Assert(c, icmd.Expected{
		ExitCode: 137,
	})
	c.Assert(result.Stderr(), checker.Not(checker.Contains), "No such container")
}

func (s *DockerSuite) TestStartReturnCorrectExitCode(c *check.C) {
	dockerCmd(c, "create", "--restart=on-failure:2", "--name", "withRestart", "busybox", "sh", "-c", "exit 11")
	dockerCmd(c, "create", "--rm", "--name", "withRm", "busybox", "sh", "-c", "exit 12")

	out, exitCode, err := dockerCmdWithError("start", "-a", "withRestart")
	c.Assert(err, checker.NotNil)
	c.Assert(exitCode, checker.Equals, 11, check.Commentf("out: %s", out))

	out, exitCode, err = dockerCmdWithError("start", "-a", "withRm")
	c.Assert(err, checker.NotNil)
	c.Assert(exitCode, checker.Equals, 12, check.Commentf("out: %s", out))
}
<|endoftext|>


<commit_before>package jsonapivalidator

import "fmt"

func validateIncluded(included interface{}, result *Result) {
	// included MUST be an array
	includes, ok := included.([]interface{})
	if !ok {
		result.AddError(ErrInvalidIncludedType)
	}

	for _, i := range includes {
		fmt.Println(i)
	}
}
<commit_msg>Initial included support.<commit_after>package jsonapivalidator

func validateIncluded(included interface{}, result *Result) {
	// included MUST be an array
	includes, ok := included.([]interface{})
	if !ok {
		result.AddError(ErrInvalidIncludedType)
	}

	for _, i := range includes {
		r := i.(map[string]interface{})
		validateResourceObject(r, result)
	}
}
<|endoftext|>


<commit_before>package isolated

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"

	"code.cloudfoundry.org/cli/integration/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("app command", func() {
	Describe("help", func() {
		Context("when --help flag is set", func() {
			It("Displays command usage to output", func() {
				session := helpers.CF("app", "--help")
				Eventually(session).Should(Say("NAME:"))
				Eventually(session).Should(Say("app - Display health and status for an app"))
				Eventually(session).Should(Say("USAGE:"))
				Eventually(session).Should(Say("cf app APP_NAME"))
				Eventually(session).Should(Say("OPTIONS:"))
				Eventually(session).Should(Say("--guid      Retrieve and display the given app's guid.  All other health and status output for the app is suppressed."))
				Eventually(session).Should(Say("SEE ALSO:"))
				Eventually(session).Should(Say("apps, events, logs, map-route, push, unmap-route"))
				Eventually(session).Should(Exit(0))
			})
		})
	})

	Context("when the environment is not setup correctly", func() {
		It("fails with the appropriate errors", func() {
			helpers.CheckEnvironmentTargetedCorrectly(true, true, ReadOnlyOrg, "app", "some-app")
		})
	})

	Context("when the environment is set up correctly", func() {
		var (
			orgName   string
			spaceName string
		)

		BeforeEach(func() {
			orgName = helpers.NewOrgName()
			spaceName = helpers.NewSpaceName()

			helpers.SetupCF(orgName, spaceName)
		})

		AfterEach(func() {
			helpers.QuickDeleteOrg(orgName)
		})

		Context("when the app name is not provided", func() {
			It("tells the user that the app name is required, prints help text, and exits 1", func() {
				session := helpers.CF("app")

				Eventually(session.Err).Should(Say("Incorrect Usage: the required argument `APP_NAME` was not provided"))
				Eventually(session).Should(Say("NAME:"))
				Eventually(session).Should(Exit(1))
			})
		})

		Context("when the app does not exist", func() {
			Context("when no flags are given", func() {
				It("tells the user that the app is not found and exits 1", func() {
					appName := helpers.PrefixedRandomName("app")
					session := helpers.CF("app", appName)

					Eventually(session).Should(Say("FAILED"))
					Eventually(session.Err).Should(Say("App %s not found", appName))
					Eventually(session).Should(Exit(1))
				})
			})

			Context("when the --guid flag is given", func() {
				It("tells the user that the app is not found and exits 1", func() {
					appName := helpers.PrefixedRandomName("app")
					session := helpers.CF("app", "--guid", appName)

					Eventually(session).Should(Say("FAILED"))
					Eventually(session.Err).Should(Say("App %s not found", appName))
					Eventually(session).Should(Exit(1))
				})
			})
		})

		Context("when the app does exist", func() {
			Context("when the app is a buildpack app", func() {
				var (
					domainName string
					tcpDomain  helpers.Domain
					appName    string
				)

				BeforeEach(func() {
					Eventually(helpers.CF("create-isolation-segment", RealIsolationSegment)).Should(Exit(0))
					Eventually(helpers.CF("enable-org-isolation", orgName, RealIsolationSegment)).Should(Exit(0))
					Eventually(helpers.CF("set-space-isolation-segment", spaceName, RealIsolationSegment)).Should(Exit(0))

					appName = helpers.PrefixedRandomName("app")
					domainName = helpers.DefaultSharedDomain()
					tcpDomain = helpers.NewDomain(orgName, helpers.DomainName("tcp"))
					tcpDomain.CreateWithRouterGroup(helpers.FindOrCreateTCPRouterGroup(GinkgoParallelNode()))
					helpers.WithHelloWorldApp(func(appDir string) {
						manifestContents := []byte(fmt.Sprintf(`
---
applications:
- name: %s
  memory: 128M
  instances: 2
  disk_quota: 128M
  routes:
  - route: %s.%s
  - route: %s:1024
`, appName, appName, domainName, tcpDomain.Name))
						manifestPath := filepath.Join(appDir, "manifest.yml")
						err := ioutil.WriteFile(manifestPath, manifestContents, 0666)
						Expect(err).ToNot(HaveOccurred())

						// Create manifest
						Eventually(helpers.CF("push", appName, "-p", appDir, "-f", manifestPath, "-b", "staticfile_buildpack")).Should(Exit(0))
					})
				})

				Context("when the app is started and has 2 instances", func() {
					It("displays the app information with instances table", func() {
						session := helpers.CF("app", appName)
						Eventually(session).Should(Say("name:\\s+%s", appName))
						Eventually(session).Should(Say("requested state:\\s+started"))
						Eventually(session).Should(Say("instances:\\s+2/2"))
						Eventually(session).Should(Say("isolation segment:\\s+%s", RealIsolationSegment))
						Eventually(session).Should(Say("usage:\\s+128M x 2 instances"))
						Eventually(session).Should(Say("routes:\\s+[\\w\\d-]+\\.%s, %s:1024", domainName, tcpDomain.Name))
						Eventually(session).Should(Say("last uploaded:\\s+\\w{3} [0-3]\\d \\w{3} [0-2]\\d:[0-5]\\d:[0-5]\\d \\w+ \\d{4}"))
						Eventually(session).Should(Say("stack:\\s+cflinuxfs2"))
						Eventually(session).Should(Say("buildpack:\\s+staticfile_buildpack"))
						Eventually(session).Should(Say(""))
						Eventually(session).Should(Say("state\\s+since\\s+cpu\\s+memory\\s+disk\\s+details"))
						Eventually(session).Should(Say("#0\\s+running\\s+\\d{4}-[01]\\d-[0-3]\\dT[0-2][0-9]:[0-5]\\d:[0-5]\\dZ\\s+\\d+\\.\\d+%.*of 128M.*of 128M"))
						Eventually(session).Should(Say("#1\\s+running\\s+\\d{4}-[01]\\d-[0-3]\\dT[0-2][0-9]:[0-5]\\d:[0-5]\\dZ\\s+\\d+\\.\\d+%.*of 128M.*of 128M"))
						Eventually(session).Should(Exit(0))
					})
				})

				Context("when the app is stopped", func() {
					BeforeEach(func() {
						Eventually(helpers.CF("stop", appName)).Should(Exit(0))
					})

					It("displays the app information", func() {
						session := helpers.CF("app", appName)
						Eventually(session).Should(Say("name:\\s+%s", appName))
						Eventually(session).Should(Say("requested state:\\s+stopped"))
						Eventually(session).Should(Say("instances:\\s+0/2"))
						Eventually(session).Should(Say("usage:\\s+128M x 2 instances"))
						Eventually(session).Should(Say("routes:\\s+[\\w\\d-]+.%s, %s:1024", domainName, tcpDomain.Name))
						Eventually(session).Should(Say("last uploaded:"))
						Eventually(session).Should(Say("stack:\\s+cflinuxfs2"))
						Eventually(session).Should(Say("buildpack:\\s+staticfile_buildpack"))

						Eventually(session).Should(Say("There are no running instances of this app."))
						Eventually(session).Should(Exit(0))
					})
				})

				Context("when the app has 0 instances", func() {
					BeforeEach(func() {
						Eventually(helpers.CF("scale", appName, "-i", "0")).Should(Exit(0))
					})

					It("displays the app information", func() {
						session := helpers.CF("app", appName)
						Eventually(session).Should(Say("name:\\s+%s", appName))
						Eventually(session).Should(Say("requested state:\\s+started"))
						Eventually(session).Should(Say("instances:\\s+0/0"))
						Eventually(session).Should(Say("usage:\\s+128M x 0 instances"))
						Eventually(session).Should(Say("routes:\\s+[\\w\\d-]+\\.%s, %s:1024", domainName, tcpDomain.Name))
						Eventually(session).Should(Say("last uploaded:"))
						Eventually(session).Should(Say("stack:\\s+cflinuxfs2"))
						Eventually(session).Should(Say("buildpack:\\s+staticfile_buildpack"))

						Eventually(session).Should(Say("There are no running instances of this app."))
						Eventually(session).Should(Exit(0))
					})
				})

				Context("when the --guid flag is given", func() {
					var appGUID string

					BeforeEach(func() {
						session := helpers.CF("curl", fmt.Sprintf("/v2/apps?q=name:%s", appName))
						Eventually(session).Should(Exit(0))
						rawJSON := strings.TrimSpace(string(session.Out.Contents()))
						var AppInfo struct {
							Resources []struct {
								Metadata struct {
									GUID string `json:"guid"`
								} `json:"metadata"`
							} `json:"resources"`
						}

						err := json.Unmarshal([]byte(rawJSON), &AppInfo)
						Expect(err).NotTo(HaveOccurred())

						appGUID = AppInfo.Resources[0].Metadata.GUID
					})

					It("displays the app guid", func() {
						session := helpers.CF("app", "--guid", appName)
						Eventually(session).Should(Say(appGUID))
						Eventually(session).Should(Exit(0))
					})
				})
			})

			Context("when the app is a Docker app", func() {
				var (
					tcpDomain helpers.Domain
					appName   string
				)

				BeforeEach(func() {
					appName = helpers.PrefixedRandomName("app")
					tcpDomain = helpers.NewDomain(orgName, helpers.DomainName("tcp"))
					tcpDomain.CreateWithRouterGroup(helpers.FindOrCreateTCPRouterGroup(GinkgoParallelNode()))
					Eventually(helpers.CF("push", appName, "-o", DockerImage)).Should(Exit())
				})

				It("displays the docker image and does not display buildpack", func() {
					session := helpers.CF("app", appName)
					Eventually(session).Should(Say("name:\\s+%s", appName))
					Consistently(session).ShouldNot(Say("buildpack:"))
					Eventually(session).Should(Say("docker image:\\s+%s", DockerImage))
					Consistently(session).ShouldNot(Say("buildpack:"))
					Eventually(session).Should(Exit(0))
				})
			})
		})
	})
})
<commit_msg>improve regex for app command integration test<commit_after>package isolated

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"

	"code.cloudfoundry.org/cli/integration/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("app command", func() {
	Describe("help", func() {
		Context("when --help flag is set", func() {
			It("Displays command usage to output", func() {
				session := helpers.CF("app", "--help")
				Eventually(session).Should(Say("NAME:"))
				Eventually(session).Should(Say("app - Display health and status for an app"))
				Eventually(session).Should(Say("USAGE:"))
				Eventually(session).Should(Say("cf app APP_NAME"))
				Eventually(session).Should(Say("OPTIONS:"))
				Eventually(session).Should(Say("--guid      Retrieve and display the given app's guid.  All other health and status output for the app is suppressed."))
				Eventually(session).Should(Say("SEE ALSO:"))
				Eventually(session).Should(Say("apps, events, logs, map-route, push, unmap-route"))
				Eventually(session).Should(Exit(0))
			})
		})
	})

	Context("when the environment is not setup correctly", func() {
		It("fails with the appropriate errors", func() {
			helpers.CheckEnvironmentTargetedCorrectly(true, true, ReadOnlyOrg, "app", "some-app")
		})
	})

	Context("when the environment is set up correctly", func() {
		var (
			orgName   string
			spaceName string
		)

		BeforeEach(func() {
			orgName = helpers.NewOrgName()
			spaceName = helpers.NewSpaceName()

			helpers.SetupCF(orgName, spaceName)
		})

		AfterEach(func() {
			helpers.QuickDeleteOrg(orgName)
		})

		Context("when the app name is not provided", func() {
			It("tells the user that the app name is required, prints help text, and exits 1", func() {
				session := helpers.CF("app")

				Eventually(session.Err).Should(Say("Incorrect Usage: the required argument `APP_NAME` was not provided"))
				Eventually(session).Should(Say("NAME:"))
				Eventually(session).Should(Exit(1))
			})
		})

		Context("when the app does not exist", func() {
			Context("when no flags are given", func() {
				It("tells the user that the app is not found and exits 1", func() {
					appName := helpers.PrefixedRandomName("app")
					session := helpers.CF("app", appName)

					Eventually(session).Should(Say("FAILED"))
					Eventually(session.Err).Should(Say("App %s not found", appName))
					Eventually(session).Should(Exit(1))
				})
			})

			Context("when the --guid flag is given", func() {
				It("tells the user that the app is not found and exits 1", func() {
					appName := helpers.PrefixedRandomName("app")
					session := helpers.CF("app", "--guid", appName)

					Eventually(session).Should(Say("FAILED"))
					Eventually(session.Err).Should(Say("App %s not found", appName))
					Eventually(session).Should(Exit(1))
				})
			})
		})

		Context("when the app does exist", func() {
			Context("when the app is a buildpack app", func() {
				var (
					domainName string
					tcpDomain  helpers.Domain
					appName    string
				)

				BeforeEach(func() {
					Eventually(helpers.CF("create-isolation-segment", RealIsolationSegment)).Should(Exit(0))
					Eventually(helpers.CF("enable-org-isolation", orgName, RealIsolationSegment)).Should(Exit(0))
					Eventually(helpers.CF("set-space-isolation-segment", spaceName, RealIsolationSegment)).Should(Exit(0))

					appName = helpers.PrefixedRandomName("app")
					domainName = helpers.DefaultSharedDomain()
					tcpDomain = helpers.NewDomain(orgName, helpers.DomainName("tcp"))
					tcpDomain.CreateWithRouterGroup(helpers.FindOrCreateTCPRouterGroup(GinkgoParallelNode()))
					helpers.WithHelloWorldApp(func(appDir string) {
						manifestContents := []byte(fmt.Sprintf(`
---
applications:
- name: %s
  memory: 128M
  instances: 2
  disk_quota: 128M
  routes:
  - route: %s.%s
  - route: %s:1024
`, appName, appName, domainName, tcpDomain.Name))
						manifestPath := filepath.Join(appDir, "manifest.yml")
						err := ioutil.WriteFile(manifestPath, manifestContents, 0666)
						Expect(err).ToNot(HaveOccurred())

						// Create manifest
						Eventually(helpers.CF("push", appName, "-p", appDir, "-f", manifestPath, "-b", "staticfile_buildpack")).Should(Exit(0))
					})
				})

				Context("when the app is started and has 2 instances", func() {
					It("displays the app information with instances table", func() {
						session := helpers.CF("app", appName)
						Eventually(session).Should(Say("name:\\s+%s", appName))
						Eventually(session).Should(Say("requested state:\\s+started"))
						Eventually(session).Should(Say("instances:\\s+2/2"))
						Eventually(session).Should(Say("isolation segment:\\s+%s", RealIsolationSegment))
						Eventually(session).Should(Say("usage:\\s+128M x 2 instances"))
						Eventually(session).Should(Say("routes:\\s+[\\w\\d-]+\\.%s, %s:1024", domainName, tcpDomain.Name))
						Eventually(session).Should(Say("last uploaded:\\s+\\w{3} [0-3]\\d \\w{3} [0-2]\\d:[0-5]\\d:[0-5]\\d \\w+ \\d{4}"))
						Eventually(session).Should(Say("stack:\\s+cflinuxfs2"))
						Eventually(session).Should(Say("buildpack:\\s+staticfile_buildpack"))
						Eventually(session).Should(Say(""))
						Eventually(session).Should(Say("state\\s+since\\s+cpu\\s+memory\\s+disk\\s+details"))
						Eventually(session).Should(Say("#0\\s+(running|starting)\\s+\\d{4}-[01]\\d-[0-3]\\dT[0-2][0-9]:[0-5]\\d:[0-5]\\dZ\\s+\\d+\\.\\d+%.*of 128M.*of 128M"))
						Eventually(session).Should(Say("#1\\s+(running|starting)\\s+\\d{4}-[01]\\d-[0-3]\\dT[0-2][0-9]:[0-5]\\d:[0-5]\\dZ\\s+\\d+\\.\\d+%.*of 128M.*of 128M"))
						Eventually(session).Should(Exit(0))
					})
				})

				Context("when the app is stopped", func() {
					BeforeEach(func() {
						Eventually(helpers.CF("stop", appName)).Should(Exit(0))
					})

					It("displays the app information", func() {
						session := helpers.CF("app", appName)
						Eventually(session).Should(Say("name:\\s+%s", appName))
						Eventually(session).Should(Say("requested state:\\s+stopped"))
						Eventually(session).Should(Say("instances:\\s+0/2"))
						Eventually(session).Should(Say("usage:\\s+128M x 2 instances"))
						Eventually(session).Should(Say("routes:\\s+[\\w\\d-]+.%s, %s:1024", domainName, tcpDomain.Name))
						Eventually(session).Should(Say("last uploaded:"))
						Eventually(session).Should(Say("stack:\\s+cflinuxfs2"))
						Eventually(session).Should(Say("buildpack:\\s+staticfile_buildpack"))

						Eventually(session).Should(Say("There are no running instances of this app."))
						Eventually(session).Should(Exit(0))
					})
				})

				Context("when the app has 0 instances", func() {
					BeforeEach(func() {
						Eventually(helpers.CF("scale", appName, "-i", "0")).Should(Exit(0))
					})

					It("displays the app information", func() {
						session := helpers.CF("app", appName)
						Eventually(session).Should(Say("name:\\s+%s", appName))
						Eventually(session).Should(Say("requested state:\\s+started"))
						Eventually(session).Should(Say("instances:\\s+0/0"))
						Eventually(session).Should(Say("usage:\\s+128M x 0 instances"))
						Eventually(session).Should(Say("routes:\\s+[\\w\\d-]+\\.%s, %s:1024", domainName, tcpDomain.Name))
						Eventually(session).Should(Say("last uploaded:"))
						Eventually(session).Should(Say("stack:\\s+cflinuxfs2"))
						Eventually(session).Should(Say("buildpack:\\s+staticfile_buildpack"))

						Eventually(session).Should(Say("There are no running instances of this app."))
						Eventually(session).Should(Exit(0))
					})
				})

				Context("when the --guid flag is given", func() {
					var appGUID string

					BeforeEach(func() {
						session := helpers.CF("curl", fmt.Sprintf("/v2/apps?q=name:%s", appName))
						Eventually(session).Should(Exit(0))
						rawJSON := strings.TrimSpace(string(session.Out.Contents()))
						var AppInfo struct {
							Resources []struct {
								Metadata struct {
									GUID string `json:"guid"`
								} `json:"metadata"`
							} `json:"resources"`
						}

						err := json.Unmarshal([]byte(rawJSON), &AppInfo)
						Expect(err).NotTo(HaveOccurred())

						appGUID = AppInfo.Resources[0].Metadata.GUID
					})

					It("displays the app guid", func() {
						session := helpers.CF("app", "--guid", appName)
						Eventually(session).Should(Say(appGUID))
						Eventually(session).Should(Exit(0))
					})
				})
			})

			Context("when the app is a Docker app", func() {
				var (
					tcpDomain helpers.Domain
					appName   string
				)

				BeforeEach(func() {
					appName = helpers.PrefixedRandomName("app")
					tcpDomain = helpers.NewDomain(orgName, helpers.DomainName("tcp"))
					tcpDomain.CreateWithRouterGroup(helpers.FindOrCreateTCPRouterGroup(GinkgoParallelNode()))
					Eventually(helpers.CF("push", appName, "-o", DockerImage)).Should(Exit())
				})

				It("displays the docker image and does not display buildpack", func() {
					session := helpers.CF("app", appName)
					Eventually(session).Should(Say("name:\\s+%s", appName))
					Consistently(session).ShouldNot(Say("buildpack:"))
					Eventually(session).Should(Say("docker image:\\s+%s", DockerImage))
					Consistently(session).ShouldNot(Say("buildpack:"))
					Eventually(session).Should(Exit(0))
				})
			})
		})
	})
})
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"fmt"

	"github.com/EngineerBetter/concourse-up/aws"
)

const terraformStateFileName = "terraform.tfstate"
const configFilePath = "config.json"

// IClient is an interface for the config file client
type IClient interface {
	Load() (*Config, error)
	DeleteAll(config *Config) error
	LoadOrCreate(deployArgs *DeployArgs) (*Config, bool, error)
	Update(*Config) error
	StoreAsset(filename string, contents []byte) error
	HasAsset(filename string) (bool, error)
	LoadAsset(filename string) ([]byte, error)
	DeleteAsset(filename string) error
}

// Client is a client for loading the config file  from S3
type Client struct {
	Project  string
	S3Region string
}

// StoreAsset stores an associated configuration file
func (client *Client) StoreAsset(filename string, contents []byte) error {
	return aws.WriteFile(client.configBucket(),
		filename,
		client.S3Region,
		contents,
	)
}

// LoadAsset loads an associated configuration file
func (client *Client) LoadAsset(filename string) ([]byte, error) {
	return aws.LoadFile(
		client.configBucket(),
		filename,
		client.S3Region,
	)
}

// DeleteAsset deletes an associated configuration file
func (client *Client) DeleteAsset(filename string) error {
	return aws.DeleteFile(
		client.configBucket(),
		filename,
		client.S3Region,
	)
}

// HasAsset returns true if an associated configuration file exists
func (client *Client) HasAsset(filename string) (bool, error) {
	return aws.HasFile(
		client.configBucket(),
		filename,
		client.S3Region,
	)
}

// Update stores the conconcourse up config file to S3
func (client *Client) Update(config *Config) error {
	bytes, err := json.Marshal(config)
	if err != nil {
		return err
	}

	return aws.WriteFile(client.configBucket(), configFilePath, client.S3Region, bytes)
}

// DeleteAll deletes the entire configuration bucket
func (client *Client) DeleteAll(config *Config) error {
	return aws.DeleteVersionedBucket(config.ConfigBucket, client.S3Region)
}

// Load loads an existing config file from S3
func (client *Client) Load() (*Config, error) {
	fmt.Println(client.configBucket())
	configBytes, err := aws.LoadFile(
		client.configBucket(),
		configFilePath,
		client.S3Region,
	)
	if err != nil {
		return nil, err
	}

	conf := Config{}
	if err := json.Unmarshal(configBytes, &conf); err != nil {
		return nil, err
	}

	return &conf, nil
}

// LoadOrCreate loads an existing config file from S3, or creates a default if one doesn't already exist
func (client *Client) LoadOrCreate(deployArgs *DeployArgs) (*Config, bool, error) {
	defaultConfigBytes, err := generateDefaultConfig(
		client.Project,
		client.deployment(),
		client.configBucket(),
		deployArgs.AWSRegion,
	)
	if err != nil {
		return nil, false, err
	}

	if err = aws.EnsureBucketExists(client.configBucket(), client.S3Region); err != nil {
		return nil, false, err
	}

	configBytes, createdNewFile, err := aws.EnsureFileExists(
		client.configBucket(),
		configFilePath,
		client.S3Region,
		defaultConfigBytes,
	)
	if err != nil {
		return nil, false, err
	}

	conf := Config{}
	if err := json.Unmarshal(configBytes, &conf); err != nil {
		return nil, false, err
	}

	return &conf, createdNewFile, nil
}

func (client *Client) deployment() string {
	return fmt.Sprintf("concourse-up-%s", client.Project)
}

func (client *Client) configBucket() string {
	return fmt.Sprintf("%s-%s-config", client.deployment(), client.S3Region)
}
<commit_msg>remove debug statement<commit_after>package config

import (
	"encoding/json"
	"fmt"

	"github.com/EngineerBetter/concourse-up/aws"
)

const terraformStateFileName = "terraform.tfstate"
const configFilePath = "config.json"

// IClient is an interface for the config file client
type IClient interface {
	Load() (*Config, error)
	DeleteAll(config *Config) error
	LoadOrCreate(deployArgs *DeployArgs) (*Config, bool, error)
	Update(*Config) error
	StoreAsset(filename string, contents []byte) error
	HasAsset(filename string) (bool, error)
	LoadAsset(filename string) ([]byte, error)
	DeleteAsset(filename string) error
}

// Client is a client for loading the config file  from S3
type Client struct {
	Project  string
	S3Region string
}

// StoreAsset stores an associated configuration file
func (client *Client) StoreAsset(filename string, contents []byte) error {
	return aws.WriteFile(client.configBucket(),
		filename,
		client.S3Region,
		contents,
	)
}

// LoadAsset loads an associated configuration file
func (client *Client) LoadAsset(filename string) ([]byte, error) {
	return aws.LoadFile(
		client.configBucket(),
		filename,
		client.S3Region,
	)
}

// DeleteAsset deletes an associated configuration file
func (client *Client) DeleteAsset(filename string) error {
	return aws.DeleteFile(
		client.configBucket(),
		filename,
		client.S3Region,
	)
}

// HasAsset returns true if an associated configuration file exists
func (client *Client) HasAsset(filename string) (bool, error) {
	return aws.HasFile(
		client.configBucket(),
		filename,
		client.S3Region,
	)
}

// Update stores the conconcourse up config file to S3
func (client *Client) Update(config *Config) error {
	bytes, err := json.Marshal(config)
	if err != nil {
		return err
	}

	return aws.WriteFile(client.configBucket(), configFilePath, client.S3Region, bytes)
}

// DeleteAll deletes the entire configuration bucket
func (client *Client) DeleteAll(config *Config) error {
	return aws.DeleteVersionedBucket(config.ConfigBucket, client.S3Region)
}

// Load loads an existing config file from S3
func (client *Client) Load() (*Config, error) {
	configBytes, err := aws.LoadFile(
		client.configBucket(),
		configFilePath,
		client.S3Region,
	)
	if err != nil {
		return nil, err
	}

	conf := Config{}
	if err := json.Unmarshal(configBytes, &conf); err != nil {
		return nil, err
	}

	return &conf, nil
}

// LoadOrCreate loads an existing config file from S3, or creates a default if one doesn't already exist
func (client *Client) LoadOrCreate(deployArgs *DeployArgs) (*Config, bool, error) {
	defaultConfigBytes, err := generateDefaultConfig(
		client.Project,
		client.deployment(),
		client.configBucket(),
		deployArgs.AWSRegion,
	)
	if err != nil {
		return nil, false, err
	}

	if err = aws.EnsureBucketExists(client.configBucket(), client.S3Region); err != nil {
		return nil, false, err
	}

	configBytes, createdNewFile, err := aws.EnsureFileExists(
		client.configBucket(),
		configFilePath,
		client.S3Region,
		defaultConfigBytes,
	)
	if err != nil {
		return nil, false, err
	}

	conf := Config{}
	if err := json.Unmarshal(configBytes, &conf); err != nil {
		return nil, false, err
	}

	return &conf, createdNewFile, nil
}

func (client *Client) deployment() string {
	return fmt.Sprintf("concourse-up-%s", client.Project)
}

func (client *Client) configBucket() string {
	return fmt.Sprintf("%s-%s-config", client.deployment(), client.S3Region)
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"net/http"
	"os"
	"os/user"
	"path/filepath"
	"strconv"
	"time"

	"path"

	"github.com/mitchellh/go-homedir"
	"github.com/sirupsen/logrus"
	"gopkg.in/ini.v1"
)

// if not empty, this will be used instead of `~/.config/ondevice/ondevice.conf`
var _configPath string

var version = "0.0.1-devel"

// Config -- config file's contents, acquired using config.Read()
type Config struct {
	cfg *ini.File
}

// Read -- fetches the contents of ondevice.conf
func Read() (Config, error) {
	var rc Config
	var err error
	path := GetConfigPath("ondevice.conf")

	if rc.cfg, err = ini.InsensitiveLoad(path); err != nil {
		return rc, err
	}
	return rc, nil
}

// AllValues -- returns a flattened key/value dictionary for all values in ondevice.conf
func (c Config) AllValues() map[string]string {
	var rc = make(map[string]string)

	for _, s := range c.cfg.Sections() {
		for _, k := range s.Keys() {
			var key = fmt.Sprintf("%s.%s", s.Name(), k.Name())
			var value = k.String()
			rc[key] = value
		}
	}

	return rc
}

// GetConfigPath -- Return the full path of a file in our config directory (usually ~/.config/ondevice/)
// Can be overridden using setConfigPath() (for testing only) or SetFilePath()
func GetConfigPath(filename string) string {
	// global config path override (used in unit tests)
	// TODO replace with single file overrides
	if _configPath != "" {
		return path.Join(filepath.Dir(_configPath), filename)
	}

	var u, err = user.Current()
	var homeDir string

	if err == nil {
		homeDir = u.HomeDir
	} else {
		// This can happen when cross-compiling (it crept up on build-linux-armhf
		// even though https://github.com/golang/go/issues/14626 has been closed for quite some time now)
		//logrus.WithError(err).Warning("failed to get user.Current(), using $HOME instead")
		homeDir = os.Getenv("HOME")
		if homeDir == "" {
			logrus.Fatal("Couldn't get current user (and $HOME is empty): ", err)
		}
	}

	return path.Join(homeDir, ".config/ondevice", filename)
}

// GetInt -- Returns the specified integer config value (or defaultValue if not found or on error)
func GetInt(section string, key string, defaultValue int) int {
	var val, err = GetString(section, key)
	if err != nil {
		return defaultValue
	}

	rc, err := strconv.ParseInt(val, 10, 32)
	if err != nil {
		logrus.WithError(err).Warningf("error parsing '%s.%s'", section, key)
		return defaultValue
	}

	return int(rc)
}

// GetVersion -- Returns the app version
func GetVersion() string {
	return version
}

// GetString -- Get a configuration value (as string)
func GetString(section string, key string) (string, error) {
	path := GetConfigPath("ondevice.conf")

	cfg, err := ini.InsensitiveLoad(path)
	if err != nil {
		return "", err
	}

	s, err := cfg.GetSection(section)
	if err != nil {
		return "", err
	}

	val, err := s.GetKey(key)
	if err != nil {
		return "", err
	}

	return val.String(), nil
}

// SetAuth -- Set client/user authentication details
func SetAuth(scope, user, auth string) error {
	if scope != "client" && scope != "device" {
		// panic instead of returning an error (since it pretty much has to be a programming error)
		logrus.Fatal("config.SetAuth(): scope needs to be one of 'device' and 'client': ", scope)
	}

	if err := SetValue(scope, "user", user); err != nil {
		return err
	}

	if err := SetValue(scope, "auth", auth); err != nil {
		return err
	}

	return nil
}

// SetValue -- create/update a config value
func SetValue(section string, key string, value string) error {
	path := GetConfigPath("ondevice.conf")

	if err := os.MkdirAll(filepath.Dir(path), 0750); err != nil {
		return err
	}

	cfg, err := ini.InsensitiveLoad(path)
	if os.IsNotExist(err) {
		logrus.Debug("creating new ondevice.conf")
		cfg = ini.Empty()
	} else if err != nil {
		return err
	}

	s := cfg.Section(section)
	k := s.Key(key)

	k.SetValue(value)

	// save to a temporary file and only replace the old file if successful
	// (to avoid corrupting the config file)
	tmpPath := filepath.Join(filepath.Dir(path), ".ondevice.conf.tmp")
	if err = cfg.SaveTo(tmpPath); err != nil {
		return err
	}
	if err = os.Chmod(tmpPath, 0600); err != nil {
		return err
	}
	if err = os.Rename(tmpPath, path); err != nil {
		return err
	}

	return nil
}

// Init -- sets up configuration, called by cobra.OnInitialize()
func Init(cfgFile string) {
	if cfgFile != "" {
		// Use config file from the flag.
		_configPath = cfgFile
	} else {
		// Find home directory.
		home, err := homedir.Dir()
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		// TODO maybe use another path on windows (and other )
		_configPath = filepath.Join(home, ".config/ondevice/ondevice.conf")
	}

	// create parent directory
	if err := os.MkdirAll(filepath.Dir(_configPath), 0o755); err != nil {
		logrus.WithError(err).Fatalf("failed to create config directory: '%s'", filepath.Dir(_configPath))
	}

	// set a default timeout of 30sec for REST API calls (will be reset in long-running commands)
	// TODO use a builder pattern to be able to specify this on a per-request basis
	// Note: doesn't affect websocket connections
	var timeout = time.Duration(GetInt("client", "timeout", 30))
	http.DefaultClient.Timeout = timeout * time.Second
}
<commit_msg>moved config.GetInt() into config.Config<commit_after>package config

import (
	"fmt"
	"net/http"
	"os"
	"os/user"
	"path/filepath"
	"time"

	"path"

	"github.com/mitchellh/go-homedir"
	"github.com/sirupsen/logrus"
	"gopkg.in/ini.v1"
)

// if not empty, this will be used instead of `~/.config/ondevice/ondevice.conf`
var _configPath string

var version = "0.0.1-devel"

// Config -- config file's contents, acquired using config.Read()
type Config struct {
	cfg *ini.File
}

// Read -- fetches the contents of ondevice.conf
func Read() (Config, error) {
	var rc Config
	var err error
	path := GetConfigPath("ondevice.conf")

	if rc.cfg, err = ini.InsensitiveLoad(path); err != nil {
		return rc, err
	}
	return rc, nil
}

// AllValues -- returns a flattened key/value dictionary for all values in ondevice.conf
func (c Config) AllValues() map[string]string {
	var rc = make(map[string]string)

	for _, s := range c.cfg.Sections() {
		for _, k := range s.Keys() {
			var key = fmt.Sprintf("%s.%s", s.Name(), k.Name())
			var value = k.String()
			rc[key] = value
		}
	}

	return rc
}

// GetInt -- Returns the specified integer config value (or defaultValue if not found or on error)
func (c Config) GetInt(section string, key string, defaultValue int) int {
	var s = c.cfg.Section(section)
	if s != nil {
		return defaultValue // missing section
	}

	var k = s.Key(key)
	if k != nil {
		return defaultValue // missing key
	}

	var rc, err = k.Int()
	if err != nil {
		logrus.WithError(err).Errorf("expected integer value for config key '%s.%s'", section, key)
		return defaultValue
	}

	return rc
}

// GetConfigPath -- Return the full path of a file in our config directory (usually ~/.config/ondevice/)
// Can be overridden using setConfigPath() (for testing only) or SetFilePath()
func GetConfigPath(filename string) string {
	// global config path override (used in unit tests)
	// TODO replace with single file overrides
	if _configPath != "" {
		return path.Join(filepath.Dir(_configPath), filename)
	}

	var u, err = user.Current()
	var homeDir string

	if err == nil {
		homeDir = u.HomeDir
	} else {
		// This can happen when cross-compiling (it crept up on build-linux-armhf
		// even though https://github.com/golang/go/issues/14626 has been closed for quite some time now)
		//logrus.WithError(err).Warning("failed to get user.Current(), using $HOME instead")
		homeDir = os.Getenv("HOME")
		if homeDir == "" {
			logrus.Fatal("Couldn't get current user (and $HOME is empty): ", err)
		}
	}

	return path.Join(homeDir, ".config/ondevice", filename)
}

// GetVersion -- Returns the app version
func GetVersion() string {
	return version
}

// GetString -- Get a configuration value (as string)
func GetString(section string, key string) (string, error) {
	path := GetConfigPath("ondevice.conf")

	cfg, err := ini.InsensitiveLoad(path)
	if err != nil {
		return "", err
	}

	s, err := cfg.GetSection(section)
	if err != nil {
		return "", err
	}

	val, err := s.GetKey(key)
	if err != nil {
		return "", err
	}

	return val.String(), nil
}

// SetAuth -- Set client/user authentication details
func SetAuth(scope, user, auth string) error {
	if scope != "client" && scope != "device" {
		// panic instead of returning an error (since it pretty much has to be a programming error)
		logrus.Fatal("config.SetAuth(): scope needs to be one of 'device' and 'client': ", scope)
	}

	if err := SetValue(scope, "user", user); err != nil {
		return err
	}

	if err := SetValue(scope, "auth", auth); err != nil {
		return err
	}

	return nil
}

// SetValue -- create/update a config value
func SetValue(section string, key string, value string) error {
	path := GetConfigPath("ondevice.conf")

	if err := os.MkdirAll(filepath.Dir(path), 0750); err != nil {
		return err
	}

	cfg, err := ini.InsensitiveLoad(path)
	if os.IsNotExist(err) {
		logrus.Debug("creating new ondevice.conf")
		cfg = ini.Empty()
	} else if err != nil {
		return err
	}

	s := cfg.Section(section)
	k := s.Key(key)

	k.SetValue(value)

	// save to a temporary file and only replace the old file if successful
	// (to avoid corrupting the config file)
	tmpPath := filepath.Join(filepath.Dir(path), ".ondevice.conf.tmp")
	if err = cfg.SaveTo(tmpPath); err != nil {
		return err
	}
	if err = os.Chmod(tmpPath, 0600); err != nil {
		return err
	}
	if err = os.Rename(tmpPath, path); err != nil {
		return err
	}

	return nil
}

// Init -- sets up configuration, called by cobra.OnInitialize()
func Init(cfgFile string) {
	if cfgFile != "" {
		// Use config file from the flag.
		_configPath = cfgFile
	} else {
		// Find home directory.
		home, err := homedir.Dir()
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		// TODO maybe use another path on windows (and other )
		_configPath = filepath.Join(home, ".config/ondevice/ondevice.conf")
	}

	// create parent directory
	var err error
	if err = os.MkdirAll(filepath.Dir(_configPath), 0o755); err != nil {
		logrus.WithError(err).Fatalf("failed to create config directory: '%s'", filepath.Dir(_configPath))
	}

	var cfg Config
	if cfg, err = Read(); err != nil {
		logrus.WithError(err).Error("failed to read ondevice.conf")
		return
	}

	// set a default timeout of 30sec for REST API calls (will be reset in long-running commands)
	// TODO use a builder pattern to be able to specify this on a per-request basis
	// Note: doesn't affect websocket connections
	var timeout = time.Duration(cfg.GetInt("client", "timeout", 30))
	http.DefaultClient.Timeout = timeout * time.Second
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"
)

var Settings *Config

type Config struct {
	Get struct {
		// Settings for daemon
		Address string
		Port    uint
	}

	Post struct {
		// Settings for daemon
		Address string
		Port    uint
	}

	Admin struct {
		// Settings for daemon
		Address string
		Port    uint
	}

	Directories struct {
		// Storage directory for images
		ImageDir     string
		ThumbnailDir string
	}

	// sites for CORS
	CORS struct {
		Sites []string
	}

	Database struct {
		// Database connection settings
		User           string
		Password       string
		Proto          string
		Host           string
		Database       string
		MaxIdle        int
		MaxConnections int
	}

	Redis struct {
		// Redis address and max pool connections
		Protocol       string
		Address        string
		MaxIdle        int
		MaxConnections int
	}

	// HMAC secret for bcrypt
	Session struct {
		Secret string
	}
}

func Print() {

	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n", "Local Config")
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Server")
	fmt.Printf("%-20v%40v\n", "Type", "Post")
	fmt.Printf("%-20v%40v\n", "Address", Settings.Post.Address)
	fmt.Printf("%-20v%40v\n", "Port", Settings.Post.Port)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Directories")
	fmt.Printf("%-20v%40v\n", "Images", Settings.Directories.ImageDir)
	fmt.Printf("%-20v%40v\n", "Thumbnails", Settings.Directories.ThumbnailDir)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "CORS")
	fmt.Printf("%-20v%40v\n", "Domains", strings.Join(Settings.CORS.Sites, ", "))
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Database")
	fmt.Printf("%-20v%40v\n", "User", Settings.Database.User)
	fmt.Printf("%-20v%40v\n", "Password", Settings.Database.Password)
	fmt.Printf("%-20v%40v\n", "Protocol", Settings.Database.Proto)
	fmt.Printf("%-20v%40v\n", "Host", Settings.Database.Host)
	fmt.Printf("%-20v%40v\n", "Database", Settings.Database.Database)
	fmt.Printf("%-20v%40v\n", "Max Idle", Settings.Database.MaxIdle)
	fmt.Printf("%-20v%40v\n", "Max Connections", Settings.Database.MaxConnections)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Redis")
	fmt.Printf("%-20v%40v\n", "Protocol", Settings.Redis.Protocol)
	fmt.Printf("%-20v%40v\n", "Address", Settings.Redis.Address)
	fmt.Printf("%-20v%40v\n", "Max Idle", Settings.Redis.MaxIdle)
	fmt.Printf("%-20v%40v\n", "Max Connections", Settings.Redis.MaxConnections)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Session")
	fmt.Printf("%-20v%40v\n", "Secret", Settings.Session.Secret)
	fmt.Println(strings.Repeat("*", 60))

}

func init() {
	file, err := os.Open("/etc/pram/pram.conf")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	Settings = &Config{}

	decoder := json.NewDecoder(file)

	err = decoder.Decode(&Settings)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

}
<commit_msg>clean up local config<commit_after>package config

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"
)

var Settings *Config

type Config struct {
	Post struct {
		// Settings for daemon
		Address string
		Port    uint
	}

	Directories struct {
		// Storage directory for images
		ImageDir     string
		ThumbnailDir string
	}

	// sites for CORS
	CORS struct {
		Sites []string
	}

	Database struct {
		// Database connection settings
		User           string
		Password       string
		Proto          string
		Host           string
		Database       string
		MaxIdle        int
		MaxConnections int
	}

	Redis struct {
		// Redis address and max pool connections
		Protocol       string
		Address        string
		MaxIdle        int
		MaxConnections int
	}

	// HMAC secret for bcrypt
	Session struct {
		Secret string
	}
}

func Print() {

	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n", "Local Config")
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Server")
	fmt.Printf("%-20v%40v\n", "Type", "POST")
	fmt.Printf("%-20v%40v\n", "Address", Settings.Post.Address)
	fmt.Printf("%-20v%40v\n", "Port", Settings.Post.Port)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Directories")
	fmt.Printf("%-20v%40v\n", "Images", Settings.Directories.ImageDir)
	fmt.Printf("%-20v%40v\n", "Thumbnails", Settings.Directories.ThumbnailDir)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "CORS")
	fmt.Printf("%-20v%40v\n", "Domains", strings.Join(Settings.CORS.Sites, ", "))
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Database")
	fmt.Printf("%-20v%40v\n", "User", Settings.Database.User)
	fmt.Printf("%-20v%40v\n", "Password", Settings.Database.Password)
	fmt.Printf("%-20v%40v\n", "Protocol", Settings.Database.Proto)
	fmt.Printf("%-20v%40v\n", "Host", Settings.Database.Host)
	fmt.Printf("%-20v%40v\n", "Database", Settings.Database.Database)
	fmt.Printf("%-20v%40v\n", "Max Idle", Settings.Database.MaxIdle)
	fmt.Printf("%-20v%40v\n", "Max Connections", Settings.Database.MaxConnections)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Redis")
	fmt.Printf("%-20v%40v\n", "Protocol", Settings.Redis.Protocol)
	fmt.Printf("%-20v%40v\n", "Address", Settings.Redis.Address)
	fmt.Printf("%-20v%40v\n", "Max Idle", Settings.Redis.MaxIdle)
	fmt.Printf("%-20v%40v\n", "Max Connections", Settings.Redis.MaxConnections)
	fmt.Println(strings.Repeat("*", 60))
	fmt.Printf("%-20v\n\n", "Session")
	fmt.Printf("%-20v%40v\n", "Secret", Settings.Session.Secret)
	fmt.Println(strings.Repeat("*", 60))

}

func init() {
	file, err := os.Open("/etc/pram/pram.conf")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	Settings = &Config{}

	decoder := json.NewDecoder(file)

	err = decoder.Decode(&Settings)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

}
<|endoftext|>


<commit_before>package routers

import (
	"github.com/gorilla/mux"
	"github.com/unrolled/secure"
)

var secureMiddleware = secure.New(secure.Options{
	SSLRedirect:        true,
	FrameDeny:          true,
	ContentTypeNosniff: true,
	BrowserXssFilter:   true,
	IsDevelopment:      false,
})

func InitRoutes() *mux.Router {
	router := mux.NewRouter()
	SetTeamRoutes(router)
	SetGameRoutes(router)
	SetPlayerRoutes(router)
	SetShotRoutes(router)

	return router
}
<commit_msg>Remove SSL redirect<commit_after>package routers

import (
	"github.com/gorilla/mux"
	"github.com/unrolled/secure"
)

var secureMiddleware = secure.New(secure.Options{
	// SSLRedirect:        true,
	FrameDeny:          true,
	ContentTypeNosniff: true,
	BrowserXssFilter:   true,
	IsDevelopment:      false,
})

func InitRoutes() *mux.Router {
	router := mux.NewRouter()
	SetTeamRoutes(router)
	SetGameRoutes(router)
	SetPlayerRoutes(router)
	SetShotRoutes(router)

	return router
}
<|endoftext|>


<commit_before>package config

import (
	"github.com/jingweno/jqplay/jq"
	"github.com/joeshaw/envdecode"
)

type Config struct {
	Host        string `env:"HOST,default=0.0.0.0",required`
	Port        string `env:"PORT,default=3000",required`
	GinMode     string `env:"GIN_MODE,default=debug",required`
	DatabaseURL string `env:"DATABASE_URL",required`
	SnippetSalt string `env:"SNIPPET_SALT",required`
	AssetHost   string `env:"ASSET_HOST"`
	JQVer       string
}

func (c *Config) IsProd() bool {
	return c.GinMode == "release"
}

func Load() (*Config, error) {
	conf := &Config{}
	err := envdecode.Decode(conf)
	if err != nil {
		return nil, err
	}

	conf.JQVer = jq.Version

	return conf, nil
}
<commit_msg>Fix config struct tags<commit_after>package config

import (
	"github.com/jingweno/jqplay/jq"
	"github.com/joeshaw/envdecode"
)

type Config struct {
	Host        string `env:"HOST,default=0.0.0.0,required"`
	Port        string `env:"PORT,default=3000,required"`
	GinMode     string `env:"GIN_MODE,default=debug,required"`
	DatabaseURL string `env:"DATABASE_URL,required"`
	SnippetSalt string `env:"SNIPPET_SALT,required"`
	AssetHost   string `env:"ASSET_HOST"`
	JQVer       string
}

func (c *Config) IsProd() bool {
	return c.GinMode == "release"
}

func Load() (*Config, error) {
	conf := &Config{}
	err := envdecode.Decode(conf)
	if err != nil {
		return nil, err
	}

	conf.JQVer = jq.Version

	return conf, nil
}
<|endoftext|>