ml-puppetdb-terminus 3.2.1

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.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +202 -0
  3. data/NOTICE.txt +17 -0
  4. data/README.md +22 -0
  5. data/puppet/lib/puppet/application/storeconfigs.rb +4 -0
  6. data/puppet/lib/puppet/face/node/deactivate.rb +37 -0
  7. data/puppet/lib/puppet/face/node/status.rb +80 -0
  8. data/puppet/lib/puppet/face/storeconfigs.rb +193 -0
  9. data/puppet/lib/puppet/indirector/catalog/puppetdb.rb +400 -0
  10. data/puppet/lib/puppet/indirector/facts/puppetdb.rb +152 -0
  11. data/puppet/lib/puppet/indirector/facts/puppetdb_apply.rb +25 -0
  12. data/puppet/lib/puppet/indirector/node/puppetdb.rb +19 -0
  13. data/puppet/lib/puppet/indirector/resource/puppetdb.rb +108 -0
  14. data/puppet/lib/puppet/reports/puppetdb.rb +188 -0
  15. data/puppet/lib/puppet/util/puppetdb.rb +108 -0
  16. data/puppet/lib/puppet/util/puppetdb/char_encoding.rb +316 -0
  17. data/puppet/lib/puppet/util/puppetdb/command.rb +116 -0
  18. data/puppet/lib/puppet/util/puppetdb/command_names.rb +8 -0
  19. data/puppet/lib/puppet/util/puppetdb/config.rb +148 -0
  20. data/puppet/lib/puppet/util/puppetdb/http.rb +121 -0
  21. data/puppet/spec/README.markdown +8 -0
  22. data/puppet/spec/spec.opts +6 -0
  23. data/puppet/spec/spec_helper.rb +38 -0
  24. data/puppet/spec/unit/face/node/deactivate_spec.rb +28 -0
  25. data/puppet/spec/unit/face/node/status_spec.rb +43 -0
  26. data/puppet/spec/unit/face/storeconfigs_spec.rb +199 -0
  27. data/puppet/spec/unit/indirector/catalog/puppetdb_spec.rb +703 -0
  28. data/puppet/spec/unit/indirector/facts/puppetdb_apply_spec.rb +27 -0
  29. data/puppet/spec/unit/indirector/facts/puppetdb_spec.rb +347 -0
  30. data/puppet/spec/unit/indirector/node/puppetdb_spec.rb +61 -0
  31. data/puppet/spec/unit/indirector/resource/puppetdb_spec.rb +199 -0
  32. data/puppet/spec/unit/reports/puppetdb_spec.rb +249 -0
  33. data/puppet/spec/unit/util/puppetdb/char_encoding_spec.rb +212 -0
  34. data/puppet/spec/unit/util/puppetdb/command_spec.rb +98 -0
  35. data/puppet/spec/unit/util/puppetdb/config_spec.rb +227 -0
  36. data/puppet/spec/unit/util/puppetdb/http_spec.rb +138 -0
  37. data/puppet/spec/unit/util/puppetdb_spec.rb +33 -0
  38. metadata +115 -0
