ami 1.0.0

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.
@@ -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: []