Esempio n. 1
0
// 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

}
Esempio n. 2
0
// 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
}
Esempio n. 3
0
// 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
}