cfer 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,6 +6,9 @@ module Cfer::Util
6
6
  class StackExistsError < CferError
7
7
  end
8
8
 
9
+ class StackDoesNotExistError < CferError
10
+ end
11
+
9
12
  class TemplateError < CferError
10
13
  attr_reader :template_backtrace
11
14
 
@@ -1,3 +1,3 @@
1
1
  module Cfer
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -0,0 +1,15 @@
1
+ require 'cferext/provisioning'
2
+
3
+ require 'base64'
4
+ require 'yaml'
5
+
6
+ module CferExt::AWS::AutoScaling
7
+ class LaunchConfiguration < Cfer::Cfn::Resource
8
+ include CferExt::Provisioning
9
+
10
+ def initialize(name, type, options = {}, &block)
11
+ options[:Metadata] ||= {}
12
+ super(name, type, options, &block)
13
+ end
14
+ end
15
+ end
@@ -1,5 +1,8 @@
1
1
  require 'cferext/provisioning'
2
2
 
3
+ require 'base64'
4
+ require 'yaml'
5
+
3
6
  module CferExt::AWS::EC2
4
7
  class Instance < Cfer::Cfn::Resource
5
8
  include CferExt::Provisioning
@@ -1,6 +1,16 @@
1
1
  module CferExt::Provisioning
2
- def cloud_init(doc)
3
- userdata doc
2
+ def cloud_config(options)
3
+ data = ::YAML.dump(cloud_init_data.stringify_keys)
4
+ user_data Cfer::Core::Fn::base64("#cloud-config\n##{data}")
4
5
  end
6
+
7
+ def shell_init(script, options = {})
8
+ data = "#!#{options[:shell] || '/usr/bin/env bash'}\n#{cloud_init_data}"
9
+ user_data Cfer::Core::Fn::base64(data)
10
+ end
11
+
5
12
  end
6
13
 
