sonic-screwdriver 2.0.0 → 2.2.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.
@@ -11,16 +11,16 @@ reference: true
11
11
 
12
12
  Lists ec2 instances.
13
13
 
14
- List ec2 servers. A filter must be provided. The filter can be a mix of instance ids and ec2 tags. sonic list will auto-detect the what type of filter it is filter appropriately. The filter for listing is optional.
14
+ A filter must be provided. The filter can be a mix of instance ids and ec2 tags. sonic list will auto-detect the what type of filter it is. The filter is optional.
15
15
 
16
- Examples:
16
+ ## Examples
17
17
 
18
18
  $ sonic list
19
19
  $ sonic list hi-web-prod
20
20
  $ sonic list hi-web-prod,hi-clock-prod
21
21
  $ sonic list i-09482b1a6e330fbf7
22
22
 
23
- Example Output:
23
+ ## Example Output
24
24
 
25
25
  $ sonic list --header i-09482b1a6e330fbf7
26
26
  Instance Id Public IP Private IP Type
data/docs/bin/web CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/bin/bash -ex
2
2
 
3
3
  bundle exec jekyll clean
4
- exec bundle exec jekyll serve
4
+ exec bundle exec jekyll serve --host 0.0.0.0 "$@"
data/docs/quick-start.md CHANGED
@@ -6,35 +6,31 @@ In a hurry? No sweat! Here's a quick overview of how to use sonic.
6
6
 
7
7
  ### Install
8
8
 
9
- ```sh
10
- gem install sonic-screwdriver
11
- ```
9
+ gem install sonic-screwdriver
12
10
 
13
11
  ### Usage
14
12
 
15
- ```sh
16
- # ssh into an instance
17
- sonic ssh i-0f7f833131a51ce35
18
- sonic ssh hi-web # ec2 tag
19
- sonic ssh hi-web --cluster staging # ecs service name
20
- sonic ssh 7fbc8c75-4675-4d39-a5a4-0395ff8cd474 --cluster staging # ECS container id
21
- sonic ssh 1ed12abd-645c-4a05-9acf-739b9d790170 --cluster staging # ECS task id
13
+ # ssh into an instance
14
+ sonic ssh i-0f7f833131a51ce35
15
+ sonic ssh demo-web # ec2 tag
16
+ sonic ssh demo-web --cluster staging # ecs service name
17
+ sonic ssh 7fbc8c75-4675-4d39-a5a4-0395ff8cd474 --cluster staging # ECS container id
18
+ sonic ssh 1ed12abd-645c-4a05-9acf-739b9d790170 --cluster staging # ECS task id
22
19
 
23
- # docker exec to a running ECS docker container
24
- sonic ecs exec hi-web
20
+ # docker exec to a running ECS docker container
21
+ sonic ecs exec demo-web
25
22
 
26
- # docker run with same environment as the ECS docker running containers
27
- sonic ecs sh hi-web
23
+ # docker run with same environment as the ECS docker running containers
24
+ sonic ecs sh demo-web
28
25
 
29
- # run command on 1 instance
30
- sonic execute i-0f7f833131a51ce35 uptime
26
+ # run command on 1 instance
27
+ sonic execute --instance-ids i-0f7f833131a51ce35 uptime
31
28
 
32
- # run command on all instances tagged with hi-web and worker
33
- sonic execute hi-web,hi-worker uptime
29
+ # run command on all instances tagged with demo-web and worker
30
+ sonic execute --tags Name=demo-web,demo-worker uptime
34
31
 
35
- # list ec2 instances
36
- sonic list hi-web
37
- ```
32
+ # list ec2 instances
33
+ sonic list demo-web
38
34
 
39
35
  Congratulations! You now know the basic sonic screwdriver commands now.
40
36
 
data/lib/sonic.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  $:.unshift(File.expand_path("../", __FILE__))
2
+ require "rainbow/ext/string"
2
3
  require "sonic/version"
3
- require "colorize"
4
4
 
5
5
  module Sonic
6
6
  autoload :Core, 'sonic/core'
data/lib/sonic/checks.rb CHANGED
@@ -3,7 +3,7 @@ module Sonic
3
3
  def check_cluster_exists!
4
4
  cluster = ecs.describe_clusters(clusters: [@cluster]).clusters.first
5
5
  unless cluster
6
- UI.error "The #{@cluster.green} cluster does not exist. Are you sure you specified the right cluster?"
6
+ UI.error "The #{@cluster.color(:green)} cluster does not exist. Are you sure you specified the right cluster?"
7
7
  exit 1
8
8
  end
9
9
  end
@@ -20,7 +20,7 @@ module Sonic
20
20
 
21
21
  service = resp.services.first
22
22
  unless service
23
- UI.error "The #{@service.green} service does not exist in #{@cluster.green} cluster. Are you sure you specified the right service and cluster?"
23
+ UI.error "The #{@service.color(:green)} service does not exist in #{@cluster.color(:green)} cluster. Are you sure you specified the right service and cluster?"
24
24
  exit 1
25
25
  end
26
26
  end
data/lib/sonic/cli.rb CHANGED
@@ -27,8 +27,10 @@ module Sonic
27
27
  long_desc Help.text("execute")
28
28
  option :zero_warn, type: :boolean, default: true, desc: "Warns user when no instances found"
29
29
  # filter - Filter ec2 instances by tag name or instance_ids separated by commas
30
- def execute(filter, *command)
31
- Execute.new(command, options.merge(filter: filter)).execute
30
+ option :instance_ids, desc: %Q|Instance ids to execute command on. Format: --instance-ids "i-111,i-222"|
31
+ option :tags, desc: %Q|Tags used to determine what instances to execute command on. Format: --tags "Key1=v1,v2;Key2=v3"|
32
+ def execute(*command)
33
+ Execute.new(command, options).execute
32
34
  end
33
35
 
34
36
  desc "list [FILTER]", "Lists ec2 instances."
@@ -1,5 +1,8 @@
1
1
  bastion:
2
2
  host_key_check: false
3
+ # host:
4
+
5
+ ssh:
3
6
  user: ec2-user
4
7
 
5
8
  ecs_service_cluster_map:
data/lib/sonic/docker.rb CHANGED
@@ -30,7 +30,7 @@ module Sonic
30
30
 
31
31
  # command is an Array
32
32
  def execute(*command)
33
- UI.say "=> #{command.join(' ')}".colorize(:green)
33
+ UI.say "=> #{command.join(' ')}".color(:green)
34
34
  success = system(*command)
35
35
  unless success
36
36
  UI.error(command.join(' '))
@@ -59,7 +59,7 @@ module Sonic
59
59
 
60
60
  ssh = ["ssh", ssh_options, "-At", host, "uptime", "2>&1"]
61
61
  command = ssh.join(' ')
62
- puts "=> #{command}".colorize(:green)
62
+ puts "=> #{command}".color(:green)
63
63
  output = `#{command}`
64
64
  if output.include?("Permission denied")
65
65
  puts output
data/lib/sonic/execute.rb CHANGED
@@ -1,4 +1,3 @@
1
- require 'colorize'
2
1
  require 'yaml'
3
2
  require 'active_support/core_ext/hash'
4
3
 
@@ -9,7 +8,8 @@ module Sonic
9
8
  def initialize(command, options)
10
9
  @command = command
11
10
  @options = options
12
- @filter = @options[:filter].split(',').map{|s| s.strip}
11
+ @tags = @options[:tags]
12
+ @instance_ids = @options[:instance_ids]
13
13
  end
14
14
 
15
15
  # aws ssm send-command \
@@ -19,6 +19,7 @@ module Sonic
19
19
  # --parameters '{"commands":["#!/usr/bin/python","print \"Hello world from python\""]}' \
20
20
  # --query "Command.CommandId"
21
21
  def execute
22
+ check_filter_options!
22
23
  ssm_options = build_ssm_options
23
24
  if @options[:noop]
24
25
  UI.noop = true
@@ -34,6 +35,7 @@ module Sonic
34
35
  puts
35
36
  begin
36
37
  resp = send_command(ssm_options)
38
+
37
39
  command_id = resp.command.command_id
38
40
  success = true
39
41
  rescue Aws::SSM::Errors::InvalidInstanceId => e
@@ -50,9 +52,31 @@ module Sonic
50
52
  display_ssm_commands(command_id, ssm_options)
51
53
  puts
52
54
  return if @options[:noop]
53
- wait(command_id)
54
- display_ssm_output(command_id, ssm_options)
55
+ status = wait(command_id)
56
+ instances_found = display_ssm_output(command_id)
55
57
  display_console_url(command_id)
58
+
59
+ if status == "Success"
60
+ puts "Command successful: #{status}".color(:green)
61
+ exit_status(0)
62
+ else
63
+ puts "Command unsuccessful: #{status}".color(:red)
64
+ exit_status(1)
65
+ end
66
+ end
67
+
68
+ def exit_status(code)
69
+ exit(code) unless cli?
70
+
71
+ if code == 0
72
+ true
73
+ else
74
+ raise "Error running command"
75
+ end
76
+ end
77
+
78
+ def cli?
79
+ $0.include?('sonic')
56
80
  end
57
81
 
58
82
  def wait(command_id)
@@ -69,15 +93,21 @@ module Sonic
69
93
  end
70
94
  puts "\nCommand finished."
71
95
  puts
96
+ status
72
97
  end
73
98
 
74
- def display_ssm_output(command_id, ssm_options)
75
- instance_ids = ssm_options[:instance_ids]
76
- return unless instance_ids && instance_ids.size > 0
99
+ def display_ssm_output(command_id)
100
+ resp = ssm.list_command_invocations(command_id: command_id)
101
+ command_invocations = resp.command_invocations
102
+ command_invocation = command_invocations.first
103
+ unless command_invocation
104
+ puts "WARN: No instances found that matches the --tags or --instance-ids option".color(:yellow)
105
+ return false # instances_found
106
+ end
107
+ instance_id = command_invocation.instance_id
77
108
 
78
- instance_id = instance_ids.first
79
- if ssm_options[:instance_ids].size > 1
80
- puts "Multiple instance targets. Only displaying output for #{instance_id}."
109
+ if command_invocations.size > 1
110
+ puts "Multiple instance targets. Total targets: #{command_invocations.size}. Only displaying output for #{instance_id}."
81
111
  else
82
112
  puts "Displaying output for #{instance_id}."
83
113
  end
@@ -89,6 +119,7 @@ module Sonic
89
119
  ssm_output(resp, "output")
90
120
  ssm_output(resp, "error")
91
121
  puts
122
+ true # instances_found
92
123
  end
93
124
 
94
125
  def display_console_url(command_id)
@@ -97,16 +128,15 @@ module Sonic
97
128
  puts "To see the more output details visit:"
98
129
  puts " #{console_url}"
99
130
  puts
100
- copy_paste_clipboard(console_url)
101
- UI.say "Pro tip: the console url is already in your copy/paste clipboard."
131
+ copy_paste_clipboard(console_url, "Pro tip: the console url is already in your copy/paste clipboard.")
102
132
  end
103
133
 
104
134
  def colorized_status(status)
105
135
  case status
106
136
  when "Success"
107
- status.colorize(:green)
137
+ status.color(:green)
108
138
  when "Failed"
109
- status.colorize(:red)
139
+ status.color(:red)
110
140
  else
111
141
  status
112
142
  end
@@ -121,7 +151,7 @@ module Sonic
121
151
  return if content.empty?
122
152
 
123
153
  puts "Command standard #{type}:"
124
- # "https://s3.amazonaws.com/lr-infrastructure-prod/ssm/commands/sonic/0a4f4bef-8f63-4235-8b30-ae296477261a/i-0b2e6e187a3f9ada9/awsrunPowerShellScript/0.awsrunPowerShellScript/stderr">
154
+ # "https://s3.amazonaws.com/infra-prod/ssm/commands/sonic/0a4f4bef-8f63-4235-8b30-ae296477261a/i-0b2e6e187a3f9ada9/awsrunPowerShellScript/0.awsrunPowerShellScript/stderr">
125
155
  if content.include?("--output truncated--") && !resp[s3_key].empty?
126
156
  s3_url = resp[s3_key]
127
157
  info = s3_url.sub('https://s3.amazonaws.com/', '').split('/')
@@ -147,7 +177,6 @@ module Sonic
147
177
 
148
178
  begin
149
179
  resp = ssm.send_command(options)
150
- # puts "NOOP FOR NOW"
151
180
  rescue Aws::SSM::Errors::UnsupportedPlatformType
152
181
  retries += 1
153
182
  # toggle AWS-RunShellScript / AWS-RunPowerShellScript
@@ -166,12 +195,18 @@ module Sonic
166
195
  end
167
196
 
168
197
  def build_ssm_options
169
- criteria = transform_filter(@filter)
198
+ criteria = transform_filter_option
170
199
  command = build_command(@command)
171
200
  options = criteria.merge(
172
201
  document_name: "AWS-RunShellScript", # default
173
202
  comment: "sonic #{ARGV.join(' ')}"[0..99], # comment has a max of 100 chars
174
- parameters: { "commands" => command }
203
+ parameters: { "commands" => command },
204
+ # Default CloudWatchLog settings. Can be overwritten with settings.yml send_command
205
+ # IMPORTANT: make sure the EC2 instance the command runs on has access to write to CloudWatch Logs.
206
+ cloud_watch_output_config: {
207
+ # cloud_watch_log_group_name: "ssm", # Defaults to /aws/ssm/AWS-RunShellScript (aws/ssm/SystemsManagerDocumentName https://amzn.to/38TKVse)
208
+ cloud_watch_output_enabled: true,
209
+ },
175
210
  )
176
211
  settings_options = settings["send_command"] || {}
177
212
  options.merge(settings_options.deep_symbolize_keys)
@@ -181,6 +216,12 @@ module Sonic
181
216
  @settings ||= Setting.new.data
182
217
  end
183
218
 
219
+ def check_filter_options!
220
+ return if @tags || @instance_ids
221
+ puts "ERROR: Please provide --tags or --instance-ids option".color(:red)
222
+ exit 1
223
+ end
224
+
184
225
  #
185
226
  # Public: Transform the filter to the ssm send_command equivalent options
186
227
  #
@@ -188,41 +229,31 @@ module Sonic
188
229
  #
189
230
  # Examples
190
231
  #
191
- # transform_filter(["hi-web-prod", "hi-worker-prod", "i-006a097bb10643e20"])
232
+ # transform_filter_option
192
233
  # # => {
193
234
  # instance_ids: ["i-006a097bb10643e20"],
194
235
  # targets: [{key: "Name", values: "hi-web-prod,hi-worker-prod"}]
195
236
  # }
196
237
  #
197
238
  # Returns the duplicated String.
198
- def transform_filter(filter)
199
- valid = validate_filter(filter)
200
- unless valid
201
- UI.error("The filter you provided '#{filter.join(',')}' is not valid.")
202
- UI.say("The filter must either be all instance ids or just a list of tag names.")
203
- exit 1
204
- end
205
-
206
- if filter.detect { |i| instance_id?(i) }
207
- instance_ids = filter
208
- {instance_ids: instance_ids}
209
- else
210
- tags = filter
211
- targets = [{
212
- key: "tag:#{tag_name}",
213
- values: tags
214
- }]
239
+ def transform_filter_option
240
+ if @tags
241
+ list = @tags.split(';')
242
+ targets = list.inject([]) do |final,item|
243
+ tag_name,value_list = item.split('=')
244
+ values = value_list.split(',').map(&:strip)
245
+ # structure expected by ssm send_command
246
+ option = {
247
+ key: "tag:#{tag_name}",
248
+ values: values
249
+ }
250
+ final << option
251
+ final
252
+ end
215
253
  {targets: targets}
216
- end
217
- end
218
-
219
- # Either all instance ids are no instance ids is a valid filter
220
- def validate_filter(filter)
221
- if filter.detect { |i| instance_id?(i) }
222
- instance_ids = filter.select { |i| instance_id?(i) }
223
- instance_ids.size == filter.size
224
- else
225
- true
254
+ else # @instance_ids
255
+ instance_ids = @instance_ids.split(',')
256
+ {instance_ids: instance_ids}
226
257
  end
227
258
  end
228
259
 
@@ -239,7 +270,7 @@ module Sonic
239
270
  # The script is being feed inline so just join the command together into one script.
240
271
  # Still keep in an array form because that's how ssn.send_command works with AWS-RunShellScript
241
272
  # usually reads the command.
242
- [command.join(" ")]
273
+ command.is_a?(Array) ? command : [command]
243
274
  end
244
275
  end
245
276
 
@@ -254,8 +285,7 @@ You can use the following command to check registered instances to SSM.
254
285
  #{ssm_describe_command}
255
286
  EOS
256
287
  UI.warn(message)
257
- copy_paste_clipboard(ssm_describe_command)
258
- UI.say "Pro tip: ssm describe-instance-information already in your copy/paste clipboard."
288
+ copy_paste_clipboard(ssm_describe_command, "Pro tip: ssm describe-instance-information already in your copy/paste clipboard.")
259
289
  end
260
290
 
261
291
  def file_path?(command)
@@ -312,9 +342,10 @@ EOL
312
342
  end
313
343
  end
314
344
 
315
- def copy_paste_clipboard(command)
345
+ def copy_paste_clipboard(command, text)
316
346
  return unless RUBY_PLATFORM =~ /darwin/
317
347
  system("echo '#{command}' | pbcopy")
348
+ UI.say text
318
349
  end
319
350
  end
320
351
  end
@@ -1,11 +1,12 @@
1
- Run as a command across a list of servers. A filter must be provided. The filter can be a mix of instance ids and ec2 tags. This command can also take a path to a file. To specify a path to a file use file:// at the beginning of your file.
1
+ * A filter must be provided. The filter can be a mix of instance ids and ec2 tags.
2
+ * The command can be provided inline or as a file. To a file use `file://` at the beginning of your file.
2
3
 
3
4
  ## Examples Summary
4
5
 
5
- $ sonic execute hi-web-prod uptime
6
- $ sonic execute hi-web-prod,hi-worker-prod,hi-clock-prod uptime
7
- $ sonic execute i-030033c20c54bf149,i-030033c20c54bf150 uname -a
8
- $ sonic execute i-030033c20c54bf149 file://hello.sh
6
+ sonic execute --tags Name=demo-web uptime
7
+ sonic execute --tags Name=demo-web,demo-worker uptime # multiple values
8
+ sonic execute --instance-ids i-030033c20c54bf149,i-030033c20c54bf150 uname -a
9
+ sonic execute --instance-ids i-030033c20c54bf149 file://hello.sh # script from file
9
10
 
10
11
  You cannot mix instance ids and tag names in the filter.
11
12
 
@@ -13,39 +14,37 @@ You cannot mix instance ids and tag names in the filter.
13
14
 
14
15
  Here's a command example output in detailed:
15
16
 
16
- ```sh
17
- $ sonic execute i-0bf51a000ab4e73a8 uptime
18
- Sending command to SSM with options:
19
- ---
20
- instance_ids:
21
- - i-0bf51a000ab4e73a8
22
- document_name: AWS-RunShellScript
23
- comment: sonic execute i-0bf51a000ab4e73a8 uptime
24
- parameters:
25
- commands:
26
- - uptime
27
- output_s3_region: us-east-1
28
- output_s3_bucket_name: [reacted]
29
- output_s3_key_prefix: ssm/commands/sonic
30
-
31
- Command sent to AWS SSM. To check the details of the command:
32
- aws ssm list-commands --command-id 0bb18d58-6436-49fd-9bfd-0c4b6c51c7a2
33
- aws ssm get-command-invocation --command-id 0bb18d58-6436-49fd-9bfd-0c4b6c51c7a2 --instance-id i-0bf51a000ab4e73a8
34
-
35
- Waiting for ssm command to finish.....
36
- Command finished.
37
-
38
- Displaying output for i-0bf51a000ab4e73a8.
39
- Command status: Success
40
- Command standard output:
41
- 01:08:10 up 8 days, 6:41, 0 users, load average: 0.00, 0.00, 0.00
42
-
43
- To see the more output details visit:
44
- https://us-west-2.console.aws.amazon.com/systems-manager/run-command/0bb18d58-6436-49fd-9bfd-0c4b6c51c7a2
45
-
46
- Pro tip: the console url is already in your copy/paste clipboard.
47
- $
48
- ```
17
+ $ sonic execute --instance-ids i-0bf51a000ab4e73a8 uptime
18
+ Sending command to SSM with options:
19
+ ---
20
+ instance_ids:
21
+ - i-0bf51a000ab4e73a8
22
+ document_name: AWS-RunShellScript
23
+ comment: sonic execute --instance-ids i-0bf51a000ab4e73a8 uptime
24
+ parameters:
25
+ commands:
26
+ - uptime
27
+ output_s3_region: us-east-1
28
+ output_s3_bucket_name: [reacted]
29
+ output_s3_key_prefix: ssm/commands/sonic
30
+
31
+ Command sent to AWS SSM. To check the details of the command:
32
+ aws ssm list-commands --command-id 0bb18d58-6436-49fd-9bfd-0c4b6c51c7a2
33
+ aws ssm get-command-invocation --command-id 0bb18d58-6436-49fd-9bfd-0c4b6c51c7a2 --instance-id i-0bf51a000ab4e73a8
34
+
35
+ Waiting for ssm command to finish.....
36
+ Command finished.
37
+
38
+ Displaying output for i-0bf51a000ab4e73a8.
39
+ Command status: Success
40
+ Command standard output:
41
+ 01:08:10 up 8 days, 6:41, 0 users, load average: 0.00, 0.00, 0.00
42
+
43
+ To see the more output details visit:
44
+ https://us-west-2.console.aws.amazon.com/systems-manager/run-command/0bb18d58-6436-49fd-9bfd-0c4b6c51c7a2
45
+
46
+ Pro tip: the console url is already in your copy/paste clipboard.
47
+ $
49
48
 
50
49
  Notice the conveniences of `sonic execute`, it:
51
50