gaptool-client 0.8.0.pre.alpha3 → 0.8.0.pre.alpha4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/bin/gt +1 -0
  4. data/lib/gaptool_client.rb +2 -597
  5. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cb7da2336580dbd7f63b160a83bbd066b0c9b5aa
4
- data.tar.gz: 2ac19b1d8a8d2c70620b3b943477197514f2c850
3
+ metadata.gz: 3528f94e83fb0e8209a053b24683729e830b61ef
4
+ data.tar.gz: 1f768dc1e8b37c94b289d00cce7e5935506adf13
5
5
  SHA512:
6
- metadata.gz: e72b4610e2fc678c8a998583368b43c445a429bb8c4efd0ae18e6b1c55b28e0e81ccfaa75d0c52ae6d01c673eb1bbaba33de8597e32e8b6956732a50b1259bd0
7
- data.tar.gz: b34660bb638816dfac2a64051daa9e0af012afdb865ad961eb47db2dbfccf61e731af913cefe716916db3f3dcc95fbcba090d40ffa2cbe8737f24c35ca3e3617
6
+ metadata.gz: 55aaebc78a856cfd8b3d92789507855c3b394a04c3d66ce2d6c3ad1283a80a5b5ab73773747a502b01f28f661a1574d36d199affb7f59855301c3f964a9d5310
7
+ data.tar.gz: 63fbd7d33485fba03a7de11c19db639bf096cd6286ffa31085481fedeed1c62016ae616ad5da7ed2f2363455eaef24618163bc1ff215b51a672eae5a7f506886
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.8.0-alpha3
1
+ 0.8.0-alpha4
data/bin/gt CHANGED
@@ -15,3 +15,4 @@ end
15
15
  # Run client
16
16
  require File.realpath(File.join(File.dirname(__FILE__),
17
17
  '..', 'lib', 'gaptool_client.rb'))
18
+ Gaptool::MainCommand.run
@@ -1,604 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
  # coding: utf-8
3
- # rubocop:disable Metrics/LineLength, Lint/Eval
3
+ # rubocop:disable Metrics/LineLength
4
4
 
5
- require 'rainbow'
6
- require 'json'
7
5
  require 'clamp'
8
- require 'sshkit'
9
- require 'sshkit/dsl'
10
- require 'set'
11
- require 'gaptool-api'
12
- require 'logger'
6
+ require 'gaptool_client/commands'
13
7
 
14
8
  module Gaptool
