choria-mcorpc-support 2.21.0 → 2.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mcollective.rb +1 -1
  3. data/lib/mcollective/agent/bolt_tasks.ddl +235 -0
  4. data/lib/mcollective/agent/bolt_tasks.json +347 -0
  5. data/lib/mcollective/agent/bolt_tasks.rb +176 -0
  6. data/lib/mcollective/agent/choria_util.ddl +152 -0
  7. data/lib/mcollective/agent/choria_util.json +244 -0
  8. data/lib/mcollective/agent/rpcutil.ddl +7 -3
  9. data/lib/mcollective/agent/rpcutil.json +333 -0
  10. data/lib/mcollective/agent/scout.ddl +169 -0
  11. data/lib/mcollective/agent/scout.json +224 -0
  12. data/lib/mcollective/agents.rb +7 -6
  13. data/lib/mcollective/aggregate.rb +4 -4
  14. data/lib/mcollective/aggregate/average.rb +2 -2
  15. data/lib/mcollective/aggregate/base.rb +2 -2
  16. data/lib/mcollective/aggregate/result.rb +3 -3
  17. data/lib/mcollective/aggregate/result/collection_result.rb +2 -2
  18. data/lib/mcollective/aggregate/result/numeric_result.rb +2 -2
  19. data/lib/mcollective/aggregate/sum.rb +2 -2
  20. data/lib/mcollective/aggregate/summary.rb +3 -4
  21. data/lib/mcollective/application.rb +57 -21
  22. data/lib/mcollective/application/choria.rb +249 -0
  23. data/lib/mcollective/application/completion.rb +6 -6
  24. data/lib/mcollective/application/describe_filter.rb +20 -20
  25. data/lib/mcollective/application/facts.rb +19 -11
  26. data/lib/mcollective/application/federation.rb +239 -0
  27. data/lib/mcollective/application/find.rb +4 -4
  28. data/lib/mcollective/application/help.rb +3 -3
  29. data/lib/mcollective/application/inventory.rb +3 -341
  30. data/lib/mcollective/application/ping.rb +3 -77
  31. data/lib/mcollective/application/playbook.rb +207 -0
  32. data/lib/mcollective/application/plugin.rb +106 -106
  33. data/lib/mcollective/application/rpc.rb +3 -108
  34. data/lib/mcollective/application/tasks.rb +416 -0
  35. data/lib/mcollective/applications.rb +11 -10
  36. data/lib/mcollective/audit/choria.rb +33 -0
  37. data/lib/mcollective/cache.rb +2 -4
  38. data/lib/mcollective/client.rb +11 -10
  39. data/lib/mcollective/config.rb +21 -34
  40. data/lib/mcollective/connector/base.rb +2 -1
  41. data/lib/mcollective/connector/nats.ddl +9 -0
  42. data/lib/mcollective/connector/nats.rb +450 -0
  43. data/lib/mcollective/data.rb +8 -3
  44. data/lib/mcollective/data/agent_data.rb +1 -1
  45. data/lib/mcollective/data/base.rb +6 -5
  46. data/lib/mcollective/data/bolt_task_data.ddl +90 -0
  47. data/lib/mcollective/data/bolt_task_data.rb +32 -0
  48. data/lib/mcollective/data/collective_data.rb +1 -1
  49. data/lib/mcollective/data/fact_data.rb +6 -6
  50. data/lib/mcollective/data/fstat_data.rb +2 -4
  51. data/lib/mcollective/data/result.rb +7 -2
  52. data/lib/mcollective/ddl/agentddl.rb +5 -17
  53. data/lib/mcollective/ddl/base.rb +10 -13
  54. data/lib/mcollective/discovery.rb +12 -26
  55. data/lib/mcollective/discovery/choria.ddl +11 -0
  56. data/lib/mcollective/discovery/choria.rb +223 -0
  57. data/lib/mcollective/discovery/flatfile.rb +7 -8
  58. data/lib/mcollective/discovery/mc.rb +2 -2
  59. data/lib/mcollective/discovery/stdin.rb +17 -18
  60. data/lib/mcollective/exceptions.rb +13 -0
  61. data/lib/mcollective/facts/base.rb +9 -9
  62. data/lib/mcollective/facts/yaml_facts.rb +12 -12
  63. data/lib/mcollective/generators.rb +3 -3
  64. data/lib/mcollective/generators/agent_generator.rb +3 -4
  65. data/lib/mcollective/generators/base.rb +14 -15
  66. data/lib/mcollective/generators/data_generator.rb +5 -6
  67. data/lib/mcollective/log.rb +2 -2
  68. data/lib/mcollective/logger/base.rb +3 -2
  69. data/lib/mcollective/logger/console_logger.rb +10 -10
  70. data/lib/mcollective/logger/file_logger.rb +7 -7
  71. data/lib/mcollective/logger/syslog_logger.rb +11 -15
  72. data/lib/mcollective/matcher.rb +14 -14
  73. data/lib/mcollective/matcher/parser.rb +31 -41
  74. data/lib/mcollective/matcher/scanner.rb +69 -74
  75. data/lib/mcollective/message.rb +10 -17
  76. data/lib/mcollective/monkey_patches.rb +2 -4
  77. data/lib/mcollective/optionparser.rb +1 -0
  78. data/lib/mcollective/pluginmanager.rb +3 -5
  79. data/lib/mcollective/pluginpackager.rb +1 -3
  80. data/lib/mcollective/pluginpackager/agent_definition.rb +3 -8
  81. data/lib/mcollective/pluginpackager/forge_packager.rb +7 -9
  82. data/lib/mcollective/pluginpackager/standard_definition.rb +1 -2
  83. data/lib/mcollective/registration/base.rb +18 -16
  84. data/lib/mcollective/rpc.rb +2 -4
  85. data/lib/mcollective/rpc/actionrunner.rb +16 -18
  86. data/lib/mcollective/rpc/agent.rb +26 -43
  87. data/lib/mcollective/rpc/audit.rb +1 -0
  88. data/lib/mcollective/rpc/client.rb +67 -85
  89. data/lib/mcollective/rpc/helpers.rb +55 -62
  90. data/lib/mcollective/rpc/progress.rb +2 -2
  91. data/lib/mcollective/rpc/reply.rb +17 -19
  92. data/lib/mcollective/rpc/request.rb +7 -5
  93. data/lib/mcollective/rpc/result.rb +6 -8
  94. data/lib/mcollective/rpc/stats.rb +49 -58
  95. data/lib/mcollective/security/base.rb +29 -36
  96. data/lib/mcollective/security/choria.rb +765 -0
  97. data/lib/mcollective/shell.rb +9 -4
  98. data/lib/mcollective/signer/base.rb +28 -0
  99. data/lib/mcollective/signer/choria.rb +185 -0
  100. data/lib/mcollective/ssl.rb +8 -6
  101. data/lib/mcollective/util.rb +58 -55
  102. data/lib/mcollective/util/bolt_support.rb +176 -0
  103. data/lib/mcollective/util/bolt_support/plan_runner.rb +167 -0
  104. data/lib/mcollective/util/bolt_support/task_result.rb +94 -0
  105. data/lib/mcollective/util/bolt_support/task_results.rb +128 -0
  106. data/lib/mcollective/util/choria.rb +1103 -0
  107. data/lib/mcollective/util/indifferent_hash.rb +12 -0
  108. data/lib/mcollective/util/natswrapper.rb +242 -0
  109. data/lib/mcollective/util/playbook.rb +435 -0
  110. data/lib/mcollective/util/playbook/data_stores.rb +201 -0
  111. data/lib/mcollective/util/playbook/data_stores/base.rb +99 -0
  112. data/lib/mcollective/util/playbook/data_stores/consul_data_store.rb +88 -0
  113. data/lib/mcollective/util/playbook/data_stores/environment_data_store.rb +33 -0
  114. data/lib/mcollective/util/playbook/data_stores/etcd_data_store.rb +42 -0
  115. data/lib/mcollective/util/playbook/data_stores/file_data_store.rb +106 -0
  116. data/lib/mcollective/util/playbook/data_stores/shell_data_store.rb +103 -0
  117. data/lib/mcollective/util/playbook/inputs.rb +265 -0
  118. data/lib/mcollective/util/playbook/nodes.rb +207 -0
  119. data/lib/mcollective/util/playbook/nodes/mcollective_nodes.rb +86 -0
  120. data/lib/mcollective/util/playbook/nodes/pql_nodes.rb +40 -0
  121. data/lib/mcollective/util/playbook/nodes/shell_nodes.rb +55 -0
  122. data/lib/mcollective/util/playbook/nodes/terraform_nodes.rb +65 -0
  123. data/lib/mcollective/util/playbook/nodes/yaml_nodes.rb +47 -0
  124. data/lib/mcollective/util/playbook/playbook_logger.rb +47 -0
  125. data/lib/mcollective/util/playbook/puppet_logger.rb +51 -0
  126. data/lib/mcollective/util/playbook/report.rb +152 -0
  127. data/lib/mcollective/util/playbook/task_result.rb +55 -0
  128. data/lib/mcollective/util/playbook/tasks.rb +196 -0
  129. data/lib/mcollective/util/playbook/tasks/base.rb +45 -0
  130. data/lib/mcollective/util/playbook/tasks/graphite_event_task.rb +64 -0
  131. data/lib/mcollective/util/playbook/tasks/mcollective_task.rb +356 -0
  132. data/lib/mcollective/util/playbook/tasks/shell_task.rb +93 -0
  133. data/lib/mcollective/util/playbook/tasks/slack_task.rb +105 -0
  134. data/lib/mcollective/util/playbook/tasks/webhook_task.rb +136 -0
  135. data/lib/mcollective/util/playbook/template_util.rb +98 -0
  136. data/lib/mcollective/util/playbook/uses.rb +169 -0
  137. data/lib/mcollective/util/tasks_support.rb +733 -0
  138. data/lib/mcollective/util/tasks_support/cli.rb +260 -0
  139. data/lib/mcollective/util/tasks_support/default_formatter.rb +138 -0
  140. data/lib/mcollective/util/tasks_support/json_formatter.rb +108 -0
  141. data/lib/mcollective/validator.rb +6 -1
  142. data/lib/mcollective/validator/bolt_task_name_validator.ddl +7 -0
  143. data/lib/mcollective/validator/bolt_task_name_validator.rb +11 -0
  144. data/lib/mcollective/validator/length_validator.rb +1 -3
  145. metadata +65 -2
