collins_shell 0.2.17 → 0.2.18

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.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.17
1
+ 0.2.18
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "collins_shell"
8
- s.version = "0.2.17"
8
+ s.version = "0.2.18"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Blake Matheny"]
12
- s.date = "2012-12-18"
12
+ s.date = "2013-03-19"
13
13
  s.description = "Provides basic CLI for interacting with Collins API"
14
14
  s.email = "bmatheny@tumblr.com"
15
15
  s.executables = ["collins-shell"]
@@ -49,8 +49,8 @@ module CollinsShell
49
49
  method_option :exec, :type => :string, :desc => 'Execute a command using the data from this asset. Use {{hostname}}, {{ipmi.password}}, etc for substitution'
50
50
  method_option :header, :type => :boolean, :default => true, :desc => 'Display a tag header. Defaults to true'
51
51
  method_option :logs, :type => :boolean, :default => false, :desc => 'Also display asset logs, only used with details (SLOW)'
52
- method_option :size, :type => :numeric, :default => 50, :desc => 'Number of results to find. Defaults to 50'
53
52
  method_option :tags, :type => :array, :desc => 'Tags to display of form: tag1 tag2 tag3. e.g. --tags=hostname tag backend_ip_address'
53
+ method_option :threads, :type => :numeric, :default => 1, :desc => 'Number of threads to use for --exec. Defaults to 1.'
54
54
  method_option :url, :type => :boolean, :default => true, :desc => 'Display a URL along with tags or the default listing'
55
55
  def find
56
56
  client = get_collins_client
@@ -69,14 +69,15 @@ module CollinsShell
69
69
  :detailed => !options.exec?
70
70
  puts printer
71
71
  end
72
- asset_exec asset, options.exec, options.confirm
72
+ asset_exec asset, options.exec, options.confirm, options.threads
73
73
  end
74
74
  else
75
75
  if not options.quiet then
76
76
  print_find_results assets, options.tags, :header => options.header, :url => options.url
77
77
  end
78
- assets.each {|asset| asset_exec(asset, options.exec, options.confirm)}
78
+ assets.each {|asset| asset_exec(asset, options.exec, options.confirm, options.threads)}
79
79
  end
80
+ finalize_exec
80
81
  end
81
82
 
82
83
  desc 'find_similar TAG', 'get similar unallocated assets for a gven asset'
