chef 12.19.36 → 12.20.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/chef/client.rb +45 -0
- data/lib/chef/knife/data_bag_create.rb +6 -4
- data/lib/chef/provider/apt_repository.rb +5 -3
- data/lib/chef/version.rb +1 -1
- data/spec/functional/resource/link_spec.rb +3 -3
- data/spec/support/shared/context/client.rb +7 -0
- data/spec/unit/client_spec.rb +49 -0
- data/spec/unit/knife/data_bag_create_spec.rb +65 -45
- data/spec/unit/provider/apt_repository_spec.rb +33 -27
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b76fcc06d56bcab5fc551164c0522fdc88252c06
|
4
|
+
data.tar.gz: 734f7fda8fb0ea35b678ed60fd91aab39a7ee834
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce495ae0e26ca2d7eeef5c7bce3e4e81a569a70149150e468cf87cbde16611fde6f7b44e3a82cbfc53d00f8aa8a67d46bbffa5bfe7c254878b3b06e3342c462a
|
7
|
+
data.tar.gz: 5aaaf3599b394c59041bcbcac810e8df7ff51afbee519bbecc7622de3c7b30650d6e1a14cf2a42b2dc7e974ca7b08205c94f5e98c1af65b41a21508725d6022f
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
12.
|
1
|
+
12.20.3
|
data/lib/chef/client.rb
CHANGED
@@ -280,6 +280,8 @@ class Chef
|
|
280
280
|
|
281
281
|
run_context = setup_run_context
|
282
282
|
|
283
|
+
load_required_recipe(@rest, run_context) unless Chef::Config[:solo_legacy_mode]
|
284
|
+
|
283
285
|
if Chef::Config[:audit_mode] != :audit_only
|
284
286
|
converge_error = converge_and_save(run_context)
|
285
287
|
end
|
@@ -514,6 +516,49 @@ class Chef
|
|
514
516
|
run_context
|
515
517
|
end
|
516
518
|
|
519
|
+
#
|
520
|
+
# Adds a required recipe as specified by the Chef Server
|
521
|
+
#
|
522
|
+
# @return The modified run context
|
523
|
+
#
|
524
|
+
# @api private
|
525
|
+
#
|
526
|
+
# TODO: @rest doesn't appear to be used anywhere outside
|
527
|
+
# of client.register except for here. If it's common practice
|
528
|
+
# to create your own rest client, perhaps we should do that
|
529
|
+
# here but it seems more appropriate to reuse one that we
|
530
|
+
# know is already created. for ease of testing, we'll pass
|
531
|
+
# the existing rest client in as a parameter
|
532
|
+
#
|
533
|
+
def load_required_recipe(rest, run_context)
|
534
|
+
required_recipe_contents = rest.get("required_recipe")
|
535
|
+
Chef::Log.info("Required Recipe found, loading it")
|
536
|
+
Chef::FileCache.store("required_recipe", required_recipe_contents)
|
537
|
+
required_recipe_file = Chef::FileCache.load("required_recipe", false)
|
538
|
+
|
539
|
+
# TODO: add integration tests with resource reporting turned on
|
540
|
+
# (presumably requires changes to chef-zero)
|
541
|
+
#
|
542
|
+
# Chef::Recipe.new takes a cookbook name and a recipe name along
|
543
|
+
# with the run context. These names are eventually used in the
|
544
|
+
# resource reporter, and if the cookbook name cannot be found in the
|
545
|
+
# cookbook collection then we will fail with an exception. Cases where
|
546
|
+
# we currently also fail:
|
547
|
+
# - specific recipes
|
548
|
+
# - chef-apply would fail if resource reporting was enabled
|
549
|
+
#
|
550
|
+
recipe = Chef::Recipe.new(nil, nil, run_context)
|
551
|
+
recipe.from_file(required_recipe_file)
|
552
|
+
run_context
|
553
|
+
rescue Net::HTTPServerException => e
|
554
|
+
case e.response
|
555
|
+
when Net::HTTPNotFound
|
556
|
+
Chef::Log.debug("Required Recipe not configured on the server, skipping it")
|
557
|
+
else
|
558
|
+
raise
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
517
562
|
#
|
518
563
|
# The PolicyBuilder strategy for figuring out run list and cookbooks.
|
519
564
|
#
|
@@ -49,13 +49,15 @@ class Chef
|
|
49
49
|
exit(1)
|
50
50
|
end
|
51
51
|
|
52
|
-
#
|
52
|
+
# Verify if the data bag exists
|
53
53
|
begin
|
54
|
+
rest.get("data/#{@data_bag_name}")
|
55
|
+
ui.info("Data bag #{@data_bag_name} already exists")
|
56
|
+
rescue Net::HTTPServerException => e
|
57
|
+
raise unless e.to_s =~ /^404/
|
58
|
+
# if it doesn't exists, try to create it
|
54
59
|
rest.post("data", { "name" => @data_bag_name })
|
55
60
|
ui.info("Created data_bag[#{@data_bag_name}]")
|
56
|
-
rescue Net::HTTPServerException => e
|
57
|
-
raise unless e.to_s =~ /^409/
|
58
|
-
ui.info("Data bag #{@data_bag_name} already exists")
|
59
61
|
end
|
60
62
|
|
61
63
|
# if an item is specified, create it, as well
|
@@ -115,7 +115,7 @@ class Chef
|
|
115
115
|
so = shell_out(cmd)
|
116
116
|
so.run_command
|
117
117
|
so.stdout.split(/\n/).map do |t|
|
118
|
-
if z = t.match(/^
|
118
|
+
if z = t.match(/^fpr:+([0-9A-F]+):/)
|
119
119
|
z[1].split.join
|
120
120
|
end
|
121
121
|
end.compact
|
@@ -147,8 +147,10 @@ class Chef
|
|
147
147
|
end
|
148
148
|
|
149
149
|
def no_new_keys?(file)
|
150
|
-
|
151
|
-
|
150
|
+
# Now we are using the option --with-colons that works across old os versions
|
151
|
+
# as well as the latest (16.10). This for both `apt-key` and `gpg` commands
|
152
|
+
installed_keys = extract_fingerprints_from_cmd("apt-key adv --list-public-keys --with-fingerprint --with-colons")
|
153
|
+
proposed_keys = extract_fingerprints_from_cmd("gpg --with-fingerprint --with-colons #{file}")
|
152
154
|
(installed_keys & proposed_keys).sort == proposed_keys.sort
|
153
155
|
end
|
154
156
|
|
data/lib/chef/version.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#
|
2
2
|
# Author:: John Keiser (<jkeiser@chef.io>)
|
3
|
-
# Copyright:: Copyright 2011-
|
3
|
+
# Copyright:: Copyright 2011-2017, Chef Software Inc.
|
4
4
|
# License:: Apache License, Version 2.0
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -654,7 +654,7 @@ describe Chef::Resource::Link do
|
|
654
654
|
end
|
655
655
|
context "and the link does not yet exist" do
|
656
656
|
it "links to the target file" do
|
657
|
-
skip("OS X/FreeBSD/AIX symlink? and readlink working on hard links to symlinks") if os_x? || freebsd? || aix?
|
657
|
+
skip("OS X/FreeBSD/AIX/Solaris symlink? and readlink working on hard links to symlinks") if os_x? || freebsd? || aix? || solaris?
|
658
658
|
resource.run_action(:create)
|
659
659
|
expect(File.exists?(target_file)).to be_truthy
|
660
660
|
# OS X gets angry about this sort of link. Bug in OS X, IMO.
|
@@ -673,7 +673,7 @@ describe Chef::Resource::Link do
|
|
673
673
|
end
|
674
674
|
context "and the link does not yet exist" do
|
675
675
|
it "links to the target file" do
|
676
|
-
skip("OS X/FreeBSD/AIX fails to create hardlinks to broken symlinks") if os_x? || freebsd? || aix?
|
676
|
+
skip("OS X/FreeBSD/AIX/Solaris fails to create hardlinks to broken symlinks") if os_x? || freebsd? || aix? || solaris?
|
677
677
|
resource.run_action(:create)
|
678
678
|
expect(File.exists?(target_file) || File.symlink?(target_file)).to be_truthy
|
679
679
|
expect(symlink?(target_file)).to be_truthy
|
@@ -135,6 +135,12 @@ shared_context "a client run" do
|
|
135
135
|
and_return({})
|
136
136
|
end
|
137
137
|
|
138
|
+
def stub_for_required_recipe
|
139
|
+
response = Net::HTTPNotFound.new("1.1", "404", "Not Found")
|
140
|
+
exception = Net::HTTPServerException.new('404 "Not Found"', response)
|
141
|
+
expect(http_node_load).to receive(:get).with("required_recipe").and_raise(exception)
|
142
|
+
end
|
143
|
+
|
138
144
|
def stub_for_converge
|
139
145
|
# define me
|
140
146
|
end
|
@@ -165,6 +171,7 @@ shared_context "a client run" do
|
|
165
171
|
stub_for_data_collector_init
|
166
172
|
stub_for_node_load
|
167
173
|
stub_for_sync_cookbooks
|
174
|
+
stub_for_required_recipe
|
168
175
|
stub_for_converge
|
169
176
|
stub_for_audit
|
170
177
|
stub_for_node_save
|
data/spec/unit/client_spec.rb
CHANGED
@@ -402,6 +402,55 @@ describe Chef::Client do
|
|
402
402
|
end
|
403
403
|
end
|
404
404
|
|
405
|
+
describe "load_required_recipe" do
|
406
|
+
let(:rest) { double("Chef::ServerAPI (required recipe)") }
|
407
|
+
let(:run_context) { double("Chef::RunContext") }
|
408
|
+
let(:recipe) { double("Chef::Recipe (required recipe)") }
|
409
|
+
let(:required_recipe) do
|
410
|
+
<<EOM
|
411
|
+
fake_recipe_variable = "for reals"
|
412
|
+
EOM
|
413
|
+
end
|
414
|
+
|
415
|
+
context "when required_recipe is configured" do
|
416
|
+
|
417
|
+
before(:each) do
|
418
|
+
expect(rest).to receive(:get).with("required_recipe").and_return(required_recipe)
|
419
|
+
expect(Chef::Recipe).to receive(:new).with(nil, nil, run_context).and_return(recipe)
|
420
|
+
expect(recipe).to receive(:from_file)
|
421
|
+
end
|
422
|
+
|
423
|
+
it "fetches the recipe and adds it to the run context" do
|
424
|
+
client.load_required_recipe(rest, run_context)
|
425
|
+
end
|
426
|
+
|
427
|
+
context "when the required_recipe has bad contents" do
|
428
|
+
let(:required_recipe) do
|
429
|
+
<<EOM
|
430
|
+
this is not a recipe
|
431
|
+
EOM
|
432
|
+
end
|
433
|
+
it "should not raise an error" do
|
434
|
+
expect { client.load_required_recipe(rest, run_context) }.not_to raise_error()
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
context "when required_recipe returns 404" do
|
440
|
+
let(:http_response) { Net::HTTPNotFound.new("1.1", "404", "Not Found") }
|
441
|
+
let(:http_exception) { Net::HTTPServerException.new('404 "Not Found"', http_response) }
|
442
|
+
|
443
|
+
before(:each) do
|
444
|
+
expect(rest).to receive(:get).with("required_recipe").and_raise(http_exception)
|
445
|
+
end
|
446
|
+
|
447
|
+
it "should log and continue on" do
|
448
|
+
expect(Chef::Log).to receive(:debug)
|
449
|
+
client.load_required_recipe(rest, run_context)
|
450
|
+
end
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
405
454
|
describe "windows_admin_check" do
|
406
455
|
context "platform is not windows" do
|
407
456
|
before do
|
@@ -46,64 +46,84 @@ describe Chef::Knife::DataBagCreate do
|
|
46
46
|
allow(knife).to receive(:config).and_return(config)
|
47
47
|
end
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
context "when given one argument" do
|
56
|
-
before do
|
57
|
-
knife.name_args = [bag_name]
|
58
|
-
end
|
59
|
-
|
60
|
-
it "creates a data bag" do
|
61
|
-
expect(rest).to receive(:post).with("data", { "name" => bag_name })
|
62
|
-
expect(knife.ui).to receive(:info).with("Created data_bag[#{bag_name}]")
|
49
|
+
context "when data_bag already exist" do
|
50
|
+
it "doesn't creates a data bag" do
|
51
|
+
expect(knife).to receive(:create_object).and_yield(raw_hash)
|
52
|
+
expect(rest).to receive(:get).with("data/#{bag_name}")
|
53
|
+
expect(rest).to_not receive(:post).with("data", { "name" => bag_name })
|
54
|
+
expect(knife.ui).to receive(:info).with("Data bag #{bag_name} already exists")
|
63
55
|
|
64
56
|
knife.run
|
65
57
|
end
|
66
58
|
end
|
67
59
|
|
68
|
-
context "
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
60
|
+
context "when data_bag doesn't exist" do
|
61
|
+
before do
|
62
|
+
# Data bag doesn't exist by default so we mock the GET request to return 404
|
63
|
+
exception = double("404 error", :code => "404")
|
64
|
+
allow(rest).to receive(:get)
|
65
|
+
.with("data/#{bag_name}")
|
66
|
+
.and_raise(Net::HTTPServerException.new("404", exception))
|
73
67
|
end
|
74
68
|
|
75
|
-
it "
|
76
|
-
|
77
|
-
expect(
|
78
|
-
expect
|
79
|
-
expect(rest).to receive(:post).with("data/#{bag_name}", item).ordered
|
80
|
-
|
81
|
-
knife.run
|
69
|
+
it "tries to create a data bag with an invalid name when given one argument" do
|
70
|
+
knife.name_args = ["invalid&char"]
|
71
|
+
expect(Chef::DataBag).to receive(:validate_name!).with(knife.name_args[0]).and_raise(Chef::Exceptions::InvalidDataBagName)
|
72
|
+
expect { knife.run }.to exit_with_code(1)
|
82
73
|
end
|
83
|
-
end
|
84
74
|
|
85
|
-
|
86
|
-
|
75
|
+
context "when given one argument" do
|
76
|
+
before do
|
77
|
+
knife.name_args = [bag_name]
|
78
|
+
end
|
79
|
+
|
80
|
+
it "creates a data bag" do
|
81
|
+
expect(rest).to receive(:post).with("data", { "name" => bag_name })
|
82
|
+
expect(knife.ui).to receive(:info).with("Created data_bag[#{bag_name}]")
|
87
83
|
|
88
|
-
|
89
|
-
|
90
|
-
item.data_bag(bag_name)
|
91
|
-
item
|
84
|
+
knife.run
|
85
|
+
end
|
92
86
|
end
|
93
87
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
88
|
+
context "no secret is specified for encryption" do
|
89
|
+
let(:item) do
|
90
|
+
item = Chef::DataBagItem.from_hash(raw_hash)
|
91
|
+
item.data_bag(bag_name)
|
92
|
+
item
|
93
|
+
end
|
94
|
+
|
95
|
+
it "creates a data bag item" do
|
96
|
+
expect(knife).to receive(:create_object).and_yield(raw_hash)
|
97
|
+
expect(knife).to receive(:encryption_secret_provided?).and_return(false)
|
98
|
+
expect(rest).to receive(:post).with("data", { "name" => bag_name }).ordered
|
99
|
+
expect(rest).to receive(:post).with("data/#{bag_name}", item).ordered
|
100
|
+
|
101
|
+
knife.run
|
102
|
+
end
|
103
|
+
end
|
104
104
|
|
105
|
-
|
105
|
+
context "a secret is specified for encryption" do
|
106
|
+
let(:encoded_data) { Chef::EncryptedDataBagItem.encrypt_data_bag_item(raw_hash, secret) }
|
107
|
+
|
108
|
+
let(:item) do
|
109
|
+
item = Chef::DataBagItem.from_hash(encoded_data)
|
110
|
+
item.data_bag(bag_name)
|
111
|
+
item
|
112
|
+
end
|
113
|
+
|
114
|
+
it "creates an encrypted data bag item" do
|
115
|
+
expect(knife).to receive(:create_object).and_yield(raw_hash)
|
116
|
+
expect(knife).to receive(:encryption_secret_provided?).and_return(true)
|
117
|
+
expect(knife).to receive(:read_secret).and_return(secret)
|
118
|
+
expect(Chef::EncryptedDataBagItem)
|
119
|
+
.to receive(:encrypt_data_bag_item)
|
120
|
+
.with(raw_hash, secret)
|
121
|
+
.and_return(encoded_data)
|
122
|
+
expect(rest).to receive(:post).with("data", { "name" => bag_name }).ordered
|
123
|
+
expect(rest).to receive(:post).with("data/#{bag_name}", item).ordered
|
124
|
+
|
125
|
+
knife.run
|
126
|
+
end
|
106
127
|
end
|
107
128
|
end
|
108
|
-
|
109
129
|
end
|
@@ -18,32 +18,30 @@
|
|
18
18
|
|
19
19
|
require "spec_helper"
|
20
20
|
|
21
|
+
# Now we are using the option --with-colons that works across old os versions
|
22
|
+
# as well as the latest (16.10). This for both `apt-key` and `gpg` commands
|
23
|
+
#
|
24
|
+
# Output of the command:
|
25
|
+
# => apt-key adv --list-public-keys --with-fingerprint --with-colons
|
21
26
|
APT_KEY_FINGER = <<-EOF
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
pub 4096R/C0B21F32 2012-05-11
|
34
|
-
Key fingerprint = 790B C727 7767 219C 42C8 6F93 3B4F E6AC C0B2 1F32
|
35
|
-
uid Ubuntu Archive Automatic Signing Key (2012) <ftpmaster@ubuntu.com>
|
36
|
-
|
37
|
-
pub 4096R/EFE21092 2012-05-11
|
38
|
-
Key fingerprint = 8439 38DF 228D 22F7 B374 2BC0 D94A A3F0 EFE2 1092
|
39
|
-
uid Ubuntu CD Image Automatic Signing Key (2012) <cdimage@ubuntu.com>
|
40
|
-
|
27
|
+
tru:t:1:1488924856:0:3:1:5
|
28
|
+
pub:-:1024:17:40976EAF437D05B5:2004-09-12:::-:Ubuntu Archive Automatic Signing Key <ftpmaster@ubuntu.com>::scESC:
|
29
|
+
fpr:::::::::630239CC130E1A7FD81A27B140976EAF437D05B5:
|
30
|
+
sub:-:2048:16:251BEFF479164387:2004-09-12::::::e:
|
31
|
+
pub:-:1024:17:46181433FBB75451:2004-12-30:::-:Ubuntu CD Image Automatic Signing Key <cdimage@ubuntu.com>::scSC:
|
32
|
+
fpr:::::::::C5986B4F1257FFA86632CBA746181433FBB75451:
|
33
|
+
pub:-:4096:1:3B4FE6ACC0B21F32:2012-05-11:::-:Ubuntu Archive Automatic Signing Key (2012) <ftpmaster@ubuntu.com>::scSC:
|
34
|
+
fpr:::::::::790BC7277767219C42C86F933B4FE6ACC0B21F32:
|
35
|
+
pub:-:4096:1:D94AA3F0EFE21092:2012-05-11:::-:Ubuntu CD Image Automatic Signing Key (2012) <cdimage@ubuntu.com>::scSC:
|
36
|
+
fpr:::::::::843938DF228D22F7B3742BC0D94AA3F0EFE21092:
|
41
37
|
EOF
|
42
38
|
|
39
|
+
# Output of the command:
|
40
|
+
# => gpg --with-fingerprint --with-colons [FILE]
|
43
41
|
GPG_FINGER = <<-EOF
|
44
|
-
pub
|
45
|
-
|
46
|
-
sub
|
42
|
+
pub:-:1024:17:327574EE02A818DD:2009-04-22:::-:Cloudera Apt Repository:
|
43
|
+
fpr:::::::::F36A89E33CC1BD0F71079007327574EE02A818DD:
|
44
|
+
sub:-:2048:16:84080586D1CA74A1:2009-04-22::::
|
47
45
|
EOF
|
48
46
|
|
49
47
|
describe Chef::Provider::AptRepository do
|
@@ -57,6 +55,10 @@ describe Chef::Provider::AptRepository do
|
|
57
55
|
Chef::Provider::AptRepository.new(new_resource, run_context)
|
58
56
|
end
|
59
57
|
|
58
|
+
let(:apt_key_finger_cmd) do
|
59
|
+
"apt-key adv --list-public-keys --with-fingerprint --with-colons"
|
60
|
+
end
|
61
|
+
|
60
62
|
let(:apt_key_finger) do
|
61
63
|
r = double("Mixlib::ShellOut", stdout: APT_KEY_FINGER, exitstatus: 0, live_stream: true)
|
62
64
|
allow(r).to receive(:run_command)
|
@@ -102,28 +104,32 @@ C5986B4F1257FFA86632CBA746181433FBB75451
|
|
102
104
|
|
103
105
|
it "should run the desired command" do
|
104
106
|
expect(apt_key_finger).to receive(:run_command)
|
105
|
-
provider.extract_fingerprints_from_cmd(
|
107
|
+
provider.extract_fingerprints_from_cmd(apt_key_finger_cmd)
|
106
108
|
end
|
107
109
|
|
108
110
|
it "should return a list of key fingerprints" do
|
109
|
-
expect(provider.extract_fingerprints_from_cmd(
|
111
|
+
expect(provider.extract_fingerprints_from_cmd(apt_key_finger_cmd)).to eql(apt_fingerprints)
|
110
112
|
end
|
111
113
|
end
|
112
114
|
|
113
115
|
describe "#no_new_keys?" do
|
114
116
|
before do
|
115
|
-
allow(provider).to receive(:extract_fingerprints_from_cmd).with(
|
117
|
+
allow(provider).to receive(:extract_fingerprints_from_cmd).with(apt_key_finger_cmd).and_return(apt_fingerprints)
|
116
118
|
end
|
117
119
|
|
118
120
|
let(:file) { "/tmp/remote-gpg-keyfile" }
|
119
121
|
|
120
122
|
it "should match a set of keys" do
|
121
|
-
allow(provider).to receive(:extract_fingerprints_from_cmd)
|
123
|
+
allow(provider).to receive(:extract_fingerprints_from_cmd)
|
124
|
+
.with("gpg --with-fingerprint --with-colons #{file}")
|
125
|
+
.and_return(Array(apt_fingerprints.first))
|
122
126
|
expect(provider.no_new_keys?(file)).to be_truthy
|
123
127
|
end
|
124
128
|
|
125
129
|
it "should notice missing keys" do
|
126
|
-
allow(provider).to receive(:extract_fingerprints_from_cmd)
|
130
|
+
allow(provider).to receive(:extract_fingerprints_from_cmd)
|
131
|
+
.with("gpg --with-fingerprint --with-colons #{file}")
|
132
|
+
.and_return(%w{ F36A89E33CC1BD0F71079007327574EE02A818DD })
|
127
133
|
expect(provider.no_new_keys?(file)).to be_falsey
|
128
134
|
end
|
129
135
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chef
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 12.
|
4
|
+
version: 12.20.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Jacob
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-04-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chef-config
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 12.
|
19
|
+
version: 12.20.3
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 12.
|
26
|
+
version: 12.20.3
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: mixlib-cli
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|