gersberms 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.
- checksums.yaml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +139 -0
- data/LICENSE +21 -0
- data/README.md +79 -0
- data/bin/gersberms +4 -0
- data/examples/config.yml +11 -0
- data/gersberms.gemspec +27 -0
- data/lib/gersberms.rb +7 -0
- data/lib/gersberms/cli.rb +14 -0
- data/lib/gersberms/gersberms.rb +244 -0
- data/lib/gersberms/rake_task.rb +52 -0
- data/lib/gersberms/version.rb +3 -0
- metadata +141 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 8abab76e1434ecebc2ffc47517386f6b8d05d536
|
|
4
|
+
data.tar.gz: f95ed711dac01e689ee309e62e50c52daa9c73e3
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 498254fec3073a66f3049db28e29f7e6bddf6cde9004eb439af8e5a5a397c35af9262e947c997e1182cdd85114f20a7f9b805886ee304ed843398c24857fdbd8
|
|
7
|
+
data.tar.gz: b44efaf64dba7ae4b5c13d477de7b3261dca8715dffae653cbdd05315f4e1207bc8c860fec17bd057b83139a4131956ac03ccc7d0cea5555856dd6767e271d09
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
gersberms (1.0.0)
|
|
5
|
+
aws-sdk (~> 1.0)
|
|
6
|
+
berkshelf (~> 3.2)
|
|
7
|
+
net-scp (~> 1.2)
|
|
8
|
+
net-ssh (~> 2.9)
|
|
9
|
+
rake (~> 10.4)
|
|
10
|
+
thor (~> 0.19)
|
|
11
|
+
|
|
12
|
+
GEM
|
|
13
|
+
remote: https://rubygems.org/
|
|
14
|
+
specs:
|
|
15
|
+
addressable (2.3.8)
|
|
16
|
+
aws-sdk (1.64.0)
|
|
17
|
+
aws-sdk-v1 (= 1.64.0)
|
|
18
|
+
aws-sdk-v1 (1.64.0)
|
|
19
|
+
json (~> 1.4)
|
|
20
|
+
nokogiri (>= 1.4.4)
|
|
21
|
+
berkshelf (3.2.3)
|
|
22
|
+
addressable (~> 2.3.4)
|
|
23
|
+
berkshelf-api-client (~> 1.2)
|
|
24
|
+
buff-config (~> 1.0)
|
|
25
|
+
buff-extensions (~> 1.0)
|
|
26
|
+
buff-shell_out (~> 0.1)
|
|
27
|
+
celluloid (~> 0.16.0)
|
|
28
|
+
celluloid-io (~> 0.16.1)
|
|
29
|
+
cleanroom (~> 1.0)
|
|
30
|
+
faraday (~> 0.9.0)
|
|
31
|
+
minitar (~> 0.5.4)
|
|
32
|
+
octokit (~> 3.0)
|
|
33
|
+
retryable (~> 2.0)
|
|
34
|
+
ridley (~> 4.0)
|
|
35
|
+
solve (~> 1.1)
|
|
36
|
+
thor (~> 0.19)
|
|
37
|
+
berkshelf-api-client (1.2.1)
|
|
38
|
+
faraday (~> 0.9.0)
|
|
39
|
+
buff-config (1.0.1)
|
|
40
|
+
buff-extensions (~> 1.0)
|
|
41
|
+
varia_model (~> 0.4)
|
|
42
|
+
buff-extensions (1.0.0)
|
|
43
|
+
buff-ignore (1.1.1)
|
|
44
|
+
buff-ruby_engine (0.1.0)
|
|
45
|
+
buff-shell_out (0.2.0)
|
|
46
|
+
buff-ruby_engine (~> 0.1.0)
|
|
47
|
+
celluloid (0.16.0)
|
|
48
|
+
timers (~> 4.0.0)
|
|
49
|
+
celluloid-io (0.16.2)
|
|
50
|
+
celluloid (>= 0.16.0)
|
|
51
|
+
nio4r (>= 1.1.0)
|
|
52
|
+
cleanroom (1.0.0)
|
|
53
|
+
coderay (1.1.0)
|
|
54
|
+
dep-selector-libgecode (1.0.2)
|
|
55
|
+
dep_selector (1.0.3)
|
|
56
|
+
dep-selector-libgecode (~> 1.0)
|
|
57
|
+
ffi (~> 1.9)
|
|
58
|
+
diff-lcs (1.2.5)
|
|
59
|
+
erubis (2.7.0)
|
|
60
|
+
faraday (0.9.1)
|
|
61
|
+
multipart-post (>= 1.2, < 3)
|
|
62
|
+
ffi (1.9.8)
|
|
63
|
+
hashie (2.1.2)
|
|
64
|
+
hitimes (1.2.2)
|
|
65
|
+
json (1.8.2)
|
|
66
|
+
method_source (0.8.2)
|
|
67
|
+
mini_portile (0.6.2)
|
|
68
|
+
minitar (0.5.4)
|
|
69
|
+
mixlib-authentication (1.3.0)
|
|
70
|
+
mixlib-log
|
|
71
|
+
mixlib-log (1.6.0)
|
|
72
|
+
multipart-post (2.0.0)
|
|
73
|
+
net-http-persistent (2.9.4)
|
|
74
|
+
net-scp (1.2.1)
|
|
75
|
+
net-ssh (>= 2.6.5)
|
|
76
|
+
net-ssh (2.9.2)
|
|
77
|
+
nio4r (1.1.0)
|
|
78
|
+
nokogiri (1.6.6.2)
|
|
79
|
+
mini_portile (~> 0.6.0)
|
|
80
|
+
octokit (3.8.0)
|
|
81
|
+
sawyer (~> 0.6.0, >= 0.5.3)
|
|
82
|
+
pry (0.10.1)
|
|
83
|
+
coderay (~> 1.1.0)
|
|
84
|
+
method_source (~> 0.8.1)
|
|
85
|
+
slop (~> 3.4)
|
|
86
|
+
rake (10.4.2)
|
|
87
|
+
retryable (2.0.1)
|
|
88
|
+
ridley (4.1.2)
|
|
89
|
+
addressable
|
|
90
|
+
buff-config (~> 1.0)
|
|
91
|
+
buff-extensions (~> 1.0)
|
|
92
|
+
buff-ignore (~> 1.1)
|
|
93
|
+
buff-shell_out (~> 0.1)
|
|
94
|
+
celluloid (~> 0.16.0)
|
|
95
|
+
celluloid-io (~> 0.16.1)
|
|
96
|
+
erubis
|
|
97
|
+
faraday (~> 0.9.0)
|
|
98
|
+
hashie (>= 2.0.2, < 3.0.0)
|
|
99
|
+
json (>= 1.7.7)
|
|
100
|
+
mixlib-authentication (>= 1.3.0)
|
|
101
|
+
net-http-persistent (>= 2.8)
|
|
102
|
+
retryable (>= 2.0.0)
|
|
103
|
+
semverse (~> 1.1)
|
|
104
|
+
varia_model (~> 0.4)
|
|
105
|
+
rspec (3.2.0)
|
|
106
|
+
rspec-core (~> 3.2.0)
|
|
107
|
+
rspec-expectations (~> 3.2.0)
|
|
108
|
+
rspec-mocks (~> 3.2.0)
|
|
109
|
+
rspec-core (3.2.3)
|
|
110
|
+
rspec-support (~> 3.2.0)
|
|
111
|
+
rspec-expectations (3.2.1)
|
|
112
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
113
|
+
rspec-support (~> 3.2.0)
|
|
114
|
+
rspec-mocks (3.2.1)
|
|
115
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
116
|
+
rspec-support (~> 3.2.0)
|
|
117
|
+
rspec-support (3.2.2)
|
|
118
|
+
sawyer (0.6.0)
|
|
119
|
+
addressable (~> 2.3.5)
|
|
120
|
+
faraday (~> 0.8, < 0.10)
|
|
121
|
+
semverse (1.2.1)
|
|
122
|
+
slop (3.6.0)
|
|
123
|
+
solve (1.2.1)
|
|
124
|
+
dep_selector (~> 1.0)
|
|
125
|
+
semverse (~> 1.1)
|
|
126
|
+
thor (0.19.1)
|
|
127
|
+
timers (4.0.1)
|
|
128
|
+
hitimes
|
|
129
|
+
varia_model (0.4.0)
|
|
130
|
+
buff-extensions (~> 1.0)
|
|
131
|
+
hashie (>= 2.0.2, < 3.0.0)
|
|
132
|
+
|
|
133
|
+
PLATFORMS
|
|
134
|
+
ruby
|
|
135
|
+
|
|
136
|
+
DEPENDENCIES
|
|
137
|
+
gersberms!
|
|
138
|
+
pry
|
|
139
|
+
rspec
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2014 Joseph Glanville <jpg@jpg.id.au>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Gersberms
|
|
2
|
+
|
|
3
|
+
Build AMIs the right way with chef-solo and Berkshelf.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
For now Gersberms is not yet released on RubyGems.org, so currently you will need to install from
|
|
8
|
+
Github.
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
Gersberms can be used via the CLI, as a library or as a Rake task.
|
|
13
|
+
|
|
14
|
+
All of the following are equivalent
|
|
15
|
+
|
|
16
|
+
### CLI
|
|
17
|
+
|
|
18
|
+
To use the CLI you must create a YAML formatted config file.
|
|
19
|
+
|
|
20
|
+
```yaml
|
|
21
|
+
ssh_user: ubuntu
|
|
22
|
+
base_ami: ami-950b62af
|
|
23
|
+
type: t2.micro
|
|
24
|
+
ami_name: example-ami
|
|
25
|
+
security_groups:
|
|
26
|
+
- example_security_group
|
|
27
|
+
runlist:
|
|
28
|
+
- example_cookbook::default
|
|
29
|
+
json:
|
|
30
|
+
example_cookbook:
|
|
31
|
+
example_attribute: example_value
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Which you can then use with the `bake` command like so:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
gersberms bake --config=config.yml
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Library
|
|
41
|
+
```ruby
|
|
42
|
+
require 'gersberms'
|
|
43
|
+
|
|
44
|
+
config = {
|
|
45
|
+
ssh_user: 'ubuntu',
|
|
46
|
+
base_ami: 'ami-950b62af'
|
|
47
|
+
instance_type: 't2.micro',
|
|
48
|
+
ami_name: 'example-ami',
|
|
49
|
+
security_groups: ['example_security_group'],
|
|
50
|
+
runlist: ['example_cookbook::default'],
|
|
51
|
+
json: {
|
|
52
|
+
example_cookbook: {
|
|
53
|
+
example_attribute: "example_value"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
Gersberms.bake(config)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Rake Task
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
require 'gersberms/rake_task'
|
|
65
|
+
|
|
66
|
+
Gersberms::RakeTask.new(:ami) do |ami|
|
|
67
|
+
ami.ssh_user = 'ubuntu'
|
|
68
|
+
ami.base_ami = 'ami-950b62af'
|
|
69
|
+
ami.instance_type = 't2.micro'
|
|
70
|
+
ami.ami_name = 'example-ami'
|
|
71
|
+
ami.security_groups = ['example_security_group']
|
|
72
|
+
ami.runlist = ['example_cookbook::default']
|
|
73
|
+
ami.json = {
|
|
74
|
+
example_cookbook: {
|
|
75
|
+
example_attribute: 'example_value'
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
end
|
|
79
|
+
```
|
data/bin/gersberms
ADDED
data/examples/config.yml
ADDED
data/gersberms.gemspec
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
|
2
|
+
require 'gersberms/version'
|
|
3
|
+
|
|
4
|
+
Gem::Specification.new do |s|
|
|
5
|
+
s.required_rubygems_version = Gem::Requirement.new('> 0') if s.respond_to? :required_rubygems_version=
|
|
6
|
+
s.version = Gersberms::VERSION
|
|
7
|
+
s.name = 'gersberms'
|
|
8
|
+
s.summary = 'Build AMIs with Chef and Berkshelf'
|
|
9
|
+
s.description = 'Gersberms is a simple system for building AWS EC2 AMIs using Chef and Berkshelf'
|
|
10
|
+
s.homepage = 'http://github.com/josephglanville/gersberms'
|
|
11
|
+
s.authors = ['Joseph Glanville']
|
|
12
|
+
s.email = 'jpg@jpg.id.au'
|
|
13
|
+
s.date = '2014-08-15'
|
|
14
|
+
s.default_executable = 'gersberms'
|
|
15
|
+
s.license = 'MIT'
|
|
16
|
+
s.files = `git ls-files | grep -v -E '(^test|^\.git|client.rb)'`.split
|
|
17
|
+
s.test_files = `git ls-files | grep ^test`.split
|
|
18
|
+
s.require_paths = ['lib']
|
|
19
|
+
s.executables = ['gersberms']
|
|
20
|
+
|
|
21
|
+
s.add_dependency('berkshelf', '~> 3.2')
|
|
22
|
+
s.add_dependency('aws-sdk', '~> 1.0')
|
|
23
|
+
s.add_dependency('net-ssh', '~> 2.9')
|
|
24
|
+
s.add_dependency('net-scp', '~> 1.2')
|
|
25
|
+
s.add_dependency('rake', '~> 10.4')
|
|
26
|
+
s.add_dependency('thor', '~> 0.19')
|
|
27
|
+
end
|
data/lib/gersberms.rb
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require 'gersberms'
|
|
2
|
+
|
|
3
|
+
module Gersberms
|
|
4
|
+
class CLI < Thor
|
|
5
|
+
desc 'bake', 'Create AMI using specified configuration file'
|
|
6
|
+
option :config, type: :string, required: true
|
|
7
|
+
def bake
|
|
8
|
+
cfg = YAML.load_file(options[:config])
|
|
9
|
+
cfg = cfg.each_with_object({}) { |(k, v), m| m[k.to_sym] = v }
|
|
10
|
+
baker = Gersberms.new(cfg)
|
|
11
|
+
baker.bake
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
require 'aws-sdk'
|
|
2
|
+
require 'berkshelf'
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'net/ssh'
|
|
5
|
+
require 'net/scp'
|
|
6
|
+
|
|
7
|
+
module Gersberms
|
|
8
|
+
class Gersberms
|
|
9
|
+
attr_accessor :options, :key_pair, :image
|
|
10
|
+
|
|
11
|
+
CHEF_PATH = '/tmp/gersberms-chef'
|
|
12
|
+
|
|
13
|
+
DEFAULT_OPTIONS = {
|
|
14
|
+
# Instance options
|
|
15
|
+
ssh_user: 'ubuntu',
|
|
16
|
+
base_ami: 'ami-950b62af',
|
|
17
|
+
instance_type: 't2.micro',
|
|
18
|
+
public_address: true,
|
|
19
|
+
security_groups: nil,
|
|
20
|
+
subnet: nil,
|
|
21
|
+
# Chef options
|
|
22
|
+
berksfile: 'Berksfile',
|
|
23
|
+
vendor_path: 'vendor/cookbooks',
|
|
24
|
+
runlist: [],
|
|
25
|
+
json: {},
|
|
26
|
+
# AMI options
|
|
27
|
+
ami_name: 'gersberms-ami',
|
|
28
|
+
tags: [],
|
|
29
|
+
accounts: [],
|
|
30
|
+
# Additional files or directories to upload
|
|
31
|
+
# files: [{ source: '.', destination: '/tmp/staging' }]
|
|
32
|
+
files: [],
|
|
33
|
+
# Gersberms options
|
|
34
|
+
logger: Logger.new(STDOUT),
|
|
35
|
+
max_ssh_attempts: 60
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
def initialize(options = {})
|
|
39
|
+
@ec2 = AWS::EC2.new
|
|
40
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def logger
|
|
44
|
+
@options[:logger]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# TODO(jpg): move this
|
|
48
|
+
def rand
|
|
49
|
+
('a'..'z').to_a.shuffle[0, 8].join
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def create_keypair
|
|
53
|
+
name = 'gersberms-' + rand
|
|
54
|
+
logger.info "Creating keypair: #{name}"
|
|
55
|
+
@key_pair = @ec2.key_pairs.create(name)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def create_instance
|
|
59
|
+
logger.info 'Creating instance'
|
|
60
|
+
create_options = {
|
|
61
|
+
image_id: @options[:base_ami],
|
|
62
|
+
instance_type: @options[:instance_type],
|
|
63
|
+
count: 1,
|
|
64
|
+
key_pair: @key_pair,
|
|
65
|
+
associate_public_ip_address: @options[:public_address]
|
|
66
|
+
}
|
|
67
|
+
create_options[:security_group_ids] = @options[:security_groups] if @options[:security_groups]
|
|
68
|
+
create_options[:subnet] = @options[:subnet] if @options[:subnet]
|
|
69
|
+
@instance = @ec2.instances.create(create_options)
|
|
70
|
+
sleep 1 until @instance.exists?
|
|
71
|
+
logger.info "Launched instance #{@instance.id}, waiting to become running"
|
|
72
|
+
sleep 1 until @instance.status == :running
|
|
73
|
+
wait_for_ssh
|
|
74
|
+
logger.info 'Instance now running'
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def wait_for_ssh
|
|
78
|
+
cmd('true')
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def ssh(&block)
|
|
82
|
+
attempts = 0
|
|
83
|
+
Net::SSH.start(
|
|
84
|
+
@instance.public_ip_address,
|
|
85
|
+
@options[:ssh_user],
|
|
86
|
+
key_data: [@key_pair.private_key],
|
|
87
|
+
&block
|
|
88
|
+
)
|
|
89
|
+
rescue Timeout::Error, Errno::EHOSTUNREACH, Errno::ECONNREFUSED => e
|
|
90
|
+
sleep 1
|
|
91
|
+
attempts += 1
|
|
92
|
+
retry unless attempts > @options[:max_ssh_attempts]
|
|
93
|
+
logger.error "Exceeded max SSH attempts: #{@options[:max_ssh_attempts]}"
|
|
94
|
+
raise
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def cmd(command)
|
|
98
|
+
ssh do |s|
|
|
99
|
+
s.exec!(command)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def chef_path(*args)
|
|
104
|
+
args.unshift(CHEF_PATH)
|
|
105
|
+
File.join(*args)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def chef_config
|
|
109
|
+
# TODO(jpg): unindent
|
|
110
|
+
StringIO.new <<-EOF
|
|
111
|
+
file_cache_path "/var/chef/cache"
|
|
112
|
+
file_backup_path "/var/chef/backup"
|
|
113
|
+
cookbook_path ['#{chef_path('cookbooks')}']
|
|
114
|
+
if Chef::VERSION.to_f < 11.8
|
|
115
|
+
role_path nil
|
|
116
|
+
else
|
|
117
|
+
role_path []
|
|
118
|
+
end
|
|
119
|
+
log_level :info
|
|
120
|
+
verbose_logging false
|
|
121
|
+
encrypted_data_bag_secret nil
|
|
122
|
+
http_proxy nil
|
|
123
|
+
http_proxy_user nil
|
|
124
|
+
http_proxy_pass nil
|
|
125
|
+
https_proxy nil
|
|
126
|
+
https_proxy_user nil
|
|
127
|
+
https_proxy_pass nil
|
|
128
|
+
no_proxy nil
|
|
129
|
+
EOF
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def chef_json
|
|
133
|
+
StringIO.new(JSON.pretty_generate(@options[:json]))
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def install_chef
|
|
137
|
+
logger.info 'Installing Chef'
|
|
138
|
+
# TODO(jpg): means to force upgrade of Chef etc.
|
|
139
|
+
cmd('which chef-solo || curl -L https://www.opscode.com/chef/install.sh | sudo bash')
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def upload_files
|
|
143
|
+
return unless @options[:files].count > 1
|
|
144
|
+
puts 'Uploading files'
|
|
145
|
+
ssh do |s|
|
|
146
|
+
@options[:files].each do |f|
|
|
147
|
+
s.scp.upload!(f[:source], f[:destination], recursive: true)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def upload_cookbooks
|
|
153
|
+
logger.info 'Vendoring cookbooks'
|
|
154
|
+
berksfile = Berkshelf::Berksfile.from_file(@options[:berksfile])
|
|
155
|
+
berksfile.vendor(@options[:vendor_path])
|
|
156
|
+
ssh do |s|
|
|
157
|
+
logger.debug "Creating #{CHEF_PATH}"
|
|
158
|
+
s.exec!("mkdir -p #{CHEF_PATH}")
|
|
159
|
+
logger.debug "Creating #{chef_path('solo.rb')}"
|
|
160
|
+
s.scp.upload!(chef_config, chef_path('solo.rb'))
|
|
161
|
+
logger.debug "Uploading cookbooks to #{chef_path('cookbooks')}"
|
|
162
|
+
s.scp.upload!(@options[:vendor_path], chef_path('cookbooks'), recursive: true)
|
|
163
|
+
logger.debug "Create #{chef_path('node.json')}"
|
|
164
|
+
s.scp.upload!(chef_json, chef_path('node.json'))
|
|
165
|
+
logger.debug s.exec!("cat #{chef_path('node.json')}")
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def run_chef
|
|
170
|
+
logger.info 'Running Chef'
|
|
171
|
+
command = 'sudo chef-solo'
|
|
172
|
+
command += " --config #{chef_path('solo.rb')}"
|
|
173
|
+
command += " --json-attributes #{chef_path('node.json')}"
|
|
174
|
+
command += " --override-runlist '#{@options[:runlist].join(',')}'"
|
|
175
|
+
logger.debug command
|
|
176
|
+
output = cmd(command)
|
|
177
|
+
logger.debug 'Chef output:'
|
|
178
|
+
logger.debug output
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def create_ami
|
|
182
|
+
logger.info "Creating AMI: #{@options[:ami_name]}"
|
|
183
|
+
@image = @instance.create_image(@options[:ami_name])
|
|
184
|
+
@options[:tags].each do |tag|
|
|
185
|
+
logger.info "Adding tag to AMI: #{tag}"
|
|
186
|
+
@image.add_tag(tag)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
if @options[:share_accounts]
|
|
190
|
+
logger.info "Sharing AMI with: #{@options[:share_accounts]}"
|
|
191
|
+
@image.permissions.add(*@options[:share_accounts])
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
logger.debug "Waiting until AMI: #{@options[:ami_name]} exists"
|
|
195
|
+
sleep 1 until @image.exists?
|
|
196
|
+
|
|
197
|
+
logger.debug "Waiting until AMI: #{@options[:ami_name]} becomes available"
|
|
198
|
+
sleep 1 until @image.state == :available
|
|
199
|
+
|
|
200
|
+
logger.info "AMI #{@image.id} created sucessfully"
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def stop_instance
|
|
204
|
+
logger.info "Stopping instance: #{@instance.id}"
|
|
205
|
+
@instance.stop
|
|
206
|
+
sleep 1 until @instance.status == :stopped
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def destroy_instance
|
|
210
|
+
return unless @instance
|
|
211
|
+
logger.info "Destroying instance: #{@instance.id}"
|
|
212
|
+
@instance.terminate
|
|
213
|
+
sleep 1 until @instance.status == :terminated
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def destroy_keypair
|
|
217
|
+
logger.info "Destroying keypair: #{@key_pair.name}"
|
|
218
|
+
@key_pair.delete if @key_pair
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def preflight
|
|
222
|
+
fail "AMI #{@options[:ami_name]} already exists" if @ec2.images[@options[:ami_name]].exists?
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def bake
|
|
226
|
+
preflight
|
|
227
|
+
create_keypair
|
|
228
|
+
create_instance
|
|
229
|
+
install_chef
|
|
230
|
+
upload_cookbooks
|
|
231
|
+
upload_files
|
|
232
|
+
run_chef
|
|
233
|
+
stop_instance
|
|
234
|
+
create_ami
|
|
235
|
+
destroy_instance
|
|
236
|
+
destroy_keypair
|
|
237
|
+
@image.id
|
|
238
|
+
rescue => e
|
|
239
|
+
logger.error "Failed!: #{e.message} \n#{e.backtrace.join("\n")}"
|
|
240
|
+
destroy_instance
|
|
241
|
+
destroy_keypair
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require 'gersberms'
|
|
2
|
+
require 'rake'
|
|
3
|
+
require 'rake/tasklib'
|
|
4
|
+
|
|
5
|
+
module Gersberms
|
|
6
|
+
# Provides a custom rake task.
|
|
7
|
+
#
|
|
8
|
+
# First require this file:
|
|
9
|
+
# require 'gersberms/rake_task'
|
|
10
|
+
#
|
|
11
|
+
# Then you can configure the task like so.
|
|
12
|
+
# Gersberms::RakeTask.new(:ami) do |ami|
|
|
13
|
+
# ami.ssh_user = 'ubuntu'
|
|
14
|
+
# ami.base_ami = 'ami-950b62af'
|
|
15
|
+
# ami.instance_type = 't2.micro'
|
|
16
|
+
# ami.security_groups = ['example_security_group']
|
|
17
|
+
# ami.subnet = 'example_subnet'
|
|
18
|
+
# ami.berksfile = 'Berksfile'
|
|
19
|
+
# ami.vendor_path = 'vendor/cookbooks'
|
|
20
|
+
# ami.runlist = ['example_cookbook::example_recipe']
|
|
21
|
+
# ami.json = {
|
|
22
|
+
# example_cookbook: {
|
|
23
|
+
# example_attribute: 'example_value'
|
|
24
|
+
# }
|
|
25
|
+
# }
|
|
26
|
+
# ami.ami_name = 'example-ami'
|
|
27
|
+
# ami.tags = ['example_tag']
|
|
28
|
+
# ami.accounts = ['example_account_id']
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
# The name you pass into the constructor will be the name of the task itself.
|
|
32
|
+
# ie. rake ami for the above example
|
|
33
|
+
class RakeTask < Rake::TaskLib
|
|
34
|
+
ATTRS = [:ssh_user, :base_ami, :instance_type, :security_groups, :accounts,
|
|
35
|
+
:subnet, :berksfile, :vendor_path, :runlist, :json, :ami_name, :tags]
|
|
36
|
+
attr_accessor(*ATTRS)
|
|
37
|
+
|
|
38
|
+
def initialize(*task_args, &task_block)
|
|
39
|
+
task_block.call(*[self, task_args].slice(0, task_block.arity)) if task_block
|
|
40
|
+
|
|
41
|
+
desc 'Build AMI using Gersberms' unless Rake.application.last_comment
|
|
42
|
+
task @name do
|
|
43
|
+
baker = Gersberms.new(to_h)
|
|
44
|
+
baker.bake
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def to_h
|
|
49
|
+
ATTRS.each_with_object({}) { m[k] = send(k) }.delete_if { |k, v| v.nil? }
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: gersberms
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Joseph Glanville
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2014-08-15 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: berkshelf
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '3.2'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '3.2'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: aws-sdk
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '1.0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '1.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: net-ssh
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '2.9'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '2.9'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: net-scp
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '1.2'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '1.2'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rake
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '10.4'
|
|
76
|
+
type: :runtime
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '10.4'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: thor
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0.19'
|
|
90
|
+
type: :runtime
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0.19'
|
|
97
|
+
description: Gersberms is a simple system for building AWS EC2 AMIs using Chef and
|
|
98
|
+
Berkshelf
|
|
99
|
+
email: jpg@jpg.id.au
|
|
100
|
+
executables:
|
|
101
|
+
- gersberms
|
|
102
|
+
extensions: []
|
|
103
|
+
extra_rdoc_files: []
|
|
104
|
+
files:
|
|
105
|
+
- Gemfile
|
|
106
|
+
- Gemfile.lock
|
|
107
|
+
- LICENSE
|
|
108
|
+
- README.md
|
|
109
|
+
- bin/gersberms
|
|
110
|
+
- examples/config.yml
|
|
111
|
+
- gersberms.gemspec
|
|
112
|
+
- lib/gersberms.rb
|
|
113
|
+
- lib/gersberms/cli.rb
|
|
114
|
+
- lib/gersberms/gersberms.rb
|
|
115
|
+
- lib/gersberms/rake_task.rb
|
|
116
|
+
- lib/gersberms/version.rb
|
|
117
|
+
homepage: http://github.com/josephglanville/gersberms
|
|
118
|
+
licenses:
|
|
119
|
+
- MIT
|
|
120
|
+
metadata: {}
|
|
121
|
+
post_install_message:
|
|
122
|
+
rdoc_options: []
|
|
123
|
+
require_paths:
|
|
124
|
+
- lib
|
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
126
|
+
requirements:
|
|
127
|
+
- - ">="
|
|
128
|
+
- !ruby/object:Gem::Version
|
|
129
|
+
version: '0'
|
|
130
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
|
+
requirements:
|
|
132
|
+
- - ">"
|
|
133
|
+
- !ruby/object:Gem::Version
|
|
134
|
+
version: '0'
|
|
135
|
+
requirements: []
|
|
136
|
+
rubyforge_project:
|
|
137
|
+
rubygems_version: 2.4.3
|
|
138
|
+
signing_key:
|
|
139
|
+
specification_version: 4
|
|
140
|
+
summary: Build AMIs with Chef and Berkshelf
|
|
141
|
+
test_files: []
|