beaker 1.3.1 → 1.3.2
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.
- checksums.yaml +9 -9
- data/.travis.yml +3 -0
- data/beaker.gemspec +4 -2
- data/lib/beaker.rb +1 -1
- data/lib/beaker/cli.rb +1 -3
- data/lib/beaker/dsl/helpers.rb +4 -1
- data/lib/beaker/dsl/install_utils.rb +27 -16
- data/lib/beaker/hypervisor/vagrant.rb +23 -1
- data/lib/beaker/version.rb +5 -0
- data/spec/beaker/dsl/helpers_spec.rb +18 -18
- data/spec/beaker/dsl/install_utils_spec.rb +2 -5
- data/spec/beaker/hypervisor/vagrant_spec.rb +28 -0
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
Y2M3OTY2ZTdlMDEyYzA1YWIyMmUwN2VlMzlkYzdlNzI5MzQzNjQ1YQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
7
|
-
|
6
|
+
OGQ4MTNjYWJlNDExMGI0ODliMDY1OGJkOGI0YTY0NjQzOWY2MTQ4ZQ==
|
7
|
+
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NDJmNTcwMjFjYzhjNDM4MzQ5OTY1OTdhYWNkN2IzYTI2NjIzZjZmNjEzZjky
|
10
|
+
ODM3YjVjMWM5ZmFmY2RhNWIyZGQxZjI4OTY4YzJmZDk1OTM3NzVjY2Q5Mjhl
|
11
|
+
MzYwMTlmZjkyZTQxYWNjYzQyNWUzNTQwNGY1Yjc2YjNjNTFlMWY=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
OTA4MDAxMjhhNTY0OTYzZjRjZjViYTNiNjlhOTZkOTUxMDZmM2YxNGVkYzYy
|
14
|
+
NTg2NDA2MTM4OWUzNTRjZWI1NjBlMTFiNWI3YjQyNzM2MzcwN2YwMDc1Mzg5
|
15
|
+
YTkwNGI1ODQ0ODMyMTdiYzY1N2ZkZWZiNzdiNzIzMmE0MTA5NjA=
|
data/.travis.yml
CHANGED
data/beaker.gemspec
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
3
|
+
require 'beaker/version'
|
2
4
|
|
3
5
|
Gem::Specification.new do |s|
|
4
6
|
s.name = "beaker"
|
5
|
-
s.version =
|
7
|
+
s.version = Beaker::Version::STRING
|
6
8
|
s.authors = ["Puppetlabs"]
|
7
9
|
s.email = ["delivery@puppetlabs.com"]
|
8
10
|
s.homepage = "https://github.com/puppetlabs/beaker"
|
@@ -33,7 +35,7 @@ Gem::Specification.new do |s|
|
|
33
35
|
s.add_runtime_dependency 'inifile', '~> 2.0'
|
34
36
|
|
35
37
|
# Optional provisioner specific support
|
36
|
-
s.add_runtime_dependency 'rbvmomi', '1.
|
38
|
+
s.add_runtime_dependency 'rbvmomi', '1.8.1'
|
37
39
|
s.add_runtime_dependency 'blimpy', '~> 0.6'
|
38
40
|
s.add_runtime_dependency 'fission', '~> 0.4'
|
39
41
|
|
data/lib/beaker.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'rubygems' unless defined?(Gem)
|
2
2
|
module Beaker
|
3
3
|
|
4
|
-
%w( utils test_suite result command options network_manager cli ).each do |lib|
|
4
|
+
%w( version utils test_suite result command options network_manager cli ).each do |lib|
|
5
5
|
begin
|
6
6
|
require "beaker/#{lib}"
|
7
7
|
rescue LoadError
|
data/lib/beaker/cli.rb
CHANGED
@@ -21,9 +21,7 @@ module Beaker
|
|
21
21
|
exit
|
22
22
|
end
|
23
23
|
if @options[:version]
|
24
|
-
|
25
|
-
spec = Gem::Specification::load(GEMSPEC)
|
26
|
-
@logger.notify(VERSION_STRING % spec.version)
|
24
|
+
@logger.notify(VERSION_STRING % Beaker::Version::STRING)
|
27
25
|
exit
|
28
26
|
end
|
29
27
|
@logger.info(@options.dump)
|
data/lib/beaker/dsl/helpers.rb
CHANGED
@@ -669,7 +669,7 @@ module Beaker
|
|
669
669
|
# validation, etc.
|
670
670
|
#
|
671
671
|
def apply_manifest_on(host, manifest, opts = {}, &block)
|
672
|
-
on_options = {
|
672
|
+
on_options = {}
|
673
673
|
on_options[:acceptable_exit_codes] = Array(opts.delete(:acceptable_exit_codes))
|
674
674
|
args = ["--verbose"]
|
675
675
|
args << "--parseonly" if opts[:parseonly]
|
@@ -714,6 +714,9 @@ module Beaker
|
|
714
714
|
args << { :environment => opts[:environment]}
|
715
715
|
end
|
716
716
|
|
717
|
+
file_path = "/tmp/apply_manifest.#{rand(1000000000).to_s}.pp"
|
718
|
+
create_remote_file(host, file_path, manifest + "\n")
|
719
|
+
args << file_path
|
717
720
|
on host, puppet( 'apply', *args), on_options, &block
|
718
721
|
end
|
719
722
|
|
@@ -12,7 +12,7 @@ module Beaker
|
|
12
12
|
SourcePath = "/opt/puppet-git-repos"
|
13
13
|
|
14
14
|
# A regex to know if the uri passed is pointing to a git repo
|
15
|
-
GitURI = %r{^(git|https?|file)://|^git@}
|
15
|
+
GitURI = %r{^(git|https?|file)://|^git@|^gitmirror@}
|
16
16
|
|
17
17
|
# Github's ssh signature for cloning via ssh
|
18
18
|
GitHubSig = 'github.com,207.97.227.239 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ=='
|
@@ -160,9 +160,12 @@ module Beaker
|
|
160
160
|
# @api private
|
161
161
|
def link_exists?(link)
|
162
162
|
require "net/http"
|
163
|
+
require "net/https"
|
163
164
|
require "open-uri"
|
164
165
|
url = URI.parse(link)
|
165
|
-
Net::HTTP.
|
166
|
+
http = Net::HTTP.new(url.host, url.port)
|
167
|
+
http.use_ssl = (url.scheme == 'https')
|
168
|
+
http.start do |http|
|
166
169
|
return http.head(url.request_uri).code == "200"
|
167
170
|
end
|
168
171
|
end
|
@@ -202,21 +205,29 @@ module Beaker
|
|
202
205
|
end
|
203
206
|
end
|
204
207
|
if local
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
208
|
+
if not File.exists?("#{path}/#{filename}#{extension}")
|
209
|
+
raise "attempting installation on #{host}, #{path}/#{filename}#{extension} does not exist"
|
210
|
+
end
|
211
|
+
scp_to host, "#{path}/#{filename}#{extension}", "#{host['working_dir']}/#{filename}#{extension}"
|
212
|
+
if extension =~ /gz/
|
213
|
+
on host, "cd #{host['working_dir']}; gunzip #{filename}#{extension}"
|
214
|
+
end
|
215
|
+
if extension =~ /tar/
|
216
|
+
on host, "cd #{host['working_dir']}; tar -xvf #{filename}.tar"
|
217
|
+
end
|
209
218
|
else
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
219
|
+
if not link_exists?("#{path}/#{filename}#{extension}")
|
220
|
+
raise "attempting installation on #{host}, #{path}/#{filename}#{extension} does not exist"
|
221
|
+
end
|
222
|
+
gunzip = ""
|
223
|
+
untar = ""
|
224
|
+
if extension =~ /gz/
|
225
|
+
gunzip = "| gunzip"
|
226
|
+
end
|
227
|
+
if extension =~ /tar/
|
228
|
+
untar = "| tar -xvf -"
|
229
|
+
end
|
230
|
+
on host, "cd #{host['working_dir']}; curl #{path}/#{filename}#{extension} #{gunzip} #{untar}"
|
220
231
|
end
|
221
232
|
end
|
222
233
|
end
|
@@ -29,7 +29,7 @@ module Beaker
|
|
29
29
|
v_file << " v.vm.box = '#{host['box']}'\n"
|
30
30
|
v_file << " v.vm.box_url = '#{host['box_url']}'\n" unless host['box_url'].nil?
|
31
31
|
v_file << " v.vm.base_mac = '#{randmac}'\n"
|
32
|
-
v_file << " v.vm.network :private_network, ip: \"#{host['ip'].to_s}\", :netmask => \"255.255.0.0\"\n"
|
32
|
+
v_file << " v.vm.network :private_network, ip: \"#{host['ip'].to_s}\", :netmask => \"#{host['netmask'] ||= "255.255.0.0"}\"\n"
|
33
33
|
v_file << " end\n"
|
34
34
|
@logger.debug "created Vagrantfile for VagrantHost #{host.name}"
|
35
35
|
end
|
@@ -82,6 +82,23 @@ module Beaker
|
|
82
82
|
@temp_files << f
|
83
83
|
end
|
84
84
|
|
85
|
+
def get_ip_from_vagrant_file(hostname)
|
86
|
+
ip = ''
|
87
|
+
if File.file?(@vagrant_file) #we should have a vagrant file available to us for reading
|
88
|
+
f = File.read(@vagrant_file)
|
89
|
+
m = /#{hostname}.*?ip:\s*('|")\s*([^'"]+)('|")/m.match(f)
|
90
|
+
if m
|
91
|
+
ip = m[2]
|
92
|
+
@logger.debug("Determined existing vagrant box #{hostname} ip to be: #{ip} ")
|
93
|
+
else
|
94
|
+
raise("Unable to determine ip for vagrant box #{hostname}")
|
95
|
+
end
|
96
|
+
else
|
97
|
+
raise("No vagrant file found (should be located at #{@vagrant_file})")
|
98
|
+
end
|
99
|
+
ip
|
100
|
+
end
|
101
|
+
|
85
102
|
def initialize(vagrant_hosts, options)
|
86
103
|
require 'tempfile'
|
87
104
|
@options = options
|
@@ -103,7 +120,12 @@ module Beaker
|
|
103
120
|
make_vfile @vagrant_hosts
|
104
121
|
|
105
122
|
vagrant_cmd("up")
|
123
|
+
else #set host ip of already up boxes
|
124
|
+
@vagrant_hosts.each do |host|
|
125
|
+
host[:ip] = get_ip_from_vagrant_file(host.name)
|
126
|
+
end
|
106
127
|
end
|
128
|
+
|
107
129
|
@logger.debug "configure vagrant boxes (set ssh-config, switch to root user, hack etc/hosts)"
|
108
130
|
@vagrant_hosts.each do |host|
|
109
131
|
default_user = host['user']
|
@@ -298,26 +298,26 @@ describe ClassMixedWithDSLHelpers do
|
|
298
298
|
|
299
299
|
describe '#apply_manifest_on' do
|
300
300
|
it 'calls puppet' do
|
301
|
+
subject.should_receive( :create_remote_file ).and_return( true )
|
301
302
|
subject.should_receive( :puppet ).
|
302
|
-
with( 'apply', '--verbose').
|
303
|
+
with( 'apply', '--verbose', /apply_manifest.\d+.pp/ ).
|
303
304
|
and_return( 'puppet_command' )
|
304
305
|
|
305
306
|
subject.should_receive( :on ).
|
306
307
|
with( 'my_host', 'puppet_command',
|
307
|
-
:acceptable_exit_codes => [0]
|
308
|
-
:stdin => "class { \"boo\": }\n" )
|
308
|
+
:acceptable_exit_codes => [0] )
|
309
309
|
|
310
310
|
subject.apply_manifest_on( 'my_host', 'class { "boo": }')
|
311
311
|
end
|
312
312
|
it 'adds acceptable exit codes with :catch_failures' do
|
313
|
+
subject.should_receive( :create_remote_file ).and_return( true )
|
313
314
|
subject.should_receive( :puppet ).
|
314
|
-
with( 'apply', '--verbose', '--trace', '--detailed-exitcodes' ).
|
315
|
+
with( 'apply', '--verbose', '--trace', '--detailed-exitcodes', /apply_manifest.\d+.pp/ ).
|
315
316
|
and_return( 'puppet_command' )
|
316
317
|
|
317
318
|
subject.should_receive( :on ).
|
318
319
|
with( 'my_host', 'puppet_command',
|
319
|
-
:acceptable_exit_codes => [0,2]
|
320
|
-
:stdin => "class { \"boo\": }\n" )
|
320
|
+
:acceptable_exit_codes => [0,2] )
|
321
321
|
|
322
322
|
subject.apply_manifest_on( 'my_host',
|
323
323
|
'class { "boo": }',
|
@@ -325,14 +325,14 @@ describe ClassMixedWithDSLHelpers do
|
|
325
325
|
:catch_failures => true )
|
326
326
|
end
|
327
327
|
it 'allows acceptable exit codes through :catch_failures' do
|
328
|
+
subject.should_receive( :create_remote_file ).and_return( true )
|
328
329
|
subject.should_receive( :puppet ).
|
329
|
-
with( 'apply', '--verbose', '--trace', '--detailed-exitcodes' ).
|
330
|
+
with( 'apply', '--verbose', '--trace', '--detailed-exitcodes', /apply_manifest.\d+.pp/ ).
|
330
331
|
and_return( 'puppet_command' )
|
331
332
|
|
332
333
|
subject.should_receive( :on ).
|
333
334
|
with( 'my_host', 'puppet_command',
|
334
|
-
:acceptable_exit_codes => [4,0,2]
|
335
|
-
:stdin => "class { \"boo\": }\n" )
|
335
|
+
:acceptable_exit_codes => [4,0,2] )
|
336
336
|
|
337
337
|
subject.apply_manifest_on( 'my_host',
|
338
338
|
'class { "boo": }',
|
@@ -341,15 +341,15 @@ describe ClassMixedWithDSLHelpers do
|
|
341
341
|
:catch_failures => true )
|
342
342
|
end
|
343
343
|
it 'enforces a 0 exit code through :catch_changes' do
|
344
|
+
subject.should_receive( :create_remote_file ).and_return( true )
|
344
345
|
subject.should_receive( :puppet ).
|
345
|
-
with( 'apply', '--verbose', '--trace', '--detailed-exitcodes' ).
|
346
|
+
with( 'apply', '--verbose', '--trace', '--detailed-exitcodes', /apply_manifest.\d+.pp/ ).
|
346
347
|
and_return( 'puppet_command' )
|
347
348
|
|
348
349
|
subject.should_receive( :on ).with(
|
349
350
|
'my_host',
|
350
351
|
'puppet_command',
|
351
|
-
:acceptable_exit_codes => [0]
|
352
|
-
:stdin => "class { \"boo\": }\n"
|
352
|
+
:acceptable_exit_codes => [0]
|
353
353
|
)
|
354
354
|
|
355
355
|
subject.apply_manifest_on(
|
@@ -360,15 +360,15 @@ describe ClassMixedWithDSLHelpers do
|
|
360
360
|
)
|
361
361
|
end
|
362
362
|
it 'enforces exit codes through :expect_failures' do
|
363
|
+
subject.should_receive( :create_remote_file ).and_return( true )
|
363
364
|
subject.should_receive( :puppet ).
|
364
|
-
with( 'apply', '--verbose', '--trace', '--detailed-exitcodes' ).
|
365
|
+
with( 'apply', '--verbose', '--trace', '--detailed-exitcodes', /apply_manifest.\d+.pp/ ).
|
365
366
|
and_return( 'puppet_command' )
|
366
367
|
|
367
368
|
subject.should_receive( :on ).with(
|
368
369
|
'my_host',
|
369
370
|
'puppet_command',
|
370
|
-
:acceptable_exit_codes => [1,4,6]
|
371
|
-
:stdin => "class { \"boo\": }\n"
|
371
|
+
:acceptable_exit_codes => [1,4,6]
|
372
372
|
)
|
373
373
|
|
374
374
|
subject.apply_manifest_on(
|
@@ -390,15 +390,15 @@ describe ClassMixedWithDSLHelpers do
|
|
390
390
|
}.to raise_error ArgumentError, /catch_failures.+expect_failures/
|
391
391
|
end
|
392
392
|
it 'enforces added exit codes through :expect_failures' do
|
393
|
+
subject.should_receive( :create_remote_file ).and_return( true )
|
393
394
|
subject.should_receive( :puppet ).
|
394
|
-
with( 'apply', '--verbose', '--trace', '--detailed-exitcodes' ).
|
395
|
+
with( 'apply', '--verbose', '--trace', '--detailed-exitcodes', /apply_manifest.\d+.pp/ ).
|
395
396
|
and_return( 'puppet_command' )
|
396
397
|
|
397
398
|
subject.should_receive( :on ).with(
|
398
399
|
'my_host',
|
399
400
|
'puppet_command',
|
400
|
-
:acceptable_exit_codes => [1,2,3,4,5,6]
|
401
|
-
:stdin => "class { \"boo\": }\n"
|
401
|
+
:acceptable_exit_codes => [1,2,3,4,5,6]
|
402
402
|
)
|
403
403
|
|
404
404
|
subject.apply_manifest_on(
|
@@ -166,8 +166,7 @@ describe ClassMixedWithDSLInstallUtils do
|
|
166
166
|
path = unixhost['pe_dir']
|
167
167
|
filename = "#{ unixhost['dist'] }"
|
168
168
|
extension = '.tar'
|
169
|
-
subject.should_receive( :on ).with( unixhost, "cd #{ unixhost['working_dir'] }; curl #{ path }/#{ filename }#{ extension } -
|
170
|
-
subject.should_receive( :on ).with( unixhost, /tar -xvf/ ).once
|
169
|
+
subject.should_receive( :on ).with( unixhost, "cd #{ unixhost['working_dir'] }; curl #{ path }/#{ filename }#{ extension } | tar -xvf -" ).once
|
171
170
|
subject.fetch_puppet( [unixhost], {} )
|
172
171
|
end
|
173
172
|
|
@@ -180,9 +179,7 @@ describe ClassMixedWithDSLInstallUtils do
|
|
180
179
|
path = unixhost['pe_dir']
|
181
180
|
filename = "#{ unixhost['dist'] }"
|
182
181
|
extension = '.tar.gz'
|
183
|
-
subject.should_receive( :on ).with( unixhost, "cd #{ unixhost['working_dir'] }; curl #{ path }/#{ filename }#{ extension }
|
184
|
-
subject.should_receive( :on ).with( unixhost, /gunzip/ ).once
|
185
|
-
subject.should_receive( :on ).with( unixhost, /tar -xvf/ ).once
|
182
|
+
subject.should_receive( :on ).with( unixhost, "cd #{ unixhost['working_dir'] }; curl #{ path }/#{ filename }#{ extension } | gunzip | tar -xvf -" ).once
|
186
183
|
subject.fetch_puppet( [unixhost], {} )
|
187
184
|
end
|
188
185
|
|
@@ -96,6 +96,34 @@ module Beaker
|
|
96
96
|
|
97
97
|
end
|
98
98
|
|
99
|
+
describe "get_ip_from_vagrant_file" do
|
100
|
+
before :each do
|
101
|
+
FakeFS.activate!
|
102
|
+
vagrant.stub( :randmac ).and_return( "0123456789" )
|
103
|
+
vagrant.make_vfile( @hosts )
|
104
|
+
end
|
105
|
+
|
106
|
+
it "can find the correct ip for the provided hostname" do
|
107
|
+
@hosts.each do |host|
|
108
|
+
expect( vagrant.get_ip_from_vagrant_file(host.name) ).to be === host[:ip]
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
it "raises an error if it is unable to find an ip" do
|
114
|
+
expect{ vagrant.get_ip_from_vagrant_file("unknown") }.to raise_error
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
it "raises an error if no Vagrantfile is present" do
|
119
|
+
File.delete( vagrant.instance_variable_get( :@vagrant_file ) )
|
120
|
+
@hosts.each do |host|
|
121
|
+
expect{ vagrant.get_ip_from_vagrant_file(host.name) }.to raise_error
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
99
127
|
describe "provisioning and cleanup" do
|
100
128
|
|
101
129
|
before :each do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: beaker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppetlabs
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-01-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -170,14 +170,14 @@ dependencies:
|
|
170
170
|
requirements:
|
171
171
|
- - '='
|
172
172
|
- !ruby/object:Gem::Version
|
173
|
-
version:
|
173
|
+
version: 1.8.1
|
174
174
|
type: :runtime
|
175
175
|
prerelease: false
|
176
176
|
version_requirements: !ruby/object:Gem::Requirement
|
177
177
|
requirements:
|
178
178
|
- - '='
|
179
179
|
- !ruby/object:Gem::Version
|
180
|
-
version:
|
180
|
+
version: 1.8.1
|
181
181
|
- !ruby/object:Gem::Dependency
|
182
182
|
name: blimpy
|
183
183
|
requirement: !ruby/object:Gem::Requirement
|
@@ -335,6 +335,7 @@ files:
|
|
335
335
|
- lib/beaker/utils/repo_control.rb
|
336
336
|
- lib/beaker/utils/setup_helper.rb
|
337
337
|
- lib/beaker/utils/validator.rb
|
338
|
+
- lib/beaker/version.rb
|
338
339
|
- spec/beaker/answers_spec.rb
|
339
340
|
- spec/beaker/command_spec.rb
|
340
341
|
- spec/beaker/dsl/assertions_spec.rb
|
@@ -407,7 +408,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
407
408
|
version: '0'
|
408
409
|
requirements: []
|
409
410
|
rubyforge_project:
|
410
|
-
rubygems_version: 2.
|
411
|
+
rubygems_version: 2.2.1
|
411
412
|
signing_key:
|
412
413
|
specification_version: 4
|
413
414
|
summary: Let's test Puppet!
|