mcollective-client 1.3.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of mcollective-client might be problematic. Click here for more details.

Files changed (103) hide show
  1. data/bin/mc-call-agent +54 -0
  2. data/bin/mco +27 -0
  3. data/lib/mcollective.rb +70 -0
  4. data/lib/mcollective/agents.rb +160 -0
  5. data/lib/mcollective/application.rb +354 -0
  6. data/lib/mcollective/applications.rb +145 -0
  7. data/lib/mcollective/client.rb +292 -0
  8. data/lib/mcollective/config.rb +202 -0
  9. data/lib/mcollective/connector.rb +18 -0
  10. data/lib/mcollective/connector/base.rb +24 -0
  11. data/lib/mcollective/facts.rb +39 -0
  12. data/lib/mcollective/facts/base.rb +86 -0
  13. data/lib/mcollective/log.rb +103 -0
  14. data/lib/mcollective/logger.rb +5 -0
  15. data/lib/mcollective/logger/base.rb +73 -0
  16. data/lib/mcollective/logger/console_logger.rb +61 -0
  17. data/lib/mcollective/logger/file_logger.rb +46 -0
  18. data/lib/mcollective/logger/syslog_logger.rb +53 -0
  19. data/lib/mcollective/matcher.rb +16 -0
  20. data/lib/mcollective/matcher/parser.rb +93 -0
  21. data/lib/mcollective/matcher/scanner.rb +123 -0
  22. data/lib/mcollective/message.rb +201 -0
  23. data/lib/mcollective/monkey_patches.rb +104 -0
  24. data/lib/mcollective/optionparser.rb +164 -0
  25. data/lib/mcollective/pluginmanager.rb +180 -0
  26. data/lib/mcollective/pluginpackager.rb +26 -0
  27. data/lib/mcollective/pluginpackager/agent_definition.rb +79 -0
  28. data/lib/mcollective/pluginpackager/standard_definition.rb +59 -0
  29. data/lib/mcollective/registration.rb +16 -0
  30. data/lib/mcollective/registration/base.rb +75 -0
  31. data/lib/mcollective/rpc.rb +188 -0
  32. data/lib/mcollective/rpc/actionrunner.rb +142 -0
  33. data/lib/mcollective/rpc/agent.rb +441 -0
  34. data/lib/mcollective/rpc/audit.rb +38 -0
  35. data/lib/mcollective/rpc/client.rb +793 -0
  36. data/lib/mcollective/rpc/ddl.rb +258 -0
  37. data/lib/mcollective/rpc/helpers.rb +339 -0
  38. data/lib/mcollective/rpc/progress.rb +63 -0
  39. data/lib/mcollective/rpc/reply.rb +61 -0
  40. data/lib/mcollective/rpc/request.rb +51 -0
  41. data/lib/mcollective/rpc/result.rb +41 -0
  42. data/lib/mcollective/rpc/stats.rb +185 -0
  43. data/lib/mcollective/runnerstats.rb +90 -0
  44. data/lib/mcollective/security.rb +26 -0
  45. data/lib/mcollective/security/base.rb +237 -0
  46. data/lib/mcollective/shell.rb +87 -0
  47. data/lib/mcollective/ssl.rb +246 -0
  48. data/lib/mcollective/unix_daemon.rb +37 -0
  49. data/lib/mcollective/util.rb +274 -0
  50. data/lib/mcollective/vendor.rb +41 -0
  51. data/lib/mcollective/vendor/require_vendored.rb +2 -0
  52. data/lib/mcollective/windows_daemon.rb +25 -0
  53. data/spec/Rakefile +16 -0
  54. data/spec/fixtures/application/test.rb +7 -0
  55. data/spec/fixtures/test-cert.pem +15 -0
  56. data/spec/fixtures/test-private.pem +15 -0
  57. data/spec/fixtures/test-public.pem +6 -0
  58. data/spec/monkey_patches/instance_variable_defined.rb +7 -0
  59. data/spec/spec.opts +1 -0
  60. data/spec/spec_helper.rb +25 -0
  61. data/spec/unit/agents_spec.rb +280 -0
  62. data/spec/unit/application_spec.rb +636 -0
  63. data/spec/unit/applications_spec.rb +155 -0
  64. data/spec/unit/array.rb +30 -0
  65. data/spec/unit/config_spec.rb +148 -0
  66. data/spec/unit/facts/base_spec.rb +118 -0
  67. data/spec/unit/facts_spec.rb +39 -0
  68. data/spec/unit/log_spec.rb +71 -0
  69. data/spec/unit/logger/base_spec.rb +110 -0
  70. data/spec/unit/logger/syslog_logger_spec.rb +86 -0
  71. data/spec/unit/matcher/parser_spec.rb +106 -0
  72. data/spec/unit/matcher/scanner_spec.rb +71 -0
  73. data/spec/unit/message_spec.rb +401 -0
  74. data/spec/unit/optionparser_spec.rb +113 -0
  75. data/spec/unit/pluginmanager_spec.rb +173 -0
  76. data/spec/unit/pluginpackager/agent_definition_spec.rb +130 -0
  77. data/spec/unit/pluginpackager/standard_definition_spec.rb +75 -0
  78. data/spec/unit/plugins/mcollective/connector/activemq_spec.rb +533 -0
  79. data/spec/unit/plugins/mcollective/connector/stomp/eventlogger_spec.rb +34 -0
  80. data/spec/unit/plugins/mcollective/connector/stomp_spec.rb +417 -0
  81. data/spec/unit/plugins/mcollective/packagers/ospackage_spec.rb +229 -0
  82. data/spec/unit/plugins/mcollective/security/psk_spec.rb +156 -0
  83. data/spec/unit/registration/base_spec.rb +77 -0
  84. data/spec/unit/rpc/actionrunner_spec.rb +213 -0
  85. data/spec/unit/rpc/agent_spec.rb +155 -0
  86. data/spec/unit/rpc/client_spec.rb +523 -0
  87. data/spec/unit/rpc/ddl_spec.rb +388 -0
  88. data/spec/unit/rpc/helpers_spec.rb +55 -0
  89. data/spec/unit/rpc/reply_spec.rb +143 -0
  90. data/spec/unit/rpc/request_spec.rb +115 -0
  91. data/spec/unit/rpc/result_spec.rb +66 -0
  92. data/spec/unit/rpc/stats_spec.rb +288 -0
  93. data/spec/unit/runnerstats_spec.rb +40 -0
  94. data/spec/unit/security/base_spec.rb +279 -0
  95. data/spec/unit/shell_spec.rb +144 -0
  96. data/spec/unit/ssl_spec.rb +244 -0
  97. data/spec/unit/symbol.rb +11 -0
  98. data/spec/unit/unix_daemon.rb +41 -0
  99. data/spec/unit/util_spec.rb +342 -0
  100. data/spec/unit/vendor_spec.rb +34 -0
  101. data/spec/unit/windows_daemon.rb +43 -0
  102. data/spec/windows_spec.opts +1 -0
  103. metadata +242 -0