15
- START_MARKER = '###### GAPTOOL ######'
16
- STOP_MARKER = '###### END GAPTOOL ######'
17
-
18
- def self.api
19
- @api ||= GTAPI::GaptoolServer.new(
20
- ENV['GT_USER'], ENV['GT_KEY'],
21
- ENV['GT_URL'], ENV['GT_AWS_ZONE']
22
- )
23
- end
24
-
25
- def self.infohelper(nodes, parseable, grepable, short = false)
26
- if parseable && !short
27
- puts({ nodes: nodes }.to_json)
28
- elsif parseable
29
- puts({ nodes: nodes.map { |node| node.select { |k, _v| %w(role environment instance).include?(k) } } }.to_json)
30
- else
31
- nodes.each do |node|
32
- host = "#{node['role']}:#{node['environment']}:#{node['instance']}"
33
- if grepable && short
34
- puts host
35
- elsif !grepable
36
- puts Rainbow(host).green
37
- end
38
- next if short
39
- keys = node.keys.sort
40
- keys.each do |key|
41
- value = node[key]
42
- if grepable
43
- puts "#{host}|#{key}|#{value}"
44
- else
45
- value = Time.at(node[key].to_i) if key == 'launch_time'
46
- if key == keys.last
47
- puts " ┖ #{Rainbow(key).cyan}: #{value}\n\n"
48
- else
49
- puts " ┠ #{Rainbow(key).cyan}: #{value}"
50
- end
51
- end
52
- end
53
- end
54
- end
55
- end
56
-
57
- def self.error(message, opts = {})
58
- code = opts[:code] || 1
59
- color = opts[:color] || :red
60
- STDERR.puts(Rainbow(message).send(color))
61
- exit code
62
- end
63
-
64
- def self.get_host(node)
65
- "#{node['role']}-#{node['environment']}-#{node['instance']}"
66
- end
67
-
68
- def self.ssh_snip_for_node(node)
69
- host = Gaptool.get_host(node)
70
- <<-EOF
71
- # -- #{node['instance']}
72
- Host #{host} #{node['instance']}
73
- Hostname #{node['hostname']}
74
- User #{ENV['GT_USER']}
75
- LogLevel FATAL
76
- PreferredAuthentications publickey
77
- CheckHostIP no
78
- StrictHostKeyChecking no
79
- UserKnownHostsFile /dev/null
80
- # -- end #{node['instance']}
81
- EOF
82
- end
83
-
84
- def self.ssh_config
85
- config = File.join(Dir.home, '.ssh', 'config')
86
- dir = File.dirname(config)
87
- parent = File.join(dir, '..')
88
- if File.exist?(config)
89
- Gaptool.error "#{config}: not writable" unless File.writable?(config)
90
- content = File.read(config)
91
- # puts Rainbow("Creating backup file at #{config}.bck").green
92
- File.open("#{config}.bck", 'w') { |f| f.write(content) }
93
- else
94
- if !File.exist?(dir)
95
- Gaptool.error "Home directory #{parent} does not exists"\
96
- unless Dir.exist?(parent)
97
- Dir.mkdir(dir, 0700)
98
- elsif !File.directory?(dir)
99
- Gaptool.error "#{dir}: not a directory"
100
- end
101
- content = ''
102
- end
103
- [config, content]
104
- end
105
-
106
- def self.update_ssh_config_for(nodes, verbose=true)
107
- nodes = [nodes] unless nodes.is_a?(Array)
108
- config, content = Gaptool.ssh_config
109
- nodes.each do |node|
110
- snip = Gaptool.ssh_snip_for_node(node)
111
- mark = "# -- #{node['instance']}"
112
- emark = "# -- end #{node['instance']}"
113
-
114
- if Regexp.new(mark).match(content)
115
- puts Rainbow("Updating ssh config for #{get_host(node)}").green if verbose
116
- content.gsub!(/#{mark}.*?#{emark}/m, snip.strip)
117
- elsif Regexp.new(Gaptool::STOP_MARKER).match(content)
118
- puts Rainbow("Adding ssh config for #{get_host(node)}").green if verbose
119
- content.gsub!(/#{Gaptool::STOP_MARKER}/m, snip + "\n" + Gaptool::STOP_MARKER)
120
- else
121
- puts Rainbow('No gt ssh config found: please run gt ssh-config').yellow
122
- puts Rainbow("Adding ssh config for #{get_host(node)}").green if verbose
123
- content = <<-EOF
124
- #{content}
125
- #{Gaptool::START_MARKER}
126
- #{snip}
127
- #{Gaptool::STOP_MARKER}
128
- EOF
129
- end
130
- end
131
- File.open(config, 'w') { |f| f.write(content) }
132
- end
133
-
134
- def self.configure_sshkit
135
- SSHKit.config.output_verbosity = Logger::WARN
136
- SSHKit::Backend::Netssh.configure do |ssh|
137
- ssh.connection_timeout = 30
138
- ssh.ssh_options = {
139
- forward_agent: true,
140
- global_known_hosts_file: '/dev/null',
141
- keys_only: true,
142
- port: 22,
143
- user: ENV['GT_USER']
144
- }
145
- end
146
- end
147
-
148
- def self.query_nodes(opts)
149
- instance = opts[:instance]
150
- role = opts[:role]
151
- environment = opts[:environment]
152
- params = opts[:params]
153
-
154
- if instance
155
- puts Rainbow('Ignoring role and environment as instance is set').red if role || environment
156
- [Gaptool.api.getonenode(instance)]
157
- elsif role && environment
158
- Gaptool.api.getenvroles(role, environment, params)
159
- elsif role
160
- Gaptool.api.getrolenodes(role, params)
161
- elsif environment
162
- Gaptool.api.getenvnodes(environment, params)
163
- else
164
- Gaptool.api.getallnodes(params)
165
- end
166
- end
167
-
168
- def self.remote_exec(nodes, commands, opts = {})
169
- serial = opts[:serial]
170
- group_size = opts[:group_size] || 10
171
- if opts[:update_ssh_config].nil? || opts[:update_ssh_config]
172
- nodes.each { |n| Gaptool.update_ssh_config_for(n, false) }
173
- end
174
- pre = opts[:pre_hooks] || []
175
- post = opts[:post_hooks] || []
176
- nodes = Hash[nodes.map { |n| [n['hostname'], n] }]
177
- handlers = Hash[nodes.map { |hostname, node| [hostname, InteractionHandler.new(Gaptool.get_host(node))] }]
178
- opts = { in: :groups, limit: group_size }
179
- opts = { in: :sequence } if serial
180
- Gaptool.configure_sshkit
181
- on(nodes.keys, opts) do |host|
182
- pre.each { |h| instance_exec(nodes[host.hostname], &h) }
183
- commands.each do |cmd|
184
- execute(:bash, "-l -c '#{cmd}'", interaction_handler: handlers[host.hostname])
185
- end
186
- post.each { |h| instance_exec(nodes[host.hostname], &h) }
187
- end
188
- end
189
-
190
- def self.split_attrs(attribute_list)
191
- opts = {}
192
- attribute_list.each do |attr_|
193
- key, value = attr_.split('=', 2)
194
- split = key.split('.')
195
- cur = opts
196
- split.each_with_index do |part, idx|
197
- if idx == split.size - 1
198
- # leaf, add the value
199
- cur[part] = value
200
- else
201
- cur[part] ||= {}
202
- end
203
- cur = cur[part]
204
- end
205
- end
206
- opts
207
- end
208
-
209
- class InteractionHandler
210
- attr_reader :host
211
- def initialize(host)
212
- @host = host
213
- end
214
-
215
- def on_data(_command, stream_name, data, _channel)
216
- case stream_name
217
- when :stdout
218
- puts "#{Rainbow("#{@host}").yellow}> #{data}"
219
- when :stderr
220
- STDERR.puts "#{Rainbow("#{@host}").red}> #{data}"
221
- end
222
- end
223
- end
224
-
225
- class InitCommand < Clamp::Command
226
- option ['-r', '--role'], 'ROLE', 'Resource name to initilize', required: true
227
- option ['-e', '--environment'], 'ENVIRONMENT', 'Which environment, e.g. production', required: true
228
- option ['-z', '--zone'], 'ZONE', 'AWS availability zone to put node in', default: 'us-west-2c'
229
- option ['-t', '--type'], 'TYPE', 'Type of instance, e.g. m1.large', required: true
230
- option ['-s', '--security-group'], 'SECURITY_GROUP', 'Security group name. Defaults to $role-$environment'
231
- option ['-a', '--ami'], 'AMI_ID', 'Use a specific AMI for the instance (i.e. ami-xxxxxxx)'
232
- option ['-C', '--chef-repo'], 'GITURL', 'git url for the chef repository'
233
- option ['-b', '--chef-branch'], 'BRANCH', 'branch of the chef repository to use'
234
- option(['-R', '--chef_runlist'], 'RECIPE|ROLE',
235
- 'override chef run_list. recipe[cb::recipe] or role[myrole]. Can be specified multiple times',
236
- multivalued: true, attribute_name: 'chef_runlist')
237
- option ['--no-terminate'], :flag, 'Add terminate protection'
238
- def execute
239
- no_terminate = no_terminate? ? true : nil
240
- Gaptool.api.addnode(zone, type, role, environment, nil, security_group,
241
- ami, chef_repo, chef_branch, chef_runlist,
242
- no_terminate)
243
- end
244
- end
245
-
246
- class TerminateCommand < Clamp::Command
247
- option ['-i', '--instance'], 'INSTANCE', 'Instance ID, e.g. i-12345678', required: true
248
- option ['-z', '--zone'], 'ZONE', 'AWS region of the node (deprecated/ignored)'
249
- option ['-r', '--role'], 'ROLE', 'Resource name to initilize', required: true
250
- option ['-e', '--environment'], 'ENVIRONMENT', 'Which environment, e.g. production', required: true
251
-
252
- def execute
253
- node = Gaptool.api.getonenode(instance)
254
- nodes = [node]
255
- Gaptool.infohelper(nodes, false, false)
256
- zone = node['zone'][0..-2]
257
- if node['environment'] != environment || node['role'] != role
258
- puts Rainbow("'#{node['role']}-#{node['environment']}' do not match provided value (#{role}-#{environment})").red
259
- exit 1
260
- end
261
- if node['terminate'] == 'false'
262
- puts Rainbow('"terminate" command is disabled for this instance').red
263
- exit 2
264
- end
265
- print Rainbow('Terminate instance? [type yes to confirm]: ').green
266
- res = $stdin.gets.chomp
267
- return 0 unless res.downcase == 'yes'
268
- puts "Terminating instance #{node['role']}:#{node['environment']}:#{node['instance']} in region #{zone}"
269
- begin
270
- Gaptool.api.terminatenode(instance, zone)
271
- rescue
272
- puts Rainbow('Cannot terminate instance').red
273
- end
274
- end
275
- end
276
-
277
- class RuncmdCommand < Clamp::Command
278
- option ['-r', '--role'], 'ROLE', 'Instance role'
279
- option ['-e', '--environment'], 'ENVIRONMENT', 'Instance environment'
280
- option ['-i', '--instance'], 'INSTANCE', 'Instance id (i-xxxxxxxx)'
281
- option ['-s', '--serial'], :flag, 'Run command serially. Order of execution is unknown.'
282
- option ['-x', '--exclude-hidden'], :flag, 'Exclude hidden hosts'
283
- parameter 'COMMAND ...', 'Command to run', attribute_name: :commands
284
-
285
- def execute
286
- params = exclude_hidden? ? {} : { hidden: true }
287
- nodes = Gaptool.query_nodes(params.merge(instance: instance,
288
- role: role, environment: environment))
289
- Gaptool.remote_exec(nodes, [commands.join(' ')], serial: serial?)
290
- end
291
- end
292
-
293
- class SSHConfigCommand < Clamp::Command
294
- option ['-r', '--remove'], :flag, 'Remove ssh configuration'
295
-
296
- def execute
297
- config, content = Gaptool.ssh_config
298
-
299
- if remove?
300
- data = ''
301
- else
302
- puts Rainbow('Getting nodes from API').green
303
- data = [Gaptool::START_MARKER, '# to remove this block, run gt ssh-config --remove']
304
- Gaptool.api.getallnodes(hidden: true).sort_by { |n| n['instance'] }.each do |node|
305
- host = Gaptool.get_host(node)
306
- puts " - #{Rainbow(host).blue}"
307
- data << Gaptool.ssh_snip_for_node(node)
308
- end
309
- data << Gaptool::STOP_MARKER
310
- data = data.join("\n")
311
- end
312
-
313
- if Regexp.new(Gaptool::START_MARKER).match(content)
314
- content.gsub!(/#{Gaptool::START_MARKER}.*?#{Gaptool::STOP_MARKER}/m, data)
315
- elsif !remove?
316
- content = content + "\n" + data
317
- end
318
- File.open(config, 'w') { |f| f.write(content) }
319
- end
320
- end
321
-
322
- class SSHCommand < Clamp::Command
323
- option ['-r', '--role'], 'ROLE', 'Instance role'
324
- option ['-e', '--environment'], 'ENVIRONMENT', 'Instance environment'
325
- option ['-i', '--instance'], 'INSTANCE', 'Instance id (i-xxxxxxxx)'
326
- option ['-f', '--first'], :flag, 'Just connect to first available instance'
327
- option ['-t', '--tmux'], :flag, 'No-op, DEPRECATED'
328
-
329
- def execute
330
- puts Rainbow('tmux support has been removed').yellow if tmux?
331
- nodes = Gaptool.query_nodes(hidden: true,
332
- instance: instance,
333
- environment: environment,
334
- role: role)
335
-
336
- if first? || (nodes.length == 1 && !instance)
337
- puts Rainbow('No instance specified, but only one instance in cluster or first forced').green
338
- node = nodes.first
339
- elsif !instance
340
- nodes.each_index do |i|
341
- puts "#{i}: #{nodes[i]['instance']}"
342
- end
343
- print Rainbow('Select a node: ').cyan
344
- node = nodes[$stdin.gets.chomp.to_i]
345
- error 'Invalid selection' if node.nil?
346
- else
347
- node = nodes.first
348
- end
349
- Gaptool.update_ssh_config_for(node)
350
- system "ssh #{node['instance']}"
351
- end
352
- end
353
-
354
- class InfoCommand < Clamp::Command
355
- option ['-r', '--role'], 'ROLE', 'Role name, e.g. frontend'
356
- option ['-e', '--environment'], 'ENVIRONMENT', 'Which environment, e.g. production'
357
- option ['-i', '--instance'], 'INSTANCE', 'Node instance, leave blank to query avilable nodes'
358
- option ['-s', '--short'], :flag, 'Only show summary for hosts'
359
- option ['-p', '--parseable'], :flag, 'Display in non-pretty parseable JSON'
360
- option ['-g', '--grepable'], :flag, 'Display in non-pretty grep-friendly text'
361
- option ['-H', '--hidden'], :flag, 'Display hidden hosts'
362
-
363
- def execute
364
- nodes = []
365
- params = hidden? ? { hidden: true } : {}
366
- nodes = Gaptool.query_nodes(params.merge(instance: instance,
367
- role: role,
368
- environment: environment))
369
- Gaptool.infohelper(nodes, parseable?, grepable?, short?)
370
- end
371
- end
372
-
373
- class SetCommand < Clamp::Command
374
- option ['-i', '--instance'], 'INSTANCE', 'Node instance, required', required: true
375
- option ['-p', '--parseable'], :flag, 'Display in non-pretty parseable JSON'
376
- option ['-g', '--grepable'], :flag, 'Display in non-pretty grep-friendly text'
377
- option ['-k', '--parameter'], 'NAME', 'Set parameter for the node', required: true, multivalued: true
378
- option ['-v', '--value'], 'VALUE', 'Value for parameter', required: true, multivalued: true
379
-
380
- def convert_bool(v)
381
- case v.downcase
382
- when 'true'
383
- true
384
- when 'false'
385
- false
386
- else
387
- v
388
- end
389
- end
390
-
391
- def execute
392
- if parameter_list.length != value_list.length
393
- puts Rainbow('parameter and value length mismatch').red
394
- end
395
- params = Hash[parameter_list.each_with_index.map { |p, i| [p, convert_bool(value_list[i])] }]
396
- Gaptool.infohelper([Gaptool.api.setparameters(instance, params)], parseable?, grepable?)
397
- end
398
- end
399
-
400
- class ChefrunCommand < Clamp::Command
401
- option ['-r', '--role'], 'ROLE', 'Role name to ssh to'
402
- option ['-e', '--environment'], 'ENVIRONMENT', 'Which environment, e.g. production'
403
- option ['-i', '--instance'], 'INSTANCE', 'Instance ID, e.g. i-12345678'
404
- option ['-H', '--hidden'], :flag, 'Include hidden hosts'
405
- option(['-A', '--attribute'], 'ATTRIBUTE',
406
- 'Pass one or more parameters to the deploy recipe in recipe.attr=value format',
407
- multivalued: true)
408
- option(['-b', '--chef-branch'], 'BRANCH',
409
- 'branch of the chef repository to use (defaults to last branch used during init/chefrun)',
410
- default: nil)
411
- option ['-s', '--serial'], :flag, 'Run command serially. Order of execution is unknown.'
412
- option ['-W', '--whyrun'], :flag, 'Whyrun, like dry-run but different.'
413
-
414
- def execute
415
- attrs = Gaptool.split_attrs(attribute_list)
416
- nodes = Gaptool.query_nodes(hidden: hidden? ? true : nil,
417
- role: role,
418
- instance: instance,
419
- environment: environment)
420
-
421
- nodes = nodes.map do |x|
422
- x['whyrun'] = whyrun?
423
- x['chef_branch'] = chef_branch
424
- x['attrs'] = attrs
425
- x
426
- end
427
-
428
- pre_hook = Proc.new do |node|
429
- if node['chef_runlist'].nil?
430
- runlist = ['recipe[main]']
431
- elsif node['chef_runlist'].is_a? Array
432
- runlist = node['chef_runlist']
433
- else
434
- runlist = eval(node['chef_runlist'])
435
- end
436
- json = {
437
- 'this_server' => "#{node['role']}-#{node['environment']}-#{node['instance']}",
438
- 'role' => node['role'],
439
- 'environment' => node['environment'],
440
- 'app_user' => node['appuser'],
441
- 'run_list' => runlist,
442
- 'hostname' => node['hostname'],
443
- 'instance' => node['instance'],
444
- 'zone' => node['zone'],
445
- 'itype' => node['itype'],
446
- 'apps' => eval(node['apps'] || '[]'),
447
- 'gaptool' => {
448
- 'user' => ENV['GT_USER'],
449
- 'key' => ENV['GT_KEY'],
450
- 'url' => ENV['GT_URL']
451
- }
452
- }.merge(node['attrs'])
453
- git = 'sudo -u admin git'
454
- pull = "#{git} fetch --all; #{git} reset --hard origin/`#{git} rev-parse --abbrev-ref HEAD`"
455
- wopts = node['whyrun'] ? ' -W ' : ''
456
-
457
- unless node['chef_branch'].nil?
458
- json['chefbranch'] = node['chef_branch']
459
- pull = "#{git} checkout -f #{node['chef_branch']}; #{git} fetch --all; #{git} reset --hard origin/#{node['chef_branch']}"
460
- end
461
- upload!(StringIO.new(json.to_json), '/tmp/chef.json')
462
- script = <<-EOS
463
- cd /var/data/admin/ops
464
- #{pull}
465
- sudo chef-solo -c /var/data/admin/ops/cookbooks/solo.rb -j /tmp/chef.json -E #{node['environment']}#{wopts}
466
- rm -f /tmp/chef.json
467
- EOS
468
- upload!(StringIO.new(script), '/tmp/chef.sh')
469
- end
470
- Gaptool.remote_exec(nodes,
471
- ['chmod +x /tmp/chef.sh', '/tmp/chef.sh', 'rm -f /tmp/chef.sh'],
472
- pre_hooks: [pre_hook], serial: serial?)
473
- end
474
- end
475
-
476
- class DeployCommand < Clamp::Command
477
- option(['-a', '--app'], 'APP',
478
- 'Application(s) to deploy (can be set multiple times)',
479
- required: true, multivalued: true)
480
- option ['-m', '--migrate'], :flag, 'Toggle running migrations'
481
- option ['-e', '--environment'], 'ENVIRONMENT', 'Which environment, e.g. production', required: true
482
- option ['-b', '--branch'], 'BRANCH', 'Git branch to deploy, default is master'
483
- option ['-r', '--rollback'], :flag, 'Toggle this to rollback last deploy'
484
- option ['-i', '--instance'], 'INSTANCE', 'Instance ID, e.g. i-12345678. If set, all applications MUST be hosted on this node.'
485
- option ['-A', '--attribute'], 'ATTRIBUTE', 'Pass one or more parameters to the deploy recipe in recipe.attr=value format', multivalued: true
486
- option ['-H', '--hidden'], :flag, 'Display hidden hosts'
487
- option ['-s', '--serial'], :flag, 'Run command serially. Order of execution is unknown.'
488
-
489
- def execute # rubocop:disable Metrics/MethodLength
490
- attrs = Gaptool.split_attrs(attribute_list)
491
- if instance
492
- n = Gaptool.api.getonenode(instance)
493
- if n['environment'] != environment
494
- Gaptool.error "Instance #{instance} is not in environment #{environment}"
495
- else
496
- app_list.each do |app|
497
- Gaptool.error "Instance #{instance} does not host #{app} in env #{environment}" \
498
- unless n['apps'].include?(app)
499
- end
500
- end
501
- nodes = [n]
502
-
503
- else
504
- params = hidden? ? { hidden: true } : {}
505
- nodes = []
506
- app_list.each do |app|
507
- nodes.concat(Gaptool.api.getappnodes(app, environment, params))
508
- end
509
- end
510
-
511
- # dedup nodes
512
- seen = Set.new
513
- app_set = Set.new(app_list)
514
- nodes = nodes.select do |x|
515
- res = !seen.include?(x['instance'])
516
- seen << x['instance']
517
- res
518
- end
519
- nodes = nodes.map do |x|
520
- x['apps'] = eval(x['apps'])
521
- x['apps_to_deploy'] = (Set.new(x['apps']) & app_set).to_a
522
- x['rollback'] = rollback?
523
- x['branch'] = branch || 'master'
524
- x['migrate'] = migrate?
525
- x['attrs'] = attrs
526
- x
527
- end
528
-
529
- pre_hook = Proc.new do |node|
530
- host = "#{node['role']}:#{node['environment']}:#{node['instance']}"
531
- puts "#{Rainbow('Deploying apps').cyan} '" + \
532
- Rainbow(node['apps_to_deploy'].join(' ')).green + \
533
- "' #{Rainbow('on').cyan} " + \
534
- Rainbow(host).green
535
-
536
- if node['chef_runlist'].nil?
537
- runlist = ['recipe[deploy]']
538
- elsif node['chef_runlist'].is_a? Array
539
- runlist = node['chef_runlist']
540
- else
541
- runlist = eval(node['chef_runlist'])
542
- end
543
- json = {
544
- 'this_server' => "#{node['role']}-#{node['environment']}-#{node['instance']}",
545
- 'role' => node['role'],
546
- 'environment' => node['environment'],
547
- 'app_user' => node['appuser'],
548
- 'run_list' => runlist,
549
- 'hostname' => node['hostname'],
550
- 'instance' => node['instance'],
551
- 'zone' => node['zone'],
552
- 'itype' => node['itype'],
553
- 'apps' => node['apps'],
554
- 'deploy_apps' => node['apps_to_deploy'],
555
- 'rollback' => node['rollback'],
556
- 'branch' => node['branch'],
557
- 'migrate' => node['migrate'],
558
- 'gaptool' => {
559
- 'user' => ENV['GT_USER'],
560
- 'key' => ENV['GT_KEY'],
561
- 'url' => ENV['GT_URL']
562
- }
563
- }.merge(node['attrs']).to_json
564
- upload!(StringIO.new(json), '/tmp/chef.json')
565
- script = <<-EOS
566
- cd /var/data/admin/ops
567
- sudo -u admin git pull
568
- sudo chef-solo -c /var/data/admin/ops/cookbooks/solo.rb -j /tmp/chef.json -E #{node['environment']}
569
- rm -f /tmp/chef.json
570
- EOS
571
- upload!(StringIO.new(script), '/tmp/deploy.sh')
572
- end
573
-
574
- Gaptool.remote_exec(nodes,
575
- ['chmod +x /tmp/deploy.sh', '/tmp/deploy.sh', 'rm -f /tmp/deploy.sh'],
576
- pre_hooks: [pre_hook], serial: serial?)
577
- end
578
- end
579
-
580
- class RehashCommand < Clamp::Command
581
- option ['-y', '--yes'], :flag, 'YES I REALLY WANT TO DO THIS'
582
- def execute
583
- if yes?
584
- puts Gaptool.api.rehash
585
- else
586
- puts "You need to run this with -y\nIf you don't know what this does or aren't sure, DO NOT RUN IT\nThis will regenerate all host metadata on gaptool-server\nand can break in-progress operations."
587
- end
588
- end
589
- end
590
-
591
- class VersionCommand < Clamp::Command
592
- option ['-r', '--remote'], :flag, 'Include remote API version'
593
- def execute
594
- version = File.read(File.realpath(File.join(File.dirname(__FILE__), '..', 'VERSION'))).strip
595
- puts "gaptool-client #{version} using gaptool-api #{Gaptool.api.version}"
596
- return 0 unless remote?
597
- vinfo = Gaptool.api.api_version
598
- puts "gaptool-server #{vinfo['server_version']}"
599
- end
600
- end
601
-
602
9
  class MainCommand < Clamp::Command
603
10
  subcommand 'info', 'Displays information about nodes', InfoCommand
604
11
  subcommand 'init', 'Create new application cluster', InitCommand
@@ -613,5 +20,3 @@ EOS
613
20
  subcommand 'version', 'Show version', VersionCommand
614
21
  end
615
22
  end
616
-
617
- Gaptool::MainCommand.run
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gaptool-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0.pre.alpha3
4
+ version: 0.8.0.pre.alpha4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Francesco Laurita
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2015-12-30 00:00:00.000000000 Z
13
+ date: 2015-12-31 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: json