mcollective-client 2.10.6 → 2.11.0
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/lib/mcollective.rb +1 -1
- data/lib/mcollective/audit/logfile.rb +1 -1
- data/lib/mcollective/client.rb +7 -6
- data/lib/mcollective/ddl/agentddl.rb +46 -5
- data/lib/mcollective/message.rb +12 -10
- data/lib/mcollective/monkey_patches.rb +3 -1
- data/lib/mcollective/rpc/client.rb +7 -2
- data/lib/mcollective/rpc/helpers.rb +0 -1
- data/lib/mcollective/rpc/request.rb +34 -10
- data/lib/mcollective/rpc/result.rb +50 -9
- data/spec/unit/mcollective/audit/logfile_spec.rb +1 -1
- data/spec/unit/mcollective/ddl/agentddl_spec.rb +82 -18
- data/spec/unit/mcollective/message_spec.rb +13 -5
- data/spec/unit/mcollective/rpc/client_spec.rb +59 -0
- data/spec/unit/mcollective/rpc/request_spec.rb +36 -0
- data/spec/unit/mcollective/rpc/result_spec.rb +38 -10
- data/spec/unit/mcollective/runner_spec.rb +1 -1
- metadata +2 -2
data/lib/mcollective.rb
CHANGED
@@ -10,7 +10,7 @@ module MCollective
|
|
10
10
|
require 'pp'
|
11
11
|
|
12
12
|
def audit_request(request, connection)
|
13
|
-
logfile = Config.instance.pluginconf["rpcaudit.logfile"] || "/var/log/mcollective-audit.log"
|
13
|
+
logfile = Config.instance.pluginconf["rpcaudit.logfile"] || "/var/log/puppetlabs/mcollective/mcollective-audit.log"
|
14
14
|
|
15
15
|
now = Time.now
|
16
16
|
# Already told timezone to be in UTC so we don't look it up again
|
data/lib/mcollective/client.rb
CHANGED
@@ -66,10 +66,7 @@ module MCollective
|
|
66
66
|
# responses and doesn't execute any passed in code blocks for responses
|
67
67
|
def sendreq(msg, agent, filter = {})
|
68
68
|
request = createreq(msg, agent, filter)
|
69
|
-
|
70
|
-
Log.debug("Sending request #{request.requestid} to the #{request.agent} agent with ttl #{request.ttl} in collective #{request.collective}")
|
71
|
-
|
72
|
-
request.publish
|
69
|
+
publish(request)
|
73
70
|
request.requestid
|
74
71
|
end
|
75
72
|
|
@@ -224,14 +221,18 @@ module MCollective
|
|
224
221
|
Log.debug("Starting publishing with publish timeout of #{publish_timeout}")
|
225
222
|
begin
|
226
223
|
Timeout.timeout(publish_timeout) do
|
227
|
-
|
228
|
-
request.publish
|
224
|
+
publish(request)
|
229
225
|
end
|
230
226
|
rescue Timeout::Error => e
|
231
227
|
Log.warn("Could not publish all messages. Publishing timed out.")
|
232
228
|
end
|
233
229
|
end
|
234
230
|
|
231
|
+
def publish(request)
|
232
|
+
Log.info("Sending request #{request.requestid} for agent '#{request.agent}' with ttl #{request.ttl} in collective '#{request.collective}'")
|
233
|
+
request.publish
|
234
|
+
end
|
235
|
+
|
235
236
|
# Starts the response receiver routine
|
236
237
|
# Expected to return the amount of received responses.
|
237
238
|
def start_receiver(requestid, waitfor, timeout, &block)
|
@@ -166,13 +166,53 @@ module MCollective
|
|
166
166
|
return unless input
|
167
167
|
|
168
168
|
input.keys.each do |key|
|
169
|
-
if
|
169
|
+
if key.is_a?(Symbol) && arguments.include?(key.to_s) && !input.include?(key.to_s)
|
170
|
+
compat_arg = key.to_s
|
171
|
+
else
|
172
|
+
compat_arg = key
|
173
|
+
end
|
174
|
+
|
175
|
+
if !arguments.include?(compat_arg) && !input[key][:default].nil? && !input[key][:optional]
|
170
176
|
Log.debug("Setting default value for input '%s' to '%s'" % [key, input[key][:default]])
|
171
|
-
arguments[
|
177
|
+
arguments[compat_arg] = input[key][:default]
|
172
178
|
end
|
173
179
|
end
|
174
180
|
end
|
175
181
|
|
182
|
+
# Creates a new set of arguments with string arguments mapped to symbol ones
|
183
|
+
#
|
184
|
+
# This is to assist with moving to a JSON pure world where requests might come
|
185
|
+
# in from REST or other languages, those languages and indeed JSON itself does
|
186
|
+
# not support symbols.
|
187
|
+
#
|
188
|
+
# It ensures a backward compatible mode where for rpcutil both of these requests
|
189
|
+
# are equivelant
|
190
|
+
#
|
191
|
+
# c.get_fact(:fact => "cluster")
|
192
|
+
# c.get_fact("fact" => "cluster")
|
193
|
+
#
|
194
|
+
# The case where both :fact and "fact" is in the DDL cannot be handled correctly
|
195
|
+
# and this code will assume the caller means "fact" in that case. There's no
|
196
|
+
# way to represent such a request in JSON and just in general sounds like a bad
|
197
|
+
# idea, so a warning is logged which would in default client configuration appear
|
198
|
+
# on the clients display
|
199
|
+
def symbolize_basic_input_arguments(input, arguments)
|
200
|
+
warned = false
|
201
|
+
|
202
|
+
Hash[arguments.map do |key, value|
|
203
|
+
if input.include?(key.intern) && input.include?(key.to_s) && !warned
|
204
|
+
Log.warn("String and Symbol versions of input %s found in the DDL for %s, ensure your DDL keys are unique." % [key, @pluginname])
|
205
|
+
warned = true
|
206
|
+
end
|
207
|
+
|
208
|
+
if key.is_a?(String) && input.include?(key.intern) && !input.include?(key)
|
209
|
+
[key.intern, value]
|
210
|
+
else
|
211
|
+
[key, value]
|
212
|
+
end
|
213
|
+
end]
|
214
|
+
end
|
215
|
+
|
176
216
|
# Helper to use the DDL to figure out if the remote call to an
|
177
217
|
# agent should be allowed based on action name and inputs.
|
178
218
|
def validate_rpc_request(action, arguments)
|
@@ -182,16 +222,17 @@ module MCollective
|
|
182
222
|
end
|
183
223
|
|
184
224
|
input = action_interface(action)[:input] || {}
|
225
|
+
compatible_args = symbolize_basic_input_arguments(input, arguments)
|
185
226
|
|
186
227
|
input.keys.each do |key|
|
187
228
|
unless input[key][:optional]
|
188
|
-
unless
|
229
|
+
unless compatible_args.include?(key)
|
189
230
|
raise DDLValidationError, "Action #{action} needs a #{key} argument"
|
190
231
|
end
|
191
232
|
end
|
192
233
|
|
193
|
-
if
|
194
|
-
validate_input_argument(input, key,
|
234
|
+
if compatible_args.include?(key)
|
235
|
+
validate_input_argument(input, key, compatible_args[key])
|
195
236
|
end
|
196
237
|
end
|
197
238
|
|
data/lib/mcollective/message.rb
CHANGED
@@ -130,6 +130,14 @@ module MCollective
|
|
130
130
|
@base64
|
131
131
|
end
|
132
132
|
|
133
|
+
def description
|
134
|
+
cid = ""
|
135
|
+
cid += payload[:callerid] + "@" if payload.include?(:callerid)
|
136
|
+
cid += payload[:senderid]
|
137
|
+
|
138
|
+
"#{requestid} for agent '#{agent}' in collective '#{collective}' from #{cid}"
|
139
|
+
end
|
140
|
+
|
133
141
|
def encode!
|
134
142
|
case type
|
135
143
|
when :reply
|
@@ -207,17 +215,11 @@ module MCollective
|
|
207
215
|
msg_age = Time.now.utc.to_i - msgtime
|
208
216
|
|
209
217
|
if msg_age > ttl
|
210
|
-
|
211
|
-
|
212
|
-
cid += payload[:senderid]
|
213
|
-
|
214
|
-
if msg_age > ttl
|
215
|
-
PluginManager["global_stats"].ttlexpired
|
216
|
-
|
217
|
-
raise(MsgTTLExpired, "message #{requestid} from #{cid} created at #{msgtime} is #{msg_age} seconds old, TTL is #{ttl}. Rejecting message.")
|
218
|
-
end
|
218
|
+
PluginManager["global_stats"].ttlexpired
|
219
|
+
raise(MsgTTLExpired, "Message #{description} created at #{msgtime} is #{msg_age} seconds old, TTL is #{ttl}. Rejecting message.")
|
219
220
|
end
|
220
|
-
|
221
|
+
|
222
|
+
raise(NotTargettedAtUs, "Message #{description} does not pass filters. Ignoring message.") unless PluginManager["security_plugin"].validate_filter?(payload[:filter])
|
221
223
|
|
222
224
|
@validated = true
|
223
225
|
end
|
@@ -134,7 +134,9 @@ class OpenSSL::SSL::SSLContext
|
|
134
134
|
DEFAULT_PARAMS[:options] |= OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS
|
135
135
|
end
|
136
136
|
|
137
|
-
DEFAULT_PARAMS[:ciphers]
|
137
|
+
if DEFAULT_PARAMS[:ciphers]
|
138
|
+
DEFAULT_PARAMS[:ciphers] << ':!SSLv2'
|
139
|
+
end
|
138
140
|
|
139
141
|
alias __mcollective_original_initialize initialize
|
140
142
|
private :__mcollective_original_initialize
|
@@ -739,8 +739,13 @@ module MCollective
|
|
739
739
|
end
|
740
740
|
|
741
741
|
def rpc_result_from_reply(agent, action, reply)
|
742
|
-
|
743
|
-
|
742
|
+
senderid = reply.include?("senderid") ? reply["senderid"] : reply[:senderid]
|
743
|
+
body = reply.include?("body") ? reply["body"] : reply[:body]
|
744
|
+
s_code = body.include?("statuscode") ? body["statuscode"] : body[:statuscode]
|
745
|
+
s_msg = body.include?("statusmsg") ? body["statusmsg"] : body[:statusmsg]
|
746
|
+
data = body.include?("data") ? body["data"] : body[:data]
|
747
|
+
|
748
|
+
Result.new(agent, action, {:sender => senderid, :statuscode => s_code, :statusmsg => s_msg, :data => data})
|
744
749
|
end
|
745
750
|
|
746
751
|
# for requests that do not care for results just
|
@@ -6,45 +6,69 @@ module MCollective
|
|
6
6
|
|
7
7
|
def initialize(msg, ddl)
|
8
8
|
@time = msg[:msgtime]
|
9
|
-
@action = msg[:body][:action]
|
10
|
-
@data = msg[:body][:data]
|
9
|
+
@action = msg[:body][:action] || msg[:body]["action"]
|
10
|
+
@data = msg[:body][:data] || msg[:body]["data"]
|
11
11
|
@sender = msg[:senderid]
|
12
|
-
@agent = msg[:body][:agent]
|
12
|
+
@agent = msg[:body][:agent] || msg[:body]["agent"]
|
13
13
|
@uniqid = msg[:requestid]
|
14
14
|
@caller = msg[:callerid] || "unknown"
|
15
15
|
@ddl = ddl
|
16
16
|
end
|
17
17
|
|
18
|
+
# In a scenario where a request came from a JSON pure medium like a REST
|
19
|
+
# service or other language client DDL::AgentDDL#validate_rpc_request will
|
20
|
+
# check "package" against the intput :package should the input "package" not
|
21
|
+
# also be known
|
22
|
+
#
|
23
|
+
# Thus once the request is built it will also have "package" and not :package
|
24
|
+
# data, so we need to fetch the correct key out of the hash.
|
25
|
+
def compatible_key(key)
|
26
|
+
return key if data.include?(key)
|
27
|
+
|
28
|
+
if ddl
|
29
|
+
input = ddl.action_interface(action)[:input]
|
30
|
+
|
31
|
+
# if :package is requested and the DDL also declares "package" we cant tell it to fetch
|
32
|
+
# "package", hence the check against the input here
|
33
|
+
return key.to_s if key.is_a?(Symbol) && !input.include?(key.to_s) && data.include?(key.to_s)
|
34
|
+
end
|
35
|
+
|
36
|
+
key
|
37
|
+
end
|
38
|
+
|
18
39
|
# If data is a hash, quick helper to get access to it's include? method
|
19
40
|
# else returns false
|
20
41
|
def include?(key)
|
21
42
|
return false unless @data.is_a?(Hash)
|
22
|
-
|
43
|
+
|
44
|
+
@data.include?(compatible_key(key))
|
23
45
|
end
|
24
46
|
|
25
47
|
# If no :process_results is specified always respond else respond
|
26
48
|
# based on the supplied property
|
27
49
|
def should_respond?
|
50
|
+
return false unless @data.is_a?(Hash)
|
28
51
|
return @data[:process_results] if @data.include?(:process_results)
|
52
|
+
return @data["process_results"] if @data.include?("process_results")
|
29
53
|
|
30
|
-
|
54
|
+
true
|
31
55
|
end
|
32
56
|
|
33
57
|
# If data is a hash, gives easy access to its members, else returns nil
|
34
58
|
def [](key)
|
35
59
|
return nil unless @data.is_a?(Hash)
|
36
|
-
return @data[key]
|
60
|
+
return @data[compatible_key(key)]
|
37
61
|
end
|
38
62
|
|
39
63
|
def fetch(key, default)
|
40
64
|
return nil unless @data.is_a?(Hash)
|
41
|
-
return @data.fetch(key, default)
|
65
|
+
return @data.fetch(compatible_key(key), default)
|
42
66
|
end
|
43
67
|
|
44
68
|
def to_hash
|
45
|
-
|
46
|
-
|
47
|
-
|
69
|
+
{:agent => @agent,
|
70
|
+
:action => @action,
|
71
|
+
:data => @data}
|
48
72
|
end
|
49
73
|
|
50
74
|
# Validate the request against the DDL
|
@@ -14,18 +14,59 @@ module MCollective
|
|
14
14
|
@agent = agent
|
15
15
|
@action = action
|
16
16
|
@results = result
|
17
|
+
|
18
|
+
convert_data_based_on_ddl if ddl
|
19
|
+
end
|
20
|
+
|
21
|
+
def ddl
|
22
|
+
@_ddl ||= DDL.new(agent)
|
23
|
+
rescue
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def data
|
28
|
+
@results[:data] = @results.delete("data") if @results.include?("data")
|
29
|
+
|
30
|
+
self[:data]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Converts keys on the supplied data to those listed as outputs
|
34
|
+
# in the DDL. This is to facilitate JSON based transports
|
35
|
+
# without forcing everyone to rewrite DDLs and clients to
|
36
|
+
# convert symbols to strings, the data will be on symbol keys
|
37
|
+
# if the DDL has a symbol and not a string output defined
|
38
|
+
def convert_data_based_on_ddl
|
39
|
+
interface = ddl.action_interface(action)
|
40
|
+
|
41
|
+
return if interface.fetch(:output, {}).empty?
|
42
|
+
|
43
|
+
interface[:output].each do |output, properties|
|
44
|
+
next if data.include?(output)
|
45
|
+
|
46
|
+
if output.is_a?(Symbol) && data.include?(output.to_s)
|
47
|
+
data[output] = data.delete(output.to_s)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def compatible_key(key)
|
53
|
+
if key.is_a?(Symbol) && @results.include?(key.to_s)
|
54
|
+
key.to_s
|
55
|
+
else
|
56
|
+
key
|
57
|
+
end
|
17
58
|
end
|
18
59
|
|
19
|
-
def [](
|
20
|
-
@results[
|
60
|
+
def [](key)
|
61
|
+
@results[compatible_key(key)]
|
21
62
|
end
|
22
63
|
|
23
|
-
def []=(
|
24
|
-
@results[
|
64
|
+
def []=(key, item)
|
65
|
+
@results[key] = item
|
25
66
|
end
|
26
67
|
|
27
68
|
def fetch(key, default)
|
28
|
-
@results.fetch(key, default)
|
69
|
+
@results.fetch(compatible_key(key), default)
|
29
70
|
end
|
30
71
|
|
31
72
|
def each
|
@@ -35,10 +76,10 @@ module MCollective
|
|
35
76
|
def to_json(*a)
|
36
77
|
{:agent => @agent,
|
37
78
|
:action => @action,
|
38
|
-
:sender =>
|
39
|
-
:statuscode =>
|
40
|
-
:statusmsg =>
|
41
|
-
:data =>
|
79
|
+
:sender => self[:sender],
|
80
|
+
:statuscode => self[:statuscode],
|
81
|
+
:statusmsg => self[:statusmsg],
|
82
|
+
:data => data}.to_json(*a)
|
42
83
|
end
|
43
84
|
|
44
85
|
def <=>(other)
|
@@ -36,7 +36,7 @@ module MCollective
|
|
36
36
|
|
37
37
|
it 'should log to a default logfile' do
|
38
38
|
Config.any_instance.stubs(:pluginconf).returns({})
|
39
|
-
File.expects(:open).with("/var/log/mcollective-audit.log", "a").yields(file)
|
39
|
+
File.expects(:open).with("/var/log/puppetlabs/mcollective/mcollective-audit.log", "a").yields(file)
|
40
40
|
Logfile.new.audit_request(request, nil)
|
41
41
|
end
|
42
42
|
end
|
@@ -11,6 +11,54 @@ module MCollective
|
|
11
11
|
@ddl.metadata(:name => "name", :description => "description", :author => "author", :license => "license", :version => "version", :url => "url", :timeout => "timeout")
|
12
12
|
end
|
13
13
|
|
14
|
+
describe "#symbolize_basic_input_arguments" do
|
15
|
+
before(:each) do
|
16
|
+
@ddl.action(:test, :description => "rspec")
|
17
|
+
@ddl.instance_variable_set("@current_entity", :test)
|
18
|
+
@ddl.input(:test, :prompt => "prompt", :description => "descr",
|
19
|
+
:type => :string, :optional => true, :validation => "",
|
20
|
+
:maxlength => 1, :default => "default")
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should warn when there are string and symbol inputs for the same stringified key" do
|
24
|
+
@ddl.input("test", :prompt => "prompt", :description => "descr",
|
25
|
+
:type => :string, :optional => true, :validation => "",
|
26
|
+
:maxlength => 1, :default => "default")
|
27
|
+
|
28
|
+
Log.expects(:warn).with("String and Symbol versions of input test found in the DDL for rspec, ensure your DDL keys are unique.")
|
29
|
+
@ddl.symbolize_basic_input_arguments(@ddl.action_interface(:test)[:input], {:test => 1, "test" => 2})
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should use the symbol given a string argument when there is not also a matching string input" do
|
33
|
+
result = @ddl.symbolize_basic_input_arguments(@ddl.action_interface(:test)[:input], {"test" => 2})
|
34
|
+
expect(result).to eq(:test => 2)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should use the string given a string argument when there is also a matching string input" do
|
38
|
+
@ddl.input("test", :prompt => "prompt", :description => "descr",
|
39
|
+
:type => :string, :optional => true, :validation => "",
|
40
|
+
:maxlength => 1, :default => "default")
|
41
|
+
|
42
|
+
Log.stubs(:warn)
|
43
|
+
result = @ddl.symbolize_basic_input_arguments(@ddl.action_interface(:test)[:input], {"test" => 2})
|
44
|
+
expect(result).to eq("test" => 2)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should not change symbols matching symbol inputs" do
|
48
|
+
result = @ddl.symbolize_basic_input_arguments(@ddl.action_interface(:test)[:input], {:test => 2})
|
49
|
+
expect(result).to eq(:test => 2)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should not change strings matching strings inputs" do
|
53
|
+
@ddl.input("string_test", :prompt => "prompt", :description => "descr",
|
54
|
+
:type => :string, :optional => true, :validation => "",
|
55
|
+
:maxlength => 1, :default => "default")
|
56
|
+
|
57
|
+
result = @ddl.symbolize_basic_input_arguments(@ddl.action_interface(:test)[:input], {"string_test" => 2})
|
58
|
+
expect(result).to eq("string_test" => 2)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
14
62
|
describe "#input" do
|
15
63
|
it "should validate that an :optional property is set" do
|
16
64
|
expect { @ddl.input(:x, {:y => 1}) }.to raise_error("Input needs a :optional property")
|
@@ -45,20 +93,29 @@ module MCollective
|
|
45
93
|
|
46
94
|
args.should == {:required => "specified"}
|
47
95
|
end
|
48
|
-
end
|
49
96
|
|
50
|
-
|
51
|
-
|
52
|
-
@ddl.action(:test, :description => "rspec")
|
97
|
+
it "should detect json primitive key names and consider them present as their symbol equivelants" do
|
98
|
+
args = {"required" => "specified"}
|
53
99
|
|
54
|
-
|
55
|
-
@ddl.validate_rpc_request(:fail, {})
|
56
|
-
}.to raise_error("Attempted to call action fail for rspec but it's not declared in the DDL")
|
100
|
+
@ddl.set_default_input_arguments(:test, args)
|
57
101
|
|
58
|
-
|
102
|
+
args.should == {"required" => "specified"}
|
59
103
|
end
|
60
104
|
|
61
|
-
it "should
|
105
|
+
it "should not consider the string key equiv to the symbol one when both symbol and string keys exist" do
|
106
|
+
@ddl.input("required", :prompt => "prompt", :description => "descr",
|
107
|
+
:type => :string, :optional => false, :validation => "",
|
108
|
+
:maxlength => 1, :default => "string default")
|
109
|
+
|
110
|
+
args = {"required" => "specified"}
|
111
|
+
@ddl.set_default_input_arguments(:test, args)
|
112
|
+
|
113
|
+
args.should == {"required" => "specified", :required=>"default"}
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "#validate_rpc_request" do
|
118
|
+
before(:each) do
|
62
119
|
@ddl.action(:test, :description => "rspec")
|
63
120
|
@ddl.instance_variable_set("@current_entity", :test)
|
64
121
|
@ddl.input(:optional, :prompt => "prompt", :description => "descr",
|
@@ -67,7 +124,17 @@ module MCollective
|
|
67
124
|
@ddl.input(:required, :prompt => "prompt", :description => "descr",
|
68
125
|
:type => :string, :optional => false, :validation => "",
|
69
126
|
:maxlength => 1)
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should ensure the action is known" do
|
130
|
+
expect {
|
131
|
+
@ddl.validate_rpc_request(:fail, {})
|
132
|
+
}.to raise_error("Attempted to call action fail for rspec but it's not declared in the DDL")
|
70
133
|
|
134
|
+
@ddl.validate_rpc_request(:test, {:required => "f"})
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should check all required arguments are present" do
|
71
138
|
@ddl.stubs(:validate_input_argument).returns(true)
|
72
139
|
|
73
140
|
expect {
|
@@ -78,20 +145,17 @@ module MCollective
|
|
78
145
|
end
|
79
146
|
|
80
147
|
it "should input validate every supplied key" do
|
81
|
-
@ddl.
|
82
|
-
@ddl.
|
83
|
-
@ddl.input(:optional, :prompt => "prompt", :description => "descr",
|
84
|
-
:type => :string, :optional => true, :validation => "",
|
85
|
-
:maxlength => 1)
|
86
|
-
@ddl.input(:required, :prompt => "prompt", :description => "descr",
|
87
|
-
:type => :string, :optional => false, :validation => "",
|
88
|
-
:maxlength => 1)
|
148
|
+
@ddl.expects(:validate_input_argument).with(@ddl.entities[:test][:input], :required, "f")
|
149
|
+
@ddl.expects(:validate_input_argument).with(@ddl.entities[:test][:input], :optional, "f")
|
89
150
|
|
151
|
+
@ddl.validate_rpc_request(:test, {:required => "f", :optional => "f"}).should == true
|
152
|
+
end
|
90
153
|
|
154
|
+
it "should input validate string keys for symbol inputs correctly" do
|
91
155
|
@ddl.expects(:validate_input_argument).with(@ddl.entities[:test][:input], :required, "f")
|
92
156
|
@ddl.expects(:validate_input_argument).with(@ddl.entities[:test][:input], :optional, "f")
|
93
157
|
|
94
|
-
@ddl.validate_rpc_request(:test, {
|
158
|
+
@ddl.validate_rpc_request(:test, {"required" => "f", "optional" => "f"}).should == true
|
95
159
|
end
|
96
160
|
end
|
97
161
|
|
@@ -371,13 +371,17 @@ module MCollective
|
|
371
371
|
|
372
372
|
payload = mock
|
373
373
|
payload.expects("[]").with(:filter).returns({})
|
374
|
+
payload.expects(:include?).with(:callerid).returns(false)
|
375
|
+
payload.expects("[]").with(:senderid).returns('sender')
|
374
376
|
|
375
|
-
m = Message.new(payload, "message", :type => :request
|
377
|
+
m = Message.new(payload, "message", {:type => :request, :collective => 'collective',
|
378
|
+
:agent => 'rspecagent', :requestid => '1234'})
|
376
379
|
m.instance_variable_set("@msgtime", Time.now.to_i)
|
377
380
|
|
378
381
|
expect {
|
379
382
|
m.validate
|
380
|
-
}.to raise_error(NotTargettedAtUs
|
383
|
+
}.to raise_error(NotTargettedAtUs,
|
384
|
+
"Message 1234 for agent 'rspecagent' in collective 'collective' from sender does not pass filters. Ignoring message.")
|
381
385
|
end
|
382
386
|
|
383
387
|
it "should pass for good messages" do
|
@@ -413,12 +417,16 @@ module MCollective
|
|
413
417
|
|
414
418
|
MCollective::PluginManager << {:type => "global_stats", :class => stats}
|
415
419
|
|
416
|
-
m = Message.new({:callerid => "caller", :senderid => "sender"}, "message",
|
417
|
-
|
420
|
+
m = Message.new({:callerid => "caller", :senderid => "sender"}, "message",
|
421
|
+
{:type => :request, :collective => 'collective',
|
422
|
+
:agent => 'rspecagent', :requestid => '1234'})
|
423
|
+
mtime = Time.now.to_i - 120
|
424
|
+
m.instance_variable_set("@msgtime", mtime)
|
418
425
|
|
419
426
|
expect {
|
420
427
|
m.validate
|
421
|
-
}.to raise_error(MsgTTLExpired
|
428
|
+
}.to raise_error(MsgTTLExpired,
|
429
|
+
/Message 1234 for agent 'rspecagent' in collective 'collective' from caller@sender created at #{mtime} is 1[12][019] seconds old, TTL is 60. Rejecting message./)
|
422
430
|
end
|
423
431
|
end
|
424
432
|
|
@@ -39,6 +39,65 @@ module MCollective
|
|
39
39
|
@client.stubs(:ddl).returns(ddl)
|
40
40
|
end
|
41
41
|
|
42
|
+
describe "#rpc_result_from_reply" do
|
43
|
+
it "should support symbol style replies" do
|
44
|
+
raw_result = {
|
45
|
+
:senderid => "rspec.id",
|
46
|
+
:body => {
|
47
|
+
:statuscode => 1,
|
48
|
+
:statusmsg => "rspec status",
|
49
|
+
:data => {
|
50
|
+
:one => 1
|
51
|
+
}
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
result = @client.rpc_result_from_reply("rspec", "test", raw_result)
|
56
|
+
|
57
|
+
expect(result.agent).to eq("rspec")
|
58
|
+
expect(result.action).to eq("test")
|
59
|
+
expect(result[:sender]).to eq("rspec.id")
|
60
|
+
expect(result[:statuscode]).to eq(1)
|
61
|
+
expect(result[:statusmsg]).to eq("rspec status")
|
62
|
+
expect(result[:data]).to eq(:one => 1)
|
63
|
+
expect(result.results).to eq(
|
64
|
+
:sender => "rspec.id",
|
65
|
+
:statuscode => 1,
|
66
|
+
:statusmsg => "rspec status",
|
67
|
+
:data => {:one => 1}
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should support string style replies" do
|
72
|
+
raw_result = {
|
73
|
+
"senderid" => "rspec.id",
|
74
|
+
"body" => {
|
75
|
+
"statuscode" => 1,
|
76
|
+
"statusmsg" => "rspec status",
|
77
|
+
"data" => {
|
78
|
+
"one" => 1,
|
79
|
+
"two" => 2
|
80
|
+
}
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
result = @client.rpc_result_from_reply("rspec", "test", raw_result)
|
85
|
+
|
86
|
+
expect(result.agent).to eq("rspec")
|
87
|
+
expect(result.action).to eq("test")
|
88
|
+
expect(result[:sender]).to eq("rspec.id")
|
89
|
+
expect(result[:statuscode]).to eq(1)
|
90
|
+
expect(result[:statusmsg]).to eq("rspec status")
|
91
|
+
expect(result[:data]).to eq("one" => 1, "two" => 2)
|
92
|
+
expect(result.results).to eq(
|
93
|
+
:sender => "rspec.id",
|
94
|
+
:statuscode => 1,
|
95
|
+
:statusmsg => "rspec status",
|
96
|
+
:data => {"one" => 1, "two" => 2}
|
97
|
+
)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
42
101
|
describe "#detect_and_set_stdin_discovery" do
|
43
102
|
before(:each) do
|
44
103
|
@client = Client.new("foo", {:options => {:filter => Util.empty_filter, :config => "/nonexisting", :stdin => @stdin}})
|
@@ -20,6 +20,28 @@ module MCollective
|
|
20
20
|
@request = Request.new(@req, @ddl)
|
21
21
|
end
|
22
22
|
|
23
|
+
describe "#compatible_key" do
|
24
|
+
it "should return the key if its a known key already" do
|
25
|
+
expect(@request.compatible_key(:foo)).to be(:foo)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should return the symbol key if the DDL defines both" do
|
29
|
+
@ddl.action("test", :description => "rspec")
|
30
|
+
@ddl.instance_variable_set("@current_entity", "test")
|
31
|
+
@ddl.input(:test, :prompt => "test", :description => "test", :type => :boolean, :optional => true)
|
32
|
+
@ddl.input("test", :prompt => "test", :description => "test", :type => :boolean, :optional => true)
|
33
|
+
|
34
|
+
expect(@request.compatible_key(:test)).to be(:test)
|
35
|
+
expect(@request.compatible_key("test")).to eq("test")
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should return the stringified key if a interned version of known string data was requested" do
|
39
|
+
expect(@request.compatible_key(:string)).to eq(:string)
|
40
|
+
@req[:body][:data]["string"] = "string data"
|
41
|
+
expect(@request.compatible_key(:string)).to eq("string")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
23
45
|
describe "#validate!" do
|
24
46
|
it "should validate the request using the supplied DDL" do
|
25
47
|
@ddl.expects(:validate_rpc_request).with("test", {:foo => "bar", :process_results => true})
|
@@ -60,6 +82,18 @@ module MCollective
|
|
60
82
|
@req.delete(:callerid)
|
61
83
|
Request.new(@req, @ddl).caller.should == "unknown"
|
62
84
|
end
|
85
|
+
|
86
|
+
it "should support JSON pure inputs" do
|
87
|
+
@req[:body] = {"action" => "test",
|
88
|
+
"data" => {"foo" => "bar", "process_results" => true},
|
89
|
+
"agent" => "tester"}
|
90
|
+
|
91
|
+
request = Request.new(@req, @ddl)
|
92
|
+
|
93
|
+
expect(request.action).to eq("test")
|
94
|
+
expect(request.agent).to eq("tester")
|
95
|
+
expect(request.data).to eq("foo" => "bar", "process_results" => true)
|
96
|
+
end
|
63
97
|
end
|
64
98
|
|
65
99
|
describe "#include?" do
|
@@ -82,6 +116,8 @@ module MCollective
|
|
82
116
|
it "should return correct value" do
|
83
117
|
@req[:body][:data][:process_results] = false
|
84
118
|
Request.new(@req, @ddl).should_respond?.should == false
|
119
|
+
@req[:body][:data]["process_results"] = false
|
120
|
+
Request.new(@req, @ddl).should_respond?.should == false
|
85
121
|
end
|
86
122
|
end
|
87
123
|
|
@@ -27,6 +27,34 @@ module MCollective
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
+
describe "#convert_data_based_on_ddl" do
|
31
|
+
it "should convert string data to symbol data based on the DDL" do
|
32
|
+
ddl = DDL.new("rspec", :agent, false)
|
33
|
+
ddl.metadata(:name => "name", :description => "description", :author => "author", :license => "license", :version => "version", :url => "url", :timeout => "timeout")
|
34
|
+
ddl.action("test", :description => "rspec")
|
35
|
+
ddl.instance_variable_set("@current_entity", "test")
|
36
|
+
ddl.output(:one, :description => "rspec one", :display_as => "One")
|
37
|
+
|
38
|
+
DDL.expects(:new).with("rspec").returns(ddl)
|
39
|
+
|
40
|
+
raw_result = {
|
41
|
+
"sender" => "rspec.id",
|
42
|
+
"statuscode" => 1,
|
43
|
+
"statusmsg" => "rspec status",
|
44
|
+
"data" => {
|
45
|
+
"one" => 1,
|
46
|
+
"two" => 2
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
result = Result.new("rspec", "test", raw_result)
|
51
|
+
expect(result.data).to eq(
|
52
|
+
:one => 1,
|
53
|
+
"two" => 2
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
30
58
|
describe "#[]" do
|
31
59
|
it "should access the results hash and return correct data" do
|
32
60
|
@result[:foo].should == "bar"
|
@@ -71,17 +99,17 @@ module MCollective
|
|
71
99
|
|
72
100
|
describe "#<=>" do
|
73
101
|
it "should implement the Combined Comparison operator based on sender name" do
|
74
|
-
result_a = Result.new("tester",
|
75
|
-
"test",
|
76
|
-
{ :statuscode => 0,
|
77
|
-
:statusmsg => "OK",
|
78
|
-
:sender => "a_rspec",
|
102
|
+
result_a = Result.new("tester",
|
103
|
+
"test",
|
104
|
+
{ :statuscode => 0,
|
105
|
+
:statusmsg => "OK",
|
106
|
+
:sender => "a_rspec",
|
79
107
|
:data => {}})
|
80
|
-
result_b = Result.new("tester",
|
81
|
-
"test",
|
82
|
-
{ :statuscode => 0,
|
83
|
-
:statusmsg => "OK",
|
84
|
-
:sender => "b_rspec",
|
108
|
+
result_b = Result.new("tester",
|
109
|
+
"test",
|
110
|
+
{ :statuscode => 0,
|
111
|
+
:statusmsg => "OK",
|
112
|
+
:sender => "b_rspec",
|
85
113
|
:data => {}})
|
86
114
|
|
87
115
|
(result_a <=> result_b).should == -1
|
@@ -259,7 +259,7 @@ module MCollective
|
|
259
259
|
it 'should log if a message was received by not directed at the server' do
|
260
260
|
runner.expects(:receive).raises(NotTargettedAtUs)
|
261
261
|
runner.instance_variable_set(:@exit_receiver_thread, true)
|
262
|
-
Log.expects(:
|
262
|
+
Log.expects(:info)
|
263
263
|
runner.send(:receiver_thread)
|
264
264
|
end
|
265
265
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mcollective-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.11.0
|
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: 2017-
|
12
|
+
date: 2017-06-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: systemu
|