aws-ami 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README +1 -0
- data/bin/aws-ami +69 -0
- data/lib/aws_ami.rb +1 -0
- data/lib/aws_ami/ami.rb +92 -0
- data/lib/aws_ami/formation.json +63 -0
- metadata +68 -0
data/README
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
= AWS AMI Tools
|
data/bin/aws-ami
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'aws_ami')
|
5
|
+
|
6
|
+
|
7
|
+
require 'optparse'
|
8
|
+
|
9
|
+
options = {}
|
10
|
+
OptionParser.new do |opts|
|
11
|
+
opts.banner = "Usage: aws-ami -n [name] -r [region]"
|
12
|
+
|
13
|
+
opts.on("-n", "--name NAME", "New AMI name, include version number to version your AMIs") do |v|
|
14
|
+
options[:name] = v
|
15
|
+
end
|
16
|
+
|
17
|
+
opts.on("-r", "--region REGION", "AWS Region that new AMI should be in") do |v|
|
18
|
+
options[:region] = v
|
19
|
+
end
|
20
|
+
|
21
|
+
opts.on("-f", "--install_script_file INSTALL_SCRIPT_FILE", "The install script file, installs all packages and setup AMI") do |f|
|
22
|
+
options[:install_script] = f
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on("-k", "--ec2_ssh_key_name KEY_NAME", "The key name for accessing the ec2 instance that created for creating the AMI") do |n|
|
26
|
+
options[:key_name] = n
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on("-b", "--base_ami_yml BASE_AMI_YML", "A yaml file contains base ami for the new ami, region name and ami id key pairs") do |f|
|
30
|
+
options[:base_ami] = f
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on("-y", "--assume_yes", "Assume yes when asking for delete stack if the stack created failed, default is false") do
|
34
|
+
options[:assume_yes] = true
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on("-p", "--publish_to_account [AWS_ACCOUNT_NUMBER]", "AWS Account number; Allow another AWS Account to access the AMI created") do |v|
|
38
|
+
options[:publish_to_account] = v
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.on('-h', '--help') do
|
42
|
+
puts opts
|
43
|
+
exit(0)
|
44
|
+
end
|
45
|
+
|
46
|
+
opts.on('--dry', "output all options values") do
|
47
|
+
options[:dry] = true
|
48
|
+
end
|
49
|
+
end.parse!
|
50
|
+
|
51
|
+
if options[:dry]
|
52
|
+
require 'pp'
|
53
|
+
pp options
|
54
|
+
exit
|
55
|
+
end
|
56
|
+
|
57
|
+
[:name, :region, :install_script, :key_name, :base_ami].each do |n|
|
58
|
+
raise "Must provide #{n}" unless options[n]
|
59
|
+
end
|
60
|
+
|
61
|
+
# ENV variables:
|
62
|
+
# AWS_ACCESS_KEY_ID
|
63
|
+
# AWS_SECRET_ACCESS_KEY
|
64
|
+
|
65
|
+
ami = AWS::AMI.new(options)
|
66
|
+
ami.build(options[:name],
|
67
|
+
'KeyName' => options[:key_name],
|
68
|
+
'InstallScript' => File.read(options[:install_script]),
|
69
|
+
"BaseAMI" => YAML.load(File.read(options[:base_ami]))[options[:region]])
|
data/lib/aws_ami.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'aws_ami/ami'
|
data/lib/aws_ami/ami.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module AWS
|
5
|
+
class AMI
|
6
|
+
# region: aws region of the new AMI
|
7
|
+
# assume_yes: true for deleting stack when creating image failed
|
8
|
+
def initialize(options={})
|
9
|
+
@assume_yes = options[:assume_yes]
|
10
|
+
@region = options[:region]
|
11
|
+
@publish_to_account = options[:publish_to_account]
|
12
|
+
end
|
13
|
+
|
14
|
+
# name: new AMI name, for example: mingle-saas-base
|
15
|
+
# parameters:
|
16
|
+
# BaseAMI: the base AMI id for the new AMI, for example:
|
17
|
+
# "ami-0d153248" for the "ubuntu/images/ebs/ubuntu-precise-12.04-amd64-server-20121001" in us-west-1 region
|
18
|
+
# KeyName: the ssh key name for accessing the ec2 instance while building the AMI, this is only used when you need to debug problems
|
19
|
+
# InstallScript: the script installs everything need for the AMI, from 2048 to 16k bytes depending on the base ami provided
|
20
|
+
def build(name, parameters)
|
21
|
+
stack = cloudformation.stacks.create("build-#{name}-ami",
|
22
|
+
load_formation,
|
23
|
+
:disable_rollback => true,
|
24
|
+
:parameters => parameters)
|
25
|
+
logger.info "creating stack"
|
26
|
+
wait_until_created(stack)
|
27
|
+
begin
|
28
|
+
instance_id = stack.resources['EC2Instance'].physical_resource_id
|
29
|
+
|
30
|
+
logger.info "creating image"
|
31
|
+
image = ec2.instances[instance_id].create_image(name, :description => "Created at #{Time.now}")
|
32
|
+
sleep 2 until image.exists?
|
33
|
+
logger.info "image #{image.id} state: #{image.state}"
|
34
|
+
sleep 5 until image.state != :pending
|
35
|
+
if image.state == :failed
|
36
|
+
raise "Create image failed"
|
37
|
+
end
|
38
|
+
|
39
|
+
logger.info "image created"
|
40
|
+
logger.info "delete #{stack.name} stack"
|
41
|
+
stack.delete
|
42
|
+
rescue => e
|
43
|
+
logger.error "Creating AMI failed #{e.message}"
|
44
|
+
logger.error e.backtrace.join("\n")
|
45
|
+
logger.info "delete #{stack.name}? [y/n]"
|
46
|
+
if @assume_yes || gets.strip.downcase == 'y'
|
47
|
+
logger.info 'delete stack'
|
48
|
+
stack.delete
|
49
|
+
else
|
50
|
+
logger.info "left stack live"
|
51
|
+
end
|
52
|
+
raise e
|
53
|
+
end
|
54
|
+
if @publish_to_account
|
55
|
+
logger.info "add permissions for #{@publish_to_account}"
|
56
|
+
image.permissions.add(@publish_to_account.gsub(/-/, ''))
|
57
|
+
end
|
58
|
+
logger.info "Image #{name}[#{image.id}] created"
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
def ec2
|
63
|
+
@ec2 ||= AWS::EC2.new(:ec2_endpoint => "ec2.#{@region}.amazonaws.com")
|
64
|
+
end
|
65
|
+
|
66
|
+
def cloudformation
|
67
|
+
@cfm ||= AWS::CloudFormation.new(:cloud_formation_endpoint => "cloudformation.#{@region}.amazonaws.com")
|
68
|
+
end
|
69
|
+
|
70
|
+
def wait_until_created(stack)
|
71
|
+
loop do
|
72
|
+
case stack.status.to_s
|
73
|
+
when /^create_complete$/i
|
74
|
+
break
|
75
|
+
when /^create_(failed|rollback_complete)$/i
|
76
|
+
raise "Create Stack failed"
|
77
|
+
end
|
78
|
+
event = stack.events.first
|
79
|
+
logger.info("latest event: #{event.resource_type} #{event.resource_status}")
|
80
|
+
sleep 5
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def load_formation
|
85
|
+
File.read(File.join(File.dirname(__FILE__), 'formation.json'))
|
86
|
+
end
|
87
|
+
|
88
|
+
def logger
|
89
|
+
@logger ||= Logger.new(STDOUT)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
{
|
2
|
+
"Description": "Build AMI resource formation",
|
3
|
+
|
4
|
+
"Parameters": {
|
5
|
+
"KeyName": {
|
6
|
+
"Description": "Name of an existing EC2 KeyPair to enable SSH access to the instance",
|
7
|
+
"Type": "String"
|
8
|
+
},
|
9
|
+
"InstallScript": {
|
10
|
+
"Description": "Script to install everything need for the AMI",
|
11
|
+
"Type": "String"
|
12
|
+
},
|
13
|
+
"BaseAMI": {
|
14
|
+
"Description": "Base AMI for the new AMI",
|
15
|
+
"Type": "String"
|
16
|
+
}
|
17
|
+
},
|
18
|
+
|
19
|
+
"Resources": {
|
20
|
+
"SecurityGroup" : {
|
21
|
+
"Type" : "AWS::EC2::SecurityGroup",
|
22
|
+
"Properties" : {
|
23
|
+
"GroupDescription" : "Enable SSH access via port 22",
|
24
|
+
"SecurityGroupIngress" : [ {
|
25
|
+
"IpProtocol" : "tcp",
|
26
|
+
"FromPort" : "22",
|
27
|
+
"ToPort" : "22",
|
28
|
+
"CidrIp" : "0.0.0.0/0"
|
29
|
+
} ]
|
30
|
+
}
|
31
|
+
},
|
32
|
+
|
33
|
+
"EC2Instance": {
|
34
|
+
"Type": "AWS::EC2::Instance",
|
35
|
+
"Properties": {
|
36
|
+
"ImageId": { "Ref": "BaseAMI" },
|
37
|
+
"InstanceType": "m1.small",
|
38
|
+
"KeyName": { "Ref": "KeyName" },
|
39
|
+
"SecurityGroups": [ { "Ref": "SecurityGroup" } ],
|
40
|
+
"UserData": { "Fn::Base64": { "Fn::Join": ["", [
|
41
|
+
{ "Ref": "InstallScript" },
|
42
|
+
"\ncurl -X PUT -H 'Content-Type:' --data-binary '{\"Status\" : \"SUCCESS\",",
|
43
|
+
" \"Reason\" : \"ec2 instance launched\",",
|
44
|
+
" \"UniqueId\" : \"", {"Ref": "AWS::StackName"}, "-ec2-success\",",
|
45
|
+
" \"Data\" : \"Done\"}' ",
|
46
|
+
" \"", {"Ref" : "myWaitHandle"},"\"\n"
|
47
|
+
]]}}
|
48
|
+
}
|
49
|
+
},
|
50
|
+
"myWaitHandle" : {
|
51
|
+
"Type" : "AWS::CloudFormation::WaitConditionHandle",
|
52
|
+
"Properties" : {
|
53
|
+
}
|
54
|
+
},
|
55
|
+
"myWaitCondition" : {
|
56
|
+
"Type" : "AWS::CloudFormation::WaitCondition",
|
57
|
+
"Properties" : {
|
58
|
+
"Handle" : { "Ref" : "myWaitHandle" },
|
59
|
+
"Timeout" : "300"
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}
|
63
|
+
}
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: aws-ami
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Mingle SaaS team
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: aws-sdk
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.8.2
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.8.2
|
30
|
+
description:
|
31
|
+
email: mingle.saas@thoughtworks.com
|
32
|
+
executables:
|
33
|
+
- aws-ami
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- README
|
38
|
+
- lib/aws_ami/ami.rb
|
39
|
+
- lib/aws_ami.rb
|
40
|
+
- bin/aws-ami
|
41
|
+
- lib/aws_ami/formation.json
|
42
|
+
homepage: http://github.com/ThoughtWorksStudios/aws-ami
|
43
|
+
licenses:
|
44
|
+
- MIT
|
45
|
+
post_install_message: run aws-ami -h for details how to use
|
46
|
+
rdoc_options: []
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
requirements: []
|
62
|
+
rubyforge_project:
|
63
|
+
rubygems_version: 1.8.24
|
64
|
+
signing_key:
|
65
|
+
specification_version: 3
|
66
|
+
summary: AWS AMI toolsets for Ruby
|
67
|
+
test_files: []
|
68
|
+
has_rdoc:
|