// CreateAutoScaleGroups creates a new AutoScale Group of the given class func CreateAutoScaleGroups(class string, dryRun bool) (err error) { // --dry-run flag if dryRun { terminal.Information("--dry-run flag is set, not making any actual changes!") } // Verify the asg config class input cfg, err := config.LoadAutoscalingGroupClass(class) if err != nil { return err } terminal.Information("Found Autoscaling group class configuration for [" + class + "]") // Verify the launchconfig class input launchConfigurationCfg, err := config.LoadLaunchConfigurationClass(cfg.LaunchConfigurationClass) if err != nil { return err } terminal.Information("Found Launch Configuration class configuration for [" + cfg.LaunchConfigurationClass + "]") // Get the AZs azs, errs := regions.GetAZs() if errs != nil { return errors.New("Error gathering region list") } for region, regionAZs := range azs.GetRegionMap(cfg.AvailabilityZones) { // Verify that the latest Launch Configuration is available in this region lcName := GetLaunchConfigurationName(region, cfg.LaunchConfigurationClass, launchConfigurationCfg.Version) if lcName == "" { return fmt.Errorf("Launch Configuration [%s] version [%d] is not available in [%s]!", cfg.LaunchConfigurationClass, launchConfigurationCfg.Version, region) } terminal.Information(fmt.Sprintf("Found latest Launch Configuration [%s] version [%d] in [%s]", cfg.LaunchConfigurationClass, launchConfigurationCfg.Version, region)) svc := autoscaling.New(session.New(&aws.Config{Region: aws.String(region)})) params := &autoscaling.CreateAutoScalingGroupInput{ AutoScalingGroupName: aws.String(class), MaxSize: aws.Int64(int64(cfg.MaxSize)), MinSize: aws.Int64(int64(cfg.MinSize)), DefaultCooldown: aws.Int64(int64(cfg.DefaultCooldown)), DesiredCapacity: aws.Int64(int64(cfg.DesiredCapacity)), HealthCheckGracePeriod: aws.Int64(int64(cfg.HealthCheckGracePeriod)), HealthCheckType: aws.String(cfg.HealthCheckType), LaunchConfigurationName: aws.String(lcName), // InstanceId: aws.String("XmlStringMaxLen19"), // TODO ? // NewInstancesProtectedFromScaleIn: aws.Bool(true), // TODO ? // PlacementGroup: aws.String("XmlStringMaxLen255"), // TODO ? Tags: []*autoscaling.Tag{ { // Name Key: aws.String("Name"), PropagateAtLaunch: aws.Bool(true), ResourceId: aws.String(class), ResourceType: aws.String("auto-scaling-group"), Value: aws.String(lcName), }, { // Class Key: aws.String("Class"), PropagateAtLaunch: aws.Bool(true), ResourceId: aws.String(class), ResourceType: aws.String("auto-scaling-group"), Value: aws.String(class), }, }, } subList := new(Subnets) var vpcZones []string if cfg.SubnetClass != "" { err := GetRegionSubnets(region, subList, "") if err != nil { return err } } // Set the AZs for _, az := range regionAZs { if !azs.ValidAZ(az) { return cli.NewExitError("Availability Zone ["+az+"] is Invalid!", 1) } terminal.Information("Found Availability Zone [" + az + "]!") params.AvailabilityZones = append(params.AvailabilityZones, aws.String(az)) for _, sub := range *subList { if sub.Class == cfg.SubnetClass && sub.AvailabilityZone == az { vpcZones = append(vpcZones, sub.SubnetID) } } } // Set the VPCZoneIdentifier (SubnetIds seperated by comma) params.VPCZoneIdentifier = aws.String(strings.Join(vpcZones, ", ")) // Set the Load Balancers for _, elb := range cfg.LoadBalancerNames { params.LoadBalancerNames = append(params.LoadBalancerNames, aws.String(elb)) } // Set the Termination Policies for _, terminationPolicy := range cfg.LoadBalancerNames { params.TerminationPolicies = append(params.TerminationPolicies, aws.String(terminationPolicy)) // ?? } // Create it! if !dryRun { _, err := svc.CreateAutoScalingGroup(params) if err != nil { if awsErr, ok := err.(awserr.Error); ok { return errors.New(awsErr.Message()) } return err } terminal.Information("Created AutoScaling Group [" + aws.StringValue(params.AutoScalingGroupName) + "] in [" + region + "]!") terminal.Information("Done!") } else { fmt.Println(params) } } return nil }
// Private function without the confirmation terminal prompts func updateAutoScaleGroups(asgList *AutoScaleGroups, version string, double, dryRun bool) (err error) { for _, asg := range *asgList { // Get the ASG class config cfg, err := config.LoadAutoscalingGroupClass(asg.Class) if err != nil { return err } terminal.Information("Found Autoscaling group class configuration for [" + asg.Class + "]") // Get the Launch Configuration class config launchConfigurationCfg, err := config.LoadLaunchConfigurationClass(cfg.LaunchConfigurationClass) if err != nil { return err } terminal.Information("Found Launch Configuration class configuration for [" + cfg.LaunchConfigurationClass + "]") // Get the AZs azs, errs := regions.GetAZs() if errs != nil { return errors.New("Error gathering region list") } for region, regionAZs := range azs.GetRegionMap(cfg.AvailabilityZones) { // TODO check if exists yet ? // Verify that the latest Launch Configuration is available in this region lcName := GetLaunchConfigurationName(region, cfg.LaunchConfigurationClass, launchConfigurationCfg.Version) if lcName == "" { return fmt.Errorf("Launch Configuration [%s] version [%d] is not available in [%s]!", cfg.LaunchConfigurationClass, launchConfigurationCfg.Version, region) } terminal.Information(fmt.Sprintf("Found latest Launch Configuration [%s] version [%d] in [%s]", cfg.LaunchConfigurationClass, launchConfigurationCfg.Version, asg.Region)) svc := autoscaling.New(session.New(&aws.Config{Region: aws.String(region)})) params := &autoscaling.UpdateAutoScalingGroupInput{ AutoScalingGroupName: aws.String(asg.Name), AvailabilityZones: []*string{ aws.String("XmlStringMaxLen255"), // Required // More values... }, DefaultCooldown: aws.Int64(int64(cfg.DefaultCooldown)), DesiredCapacity: aws.Int64(int64(cfg.DesiredCapacity)), HealthCheckGracePeriod: aws.Int64(int64(cfg.HealthCheckGracePeriod)), HealthCheckType: aws.String(cfg.HealthCheckType), LaunchConfigurationName: aws.String(lcName), MaxSize: aws.Int64(int64(asg.MaxSize)), MinSize: aws.Int64(int64(asg.MinSize)), //NewInstancesProtectedFromScaleIn: aws.Bool(true), // TODO? //PlacementGroup: aws.String("XmlStringMaxLen255"), // TODO } subList := new(Subnets) var vpcZones []string if cfg.SubnetClass != "" { err := GetRegionSubnets(region, subList, "") if err != nil { return err } } // Set the AZs for _, az := range regionAZs { if !azs.ValidAZ(az) { return cli.NewExitError("Availability Zone ["+az+"] is Invalid!", 1) } terminal.Information("Found Availability Zone [" + az + "]!") params.AvailabilityZones = append(params.AvailabilityZones, aws.String(az)) for _, sub := range *subList { if sub.Class == cfg.SubnetClass && sub.AvailabilityZone == az { vpcZones = append(vpcZones, sub.SubnetID) } } } // Set the VPCZoneIdentifier (SubnetIds seperated by comma) params.VPCZoneIdentifier = aws.String(strings.Join(vpcZones, ", ")) // Set the Termination Policies for _, terminationPolicy := range cfg.LoadBalancerNames { params.TerminationPolicies = append(params.TerminationPolicies, aws.String(terminationPolicy)) // ?? } // Update it! if !dryRun { _, err := svc.UpdateAutoScalingGroup(params) if err != nil { if awsErr, ok := err.(awserr.Error); ok { return errors.New(awsErr.Message()) } return err } terminal.Information("Updated AutoScaling Group [" + asg.Name + "] in [" + region + "]!") } else { fmt.Println(params) } } } return nil }
// CreateLaunchConfigurations creates a new Launch Configuration of a given class func CreateLaunchConfigurations(class string, dryRun bool) (err error) { // Verify the launch config class input cfg, err := config.LoadLaunchConfigurationClass(class) if err != nil { return err } terminal.Information("Found Launch Configuration class configuration for [" + class + "]") // Instance Class Config instanceCfg, err := config.LoadInstanceClass(cfg.InstanceClass) if err != nil { return err } terminal.Information("Found Instance class configuration for [" + cfg.InstanceClass + "]") // Increment the version terminal.Information(fmt.Sprintf("Previous version of launch configuration is [%d]", cfg.Version)) cfg.Increment(class) terminal.Information(fmt.Sprintf("New version of launch configuration is [%d]", cfg.Version)) params := &autoscaling.CreateLaunchConfigurationInput{ LaunchConfigurationName: aws.String(fmt.Sprintf("%s-v%d", class, cfg.Version)), AssociatePublicIpAddress: aws.Bool(instanceCfg.PublicIPAddress), InstanceMonitoring: &autoscaling.InstanceMonitoring{ Enabled: aws.Bool(instanceCfg.Monitoring), }, InstanceType: aws.String(instanceCfg.InstanceType), UserData: aws.String(instanceCfg.UserData), //KernelId: aws.String("XmlStringMaxLen255"), //PlacementTenancy: aws.String("XmlStringMaxLen64"), //RamdiskId: aws.String("XmlStringMaxLen255"), //SpotPrice: aws.String("SpotPrice"), //ClassicLinkVPCId: aws.String("XmlStringMaxLen255"), //ClassicLinkVPCSecurityGroups: []*string{ //aws.String("XmlStringMaxLen255"), //}, } // IAM Profile if len(instanceCfg.IAMUser) > 0 { iam, err := GetIAMUser(instanceCfg.IAMUser) if err != nil { return err } terminal.Information("Found IAM User [" + iam.UserName + "]") params.IamInstanceProfile = aws.String(iam.Arn) } for _, region := range cfg.Regions { if !regions.ValidRegion(region) { return errors.New("Region [" + region + "] is not valid!") } // EBS ebsVolumes := make([]*autoscaling.BlockDeviceMapping, len(instanceCfg.EBSVolumes)) for i, ebsClass := range instanceCfg.EBSVolumes { volCfg, err := config.LoadVolumeClass(ebsClass) if err != nil { return err } terminal.Information("Found Volume Class Configuration for [" + ebsClass + "]") latestSnapshot, err := GetLatestSnapshotByTag(region, "Class", volCfg.Snapshot) if err != nil { return err } terminal.Information("Found Snapshot [" + latestSnapshot.SnapshotID + "] with class [" + latestSnapshot.Class + "] created [" + latestSnapshot.CreatedHuman + "]") ebsVolumes[i] = &autoscaling.BlockDeviceMapping{ DeviceName: aws.String(volCfg.DeviceName), Ebs: &autoscaling.Ebs{ DeleteOnTermination: aws.Bool(volCfg.DeleteOnTermination), SnapshotId: aws.String(latestSnapshot.SnapshotID), VolumeSize: aws.Int64(int64(volCfg.VolumeSize)), VolumeType: aws.String(volCfg.VolumeType), //Encrypted: aws.Bool(volCfg.Encrypted), }, //NoDevice: aws.String("String"), //VirtualName: aws.String("String"), } if volCfg.VolumeType == "io1" { ebsVolumes[i].Ebs.Iops = aws.Int64(int64(volCfg.Iops)) } } // EBS Optimized if instanceCfg.EbsOptimized { terminal.Information("Launching as EBS Optimized") params.EbsOptimized = aws.Bool(instanceCfg.EbsOptimized) } params.BlockDeviceMappings = ebsVolumes // AMI ami, err := GetLatestImageByTag(region, "Class", instanceCfg.AMI) if err != nil { return err } terminal.Information("Found AMI [" + ami.ImageID + "] with class [" + ami.Class + "] created [" + ami.CreatedHuman + "]") params.ImageId = aws.String(ami.ImageID) // KeyPair keyPair, err := GetKeyPairByName(region, instanceCfg.KeyName) if err != nil { return err } terminal.Information("Found KeyPair [" + keyPair.KeyName + "] in [" + keyPair.Region + "]") params.KeyName = aws.String(keyPair.KeyName) // VPC / Subnet var vpc Vpc var subnet Subnet secGroupIds := make([]*string, len(instanceCfg.SecurityGroups)) if instanceCfg.Vpc != "" && instanceCfg.Subnet != "" { // VPC vpc, err = GetVpcByTag(region, "Class", instanceCfg.Vpc) if err != nil { return err } terminal.Information("Found VPC [" + vpc.VpcID + "] in Region [" + region + "]") // Subnet subnet, err = vpc.GetVpcSubnetByTag("Class", instanceCfg.Subnet) if err != nil { return err } terminal.Information("Found Subnet [" + subnet.SubnetID + "] in VPC [" + subnet.VpcID + "]") // VPC Security Groups secGroups, err := vpc.GetVpcSecurityGroupByTagMulti("Class", instanceCfg.SecurityGroups) if err != nil { return err } for i, secGroup := range secGroups { terminal.Information("Found VPC Security Group [" + secGroup.GroupID + "] with name [" + secGroup.Name + "]") secGroupIds[i] = aws.String(secGroup.GroupID) } } else { terminal.Information("No VPC and/or Subnet specified for instance Class [" + class + "]") // EC2-Classic security groups secGroups, err := GetSecurityGroupByTagMulti(region, "Class", instanceCfg.SecurityGroups) if err != nil { return err } for i, secGroup := range secGroups { terminal.Information("Found Security Group [" + secGroup.GroupID + "] with name [" + secGroup.Name + "]") secGroupIds[i] = aws.String(secGroup.GroupID) } } params.SecurityGroups = secGroupIds svc := autoscaling.New(session.New(&aws.Config{Region: aws.String(region)})) _, err = svc.CreateLaunchConfiguration(params) if err != nil { if awsErr, ok := err.(awserr.Error); ok { return errors.New(awsErr.Message()) } return err } } // Rotate out older launch configurations if cfg.Retain > 1 { err := RotateLaunchConfigurations(class, cfg, dryRun) if err != nil { terminal.ShowErrorMessage(fmt.Sprintf("Error rotating [%s] launch configurations!", class), err.Error()) return err } } return nil }