14
+ require_relative 'provisioning/cfn-bootstrap.rb'
15
+ require_relative 'provisioning/chef.rb'
16
+
@@ -0,0 +1,186 @@
1
+ module CferExt::Provisioning
2
+
3
+ def cfn_auth(name, options = {})
4
+ self[:Metadata]['AWS::CloudFormation::Authentication'] ||= {}
5
+ self[:Metadata]['AWS::CloudFormation::Authentication'][name] = options
6
+ end
7
+
8
+ def cfn_init_setup(options = {})
9
+ self[:Metadata]['AWS::CloudFormation::Init'] = {}
10
+
11
+ script = [ "#!/bin/bash -xe\n" ]
12
+
13
+ script.concat case options[:flavor]
14
+ when :redhat, :centos, :amazon
15
+ [
16
+ "rpm -Uvh https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.amzn1.noarch.rpm\n"
17
+ ]
18
+ when :ubuntu, :debian, nil
19
+ [
20
+ "apt-get update --fix-missing\n",
21
+ "apt-get install -y python-pip\n",
22
+ "pip install setuptools\n",
23
+ "easy_install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz\n",
24
+ ]
25
+ end
26
+
27
+ script.concat [
28
+ "# Helper function\n",
29
+ "function error_exit\n",
30
+ "{\n",
31
+ ]
32
+
33
+ if options[:signal]
34
+ script.concat [
35
+ "/usr/local/bin/cfn-signal",
36
+ " -s false",
37
+ " --resource '", options[:signal], "'",
38
+ " --stack ", Cfer::Cfn::AWS::stack_name,
39
+ " --region ", Cfer::Cfn::AWS::region,
40
+ "\n"
41
+ ]
42
+ end
43
+
44
+ script.concat [
45
+ " exit 1\n",
46
+ "}\n"
47
+ ]
48
+
49
+
50
+ script.concat [
51
+ "/usr/local/bin/cfn-init",
52
+ " --configsets '", Cfer::Core::Fn::join(',', options[:cfn_init_config_set]) || raise('Please specify a `cfn_init_config_set`'), "'",
53
+ " --stack ", Cfer::Cfn::AWS::stack_name,
54
+ " --resource ", @name,
55
+ " --region ", Cfer::Cfn::AWS::region,
56
+ " || error_exit 'Failed to run cfn-init'\n"
57
+ ]
58
+
59
+ if options[:cfn_hup_config_set]
60
+ cfn_hup(options)
61
+
62
+ script.concat [
63
+ "/usr/local/bin/cfn-hup || error_exit 'Failed to start cfn-hup'\n"
64
+ ]
65
+ end
66
+
67
+ if options[:signal]
68
+ script.concat [
69
+ "/usr/local/bin/cfn-signal",
70
+ " -s true",
71
+ " --resource '", options[:signal], "'",
72
+ " --stack ", Cfer::Cfn::AWS::stack_name,
73
+ " --region ", Cfer::Cfn::AWS::region,
74
+ "\n"
75
+ ]
76
+ end
77
+
78
+ user_data Cfer::Core::Fn::base64(
79
+ Cfer::Core::Fn::join('', script)
80
+ )
81
+ end
82
+
83
+ def config_set(name)
84
+ { "ConfigSet" => name }
85
+ end
86
+
87
+ def cfn_init_config_set(name, sections)
88
+ cfg_sets = cloudformation_init['configSets'] || {}
89
+ cfg_set = Set.new(cfg_sets[name] || [])
90
+ cfg_set.merge sections
91
+ cfg_sets[name] = cfg_set.to_a
92
+ cloudformation_init['configSets'] = cfg_sets
93
+ end
94
+
95
+ def cfn_init_config(name, options = {}, &block)
96
+ cfg = ConfigSet.new(cloudformation_init[name])
97
+ cfg.instance_eval(&block)
98
+ cloudformation_init[name] = cfg.to_h
99
+ end
100
+
101
+ private
102
+
103
+ class ConfigSet
104
+ def initialize(hash)
105
+ @config_set = hash || {}
106
+ end
107
+
108
+ def to_h
109
+ @config_set
110
+ end
111
+
112
+ def commands
113
+ @config_set['commands'] ||= {}
114
+ end
115
+
116
+ def files
117
+ @config_set['files'] ||= {}
118
+ end
119
+
120
+ def packages
121
+ @config_set['packages'] ||= {}
122
+ end
123
+
124
+ def command(name, cmd, options = {})
125
+ commands[name] = options.merge('command' => cmd)
126
+ end
127
+
128
+ def file(path, options = {})
129
+ files[path] = options
130
+ end
131
+
132
+ def package(type, name, versions = [])
133
+ packages[type] ||= {}
134
+ packages[type][name] = versions
135
+ end
136
+ end
137
+
138
+ def cloudformation_init(options = {})
139
+ raise "Set up cfn-init using cfn_init_setup first" unless self[:Metadata]['AWS::CloudFormation::Init']
140
+ self[:Metadata]['AWS::CloudFormation::Init']
141
+ end
142
+
143
+
144
+ def cfn_hup(options)
145
+ resource_name = @name
146
+ target_config_set = options[:cfn_hup_config_set] || raise('Please specify a `cfn_hup_config_set`')
147
+
148
+ cfn_init_config_set :cfn_hup, [ :cfn_hup ]
149
+
150
+ cfn_init_config(:cfn_hup) do
151
+ if options[:access_key] && options[:secret_key]
152
+ file '/etc/cfn/cfn-credentials', content: Cfer::Core::Fn::join('', [
153
+ "AWSAccessKeyId=", options[:access_key], "\n",
154
+ "AWSSecretKey=", options[:secret_key], "\n"
155
+ ]),
156
+ mode: '000400',
157
+ owner: 'root',
158
+ group: 'root'
159
+ end
160
+
161
+ file '/etc/cfn/cfn-hup.conf', content: Cfer::Core::Fn::join('', [
162
+ "[main]\n",
163
+ "stack=", Cfer::Cfn::AWS::stack_name, "\n",
164
+ "region=", Cfer::Cfn::AWS::region, "\n",
165
+ "interval=", options[:interval] || 1, "\n"
166
+ ]),
167
+ mode: '000400',
168
+ owner: 'root',
169
+ group: 'root'
170
+
171
+ file '/etc/cfn/hooks.d/cfn-init-reload.conf', content: Cfer::Core::Fn::join('', [
172
+ "[cfn-auto-reloader-hook]\n",
173
+ "triggers=post.update\n",
174
+ "path=Resources.#{resource_name}.Metadata.AWS::CloudFormation::Init\n",
175
+ "action=/usr/local/bin/cfn-init",
176
+ " -c '", Cfer::Core::Fn::join(',', target_config_set), "'",
177
+ " -s ", Cfer::Cfn::AWS::stack_name,
178
+ " --region ", Cfer::Cfn::AWS::region,
179
+ " -r #{resource_name}",
180
+ "\n",
181
+ "runas=root\n"
182
+ ])
183
+ end
184
+ end
185
+ end
186
+
@@ -0,0 +1,75 @@
1
+ require 'erubis'
2
+
3
+ module CferExt::Provisioning
4
+
5
+ def install_chef(options = {})
6
+ setup_set = [ :install_chef ]
7
+ run_set = [ :run_chef ]
8
+ cfn_init_config :install_chef do
9
+ command :install_chef, "curl https://www.opscode.com/chef/install.sh | bash -s -- -v #{options[:version] || 'latest'}"
10
+ command :make_ohai_hints, 'mkdir -p /etc/chef/ohai/hints && touch /etc/chef/ohai/hints/ec2.json'
11
+ command :make_chefdir, 'mkdir -p /var/chef/cookbooks && mkdir -p /var/chef/data_bags'
12
+ end
13
+
14
+ if options[:berksfile]
15
+ cfn_init_config :install_berkshelf do
16
+ file '/var/chef/berkshelf.sh', content: <<-EOF.strip_heredoc
17
+ export BERKSHELF_PATH=/var/chef/berkshelf
18
+
19
+ # Some cookbooks have UTF-8, and cfn-init uses US-ASCII because of reasons
20
+ export LANG=en_US.UTF-8
21
+ export RUBYOPTS="-E utf-8"
22
+
23
+ # Berkshelf seems a bit unreliable, so retry these commands a couple times.
24
+ if [ -e Berksfile.lock ]
25
+ then
26
+ for i in {1..3}; do /opt/chef/embedded/bin/berks update && break || sleep 15; done
27
+ fi
28
+ for i in {1..3}; do /opt/chef/embedded/bin/berks vendor /var/chef/cookbooks -b /var/chef/Berksfile && break || sleep 15; done
29
+ EOF
30
+ command :install_berkshelf, '/opt/chef/embedded/bin/gem install berkshelf --no-ri --no-rdoc'
31
+ command :install_git, 'apt-get install -y git'
32
+ end
33
+ setup_set.append :install_berkshelf
34
+ end
35
+
36
+ if options[:berksfile]
37
+ cfn_init_config :run_berkshelf do
38
+ file '/var/chef/Berksfile', content: options[:berksfile].strip_heredoc
39
+ command :run_berkshelf, 'bash -l /var/chef/berkshelf.sh', cwd: '/var/chef'
40
+ end
41
+ run_set.prepend :run_berkshelf
42
+ end
43
+
44
+ cfn_init_config_set :install_chef, setup_set
45
+ cfn_init_config_set :run_chef, run_set
46
+
47
+ end
48
+
49
+ def chef_solo(options = {})
50
+ raise "Chef already configured on this resource" if @chef
51
+ @chef = true
52
+
53
+ chef_config = options[:node] || raise('`node` required when setting up chef')
54
+
55
+ install_chef(options)
56
+
57
+ cfn_init_config :run_chef do
58
+ file "/etc/chef/solo.rb", content: options[:solo_rb] || Cfer::Core::Fn::join("\n", [
59
+ "cookbook_path '/var/chef/cookbooks'",
60
+ "log_location '/var/log/chef-client.log'"
61
+ ]),
62
+ owner: 'root',
63
+ group: 'root'
64
+
65
+ file "/etc/chef/config.json", content: chef_config.to_json,
66
+ owner: 'root',
67
+ group: 'root'
68
+
69
+ chef_cmd = 'chef-solo -c /etc/chef/solo.rb -j /etc/chef/config.json'
70
+ chef_cmd << " -o '#{options[:run_list].join(',')}'" if options[:run_list]
71
+ command :run_chef, chef_cmd
72
+ end
73
+ end
74
+ end
75
+
metadata CHANGED
@@ -1,209 +1,225 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cfer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Edwards
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-20 00:00:00.000000000 Z
11
+ date: 2016-02-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: docile
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ! '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ! '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: thor
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ! '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ! '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: activesupport
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ! '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ! '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: aws-sdk
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ! '>='
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ! '>='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: aws-sdk-resources
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ! '>='
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ! '>='
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: preconditions
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ! '>='
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ! '>='
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: semantic
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ! '>='
101
+ - - ">="
102
102
  - !ruby/object:Gem::Version