@@ -0,0 +1,37 @@
1
+ module MCollective
2
+ class UnixDaemon
3
+ # Daemonize the current process
4
+ def self.daemonize
5
+ fork do
6
+ Process.setsid
7
+ exit if fork
8
+ Dir.chdir('/tmp')
9
+ STDIN.reopen('/dev/null')
10
+ STDOUT.reopen('/dev/null', 'a')
11
+ STDERR.reopen('/dev/null', 'a')
12
+
13
+ yield
14
+ end
15
+ end
16
+
17
+ def self.daemonize_runner(pid=nil)
18
+ raise "The Unix Daemonizer can not be used on the Windows Platform" if Util.windows?
19
+
20
+ UnixDaemon.daemonize do
21
+ if pid
22
+ begin
23
+ File.open(pid, 'w') {|f| f.write(Process.pid) }
24
+ rescue Exception => e
25
+ end
26
+ end
27
+
28
+ begin
29
+ runner = Runner.new(nil)
30
+ runner.run
31
+ ensure
32
+ File.unlink(pid) if pid && File.exist?(pid)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,274 @@
1
+ module MCollective
2
+ # Some basic utility helper methods useful to clients, agents, runner etc.
3
+ module Util
4
+ # Finds out if this MCollective has an agent by the name passed
5
+ #
6
+ # If the passed name starts with a / it's assumed to be regex
7
+ # and will use regex to match
8
+ def self.has_agent?(agent)
9
+ agent = Regexp.new(agent.gsub("\/", "")) if agent.match("^/")
10
+
11
+ if agent.is_a?(Regexp)
12
+ if Agents.agentlist.grep(agent).size > 0
13
+ return true
14
+ else
15
+ return false
16
+ end
17
+ else
18
+ return Agents.agentlist.include?(agent)
19
+ end
20
+
21
+ false
22
+ end
23
+
24
+ # On windows ^c can't interrupt the VM if its blocking on
25
+ # IO, so this sets up a dummy thread that sleeps and this
26
+ # will have the end result of being interruptable at least
27
+ # once a second. This is a common pattern found in Rails etc
28
+ def self.setup_windows_sleeper
29
+ Thread.new { loop { sleep 1 } } if Util.windows?
30
+ end
31
+
32
+ # Checks if this node has a configuration management class by parsing the
33
+ # a text file with just a list of classes, recipes, roles etc. This is
34
+ # ala the classes.txt from puppet.
35
+ #
36
+ # If the passed name starts with a / it's assumed to be regex
37
+ # and will use regex to match
38
+ def self.has_cf_class?(klass)
39
+ klass = Regexp.new(klass.gsub("\/", "")) if klass.match("^/")
40
+ cfile = Config.instance.classesfile
41
+
42
+ Log.debug("Looking for configuration management classes in #{cfile}")
43
+
44
+ begin
45
+ File.readlines(cfile).each do |k|
46
+ if klass.is_a?(Regexp)
47
+ return true if k.chomp.match(klass)
48
+ else
49
+ return true if k.chomp == klass
50
+ end
51
+ end
52
+ rescue Exception => e
53
+ Log.warn("Parsing classes file '#{cfile}' failed: #{e.class}: #{e}")
54
+ end
55
+
56
+ false
57
+ end
58
+
59
+ # Gets the value of a specific fact, mostly just a duplicate of MCollective::Facts.get_fact
60
+ # but it kind of goes with the other classes here
61
+ def self.get_fact(fact)
62
+ Facts.get_fact(fact)
63
+ end
64
+
65
+ # Compares fact == value,
66
+ #
67
+ # If the passed value starts with a / it's assumed to be regex
68
+ # and will use regex to match
69
+ def self.has_fact?(fact, value, operator)
70
+
71
+ Log.debug("Comparing #{fact} #{operator} #{value}")
72
+ Log.debug("where :fact = '#{fact}', :operator = '#{operator}', :value = '#{value}'")
73
+
74
+ fact = Facts[fact]
75
+ return false if fact.nil?
76
+
77
+ fact = fact.clone
78
+
79
+ if operator == '=~'
80
+ # to maintain backward compat we send the value
81
+ # as /.../ which is what 1.0.x needed. this strips
82
+ # off the /'s wich is what we need here
83
+ if value =~ /^\/(.+)\/$/
84
+ value = $1
85
+ end
86
+
87
+ return true if fact.match(Regexp.new(value))
88
+
89
+ elsif operator == "=="
90
+ return true if fact == value
91
+
92
+ elsif ['<=', '>=', '<', '>', '!='].include?(operator)
93
+ # Yuk - need to type cast, but to_i and to_f are overzealous
94
+ if value =~ /^[0-9]+$/ && fact =~ /^[0-9]+$/
95
+ fact = Integer(fact)
96
+ value = Integer(value)
97
+ elsif value =~ /^[0-9]+.[0-9]+$/ && fact =~ /^[0-9]+.[0-9]+$/
98
+ fact = Float(fact)
99
+ value = Float(value)
100
+ end
101
+
102
+ return true if eval("fact #{operator} value")
103
+ end
104
+
105
+ false
106
+ end
107
+
108
+ # Checks if the configured identity matches the one supplied
109
+ #
110
+ # If the passed name starts with a / it's assumed to be regex
111
+ # and will use regex to match
112
+ def self.has_identity?(identity)
113
+ identity = Regexp.new(identity.gsub("\/", "")) if identity.match("^/")
114
+
115
+ if identity.is_a?(Regexp)
116
+ return Config.instance.identity.match(identity)
117
+ else
118
+ return true if Config.instance.identity == identity
119
+ end
120
+
121
+ false
122
+ end
123
+
124
+ # Checks if the passed in filter is an empty one
125
+ def self.empty_filter?(filter)
126
+ filter == empty_filter || filter == {}
127
+ end
128
+
129
+ # Creates an empty filter
130
+ def self.empty_filter
131
+ {"fact" => [],
132
+ "cf_class" => [],
133
+ "agent" => [],
134
+ "identity" => [],
135
+ "compound" => []}
136
+ end
137
+
138
+ # Picks a config file defaults to ~/.mcollective
139
+ # else /etc/mcollective/client.cfg
140
+ def self.config_file_for_user
141
+ # expand_path is pretty lame, it relies on HOME environment
142
+ # which isnt't always there so just handling all exceptions
143
+ # here as cant find reverting to default
144
+ begin
145
+ config = File.expand_path("~/.mcollective")
146
+
147
+ unless File.readable?(config) && File.file?(config)
148
+ config = "/etc/mcollective/client.cfg"
149
+ end
150
+ rescue Exception => e
151
+ config = "/etc/mcollective/client.cfg"
152
+ end
153
+
154
+ return config
155
+ end
156
+
157
+ # Creates a standard options hash
158
+ def self.default_options
159
+ {:verbose => false,
160
+ :disctimeout => 2,
161
+ :timeout => 5,
162
+ :config => config_file_for_user,
163
+ :collective => nil,
164
+ :filter => empty_filter}
165
+ end
166
+
167
+ def self.make_subscriptions(agent, type, collective=nil)
168
+ config = Config.instance
169
+
170
+ raise("Unknown target type #{type}") unless [:broadcast, :directed, :reply].include?(type)
171
+
172
+ if collective.nil?
173
+ config.collectives.map do |c|
174
+ {:agent => agent, :type => type, :collective => c}
175
+ end
176
+ else
177
+ raise("Unknown collective '#{collective}' known collectives are '#{config.collectives.join ', '}'") unless config.collectives.include?(collective)
178
+
179
+ [{:agent => agent, :type => type, :collective => collective}]
180
+ end
181
+ end
182
+
183
+ # Helper to subscribe to a topic on multiple collectives or just one
184
+ def self.subscribe(targets)
185
+ connection = PluginManager["connector_plugin"]
186
+
187
+ targets = [targets].flatten
188
+
189
+ targets.each do |target|
190
+ connection.subscribe(target[:agent], target[:type], target[:collective])
191
+ end
192
+ end
193
+
194
+ # Helper to unsubscribe to a topic on multiple collectives or just one
195
+ def self.unsubscribe(targets)
196
+ connection = PluginManager["connector_plugin"]
197
+
198
+ targets = [targets].flatten
199
+
200
+ targets.each do |target|
201
+ connection.unsubscribe(target[:agent], target[:type], target[:collective])
202
+ end
203
+ end
204
+
205
+ # Wrapper around PluginManager.loadclass
206
+ def self.loadclass(klass)
207
+ PluginManager.loadclass(klass)
208
+ end
209
+
210
+ # Parse a fact filter string like foo=bar into the tuple hash thats needed
211
+ def self.parse_fact_string(fact)
212
+ if fact =~ /^([^ ]+?)[ ]*=>[ ]*(.+)/
213
+ return {:fact => $1, :value => $2, :operator => '>=' }
214
+ elsif fact =~ /^([^ ]+?)[ ]*=<[ ]*(.+)/
215
+ return {:fact => $1, :value => $2, :operator => '<=' }
216
+ elsif fact =~ /^([^ ]+?)[ ]*(<=|>=|<|>|!=|==|=~)[ ]*(.+)/
217
+ return {:fact => $1, :value => $3, :operator => $2 }
218
+ elsif fact =~ /^(.+?)[ ]*=[ ]*\/(.+)\/$/
219
+ return {:fact => $1, :value => "/#{$2}/", :operator => '=~' }
220
+ elsif fact =~ /^([^= ]+?)[ ]*=[ ]*(.+)/
221
+ return {:fact => $1, :value => $2, :operator => '==' }
222
+ else
223
+ raise "Could not parse fact #{fact} it does not appear to be in a valid format"
224
+ end
225
+ end
226
+
227
+ # Escapes a string so it's safe to use in system() or backticks
228
+ #
229
+ # Taken from Shellwords#shellescape since it's only in a few ruby versions
230
+ def self.shellescape(str)
231
+ return "''" if str.empty?
232
+
233
+ str = str.dup
234
+
235
+ # Process as a single byte sequence because not all shell
236
+ # implementations are multibyte aware.
237
+ str.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/n, "\\\\\\1")
238
+
239
+ # A LF cannot be escaped with a backslash because a backslash + LF
240
+ # combo is regarded as line continuation and simply ignored.
241
+ str.gsub!(/\n/, "'\n'")
242
+
243
+ return str
244
+ end
245
+
246
+ def self.windows?
247
+ !!(RbConfig::CONFIG['host_os'] =~ /mswin|win32|dos|mingw|cygwin/i)
248
+ end
249
+
250
+ def self.eval_compound_statement(expression)
251
+ if expression.values.first =~ /^\//
252
+ return Util.has_cf_class?(expression.values.first)
253
+ elsif expression.values.first =~ />=|<=|=|<|>/
254
+ optype = expression.values.first.match(/>=|<=|=|<|>/)
255
+ name, value = expression.values.first.split(optype[0])
256
+ unless value.split("")[0] == "/"
257
+ optype[0] == "=" ? optype = "==" : optype = optype[0]
258
+ else
259
+ optype = "=~"
260
+ end
261
+
262
+ return Util.has_fact?(name,value, optype).to_s
263
+ else
264
+ return Util.has_cf_class?(expression.values.first)
265
+ end
266
+ end
267
+
268
+ # Returns the current ruby version as per RUBY_VERSION, mostly
269
+ # doing this here to aid testing
270
+ def self.ruby_version
271
+ RUBY_VERSION
272
+ end
273
+ end
274
+ end
@@ -0,0 +1,41 @@
1
+ module MCollective
2
+ # Simple module to manage vendored code.
3
+ #
4
+ # To vendor a library simply download its whole git repo or untar
5
+ # into vendor/libraryname and create a load_libraryname.rb file
6
+ # to add its libdir into the $:.
7
+ #
8
+ # Once you have that file, add a require line in vendor/require_vendored.rb
9
+ # which will run after all the load_* files.
10
+ #
11
+ # The intention is to not change vendored libraries and to eventually
12
+ # make adding them in optional so that distros can simply adjust their
13
+ # packaging to exclude this directory and the various load_xxx.rb scripts
14
+ # if they wish to install these gems as native packages.
15
+ class Vendor
16
+ class << self
17
+ def vendor_dir
18
+ File.join([File.dirname(File.expand_path(__FILE__)), "vendor"])
19
+ end
20
+
21
+ def load_entry(entry)
22
+ Log.debug("Loading vendored #{$1}")
23
+ load "#{vendor_dir}/#{entry}"
24
+ end
25
+
26
+ def require_libs
27
+ require 'mcollective/vendor/require_vendored'
28
+ end
29
+
30
+ def load_vendored
31
+ Dir.entries(vendor_dir).each do |entry|
32
+ if entry.match(/load_(\w+?)\.rb$/)
33
+ load_entry entry
34
+ end
35
+ end
36
+
37
+ require_libs
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,2 @@
1
+ require 'systemu'
2
+ require 'json'
@@ -0,0 +1,25 @@
1
+ require 'win32/daemon'
2
+
3
+ module MCollective
4
+ class WindowsDaemon < Win32::Daemon
5
+ def self.daemonize_runner(pid=nil)
6
+ raise "Writing pid files are not supported on the Windows Platform" if pid
7
+ raise "The Windows Daemonizer should only be used on the Windows Platform" unless Util.windows?
8
+
9
+ WindowsDaemon.mainloop
10
+ end
11
+
12
+ def service_main
13
+ Log.debug("Starting Windows Service Daemon")
14
+
15
+ runner = Runner.new(nil)
16
+ runner.run
17
+ end
18
+
19
+ def service_stop
20
+ Log.info("Windows service stopping")
21
+ PluginManager["connector_plugin"].disconnect
22
+ exit! 0
23
+ end
24
+ end
25
+ end
data/spec/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper.rb")
2
+ require 'rake'
3
+ require 'rspec/core/rake_task'
4
+
5
+ desc "Run all specs"
6
+ RSpec::Core::RakeTask.new(:all) do |t|
7
+ t.pattern = 'unit/**/*_spec.rb'
8
+
9
+ if MCollective::Util.windows?
10
+ t.rspec_opts = File.read("windows_spec.opts").chomp
11
+ else
12
+ t.rspec_opts = File.read("spec.opts").chomp
13
+ end
14
+ end
15
+
16
+ task :default => [:all]
@@ -0,0 +1,7 @@
1
+ module MCollective
2
+ class Application::Test < Application
3
+ def run
4
+ true
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIICRDCCAa2gAwIBAgIBAjANBgkqhkiG9w0BAQUFADAfMR0wGwYDVQQDDBRQdXBw
3
+ ZXQgQ0E6IGRldmNvLm5ldDAeFw0xMjAzMzAyMTQ2NDRaFw0xNzAzMjkyMTQ2NDRa
4
+ MA4xDDAKBgNVBAMMA2ZvbzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0X8t
5
+ vvXk9Tmfhw5T87svZ1Oc2xORR1YbPTBCpNR3dvg2Pdurt16dLMXIGT5EeXkZy6cF
6
+ MHG9pg/0ubCA7L/EdPI/Xq6n+O2kUUX8+ca0qwmj9/TwqxMSxVlKGr6SSqcCcHfi
7
+ 0hsDsHQaKyzAPbr69gbHhBzskd9bkh51Tyg96JECAwEAAaOBoDCBnTA4BglghkgB
8
+ hvhCAQ0EKxYpUHVwcGV0IFJ1YnkvT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNh
9
+ dGUwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUjZoFfKlgsGXuldlQQPOnBNrX9oMw
10
+ CwYDVR0PBAQDAgWgMCcGA1UdJQQgMB4GCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYB
11
+ BQUHAwQwDQYJKoZIhvcNAQEFBQADgYEApfZYKcYrMUtStJ0jjoZryn4ssghs8mkl
12
+ C+njqTt/X8/7drqiSHB/0WXN0366QfVEgotxh0akhi2GJxs+eD/iCIRSL29hwDjL
13
+ LpcsM/Rk0s8jG/XJDmS+BdY+wGOezasaCaqQlsU/Sjv8TQ+8D89OBfh+ZYW3gL9E
14
+ JWp47gFO/RA=
15
+ -----END CERTIFICATE-----
@@ -0,0 +1,15 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIICXQIBAAKBgQDMMGfwPZO8Xwn8XvrOy3Nc4nTkY/8lJrF3wvoGWdrcuQsNjYvZ
3
+ O59PqXGxu/No5Lt5ymMd8orxR3bPRy2uobmZ2yXPP5mh48lZkMazGHPUeZkWOWbR
4
+ 7hgAvOVpNGp6NyR7D3PxZ96jzAbH8ttJVZAt/T2ImMJrzUG1Oj0/XO9fNQIDAQAB
5
+ AoGALnA/41KN3ASdZ8lOL2P0C8bxINRhPdjL+dndNT6QWSy4h8+OY8x8kgiOdAaz
6
+ +EI1JSDTZAc6dF91dPTSPepIJH5n+ueBDHFTGUnbxCw25+6X9CaLig52fNh7vs53
7
+ sjtEvpju4Qe49FjnGe+UZ6jhd5b10pFtiuTVhMUTE77HqmECQQD4iSmj5/OVz3qk
8
+ zrJ9Z8z6NjQFjklA1reHWhYal9k+475xOyLQz4yXk+pzq+9uYMo+LBIvDaY+mzDb
9
+ I3Sp8/vJAkEA0lJKANLqkRsGqp2BITzFS3vpoYQxL13pghFfvQPkpD+JbHB0wI4a
10
+ E1xv1kr/4bQ3aMEohJnaLbAf/O20nS3mDQJBAOwZ0Tbl+J7OlSHPQfykCTOBHnZQ
11
+ rwIrd/na+LiWnEiELbx/gxl+sX6lg8oTAceHp1jcoQGWI+HBp+3lhsSVBRECQHz2
12
+ jae9qcc7kpNu79lqvSLjZeYkoACvwN0aK5MnrAL3CVTX4FbEV7PnOT/O4ggdxspD
13
+ 8ioPK7X3rpneNnEpY0UCQQCM38gg75soJTS9W3BY04gLdjLd1h5rXe28bTgnwRM2
14
+ Y2HMzPDtCWdjWvmpZMfAg+U4Rkr24Gbd1V3bq6OlJIeB
15
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,6 @@
1
+ -----BEGIN PUBLIC KEY-----
2
+ MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMMGfwPZO8Xwn8XvrOy3Nc4nTk
3
+ Y/8lJrF3wvoGWdrcuQsNjYvZO59PqXGxu/No5Lt5ymMd8orxR3bPRy2uobmZ2yXP
4
+ P5mh48lZkMazGHPUeZkWOWbR7hgAvOVpNGp6NyR7D3PxZ96jzAbH8ttJVZAt/T2I
5
+ mMJrzUG1Oj0/XO9fNQIDAQAB
6
+ -----END PUBLIC KEY-----