roark 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +19 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +75 -0
- data/Rakefile +8 -0
- data/bin/roark +6 -0
- data/examples/example.json +52 -0
- data/lib/roark/ami.rb +148 -0
- data/lib/roark/ami_create_workflow.rb +50 -0
- data/lib/roark/aws/cloud_formation/create_stack.rb +35 -0
- data/lib/roark/aws/cloud_formation/destroy_stack.rb +17 -0
- data/lib/roark/aws/cloud_formation/stack_outputs.rb +17 -0
- data/lib/roark/aws/cloud_formation/stack_status.rb +21 -0
- data/lib/roark/aws/cloud_formation.rb +4 -0
- data/lib/roark/aws/connection.rb +27 -0
- data/lib/roark/aws/ec2/ami_state.rb +19 -0
- data/lib/roark/aws/ec2/create_ami.rb +20 -0
- data/lib/roark/aws/ec2/destroy_ami.rb +34 -0
- data/lib/roark/aws/ec2/instance_status.rb +15 -0
- data/lib/roark/aws/ec2/stop_instance.rb +15 -0
- data/lib/roark/aws/ec2.rb +5 -0
- data/lib/roark/aws.rb +5 -0
- data/lib/roark/cli/create.rb +76 -0
- data/lib/roark/cli/destroy.rb +53 -0
- data/lib/roark/cli/shared.rb +28 -0
- data/lib/roark/cli.rb +66 -0
- data/lib/roark/instance.rb +56 -0
- data/lib/roark/response.rb +15 -0
- data/lib/roark/stack.rb +63 -0
- data/lib/roark/version.rb +3 -0
- data/lib/roark.rb +17 -0
- data/roark.gemspec +26 -0
- data/spec/ami_create_workflow_spec.rb +34 -0
- data/spec/ami_spec.rb +217 -0
- data/spec/aws/cloud_formation/create_stack_spec.rb +20 -0
- data/spec/aws/connection_spec.rb +26 -0
- data/spec/aws/ec2/ami_state_spec.rb +11 -0
- data/spec/aws/ec2/create_ami_spec.rb +17 -0
- data/spec/aws/ec2/destroy_ami_spec.rb +32 -0
- data/spec/create_stack_spec.rb +20 -0
- data/spec/instance_spec.rb +80 -0
- data/spec/response_spec.rb +21 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/stack_spec.rb +99 -0
- metadata +155 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Intuit
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
[](http://travis-ci.org/intuit/roark)
|
2
|
+
|
3
|
+
# Roark
|
4
|
+
|
5
|
+
Howard Roark, master architect and builder of AMIs.
|
6
|
+
|
7
|
+
## Overview
|
8
|
+
|
9
|
+
* Roark builds AMIs from an Instance created by a Cloud Formation Stack.
|
10
|
+
* Roark expects to be provided with a Cloud Formation Template that can be used to create this stack.
|
11
|
+
* This template should create an Instance that is fully configured at bootstrap (via userdata, CloudInit, etc).
|
12
|
+
* The stack must provide the ID of the Instance to be converted to an AMI (IE. i-1234abcd) as a Cloud Formation Output named **InstanceId**.
|
13
|
+
* Once the stack is created, Roark will read the Instance ID from the Cloud Formation Output, stop the Instance, convert it into an AMI and destroy the stack.
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
gem 'roark'
|
20
|
+
|
21
|
+
And then execute:
|
22
|
+
|
23
|
+
$ bundle
|
24
|
+
|
25
|
+
Or install it yourself as:
|
26
|
+
|
27
|
+
$ gem install roark
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
### CLI
|
32
|
+
|
33
|
+
Once you have a working templated, set your AWS Access and Secret Keys for the target account:
|
34
|
+
|
35
|
+
export AWS_ACCESS_KEY_ID=xxx
|
36
|
+
export AWS_SECRET_ACCESS_KEY=yyy
|
37
|
+
|
38
|
+
Create AMI
|
39
|
+
|
40
|
+
roark create -n NAME_OF_AMI
|
41
|
+
-r AWS_REGION \
|
42
|
+
-t PATH_TO_CLOUD_FORMATION_TEMPLATE \
|
43
|
+
-p 'Parameter1=value1' \
|
44
|
+
-p 'Parameter2=value2'
|
45
|
+
|
46
|
+
Destroy AMI
|
47
|
+
|
48
|
+
roark destroy -i AMI_ID -r AWS_REGION
|
49
|
+
|
50
|
+
## Example
|
51
|
+
|
52
|
+
The Cloud Formation Template [example.json](https://raw.github.com/intuit/roark/master/examples/example.json) in the /examples directory will create an AMI based off of the public Amazon Linux AMI.
|
53
|
+
|
54
|
+
It will write the file hello\_world.txt as part of bootstraping via userdata.
|
55
|
+
|
56
|
+
To create an AMI using this template, set your AWS Access Key and Secret Key:
|
57
|
+
|
58
|
+
export AWS_ACCESS_KEY_ID=xxx
|
59
|
+
export AWS_SECRET_ACCESS_KEY=yyy
|
60
|
+
|
61
|
+
Download the template:
|
62
|
+
|
63
|
+
wget https://raw.github.com/intuit/roark/master/examples/example.json
|
64
|
+
|
65
|
+
Create an AMI:
|
66
|
+
|
67
|
+
roark create -n roark-example-ami -t example.json
|
68
|
+
|
69
|
+
## Contributing
|
70
|
+
|
71
|
+
1. Fork it
|
72
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
73
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
74
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
75
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/roark
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
{
|
2
|
+
"AWSTemplateFormatVersion" : "2010-09-09",
|
3
|
+
|
4
|
+
"Description" : "AWS CloudFormation Sample Template to create instance and output ID for transforming to an image by Roark.",
|
5
|
+
|
6
|
+
"Mappings" : {
|
7
|
+
"AWSRegionArch2AMI" : {
|
8
|
+
"us-east-1" : { "PV64" : "ami-1624987f" },
|
9
|
+
"us-west-2" : { "PV64" : "ami-2a31bf1a" },
|
10
|
+
"us-west-1" : { "PV64" : "ami-1bf9de5e" },
|
11
|
+
"eu-west-1" : { "PV64" : "ami-c37474b7" },
|
12
|
+
"ap-southeast-1" : { "PV64" : "ami-a6a7e7f4" },
|
13
|
+
"ap-southeast-2" : { "PV64" : "ami-bd990e87" },
|
14
|
+
"ap-northeast-1" : { "PV64" : "ami-4e6cd34f" },
|
15
|
+
"sa-east-1" : { "PV64" : "ami-1e08d103" }
|
16
|
+
}
|
17
|
+
},
|
18
|
+
|
19
|
+
"Resources" : {
|
20
|
+
"Instance" : {
|
21
|
+
"Type" : "AWS::EC2::Instance",
|
22
|
+
"Properties" : {
|
23
|
+
"ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" }, "PV64" ]},
|
24
|
+
"InstanceType" : "m1.large",
|
25
|
+
"SecurityGroups" : [{ "Ref" : "Ec2SecurityGroup" }],
|
26
|
+
"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
|
27
|
+
"#!/bin/bash\n",
|
28
|
+
"echo 'Hello World!' > /hello_world.txt", "\n"
|
29
|
+
]]}}
|
30
|
+
}
|
31
|
+
},
|
32
|
+
|
33
|
+
"Ec2SecurityGroup" : {
|
34
|
+
"Type" : "AWS::EC2::SecurityGroup",
|
35
|
+
"Properties" : {
|
36
|
+
"GroupDescription" : "HTTP and SSH access",
|
37
|
+
"SecurityGroupIngress" : [ {
|
38
|
+
"IpProtocol" : "tcp",
|
39
|
+
"FromPort" : "22", "ToPort" : "22",
|
40
|
+
"CidrIp" : "0.0.0.0/0"
|
41
|
+
} ]
|
42
|
+
}
|
43
|
+
}
|
44
|
+
},
|
45
|
+
|
46
|
+
"Outputs" : {
|
47
|
+
"InstanceId" : {
|
48
|
+
"Value" : { "Ref" : "Instance" },
|
49
|
+
"Description" : "ID of Instance"
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
data/lib/roark/ami.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
module Roark
|
2
|
+
class Ami
|
3
|
+
|
4
|
+
attr_accessor :aws, :ami_id, :name
|
5
|
+
|
6
|
+
def initialize(args)
|
7
|
+
@aws = args[:aws]
|
8
|
+
@ami_id = args[:ami_id]
|
9
|
+
@name = args[:name]
|
10
|
+
|
11
|
+
@region = @aws.region
|
12
|
+
@logger = Roark.logger
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_instance(args)
|
16
|
+
@logger.info "Creating instance in '#{@region}'."
|
17
|
+
begin
|
18
|
+
instance.create :parameters => args[:parameters],
|
19
|
+
:template => args[:template]
|
20
|
+
rescue AWS::Errors::Base => e
|
21
|
+
return Response.new :code => 1, :message => e.message
|
22
|
+
end
|
23
|
+
|
24
|
+
@logger.info "Instance created."
|
25
|
+
Response.new :code => 0, :message => 'Instance created.'
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_ami
|
29
|
+
@logger.info "Creating AMI '#{@name}' from Instance '#{instance_id}'."
|
30
|
+
begin
|
31
|
+
ami = instance.create_ami_from_instance
|
32
|
+
@ami_id = ami.id
|
33
|
+
rescue AWS::Errors::Base => e
|
34
|
+
return Response.new :code => 1, :message => e.message
|
35
|
+
end
|
36
|
+
@logger.info "AMI '#{@ami_id}' created."
|
37
|
+
Response.new :code => 0, :message => "AMI '#{@ami_id}' created."
|
38
|
+
end
|
39
|
+
|
40
|
+
def stop_instance
|
41
|
+
@logger.info "Stopping instance."
|
42
|
+
begin
|
43
|
+
instance.stop
|
44
|
+
rescue AWS::Errors::Base => e
|
45
|
+
return Response.new :code => 1, :message => e.message
|
46
|
+
end
|
47
|
+
Response.new :code => 0, :message => "Instance stopped."
|
48
|
+
end
|
49
|
+
|
50
|
+
def destroy_instance
|
51
|
+
begin
|
52
|
+
instance.destroy
|
53
|
+
rescue AWS::Errors::Base => e
|
54
|
+
return Response.new :code => 1, :message => e.message
|
55
|
+
end
|
56
|
+
@logger.info "Instance destroyed."
|
57
|
+
Response.new :code => 0, :message => "Instance destroyed."
|
58
|
+
end
|
59
|
+
|
60
|
+
def destroy
|
61
|
+
@logger.info "Destroying AMI '#{@ami_id}'."
|
62
|
+
begin
|
63
|
+
ec2_destroy_ami.destroy @ami_id
|
64
|
+
rescue AWS::Errors::Base, AWS::Core::Resource::NotFound => e
|
65
|
+
return Response.new :code => 1, :message => e.message
|
66
|
+
end
|
67
|
+
@logger.info "Destroy completed succesfully."
|
68
|
+
Response.new :code => 0, :message => "Destroy completed succesfully."
|
69
|
+
end
|
70
|
+
|
71
|
+
def wait_for_instance
|
72
|
+
while instance.in_progress? || !instance.exists?
|
73
|
+
@logger.info "Waiting for instance to come online."
|
74
|
+
sleep 60
|
75
|
+
end
|
76
|
+
|
77
|
+
if instance.success?
|
78
|
+
msg = "Instance '#{instance_id}' completed succesfully."
|
79
|
+
@logger.info msg
|
80
|
+
Response.new :code => 0, :message => msg
|
81
|
+
else
|
82
|
+
msg = "Instance did not complete succesfully."
|
83
|
+
@logger.info msg
|
84
|
+
Response.new :code => 1, :message => msg
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def wait_for_instance_to_stop
|
89
|
+
while instance.status != :stopped
|
90
|
+
@logger.info "Waiting for instance '#{instance_id}' to stop. Current state: '#{instance.status}'."
|
91
|
+
sleep 15
|
92
|
+
end
|
93
|
+
Response.new :code => 0, :message => "Instance stopped."
|
94
|
+
end
|
95
|
+
|
96
|
+
def wait_for_ami
|
97
|
+
while !exists? || pending?
|
98
|
+
@logger.info "Waiting for AMI creation to complete. Current state: '#{state}'."
|
99
|
+
sleep 15
|
100
|
+
end
|
101
|
+
|
102
|
+
if available?
|
103
|
+
msg = "AMI completed succesfully."
|
104
|
+
@logger.info msg
|
105
|
+
Response.new :code => 0, :message => msg
|
106
|
+
else
|
107
|
+
msg = "AMI did not complete succesfully."
|
108
|
+
@logger.info msg
|
109
|
+
Response.new :code => 1, :message => msg
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def available?
|
114
|
+
state == :available
|
115
|
+
end
|
116
|
+
|
117
|
+
def pending?
|
118
|
+
state == :pending
|
119
|
+
end
|
120
|
+
|
121
|
+
def exists?
|
122
|
+
ec2_ami_state.exists? @ami_id
|
123
|
+
end
|
124
|
+
|
125
|
+
def state
|
126
|
+
ec2_ami_state.state @ami_id
|
127
|
+
end
|
128
|
+
|
129
|
+
def instance_id
|
130
|
+
instance.instance_id
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def instance
|
136
|
+
@instance ||= Instance.new :aws => @aws, :name => @name
|
137
|
+
end
|
138
|
+
|
139
|
+
def ec2_ami_state
|
140
|
+
@ec2_ami_state ||= Roark::Aws::Ec2::AmiState.new @aws
|
141
|
+
end
|
142
|
+
|
143
|
+
def ec2_destroy_ami
|
144
|
+
@ec2_destroy_ami ||= Roark::Aws::Ec2::DestroyAmi.new @aws
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Roark
|
2
|
+
class AmiCreateWorkflow
|
3
|
+
|
4
|
+
def initialize(args)
|
5
|
+
@ami = args[:ami]
|
6
|
+
@parameters = args[:parameters]
|
7
|
+
@template = args[:template]
|
8
|
+
@logger = Roark.logger
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute
|
12
|
+
%w(create_instance wait_for_instance stop_instance wait_for_instance_to_stop
|
13
|
+
create_ami wait_for_ami destroy_instance).each do |m|
|
14
|
+
response = self.send m.to_sym
|
15
|
+
return response unless response.success?
|
16
|
+
end
|
17
|
+
Response.new :code => 0, :message => "AMI create workflow completed succesfully."
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_instance
|
21
|
+
@ami.create_instance :parameters => @parameters,
|
22
|
+
:template => @template
|
23
|
+
end
|
24
|
+
|
25
|
+
def wait_for_instance
|
26
|
+
@ami.wait_for_instance
|
27
|
+
end
|
28
|
+
|
29
|
+
def stop_instance
|
30
|
+
@ami.stop_instance
|
31
|
+
end
|
32
|
+
|
33
|
+
def wait_for_instance_to_stop
|
34
|
+
@ami.wait_for_instance_to_stop
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_ami
|
38
|
+
@ami.create_ami
|
39
|
+
end
|
40
|
+
|
41
|
+
def wait_for_ami
|
42
|
+
@ami.wait_for_ami
|
43
|
+
end
|
44
|
+
|
45
|
+
def destroy_instance
|
46
|
+
@ami.destroy_instance
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Roark
|
2
|
+
module Aws
|
3
|
+
module CloudFormation
|
4
|
+
class CreateStack
|
5
|
+
|
6
|
+
def initialize(connection)
|
7
|
+
@connection = connection
|
8
|
+
end
|
9
|
+
|
10
|
+
def create(args)
|
11
|
+
name = args[:name]
|
12
|
+
parameters = args[:parameters]
|
13
|
+
template = args[:template]
|
14
|
+
|
15
|
+
@connection.cf.stacks.create name, template, { :capabilities => capabilities,
|
16
|
+
:parameters => format_parameters(parameters) }
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def format_parameters(parameters={})
|
22
|
+
parameters.map do |p|
|
23
|
+
{ :parameter_key => p.first,
|
24
|
+
:parameter_value => p.last }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def capabilities
|
29
|
+
['CAPABILITY_IAM']
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Roark
|
2
|
+
module Aws
|
3
|
+
module CloudFormation
|
4
|
+
class StackStatus
|
5
|
+
|
6
|
+
def initialize(connection)
|
7
|
+
@connection = connection
|
8
|
+
end
|
9
|
+
|
10
|
+
def status(name)
|
11
|
+
@connection.cf.stacks[name].status
|
12
|
+
end
|
13
|
+
|
14
|
+
def exists?(name)
|
15
|
+
@connection.cf.stacks[name].exists?
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Roark
|
2
|
+
module Aws
|
3
|
+
class Connection
|
4
|
+
|
5
|
+
attr_accessor :region
|
6
|
+
|
7
|
+
def initialize(args)
|
8
|
+
@access_key_id = args[:access_key_id]
|
9
|
+
@secret_access_key = args[:secret_access_key]
|
10
|
+
@region = args[:region]
|
11
|
+
end
|
12
|
+
|
13
|
+
def cf
|
14
|
+
@cf ||= AWS::CloudFormation.new :access_key_id => @access_key_id,
|
15
|
+
:secret_access_key => @secret_access_key,
|
16
|
+
:region => @region
|
17
|
+
end
|
18
|
+
|
19
|
+
def ec2
|
20
|
+
@ec2 ||= AWS::EC2.new :access_key_id => @access_key_id,
|
21
|
+
:secret_access_key => @secret_access_key,
|
22
|
+
:region => @region
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Roark
|
2
|
+
module Aws
|
3
|
+
module Ec2
|
4
|
+
class AmiState
|
5
|
+
def initialize(connection)
|
6
|
+
@connection = connection
|
7
|
+
end
|
8
|
+
|
9
|
+
def state(ami_id)
|
10
|
+
@connection.ec2.images[ami_id].state
|
11
|
+
end
|
12
|
+
|
13
|
+
def exists?(ami_id)
|
14
|
+
@connection.ec2.images[ami_id].exists?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Roark
|
2
|
+
module Aws
|
3
|
+
module Ec2
|
4
|
+
class CreateAmi
|
5
|
+
|
6
|
+
def initialize(connection)
|
7
|
+
@connection = connection
|
8
|
+
end
|
9
|
+
|
10
|
+
def create(args)
|
11
|
+
instance_id = args[:instance_id]
|
12
|
+
name = args[:name]
|
13
|
+
@connection.ec2.images.create :instance_id => instance_id,
|
14
|
+
:name => name
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Roark
|
2
|
+
module Aws
|
3
|
+
module Ec2
|
4
|
+
class DestroyAmi
|
5
|
+
|
6
|
+
def initialize(connection)
|
7
|
+
@connection = connection
|
8
|
+
@logger = Roark.logger
|
9
|
+
end
|
10
|
+
|
11
|
+
def destroy(ami_id)
|
12
|
+
ami = @connection.ec2.images[ami_id]
|
13
|
+
|
14
|
+
@block_device_mappings = ami.block_device_mappings
|
15
|
+
|
16
|
+
@logger.info "Deleting AMI '#{ami_id}'."
|
17
|
+
ami.delete
|
18
|
+
delete_snapshots
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def delete_snapshots
|
24
|
+
@block_device_mappings.each_value do |v|
|
25
|
+
snapshot_id = v[:snapshot_id]
|
26
|
+
@logger.info "Deleting snapshot '#{snapshot_id}'."
|
27
|
+
@connection.ec2.snapshots[snapshot_id].delete
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|