@@ -136,6 +137,7 @@ module CollinsShell
136
137
  batch_selector_operation Hash[
137
138
  :remote => options.remote,
138
139
  :operation => "set_attribute",
140
+ :size => options["size"],
139
141
  :success_message => proc {|asset| "Set attribute on #{asset.tag}"},
140
142
  :error_message => proc{|asset| "Setting attribute on #{asset.tag}"},
141
143
  :confirmation_message => proc do |assets|
@@ -172,6 +174,7 @@ module CollinsShell
172
174
  batch_selector_operation Hash[
173
175
  :remote => options.remote,
174
176
  :operation => "set_attributes",
177
+ :size => options["size"],
175
178
  :success_message => proc {|asset| "Set attributes on #{asset.tag}"},
176
179
  :error_message => proc{|asset| "Setting attributes on #{asset.tag}"},
177
180
  :confirmation_message => proc do |assets|
@@ -190,6 +193,7 @@ module CollinsShell
190
193
  batch_selector_operation Hash[
191
194
  :remote => options.remote,
192
195
  :operation => "delete_attribute",
196
+ :size => options["size"],
193
197
  :success_message => proc {|asset| "Delete attribute on #{asset.tag}"},
194
198
  :error_message => proc{|asset| "Delete attribute on #{asset.tag}"},
195
199
  :confirmation_message => proc do |assets|
@@ -217,6 +221,7 @@ module CollinsShell
217
221
  batch_selector_operation Hash[
218
222
  :remote => options.remote,
219
223
  :operation => "set_status",
224
+ :size => options["size"],
220
225
  :success_message => proc {|asset| "Set status to #{status} on #{asset.tag}"},
221
226
  :error_message => proc{|asset| "Setting status on #{asset.tag}"},
222
227
  :confirmation_message => proc do |assets|
@@ -96,6 +96,7 @@ module CollinsShell; module Console; module Commands
96
96
  end
97
97
 
98
98
  def display_asset tag
99
+ tag = tag.gsub(/^\//, '')
99
100
  if asset_exists? tag then
100
101
  asset = get_asset tag
101
102
  show_logs = opts.logs?
@@ -48,6 +48,7 @@ module CollinsShell
48
48
  def use_selector_option required = false
49
49
  method_option :selector, :type => :hash, :required => required, :desc => 'Selector to query collins. Takes the form of --selector=key1:val1 key2:val2 etc'
50
50
  method_option :remote, :type => :boolean, :default => false, :desc => 'Search all collins instances, including remote ones'
51
+ method_option :size, :type => :numeric, :default => 50, :desc => 'Number of results to find. Defaults to 50'
51
52
  end
52
53
 
53
54
  def selector_or_tag
@@ -61,9 +62,16 @@ module CollinsShell
61
62
  end
62
63
 
63
64
  def require_yes message, color = nil, should_exit = true
65
+ def appropriate_answer?(a); na = a.to_s.downcase.strip; na == 'yes' || na == 'no'; end
64
66
  highline = HighLine.new
65
67
  colored_message = set_color(message, color)
66
- answer = ask(colored_message)
68
+ answer = nil
69
+ while !appropriate_answer?(answer) do
70
+ unless answer.nil? then
71
+ say_status "error", "Please type 'yes' or 'no'.", :red
72
+ end
73
+ answer = ask(colored_message)
74
+ end
67
75
  if answer.downcase.strip !~ /^yes$/ then
68
76
  if should_exit then
69
77
  exit(0)
@@ -84,7 +92,7 @@ module CollinsShell
84
92
  require_non_empty(success_message, "success_message not set")
85
93
  require_non_empty(error_message, "error_message not set")
86
94
  require_non_empty(operation, "operation not set")
87
- selector = get_selector selector_or_tag, [], nil, options[:remote]
95
+ selector = get_selector selector_or_tag, [], options[:size], options[:remote]
88
96
  call_collins get_collins_client, operation do |client|
89
97
  assets = client.find selector
90
98
  if assets.length > 1 then
@@ -1,5 +1,6 @@
1
1
  require 'collins_shell/monkeypatch'
2
2
  require 'collins_shell/util/asset_stache'
3
+ require 'open3'
3
4
 
4
5
  module CollinsShell
5
6
  module Util
@@ -50,13 +51,65 @@ module CollinsShell
50
51
  selector
51
52
  end
52
53
 
53
- def asset_exec asset, execs, confirm = true
54
+ def asset_exec asset, execs, confirm = true, threads = 1
54
55
  return unless execs
55
56
  mustache = CollinsShell::Util::AssetStache.new asset
56
57
  rendered = mustache.render "/bin/bash -c '#{execs}'"
57
58
  say_status("exec", rendered, :red)
58
59
  require_yes("Running on #{asset.tag}. ARE YOU SURE?", :red) if confirm
59
- system(rendered)
60
+ if threads < 2 then
61
+ system(rendered)
62
+ else
63
+ run_command_in_thread(rendered, threads)
64
+ end
65
+ end
66
+
67
+ # Should be called after asset_exec
68
+ def finalize_exec
69
+ thread_mgr.threads.each(&:join)
70
+ end
71
+
72
+ # Wraps open threads and provides a synchronized output buffer for concurrently writing output
73
+ def thread_mgr
74
+ @_thread_mgr ||= OpenStruct.new({
75
+ :threads => [],
76
+ :lock => Mutex.new
77
+ })
78
+ end
79
+
80
+ def say_status(status, message, log_status=true)
81
+ thread_mgr.lock.synchronize {
82
+ super
83
+ }
84
+ end
85
+
86
+ # @param [String] syscmd The system command to execute
87
+ # @param [Fixnum] thread_count The numbers of threads to allow running at one time
88
+ def run_command_in_thread syscmd, thread_count
89
+ if thread_mgr.threads.size > thread_count then
90
+ thread_mgr.threads.each(&:join)
91
+ thread_mgr.threads.clear
92
+ else
93
+ thread_mgr.threads << Thread.new(syscmd) do |cmd|
94
+ begin
95
+ stdin, stdout, stderr, thread = Open3.popen3(cmd)
96
+ stdin.close
97
+ o, e = [stdout, stderr].map {|p| begin p.read ensure p.close end}
98
+ unless o.empty? then
99
+ say_status("stdout: #{cmd}", o, :green)
100
+ end
101
+ unless e.empty? then
102
+ say_status("stderr: #{cmd}", e, :red)
103
+ end
104
+ if thread then
105
+ Process.kill('TERM', thread.pid) rescue nil
106
+ thread.kill unless nil
107
+ end
108
+ rescue Exception => e
109
+ say_status("exception: #{cmd}", e.to_s, :red)
110
+ end
111
+ end
112
+ end
60
113
  end
61
114
 
62
115
  def asset_get tag, options
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: collins_shell
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.17
4
+ version: 0.2.18
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-18 00:00:00.000000000 Z
12
+ date: 2013-03-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: collins_client
@@ -184,7 +184,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
184
184
  version: '0'
185
185
  segments:
186
186
  - 0
187
- hash: -728758615218698730
187
+ hash: -4494318623502944839
188
188
  required_rubygems_version: !ruby/object:Gem::Requirement
189
189
  none: false
190
190
  requirements: