diff --git a/vmlifecycle/configfetchers/aws.go b/vmlifecycle/configfetchers/aws.go index 50363b80..2683d35d 100644 --- a/vmlifecycle/configfetchers/aws.go +++ b/vmlifecycle/configfetchers/aws.go @@ -93,6 +93,7 @@ func (a *AWSConfigFetcher) createConfig(output *ec2.DescribeInstancesOutput, vol PrivateIP: *instance.PrivateIpAddress, InstanceType: *instance.InstanceType, BootDiskSize: strconv.Itoa(int(*volumesOutput.Volumes[0].Size)), + BootDiskType: *volumesOutput.Volumes[0].VolumeType, }, }, } @@ -103,4 +104,4 @@ func (a *AWSConfigFetcher) createConfig(output *ec2.DescribeInstancesOutput, vol } return opsmanConfig -} +} \ No newline at end of file diff --git a/vmlifecycle/configfetchers/aws_test.go b/vmlifecycle/configfetchers/aws_test.go index fac987fd..5ea5ad28 100644 --- a/vmlifecycle/configfetchers/aws_test.go +++ b/vmlifecycle/configfetchers/aws_test.go @@ -42,6 +42,7 @@ var _ = Describe("aws", func() { PrivateIP: "5.6.7.8", VMName: "opsman-vm", BootDiskSize: "160", + BootDiskType: "gp3", InstanceType: "some-instance-type", }, }, @@ -94,7 +95,8 @@ var _ = Describe("aws", func() { return &ec2.DescribeVolumesOutput{ Volumes: []*ec2.Volume{{ - Size: aws.Int64(160), + Size: aws.Int64(160), + VolumeType: aws.String("gp3"), }}, }, nil } @@ -188,4 +190,4 @@ var _ = Describe("aws", func() { Expect(err).To(HaveOccurred()) }) }) -}) +}) \ No newline at end of file diff --git a/vmlifecycle/vmmanagers/aws.go b/vmlifecycle/vmmanagers/aws.go index fc85ada7..e600676f 100644 --- a/vmlifecycle/vmmanagers/aws.go +++ b/vmlifecycle/vmmanagers/aws.go @@ -37,6 +37,7 @@ type AWSConfig struct { PrivateIP string `yaml:"private_ip" validate:"omitempty,ip"` VMName string `yaml:"vm_name"` BootDiskSize string `yaml:"boot_disk_size"` + BootDiskType string `yaml:"boot_disk_type"` InstanceType string `yaml:"instance_type"` Tags map[string]string `yaml:"tags"` AuthenticationType string `yaml:"authentication_type"` @@ -131,16 +132,6 @@ func (a *AWSVMManager) CreateVM() (Status, StateInfo, error) { } latestState.ID = instanceID - volumeID, err := a.getVolumeID(instanceID) - if err != nil { - return Incomplete, latestState, err - } - - err = a.modifyVolume(volumeID) - if err != nil { - return Incomplete, latestState, err - } - if iaasConfig.PublicIP != "" { addressID, err := a.getIPAddressID() if err != nil { @@ -188,6 +179,9 @@ func (a *AWSVMManager) addDefaultConfigFields() { if a.Config.OpsmanConfig.AWS.BootDiskSize == "" { a.Config.OpsmanConfig.AWS.BootDiskSize = "200" } + if a.Config.OpsmanConfig.AWS.BootDiskType == "" { + a.Config.OpsmanConfig.AWS.BootDiskType = "gp2" + } if a.Config.OpsmanConfig.AWS.InstanceType == "" { a.Config.OpsmanConfig.AWS.InstanceType = "m5.large" } @@ -247,6 +241,10 @@ func (a *AWSVMManager) createVM(ami string) (string, error) { ), "--image-id", ami, "--subnet-id", config.VPCSubnetId, + "--block-device-mappings", fmt.Sprintf( + "[{\"DeviceName\": \"/dev/xvda\", \"Ebs\": {\"VolumeType\": \"%s\", \"VolumeSize\": %s}}]", + a.Config.OpsmanConfig.AWS.BootDiskType, a.Config.OpsmanConfig.AWS.BootDiskSize, + ), "--security-group-ids", } for _, sgID := range config.SecurityGroupIds { @@ -272,64 +270,6 @@ func (a *AWSVMManager) createVM(ami string) (string, error) { return cleanupString(instanceID.String()), nil } -func (a *AWSVMManager) getVolumeID(instanceID string) (volumeID string, err error) { - // wait until available - for { - volumeID, _, err := a.ExecuteWithInstanceProfile(a.addEnvVars(), - []interface{}{ - "ec2", "describe-volumes", - "--filters", - fmt.Sprintf("Name=attachment.instance-id,Values=%s", instanceID), - "Name=attachment.status,Values=attached", - "Name=status,Values=in-use", - "--query", "Volumes[0].VolumeId", - }) - if err != nil { - return "", fmt.Errorf("aws error finding volumeID of the root device: %s", err) - } - if !strings.Contains(volumeID.String(), "null") { - return cleanupString(volumeID.String()), nil - } - _, _ = a.stderr.Write([]byte(fmt.Sprintf("volume not available yet, polling in %s\n", a.pollingInterval))) - time.Sleep(a.pollingInterval) - } -} - -func (a *AWSVMManager) modifyVolume(volumeID string) error { - // wait until available - var currentRetryCount float64 - timeoutTime := time.Now().Add(time.Hour) - for { - _, _, err := a.ExecuteWithInstanceProfile(a.addEnvVars(), - []interface{}{ - "ec2", "modify-volume", - "--volume-id", volumeID, - "--size", a.Config.OpsmanConfig.AWS.BootDiskSize, - }) - - if err == nil { - break - } - - if !strings.Contains(err.Error(), "exit status 255") { - return fmt.Errorf("aws error modifying size of root device: %s", err) - } - - // Encrypted volume is not available - waitTime := getExponentialWaitTime(currentRetryCount, a.pollingInterval) - _, _ = a.stderr.Write([]byte(fmt.Sprintf("volume not available to configure yet, polling in %s\n", waitTime))) - - if time.Now().After(timeoutTime) { - return errors.New("failed to modify VM disk volume within the hour allowed") - } - - time.Sleep(waitTime) - currentRetryCount += 1 - } - - return nil -} - func (a *AWSVMManager) getIPAddressID() (ipAddress string, err error) { allocationID, _, err := a.ExecuteWithInstanceProfile(a.addEnvVars(), []interface{}{ @@ -530,4 +470,4 @@ func getExponentialWaitTime(currentRetryCount float64, pollingMultiplier time.Du } return waitTime -} +} \ No newline at end of file diff --git a/vmlifecycle/vmmanagers/aws_test.go b/vmlifecycle/vmmanagers/aws_test.go index 1988fb58..2ef7ad74 100644 --- a/vmlifecycle/vmmanagers/aws_test.go +++ b/vmlifecycle/vmmanagers/aws_test.go @@ -49,6 +49,7 @@ opsman-configuration: key_pair_name: superuser iam_instance_profile_name: awesome-profile boot_disk_size: 200 + boot_disk_type: gp3 public_ip: %s private_ip: %s instance_type: m3.large @@ -60,11 +61,9 @@ opsman-configuration: command, runner := createCommand(fmt.Sprintf(configStrTemplate, accessKey, secretAccessKey, assumeRole, region, publicIP, privateIP)) runner.ExecuteWithEnvVarsReturnsOnCall(0, bytes.NewBufferString(fmt.Sprintf("\"%s\"\r\n", vmID)), nil, nil) - runner.ExecuteWithEnvVarsReturnsOnCall(1, bytes.NewBufferString("null\r\n"), bytes.NewBufferString("TestStatus: pending creation"), nil) - runner.ExecuteWithEnvVarsReturnsOnCall(2, bytes.NewBufferString("\"vol-0cf5b911680a78bb9\"\r\n"), bytes.NewBufferString("TestStatus: volume created"), nil) - runner.ExecuteWithEnvVarsReturnsOnCall(4, bytes.NewBufferString("\"eipalloc-18643c24\"\r\n"), nil, nil) - runner.ExecuteWithEnvVarsReturnsOnCall(7, bytes.NewBufferString("\"stopping\"\r\n"), nil, nil) - runner.ExecuteWithEnvVarsReturnsOnCall(8, bytes.NewBufferString("\"stopped\"\r\n"), nil, nil) + runner.ExecuteWithEnvVarsReturnsOnCall(1, bytes.NewBufferString("\"eipalloc-18643c24\"\r\n"), nil, nil) + runner.ExecuteWithEnvVarsReturnsOnCall(4, bytes.NewBufferString("\"stopping\"\r\n"), nil, nil) + runner.ExecuteWithEnvVarsReturnsOnCall(5, bytes.NewBufferString("\"stopped\"\r\n"), nil, nil) return command, runner } @@ -75,13 +74,14 @@ opsman-configuration: Context("create vm", func() { Context("with a config with all values filled out", func() { It("calls aws with correct cli arguments and does not describe instances", func() { - numCLICalls := 10 + numCLICalls := 7 commands := [][]string{ - { //1 + { `ec2`, `run-instances`, `--tag-specifications`, `ResourceType=instance,Tags=[{Key=Name,Value=awesome-vm},{Key=Owner,Value=DbAdmin},{Key=Stack,Value=Test}]`, `--image-id`, `ami-789dc900`, `--subnet-id`, `awesome-subnet`, + `--block-device-mappings`, `[{"DeviceName": "/dev/xvda", "Ebs": {"VolumeType": "gp3", "VolumeSize": 200}}]`, `--security-group-ids`, `sg-awesome`, `sg-great`, `--count`, `1`, `--instance-type`, "m3.large", @@ -91,50 +91,31 @@ opsman-configuration: `--query`, `Instances[0].InstanceId`, `--private-ip-address`, `10.10.10.10`, }, - { //2 - `ec2`, `describe-volumes`, - `--filters`, `Name=attachment.instance-id,Values=i-0016d0fe3a11c73c2`, - `Name=attachment.status,Values=attached`, - `Name=status,Values=in-use`, - `--query`, `Volumes[0].VolumeId`, - }, - { //3 - `ec2`, `describe-volumes`, - `--filters`, `Name=attachment.instance-id,Values=i-0016d0fe3a11c73c2`, - `Name=attachment.status,Values=attached`, - `Name=status,Values=in-use`, - `--query`, `Volumes[0].VolumeId`, - }, - { //4 - `ec2`, `modify-volume`, - `--volume-id`, `vol-0cf5b911680a78bb9`, - `--size`, `200`, - }, - { //5 + { `ec2`, `describe-addresses`, `--filters`, `Name=public-ip,Values=1.2.3.4`, `--query`, `Addresses[0].AllocationId`, }, - { //6 + { `ec2`, `associate-address`, `--allocation-id`, `eipalloc-18643c24`, `--instance-id`, `i-0016d0fe3a11c73c2`, }, - { //7 + { `ec2`, `stop-instances`, `--instance-ids`, `i-0016d0fe3a11c73c2`, }, - { //8 + { `ec2`, `describe-instances`, `--instance-ids`, `i-0016d0fe3a11c73c2`, `--query`, `Reservations[*].Instances[*].State.Name`, }, - { //9 + { `ec2`, `describe-instances`, `--instance-ids`, `i-0016d0fe3a11c73c2`, `--query`, `Reservations[*].Instances[*].State.Name`, }, - { //10 + { `ec2`, `start-instances`, `--instance-ids`, `i-0016d0fe3a11c73c2`, }, @@ -174,7 +155,7 @@ opsman-configuration: Expect(invokes).ToNot(HaveLen(0)) for _, args := range invokes { Expect(args[1]).ToNot(ContainElement(MatchRegexp("associate-address"))) - Expect(args[1]).ToNot(ContainElement(MatchRegexp("decsribe-address"))) + Expect(args[1]).ToNot(ContainElement(MatchRegexp("describe-address"))) } }) }) @@ -269,11 +250,6 @@ credential_source = Ec2InstanceMetadata`)), comment) command, runner := createValidCommand("1.2.3.4", "10.10.10.10", "us-west-2") runner.ExecuteWithEnvVarsReturnsOnCall(0, bytes.NewBufferString("terminated\r\n"), nil, nil) runner.ExecuteWithEnvVarsReturnsOnCall(1, bytes.NewBufferString(fmt.Sprintf("\"%s\"\r\n", vmID)), nil, nil) - runner.ExecuteWithEnvVarsReturnsOnCall(2, bytes.NewBufferString("null\r\n"), bytes.NewBufferString("TestStatus: pending creation"), nil) - runner.ExecuteWithEnvVarsReturnsOnCall(3, bytes.NewBufferString("\"vol-0cf5b911680a78bb9\"\r\n"), bytes.NewBufferString("TestStatus: volume created"), nil) - runner.ExecuteWithEnvVarsReturnsOnCall(5, bytes.NewBufferString("\"eipalloc-18643c24\"\r\n"), nil, nil) - runner.ExecuteWithEnvVarsReturnsOnCall(8, bytes.NewBufferString("\"stopping\"\r\n"), nil, nil) - runner.ExecuteWithEnvVarsReturnsOnCall(9, bytes.NewBufferString("\"stopped\"\r\n"), nil, nil) command.State = vmmanagers.StateInfo{ IAAS: "aws", @@ -293,11 +269,6 @@ credential_source = Ec2InstanceMetadata`)), comment) command, runner := createValidCommand("1.2.3.4", "10.10.10.10", "us-west-2") runner.ExecuteWithEnvVarsReturnsOnCall(0, bytes.NewBufferString("[]\n"), nil, nil) runner.ExecuteWithEnvVarsReturnsOnCall(1, bytes.NewBufferString(fmt.Sprintf("\"%s\"\r\n", vmID)), nil, nil) - runner.ExecuteWithEnvVarsReturnsOnCall(2, bytes.NewBufferString("null\r\n"), bytes.NewBufferString("TestStatus: pending creation"), nil) - runner.ExecuteWithEnvVarsReturnsOnCall(3, bytes.NewBufferString("\"vol-0cf5b911680a78bb9\"\r\n"), bytes.NewBufferString("TestStatus: volume created"), nil) - runner.ExecuteWithEnvVarsReturnsOnCall(5, bytes.NewBufferString("\"eipalloc-18643c24\"\r\n"), nil, nil) - runner.ExecuteWithEnvVarsReturnsOnCall(8, bytes.NewBufferString("\"stopping\"\r\n"), nil, nil) - runner.ExecuteWithEnvVarsReturnsOnCall(9, bytes.NewBufferString("\"stopped\"\r\n"), nil, nil) command.State = vmmanagers.StateInfo{ IAAS: "aws", @@ -579,9 +550,8 @@ opsman-configuration: _, args := runner.ExecuteWithEnvVarsArgsForCall(0) Expect(args).To(ContainElement(MatchRegexp("ops-manager-vm"))) Expect(args).To(ContainElement(MatchRegexp("m5\\.large"))) - - _, args = runner.ExecuteWithEnvVarsArgsForCall(2) Expect(args).To(ContainElement(MatchRegexp("200"))) + Expect(args).To(ContainElement(MatchRegexp("gp2"))) }) }) @@ -843,23 +813,17 @@ func happyPathAWSRunnerStubFunc(vmID string) func() (*bytes.Buffer, *bytes.Buffe case 0: return bytes.NewBufferString(fmt.Sprintf("%q\r\n", vmID)), nil, nil case 1: - return bytes.NewBufferString("null\r\n"), bytes.NewBufferString("TestStatus: pending creation"), nil - case 2: - return bytes.NewBufferString("\"vol-0cf5b911680a78bb9\"\r\n"), bytes.NewBufferString("TestStatus: volume created"), nil - case 3: - return nil, nil, nil - case 4: return bytes.NewBufferString("\"eipalloc-18643c24\"\r\n"), nil, nil - case 5, 6: + case 2, 3: return nil, nil, nil - case 7: + case 4: return bytes.NewBufferString("\"stopping\"\r\n"), nil, nil - case 8: + case 5: return bytes.NewBufferString("\"stopped\"\r\n"), nil, nil - case 9, 10: + case 6, 7: return nil, nil, nil default: panic("stub for nth call not implemented") } } -} +} \ No newline at end of file