@@ -38,18 +38,22 @@ module MCollective
38
38
  case opt.to_s
39
39
  when "stdout"
40
40
  raise "stdout should support <<" unless val.respond_to?("<<")
41
+
41
42
  @stdout = val
42
43
 
43
44
  when "stderr"
44
45
  raise "stderr should support <<" unless val.respond_to?("<<")
46
+
45
47
  @stderr = val
46
48
 
47
49
  when "stdin"
48
50
  raise "stdin should be a String" unless val.is_a?(String)
51
+
49
52
  @stdin = val
50
53
 
51
54
  when "cwd"
52
55
  raise "Directory #{val} does not exist" unless File.directory?(val)
56
+
53
57
  @cwd = val
54
58
 
55
59
  when "environment"
@@ -62,6 +66,7 @@ module MCollective
62
66
 
63
67
  when "timeout"
64
68
  raise "timeout should be a positive integer or the symbol :on_thread_exit symbol" unless val.eql?(:on_thread_exit) || (val.is_a?(Integer) && val > 0)
69
+
65
70
  @timeout = val
66
71
  end
67
72
  end
@@ -69,10 +74,10 @@ module MCollective
69
74
 
70
75
  # Actually does the systemu call passing in the correct environment, stdout and stderr
71
76
  def runcommand
72
- opts = {"env" => @environment,
77
+ opts = {"env" => @environment,
73
78
  "stdout" => @stdout,
74
79
  "stderr" => @stderr,
75
- "cwd" => @cwd}
80
+ "cwd" => @cwd}
76
81
 
77
82
  opts["stdin"] = @stdin if @stdin
78
83
 
@@ -108,8 +113,8 @@ module MCollective
108
113
  # only wait if the parent thread is dead
109
114
  Process.waitpid(cid) unless thread.alive?
110
115
  end
111
- rescue SystemExit # rubocop:disable Lint/HandleExceptions
112
- rescue Errno::ESRCH # rubocop:disable Lint/HandleExceptions
116
+ rescue SystemExit # rubocop:disable Lint/SuppressedException
117
+ rescue Errno::ESRCH # rubocop:disable Lint/SuppressedException
113
118
  rescue Errno::ECHILD
114
119
  Log.warn("Could not reap process '#{cid}'.")
115
120
  rescue Exception => e # rubocop:disable Lint/RescueException
@@ -0,0 +1,28 @@
1
+ module MCollective
2
+ module Signer
3
+ class Base
4
+ # Register plugins that inherits base
5
+ def self.inherited(klass)
6
+ PluginManager << {:type => "choria_signer_plugin", :class => klass.to_s}
7
+ super
8
+ end
9
+
10
+ def initialize
11
+ @config = Config.instance
12
+ @log = Log
13
+ end
14
+
15
+ # Signs a secure request
16
+ #
17
+ # Generally for local mode this would just use the users own certificate but if you have a
18
+ # remote signer this might use a token to speak to a remote API, by default Choria supports
19
+ # a standard web service for remote signatures
20
+ #
21
+ # @param secure_request [Hash] a choria:secure:request:1 hash
22
+ # @raise [StandardError] when signing fails
23
+ def sign_secure_request!(secure_request)
24
+ raise(NoMethodError, "undefined method `sign_secure_request!' for %s" % inspect)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,185 @@
1
+ require_relative "base"
2
+
3
+ module MCollective
4
+ module Signer
5
+ # This is a Secure Request Signer that allows either local signing of requests using the users
6
+ # own certificate or delegation based signing via a webservice
7
+ #
8
+ # This allows one to integrate the Choria CLI into a centralised authentication, authorization and
9
+ # auditing system
10
+ #
11
+ # Available settings:
12
+ #
13
+ # choria.security.request_signer.plugin - the plugin to use, `choria` for this one - the default.
14
+ # choria.security.request_signer.token_file - a file holding a token like a JWT or similar
15
+ # choria.security.request_signer.token_environment - a ENV key holding a token like a JWT or similar
16
+ # choria.security.request_signer.url - a endpoint that implements the v1 signer protocol
17
+ #
18
+ # The webservice has to support the specification found at https://choria.io/schemas/choria/signer/v1/service.json
19
+ class Choria < Base
20
+ # Retrieves the token from either a local file or the users environment
21
+ #
22
+ # @return [String, nil]
23
+ def token
24
+ file = @config.pluginconf["choria.security.request_signer.token_file"]
25
+ env = @config.pluginconf["choria.security.request_signer.token_environment"]
26
+
27
+ if file
28
+ file = File.expand_path(file)
29
+
30
+ raise("No token found in %s, please authenticate using your configured authentication service" % file) unless File.exist?(file)
31
+
32
+ return File.read(file).chomp
33
+ end
34
+
35
+ raise("could not find token in environment variable %s" % env) unless ENV[env]
36
+
37
+ ENV[env].chomp
38
+ end
39
+
40
+ # Signs the secure request
41
+ #
42
+ # Signing supports either local mode using local certificates or delegating to a remote
43
+ # signer that is written in conformance with the signer specification version 1
44
+ #
45
+ # @param secure_request [Hash] a v1 secure request
46
+ def sign_secure_request!(secure_request)
47
+ return if $choria_unsafe_disable_protocol_security # rubocop:disable Style/GlobalVars
48
+
49
+ if remote_signer?
50
+ remote_sign!(secure_request)
51
+ else
52
+ local_sign!(secure_request)
53
+ end
54
+ end
55
+
56
+ # Determines the callerid for this client
57
+ #
58
+ # When a remote signer is enabled the caller is extracted from the JWT
59
+ # otherwise a choria=user style ID is generated
60
+ #
61
+ # @return [String]
62
+ # @raise [Exception] when the JWT is invalid
63
+ def callerid
64
+ if remote_signer?
65
+ parts = token.split(".")
66
+
67
+ raise("Invalid JWT token") unless parts.length == 3
68
+
69
+ claims = JSON.parse(Base64.decode64(parts[1]))
70
+
71
+ raise("Invalid JWT token") unless claims.include?("callerid")
72
+ raise("Invalid JWT token") unless claims["callerid"].is_a?(String)
73
+ raise("Invalid JWT token") if claims["callerid"].empty?
74
+
75
+ claims["callerid"]
76
+ else
77
+ "choria=%s" % choria.certname
78
+ end
79
+ end
80
+
81
+ # The body that would be submitted to the remote service
82
+ #
83
+ # @param secure_request [Hash] a v1 secure request
84
+ # @return [Hash]
85
+ def sign_request_body(secure_request)
86
+ {
87
+ "token" => token,
88
+ "request" => Base64.encode64(secure_request["message"])
89
+ }
90
+ end
91
+
92
+ # Performs a remote sign operation against a configured web service
93
+ #
94
+ # @param secure_request [Hash] a v1 secure request
95
+ # @raise [StandardError] on signing error
96
+ def remote_sign!(secure_request)
97
+ Log.info("Signing secure request using remote signer %s" % remote_signer_url)
98
+
99
+ uri = remote_signer_url
100
+ post = choria.http_post(uri.request_uri)
101
+ post.body = sign_request_body(secure_request).to_json
102
+ post["Content-type"] = "application/json"
103
+
104
+ http = choria.https(:target => uri.host, :port => uri.port)
105
+ http.use_ssl = false if uri.scheme == "http"
106
+
107
+ # While this might appear alarming it's expected that the clients
108
+ # in this situation will not have any Choria CA issued certificates
109
+ # and so wish to use a remote signer - the certificate management woes
110
+ # being one of the main reasons for centralised AAA.
111
+ #
112
+ # So there is no realistic way to verify these requests especially in the
113
+ # event that these signers run on private IPs and such as would be typical
114
+ # so while we do this big No No of disabling verify here it really is the
115
+ # only thing that make sense.
116
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl?
117
+
118
+ resp = http.request(post)
119
+
120
+ signature = {}
121
+
122
+ if resp.code == "200"
123
+ signature = JSON.parse(resp.body)
124
+ else
125
+ raise("Could not get remote signature: %s: %s" % [resp.code, resp.body])
126
+ end
127
+
128
+ raise("Could not get remote signature: %s" % signature["error"]) if signature["error"]
129
+
130
+ signed_request = JSON.parse(Base64.decode64(signature["secure_request"]))
131
+ signed_request.each do |k, v|
132
+ secure_request[k] = v
133
+ end
134
+ end
135
+
136
+ # Signs using local certificates
137
+ #
138
+ # @param secure_request [Hash] a v1 secure request
139
+ # @raise [StandardError] on signing error
140
+ def local_sign!(secure_request)
141
+ Log.info("Signing secure request using local credentials")
142
+
143
+ secure_request["signature"] = sign(secure_request["message"])
144
+ secure_request["pubcert"] = File.read(client_public_cert).chomp
145
+
146
+ nil
147
+ end
148
+
149
+ # (see Security::Choria#sign)
150
+ def sign(string, id=nil)
151
+ security.sign(string, id)
152
+ end
153
+
154
+ # (see Security::Choria#client_public_cert)
155
+ def client_public_cert
156
+ security.client_public_cert
157
+ end
158
+
159
+ # Determines if a remote signer is configured
160
+ #
161
+ # @return [Boolean]
162
+ def remote_signer?
163
+ !!(remote_signer_url == "" || remote_signer_url)
164
+ end
165
+
166
+ # Determines the remote url to submit standard signing requests to
167
+ #
168
+ # @return [URI, Nil]
169
+ def remote_signer_url
170
+ return nil unless @config.pluginconf["choria.security.request_signer.url"]
171
+ return nil if @config.pluginconf["choria.security.request_signer.url"] == ""
172
+
173
+ URI.parse(@config.pluginconf["choria.security.request_signer.url"])
174
+ end
175
+
176
+ def security
177
+ @security ||= PluginManager["security_plugin"]
178
+ end
179
+
180
+ def choria
181
+ @choria ||= security.choria
182
+ end
183
+ end
184
+ end
185
+ end
@@ -166,7 +166,7 @@ module MCollective
166
166
 
167
167
  # Signs a string using the private key
168
168
  def sign(string, base64=false)
169
- sig = @private_key.sign(OpenSSL::Digest::SHA1.new, string)
169
+ sig = @private_key.sign(OpenSSL::Digest.new("SHA1"), string)
170
170
 
171
171
  base64 ? base64_encode(sig) : sig
172
172
  end
@@ -175,7 +175,7 @@ module MCollective
175
175
  def verify_signature(signature, string, base64=false)
176
176
  signature = base64_decode(signature) if base64
177
177
 
178
- @public_key.verify(OpenSSL::Digest::SHA1.new, signature, string)
178
+ @public_key.verify(OpenSSL::Digest.new("SHA1"), signature, string)
179
179
  end
180
180
 
181
181
  # base 64 encode a string
@@ -196,6 +196,7 @@ module MCollective
196
196
  # The Base 64 character set is A-Z a-z 0-9 + / =
197
197
  # Also allow for whitespace, but raise if we get anything else
198
198
  raise(ArgumentError, "invalid base64") if string !~ /^[A-Za-z0-9+\/=\s]+$/
199
+
199
200
  Base64.decode64(string)
200
201
  end
201
202
 
@@ -248,7 +249,8 @@ module MCollective
248
249
  raise "Could not find key #{key}" unless File.exist?(key)
249
250
  raise "#{type} key file '#{key}' is empty" if File.zero?(key)
250
251
 
251
- if type == :public
252
+ case type
253
+ when :public
252
254
  begin
253
255
  key = OpenSSL::PKey::RSA.new(File.read(key))
254
256
  rescue OpenSSL::PKey::RSAError
@@ -271,9 +273,9 @@ module MCollective
271
273
  # See http://bugs.ruby-lang.org/issues/4550
272
274
  OpenSSL.errors if Util.ruby_version =~ /^1.8/
273
275
 
274
- return key
275
- elsif type == :private
276
- return OpenSSL::PKey::RSA.new(File.read(key), passphrase)
276
+ key
277
+ when :private
278
+ OpenSSL::PKey::RSA.new(File.read(key), passphrase)
277
279
  else
278
280
  raise "Can only load :public or :private keys"
279
281
  end
@@ -10,12 +10,12 @@ module MCollective
10
10
 
11
11
  if agent.is_a?(Regexp)
12
12
  if !Agents.agentlist.grep(agent).empty?
13
- return true
13
+ true
14
14
  else
15
- return false
15
+ false
16
16
  end
17
17
  else
18
- return Agents.agentlist.include?(agent)
18
+ Agents.agentlist.include?(agent)
19
19
  end
20
20
  end
21
21
 
@@ -41,9 +41,10 @@ module MCollective
41
41
 
42
42
  begin
43
43
  File.readlines(cfile).each do |k|
44
- if klass.is_a?(Regexp)
44
+ case klass
45
+ when Regexp
45
46
  return true if k.chomp.match(klass)
46
- elsif k.chomp == klass
47
+ when k.chomp
47
48
  return true
48
49
  end
49
50
  end
@@ -74,11 +75,11 @@ module MCollective
74
75
  fact = fact.clone
75
76
  case fact
76
77
  when Array
77
- return fact.any? { |element| test_fact_value(element, value, operator)}
78
+ fact.any? { |element| test_fact_value(element, value, operator)}
78
79
  when Hash
79
- return fact.keys.any? { |element| test_fact_value(element, value, operator)}
80
+ fact.keys.any? { |element| test_fact_value(element, value, operator)}
80
81
  else
81
- return test_fact_value(fact, value, operator)
82
+ test_fact_value(fact, value, operator)
82
83
  end
83
84
  end
84
85
 
@@ -104,7 +105,7 @@ module MCollective
104
105
  value = Float(value) # rubocop:disable Lint/UselessAssignment
105
106
  end
106
107
 
107
- return true if eval("fact #{operator} value") # rubocop:disable Security/Eval
108
+ return true if eval("fact #{operator} value") # rubocop:disable Security/Eval, Style/EvalWithLocation
108
109
  end
109
110
 
110
111
  false
@@ -118,9 +119,10 @@ module MCollective
118
119
  def self.has_identity?(identity)
119
120
  identity = Regexp.new(identity.gsub("\/", "")) if identity.start_with?("/")
120
121
 
121
- if identity.is_a?(Regexp)
122
+ case identity
123
+ when Regexp
122
124
  return Config.instance.identity.match(identity)
123
- elsif Config.instance.identity == identity
125
+ when Config.instance.identity
124
126
  return true
125
127
  end
126
128
 
@@ -135,9 +137,9 @@ module MCollective
135
137
  # Creates an empty filter
136
138
  def self.empty_filter
137
139
  {
138
- "fact" => [],
140
+ "fact" => [],
139
141
  "cf_class" => [],
140
- "agent" => [],
142
+ "agent" => [],
141
143
  "identity" => [],
142
144
  "compound" => []
143
145
  }
@@ -146,7 +148,7 @@ module MCollective
146
148
  # Returns the PuppetLabs mcollective path for windows
147
149
  def self.windows_prefix
148
150
  require "win32/dir"
149
- File.join(Dir::COMMON_APPDATA, "PuppetLabs", "mcollective")
151
+ File.join(Dir::COMMON_APPDATA, "PuppetLabs", "choria")
150
152
  end
151
153
 
152
154
  def self.choria_windows_prefix
@@ -161,7 +163,7 @@ module MCollective
161
163
  # File.expand_path will raise if HOME isn't set, catch it
162
164
  user_path = File.expand_path("~/.mcollective")
163
165
  config_paths << user_path
164
- rescue Exception # rubocop:disable Lint/RescueException, Lint/HandleExceptions
166
+ rescue Exception # rubocop:disable Lint/RescueException, Lint/SuppressedException
165
167
  end
166
168
 
167
169
  if windows?
@@ -169,6 +171,7 @@ module MCollective
169
171
  else
170
172
  config_paths << "/etc/puppetlabs/mcollective/client.cfg"
171
173
  config_paths << "/etc/mcollective/client.cfg"
174
+ config_paths << "/usr/local/etc/mcollective/client.cfg"
172
175
  end
173
176
 
174
177
  config_paths
@@ -181,29 +184,32 @@ module MCollective
181
184
  # File.expand_path will raise if HOME isn't set, catch it
182
185
  user_path = File.expand_path("~/.choriarc")
183
186
  config_paths << user_path
184
- rescue Exception # rubocop:disable Lint/RescueException, Lint/HandleExceptions
187
+ rescue Exception # rubocop:disable Lint/RescueException, Lint/SuppressedException
185
188
  end
186
189
 
187
190
  if windows?
188
191
  config_paths << File.join(choria_windows_prefix, "etc", "client.conf")
189
192
  else
190
193
  config_paths << "/etc/choria/client.conf"
194
+ config_paths << "/usr/local/etc/choria/client.conf"
191
195
  end
192
196
 
193
197
  config_paths
194
198
  end
195
199
 
196
- # Picks the default user config file, pririties are first Choria ones then old MCollective ones
200
+ # Picks the default user config file, priorities are first Choria ones then old MCollective ones
197
201
  #
198
202
  # In roughly this order, first to exist is used:
199
203
  #
200
204
  # - ~/.choriarc
201
205
  # - APPData/ChoriaIO/choria/etc/client.conf on windows
202
- # - /etc/choria/client.conf on unix
206
+ # - /etc/choria/client.conf then
207
+ # - /usr/local/etc/choria/client.conf on unix
203
208
  # - ~/.mcollective
204
209
  # - APPData/PuppetLabs/mcollective/etc/client.cfg on windows
205
210
  # - /etc/puppetlabs/mcollective/client.cfg
206
211
  # - /etc/mcollective/client.cfg
212
+ # - /usr/local/etc/mcollective/client.cfg
207
213
  def self.config_file_for_user
208
214
  config_paths = choria_config_paths_for_user + mcollective_config_paths_for_user
209
215
  found = config_paths.find_index { |file| File.readable?(file) } || 0
@@ -213,14 +219,14 @@ module MCollective
213
219
  # Creates a standard options hash
214
220
  def self.default_options
215
221
  {
216
- :verbose => false,
217
- :disctimeout => nil,
218
- :timeout => 5,
219
- :config => config_file_for_user,
220
- :collective => nil,
221
- :discovery_method => nil,
222
+ :verbose => false,
223
+ :disctimeout => nil,
224
+ :timeout => 5,
225
+ :config => config_file_for_user,
226
+ :collective => nil,
227
+ :discovery_method => nil,
222
228
  :discovery_options => Config.instance.default_discovery_options,
223
- :filter => empty_filter
229
+ :filter => empty_filter
224
230
  }
225
231
  end
226
232
 
@@ -269,15 +275,16 @@ module MCollective
269
275
 
270
276
  # Parse a fact filter string like foo=bar into the tuple hash thats needed
271
277
  def self.parse_fact_string(fact)
272
- if fact =~ /^([^ ]+?)[ ]*=>[ ]*(.+)/
278
+ case fact
279
+ when /^([^ ]+?) *=> *(.+)/
273
280
  {:fact => $1, :value => $2, :operator => ">="}
274
- elsif fact =~ /^([^ ]+?)[ ]*=<[ ]*(.+)/
281
+ when /^([^ ]+?) *=< *(.+)/
275
282
  {:fact => $1, :value => $2, :operator => "<="}
276
- elsif fact =~ /^([^ ]+?)[ ]*(<=|>=|<|>|!=|==|=~)[ ]*(.+)/
283
+ when /^([^ ]+?) *(<=|>=|<|>|!=|==|=~) *(.+)/
277
284
  {:fact => $1, :value => $3, :operator => $2}
278
- elsif fact =~ /^(.+?)[ ]*=[ ]*\/(.+)\/$/
285
+ when /^(.+?) *= *\/(.+)\/$/
279
286
  {:fact => $1, :value => "/#{$2}/", :operator => "=~"}
280
- elsif fact =~ /^([^= ]+?)[ ]*=[ ]*(.+)/
287
+ when /^([^= ]+?) *= *(.+)/
281
288
  {:fact => $1, :value => $2, :operator => "=="}
282
289
  else
283
290
  raise "Could not parse fact #{fact} it does not appear to be in a valid format"
@@ -322,9 +329,9 @@ module MCollective
322
329
  }
323
330
 
324
331
  if colorize
325
- return colors[code] || ""
332
+ colors[code] || ""
326
333
  else
327
- return ""
334
+ ""
328
335
  end
329
336
  end
330
337
 
@@ -375,9 +382,7 @@ module MCollective
375
382
  text.each_with_index do |line, i|
376
383
  whitespace = 0
