moonshot 0.7.5 → 0.7.6

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: c8889da0008af3d77a443464223983f148141bbb
4
- data.tar.gz: e2e9760d81c2c9b0a448b1ca489d9f7f9ec080a8
3
+ metadata.gz: d67cb66c5dd32a605e63ad9c2878ba6148786aeb
4
+ data.tar.gz: ca5974cfe33c7a40d1040984017d5180e67a1da7
5
5
  SHA512:
6
- metadata.gz: 7458636f1cc8d0f00ce9201bfbbcb4205c022226cd3aa8d56a38bba4d87c1a4882b4402cb7fff126e7bce6c241a2944f97b2d9274a6d8e58b0ad262a5694ae9e
7
- data.tar.gz: 16321c8a1995e6e3b5c3cb031623b50a157b3b187be722c706ef42fb395308fa720472e8237008057e62a777ee426f6d9460f99d3a1da51395551c1392ed469f
6
+ metadata.gz: 970d41d6ae16a11f8285381efe2f22a8b9c47fae952e9a860c713bb3aabf57af6e4fc241ccbe72e1640b5a06edea0fc6acdc88db1148a24d03c685eabaecb92f
7
+ data.tar.gz: dde9fcf2087fdd0aa7918079aa51944b9409567866ab174a0e0c8ab5320b2dbc9ec6adb6257d6edc807b7351063e1c61e640d50c6dee9f0a7d545c0746425370
@@ -76,10 +76,40 @@ module Moonshot::BuildMechanism
76
76
  end
77
77
 
78
78
  def git_tag(tag, sha, annotation)
79
+ return if git_tag_exists(tag, sha)
80
+
79
81
  cmd = "git tag -a #{tag} #{sha} --file=-"
80
82
  sh_step(cmd, stdin: annotation)
81
83
  end
82
84
 
85
+ # Determines if a valid git tag already exists.
86
+ #
87
+ # @param tag [String] Tag to check existence for.
88
+ # @param sha [String] SHA to verify the tag against.
89
+ #
90
+ # @return [Boolean] Whether or not the tag exists.
91
+ #
92
+ # @raise [RuntimeError] if the SHAs do not match.
93
+ def git_tag_exists(tag, sha)
94
+ exists = false
95
+ sh_step("git tag -l #{tag}") do |_, output|
96
+ exists = (output.strip == tag)
97
+ end
98
+
99
+ # If the tag does exist, make sure the existing SHA matches the SHA we're
100
+ # trying to build from.
101
+ if exists
102
+ sh_step("git rev-list -n 1 #{tag}") do |_, output|
103
+ raise "#{tag} already exists at a different SHA" \
104
+ if output.strip != sha
105
+ end
106
+
107
+ log.info("tag #{tag} already exists")
108
+ end
109
+
110
+ exists
111
+ end
112
+
83
113
  def git_push_tag(remote, tag)
84
114
  cmd = "git push #{remote} refs/tags/#{tag}:refs/tags/#{tag}"
85
115
  sh_step(cmd) do
@@ -88,6 +118,8 @@ module Moonshot::BuildMechanism
88
118
  end
89
119
 
90
120
  def hub_create_release(semver, commitish, changelog_entry)
121
+ return if hub_release_exists(semver, commitish)
122
+
91
123
  message = "#{semver}\n\n#{changelog_entry}"
92
124
  cmd = "hub release create #{semver} --commitish=#{commitish}"
93
125
  cmd << ' --prerelease' if semver.pre || semver.build
@@ -95,6 +127,19 @@ module Moonshot::BuildMechanism
95
127
  sh_step(cmd)
96
128
  end
97
129
 
130
+ # Determines if a github release already exists.
131
+ #
132
+ # @param semver [String] Semantic version string for the release.
133
+ #
134
+ # @return [Boolean]
135
+ 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
141
+ end
142
+
98
143
  def validate_commit
99
144
  cmd = "git show --stat #{@sha}"
100
145
  sh_step(cmd, msg: "Validate commit #{@sha}.") do |_, out|
@@ -8,6 +8,8 @@ module Moonshot::BuildMechanism
8
8
  include Moonshot::DoctorHelper
9
9
  include Moonshot::Shell
10
10
 
11
+ MAX_BUILD_FIND_ATTEMPTS = 10
12
+
11
13
  attr_reader :output_file
12
14
 
13
15
  def initialize(slug, pro: false)
@@ -33,13 +35,46 @@ module Moonshot::BuildMechanism
33
35
  def find_build_and_job(version)
34
36
  job_number = nil
35
37
  ilog.start_threaded('Find Travis CI build') do |step|
36
- sleep 2
37
- build_out = sh_out("bundle exec travis show #{@cli_args} #{version}")
38
+ job_number = wait_for_build(version)
39
+
40
+ step.success("Travis CI ##{job_number.gsub(/\..*/, '')} running.")
41
+ end
42
+ job_number
43
+ end
44
+
45
+ # Looks for the travis build and attempts to retry if the build does not
46
+ # exist yet.
47
+ #
48
+ # @param verison [String] Build version to look for.
49
+ #
50
+ # @return [String] Job number for the travis build.
51
+ def wait_for_build(version)
52
+ job_number = nil
53
+ attempts = 0
54
+ loop do
55
+ # Give travis some time to start the build.
56
+ attempts += 1
57
+ sleep 10
58
+
59
+ # Attempt to find the build. Rescue and re-attempt if the build can not
60
+ # be found on travis yet.
61
+ begin
62
+ build_out = sh_out("bundle exec travis show #{@cli_args} #{version}")
63
+ rescue RuntimeError => e
64
+ next unless attempts >= MAX_BUILD_FIND_ATTEMPTS
65
+ raise e
66
+ end
67
+
38
68
  unless (job_number = build_out.match(/^#(\d+\.\d+) .+BUILD=1.+/)[1])
69
+ next unless attempts >= MAX_BUILD_FIND_ATTEMPTS
39
70
  raise "Build for #{version} not found.\n#{build_out}"
40
71
  end
41
- step.success("Travis CI ##{job_number.gsub(/\..*/, '')} running.")
72
+
73
+ # If we've reached this point then everything went smoothly and we can
74
+ # exit the loop.
75
+ break
42
76
  end
77
+
43
78
  job_number
44
79
  end
45
80
 
data/lib/moonshot/cli.rb CHANGED
@@ -47,6 +47,24 @@ module Moonshot
47
47
  base.include(Moonshot::BuildMechanism)
48
48
  base.include(Moonshot::DeploymentMechanism)
49
49
  end
50
+
51
+ def ssh_options!
52
+ option :user, default: ENV['MOONSHOT_SSH_USER'] || ENV['USER'], type: :string,
53
+ aliases: '-l',
54
+ desc: 'Specifies the user to log in as on the remote machine.'
55
+ option :identity_file, default: ENV['MOONSHOT_SSH_KEY_FILE'], type: :string,
56
+ aliases: '-i',
57
+ desc: 'Selects a file from which the identity (private key) for '\
58
+ 'public key authentication is read.'
59
+ option :instance, default: nil, type: :string, aliases: '-s',
60
+ desc: 'Connect to specified instance ID instead of the first one.'
61
+ option :command, default: nil, type: :string, aliases: '-c',
62
+ desc: 'Execute the specified command instead of opening a shell.'
63
+ option :auto_scaling_group, default: nil, type: :string, aliases: '-g',
64
+ desc: 'The logical ID of the auto scaling group to SSH into. '\
65
+ 'Mandatory if the stack has multiple ASGs, '\
66
+ 'ignored if you have only one.'
67
+ end
50
68
  end
51
69
 
52
70
  def initialize(*args)
@@ -91,6 +109,12 @@ module Moonshot
91
109
  parameter_strategy = options[:parameter_strategy] || self.class.default_parameter_strategy
92
110
  config.parameter_strategy = parameter_strategy_factory(parameter_strategy) \
93
111
  unless parameter_strategy.nil?
112
+
113
+ config.ssh_user = options[:user]
114
+ config.ssh_identity_file = options[:identity_file]
115
+ config.ssh_instance = options[:instance]
116
+ config.ssh_command = options[:command]
117
+ config.ssh_auto_scaling_group_name = options[:auto_scaling_group]
94
118
  end
95
119
  rescue => e
96
120
  raise Thor::Error, e.message
@@ -106,6 +130,12 @@ module Moonshot
106
130
  raise Thor::Error, "Unknown parameter strategy: #{value}"
107
131
  end
108
132
  end
133
+
134
+ def ssh_command(command)
135
+ arguments = ARGV.drop(1)
136
+ arguments += ['--command', command]
137
+ invoke :ssh, [], arguments
138
+ end
109
139
  end
110
140
 
111
141
  desc :list, 'List stacks for this application.'
@@ -180,5 +210,11 @@ module Moonshot
180
210
  success = controller.doctor
181
211
  raise Thor::Error, 'One or more checks failed.' unless success
182
212
  end
213
+
214
+ desc :ssh, 'SSH into the first or specified instance on the stack.'
215
+ ssh_options!
216
+ def ssh
217
+ controller.ssh
218
+ end
183
219
  end
184
220
  end
@@ -80,6 +80,12 @@ module Moonshot
80
80
  success
81
81
  end
82
82
 
83
+ def ssh
84
+ run_plugins(:pre_ssh)
85
+ stack.ssh
86
+ run_plugins(:post_ssh)
87
+ end
88
+
83
89
  def stack
84
90
  @stack ||= Stack.new(stack_name,
85
91
  app_name: @config.app_name,
@@ -88,6 +94,11 @@ module Moonshot
88
94
  config.parent_stacks = @config.parent_stacks
89
95
  config.show_all_events = @config.show_all_stack_events
90
96
  config.parameter_strategy = @config.parameter_strategy
97
+ config.ssh_user = @config.ssh_user
98
+ config.ssh_identity_file = @config.ssh_identity_file
99
+ config.ssh_instance = @config.ssh_instance
100
+ config.ssh_command = @config.ssh_command
101
+ config.ssh_auto_scaling_group_name = @config.ssh_auto_scaling_group_name
91
102
  end
92
103
  end
93
104
 
@@ -15,6 +15,11 @@ module Moonshot
15
15
  attr_accessor :plugins
16
16
  attr_accessor :show_all_stack_events
17
17
  attr_accessor :parameter_strategy
18
+ attr_accessor :ssh_instance
19
+ attr_accessor :ssh_identity_file
20
+ attr_accessor :ssh_user
21
+ attr_accessor :ssh_command
22
+ attr_accessor :ssh_auto_scaling_group_name
18
23
 
19
24
  def initialize
20
25
  @auto_prefix_stack = true
@@ -93,6 +93,18 @@ module Moonshot
93
93
  end
94
94
  end
95
95
 
96
+ def ssh
97
+ box_id = @config.ssh_instance || instances.sort.first
98
+ box_ip = instance_ip(box_id)
99
+ cmd = ['ssh', '-t']
100
+ cmd << "-i #{@config.ssh_identity_file}" if @config.ssh_identity_file
101
+ cmd << "-l #{@config.ssh_user}" if @config.ssh_user
102
+ cmd << box_ip
103
+ cmd << @config.ssh_command if @config.ssh_command
104
+ puts "Opening SSH connection to #{box_id} (#{box_ip})..."
105
+ exec(cmd.join(' '))
106
+ end
107
+
96
108
  def parameters
97
109
  get_stack(@name)
98
110
  .parameters
@@ -176,6 +188,37 @@ module Moonshot
176
188
 
177
189
  private
178
190
 
191
+ def asgs
192
+ resources_of_type('AWS::AutoScaling::AutoScalingGroup')
193
+ end
194
+
195
+ def instance_ip(instance_id)
196
+ Aws::EC2::Client.new.describe_instances(instance_ids: [instance_id])
197
+ .reservations.first.instances.first.public_ip_address
198
+ rescue
199
+ raise "Failed to determine public IP address for instance #{instance_id}."
200
+ end
201
+
202
+ def instances # rubocop:disable Metrics/AbcSize
203
+ groups = asgs
204
+ asg = if groups.count == 1
205
+ groups.first
206
+ elsif asgs.count > 1
207
+ unless @config.ssh_auto_scaling_group_name
208
+ raise 'Multiple Auto Scaling Groups found in the stack. Please specify which '\
209
+ 'one to SSH into using the --auto-scaling-group (-g) option.'
210
+ end
211
+ groups.detect { |x| x.logical_resource_id == @config.ssh_auto_scaling_group_name }
212
+ end
213
+ raise 'Failed to find the Auto Scaling Group.' unless asg
214
+
215
+ Aws::AutoScaling::Client.new.describe_auto_scaling_groups(
216
+ auto_scaling_group_names: [asg.physical_resource_id]
217
+ ).auto_scaling_groups.first.instances.map(&:instance_id)
218
+ rescue
219
+ raise 'Failed to find instances in the Auto Scaling Group.'
220
+ end
221
+
179
222
  def stack_name
180
223
  "CloudFormation Stack #{@name.blue}"
181
224
  end
@@ -4,6 +4,11 @@ module Moonshot
4
4
  attr_accessor :parent_stacks
5
5
  attr_accessor :show_all_events
6
6
  attr_accessor :parameter_strategy
7
+ attr_accessor :ssh_instance
8
+ attr_accessor :ssh_identity_file
9
+ attr_accessor :ssh_user
10
+ attr_accessor :ssh_command
11
+ attr_accessor :ssh_auto_scaling_group_name
7
12
 
8
13
  def initialize
9
14
  @parent_stacks = []
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: 0.7.5
4
+ version: 0.7.6
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: 2016-07-08 00:00:00.000000000 Z
11
+ date: 2016-07-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk