moonshot 2.0.0.beta2 → 2.0.0.beta3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5cbd23bad6cfc674f3d79a842b49c50fe0de46cb
4
- data.tar.gz: cbe254ca2d50e6c12e3e916e3d043c054e1a18af
3
+ metadata.gz: 36e066b2e7b8b790bbdf2a6ae2f0ff2b460be924
4
+ data.tar.gz: ca20969b8fe43fe8cf7296ab3559a9af2132505c
5
5
  SHA512:
6
- metadata.gz: 1a12347cc5ce9a357b932ca7772695af0d3bf527a1c7dd44dfa02dd2e2b01b8b70c1256e22a94505633af5378a142954c9ffe3c3f8400884e13ee1734e493aab
7
- data.tar.gz: 6edbc749ab02ff73155d7e738b67661d90b2570651f092666a6cfc20e7f893692ebc1e7fa29a6010c83ad4824c02bfde7ea9919c6d2a6b824cb8e1d255a931dd
6
+ metadata.gz: afd2ff66f8f3892cebd6c66c32c90af442eb2d518b86b2c6fd315da716dba8d5097a0c16f9563ad3f85ba6664d905a1dfc0e4790c03ed83eac5288f75f0072c6
7
+ data.tar.gz: 76667b1b524cabecd1b6994c29d5ecc7b38e80fb92bfcce9d55f6620d4d1566873d612d6177ce58ca50136116e898415e4b2d74d3b2c7bc9fe3cd2ca163f1cb1
@@ -0,0 +1,12 @@
1
+ version:
2
+ os:
3
+ files:
4
+ - source:
5
+ destination:
6
+ hooks:
7
+ ApplicationStop:
8
+ - location:
9
+ BeforeInstall:
10
+ - location:
11
+ ApplicationStart:
12
+ - location:
@@ -0,0 +1,15 @@
1
+ #!/bin/bash
2
+
3
+ echo "Building version '$VERSION_NAME', using target file '$OUTPUT_FILE'."
4
+
5
+ # Add code here to assemble your project into a compressed tarball (.tar.gz)
6
+ # located at $OUTPUT_FILE. Be sure to include your appspec.yml in the tarball's
7
+ # root!
8
+ #
9
+ # For example:
10
+ #
11
+ # mkdir build_dir
12
+ # rsync -avP path-to-source build_dir/src
13
+ # cp moonshot/appspec.yml build_dir/
14
+ # tar czf $OUTPUT_FILE -C build_dir .
15
+ # rm -rf build_dir
@@ -0,0 +1,5 @@
1
+ # Place your CloudFormation template here!
2
+ ---
3
+ Resources:
4
+ Parameters:
5
+ Outputs:
@@ -4,6 +4,7 @@ require 'digest'
4
4
  require 'securerandom'
5
5
  require 'semantic'
6
6
  require 'tmpdir'
7
+ require 'retriable'
7
8
 
8
9
  module Moonshot::ArtifactRepository
9
10
  # S3 Bucket repository backed by GitHub releases.
@@ -115,25 +116,26 @@ module Moonshot::ArtifactRepository
115
116
  # @raise [RuntimeError] If the file fails to download correctly after 3
116
117
  # attempts.
117
118
  def download_from_github(version)
119
+ file_pattern = "*#{version}*.tar.gz"
118
120
  attempts = 0
119
- begin
121
+
122
+ Retriable.retriable on: RuntimeError do
120
123
  # Make sure the directory is empty before downloading the release.
121
124
  FileUtils.rm(Dir.glob('*'))
122
125
 
123
126
  # Download the release and find the actual build file.
124
127
  sh_out("hub release download #{version}")
125
- file = Dir.glob("*#{version}*.tar.gz").fetch(0)
126
128
 
129
+ raise "File '#{file_pattern}' not found." if Dir.glob(file_pattern).empty?
130
+
131
+ file = Dir.glob(file_pattern).fetch(0)
127
132
  unless (checksum = checksum_file(file)).nil?
128
133
  verify_download_checksum(file, checksum, attempt: attempts)
129
134
  end
130
- rescue RuntimeError => e
131
135
  attempts += 1
132
- retry unless attempts > 3
133
- raise e
134
- end
135
136
 
136
- file
137
+ file
138
+ end
137
139
  end
138
140
 
139
141
  # Find the checksum file for a release, if there is one.
@@ -17,8 +17,10 @@ module Moonshot::BuildMechanism
17
17
  def_delegator :@build_mechanism, :output_file
18
18
 
19
19
  # @param build_mechanism Delegates building after GitHub release is created.
20
- def initialize(build_mechanism)
20
+ def initialize(build_mechanism, ci_status_timeout: 600, max_tag_find_timeout: 240)
21
21
  @build_mechanism = build_mechanism
22
+ @ci_status_timeout = ci_status_timeout
23
+ @max_tag_find_timeout = max_tag_find_timeout
22
24
  end
23
25
 
24
26
  def doctor_hook
@@ -113,13 +115,12 @@ module Moonshot::BuildMechanism
113
115
  def git_push_tag(remote, tag)
114
116
  cmd = "git push #{remote} refs/tags/#{tag}:refs/tags/#{tag}"
115
117
  sh_step(cmd) do
116
- sleep 2 # GitHub needs a moment to register the tag.
118
+ hub_find_remote_tag(tag)
117
119
  end
118
120
  end
119
121
 
120
122
  def hub_create_release(semver, commitish, changelog_entry)
121
123
  return if hub_release_exists(semver)
122
-
123
124
  message = "#{semver}\n\n#{changelog_entry}"
124
125
  cmd = "hub release create #{semver} --commitish=#{commitish}"
125
126
  cmd << ' --prerelease' if semver.pre || semver.build
@@ -133,11 +134,24 @@ module Moonshot::BuildMechanism
133
134
  #
134
135
  # @return [Boolean]
135
136
  def hub_release_exists(semver)
136
- sh_step("hub release show #{semver}")
137
- log.info("release #{semver} already exists")
138
- true
139
- rescue RuntimeError
140
- false
137
+ exists = false
138
+ sh_step("hub release show #{semver}", fail: false) do |_, output|
139
+ first_line = output.split("\n").first
140
+ exists = !first_line.nil? && first_line.strip == semver.to_s
141
+ end
142
+ log.info("release #{semver} already exists") if exists
143
+ exists
144
+ end
145
+
146
+ # Attempt to find the build. Rescue and re-attempt if the build can not
147
+ # be found on github yet.
148
+ def hub_find_remote_tag(tag_name)
149
+ retry_opts = {
150
+ max_elapsed_time: @max_tag_find_timeout,
151
+ multiplier: 2
152
+ }
153
+ sh_retry("hub ls-remote --exit-code --tags upstream #{tag_name}",
154
+ opts: retry_opts)
141
155
  end
142
156
 
143
157
  def validate_commit
@@ -145,10 +159,7 @@ module Moonshot::BuildMechanism
145
159
  sh_step(cmd, msg: "Validate commit #{@sha}.") do |_, out|
146
160
  @commit_detail = out
147
161
  end
148
- cmd = "hub ci-status --verbose #{@sha}"
149
- sh_step(cmd, msg: "Check CI status for #{@sha}.") do |_, out|
150
- @ci_statuses = out
151
- end
162
+ @ci_statuses = check_ci_status(@sha)
152
163
  end
153
164
 
154
165
  def validate_changelog(version)
@@ -170,6 +181,25 @@ module Moonshot::BuildMechanism
170
181
  end
171
182
  end
172
183
 
184
+ # Checks for the commit's CI job status. If its not finished yet,
185
+ # wait till timeout.
186
+ #
187
+ # @param sha [String] Commit sha.
188
+ #
189
+ # @return [String] Status and links to the CI jobs
190
+ def check_ci_status(sha)
191
+ out = nil
192
+ retry_opts = {
193
+ max_elapsed_time: @ci_status_timeout,
194
+ base_interval: 10
195
+ }
196
+ ilog.start_threaded("Check CI status for #{sha}.") do |step|
197
+ out = sh_retry("hub ci-status --verbose #{sha}", opts: retry_opts)
198
+ step.success
199
+ end
200
+ out
201
+ end
202
+
173
203
  def releases_url
174
204
  `hub browse -u -- releases`.chomp
175
205
  end
@@ -68,32 +68,18 @@ module Moonshot::BuildMechanism
68
68
  #
69
69
  # @return [String] Job number for the travis build.
70
70
  def wait_for_build(version)
71
+ # Attempt to find the build. Re-attempt if the build can not
72
+ # be found on travis yet.
73
+ retry_opts = {
74
+ tries: MAX_BUILD_FIND_ATTEMPTS,
75
+ base_interval: 10
76
+ }
71
77
  job_number = nil
72
- attempts = 0
73
- loop do
74
- # Give travis some time to start the build.
75
- attempts += 1
76
- sleep 10
77
-
78
- # Attempt to find the build. Rescue and re-attempt if the build can not
79
- # be found on travis yet.
80
- begin
81
- build_out = sh_out("bundle exec travis show #{@cli_args} #{version}")
82
- rescue RuntimeError => e
83
- next unless attempts >= MAX_BUILD_FIND_ATTEMPTS
84
- raise e
85
- end
86
-
87
- unless (job_number = build_out.match(/^#(\d+\.\d+) .+BUILD=1.+/)[1])
88
- next unless attempts >= MAX_BUILD_FIND_ATTEMPTS
89
- raise "Build for #{version} not found.\n#{build_out}"
90
- end
91
-
92
- # If we've reached this point then everything went smoothly and we can
93
- # exit the loop.
94
- break
78
+ sh_retry("bundle exec travis show #{@cli_args} #{version}",
79
+ opts: retry_opts) do |build_out|
80
+ raise CommandError, "Build for #{version} not found.\n#{build_out}" \
81
+ unless (job_number = build_out.match(/^#(\d+\.\d+) .+BUILD=1.+/)[1])
95
82
  end
96
-
97
83
  job_number
98
84
  end
99
85
 
@@ -76,7 +76,10 @@ module Moonshot
76
76
  # NOTE: At the time of this patch, AWS-SDK native Waiters do not
77
77
  # have support for ChangeSets. Once they add this, we can make
78
78
  # this code much better.
79
+ # Still no support for this waiter, but it's planned.
80
+ # https://github.com/aws/aws-sdk-ruby/issues/1388
79
81
  def wait_for_change_set
82
+ wait_seconds = Moonshot.config.changeset_wait_time || 90
80
83
  start = Time.now.to_i
81
84
 
82
85
  loop do
@@ -89,11 +92,14 @@ module Moonshot
89
92
  return
90
93
  end
91
94
 
92
- if Time.now.to_i > start + 30
93
- raise 'ChangeSet did not complete creation within 30 seconds!'
95
+ if Time.now.to_i > start + wait_seconds
96
+ raise "ChangeSet did not complete creation within #{wait_seconds} seconds!"
94
97
  end
95
98
 
96
- sleep 0.25 # http://bit.ly/1qY1ZXJ
99
+ sleep 5 # http://bit.ly/1qY1ZXJ
100
+ # Wait 5 seconds because other waiters seem to wait at least 5 seconds
101
+ # before repeating requests.
102
+ # See: https://github.com/aws/aws-sdk-ruby/blob/master/aws-sdk-core/apis/cloudformation/2010-05-15/waiters-2.json#L5
97
103
  end
98
104
  end
99
105
  end
@@ -4,6 +4,14 @@ module Moonshot
4
4
  self.usage = 'deploy VERSION'
5
5
  self.description = 'Deploy a versioned release to the environment'
6
6
 
7
+ def parser
8
+ parser = super
9
+
10
+ parser.on('--[no-]interactive', TrueClass, 'Use interactive prompts.') do |v|
11
+ Moonshot.config.interactive = v
12
+ end
13
+ end
14
+
7
15
  def execute(version_name)
8
16
  controller.deploy_version(version_name)
9
17
  end
@@ -8,6 +8,11 @@ module Moonshot
8
8
  'Parent stack to import parameters from') do |v|
9
9
  Moonshot.config.parent_stacks = [v]
10
10
  end
11
+
12
+ parser.on('--parents a,b,c', Array,
13
+ 'List of parent stacks to import parameters from') do |v|
14
+ Moonshot.config.parent_stacks = v
15
+ end
11
16
  end
12
17
  end
13
18
  end
@@ -18,11 +18,15 @@ module Moonshot
18
18
  parser.on('--force', '-f', TrueClass, 'Apply ChangeSet without confirmation') do |v|
19
19
  @force = v
20
20
  end
21
+
22
+ parser.on('--refresh-parameters', TrueClass, 'Update parameters from parent stacks') do |v|
23
+ @refresh_parameters = v
24
+ end
21
25
  end
22
26
 
23
27
  def execute
24
28
  @force = true unless Moonshot.config.interactive
25
- controller.update(dry_run: @dry_run, force: @force)
29
+ controller.update(dry_run: @dry_run, force: @force, refresh_parameters: @refresh_parameters)
26
30
  end
27
31
  end
28
32
  end
@@ -63,7 +63,7 @@ module Moonshot
63
63
  end
64
64
  end
65
65
 
66
- def update(dry_run:, force:) # rubocop:disable AbcSize
66
+ def update(dry_run:, force:, refresh_parameters:) # rubocop:disable AbcSize
67
67
  # Scan the template for all required parameters and configure
68
68
  # the ParameterCollection.
69
69
  @config.parameters = ParameterCollection.from_template(stack.template)
@@ -75,7 +75,8 @@ module Moonshot
75
75
 
76
76
  # Import all Outputs from parent stacks as Parameters on this
77
77
  # stack.
78
- ParentStackParameterLoader.new(@config).load_missing_only!
78
+ parent_stack_params = ParentStackParameterLoader.new(@config)
79
+ refresh_parameters ? parent_stack_params.load! : parent_stack_params.load_missing_only!
79
80
 
80
81
  # If there is an answer file, use it to populate parameters.
81
82
  if @config.answer_file
@@ -91,9 +92,7 @@ module Moonshot
91
92
 
92
93
  # Interview the user for missing parameters, using the
93
94
  # appropriate prompts.
94
- @config.parameters.values.each do |sp|
95
- next if sp.set?
96
-
95
+ @config.parameters.values.reject(&:set?).each do |sp|
97
96
  parameter_source = @config.parameter_sources.fetch(sp.name,
98
97
  @config.default_parameter_source)
99
98
  parameter_source.get(sp)
@@ -153,9 +152,13 @@ module Moonshot
153
152
 
154
153
  run_plugins(:pre_delete)
155
154
  run_hook(:deploy, :pre_delete)
156
- stack.delete
157
- run_hook(:deploy, :post_delete)
158
- run_plugins(:post_delete)
155
+ stack_ok = stack.delete
156
+ if stack_ok # rubocop:disable GuardClause
157
+ run_hook(:deploy, :post_delete)
158
+ run_plugins(:post_delete)
159
+ else
160
+ raise 'Stack deletion failed!'
161
+ end
159
162
  end
160
163
 
161
164
  def doctor
@@ -8,6 +8,7 @@ module Moonshot
8
8
  attr_accessor :app_name
9
9
  attr_accessor :artifact_repository
10
10
  attr_accessor :build_mechanism
11
+ attr_accessor :changeset_wait_time
11
12
  attr_accessor :deployment_mechanism
12
13
  attr_accessor :dev_build_name_proc
13
14
  attr_accessor :environment_name
@@ -1,28 +1,28 @@
1
1
  module Moonshot
2
2
  # Create convenience methods for various AWS client creation.
3
3
  module CredsHelper
4
- def cf_client
5
- Aws::CloudFormation::Client.new
4
+ def cf_client(**args)
5
+ Aws::CloudFormation::Client.new(args)
6
6
  end
7
7
 
8
- def cd_client
9
- Aws::CodeDeploy::Client.new
8
+ def cd_client(**args)
9
+ Aws::CodeDeploy::Client.new(args)
10
10
  end
11
11
 
12
- def ec2_client
13
- Aws::EC2::Client.new
12
+ def ec2_client(**args)
13
+ Aws::EC2::Client.new(args)
14
14
  end
15
15
 
16
- def iam_client
17
- Aws::IAM::Client.new
16
+ def iam_client(**args)
17
+ Aws::IAM::Client.new(args)
18
18
  end
19
19
 
20
- def as_client
21
- Aws::AutoScaling::Client.new
20
+ def as_client(**args)
21
+ Aws::AutoScaling::Client.new(args)
22
22
  end
23
23
 
24
- def s3_client
25
- Aws::S3::Client.new
24
+ def s3_client(**args)
25
+ Aws::S3::Client.new(args)
26
26
  end
27
27
  end
28
28
  end
@@ -13,6 +13,8 @@ class Moonshot::DeploymentMechanism::CodeDeploy # rubocop:disable ClassLength
13
13
  include Moonshot::CredsHelper
14
14
  include Moonshot::DoctorHelper
15
15
 
16
+ DEFAULT_ROLE_NAME = 'CodeDeployRole'.freeze
17
+
16
18
  # @param asg [Array, String]
17
19
  # The logical name of the AutoScalingGroup to create and manage a Deployment
18
20
  # Group for in CodeDeploy.
@@ -37,7 +39,7 @@ class Moonshot::DeploymentMechanism::CodeDeploy # rubocop:disable ClassLength
37
39
  def initialize(
38
40
  asg: [],
39
41
  optional_asg: [],
40
- role: 'CodeDeployRole',
42
+ role: DEFAULT_ROLE_NAME,
41
43
  app_name: nil,
42
44
  group_name: nil,
43
45
  config_name: 'CodeDeployDefault.OneAtATime')
@@ -247,7 +249,35 @@ class Moonshot::DeploymentMechanism::CodeDeploy # rubocop:disable ClassLength
247
249
  def role
248
250
  iam_client.get_role(role_name: @codedeploy_role).role
249
251
  rescue Aws::IAM::Errors::NoSuchEntity
250
- raise "Did not find an IAM Role: #{@codedeploy_role}"
252
+ # Auto create the IAM Role if it does not exist in the current AWS account
253
+ ilog.start "Missing IAM Role: #{@codedeploy_role.blue}. Creating it now ..." do |s|
254
+ code_deploy_policy = {
255
+ 'Version' => '2012-10-17',
256
+ 'Statement' => [
257
+ {
258
+ 'Sid' => '',
259
+ 'Effect' => 'Allow',
260
+ 'Principal' => {
261
+ 'Service' => [
262
+ 'codedeploy.amazonaws.com'
263
+ ]
264
+ },
265
+ 'Action' => 'sts:AssumeRole'
266
+ }
267
+ ]
268
+ }
269
+
270
+ result = iam_client.create_role(
271
+ role_name: @codedeploy_role,
272
+ assume_role_policy_document: code_deploy_policy.to_json
273
+ )
274
+ iam_client.attach_role_policy(
275
+ role_name: @codedeploy_role,
276
+ policy_arn: 'arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole'
277
+ )
278
+ s.success "Created IAM Role successfully: #{@codedeploy_role.blue}"
279
+ result.role
280
+ end
251
281
  end
252
282
 
253
283
  def delete_deployment_group
@@ -372,7 +402,7 @@ class Moonshot::DeploymentMechanism::CodeDeploy # rubocop:disable ClassLength
372
402
  end
373
403
 
374
404
  def doctor_check_code_deploy_role
375
- iam_client.get_role(role_name: @codedeploy_role).role
405
+ role
376
406
  success("#{@codedeploy_role} exists.")
377
407
  rescue => e
378
408
  help = <<-EOF
@@ -1,7 +1,20 @@
1
1
  require 'thor'
2
+ require 'retriable'
2
3
 
3
4
  # Mixin providing the Thor::Shell methods and other shell execution helpers.
4
5
  module Moonshot::Shell
6
+ CommandError = Class.new(RuntimeError)
7
+
8
+ # Retry every 10 seconds for a maximum of 2 minutes.
9
+ DEFAULT_RETRY_OPTIONS = {
10
+ on: RuntimeError,
11
+ tries: 50,
12
+ multiplier: 1,
13
+ base_interval: 10,
14
+ max_elapsed_time: 120,
15
+ rand_factor: 0
16
+ }.freeze
17
+
5
18
  # Run a command, returning stdout. Stderr is suppressed unless the command
6
19
  # returns non-zero.
7
20
  def sh_out(cmd, fail: true, stdin: '')
@@ -22,7 +35,7 @@ module Moonshot::Shell
22
35
  r_err.close
23
36
 
24
37
  if fail && $CHILD_STATUS.exitstatus != 0
25
- raise "`#{cmd}` exited #{$CHILD_STATUS.exitstatus}\n" \
38
+ raise CommandError, "`#{cmd}` exited #{$CHILD_STATUS.exitstatus}\n" \
26
39
  "stdout:\n" \
27
40
  "#{stdout}\n" \
28
41
  "stderr:\n" \
@@ -51,4 +64,22 @@ module Moonshot::Shell
51
64
  step.success
52
65
  end
53
66
  end
67
+
68
+ # Retries every second upto maximum of 2 minutes with the default options.
69
+ #
70
+ # @param cmd [String] command to execute.
71
+ # @param fail [Boolean] Raise error when the command exits with non-zero.
72
+ # @param stdin [String] Input to the command.
73
+ # @param opts [Hash] Options for retriable.
74
+ #
75
+ # @return [String] Stdout form the command.
76
+ def sh_retry(cmd, fail: true, stdin: '', opts: {})
77
+ Retriable.retriable(DEFAULT_RETRY_OPTIONS.merge(opts)) do
78
+ out = sh_out(cmd, stdin: stdin)
79
+ yield out if block_given?
80
+ out
81
+ end
82
+ rescue CommandError => e
83
+ raise e if fail
84
+ end
54
85
  end
@@ -180,7 +180,7 @@ module Moonshot
180
180
  cf_client.create_stack(
181
181
  stack_name: @name,
182
182
  template_body: template.body,
183
- capabilities: ['CAPABILITY_IAM'],
183
+ capabilities: %w(CAPABILITY_IAM CAPABILITY_NAMED_IAM),
184
184
  parameters: @config.parameters.values.map(&:to_cf),
185
185
  tags: make_tags
186
186
  )
@@ -200,7 +200,7 @@ module Moonshot
200
200
  description: "Moonshot update command for application '#{Moonshot.config.app_name}'",
201
201
  stack_name: @name,
202
202
  template_body: template.body,
203
- capabilities: ['CAPABILITY_IAM'],
203
+ capabilities: %w(CAPABILITY_IAM CAPABILITY_NAMED_IAM),
204
204
  parameters: @config.parameters.values.map(&:to_cf)
205
205
  )
206
206
 
@@ -220,7 +220,7 @@ module Moonshot
220
220
  begin
221
221
  cf_client.wait_until(wait_target, stack_name: stack_id) do |w|
222
222
  w.delay = 10
223
- w.max_attempts = 180 # 30 minutes.
223
+ w.max_attempts = 360 # 60 minutes.
224
224
  w.before_wait do |attempt, resp|
225
225
  begin
226
226
  events.latest_events.each { |e| @ilog.error(format_event(e)) }
@@ -112,7 +112,11 @@ module Moonshot
112
112
 
113
113
  def instance_row(asg_instance, ec2_instance)
114
114
  if ec2_instance
115
- ip_address = ec2_instance.public_ip_address || "#{ec2_instance.private_ip_address} (PRV)"
115
+ if ec2_instance.public_ip_address
116
+ ip_address = "#{ec2_instance.public_ip_address} (#{ec2_instance.private_ip_address})"
117
+ else
118
+ ip_address = "#{ec2_instance.private_ip_address} (PRV)"
119
+ end
116
120
  uptime = uptime_format(ec2_instance.launch_time)
117
121
  else
118
122
  # We've seen race conditions where ASG tells us about instances that EC2 is no longer
@@ -8,9 +8,10 @@ module Moonshot
8
8
 
9
9
  def print
10
10
  o_table = @table.add_leaf('Stack Outputs')
11
- @stack.outputs.each do |k, v|
12
- o_table.add_line("#{k}: #{v}")
11
+ rows = @stack.outputs.sort.map do |key, value|
12
+ ["#{key}:", value]
13
13
  end
14
+ o_table.add_table(rows)
14
15
  end
15
16
  end
16
17
  end
@@ -14,7 +14,8 @@ module Moonshot
14
14
  :hooks,
15
15
  :target_name,
16
16
  :backup_parameters,
17
- :backup_template
17
+ :backup_template,
18
+ :bucket_region
18
19
 
19
20
  def initialize
20
21
  yield self if block_given?
@@ -99,10 +100,10 @@ module Moonshot
99
100
  end
100
101
 
101
102
  # adding parameters
102
- if @backup_parameters
103
+ if @backup_parameters && Moonshot.config.answer_file
103
104
  add_str_to_tar(
104
105
  writer,
105
- render('%{stack_name}-parameters.yml'),
106
+ Moonshot.config.answer_file,
106
107
  @parameters
107
108
  )
108
109
  end
@@ -162,7 +163,9 @@ module Moonshot
162
163
  #
163
164
  # @param io_zip [IO] tar stream
164
165
  def upload(io_zip)
165
- s3_client.put_object(
166
+ opts = {}
167
+ opts[:region] = @bucket_region if @bucket_region
168
+ s3_client(opts).put_object(
166
169
  acl: 'private',
167
170
  bucket: @target_bucket,
168
171
  key: @target_name,
@@ -0,0 +1,56 @@
1
+ module Moonshot
2
+ module Plugins
3
+ # Plugin to ensure CodeDeploy has all necessary S3 buckets created.
4
+ # Defaults to using ENV['AWS_REGION'] as default region if not configured.
5
+ class CodeDeploySetup
6
+ include Moonshot::CredsHelper
7
+
8
+ attr_reader :name, :prefix, :regions
9
+
10
+ def initialize(name, prefix: '', regions: [ENV['AWS_REGION']])
11
+ @regions = regions.reject(&:nil?)
12
+ if @regions.empty?
13
+ raise ArgumentError, 'CodeDeploySetup requires at least one region.' \
14
+ ' Set regions argument or ENV[\'AWS_REGION\'].'
15
+ end
16
+
17
+ @name = name
18
+ @prefix = prefix
19
+ end
20
+
21
+ def bucket_name(region = ENV['AWS_REGION'])
22
+ if ENV.key?('S3_BUCKET')
23
+ ENV['S3_BUCKET']
24
+ else
25
+ "#{@name}-#{region}"
26
+ end
27
+ end
28
+
29
+ def bucket_prefix
30
+ @prefix.empty? ? '' : "#{@prefix}/"
31
+ end
32
+
33
+ # Create an S3 bucket in each supported region for CodeDeploy
34
+ def setup_code_deploy_s3_buckets
35
+ @regions.uniq.each do |region|
36
+ client = s3_client(region: region)
37
+ name = bucket_name(region)
38
+ bucket = Aws::S3::Bucket.new(
39
+ name,
40
+ client: client
41
+ )
42
+ bucket.create unless bucket.exists?
43
+ end
44
+ end
45
+
46
+ # Hook entry points to ensure S3 Buckets are available for CodeDeploy
47
+ def run_hook(_resources)
48
+ setup_code_deploy_s3_buckets
49
+ end
50
+
51
+ # Moonshot hooks to trigger this plugin
52
+ alias pre_create run_hook
53
+ alias pre_deploy run_hook
54
+ end
55
+ end
56
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moonshot
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.beta2
4
+ version: 2.0.0.beta3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cloud Engineering <engineering@acquia.com>
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-22 00:00:00.000000000 Z
11
+ date: 2017-07-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk
@@ -100,6 +100,20 @@ dependencies:
100
100
  - - "~>"
101
101
  - !ruby/object:Gem::Version
102
102
  version: 3.2.3
103
+ - !ruby/object:Gem::Dependency
104
+ name: retriable
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :runtime
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
103
117
  - !ruby/object:Gem::Dependency
104
118
  name: activesupport
105
119
  requirement: !ruby/object:Gem::Requirement
@@ -249,6 +263,9 @@ extra_rdoc_files: []
249
263
  files:
250
264
  - bin/moonshot
251
265
  - lib/default/Moonfile.rb
266
+ - lib/default/appspec.yml
267
+ - lib/default/bin/build.sh
268
+ - lib/default/moonshot/template.yml
252
269
  - lib/moonshot.rb
253
270
  - lib/moonshot/account_context.rb
254
271
  - lib/moonshot/always_use_default_source.rb
@@ -317,6 +334,7 @@ files:
317
334
  - lib/moonshot/unicode_table.rb
318
335
  - lib/moonshot/yaml_stack_template.rb
319
336
  - lib/plugins/backup.rb
337
+ - lib/plugins/code_deploy_setup.rb
320
338
  - lib/plugins/encrypted_parameters.rb
321
339
  - lib/plugins/encrypted_parameters/kms_key.rb
322
340
  - lib/plugins/encrypted_parameters/parameter_encrypter.rb