func createCluster()

in ecs-cli/modules/cli/cluster/cluster_app.go [201:383]


func createCluster(context *cli.Context, awsClients *AWSClients, commandConfig *config.CommandConfig) error {
	var err error

	ecsClient := awsClients.ECSClient
	cfnClient := awsClients.CFNClient
	metadataClient := awsClients.AMIMetadataClient

	// Check if cluster is specified
	if commandConfig.Cluster == "" {
		return clusterNotSetError()
	}

	if context.Bool(flags.EmptyFlag) {
		err = createEmptyCluster(context, ecsClient, cfnClient, commandConfig)
		if err != nil {
			return err
		}
		return nil
	}

	launchType := commandConfig.LaunchType
	if launchType == "" {
		launchType = config.LaunchTypeDefault
	}

	// InstanceRole not needed when creating empty cluster for Fargate tasks
	if launchType == config.LaunchTypeEC2 {
		if err := validateInstanceRole(context); err != nil {
			return err
		}
		// Display warning if keypair not specified
		if context.String(flags.KeypairNameFlag) == "" {
			logrus.Warn("You will not be able to SSH into your EC2 instances without a key pair.")
		}

	}

	// Check if cfn stack already exists
	stackName := commandConfig.CFNStackName
	var deleteStack bool
	if err = cfnClient.ValidateStackExists(stackName); err == nil {
		if !isForceSet(context) {
			return fmt.Errorf("A CloudFormation stack already exists for the cluster '%s'. Please specify '--%s' to clean up your existing resources", commandConfig.Cluster, flags.ForceFlag)
		}
		deleteStack = true
	}

	tags := make([]*ecs.Tag, 0)
	if tagVal := context.String(flags.ResourceTagsFlag); tagVal != "" {
		tags, err = utils.ParseTags(tagVal, tags)
		if err != nil {
			return err
		}
	}

	var containerInstanceTaggingSupported bool

	if len(tags) > 0 {
		// determine if container instance tagging is supported
		containerInstanceTaggingSupported, err = canEnableContainerInstanceTagging(awsClients.ECSClient)
		if err != nil {
			return err
		}
	}

	// Populate cfn params
	var cfnParams *cloudformation.CfnStackParams
	if containerInstanceTaggingSupported {
		cfnParams, err = cliFlagsToCfnStackParams(context, commandConfig.Cluster, launchType, tags)
	} else {
		cfnParams, err = cliFlagsToCfnStackParams(context, commandConfig.Cluster, launchType, nil)
	}
	if err != nil {
		return err
	}

	cfnParams.Add(ParameterKeyCluster, commandConfig.Cluster)
	if context.Bool(flags.NoAutoAssignPublicIPAddressFlag) {
		cfnParams.Add(ParameterKeyAssociatePublicIPAddress, "false")
	}

	if launchType == config.LaunchTypeFargate {
		cfnParams.Add(ParameterKeyIsFargate, "true")
	}

	// Check if vpc and AZs are not both specified.
	if validateMutuallyExclusiveParams(cfnParams, ParameterKeyVPCAzs, ParameterKeyVpcId) {
		return fmt.Errorf("You can only specify '--%s' or '--%s'", flags.VpcIdFlag, flags.VpcAzFlag)
	}

	// Check that user data is not specified with Fargate
	if validateMutuallyExclusiveParams(cfnParams, ParameterKeyIsFargate, ParameterKeyUserData) {
		return fmt.Errorf("You can only specify '--%s' with the EC2 launch type", flags.UserDataFlag)
	}

	// Check if 2 AZs are specified
	if validateCommaSeparatedParam(cfnParams, ParameterKeyVPCAzs, 2, 2) {
		return fmt.Errorf("You must specify 2 comma-separated availability zones with the '--%s' flag", flags.VpcAzFlag)
	}

	// Check if more than one custom instance role is specified
	if validateCommaSeparatedParam(cfnParams, ParameterKeyInstanceRole, 1, 1) {
		return fmt.Errorf("You can only specify one instance role name with the '--%s' flag", flags.InstanceRoleFlag)
	}

	// Check if vpc exists when security group is specified
	if validateDependentParams(cfnParams, ParameterKeySecurityGroup, ParameterKeyVpcId) {
		return fmt.Errorf("You have selected a security group. Please specify a VPC with the '--%s' flag", flags.VpcIdFlag)
	}

	// Check if subnets exists when vpc is specified
	if validateDependentParams(cfnParams, ParameterKeyVpcId, ParameterKeySubnetIds) {
		return fmt.Errorf("You have selected a VPC. Please specify 2 comma-separated subnets with the '--%s' flag", flags.SubnetIdsFlag)
	}

	// Check if vpc exists when subnets is specified
	if validateDependentParams(cfnParams, ParameterKeySubnetIds, ParameterKeyVpcId) {
		return fmt.Errorf("You have selected subnets. Please specify a VPC with the '--%s' flag", flags.VpcIdFlag)
	}

	if launchType == config.LaunchTypeEC2 {
		instanceType, err := getInstanceType(cfnParams)
		if err != nil {
			return err
		}
		supportedInstanceTypes, err := awsClients.EC2Client.DescribeInstanceTypeOfferings(commandConfig.Region())
		if err != nil {
			return fmt.Errorf("describe instance type offerings: %w", err)
		}

		if err = validateInstanceType(instanceType, supportedInstanceTypes); err != nil {
			// if we detect the default value is unsupported then we'll suggest to the user overriding the value with the appropriate flag
			if instanceType == cloudformation.DefaultECSInstanceType {
				logrus.Warnf("Default instance type %s not supported in region %s. Override the default instance type with the --%s flag and provide a supported value.",
					instanceType, commandConfig.Region(), flags.InstanceTypeFlag)
			}
			return fmt.Errorf(instanceTypeUnsupportedFmt, instanceType, commandConfig.Region(), err)
		}

		// Check if image id was supplied, else populate
		_, err = cfnParams.GetParameter(ParameterKeyAmiId)
		if err == cloudformation.ParameterNotFoundError {
			err := populateAMIID(cfnParams, metadataClient)
			if err != nil {
				return err
			}
		} else if err != nil {
			return err
		}
	}
	if err := cfnParams.Validate(); err != nil {
		return err
	}

	// Create ECS cluster
	if _, err := ecsClient.CreateCluster(commandConfig.Cluster, tags); err != nil {
		return err
	}

	// Delete cfn stack
	if deleteStack {
		if err := cfnClient.DeleteStack(stackName); err != nil {
			return err
		}
		logrus.Info("Waiting for your CloudFormation stack resources to be deleted...")
		if err := cfnClient.WaitUntilDeleteComplete(stackName); err != nil {
			return err
		}
	}
	// Create cfn stack
	template, err := cloudformation.GetClusterTemplate(tags, stackName)
	if err != nil {
		return errors.Wrapf(err, "Error building cloudformation template")
	}

	if _, err := cfnClient.CreateStack(template, stackName, true, cfnParams, convertToCFNTags(tags)); err != nil {
		return err
	}

	logrus.Info("Waiting for your cluster resources to be created...")
	// Wait for stack creation
	return cfnClient.WaitUntilCreateComplete(stackName)
}