choria-mcorpc-support 2.20.8 → 2.23.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/mcollective.rb +1 -1
- data/lib/mcollective/agent/bolt_tasks.ddl +235 -0
- data/lib/mcollective/agent/bolt_tasks.json +347 -0
- data/lib/mcollective/agent/bolt_tasks.rb +176 -0
- data/lib/mcollective/agent/choria_util.ddl +152 -0
- data/lib/mcollective/agent/choria_util.json +244 -0
- data/lib/mcollective/agent/rpcutil.ddl +7 -3
- data/lib/mcollective/agent/rpcutil.json +333 -0
- data/lib/mcollective/agent/scout.ddl +169 -0
- data/lib/mcollective/agent/scout.json +224 -0
- data/lib/mcollective/agents.rb +7 -6
- data/lib/mcollective/aggregate.rb +4 -4
- data/lib/mcollective/aggregate/average.rb +2 -2
- data/lib/mcollective/aggregate/base.rb +2 -2
- data/lib/mcollective/aggregate/result.rb +3 -3
- data/lib/mcollective/aggregate/result/collection_result.rb +2 -2
- data/lib/mcollective/aggregate/result/numeric_result.rb +2 -2
- data/lib/mcollective/aggregate/sum.rb +2 -2
- data/lib/mcollective/aggregate/summary.rb +3 -4
- data/lib/mcollective/application.rb +57 -21
- data/lib/mcollective/application/choria.rb +249 -0
- data/lib/mcollective/application/completion.rb +6 -6
- data/lib/mcollective/application/describe_filter.rb +20 -20
- data/lib/mcollective/application/facts.rb +19 -11
- data/lib/mcollective/application/federation.rb +239 -0
- data/lib/mcollective/application/find.rb +4 -4
- data/lib/mcollective/application/help.rb +3 -3
- data/lib/mcollective/application/inventory.rb +3 -341
- data/lib/mcollective/application/ping.rb +3 -77
- data/lib/mcollective/application/playbook.rb +207 -0
- data/lib/mcollective/application/plugin.rb +106 -106
- data/lib/mcollective/application/rpc.rb +3 -108
- data/lib/mcollective/application/tasks.rb +416 -0
- data/lib/mcollective/applications.rb +11 -10
- data/lib/mcollective/audit/choria.rb +33 -0
- data/lib/mcollective/cache.rb +2 -4
- data/lib/mcollective/client.rb +11 -10
- data/lib/mcollective/config.rb +21 -34
- data/lib/mcollective/connector/base.rb +2 -1
- data/lib/mcollective/connector/nats.ddl +9 -0
- data/lib/mcollective/connector/nats.rb +450 -0
- data/lib/mcollective/data.rb +8 -3
- data/lib/mcollective/data/agent_data.rb +1 -1
- data/lib/mcollective/data/base.rb +6 -5
- data/lib/mcollective/data/bolt_task_data.ddl +90 -0
- data/lib/mcollective/data/bolt_task_data.rb +32 -0
- data/lib/mcollective/data/collective_data.rb +1 -1
- data/lib/mcollective/data/fact_data.rb +6 -6
- data/lib/mcollective/data/fstat_data.rb +2 -4
- data/lib/mcollective/data/result.rb +7 -2
- data/lib/mcollective/ddl/agentddl.rb +5 -17
- data/lib/mcollective/ddl/base.rb +11 -14
- data/lib/mcollective/discovery.rb +12 -26
- data/lib/mcollective/discovery/choria.ddl +11 -0
- data/lib/mcollective/discovery/choria.rb +223 -0
- data/lib/mcollective/discovery/flatfile.rb +7 -8
- data/lib/mcollective/discovery/mc.rb +2 -2
- data/lib/mcollective/discovery/stdin.rb +17 -18
- data/lib/mcollective/exceptions.rb +13 -0
- data/lib/mcollective/facts/base.rb +9 -9
- data/lib/mcollective/facts/yaml_facts.rb +12 -12
- data/lib/mcollective/generators.rb +3 -3
- data/lib/mcollective/generators/agent_generator.rb +3 -4
- data/lib/mcollective/generators/base.rb +14 -15
- data/lib/mcollective/generators/data_generator.rb +5 -6
- data/lib/mcollective/log.rb +2 -2
- data/lib/mcollective/logger/base.rb +3 -2
- data/lib/mcollective/logger/console_logger.rb +10 -10
- data/lib/mcollective/logger/file_logger.rb +7 -7
- data/lib/mcollective/logger/syslog_logger.rb +11 -15
- data/lib/mcollective/matcher.rb +14 -14
- data/lib/mcollective/matcher/parser.rb +31 -41
- data/lib/mcollective/matcher/scanner.rb +69 -74
- data/lib/mcollective/message.rb +10 -17
- data/lib/mcollective/monkey_patches.rb +2 -4
- data/lib/mcollective/optionparser.rb +1 -0
- data/lib/mcollective/pluginmanager.rb +3 -5
- data/lib/mcollective/pluginpackager.rb +1 -3
- data/lib/mcollective/pluginpackager/agent_definition.rb +10 -11
- data/lib/mcollective/pluginpackager/forge_packager.rb +7 -9
- data/lib/mcollective/pluginpackager/standard_definition.rb +1 -2
- data/lib/mcollective/registration/base.rb +18 -16
- data/lib/mcollective/rpc.rb +2 -4
- data/lib/mcollective/rpc/actionrunner.rb +16 -18
- data/lib/mcollective/rpc/agent.rb +26 -43
- data/lib/mcollective/rpc/audit.rb +1 -0
- data/lib/mcollective/rpc/client.rb +67 -85
- data/lib/mcollective/rpc/helpers.rb +55 -62
- data/lib/mcollective/rpc/progress.rb +2 -2
- data/lib/mcollective/rpc/reply.rb +17 -19
- data/lib/mcollective/rpc/request.rb +7 -5
- data/lib/mcollective/rpc/result.rb +6 -8
- data/lib/mcollective/rpc/stats.rb +49 -58
- data/lib/mcollective/security/base.rb +29 -36
- data/lib/mcollective/security/choria.rb +765 -0
- data/lib/mcollective/shell.rb +9 -4
- data/lib/mcollective/signer/base.rb +28 -0
- data/lib/mcollective/signer/choria.rb +185 -0
- data/lib/mcollective/ssl.rb +8 -6
- data/lib/mcollective/util.rb +58 -55
- data/lib/mcollective/util/bolt_support.rb +176 -0
- data/lib/mcollective/util/bolt_support/plan_runner.rb +167 -0
- data/lib/mcollective/util/bolt_support/task_result.rb +94 -0
- data/lib/mcollective/util/bolt_support/task_results.rb +128 -0
- data/lib/mcollective/util/choria.rb +1103 -0
- data/lib/mcollective/util/indifferent_hash.rb +12 -0
- data/lib/mcollective/util/natswrapper.rb +242 -0
- data/lib/mcollective/util/playbook.rb +435 -0
- data/lib/mcollective/util/playbook/data_stores.rb +201 -0
- data/lib/mcollective/util/playbook/data_stores/base.rb +99 -0
- data/lib/mcollective/util/playbook/data_stores/consul_data_store.rb +88 -0
- data/lib/mcollective/util/playbook/data_stores/environment_data_store.rb +33 -0
- data/lib/mcollective/util/playbook/data_stores/etcd_data_store.rb +42 -0
- data/lib/mcollective/util/playbook/data_stores/file_data_store.rb +106 -0
- data/lib/mcollective/util/playbook/data_stores/shell_data_store.rb +103 -0
- data/lib/mcollective/util/playbook/inputs.rb +265 -0
- data/lib/mcollective/util/playbook/nodes.rb +207 -0
- data/lib/mcollective/util/playbook/nodes/mcollective_nodes.rb +86 -0
- data/lib/mcollective/util/playbook/nodes/pql_nodes.rb +40 -0
- data/lib/mcollective/util/playbook/nodes/shell_nodes.rb +55 -0
- data/lib/mcollective/util/playbook/nodes/terraform_nodes.rb +65 -0
- data/lib/mcollective/util/playbook/nodes/yaml_nodes.rb +47 -0
- data/lib/mcollective/util/playbook/playbook_logger.rb +47 -0
- data/lib/mcollective/util/playbook/puppet_logger.rb +51 -0
- data/lib/mcollective/util/playbook/report.rb +152 -0
- data/lib/mcollective/util/playbook/task_result.rb +55 -0
- data/lib/mcollective/util/playbook/tasks.rb +196 -0
- data/lib/mcollective/util/playbook/tasks/base.rb +45 -0
- data/lib/mcollective/util/playbook/tasks/graphite_event_task.rb +64 -0
- data/lib/mcollective/util/playbook/tasks/mcollective_task.rb +356 -0
- data/lib/mcollective/util/playbook/tasks/shell_task.rb +93 -0
- data/lib/mcollective/util/playbook/tasks/slack_task.rb +105 -0
- data/lib/mcollective/util/playbook/tasks/webhook_task.rb +136 -0
- data/lib/mcollective/util/playbook/template_util.rb +98 -0
- data/lib/mcollective/util/playbook/uses.rb +169 -0
- data/lib/mcollective/util/tasks_support.rb +733 -0
- data/lib/mcollective/util/tasks_support/cli.rb +260 -0
- data/lib/mcollective/util/tasks_support/default_formatter.rb +138 -0
- data/lib/mcollective/util/tasks_support/json_formatter.rb +108 -0
- data/lib/mcollective/validator.rb +8 -3
- data/lib/mcollective/validator/bolt_task_name_validator.ddl +7 -0
- data/lib/mcollective/validator/bolt_task_name_validator.rb +11 -0
- data/lib/mcollective/validator/length_validator.rb +1 -3
- data/lib/mcollective/validator/typecheck_validator.rb +4 -0
- metadata +67 -4
data/lib/mcollective/shell.rb
CHANGED
@@ -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"
|
77
|
+
opts = {"env" => @environment,
|
73
78
|
"stdout" => @stdout,
|
74
79
|
"stderr" => @stderr,
|
75
|
-
"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/
|
112
|
-
rescue Errno::ESRCH # rubocop:disable Lint/
|
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
|
data/lib/mcollective/ssl.rb
CHANGED
@@ -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
|
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
|
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
|
-
|
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
|
-
|
275
|
-
|
276
|
-
|
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
|
data/lib/mcollective/util.rb
CHANGED
@@ -10,12 +10,12 @@ module MCollective
|
|
10
10
|
|
11
11
|
if agent.is_a?(Regexp)
|
12
12
|
if !Agents.agentlist.grep(agent).empty?
|
13
|
-
|
13
|
+
true
|
14
14
|
else
|
15
|
-
|
15
|
+
false
|
16
16
|
end
|
17
17
|
else
|
18
|
-
|
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
|
-
|
44
|
+
case klass
|
45
|
+
when Regexp
|
45
46
|
return true if k.chomp.match(klass)
|
46
|
-
|
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
|
-
|
78
|
+
fact.any? { |element| test_fact_value(element, value, operator)}
|
78
79
|
when Hash
|
79
|
-
|
80
|
+
fact.keys.any? { |element| test_fact_value(element, value, operator)}
|
80
81
|
else
|
81
|
-
|
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
|
-
|
122
|
+
case identity
|
123
|
+
when Regexp
|
122
124
|
return Config.instance.identity.match(identity)
|
123
|
-
|
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", "
|
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/
|
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/
|
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,
|
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
|
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
|
217
|
-
:disctimeout
|
218
|
-
:timeout
|
219
|
-
:config
|
220
|
-
:collective
|
221
|
-
:discovery_method
|
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
|
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
|
-
|
278
|
+
case fact
|
279
|
+
when /^([^ ]+?) *=> *(.+)/
|
273
280
|
{:fact => $1, :value => $2, :operator => ">="}
|
274
|
-
|
281
|
+
when /^([^ ]+?) *=< *(.+)/
|
275
282
|
{:fact => $1, :value => $2, :operator => "<="}
|
276
|
-
|
283
|
+
when /^([^ ]+?) *(<=|>=|<|>|!=|==|=~) *(.+)/
|
277
284
|
{:fact => $1, :value => $3, :operator => $2}
|
278
|
-
|
285
|
+
when /^(.+?) *= *\/(.+)\/$/
|
279
286
|
{:fact => $1, :value => "/#{$2}/", :operator => "=~"}
|
280
|
-
|
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
|
-
|
332
|
+
colors[code] || ""
|
326
333
|
else
|
327
|
-
|
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
|
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
|
-
|
445
|
+
[environment["COLUMNS"].to_i, environment["LINES"].to_i]
|
441
446
|
|
442
447
|
elsif environment["TERM"] && command_in_path?("tput")
|
443
|
-
|
448
|
+
[`tput cols`.to_i, `tput lines`.to_i]
|
444
449
|
|
445
450
|
elsif command_in_path?("stty")
|
446
|
-
|
451
|
+
`stty size`.scan(/\d+/).map(&:to_i)
|
447
452
|
else
|
448
|
-
|
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
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
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
|
-
|
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
|
-
|
575
|
-
|
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
|
-
|
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
|
|