Example #1
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
}
Example #2
0
// LaunchInstance Launches a new EC2 Instance
func LaunchInstance(class, sequence, az string, dryRun bool) error {

	// --dry-run flag
	if dryRun {
		terminal.Information("--dry-run flag is set, not making any actual changes!")
	}

	// Instance Class Config
	instanceCfg, err := config.LoadInstanceClass(class)
	if err != nil {
		return err
	}

	terminal.Information("Found Instance class configuration for [" + class + "]!")

	// AZ
	azs, _ := regions.GetAZs()
	if !azs.ValidAZ(az) {
		return cli.NewExitError("Availability Zone ["+az+"] is Invalid!", 1)
	}

	terminal.Information("Found Availability Zone [" + az + "]!")

	region := azs.GetRegion(az)

	// 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 + "]!")

	// EBS
	ebsVolumes := make([]*ec2.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] = &ec2.BlockDeviceMapping{
			DeviceName: aws.String(volCfg.DeviceName),
			Ebs: &ec2.EbsBlockDevice{
				DeleteOnTermination: aws.Bool(volCfg.DeleteOnTermination),
				//Encrypted:           aws.Bool(volCfg.Encrypted),
				SnapshotId: aws.String(latestSnapshot.SnapshotID),
				VolumeSize: aws.Int64(int64(volCfg.VolumeSize)),
				VolumeType: aws.String(volCfg.VolumeType),
			},
			//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")
	}

	// IAM Profile
	var iam IAMUser
	if len(instanceCfg.IAMUser) > 0 {
		iam, err := GetIAMUser(instanceCfg.IAMUser)
		if err != nil {
			return err
		}

		terminal.Information("Found IAM User [" + iam.UserName + "]!")

	}

	// KeyPair
	keyPair, err := GetKeyPairByName(region, instanceCfg.KeyName)
	if err != nil {
		return err
	}

	terminal.Information("Found KeyPair [" + keyPair.KeyName + "] in [" + keyPair.Region + "]!")

	// Network Interfaces

	// Placement ??

	// VPC / Subnet
	var vpc Vpc
	var subnet Subnet
	var subnetID string
	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
		}

		subnetID = subnet.SubnetID
		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)
		}

	}

	// User Data

	// ================================================================
	// ================================================================
	// ================================================================

	svc := ec2.New(session.New(&aws.Config{Region: aws.String(region)}))

	params := &ec2.RunInstancesInput{
		ImageId:             aws.String(ami.ImageID),
		MaxCount:            aws.Int64(1),
		MinCount:            aws.Int64(1),
		BlockDeviceMappings: ebsVolumes,
		DryRun:              aws.Bool(dryRun),
		EbsOptimized:        aws.Bool(instanceCfg.EbsOptimized),
		IamInstanceProfile: &ec2.IamInstanceProfileSpecification{
			Arn:  aws.String(iam.Arn),
			Name: aws.String(iam.UserName),
		},
		InstanceInitiatedShutdownBehavior: aws.String(instanceCfg.ShutdownBehavior),
		InstanceType:                      aws.String(instanceCfg.InstanceType),
		KeyName:                           aws.String(keyPair.KeyName),
		Monitoring: &ec2.RunInstancesMonitoringEnabled{
			Enabled: aws.Bool(instanceCfg.Monitoring),
		},
		NetworkInterfaces: []*ec2.InstanceNetworkInterfaceSpecification{ // only needed when we launch with a public ip. TODO
			{
			/*
				AssociatePublicIpAddress: aws.Bool(instanceCfg.PublicIpAddress),
				DeleteOnTermination:      aws.Bool(true),
				//Description:              aws.String("String"),
				DeviceIndex: aws.Int64(0),
				Groups: []*string{
					aws.String("String"), // Required
				},

					PrivateIpAddress:   aws.String("String"),
					PrivateIpAddresses: []*ec2.PrivateIpAddressSpecification{
						{ // Required
							PrivateIpAddress: aws.String("String"), // Required
							Primary:          aws.Bool(true),
						},
					},
					SecondaryPrivateIpAddressCount: aws.Int64(1),

					SubnetId:                       aws.String("String"),
			*/
			},
		},
		/*
			Placement: &ec2.Placement{ // havent played around with placements yet, TODO?
				Affinity:         aws.String("String"),
				AvailabilityZone: aws.String("String"),
				GroupName:        aws.String("String"),
				HostId:           aws.String("String"),
				Tenancy:          aws.String("Tenancy"),
			},
		*/
		// PrivateIpAddress: aws.String("String"),
		SecurityGroupIds: secGroupIds,
		SubnetId:         aws.String(subnetID),
		UserData:         aws.String(base64.StdEncoding.EncodeToString([]byte(instanceCfg.UserData))),

		//KernelId:         aws.String("String"),
		//RamdiskId:        aws.String("String"),
	}

	launchInstanceResp, err := svc.RunInstances(params)

	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok {
			return errors.New(awsErr.Message())
		}
		return err
	}

	// Add Tags
	instanceTagsParams := &ec2.CreateTagsInput{
		Resources: []*string{
			launchInstanceResp.Instances[0].InstanceId,
		},
		Tags: []*ec2.Tag{
			{
				Key:   aws.String("Name"),
				Value: aws.String(class + sequence),
			},
			{
				Key:   aws.String("Sequence"),
				Value: aws.String(sequence),
			},
			{
				Key:   aws.String("Class"),
				Value: aws.String(class),
			},
		},
		DryRun: aws.Bool(dryRun),
	}
	_, err = svc.CreateTags(instanceTagsParams)

	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok {
			return errors.New(awsErr.Message())
		}
		return err
	}

	terminal.Information("Finished Launching Instance!")

	inst := make(Instances, 1)
	inst[1].Marshal(launchInstanceResp.Instances[0], region, &Subnets{subnet}, &Vpcs{vpc}, &Images{ami})

	inst.PrintTable()

	// ================================================================
	// ================================================================
	// ================================================================

	return nil
}