103
103
  version: '0'
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ! '>='
108
+ - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: rainbow
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - ! '>='
115
+ - - ">="
116
116
  - !ruby/object:Gem::Version
117
117
  version: '0'
118
118
  type: :runtime
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - ! '>='
122
+ - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: highline
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
- - - ! '>='
129
+ - - ">="
130
130
  - !ruby/object:Gem::Version
131
131
  version: '0'
132
132
  type: :runtime
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
- - - ! '>='
136
+ - - ">="
137
137
  - !ruby/object:Gem::Version
138
138
  version: '0'
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: table_print
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
- - - ! '>='
143
+ - - ">="
144
144
  - !ruby/object:Gem::Version
145
145
  version: '0'
146
146
  type: :runtime
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
- - - ! '>='
150
+ - - ">="
151
151
  - !ruby/object:Gem::Version
152
152
  version: '0'
153
153
  - !ruby/object:Gem::Dependency
154
154
  name: rake
155
155
  requirement: !ruby/object:Gem::Requirement
156
156
  requirements:
157
- - - ! '>='
157
+ - - ">="
158
158
  - !ruby/object:Gem::Version
159
159
  version: '0'
160
160
  type: :runtime
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
- - - ! '>='
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: erubis
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
165
179
  - !ruby/object:Gem::Version
166
180
  version: '0'
167
181
  - !ruby/object:Gem::Dependency
168
182
  name: bundler
169
183
  requirement: !ruby/object:Gem::Requirement
170
184
  requirements:
171
- - - ! '>='
185
+ - - ">="
172
186
  - !ruby/object:Gem::Version
173
187
  version: '0'
174
188
  type: :development
175
189
  prerelease: false
176
190
  version_requirements: !ruby/object:Gem::Requirement
177
191
  requirements:
178
- - - ! '>='
192
+ - - ">="
179
193
  - !ruby/object:Gem::Version
180
194
  version: '0'
181
195
  - !ruby/object:Gem::Dependency
182
196
  name: yard
183
197
  requirement: !ruby/object:Gem::Requirement
184
198
  requirements:
185
- - - ! '>='
199
+ - - ">="
186
200
  - !ruby/object:Gem::Version
187
201
  version: '0'
188
202
  type: :development
189
203
  prerelease: false
190
204
  version_requirements: !ruby/object:Gem::Requirement
191
205
  requirements:
192
- - - ! '>='
206
+ - - ">="
193
207
  - !ruby/object:Gem::Version
194
208
  version: '0'
195
209
  description: Toolkit for automating infrastructure using AWS CloudFormation
196
210
  email:
197
- - stedwards87+git@gmail.com
211
+ - stedwards87+cfer@gmail.com
198
212
  executables:
199
213
  - cfer
200
214
  extensions: []
201
215
  extra_rdoc_files: []
202
216
  files:
203
- - .gitignore
204
- - .rspec
205
- - .travis.yml
206
- - .yardopts
217
+ - ".codeclimate.yml"
218
+ - ".gitignore"
219
+ - ".rspec"
220
+ - ".rubocop.yml"
221
+ - ".travis.yml"
222
+ - ".yardopts"
207
223
  - CODE_OF_CONDUCT.md
208
224
  - Dockerfile
209
225
  - Gemfile
@@ -214,8 +230,10 @@ files:
214
230
  - bin/cfer-dbg
215
231
  - bin/console
216
232
  - bin/setup
217
- - cfer-demo.gif
218
233
  - cfer.gemspec
234
+ - doc/cfer-demo.gif
235
+ - examples/chef_instance.rb
236
+ - examples/common/instance_deps.rb
219
237
  - examples/instance.rb
220
238
  - examples/vpc.rb
221
239
  - lib/cfer.rb
@@ -229,8 +247,11 @@ files:
229
247
  - lib/cfer/core/stack.rb
230
248
  - lib/cfer/util/error.rb
231
249
  - lib/cfer/version.rb
250
+ - lib/cferext/aws/auto_scaling/launch_configuration.rb
232
251
  - lib/cferext/aws/ec2/instance.rb
233
252
  - lib/cferext/provisioning.rb
253
+ - lib/cferext/provisioning/cfn-bootstrap.rb
254
+ - lib/cferext/provisioning/chef.rb
234
255
  homepage: https://github.com/seanedwards/cfer
235
256
  licenses:
236
257
  - MIT
@@ -241,12 +262,12 @@ require_paths:
241
262
  - lib
242
263
  required_ruby_version: !ruby/object:Gem::Requirement
243
264
  requirements:
244
- - - ! '>='
265
+ - - ">="
245
266
  - !ruby/object:Gem::Version
246
267
  version: '0'
247
268
  required_rubygems_version: !ruby/object:Gem::Requirement
248
269
  requirements:
249
- - - ! '>='
270
+ - - ">="
250
271
  - !ruby/object:Gem::Version
251
272
  version: '0'
252
273
  requirements: []
@@ -256,3 +277,4 @@ signing_key:
256
277
  specification_version: 4
257
278
  summary: Toolkit for automating infrastructure using AWS CloudFormation
258
279
  test_files: []
280
+ has_rdoc: