automan 2.1.2

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.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rvmrc +1 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +29 -0
  8. data/Rakefile +6 -0
  9. data/automan.gemspec +30 -0
  10. data/bin/baker +4 -0
  11. data/bin/mover +4 -0
  12. data/bin/scanner +4 -0
  13. data/bin/snapper +4 -0
  14. data/bin/stacker +4 -0
  15. data/bin/stalker +4 -0
  16. data/lib/automan.rb +27 -0
  17. data/lib/automan/base.rb +64 -0
  18. data/lib/automan/beanstalk/application.rb +74 -0
  19. data/lib/automan/beanstalk/configuration.rb +137 -0
  20. data/lib/automan/beanstalk/deployer.rb +193 -0
  21. data/lib/automan/beanstalk/errors.rb +22 -0
  22. data/lib/automan/beanstalk/package.rb +39 -0
  23. data/lib/automan/beanstalk/router.rb +102 -0
  24. data/lib/automan/beanstalk/terminator.rb +60 -0
  25. data/lib/automan/beanstalk/uploader.rb +58 -0
  26. data/lib/automan/beanstalk/version.rb +100 -0
  27. data/lib/automan/chef/uploader.rb +30 -0
  28. data/lib/automan/cli/baker.rb +63 -0
  29. data/lib/automan/cli/base.rb +14 -0
  30. data/lib/automan/cli/mover.rb +47 -0
  31. data/lib/automan/cli/scanner.rb +24 -0
  32. data/lib/automan/cli/snapper.rb +78 -0
  33. data/lib/automan/cli/stacker.rb +106 -0
  34. data/lib/automan/cli/stalker.rb +279 -0
  35. data/lib/automan/cloudformation/errors.rb +40 -0
  36. data/lib/automan/cloudformation/launcher.rb +196 -0
  37. data/lib/automan/cloudformation/replacer.rb +102 -0
  38. data/lib/automan/cloudformation/terminator.rb +61 -0
  39. data/lib/automan/cloudformation/uploader.rb +57 -0
  40. data/lib/automan/ec2/errors.rb +4 -0
  41. data/lib/automan/ec2/image.rb +137 -0
  42. data/lib/automan/ec2/instance.rb +83 -0
  43. data/lib/automan/mixins/aws_caller.rb +115 -0
  44. data/lib/automan/mixins/utils.rb +18 -0
  45. data/lib/automan/rds/errors.rb +7 -0
  46. data/lib/automan/rds/snapshot.rb +244 -0
  47. data/lib/automan/s3/downloader.rb +25 -0
  48. data/lib/automan/s3/uploader.rb +20 -0
  49. data/lib/automan/version.rb +3 -0
  50. data/lib/automan/wait_rescuer.rb +17 -0
  51. data/spec/beanstalk/application_spec.rb +49 -0
  52. data/spec/beanstalk/configuration_spec.rb +98 -0
  53. data/spec/beanstalk/deployer_spec.rb +162 -0
  54. data/spec/beanstalk/package_spec.rb +9 -0
  55. data/spec/beanstalk/router_spec.rb +65 -0
  56. data/spec/beanstalk/terminator_spec.rb +67 -0
  57. data/spec/beanstalk/uploader_spec.rb +53 -0
  58. data/spec/beanstalk/version_spec.rb +60 -0
  59. data/spec/chef/uploader_spec.rb +9 -0
  60. data/spec/cloudformation/launcher_spec.rb +240 -0
  61. data/spec/cloudformation/replacer_spec.rb +58 -0
  62. data/spec/cloudformation/templates/worker_role.json +337 -0
  63. data/spec/cloudformation/terminator_spec.rb +63 -0
  64. data/spec/cloudformation/uploader_spec.rb +50 -0
  65. data/spec/ec2/image_spec.rb +158 -0
  66. data/spec/ec2/instance_spec.rb +57 -0
  67. data/spec/mixins/aws_caller_spec.rb +39 -0
  68. data/spec/mixins/utils_spec.rb +44 -0
  69. data/spec/rds/snapshot_spec.rb +152 -0
  70. metadata +278 -0
@@ -0,0 +1,279 @@
1
+ require 'automan'
2
+
3
+ module Automan::Cli
4
+ class Stalker < Base
5
+
6
+ desc "alias", "point dns to elb"
7
+
8
+ option :environment_name,
9
+ required: true,
10
+ aliases: "-e",
11
+ desc: "Beanstalk environment containing target ELB"
12
+
13
+ option :hosted_zone_name,
14
+ required: true,
15
+ aliases: "-z",
16
+ desc: "Hosted zone for which apex will be routed to ELB"
17
+
18
+ option :hostname,
19
+ required: true,
20
+ aliases: "-h",
21
+ desc: "Alias to create (must be fqdn, e.g. 'dev1.dydev.me')"
22
+
23
+ def alias
24
+ Automan::Beanstalk::Router.new(options).run
25
+ end
26
+
27
+ desc "deploy", "deploy a beanstalk service"
28
+
29
+ option :name,
30
+ required: true,
31
+ aliases: "-n",
32
+ desc: "name of the service"
33
+
34
+ option :environment,
35
+ required: true,
36
+ aliases: "-e",
37
+ desc: "environment tag (e.g. dev, stage, prd)"
38
+
39
+ option :version_label,
40
+ aliases: "-l",
41
+ desc: "beanstalk application version label"
42
+
43
+ option :package,
44
+ aliases: "-p",
45
+ desc: "s3 path to version package (s3://bucket/package.zip)"
46
+
47
+ option :manifest,
48
+ aliases: "-m",
49
+ desc: "s3 path to manifest file containing version label and package location"
50
+
51
+ option :configuration_template,
52
+ required: true,
53
+ aliases: "-t",
54
+ desc: "beanstalk configuration template name"
55
+
56
+ option :beanstalk_name,
57
+ aliases: "-b",
58
+ desc: "alternate beanstalk environment name"
59
+
60
+ option :number_to_keep,
61
+ aliases: "-n",
62
+ type: :numeric,
63
+ desc: "cull old versions and keep newest NUMBER_TO_KEEP"
64
+
65
+ def deploy
66
+ # you must specify either a manifest file or both package and version label
67
+ if options[:manifest].nil? &&
68
+ (options[:version_label].nil? || options[:package].nil?)
69
+ puts "Must specify either manifest or both version_label and package"
70
+ help "deploy"
71
+ exit 1
72
+ end
73
+
74
+ Automan::Beanstalk::Deployer.new(options).deploy
75
+ end
76
+
77
+ desc "terminate", "terminate a beanstalk environment"
78
+
79
+ option :name,
80
+ required: true,
81
+ aliases: "-n",
82
+ desc: "name of the beanstalk environment"
83
+
84
+ def terminate
85
+ Automan::Beanstalk::Terminator.new(options).terminate
86
+ end
87
+
88
+ desc "create-app", "create beanstalk application"
89
+
90
+ option :name,
91
+ required: true,
92
+ aliases: "-n",
93
+ desc: "name of the application"
94
+
95
+ def create_app
96
+ Automan::Beanstalk::Application.new(options).create
97
+ end
98
+
99
+ desc "delete-app", "delete beanstalk application"
100
+
101
+ option :name,
102
+ required: true,
103
+ aliases: "-n",
104
+ desc: "name of the application"
105
+
106
+ def delete_app
107
+ Automan::Beanstalk::Application.new(options).delete
108
+ end
109
+
110
+ desc "create-config", "create beanstalk configuration template"
111
+
112
+ option :name,
113
+ required: true,
114
+ aliases: "-n",
115
+ desc: "name of the configuration template"
116
+
117
+ option :application,
118
+ required: true,
119
+ aliases: "-a",
120
+ desc: "name of the application"
121
+
122
+ option :template,
123
+ required: true,
124
+ aliases: "-t",
125
+ desc: "file containing configuration options"
126
+
127
+ option :platform,
128
+ required: "true",
129
+ aliases: "-p",
130
+ default: "64bit Windows Server 2012 running IIS 8",
131
+ desc: "beanstalk solution stack name"
132
+
133
+ def create_config
134
+ Automan::Beanstalk::Configuration.new(options).create
135
+ end
136
+
137
+ desc "update-config", "update beanstalk configuration template"
138
+
139
+ option :name,
140
+ required: true,
141
+ aliases: "-n",
142
+ desc: "name of the configuration template"
143
+
144
+ option :application,
145
+ required: true,
146
+ aliases: "-a",
147
+ desc: "name of the application"
148
+
149
+ option :template,
150
+ required: true,
151
+ aliases: "-t",
152
+ desc: "file containing configuration options"
153
+
154
+ option :platform,
155
+ required: "true",
156
+ aliases: "-p",
157
+ default: "64bit Windows Server 2012 running IIS 8",
158
+ desc: "beanstalk solution stack name"
159
+
160
+ def update_config
161
+ Automan::Beanstalk::Configuration.new(options).update
162
+ end
163
+
164
+ desc "delete-config", "delete beanstalk configuration template"
165
+
166
+ option :name,
167
+ required: true,
168
+ aliases: "-n",
169
+ desc: "name of the configuration template"
170
+
171
+ option :application,
172
+ required: true,
173
+ aliases: "-a",
174
+ desc: "name of the application"
175
+
176
+ def delete_config
177
+ Automan::Beanstalk::Configuration.new(options).delete
178
+ end
179
+
180
+ desc "upload-config", "validate and upload beanstalk configuration template files to s3"
181
+
182
+ option :template_files,
183
+ required: true,
184
+ aliases: "-t",
185
+ desc: "File system glob of configuration templates to upload"
186
+
187
+ option :s3_path,
188
+ required: true,
189
+ aliases: "-p",
190
+ desc: "S3 path to destination"
191
+
192
+ def upload_config
193
+ Automan::Beanstalk::Uploader.new(options).upload_config_templates
194
+ end
195
+
196
+ desc "delete-version", "delete an application version"
197
+
198
+ option :application,
199
+ required: true,
200
+ aliases: "-a",
201
+ desc: "name of the application"
202
+
203
+ option :label,
204
+ required: true,
205
+ aliases: "-l",
206
+ desc: "version label to be deleted"
207
+
208
+ def delete_version
209
+ Automan::Beanstalk::Version.new(options).delete
210
+ end
211
+
212
+ desc "cull-versions", "delete oldest versions, keeping N newest"
213
+
214
+ option :application,
215
+ required: true,
216
+ aliases: "-a",
217
+ desc: "name of the application"
218
+
219
+ option :number_to_keep,
220
+ required: true,
221
+ aliases: "-n",
222
+ type: :numeric,
223
+ desc: "keep newest NUMBER_TO_KEEP versions"
224
+
225
+ def cull_versions
226
+ Automan::Beanstalk::Version.new(options).cull_versions(options[:number_to_keep])
227
+ end
228
+
229
+ desc "show-versions", "show all versions for an application"
230
+
231
+ option :application,
232
+ required: true,
233
+ aliases: "-a",
234
+ desc: "name of the application"
235
+
236
+ def show_versions
237
+ versions = Automan::Beanstalk::Version.new(options).versions
238
+ versions.each do |v|
239
+ tokens = [
240
+ v[:application_name],
241
+ v[:version_label],
242
+ v[:date_created].iso8601
243
+ ]
244
+ say tokens.join("\t")
245
+ end
246
+ end
247
+
248
+ desc "package", "upload a zip file to s3 to be deployed to beanstalk"
249
+
250
+ option :destination,
251
+ required: true,
252
+ aliases: "-d",
253
+ desc: "full s3 path of uploaded package"
254
+
255
+ option :source,
256
+ required: true,
257
+ aliases: "-s",
258
+ desc: "path to local zip file to be uploaded"
259
+
260
+ option :manifest,
261
+ aliases: "-m",
262
+ desc: "if set, also upload a manifest file with full s3 path MANIFEST"
263
+
264
+ option :version_label,
265
+ aliases: "-l",
266
+ desc: "version_label for manifest, required if -m"
267
+
268
+ def package
269
+
270
+ if !options[:manifest].nil? && options[:version_label].nil?
271
+ puts "Must specify version_label when uploading manifest file"
272
+ help "package"
273
+ exit 1
274
+ end
275
+
276
+ Automan::Beanstalk::Package.new(options).upload_package
277
+ end
278
+ end
279
+ end
@@ -0,0 +1,40 @@
1
+ module Automan::Cloudformation
2
+ class MissingParametersError < StandardError
3
+ end
4
+
5
+ class BadTemplateError < StandardError
6
+ end
7
+
8
+ class StackExistsError < StandardError
9
+ end
10
+
11
+ class StackDoesNotExistError < StandardError
12
+ end
13
+
14
+ class StackBrokenError < StandardError
15
+ end
16
+
17
+ class StackDeletionError < StandardError
18
+ end
19
+
20
+ class StackCreationError < StandardError
21
+ end
22
+
23
+ class StackUpdateError < StandardError
24
+ end
25
+
26
+ class WaitTimedOutError < StandardError
27
+ end
28
+
29
+ class MissingAutoScalingGroupError < StandardError
30
+ end
31
+
32
+ class MissingManifestError < StandardError
33
+ end
34
+
35
+ class NoTemplatesError < StandardError
36
+ end
37
+
38
+ class InvalidTemplateError < StandardError
39
+ end
40
+ end
@@ -0,0 +1,196 @@
1
+ require 'automan'
2
+ require 'json'
3
+
4
+ # TODO: add timeout
5
+
6
+ module Automan::Cloudformation
7
+ class Launcher < Automan::Base
8
+ add_option :name,
9
+ :template,
10
+ :disable_rollback,
11
+ :enable_iam,
12
+ :enable_update,
13
+ :parameters,
14
+ :manifest,
15
+ :wait_for_completion
16
+
17
+ include Automan::Mixins::Utils
18
+
19
+ def initialize(options=nil)
20
+ @wait_for_completion = false
21
+ super
22
+ @wait = Wait.new({
23
+ delay: 120,
24
+ attempts: 15, # 15 x 2m == 30m
25
+ debug: true,
26
+ rescuer: WaitRescuer.new(),
27
+ logger: @logger
28
+ })
29
+ end
30
+
31
+ def manifest_exists?
32
+ s3_object_exists? manifest
33
+ end
34
+
35
+ def read_manifest
36
+ if !manifest_exists?
37
+ raise MissingManifestError, "Manifest #{manifest} does not exist"
38
+ end
39
+
40
+ # read manifest from s3 location
41
+ json = s3_read(manifest)
42
+
43
+ # parse it from json
44
+ data = JSON.parse json
45
+
46
+ # merge manifest parameters into cli parameters
47
+ parameters.merge!(data)
48
+ end
49
+
50
+ def template_handle(template_path)
51
+ if looks_like_s3_path? template_path
52
+ bucket, key = parse_s3_path template_path
53
+ return s3.buckets[bucket].objects[key]
54
+ else
55
+ return File.read(template_path)
56
+ end
57
+ end
58
+
59
+ def parse_template_parameters
60
+ cfn.validate_template( template_handle(template) )
61
+ end
62
+
63
+ def validate_parameters
64
+ if parameters.nil?
65
+ raise MissingParametersError, "there are no parameters!"
66
+ end
67
+
68
+ template_params_hash = parse_template_parameters
69
+
70
+ if template_params_hash.has_key? :code
71
+ mesg = template_params_hash[:message]
72
+ raise BadTemplateError, "template did not validate: #{mesg}"
73
+ end
74
+
75
+ # make sure any template parameters w/o a default are specified
76
+ template_params = template_params_hash[:parameters]
77
+ required_params = template_params.select { |x| !x.has_key? :default_value}
78
+ required_params.each do |rp|
79
+ unless parameters.has_key? rp[:parameter_key]
80
+ raise MissingParametersError, "required parameter #{rp[:parameter_key]} was not specified"
81
+ end
82
+ end
83
+
84
+ # add iam capabilities if needed
85
+ if template_params_hash.has_key?(:capabilities) &&
86
+ template_params_hash[:capabilities].include?('CAPABILITY_IAM')
87
+ self.enable_iam = true
88
+ end
89
+
90
+ end
91
+
92
+ def stack_exists?
93
+ cfn.stacks[name].exists?
94
+ end
95
+
96
+ def stack_status
97
+ cfn.stacks[name].status
98
+ end
99
+
100
+ def stack_launch_complete?
101
+ case stack_status
102
+ when 'CREATE_COMPLETE'
103
+ true
104
+ when 'CREATE_FAILED', /^ROLLBACK_/
105
+ raise StackCreationError, "Stack #{name} failed to launch"
106
+ else
107
+ false
108
+ end
109
+ end
110
+
111
+ def stack_update_complete?
112
+ case stack_status
113
+ when 'UPDATE_COMPLETE'
114
+ true
115
+ when 'UPDATE_FAILED', /^UPDATE_ROLLBACK_/
116
+ raise StackUpdateError, "Stack #{name} failed to update"
117
+ else
118
+ false
119
+ end
120
+ end
121
+
122
+ def launch
123
+ opts = {
124
+ parameters: parameters
125
+ }
126
+ opts[:capabilities] = ['CAPABILITY_IAM'] if enable_iam
127
+ opts[:disable_rollback] = disable_rollback
128
+
129
+ logger.info "launching stack #{name}"
130
+ cfn.stacks.create name, template_handle(template), opts
131
+
132
+ if wait_for_completion
133
+ logger.info "waiting for stack #{name} to launch"
134
+ wait_until { stack_launch_complete? }
135
+ end
136
+ end
137
+
138
+ def update_stack(name, opts)
139
+ cfn.stacks[name].update( opts )
140
+ end
141
+
142
+ def update
143
+ opts = {
144
+ template: template_handle(template),
145
+ parameters: parameters
146
+ }
147
+ opts[:capabilities] = ['CAPABILITY_IAM'] if enable_iam
148
+
149
+ logger.info "updating stack #{name}"
150
+
151
+ # if cfn determines that no updates are to be performed,
152
+ # it raises a ValidationError
153
+ begin
154
+ update_stack(name, opts)
155
+ rescue AWS::CloudFormation::Errors::ValidationError => e
156
+ if e.message != "No updates are to be performed."
157
+ raise e
158
+ else
159
+ logger.info e.message
160
+ end
161
+ end
162
+
163
+ if wait_for_completion
164
+ logger.info "waiting for stack #{name} to update"
165
+ wait_until { stack_update_complete? }
166
+ end
167
+
168
+ end
169
+
170
+ def launch_or_update
171
+
172
+ if !manifest.nil?
173
+ read_manifest
174
+ end
175
+
176
+ log_options
177
+
178
+ validate_parameters
179
+
180
+ if stack_exists?
181
+
182
+ logger.info "stack #{name} exists"
183
+ if enable_update == true
184
+ update
185
+ else
186
+ raise StackExistsError, "stack #{name} already exists"
187
+ end
188
+
189
+ else
190
+ launch
191
+ end
192
+
193
+ end
194
+ end
195
+
196
+ end