Skip to content

Commit d379930

Browse files
rabeytasneal
andcommitted
vmlifecycle - aws - add support for setting volume type
default to gp2 (current behavior). allows user to define boot_disk_type in their aws config to instruct the type of volume to use when modifying the volume after vm creation refactoring aws lifecycle create to no longer perform a modify volume after initial creation. pass those disk options to initial creation to allow users to modify the volume if necessary after initial create without a penalty. aws imposes a 6 hour cool down between changes and this allows a user to modify vs the create causing a 6 hour cooldown before the next change is allowed Co-authored-by: Shawn Neal <[email protected]>
1 parent 599130f commit d379930

File tree

4 files changed

+35
-128
lines changed

4 files changed

+35
-128
lines changed

vmlifecycle/configfetchers/aws.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ func (a *AWSConfigFetcher) createConfig(output *ec2.DescribeInstancesOutput, vol
9393
PrivateIP: *instance.PrivateIpAddress,
9494
InstanceType: *instance.InstanceType,
9595
BootDiskSize: strconv.Itoa(int(*volumesOutput.Volumes[0].Size)),
96+
BootDiskType: *volumesOutput.Volumes[0].VolumeType,
9697
},
9798
},
9899
}
@@ -103,4 +104,4 @@ func (a *AWSConfigFetcher) createConfig(output *ec2.DescribeInstancesOutput, vol
103104
}
104105

105106
return opsmanConfig
106-
}
107+
}

vmlifecycle/configfetchers/aws_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ var _ = Describe("aws", func() {
4242
PrivateIP: "5.6.7.8",
4343
VMName: "opsman-vm",
4444
BootDiskSize: "160",
45+
BootDiskType: "gp3",
4546
InstanceType: "some-instance-type",
4647
},
4748
},
@@ -94,7 +95,8 @@ var _ = Describe("aws", func() {
9495

9596
return &ec2.DescribeVolumesOutput{
9697
Volumes: []*ec2.Volume{{
97-
Size: aws.Int64(160),
98+
Size: aws.Int64(160),
99+
VolumeType: aws.String("gp3"),
98100
}},
99101
}, nil
100102
}
@@ -188,4 +190,4 @@ var _ = Describe("aws", func() {
188190
Expect(err).To(HaveOccurred())
189191
})
190192
})
191-
})
193+
})

vmlifecycle/vmmanagers/aws.go

