ami 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ami.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Richard Bone
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.
@@ -0,0 +1,39 @@
1
+ # Kiln
2
+
3
+ A gem for automating the creation of EC2 AMIs
4
+
5
+ ## Installation
6
+
7
+ ```
8
+ gem install kiln
9
+ ```
10
+
11
+ or add to your Gemfile
12
+
13
+ ```ruby
14
+ source "https://rubygems.org"
15
+
16
+ gem "kiln", "~> 1.0.0"
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ `kiln-create-ami <AMI class name>`
22
+
23
+ Creates an AMI using the specified AMI template e.g.
24
+
25
+ `kiln-create-ami PreciseBasic`
26
+
27
+ AMIs are expected to be found in ./kiln/
28
+
29
+ For an exmaple of what an AMI template looks like see: [PreciseBasic template](kiln/precise-basic.rb)
30
+
31
+ You will also require a config file to provide all the credentials and other
32
+ information necessary to create your AMI, this can be located in either
33
+ `~/.kiln/config.json` or `./kiln/config.json`
34
+
35
+ For an example of the values see: [example config.json](kiln/config.json)
36
+
37
+ ## LICENSE
38
+
39
+ MIT, see LICENSE file
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/ami/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ['Richard Bone']
6
+ gem.email = ['rjonbone@gmail.com']
7
+ gem.description = %q{A tool to assist in building EC2 AMIs}
8
+ gem.summary = %q{A tool to assist in building EC2 AMIs}
9
+ gem.homepage = 'https://github.com/rbone/ami'
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = 'ami'
15
+ gem.require_paths = ['lib']
16
+ gem.version = AMI::VERSION
17
+
18
+ gem.add_dependency 'gofer', '~> 0.5.0'
19
+ gem.add_dependency 'aws-sdk', '~> 1.11.2'
20
+ end
@@ -0,0 +1,9 @@
1
+ {
2
+ "access_key" : "XXXXXXXXXXXXXXXXXXXX",
3
+ "secret_key" : "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
4
+ "cert_path" : "/path/to/my/.ec2/cert-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.pem",
5
+ "pk_path" : "/path/to/my/.ec2/pk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.pem",
6
+ "key_name" : "mykey",
7
+ "account_id" : "0123-4567-8901",
8
+ "s3_bucket" : "my-s3-bucket"
9
+ }
@@ -0,0 +1,23 @@
1
+
2
+ # Creates a basic ubuntu 12.04 instance with a recent ruby version managed
3
+ # by rbenv. Primarily here as an example to reference when creating your own
4
+ # AMIs
5
+ class PreciseBasic < AMI::Definition
6
+ def name
7
+ date = DateTime.now.strftime('%Y-%m-%d-%H-%M')
8
+ "precise-basic-#{date}"
9
+ end
10
+
11
+ def server(server)
12
+ server.image_id = 'ami-cf5e2ba6'
13
+ server.user_name = 'ubuntu'
14
+ end
15
+
16
+ def configure(ssh)
17
+ AMI::Installer::RubyUnderRbenv.install('1.9.3-p429', ssh)
18
+ end
19
+
20
+ def create_ami(ssh, config)
21
+ AMI::AmiBuilder::UbuntuPrecise.create(name, ssh, config)
22
+ end
23
+ end
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift(File.expand_path("../../lib", __FILE__))
4
+
5
+ require 'ami'
6
+
7
+ AMI::Commands::CreateAmi.new.run(ARGV)
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift(File.expand_path("../../lib", __FILE__))
4
+
5
+ require 'ami'
6
+
7
+ AMI::Commands::List.new.run(ARGV)
@@ -0,0 +1,15 @@
1
+ require 'ami/version'
2
+ require 'ami/core'
3
+ require 'ami/definition'
4
+ require 'ami/server'
5
+ require 'ami/config'
6
+ require 'ami/ssh'
7
+ require 'ami/instance_provisioner'
8
+ require 'ami/config_loader'
9
+ require 'ami/commands/list'
10
+ require 'ami/commands/create_ami'
11
+ require 'ami/installer/ruby_under_rbenv'
12
+ require 'ami/ami_builder/ubuntu_precise'
13
+
14
+ module AMI
15
+ end
@@ -0,0 +1,20 @@
1
+
2
+ module AMI
3
+ module AmiBuilder
4
+ class UbuntuPrecise
5
+ def self.create(image_name, ssh, config, include_files = [])
6
+ include_files << '/etc/apt/trusted.gpg'
7
+ include_files << '/etc/apt/trustdb.gpg'
8
+ ssh.upload(config.cert_path, '/tmp/cert.pem')
9
+ ssh.upload(config.pk_path, '/tmp/pk.pem')
10
+ ssh.run 'sudo sed -i -e "s/# deb\(.*\)multiverse/deb\1multiverse/g" /etc/apt/sources.list'
11
+ ssh.run 'sudo apt-get update'
12
+ ssh.run 'sudo apt-get -y install ec2-api-tools ec2-ami-tools'
13
+ ssh.run 'mkdir /tmp/ami'
14
+ ssh.run "sudo ec2-bundle-vol -r x86_64 -d /tmp/ami -k /tmp/pk.pem -c /tmp/cert.pem -u #{config.account_id} -i #{include_files.join(',')}"
15
+ ssh.run "ec2-upload-bundle -b '#{config.s3_bucket}' -m '/tmp/ami/image.manifest.xml' -a #{config.access_key} -s #{config.secret_key}"
16
+ ssh.run "ec2-register -K /tmp/pk.pem -C /tmp/cert.pem '#{config.s3_bucket}/image.manifest.xml' -n #{image_name}"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,43 @@
1
+
2
+ module AMI
3
+ module Commands
4
+ class CreateAmi
5
+ def run(argv)
6
+ name = argv[0]
7
+ AMI::Core.load_amis
8
+
9
+ unless AMI::Core.valid_ami?(name)
10
+ warn "Invalid AMI name #{name}. Use ami-list to see available AMIs"
11
+ exit 1
12
+ end
13
+
14
+ ami = AMI::Core.ami(name).new
15
+
16
+ # Ask for the AMIs customised server requirements
17
+ server_template = AMI::Server.new
18
+ ami.server(server_template)
19
+ ami_name = ami.name
20
+
21
+ config = AMI::ConfigLoader.load_config
22
+
23
+ server = AMI::InstanceProvisioner.provision(
24
+ ami_name,
25
+ server_template,
26
+ config
27
+ )
28
+
29
+ begin
30
+ ssh = AMI::SSH.create_connection(server, server_template.user_name)
31
+ ami.configure(ssh)
32
+ ami.create_ami(ssh, config)
33
+ puts 'done'
34
+ rescue
35
+ warn "failure occurred, terminating server"
36
+ raise
37
+ ensure
38
+ server.terminate
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,14 @@
1
+
2
+ module AMI
3
+ module Commands
4
+ class List
5
+ def run(argv)
6
+ AMI::Core.load_amis
7
+
8
+ AMI::Core.all_amis.each do |name, ami|
9
+ puts "#{name}"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,41 @@
1
+
2
+ module AMI
3
+ # Defines credentials and account info required to create an AMI
4
+ class Config
5
+ # Your AWS access key
6
+ attr_reader :access_key
7
+
8
+ # Your corresponding AWS secret key
9
+ attr_reader :secret_key
10
+
11
+ # The path to your AWS certificate
12
+ # e.g. /my/keys/.ec2/cert-XXXXXXXXXXXXXXXXXXXX.pem
13
+ attr_reader :cert_path
14
+
15
+ # The path to your corresponding AWS private key file
16
+ # e.g. /my/keys/.ec2/pk-XXXXXXXXXXXXXXXXXXXXXXXX.pem
17
+ attr_reader :pk_path
18
+
19
+ # Your AWS account ID
20
+ # e.g. 0123-4567-8901
21
+ attr_reader :account_id
22
+
23
+ # The S3 bucket to store created AMIs
24
+ # e.g. my-custom-amis
25
+ attr_reader :s3_bucket
26
+
27
+ # The AWS SSH public key to use for this instance. The corresponding
28
+ # private key must be in your keychain
29
+ attr_reader :key_name
30
+
31
+ def initialize(config)
32
+ @access_key = config.fetch('access_key')
33
+ @secret_key = config.fetch('secret_key')
34
+ @cert_path = config.fetch('cert_path')
35
+ @pk_path = config.fetch('pk_path')
36
+ @account_id = config.fetch('account_id')
37
+ @s3_bucket = config.fetch('s3_bucket')
38
+ @key_name = config.fetch('key_name')
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,16 @@
1
+ require 'json'
2
+
3
+ module AMI
4
+ class ConfigLoader
5
+ def self.load_config
6
+ raw_config = {}
7
+ if File.exists?(File.expand_path('~/.ami/config.json'))
8
+ raw_config.merge!(JSON.load(File.open(File.expand_path('~/.ami/config.json'))))
9
+ end
10
+ if File.exists?(Dir.pwd + '/ami/config/json')
11
+ raw_config.merge!(JSON.load(File.open(Dir.pwd + '/ami/config.json')))
12
+ end
13
+ Config.new(raw_config)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,25 @@
1
+
2
+ module AMI
3
+ class Core
4
+ @amis = {}
5
+ def self.load_amis
6
+ Dir[Dir.pwd + '/ami/*.rb'].each { |file| require file }
7
+ end
8
+
9
+ def self.register_ami(subclass)
10
+ @amis[subclass.name] = subclass
11
+ end
12
+
13
+ def self.valid_ami?(name)
14
+ !!@amis[name]
15
+ end
16
+
17
+ def self.ami(name)
18
+ @amis[name]
19
+ end
20
+
21
+ def self.all_amis
22
+ @amis
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,20 @@
1
+
2
+ module AMI
3
+ class Definition
4
+ # Passed an object describing the server to use
5
+ def server(server)
6
+ end
7
+
8
+ # Configures the AMI as required
9
+ def configure(ssh)
10
+ end
11
+
12
+ # Creates the AMI via the ssh connection on the provided instance
13
+ def create_ami(ssh, config)
14
+ end
15
+
16
+ def self.inherited(subclass)
17
+ AMI::Core.register_ami(subclass)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+
2
+ module AMI
3
+ module Installer
4
+ class RubyUnderRbenv
5
+ def self.install(version, ssh)
6
+ ssh.run 'sudo apt-get update'
7
+ ssh.run 'sudo apt-get -y install make gcc git zlib1g-dev libreadline-dev libyaml-dev libxml2-dev libxslt-dev libssl-dev'
8
+ ssh.run 'git clone git://github.com/sstephenson/rbenv.git ~/.rbenv'
9
+ ssh.run %q{echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile}
10
+ ssh.run %q{echo 'eval "$(rbenv init -)"' >> ~/.bash_profile}
11
+ ssh.run 'mkdir -p ~/.rbenv/plugins'
12
+ ssh.run 'git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build/'
13
+ ssh.run "~/.rbenv/bin/rbenv install #{version}"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,40 @@
1
+ require 'aws-sdk'
2
+
3
+ module AMI
4
+ # Provisions an EC2 instance and returns the resulting server object
5
+ class InstanceProvisioner
6
+ def self.provision(server_name, server_template, config)
7
+ options = {
8
+ :image_id => server_template.image_id,
9
+ :instance_type => server_template.instance_type,
10
+ :security_groups => server_template.security_groups,
11
+ :key_name => config.key_name
12
+ }
13
+
14
+ ec2 = AWS::EC2.new(
15
+ :access_key_id => config.access_key,
16
+ :secret_access_key => config.secret_key
17
+ )
18
+
19
+ server = ec2.instances.create(options)
20
+
21
+ print 'creating server'
22
+ wait_for { server.status != :pending }
23
+ puts 'done'
24
+
25
+ server.tags.Name = server_name
26
+ puts "tagged as #{server_name}"
27
+ server
28
+ end
29
+
30
+ def self.wait_for(&block)
31
+ output = ['.', '.', '.', "\b\b\b \b\b\b"]
32
+
33
+ output.cycle do |o|
34
+ break if block.call
35
+ print o
36
+ sleep 1
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,22 @@
1
+
2
+ module AMI
3
+ # Stores information about the server required to generate the AMI
4
+ class Server
5
+ # The base AMI to build on top of
6
+ attr_accessor :image_id
7
+
8
+ # The instance type to use when creating the AMI
9
+ attr_accessor :instance_type
10
+
11
+ # An array of security group names to use for this instance
12
+ attr_accessor :security_groups
13
+
14
+ # The user name to use when logging into the instance
15
+ attr_accessor :user_name
16
+
17
+ def initialize
18
+ @instance_type = 'c1.xlarge'
19
+ @security_groups = ['default']
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ require 'gofer'
2
+
3
+ module AMI
4
+ class SSH
5
+ def self.create_connection(server, user)
6
+ retries = 5
7
+ begin
8
+ ssh = Gofer::Host.new(server.dns_name, user, :timeout => 30)
9
+ ssh.ls '/' # force gofer to connect to the host
10
+ return ssh
11
+ rescue Errno::ECONNREFUSED, Timeout::Error, Errno::EHOSTUNREACH
12
+ retries -= 1
13
+ if retries > 0
14
+ warn "connection attempt failed, #{retries} retries remaining"
15
+ sleep 1
16
+ retry
17
+ else
18
+ raise
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module AMI
2
+ VERSION = "1.0.0"
3
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ami
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Richard Bone
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-06-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: gofer
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.5.0
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: 0.5.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: aws-sdk
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 1.11.2
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.11.2
46
+ description: A tool to assist in building EC2 AMIs
47
+ email:
48
+ - rjonbone@gmail.com
49
+ executables:
50
+ - ami-create
51
+ - ami-list
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - .gitignore
56
+ - Gemfile
57
+ - LICENSE
58
+ - README.md
59
+ - Rakefile
60
+ - ami.gemspec
61
+ - ami/config.json
62
+ - ami/precise-basic.rb
63
+ - bin/ami-create
64
+ - bin/ami-list
65
+ - lib/ami.rb
66
+ - lib/ami/ami_builder/ubuntu_precise.rb
67
+ - lib/ami/commands/create_ami.rb
68
+ - lib/ami/commands/list.rb
69
+ - lib/ami/config.rb
70
+ - lib/ami/config_loader.rb
71
+ - lib/ami/core.rb
72
+ - lib/ami/definition.rb
73
+ - lib/ami/installer/ruby_under_rbenv.rb
74
+ - lib/ami/instance_provisioner.rb
75
+ - lib/ami/server.rb
76
+ - lib/ami/ssh.rb
77
+ - lib/ami/version.rb
78
+ homepage: https://github.com/rbone/ami
79
+ licenses: []
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubyforge_project:
98
+ rubygems_version: 1.8.23
99
+ signing_key:
100
+ specification_version: 3
101
+ summary: A tool to assist in building EC2 AMIs
102
+ test_files: []