377
384
 
378
- while whitespace < line.length && line[whitespace].chr == " "
379
- whitespace += 1
380
- end
385
+ whitespace += 1 while whitespace < line.length && line[whitespace].chr == " "
381
386
 
382
387
  # If the current line is empty, indent it so that a snippet
383
388
  # from the previous line is aligned correctly.
@@ -431,21 +436,21 @@ module MCollective
431
436
  #
432
437
  # Returns [0, 0] if it can't figure it out or if you're
433
438
  # not running on a tty
434
- def self.terminal_dimensions(stdout=STDOUT, environment=ENV)
439
+ def self.terminal_dimensions(stdout=$stdout, environment=ENV)
435
440
  return [0, 0] unless stdout.tty?
436
441
 
437
442
  return [80, 40] if Util.windows?
438
443
 
439
444
  if environment["COLUMNS"] && environment["LINES"]
440
- return [environment["COLUMNS"].to_i, environment["LINES"].to_i]
445
+ [environment["COLUMNS"].to_i, environment["LINES"].to_i]
441
446
 
442
447
  elsif environment["TERM"] && command_in_path?("tput")
443
- return [`tput cols`.to_i, `tput lines`.to_i]
448
+ [`tput cols`.to_i, `tput lines`.to_i]
444
449
 
445
450
  elsif command_in_path?("stty")
446
- return `stty size`.scan(/\d+/).map(&:to_i)
451
+ `stty size`.scan(/\d+/).map(&:to_i)
447
452
  else
448
- return [0, 0]
453
+ [0, 0]
449
454
  end
450
455
  rescue
451
456
  [0, 0]
@@ -486,6 +491,7 @@ module MCollective
486
491
  elsif b == "." then return 1
487
492
  elsif a =~ /^\d+$/ && b =~ /^\d+$/
488
493
  return a.to_s.upcase <=> b.to_s.upcase if a =~ /^0/ || b =~ /^0/
494
+
489
495
  return a.to_i <=> b.to_i
490
496
  else
491
497
  return a.upcase <=> b.upcase
@@ -512,10 +518,11 @@ module MCollective
512
518
  # Any other value will return FalseClass
513
519
  def self.str_to_bool(val)
514
520
  clean_val = val.to_s.strip
515
- if clean_val =~ /^(1|yes|true|y|t)$/i
516
- return true
517
- elsif clean_val =~ /^(0|no|false|n|f)$/i
518
- return false
521
+ case clean_val
522
+ when /^(1|yes|true|y|t)$/i
523
+ true
524
+ when /^(0|no|false|n|f)$/i
525
+ false
519
526
  else
520
527
  raise("Cannot convert string value '#{clean_val}' into a boolean.")
521
528
  end
@@ -527,8 +534,7 @@ module MCollective
527
534
  template_path = File.join(config_dir, template_file)
528
535
  return template_path if File.exist?(template_path)
529
536
 
530
- template_path = File.join("/etc/mcollective", template_file)
531
- template_path
537
+ File.join("/etc/mcollective", template_file)
532
538
  end
533
539
 
534
540
  # subscribe to the direct addressing queue
@@ -559,6 +565,7 @@ module MCollective
559
565
 
560
566
  while char = Win32API.new("crtdll", "_getch", [], "I").Call
561
567
  break if [10, 13].include?(char) # return or newline
568
+
562
569
  if [127, 8].include?(char) # backspace and delete
563
570
  input.slice!(-1, 1) unless input.empty?
564
571
  else
@@ -570,17 +577,13 @@ module MCollective
570
577
  end
571
578
 
572
579
  def self.get_hidden_input_on_unix # rubocop:disable Naming/AccessorMethodName
573
- unless $stdin.tty?
574
- raise "Could not hook to stdin to hide input. If using SSH, try using -t flag while connecting to server."
575
- end
576
- unless system "stty -echo -icanon"
577
- raise "Could not hide input using stty command."
578
- end
580
+ raise "Could not hook to stdin to hide input. If using SSH, try using -t flag while connecting to server." unless $stdin.tty?
581
+ raise "Could not hide input using stty command." unless system "stty -echo -icanon"
582
+
579
583
  input = $stdin.gets
580
584
  ensure
581
- unless system "stty echo icanon"
582
- raise "Could not enable echoing of input. Try executing `stty echo icanon` to debug."
583
- end
585
+ raise "Could not enable echoing of input. Try executing `stty echo icanon` to debug." unless system "stty echo icanon"
586
+
584
587
  input
585
588
  end
586
589