Skip to content

Commit

Permalink
Make ecs-cli prefixes configurable.
Browse files Browse the repository at this point in the history
closes #51

Fields:
* ecs-cli up : CFNStackName
* ecs-cli compose : ComposeProjectNamePrefix used for
 * task definition name
 * started by for tasks
* ecs-cli compose service : ComposeServiceNamePrefix used for
 * service name
  • Loading branch information
uttarasridhar committed Apr 11, 2016
1 parent c98043f commit a3e3eee
Show file tree
Hide file tree
Showing 18 changed files with 324 additions and 72 deletions.
4 changes: 4 additions & 0 deletions ecs-cli/modules/aws/clients/ecs/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ func (rdwr *mockReadWriter) Save(dest *config.Destination) error {
return nil
}

func (rdwr *mockReadWriter) IsKeyPresent(section, key string) bool {
return true
}

func TestNewECSClientWithRegion(t *testing.T) {
// TODO: Re-enable by making an integ test target in Makefile.
t.Skip("Integ test, Re-enable Me!")
Expand Down
21 changes: 15 additions & 6 deletions ecs-cli/modules/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,19 @@ package cli
// Flag names used by the cli.
// TODO: These need a better home.
const (
ClusterFlag = "cluster"
ProfileFlag = "profile"
RegionFlag = "region"
AccessKeyFlag = "access-key"
SecretKeyFlag = "secret-key"
VerboseFlag = "verbose"
AccessKeyFlag = "access-key"
SecretKeyFlag = "secret-key"
RegionFlag = "region"
AwsRegionEnvVar = "AWS_REGION"
AwsDefaultRegionEnvVar = "AWS_DEFAULT_REGION"
ProfileFlag = "profile"
ClusterFlag = "cluster"
VerboseFlag = "verbose"

ComposeProjectNamePrefixFlag = "compose-project-name-prefix"
ComposeProjectNamePrefixDefaultValue = "ecscompose-"
ComposeServiceNamePrefixFlag = "compose-service-name-prefix"
ComposeServiceNamePrefixDefaultValue = ComposeProjectNamePrefixDefaultValue + "service-"
CFNStackNamePrefixFlag = "cfn-stack-name-prefix"
CFNStackNamePrefixDefaultValue = "amazon-ecs-cli-setup-"
)
2 changes: 1 addition & 1 deletion ecs-cli/modules/command/cluster_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func createCluster(context *cli.Context, rdwr config.ReadWriter, ecsClient ecscl
if !isIAMAcknowledged(context) {
return fmt.Errorf("Please acknowledge that this command may create IAM resources with the '--%s' flag", capabilityIAMFlag)
}
ecsParams, err := config.NewCliParams(context, rdwr)
ecsParams, err := newCliParams(context, rdwr)
if err != nil {
return err
}
Expand Down
4 changes: 4 additions & 0 deletions ecs-cli/modules/command/cluster_app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ func (rdwr *mockReadWriter) Save(dest *config.Destination) error {
return nil
}

func (rdwr *mockReadWriter) IsKeyPresent(section, key string) bool {
return true
}

func TestClusterUp(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
Expand Down
29 changes: 27 additions & 2 deletions ecs-cli/modules/command/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ func ConfigureCommand() cli.Command {
cli.StringFlag{
Name: ecscli.RegionFlag + ", r",
Usage: fmt.Sprintf(
"Specifies the AWS region to use. If the AWS_REGION environment variable is set when ecs-cli configure is run, then the AWS region is set to the value of that environment variable.",
"Specifies the AWS region to use. If the " + ecscli.AwsRegionEnvVar + " environment variable is set when ecs-cli configure is run, then the AWS region is set to the value of that environment variable.",
),
EnvVar: "AWS_REGION",
EnvVar: ecscli.AwsRegionEnvVar,
},
cli.StringFlag{
Name: ecscli.AccessKeyFlag,
Expand Down Expand Up @@ -66,6 +66,27 @@ func ConfigureCommand() cli.Command {
// Commenting it now to avoid user misunderstanding the behavior of this env var with other ecs-cli commands
// EnvVar: "ECS_CLUSTER",
},
cli.StringFlag{
Name: ecscli.ComposeProjectNamePrefixFlag,
Value: ecscli.ComposeProjectNamePrefixDefaultValue,
Usage: fmt.Sprintf(
"[Optional] Specifies the prefix added to an ECS task definition created from a compose file. Format <prefix><project-name>.",
),
},
cli.StringFlag{
Name: ecscli.ComposeServiceNamePrefixFlag,
Value: ecscli.ComposeServiceNamePrefixDefaultValue,
Usage: fmt.Sprintf(
"[Optional] Specifies the prefix added to an ECS service created from a compose file. Format <prefix><project-name>.",
),
},
cli.StringFlag{
Name: ecscli.CFNStackNamePrefixFlag,
Value: ecscli.CFNStackNamePrefixDefaultValue,
Usage: fmt.Sprintf(
"[Optional] Specifies the prefix added to the AWS CloudFormation stack created on ecs-cli up. Format <prefix><cluster-name>.",
),
},
},
}
}
Expand Down Expand Up @@ -114,6 +135,10 @@ func createECSConfigFromCli(context *cli.Context) (*config.CliConfig, error) {
ecsConfig.AwsSecretKey = secretKey
ecsConfig.Region = region

ecsConfig.ComposeProjectNamePrefix = context.String(ecscli.ComposeProjectNamePrefixFlag)
ecsConfig.ComposeServiceNamePrefix = context.String(ecscli.ComposeServiceNamePrefixFlag)
ecsConfig.CFNStackNamePrefix = context.String(ecscli.CFNStackNamePrefixFlag)

return ecsConfig, nil
}

Expand Down
52 changes: 52 additions & 0 deletions ecs-cli/modules/command/configure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,55 @@ func TestConfigInitWithProfileAndKeys(t *testing.T) {
t.Errorf("Expected error when both AWS Profile and access keys are specified")
}
}

func TestConfigInitWithPrefixes(t *testing.T) {
setPrefixes := flag.NewFlagSet("ecs-cli", 0)
setPrefixes.String(ecscli.ProfileFlag, profileName, "")
setPrefixes.String(ecscli.ClusterFlag, clusterName, "")

composeProjectName := "projectName"
composeServiceName := "serviceName"
cfnStackName := "stackName"

setPrefixes.String(ecscli.ComposeProjectNamePrefixFlag, composeProjectName, "")
setPrefixes.String(ecscli.ComposeServiceNamePrefixFlag, composeServiceName, "")
setPrefixes.String(ecscli.CFNStackNamePrefixFlag, cfnStackName, "")

context := cli.NewContext(nil, setPrefixes, nil)

cfg, err := createECSConfigFromCli(context)
if err != nil {
t.Errorf("Error reading config from rdwr: ", err)
}
if composeProjectName != cfg.ComposeProjectNamePrefix {
t.Errorf("ComposeProjectName mismtach in config. Expected [%s] Got [%s]", clusterName, cfg.ComposeProjectNamePrefix)
}
if composeServiceName != cfg.ComposeServiceNamePrefix {
t.Errorf("ComposeServiceName mismatch in config. Expected [%s] Got [%s]", composeServiceName, cfg.ComposeServiceNamePrefix)
}
if cfnStackName != cfg.CFNStackNamePrefix {
t.Errorf("CFNStackNamePrefix mismatch in config. Expected [%s] Got [%s]", cfnStackName, cfg.CFNStackNamePrefix)
}
}

func TestConfigInitWithoutPrefixes(t *testing.T) {
setNoPrefixes := flag.NewFlagSet("ecs-cli", 0)
setNoPrefixes.String(ecscli.ProfileFlag, profileName, "")
setNoPrefixes.String(ecscli.ClusterFlag, clusterName, "")

context := cli.NewContext(nil, setNoPrefixes, nil)

cfg, err := createECSConfigFromCli(context)
if err != nil {
t.Errorf("Error reading config from rdwr: ", err)
}
if "" != cfg.ComposeProjectNamePrefix {
t.Errorf("ComposeProjectName mismtach in config. Expected empty string Got [%s]", cfg.ComposeProjectNamePrefix)
}
if "" != cfg.ComposeServiceNamePrefix {
t.Errorf("ComposeServiceName mismatch in config. Expected empty string Got [%s]", cfg.ComposeServiceNamePrefix)
}
if "" != cfg.CFNStackNamePrefix {
t.Errorf("CFNStackNamePrefix mismatch in config. Expected empty string Got [%s]", cfg.CFNStackNamePrefix)
}
}
7 changes: 6 additions & 1 deletion ecs-cli/modules/compose/ecs/entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,14 +237,19 @@ func convertMapToSlice(mapItems map[string]bool) []*string {
// getStartedBy returns an auto-generated formatted string
// that can be supplied while starting an ECS task and is used to identify the owner of ECS Task
func getStartedBy(entity ProjectEntity) string {
return composeutils.GetStartedBy(getProjectName(entity))
return composeutils.GetStartedBy(getProjectPrefix(entity), getProjectName(entity))
}

// getProjectName returns the name of the project that was set in the context we are working with
func getProjectName(entity ProjectEntity) string {
return entity.Context().Context.ProjectName
}

// getProjectPrefix returns the prefix for the project name
func getProjectPrefix(entity ProjectEntity) string {
return entity.Context().ECSParams.ComposeProjectNamePrefix
}

// getIdFromArn gets the aws String value of the input arn and returns the id part of the arn
func getIdFromArn(arn *string) string {
return composeutils.GetIdFromArn(aws.StringValue(arn))
Expand Down
9 changes: 5 additions & 4 deletions ecs-cli/modules/compose/ecs/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,24 +100,25 @@ func (p *ecsProject) Parse() error {
return err
}

if err := p.load(context.Context); err != nil {
if err := p.load(context); err != nil {
return err
}

return nil
}

// load parses the compose yml and transforms into task definition
func (p *ecsProject) load(context libcompose.Context) error {
func (p *ecsProject) load(context *Context) error {
logrus.Debug("Parsing the compose yaml...")
configs, err := utils.UnmarshalComposeConfig(context)
configs, err := utils.UnmarshalComposeConfig(context.Context)
if err != nil {
return err
}
p.serviceConfigs = configs

logrus.Debug("Transforming yaml to task definition...")
taskDefinition, err := utils.ConvertToTaskDefinition(context, p.serviceConfigs)
taskDefinitionName := utils.GetTaskDefinitionName(context.ECSParams.ComposeProjectNamePrefix, context.Context.ProjectName)
taskDefinition, err := utils.ConvertToTaskDefinition(taskDefinitionName, context.Context, p.serviceConfigs)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion ecs-cli/modules/compose/ecs/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,5 +345,5 @@ func (s *Service) updateService(count int64) error {

// getServiceName returns an autogenerated name for the service based on the current project context
func (s *Service) getServiceName() string {
return composeutils.GetServiceName(getProjectName(s))
return composeutils.GetServiceName(s.Context().ECSParams.ComposeServiceNamePrefix, getProjectName(s))
}
3 changes: 3 additions & 0 deletions ecs-cli/modules/compose/ecs/task_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
ecsClient "github.com/aws/amazon-ecs-cli/ecs-cli/modules/aws/clients/ecs"
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/aws/clients/ecs/mock"
libcompose "github.com/aws/amazon-ecs-cli/ecs-cli/modules/compose/libcompose"
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/config"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ecs"
Expand Down Expand Up @@ -51,6 +52,7 @@ func TestTaskCreate(t *testing.T) {

context := &Context{
ECSClient: mockEcs,
ECSParams: &config.CliParams{},
}
task := NewTask(context)
task.SetTaskDefinition(&taskDefinition)
Expand Down Expand Up @@ -156,6 +158,7 @@ func testInfo(setupEntity setupEntityForTestInfo, validateFunc validateListTasks
context := &Context{
ECSClient: mockEcs,
EC2Client: mockEc2,
ECSParams: &config.CliParams{},
Context: libcompose.Context{
ProjectName: projectName,
},
Expand Down
3 changes: 1 addition & 2 deletions ecs-cli/modules/compose/ecs/utils/convert_task_definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,13 @@ const (
)

// ConvertToTaskDefinition transforms the yaml configs to its ecs equivalent (task definition)
func ConvertToTaskDefinition(context libcompose.Context,
func ConvertToTaskDefinition(taskDefinitionName string, context libcompose.Context,
serviceConfigs map[string]*libcompose.ServiceConfig) (*ecs.TaskDefinition, error) {

if len(serviceConfigs) == 0 {
return nil, errors.New("cannot create a task definition with no containers; invalid service config")
}

taskDefinitionName := getTaskDefinitionName(context.ProjectName)
containerDefinitions := []*ecs.ContainerDefinition{}
volumes := make(map[string]string) // map with key:=hostSourcePath value:=VolumeName
for name, config := range serviceConfigs {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,13 +309,12 @@ func convertToTaskDefinitionInTest(t *testing.T, name string, serviceConfig *lib
serviceConfigs := make(map[string]*libcompose.ServiceConfig)
serviceConfigs[name] = serviceConfig

projectName := "ProjectName"
taskDefName := "ProjectName"
context := libcompose.Context{
ProjectName: projectName,
EnvironmentLookup: &libcompose.OsEnvLookup{},
ConfigLookup: &libcompose.FileConfigLookup{},
}
taskDefinition, err := ConvertToTaskDefinition(context, serviceConfigs)
taskDefinition, err := ConvertToTaskDefinition(taskDefName, context, serviceConfigs)
if err != nil {
t.Errorf("Expected to convert [%v] serviceConfigs without errors. But got [%v]", serviceConfigs, err)
}
Expand Down
17 changes: 7 additions & 10 deletions ecs-cli/modules/compose/ecs/utils/name.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,13 @@ const (
// changing this will cause user's caches to break.
ProjectTDCache = "tdcache"

// prefix added to all resources created by this application. TODO: Revisit this
ecsComposeProjectPrefix = "ecscompose"

// prefix for the autogenerated volume names in the task definition
ecsVolumeNamePrefix = "volume"
)

// getTaskDefinitionName uses predefined format to generate task definition for this project
func getTaskDefinitionName(projectName string) string {
return fmt.Sprintf("%s-%s", ecsComposeProjectPrefix, projectName)
// GetTaskDefinitionName uses predefined format to generate task definition for this project
func GetTaskDefinitionName(prefix, projectName string) string {
return fmt.Sprintf("%s%s", prefix, projectName)
}

// getVolumeName returns an autogenerated name for the ecs volume
Expand All @@ -41,13 +38,13 @@ func getVolumeName(suffixNum int) string {
}

// GetServiceName returns a service name
func GetServiceName(projectName string) string {
return fmt.Sprintf("%s-service-%s", ecsComposeProjectPrefix, projectName)
func GetServiceName(prefix, projectName string) string {
return fmt.Sprintf("%s%s", prefix, projectName)
}

// GetStartedBy returns an autogenerated name that can be used to represent owner id for tasks
func GetStartedBy(projectName string) string {
return fmt.Sprintf("%s-%s", ecsComposeProjectPrefix, projectName) // TODO, add taskDefHash
func GetStartedBy(prefix, projectName string) string {
return fmt.Sprintf("%s%s", prefix, projectName) // TODO, add taskDefHash
}

// GetFormattedContainerName returns the container name with task id prepended to it
Expand Down
29 changes: 16 additions & 13 deletions ecs-cli/modules/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,20 @@ import (
"os"
"time"

"github.com/aws/amazon-ecs-cli/ecs-cli/modules/cli"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
"github.com/aws/aws-sdk-go/aws/defaults"
)

// TODO: This needs a better home.

// This time.Minute value comes from the SDK defaults package
const (
RegionFlag = "region"
// This time.Minute value comes from the SDK defaults package
eC2RoleProviderExpiryWindow = 5 * time.Minute
ecsSectionKey = "ecs"
composeProjectNamePrefixKey = "compose-project-name-prefix"
composeServiceNamePrefixKey = "compose-service-name-prefix"
cfnStackNamePrefixKey = "cfn-stack-name-prefix"
)

// CliConfig is the top level struct used to map to the ini config.
Expand All @@ -40,11 +42,14 @@ type CliConfig struct {

// SectionKeys is the struct embedded in CliConfig. It groups all the keys in the 'ecs' section in the ini file.
type SectionKeys struct {
Cluster string `ini:"cluster"`
AwsProfile string `ini:"aws_profile"`
Region string `ini:"region"`
AwsAccessKey string `ini:"aws_access_key_id"`
AwsSecretKey string `ini:"aws_secret_access_key"`
Cluster string `ini:"cluster"`
AwsProfile string `ini:"aws_profile"`
Region string `ini:"region"`
AwsAccessKey string `ini:"aws_access_key_id"`
AwsSecretKey string `ini:"aws_secret_access_key"`
ComposeProjectNamePrefix string `ini:"compose-project-name-prefix"`
ComposeServiceNamePrefix string `ini:"compose-service-name-prefix"`
CFNStackNamePrefix string `ini:"cfn-stack-name-prefix"`
}

// NewCliConfig creates a new instance of CliConfig from the cluster name.
Expand All @@ -56,8 +61,7 @@ func NewCliConfig(cluster string) *CliConfig {
func (cfg *CliConfig) ToServiceConfig() (*aws.Config, error) {
region := cfg.getRegion()
if region == "" {
// TODO: Move AWS_REGION to a const.
return nil, fmt.Errorf("Set a region with the --%s flag or AWS_REGION environment variable", RegionFlag)
return nil, fmt.Errorf("Set a region with the --%s flag or %s environment variable", cli.RegionFlag, cli.AwsRegionEnvVar)
}

chainCredentials := credentials.NewChainCredentials(cfg.getCredentialProviders())
Expand Down Expand Up @@ -112,8 +116,7 @@ func (cfg *CliConfig) getRegion() string {
region := cfg.Region
if region == "" {
// Search the chain of environment variables for region.
// TODO: Move these to const's
for _, envVar := range []string{"AWS_REGION", "AWS_DEFAULT_REGION"} {
for _, envVar := range []string{cli.AwsRegionEnvVar, cli.AwsDefaultRegionEnvVar} {
region = os.Getenv(envVar)
if region != "" {
break
Expand Down
Loading

0 comments on commit a3e3eee

Please sign in to comment.