Lines changed: 9 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type AWSConfig struct {
3737
PrivateIP string `yaml:"private_ip" validate:"omitempty,ip"`
3838
VMName string `yaml:"vm_name"`
3939
BootDiskSize string `yaml:"boot_disk_size"`
40+
BootDiskType string `yaml:"boot_disk_type"`
4041
InstanceType string `yaml:"instance_type"`
4142
Tags map[string]string `yaml:"tags"`
4243
AuthenticationType string `yaml:"authentication_type"`
@@ -131,16 +132,6 @@ func (a *AWSVMManager) CreateVM() (Status, StateInfo, error) {
131132
}
132133
latestState.ID = instanceID
133134

134-
volumeID, err := a.getVolumeID(instanceID)
135-
if err != nil {
136-
return Incomplete, latestState, err
137-
}
138-
139-
err = a.modifyVolume(volumeID)
140-
if err != nil {
141-
return Incomplete, latestState, err
142-
}
143-
144135
if iaasConfig.PublicIP != "" {
145136
addressID, err := a.getIPAddressID()
146137
if err != nil {
@@ -188,6 +179,9 @@ func (a *AWSVMManager) addDefaultConfigFields() {
188179
if a.Config.OpsmanConfig.AWS.BootDiskSize == "" {
189180
a.Config.OpsmanConfig.AWS.BootDiskSize = "200"
190181
}
182+
if a.Config.OpsmanConfig.AWS.BootDiskType == "" {
183+
a.Config.OpsmanConfig.AWS.BootDiskType = "gp2"
184+
}
191185
if a.Config.OpsmanConfig.AWS.InstanceType == "" {
192186
a.Config.OpsmanConfig.AWS.InstanceType = "m5.large"
193187
}
@@ -247,6 +241,10 @@ func (a *AWSVMManager) createVM(ami string) (string, error) {
247241
),
248242
"--image-id", ami,
249243
"--subnet-id", config.VPCSubnetId,
244+
"--block-device-mappings", fmt.Sprintf(
245+
"[{\"DeviceName\": \"/dev/xvda\", \"Ebs\": {\"VolumeType\": \"%s\", \"VolumeSize\": %s}}]",
246+
a.Config.OpsmanConfig.AWS.BootDiskType, a.Config.OpsmanConfig.AWS.BootDiskSize,
247+
),
250248
"--security-group-ids",
251249
}
252250
for _, sgID := range config.SecurityGroupIds {
@@ -272,64 +270,6 @@ func (a *AWSVMManager) createVM(ami string) (string, error) {
272270
return cleanupString(instanceID.String()), nil
273271
}
274272

275-
func (a *AWSVMManager) getVolumeID(instanceID string) (volumeID string, err error) {
276-
// wait until available
277-
for {
278-
volumeID, _, err := a.ExecuteWithInstanceProfile(a.addEnvVars(),
279-
[]interface{}{
280-
"ec2", "describe-volumes",
281-
"--filters",
282-
fmt.Sprintf("Name=attachment.instance-id,Values=%s", instanceID),
283-
"Name=attachment.status,Values=attached",
284-
"Name=status,Values=in-use",
285-
"--query", "Volumes[0].VolumeId",
286-
})
287-
if err != nil {
288-
return "", fmt.Errorf("aws error finding volumeID of the root device: %s", err)
289-
}
290-
if !strings.Contains(volumeID.String(), "null") {
291-
return cleanupString(volumeID.String()), nil
292-
}
293-
_, _ = a.stderr.Write([]byte(fmt.Sprintf("volume not available yet, polling in %s\n", a.pollingInterval)))
294-
time.Sleep(a.pollingInterval)
295-
}
296-
}
297-
298-
func (a *AWSVMManager) modifyVolume(volumeID string) error {
299-
// wait until available
300-
var currentRetryCount float64
301-
timeoutTime := time.Now().Add(time.Hour)
302-
for {
303-
_, _, err := a.ExecuteWithInstanceProfile(a.addEnvVars(),
304-
[]interface{}{
305-
"ec2", "modify-volume",
306-
"--volume-id", volumeID,
307-
"--size", a.Config.OpsmanConfig.AWS.BootDiskSize,
308-
})
309-
310-
if err == nil {
311-
break
312-
}
313-
314-
if !strings.Contains(err.Error(), "exit status 255") {
315-
return fmt.Errorf("aws error modifying size of root device: %s", err)
316-
}
317-
318-
// Encrypted volume is not available
319-
waitTime := getExponentialWaitTime(currentRetryCount, a.pollingInterval)
320-
_, _ = a.stderr.Write([]byte(fmt.Sprintf("volume not available to configure yet, polling in %s\n", waitTime)))
321-
322-
if time.Now().After(timeoutTime) {
323-
return errors.New("failed to modify VM disk volume within the hour allowed")
324-
}
325-
326-
time.Sleep(waitTime)
327-
currentRetryCount += 1
328-
}
329-
330-
return nil
331-
}
332-
333273
func (a *AWSVMManager) getIPAddressID() (ipAddress string, err error) {
334274
allocationID, _, err := a.ExecuteWithInstanceProfile(a.addEnvVars(),
335275
[]interface{}{
@@ -530,4 +470,4 @@ func getExponentialWaitTime(currentRetryCount float64, pollingMultiplier time.Du
530470
}
531471

532472
return waitTime
533-
}
473+
}

vmlifecycle/vmmanagers/aws_test.go

Lines changed: 20 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ opsman-configuration:
4949
key_pair_name: superuser
5050
iam_instance_profile_name: awesome-profile
5151
boot_disk_size: 200
52+
boot_disk_type: gp3
5253
public_ip: %s
5354
private_ip: %s
5455
instance_type: m3.large
@@ -60,11 +61,9 @@ opsman-configuration:
6061

6162
command, runner := createCommand(fmt.Sprintf(configStrTemplate, accessKey, secretAccessKey, assumeRole, region, publicIP, privateIP))
6263
runner.ExecuteWithEnvVarsReturnsOnCall(0, bytes.NewBufferString(fmt.Sprintf("\"%s\"\r\n", vmID)), nil, nil)
63-
runner.ExecuteWithEnvVarsReturnsOnCall(1, bytes.NewBufferString("null\r\n"), bytes.NewBufferString("TestStatus: pending creation"), nil)
64-
runner.ExecuteWithEnvVarsReturnsOnCall(2, bytes.NewBufferString("\"vol-0cf5b911680a78bb9\"\r\n"), bytes.NewBufferString("TestStatus: volume created"), nil)
65-
runner.ExecuteWithEnvVarsReturnsOnCall(4, bytes.NewBufferString("\"eipalloc-18643c24\"\r\n"), nil, nil)
66-
runner.ExecuteWithEnvVarsReturnsOnCall(7, bytes.NewBufferString("\"stopping\"\r\n"), nil, nil)
67-
runner.ExecuteWithEnvVarsReturnsOnCall(8, bytes.NewBufferString("\"stopped\"\r\n"), nil, nil)
64+
runner.ExecuteWithEnvVarsReturnsOnCall(1, bytes.NewBufferString("\"eipalloc-18643c24\"\r\n"), nil, nil)
65+
runner.ExecuteWithEnvVarsReturnsOnCall(4, bytes.NewBufferString("\"stopping\"\r\n"), nil, nil)
66+
runner.ExecuteWithEnvVarsReturnsOnCall(5, bytes.NewBufferString("\"stopped\"\r\n"), nil, nil)
6867
return command, runner
6968
}
7069

@@ -75,13 +74,14 @@ opsman-configuration:
7574
Context("create vm", func() {
7675
Context("with a config with all values filled out", func() {
7776
It("calls aws with correct cli arguments and does not describe instances", func() {
78-
numCLICalls := 10
77+
numCLICalls := 7
7978
commands := [][]string{
80-
{ //1
79+
{
8180
`ec2`, `run-instances`,
8281
`--tag-specifications`, `ResourceType=instance,Tags=[{Key=Name,Value=awesome-vm},{Key=Owner,Value=DbAdmin},{Key=Stack,Value=Test}]`,
8382
`--image-id`, `ami-789dc900`,
8483
`--subnet-id`, `awesome-subnet`,
84+
`--block-device-mappings`, `[{"DeviceName": "/dev/xvda", "Ebs": {"VolumeType": "gp3", "VolumeSize": 200}}]`,
8585
`--security-group-ids`, `sg-awesome`, `sg-great`,
8686
`--count`, `1`,
8787
`--instance-type`, "m3.large",
@@ -91,50 +91,31 @@ opsman-configuration:
9191
`--query`, `Instances[0].InstanceId`,
9292
`--private-ip-address`, `10.10.10.10`,
9393
},
94-
{ //2
95-
`ec2`, `describe-volumes`,
96-
`--filters`, `Name=attachment.instance-id,Values=i-0016d0fe3a11c73c2`,
97-
`Name=attachment.status,Values=attached`,
98-
`Name=status,Values=in-use`,
99-
`--query`, `Volumes[0].VolumeId`,
100-
},
101-
{ //3
102-
`ec2`, `describe-volumes`,
103-
`--filters`, `Name=attachment.instance-id,Values=i-0016d0fe3a11c73c2`,
104-
`Name=attachment.status,Values=attached`,
105-
`Name=status,Values=in-use`,
106-
`--query`, `Volumes[0].VolumeId`,
107-
},
108-
{ //4
109-
`ec2`, `modify-volume`,
110-
`--volume-id`, `vol-0cf5b911680a78bb9`,
111-
`--size`, `200`,
112-
},
113-
{ //5
94+
{
11495
`ec2`, `describe-addresses`,
11596
`--filters`, `Name=public-ip,Values=1.2.3.4`,
11697
`--query`, `Addresses[0].AllocationId`,
11798
},
118-
{ //6
99+
{
119100
`ec2`, `associate-address`,
120101
`--allocation-id`, `eipalloc-18643c24`,
121102
`--instance-id`, `i-0016d0fe3a11c73c2`,
122103
},
123-
{ //7
104+
{
124105
`ec2`, `stop-instances`,
125106
`--instance-ids`, `i-0016d0fe3a11c73c2`,
126107
},
127-
{ //8
108+
{
128109
`ec2`, `describe-instances`,
129110
`--instance-ids`, `i-0016d0fe3a11c73c2`,
130111
`--query`, `Reservations[*].Instances[*].State.Name`,
131112
},
132-
{ //9
113+
{
133114
`ec2`, `describe-instances`,
134115
`--instance-ids`, `i-0016d0fe3a11c73c2`,
135116
`--query`, `Reservations[*].Instances[*].State.Name`,
136117
},
137-
{ //10
118+
{
138119
`ec2`, `start-instances`,
139120
`--instance-ids`, `i-0016d0fe3a11c73c2`,
140121
},
@@ -174,7 +155,7 @@ opsman-configuration:
174155
Expect(invokes).ToNot(HaveLen(0))
175156
for _, args := range invokes {
176157
Expect(args[1]).ToNot(ContainElement(MatchRegexp("associate-address")))
177-
Expect(args[1]).ToNot(ContainElement(MatchRegexp("decsribe-address")))
158+
Expect(args[1]).ToNot(ContainElement(MatchRegexp("describe-address")))
178159
}
179160
})
180161
})
@@ -269,11 +250,6 @@ credential_source = Ec2InstanceMetadata`)), comment)
269250
command, runner := createValidCommand("1.2.3.4", "10.10.10.10", "us-west-2")
270251
runner.ExecuteWithEnvVarsReturnsOnCall(0, bytes.NewBufferString("terminated\r\n"), nil, nil)
271252
runner.ExecuteWithEnvVarsReturnsOnCall(1, bytes.NewBufferString(fmt.Sprintf("\"%s\"\r\n", vmID)), nil, nil)
272-
runner.ExecuteWithEnvVarsReturnsOnCall(2, bytes.NewBufferString("null\r\n"), bytes.NewBufferString("TestStatus: pending creation"), nil)
273-
runner.ExecuteWithEnvVarsReturnsOnCall(3, bytes.NewBufferString("\"vol-0cf5b911680a78bb9\"\r\n"), bytes.NewBufferString("TestStatus: volume created"), nil)
274-
runner.ExecuteWithEnvVarsReturnsOnCall(5, bytes.NewBufferString("\"eipalloc-18643c24\"\r\n"), nil, nil)
275-
runner.ExecuteWithEnvVarsReturnsOnCall(8, bytes.NewBufferString("\"stopping\"\r\n"), nil, nil)
276-
runner.ExecuteWithEnvVarsReturnsOnCall(9, bytes.NewBufferString("\"stopped\"\r\n"), nil, nil)
277253

278254
command.State = vmmanagers.StateInfo{
279255
IAAS: "aws",
@@ -293,11 +269,6 @@ credential_source = Ec2InstanceMetadata`)), comment)
293269
command, runner := createValidCommand("1.2.3.4", "10.10.10.10", "us-west-2")
294270
runner.ExecuteWithEnvVarsReturnsOnCall(0, bytes.NewBufferString("[]\n"), nil, nil)
295271
runner.ExecuteWithEnvVarsReturnsOnCall(1, bytes.NewBufferString(fmt.Sprintf("\"%s\"\r\n", vmID)), nil, nil)
296-
runner.ExecuteWithEnvVarsReturnsOnCall(2, bytes.NewBufferString("null\r\n"), bytes.NewBufferString("TestStatus: pending creation"), nil)
297-
runner.ExecuteWithEnvVarsReturnsOnCall(3, bytes.NewBufferString("\"vol-0cf5b911680a78bb9\"\r\n"), bytes.NewBufferString("TestStatus: volume created"), nil)
298-
runner.ExecuteWithEnvVarsReturnsOnCall(5, bytes.NewBufferString("\"eipalloc-18643c24\"\r\n"), nil, nil)
299-
runner.ExecuteWithEnvVarsReturnsOnCall(8, bytes.NewBufferString("\"stopping\"\r\n"), nil, nil)
300-
runner.ExecuteWithEnvVarsReturnsOnCall(9, bytes.NewBufferString("\"stopped\"\r\n"), nil, nil)
301272

302273
command.State = vmmanagers.StateInfo{
303274
IAAS: "aws",
@@ -579,9 +550,8 @@ opsman-configuration:
579550
_, args := runner.ExecuteWithEnvVarsArgsForCall(0)
580551
Expect(args).To(ContainElement(MatchRegexp("ops-manager-vm")))
581552
Expect(args).To(ContainElement(MatchRegexp("m5\\.large")))
582-
583-
_, args = runner.ExecuteWithEnvVarsArgsForCall(2)
584553
Expect(args).To(ContainElement(MatchRegexp("200")))
554+
Expect(args).To(ContainElement(MatchRegexp("gp2")))
585555

586556
})
587557
})
@@ -843,23 +813,17 @@ func happyPathAWSRunnerStubFunc(vmID string) func() (*bytes.Buffer, *bytes.Buffe
843813
case 0:
844814
return bytes.NewBufferString(fmt.Sprintf("%q\r\n", vmID)), nil, nil
845815
case 1:
846-
return bytes.NewBufferString("null\r\n"), bytes.NewBufferString("TestStatus: pending creation"), nil
847-
case 2:
848-
return bytes.NewBufferString("\"vol-0cf5b911680a78bb9\"\r\n"), bytes.NewBufferString("TestStatus: volume created"), nil
849-
case 3:
850-
return nil, nil, nil
851-
case 4:
852816
return bytes.NewBufferString("\"eipalloc-18643c24\"\r\n"), nil, nil
853-
case 5, 6:
817+
case 2, 3:
854818
return nil, nil, nil
855-
case 7:
819+
case 4:
856820
return bytes.NewBufferString("\"stopping\"\r\n"), nil, nil
857-
case 8:
821+
case 5:
858822
return bytes.NewBufferString("\"stopped\"\r\n"), nil, nil
859-
case 9, 10:
823+
case 6, 7:
860824
return nil, nil, nil
861825
default:
862826
panic("stub for nth call not implemented")
863827
}
864828
}
865-
}
829+
}

0 commit comments

Comments
 (0)