@@ -0,0 +1,98 @@
1
+ require 'spec_helper'
2
+ require 'digest/sha1'
3
+ require 'puppet/network/http_pool'
4
+ require 'puppet/util/puppetdb'
5
+
6
+
7
+ describe Puppet::Util::Puppetdb::Command do
8
+ let(:payload) { {'resistance' => 'futile', 'opinion' => 'irrelevant'} }
9
+ let(:subject) { described_class.new("OPEN SESAME", 1,
10
+ 'foo.localdomain', payload) }
11
+
12
+
13
+ describe "#submit" do
14
+ let(:http) { mock 'http' }
15
+ before(:each) do
16
+ Puppet::Network::HttpPool.expects(:http_instance).returns http
17
+ end
18
+
19
+ context "when the submission succeeds" do
20
+ let(:httpok) { Net::HTTPOK.new('1.1', 200, '') }
21
+
22
+ it "should issue the HTTP POST and log success" do
23
+ httpok.stubs(:body).returns '{"uuid": "a UUID"}'
24
+ http.expects(:post).returns httpok
25
+
26
+ subject.submit
27
+ test_logs.find_all { |m|
28
+ m =~ /'#{subject.command}' command for #{subject.certname} submitted to PuppetDB/
29
+ }.length.should == 1
30
+ end
31
+ end
32
+
33
+ context "when the submission fails" do
34
+ let(:httpbad) { Net::HTTPBadRequest.new('1.1', 400, '') }
35
+
36
+ it "should issue the HTTP POST and raise an exception" do
37
+
38
+ httpbad.stubs(:body).returns 'Strange things are afoot'
39
+ http.expects(:post).returns httpbad
40
+ expect {
41
+ subject.submit
42
+ }.to raise_error(Puppet::Error, /Strange things are afoot/)
43
+ end
44
+
45
+ it 'when soft_write_failure is enabled should just invoke Puppet.err' do
46
+ subject.expects(:config).at_least_once.returns \
47
+ OpenStruct.new({:soft_write_failure => true})
48
+
49
+ httpbad.stubs(:body).returns 'Strange things are afoot'
50
+ http.expects(:post).returns httpbad
51
+ Puppet.expects(:err).with do |msg|
52
+ msg =~ /Strange things are afoot/
53
+ end
54
+ subject.submit
55
+ end
56
+ end
57
+ end
58
+
59
+ it "should not warn when the the string contains valid UTF-8 characters" do
60
+ Puppet.expects(:warning).never
61
+ cmd = described_class.new("command-1", 1, "foo.localdomain", {"foo" => "\u2192"})
62
+ cmd.payload.include?("\u2192").should be_true
63
+ end
64
+
65
+ describe "on ruby > 1.8", :if => RUBY_VERSION !~ /^1.8/ do
66
+
67
+ it "should warn when a command payload includes non-ascii UTF-8 characters" do
68
+ Puppet.expects(:warning).with {|msg| msg =~ /Error encoding a 'command-1' command for host 'foo.localdomain' ignoring invalid UTF-8 byte sequences/}
69
+ cmd = described_class.new("command-1", 1, "foo.localdomain", {"foo" => [192].pack('c*')})
70
+ cmd.payload.include?("\ufffd").should be_true
71
+ end
72
+
73
+ describe "Debug log testing of bad data" do
74
+ let!(:existing_log_level){ Puppet[:log_level]}
75
+
76
+ before :each do
77
+ Puppet[:log_level] = "debug"
78
+ end
79
+
80
+ after :each do
81
+ Puppet[:log_level] = "notice"
82
+ end
83
+
84
+ it "should warn when a command payload includes non-ascii UTF-8 characters" do
85
+ Puppet.expects(:warning).with do |msg|
86
+ msg =~ /Error encoding a 'command-1' command for host 'foo.localdomain' ignoring invalid UTF-8 byte sequences/
87
+ end
88
+ Puppet.expects(:debug).with do |msg|
89
+ msg =~ /Error encoding a 'command-1' command for host 'foo.localdomain'/ &&
90
+ msg =~ Regexp.new(Regexp.quote('"command":"command-1","version":1,"payload":{"foo"')) &&
91
+ msg =~ /1 invalid\/undefined/
92
+ end
93
+ cmd = described_class.new("command-1", 1, "foo.localdomain", {"foo" => [192].pack('c*')})
94
+ cmd.payload.include?("\ufffd").should be_true
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,227 @@
1
+ require 'spec_helper'
2
+ require 'puppet/util/puppetdb/config'
3
+ require 'puppet/util/puppetdb/command_names'
4
+
5
+ # Create a local copy of these constants so that we don't have to refer to them
6
+ # by their full namespaced name
7
+ CommandReplaceCatalog = Puppet::Util::Puppetdb::CommandNames::CommandReplaceCatalog
8
+ CommandReplaceFacts = Puppet::Util::Puppetdb::CommandNames::CommandReplaceFacts
9
+ CommandStoreReport = Puppet::Util::Puppetdb::CommandNames::CommandStoreReport
10
+
11
+ describe Puppet::Util::Puppetdb::Config do
12
+ describe "#load" do
13
+ let(:confdir) do
14
+ temp = Tempfile.new('confdir')
15
+ path = temp.path
16
+ temp.close!
17
+ Dir.mkdir(path)
18
+ path
19
+ end
20
+
21
+ before :each do
22
+ Puppet[:confdir] = confdir
23
+ end
24
+
25
+ after :each do
26
+ FileUtils.rm_rf(confdir)
27
+ end
28
+
29
+ describe "with no config file" do
30
+ it "should use the default settings" do
31
+ config = described_class.load
32
+ config.server_urls.should == [URI("https://puppetdb:8081")]
33
+ end
34
+
35
+ end
36
+
37
+ describe "with a config file" do
38
+ def write_config(content)
39
+ conf = File.join(confdir, 'puppetdb.conf')
40
+ File.open(conf, 'w') { |file| file.print(content) }
41
+ end
42
+
43
+ it "should not explode if there are comments in the config file" do
44
+ write_config <<CONF
45
+ #this is a comment
46
+ ; so is this
47
+ [main]
48
+ server = main-server
49
+ # yet another comment
50
+ port = 1234
51
+ CONF
52
+ expect { described_class.load }.to_not raise_error
53
+ end
54
+
55
+
56
+ it "should use the config value if specified" do
57
+ write_config <<CONF
58
+ [main]
59
+ server = main-server
60
+ port = 1234
61
+ soft_write_failure = true
62
+ CONF
63
+ config = described_class.load
64
+ config.server_urls.should == [URI("https://main-server:1234")]
65
+ config.soft_write_failure.should be_true
66
+ end
67
+
68
+ it "should use the default if no value is specified" do
69
+ write_config ''
70
+
71
+ config = described_class.load
72
+ config.server_urls.should == [URI("https://puppetdb:8081")]
73
+ config.soft_write_failure.should be_false
74
+ end
75
+
76
+ it "should be insensitive to whitespace" do
77
+ write_config <<CONF
78
+ [main]
79
+ server = main-server
80
+ port = 1234
81
+ CONF
82
+ config = described_class.load
83
+ config.server_urls.should == [URI("https://main-server:1234")]
84
+ end
85
+
86
+ it "should accept valid hostnames" do
87
+ write_config <<CONF
88
+ [main]
89
+ server = foo.example-thing.com
90
+ port = 8081
91
+ CONF
92
+
93
+ config = described_class.load
94
+ config.server_urls.should == [URI("https://foo.example-thing.com:8081")]
95
+ end
96
+
97
+ it "should raise if a setting is outside of a section" do
98
+ write_config 'foo = bar'
99
+
100
+ expect do
101
+ described_class.load
102
+ end.to raise_error(/Setting 'foo = bar' is illegal outside of section/)
103
+ end
104
+
105
+ it "should raise if an illegal line is encountered" do
106
+ write_config 'foo bar baz'
107
+
108
+ expect do
109
+ described_class.load
110
+ end.to raise_error(/Unparseable line 'foo bar baz'/)
111
+ end
112
+
113
+ it "should accept a single url" do
114
+ write_config <<CONF
115
+ [main]
116
+ server_urls = https://foo.something-different.com:8080
117
+ CONF
118
+
119
+ config = described_class.load
120
+ config.server_urls.should == [URI("https://foo.something-different.com:8080")]
121
+ end
122
+
123
+ it "should accept multiple urls" do
124
+ write_config <<CONF
125
+ [main]
126
+ server = foo.example-thing.com
127
+ port = 8081
128
+ server_urls = https://foo.something-different.com,https://bar.example-thing.com:8989
129
+ CONF
130
+
131
+ config = described_class.load
132
+ config.server_urls.should == [URI("https://foo.something-different.com"), URI("https://bar.example-thing.com:8989")]
133
+ end
134
+
135
+ it "should fail if given an http URL" do
136
+ write_config <<CONF
137
+ [main]
138
+ server_urls = http://foo.something-different.com
139
+ CONF
140
+
141
+ expect do
142
+ config = described_class.load
143
+ end.to raise_error(/PuppetDB 'server_urls' must be https, found 'http:\/\/foo.something-different.com'/)
144
+ end
145
+
146
+ it "should fail if given a URL path" do
147
+ uri_string = ""
148
+ write_config <<CONF
149
+ [main]
150
+ server_urls = https://foo.something-different.com/bar
151
+ CONF
152
+
153
+ expect do
154
+ config = described_class.load
155
+ end.to raise_error(/PuppetDB 'server_urls' cannot contain URL paths, found 'https:\/\/foo.something-different.com\/bar'/)
156
+ end
157
+
158
+ it "should fail if given a server/port combo" do
159
+ write_config <<CONF
160
+ [main]
161
+ server_urls = foo.com:8080
162
+ CONF
163
+
164
+ expect do
165
+ config = described_class.load
166
+ end.to raise_error(/PuppetDB 'server_urls' must be https, found 'foo.com:8080'/)
167
+ end
168
+
169
+ it "should fail if given a server only" do
170
+ write_config <<CONF
171
+ [main]
172
+ server_urls = foo.com
173
+ CONF
174
+
175
+ expect do
176
+ config = described_class.load
177
+ end.to raise_error(/PuppetDB 'server_urls' must be https, found 'foo.com'/)
178
+ end
179
+
180
+
181
+ it "should fail if given an invalid hostname" do
182
+ write_config <<CONF
183
+ [main]
184
+ server_urls = https://invalid_host_name.com
185
+ CONF
186
+
187
+ expect do
188
+ config = described_class.load
189
+ end.to raise_error(/Error parsing URL 'https:\/\/invalid_host_name.com' in PuppetDB 'server_urls'/)
190
+ end
191
+
192
+ it "should fail if given an unparsable second URI" do
193
+ write_config <<CONF
194
+ [main]
195
+ server_urls = https://foo.com,https://invalid_host_name.com
196
+ CONF
197
+
198
+ expect do
199
+ config = described_class.load
200
+ end.to raise_error(/Error parsing URL 'https:\/\/invalid_host_name.com' in PuppetDB 'server_urls'/)
201
+ end
202
+
203
+ it "should fail if given an unparsable second URI" do
204
+ write_config <<CONF
205
+ [main]
206
+ server_urls = https://foo.com,https://invalid_host_name.com
207
+ CONF
208
+
209
+ expect do
210
+ config = described_class.load
211
+ end.to raise_error(/Error parsing URL 'https:\/\/invalid_host_name.com' in PuppetDB 'server_urls'/)
212
+ end
213
+
214
+ it "should tolerate spaces between URLs" do
215
+ write_config <<CONF
216
+ [main]
217
+ server_urls = https://foo.something-different.com , https://bar.example-thing.com:8989, https://baz.example-thing.com:8989
218
+ CONF
219
+
220
+ config = described_class.load
221
+ config.server_urls.should == [URI("https://foo.something-different.com"),
222
+ URI("https://bar.example-thing.com:8989"),
223
+ URI("https://baz.example-thing.com:8989")]
224
+ end
225
+ end
226
+ end
227
+ end
@@ -0,0 +1,138 @@
1
+ require 'spec_helper'
2
+ require 'puppet/util/puppetdb/http'
3
+
4
+ describe Puppet::Util::Puppetdb::Http do
5
+
6
+ describe "#concat_url_snippets" do
7
+ it "should avoid a double slash" do
8
+ described_class.concat_url_snippets('/foo/', '/bar/').should == '/foo/bar/'
9
+ end
10
+ it "should add a slash if needed" do
11
+ described_class.concat_url_snippets('/foo', 'bar/').should == '/foo/bar/'
12
+ described_class.concat_url_snippets('/foo', 'bar/').should == '/foo/bar/'
13
+ end
14
+ it "should do the right thing if only one snippet has a slash" do
15
+ described_class.concat_url_snippets('/foo', '/bar/').should == '/foo/bar/'
16
+ described_class.concat_url_snippets('/foo/', 'bar/').should == '/foo/bar/'
17
+ end
18
+ end
19
+
20
+ before :each do
21
+ Puppet::Util::Puppetdb.stubs(:config).returns config
22
+ end
23
+
24
+ let(:config) do
25
+ config = stub 'config'
26
+ config.stubs(:server_urls).returns [URI("https://server1:8080/foo"), URI("https://server2:8181/bar")]
27
+ config.stubs(:server_url_timeout).returns 30
28
+ config.stubs(:server_url_config?).returns false
29
+ config
30
+ end
31
+ let(:http1) {stub 'http'}
32
+ let(:http2) {stub 'http'}
33
+
34
+
35
+ describe "#action" do
36
+ it "call the proc argument with a correct path" do
37
+
38
+ Puppet::Network::HttpPool.expects(:http_instance).returns(http1)
39
+ http1.expects(:get).with("/foo/bar/baz", {}).returns Net::HTTPOK.new('1.1', 200, 'OK')
40
+
41
+ described_class.action("/bar/baz") do |http_instance, path|
42
+ http_instance.get(path, {})
43
+ end
44
+ end
45
+
46
+ it "should not fail over when the first url works" do
47
+ Puppet::Network::HttpPool.expects(:http_instance).with("server1", 8080).returns(http1)
48
+ Puppet::Network::HttpPool.expects(:http_instance).with("server2", 8181).never
49
+
50
+ http1.expects(:get).with("/foo/baz", {}).returns Net::HTTPOK.new('1.1', 200, 'OK')
51
+
52
+ response = described_class.action("/baz") do |http_instance, path|
53
+ http_instance.get(path, {})
54
+ end
55
+
56
+ response.code.should == 200
57
+ response.message.should == 'OK'
58
+
59
+ end
60
+
61
+
62
+ it "fails over to the next url when it can't connect" do
63
+ Puppet::Network::HttpPool.expects(:http_instance).with("server1", 8080).returns(http1)
64
+ Puppet::Network::HttpPool.expects(:http_instance).with("server2", 8181).returns(http2)
65
+
66
+ http1.expects(:get).with("/foo/baz", {}).raises SystemCallError, "Connection refused"
67
+ http2.expects(:get).with("/bar/baz", {}).returns Net::HTTPOK.new('1.1', 200, 'OK')
68
+
69
+ response = described_class.action("/baz") do |http_instance, path|
70
+ http_instance.get(path, {})
71
+ end
72
+
73
+ response.code.should == 200
74
+ response.message.should == 'OK'
75
+
76
+ end
77
+
78
+ it "fails over to the next url on a server error" do
79
+ Puppet::Network::HttpPool.expects(:http_instance).with("server1", 8080).returns(http1)
80
+ Puppet::Network::HttpPool.expects(:http_instance).with("server2", 8181).returns(http2)
81
+
82
+ http1.expects(:get).with("/foo/baz", {}).returns Net::HTTPServiceUnavailable.new('1.1', 503, "Unavailable")
83
+ http2.expects(:get).with("/bar/baz", {}).returns Net::HTTPOK.new('1.1', 200, 'OK')
84
+
85
+ response = described_class.action("/baz") do |http_instance, path|
86
+ http_instance.get(path, {})
87
+ end
88
+
89
+ response.code.should == 200
90
+ response.message.should == 'OK'
91
+ end
92
+
93
+ it "raises an exception when all urls fail" do
94
+ Puppet::Network::HttpPool.expects(:http_instance).with("server1", 8080).returns(http1)
95
+ Puppet::Network::HttpPool.expects(:http_instance).with("server2", 8181).returns(http2)
96
+
97
+ http1.expects(:get).with("/foo/baz", {}).returns Net::HTTPServiceUnavailable.new('1.1', 503, "Unavailable")
98
+ http2.expects(:get).with("/bar/baz", {}).returns Net::HTTPServiceUnavailable.new('1.1', 503, "Unavailable")
99
+
100
+
101
+ expect {
102
+ described_class.action("/baz") do |http_instance, path|
103
+ http_instance.get(path, {})
104
+ end
105
+ }.to raise_error Puppet::Error, /Failed to execute/
106
+ end
107
+
108
+ it "fails over after IOError" do
109
+ Puppet::Network::HttpPool.expects(:http_instance).with("server1", 8080).returns(http1)
110
+ Puppet::Network::HttpPool.expects(:http_instance).with("server2", 8181).returns(http2)
111
+
112
+ http1.expects(:get).with("/foo/baz", {}).raises IOError
113
+ http2.expects(:get).with("/bar/baz", {}).returns Net::HTTPOK.new('1.1', 200, 'OK')
114
+
115
+ response = described_class.action("/baz") do |http_instance, path|
116
+ http_instance.get(path, {})
117
+ end
118
+
119
+ response.code.should == 200
120
+ response.message.should == 'OK'
121
+ end
122
+
123
+ it "times out and rolls to the next url" do
124
+ Puppet::Network::HttpPool.expects(:http_instance).with("server1", 8080).returns(http1)
125
+ Puppet::Network::HttpPool.expects(:http_instance).with("server2", 8181).returns(http2)
126
+
127
+ http1.expects(:get).with("/foo/baz", {}).raises Timeout::Error
128
+ http2.expects(:get).with("/bar/baz", {}).returns Net::HTTPOK.new('1.1', 200, 'OK')
129
+
130
+ response = described_class.action("/baz") do |http_instance, path|
131
+ http_instance.get(path, {})
132
+ end
133
+
134
+ response.code.should == 200
135
+ response.message.should == 'OK'
136
+ end
137
+ end
138
+ end