puppet 7.0.0 → 7.1.0

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

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CODEOWNERS +2 -16
  3. data/Gemfile +2 -3
  4. data/Gemfile.lock +13 -9
  5. data/ext/project_data.yaml +1 -0
  6. data/lib/puppet/application_support.rb +7 -0
  7. data/lib/puppet/defaults.rb +1 -27
  8. data/lib/puppet/environments.rb +38 -54
  9. data/lib/puppet/face/node/clean.rb +8 -0
  10. data/lib/puppet/ffi/posix.rb +10 -0
  11. data/lib/puppet/ffi/posix/constants.rb +14 -0
  12. data/lib/puppet/ffi/posix/functions.rb +24 -0
  13. data/lib/puppet/parser/templatewrapper.rb +1 -1
  14. data/lib/puppet/provider/user/aix.rb +2 -2
  15. data/lib/puppet/type/user.rb +1 -1
  16. data/lib/puppet/util/posix.rb +53 -4
  17. data/lib/puppet/version.rb +1 -1
  18. data/man/man5/puppet.conf.5 +2 -2
  19. data/man/man8/puppet-agent.8 +1 -1
  20. data/man/man8/puppet-apply.8 +1 -1
  21. data/man/man8/puppet-catalog.8 +1 -1
  22. data/man/man8/puppet-config.8 +1 -1
  23. data/man/man8/puppet-describe.8 +1 -1
  24. data/man/man8/puppet-device.8 +1 -1
  25. data/man/man8/puppet-doc.8 +1 -1
  26. data/man/man8/puppet-epp.8 +1 -1
  27. data/man/man8/puppet-facts.8 +1 -1
  28. data/man/man8/puppet-filebucket.8 +1 -1
  29. data/man/man8/puppet-generate.8 +1 -1
  30. data/man/man8/puppet-help.8 +1 -1
  31. data/man/man8/puppet-lookup.8 +1 -1
  32. data/man/man8/puppet-module.8 +1 -1
  33. data/man/man8/puppet-node.8 +1 -1
  34. data/man/man8/puppet-parser.8 +1 -1
  35. data/man/man8/puppet-plugin.8 +1 -1
  36. data/man/man8/puppet-report.8 +1 -1
  37. data/man/man8/puppet-resource.8 +1 -1
  38. data/man/man8/puppet-script.8 +1 -1
  39. data/man/man8/puppet-ssl.8 +1 -1
  40. data/man/man8/puppet.8 +2 -2
  41. data/spec/fixtures/unit/provider/user/aix/aix_passwd_file.out +4 -0
  42. data/spec/unit/application_spec.rb +34 -0
  43. data/spec/unit/defaults_spec.rb +1 -56
  44. data/spec/unit/environments_spec.rb +96 -19
  45. data/spec/unit/face/node_spec.rb +14 -2
  46. data/spec/unit/parser/templatewrapper_spec.rb +4 -3
  47. data/spec/unit/provider/user/aix_spec.rb +5 -0
  48. data/spec/unit/provider/user/pw_spec.rb +2 -0
  49. data/spec/unit/provider/user/useradd_spec.rb +1 -0
  50. data/spec/unit/util/posix_spec.rb +357 -15
  51. data/spec/unit/util/storage_spec.rb +3 -1
  52. metadata +19 -2
@@ -629,19 +629,6 @@ config_version=$vardir/random/scripts
629
629
  context "expiration policies" do
630
630
  let(:service) { ReplayExpirationService.new }
631
631
 
632
- # The environment named `:an_environment` will already be loaded when the
633
- # block is yielded to
634
- def with_environment_loaded(service, &block)
635
- loader_from(:filesystem => [directory_tree], :directory => directory_tree.children.first) do |loader|
636
- using_expiration_service(service) do
637
- cached = Puppet::Environments::Cached.new(loader)
638
- cached.get!(:an_environment)
639
-
640
- yield cached if block_given?
641
- end
642
- end
643
- end
644
-
645
632
  it "notifies when the environment is first created" do
646
633
  with_environment_loaded(service)
647
634
 
@@ -662,10 +649,7 @@ config_version=$vardir/random/scripts
662
649
  it "evicts an expired environment" do
663
650
  service = ReplayExpirationService.new
664
651
 
665
- # The `Cached#clear_all_expired` method tries to optimize the case where
666
- # no entries are expired. But if `Time.now < @next_expiration` and there is
667
- # an expired entry, then the `service#expired?` method is called twice.
668
- expect(service).to receive(:expired?).twice.and_return(true)
652
+ expect(service).to receive(:expired?).and_return(true)
669
653
 
670
654
  with_environment_loaded(service) do |cached|
671
655
  cached.get!(:an_environment)
@@ -706,8 +690,7 @@ config_version=$vardir/random/scripts
706
690
  it "evicts a recently touched environment" do
707
691
  Puppet[:environment_timeout] = 60
708
692
 
709
- # see note above about "twice"
710
- expect(service).to receive(:expired?).twice.and_return(true)
693
+ expect(service).to receive(:expired?).and_return(true)
711
694
 
712
695
  with_environment_loaded(service) do |cached|
713
696
  # even though the environment was recently touched, it's been expired
@@ -726,6 +709,87 @@ config_version=$vardir/random/scripts
726
709
  with_global_module_path([])
727
710
  end
728
711
  end
712
+
713
+ context '#clear' do
714
+ let(:service) { ReplayExpirationService.new }
715
+
716
+ it "evicts an environment" do
717
+ with_environment_loaded(service) do |cached|
718
+ cached.clear(:an_environment)
719
+ end
720
+
721
+ expect(service.evicted_envs).to eq([:an_environment])
722
+ end
723
+ end
724
+
725
+ context '#clear_all' do
726
+ let(:service) { ReplayExpirationService.new }
727
+
728
+ it 'evicts all environments' do
729
+ with_environment_loaded(service) do |cached|
730
+ cached.get(:an_environment)
731
+ cached.get(:another_environment)
732
+ cached.clear_all
733
+
734
+ expect(service.evicted_envs).to match([:an_environment, :another_environment])
735
+ end
736
+ end
737
+
738
+ it 'clears cached environment settings' do
739
+ base_dir = File.expand_path("envdir")
740
+ original_envdir = FS::MemoryFile.a_directory(base_dir, [
741
+ FS::MemoryFile.a_directory("env3", [
742
+ FS::MemoryFile.a_regular_file_containing("environment.conf", <<-EOF)
743
+ manifest=/manifest_orig
744
+ modulepath=/modules_orig
745
+ environment_timeout=60
746
+ EOF
747
+ ]),
748
+ ])
749
+
750
+ FS.overlay(original_envdir) do
751
+ dir_loader = Puppet::Environments::Directories.new(original_envdir, [])
752
+ loader = Puppet::Environments::Cached.new(dir_loader)
753
+ Puppet.override(:environments => loader) do
754
+ original_env = loader.get("env3") # force the environment.conf to be read
755
+
756
+ changed_envdir = FS::MemoryFile.a_directory(base_dir, [
757
+ FS::MemoryFile.a_directory("env3", [
758
+ FS::MemoryFile.a_regular_file_containing("environment.conf", <<-EOF)
759
+ manifest=/manifest_changed
760
+ modulepath=/modules_changed
761
+ environment_timeout=60
762
+ EOF
763
+ ]),
764
+ ])
765
+
766
+ #Clear all cached environments
767
+ loader.clear_all
768
+
769
+ FS.overlay(changed_envdir) do
770
+ changed_env = loader.get("env3")
771
+
772
+ expect(original_env).to environment(:env3).
773
+ with_manifest(File.expand_path("/manifest_orig")).
774
+ with_full_modulepath([File.expand_path("/modules_orig")])
775
+
776
+ expect(changed_env).to environment(:env3).
777
+ with_manifest(File.expand_path("/manifest_changed")).
778
+ with_full_modulepath([File.expand_path("/modules_changed")])
779
+ end
780
+ end
781
+ end
782
+ end
783
+
784
+ it 'deletes environment text domains' do
785
+ with_environment_loaded(service) do |cached|
786
+ cached.get(:an_environment)
787
+ cached.clear_all
788
+
789
+ expect(FastGettext.text_domain).to eq(Puppet::GettextConfig::DEFAULT_TEXT_DOMAIN)
790
+ end
791
+ end
792
+ end
729
793
  end
730
794
 
731
795
  RSpec::Matchers.define :environment do |name|
@@ -820,6 +884,19 @@ config_version=$vardir/random/scripts
820
884
  end
821
885
  end
822
886
 
887
+ # The environment named `:an_environment` will already be loaded when the
888
+ # block is yielded to
889
+ def with_environment_loaded(service, &block)
890
+ loader_from(:filesystem => [directory_tree], :directory => directory_tree.children.first) do |loader|
891
+ using_expiration_service(service) do
892
+ cached = Puppet::Environments::Cached.new(loader)
893
+ cached.get!(:an_environment)
894
+
895
+ yield cached if block_given?
896
+ end
897
+ end
898
+ end
899
+
823
900
  class ReplayExpirationService < Puppet::Environments::Cached::DefaultCacheExpirationService
824
901
  attr_reader :created_envs, :evicted_envs
825
902
 
@@ -89,8 +89,20 @@ describe Puppet::Face[:node, '0.0.1'] do
89
89
 
90
90
  describe "when cleaning certificate", :if => Puppet.features.puppetserver_ca? do
91
91
  it "should call the CA CLI gem's clean action" do
92
- expect_any_instance_of(Puppetserver::Ca::Action::Clean).to receive(:run).with({ 'certnames' => ['hostname'] }).and_return(0)
93
- subject.clean_cert('hostname')
92
+ expect_any_instance_of(Puppetserver::Ca::Action::Clean).
93
+ to receive(:clean_certs).
94
+ with(['hostname'], anything).
95
+ and_return(:success)
96
+
97
+ if Puppet[:cadir].start_with?(Puppet[:ssldir])
98
+ expect_any_instance_of(LoggerIO).
99
+ to receive(:warn).
100
+ with(/cadir is currently configured to be inside/)
101
+ end
102
+
103
+ expect(Puppet).not_to receive(:warning)
104
+ result = subject.clean_cert('hostname')
105
+ expect(result).to eq(0)
94
106
  end
95
107
 
96
108
  it "should not call the CA CLI gem's clean action if the gem is missing" do
@@ -57,9 +57,10 @@ describe Puppet::Parser::TemplateWrapper do
57
57
  expect(tw.all_tags).to eq(["tag1","tag2"])
58
58
  end
59
59
 
60
- it "provides the tags defined in the current scope with #tags" do
61
- expect(scope).to receive(:tags).and_return(["tag1", "tag2"])
62
- expect(tw.tags).to eq(["tag1","tag2"])
60
+ it "raises not implemented error" do
61
+ expect {
62
+ tw.tags
63
+ }.to raise_error(NotImplementedError, /Call 'all_tags' instead/)
63
64
  end
64
65
 
65
66
  it "raises error on access to removed in-scope variables via method calls" do
@@ -143,6 +143,11 @@ describe 'Puppet::Type::User::Provider::Aix' do
143
143
  it "returns the user's password" do
144
144
  expect(call_parse_password).to eql('some_password')
145
145
  end
146
+
147
+ it "returns the user's password with tabs" do
148
+ resource[:name] = 'tab_password_user'
149
+ expect(call_parse_password).to eql('some_password')
150
+ end
146
151
  end
147
152
 
148
153
  # TODO: If we move from using Mocha to rspec's mocks,
@@ -53,12 +53,14 @@ describe Puppet::Type.type(:user).provider(:pw) do
53
53
 
54
54
  it "should use -G with the correct argument when the groups property is set" do
55
55
  resource[:groups] = "group1"
56
+ allow(Puppet::Util::POSIX).to receive(:groups_of).with('testuser').and_return([])
56
57
  expect(provider).to receive(:execute).with(include("-G").and(include("group1")), kind_of(Hash))
57
58
  provider.create
58
59
  end
59
60
 
60
61
  it "should use -G with all the given groups when the groups property is set to an array" do
61
62
  resource[:groups] = ["group1", "group2"]
63
+ allow(Puppet::Util::POSIX).to receive(:groups_of).with('testuser').and_return([])
62
64
  expect(provider).to receive(:execute).with(include("-G").and(include("group1,group2")), kind_of(Hash))
63
65
  provider.create
64
66
  end
@@ -4,6 +4,7 @@ RSpec::Matchers.define_negated_matcher :excluding, :include
4
4
 
5
5
  describe Puppet::Type.type(:user).provider(:useradd) do
6
6
  before :each do
7
+ allow(Puppet::Util::POSIX).to receive(:groups_of).and_return([])
7
8
  allow(described_class).to receive(:command).with(:password).and_return('/usr/bin/chage')
8
9
  allow(described_class).to receive(:command).with(:localpassword).and_return('/usr/sbin/lchage')
9
10
  allow(described_class).to receive(:command).with(:add).and_return('/usr/sbin/useradd')
@@ -1,5 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
+ require 'puppet/ffi/posix'
3
4
  require 'puppet/util/posix'
4
5
 
5
6
  class PosixTest
@@ -11,35 +12,338 @@ describe Puppet::Util::POSIX do
11
12
  @posix = PosixTest.new
12
13
  end
13
14
 
14
- describe '.groups_of' do
15
+ describe '.groups_of' do
16
+ let(:mock_user_data) { double(user, :gid => 1000) }
17
+
18
+ let(:ngroups_ptr) { double('FFI::MemoryPointer', :address => 0x0001, :size => 4) }
19
+ let(:groups_ptr) { double('FFI::MemoryPointer', :address => 0x0002, :size => Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS) }
20
+
15
21
  let(:mock_groups) do
16
22
  [
17
- ['group1', ['user1', 'user2']],
18
- ['group2', ['user2']],
19
- ['group1', ['user1', 'user2']],
20
- ['group3', ['user1']],
21
- ['group4', ['user2']]
22
- ].map do |(name, members)|
23
+ ['root', ['root'], 0],
24
+ ['nomembers', [], 5 ],
25
+ ['group1', ['user1', 'user2'], 1001],
26
+ ['group2', ['user2'], 2002],
27
+ ['group1', ['user1', 'user2'], 1001],
28
+ ['group3', ['user1'], 3003],
29
+ ['group4', ['user2'], 4004],
30
+ ['user1', [], 1111],
31
+ ['user2', [], 2222]
32
+ ].map do |(name, members, gid)|
23
33
  group_struct = double("Group #{name}")
24
34
  allow(group_struct).to receive(:name).and_return(name)
25
35
  allow(group_struct).to receive(:mem).and_return(members)
36
+ allow(group_struct).to receive(:gid).and_return(gid)
26
37
 
27
38
  group_struct
28
39
  end
29
40
  end
30
41
 
42
+ def prepare_user_and_groups_env(user, groups)
43
+ groups_gids = []
44
+ groups_and_user = []
45
+ groups_and_user.replace(groups)
46
+ groups_and_user.push(user)
47
+
48
+ groups_and_user.each do |group|
49
+ mock_group = mock_groups.find { |m| m.name == group }
50
+ groups_gids.push(mock_group.gid)
51
+
52
+ allow(Puppet::Etc).to receive(:getgrgid).with(mock_group.gid).and_return(mock_group)
53
+ end
54
+
55
+ if groups_and_user.size > Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS
56
+ allow(ngroups_ptr).to receive(:read_int).and_return(Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS, groups_and_user.size)
57
+ else
58
+ allow(ngroups_ptr).to receive(:read_int).and_return(groups_and_user.size)
59
+ end
60
+
61
+ allow(groups_ptr).to receive(:get_array_of_uint).with(0, groups_and_user.size).and_return(groups_gids)
62
+ allow(Puppet::Etc).to receive(:getpwnam).with(user).and_return(mock_user_data)
63
+ end
64
+
31
65
  before(:each) do
32
- etc_stub = receive(:group)
33
- mock_groups.each do |mock_group|
34
- etc_stub = etc_stub.and_yield(mock_group)
66
+ allow(Puppet::FFI::POSIX::Functions).to receive(:respond_to?).with(:getgrouplist).and_return(true)
67
+ end
68
+
69
+ describe 'when it uses FFI function getgrouplist' do
70
+ before(:each) do
71
+ allow(FFI::MemoryPointer).to receive(:new).with(:int).and_yield(ngroups_ptr)
72
+ allow(FFI::MemoryPointer).to receive(:new).with(:uint, Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS).and_yield(groups_ptr)
73
+ allow(ngroups_ptr).to receive(:write_int).with(Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS).and_return(ngroups_ptr)
74
+ end
75
+
76
+ describe 'when there are groups' do
77
+ context 'for user1' do
78
+ let(:user) { 'user1' }
79
+ let(:expected_groups) { ['group1', 'group3'] }
80
+
81
+ before(:each) do
82
+ prepare_user_and_groups_env(user, expected_groups)
83
+ allow(Puppet::FFI::POSIX::Functions).to receive(:getgrouplist).and_return(1)
84
+ end
85
+
86
+ it "should return the groups for given user" do
87
+ expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)
88
+ end
89
+
90
+ it 'should not print any debug message about falling back to Puppet::Etc.group' do
91
+ expect(Puppet).not_to receive(:debug).with(/Falling back to Puppet::Etc.group:/)
92
+ Puppet::Util::POSIX.groups_of(user)
93
+ end
94
+ end
95
+
96
+ context 'for user2' do
97
+ let(:user) { 'user2' }
98
+ let(:expected_groups) { ['group1', 'group2', 'group4'] }
99
+
100
+ before(:each) do
101
+ prepare_user_and_groups_env(user, expected_groups)
102
+ allow(Puppet::FFI::POSIX::Functions).to receive(:getgrouplist).and_return(1)
103
+ end
104
+
105
+ it "should return the groups for given user" do
106
+ expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)
107
+ end
108
+
109
+ it 'should not print any debug message about falling back to Puppet::Etc.group' do
110
+ expect(Puppet).not_to receive(:debug).with(/Falling back to Puppet::Etc.group:/)
111
+ Puppet::Util::POSIX.groups_of(user)
112
+ end
113
+ end
114
+ end
115
+
116
+ describe 'when there are no groups' do
117
+ let(:user) { 'nomembers' }
118
+ let(:expected_groups) { [] }
119
+
120
+ before(:each) do
121
+ prepare_user_and_groups_env(user, expected_groups)
122
+ allow(Puppet::FFI::POSIX::Functions).to receive(:getgrouplist).and_return(1)
123
+ end
124
+
125
+ it "should return no groups for given user" do
126
+ expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)
127
+ end
128
+
129
+ it 'should not print any debug message about falling back to Puppet::Etc.group' do
130
+ expect(Puppet).not_to receive(:debug).with(/Falling back to Puppet::Etc.group:/)
131
+ Puppet::Util::POSIX.groups_of(user)
132
+ end
133
+ end
134
+
135
+ describe 'when primary group explicitly contains user' do
136
+ let(:user) { 'root' }
137
+ let(:expected_groups) { ['root'] }
138
+
139
+ before(:each) do
140
+ prepare_user_and_groups_env(user, expected_groups)
141
+ allow(Puppet::FFI::POSIX::Functions).to receive(:getgrouplist).and_return(1)
142
+ end
143
+
144
+ it "should return the groups, including primary group, for given user" do
145
+ expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)
146
+ end
147
+
148
+ it 'should not print any debug message about falling back to Puppet::Etc.group' do
149
+ expect(Puppet).not_to receive(:debug).with(/Falling back to Puppet::Etc.group:/)
150
+ Puppet::Util::POSIX.groups_of(user)
151
+ end
152
+ end
153
+
154
+ describe 'when primary group does not explicitly contain user' do
155
+ let(:user) { 'user1' }
156
+ let(:expected_groups) { ['group1', 'group3'] }
157
+
158
+ before(:each) do
159
+ prepare_user_and_groups_env(user, expected_groups)
160
+ allow(Puppet::FFI::POSIX::Functions).to receive(:getgrouplist).and_return(1)
161
+ end
162
+
163
+ it "should not return primary group for given user" do
164
+ expect(Puppet::Util::POSIX.groups_of(user)).not_to include(user)
165
+ end
166
+
167
+ it 'should not print any debug message about falling back to Puppet::Etc.group' do
168
+ expect(Puppet).not_to receive(:debug).with(/Falling back to Puppet::Etc.group:/)
169
+ Puppet::Util::POSIX.groups_of(user)
170
+ end
171
+ end
172
+
173
+ context 'number of groups' do
174
+ before(:each) do
175
+ stub_const("Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS", 2)
176
+ prepare_user_and_groups_env(user, expected_groups)
177
+
178
+ allow(FFI::MemoryPointer).to receive(:new).with(:uint, Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS).and_yield(groups_ptr)
179
+ allow(ngroups_ptr).to receive(:write_int).with(Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS).and_return(ngroups_ptr)
180
+ end
181
+
182
+ describe 'when there are less than maximum expected number of groups' do
183
+ let(:user) { 'root' }
184
+ let(:expected_groups) { ['root'] }
185
+
186
+ before(:each) do
187
+ allow(Puppet::FFI::POSIX::Functions).to receive(:getgrouplist).and_return(1)
188
+ end
189
+
190
+ it "should return the groups for given user, after one 'getgrouplist' call" do
191
+ expect(Puppet::FFI::POSIX::Functions).to receive(:getgrouplist).once
192
+ expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)
193
+ end
194
+
195
+ it 'should not print any debug message about falling back to Puppet::Etc.group' do
196
+ expect(Puppet).not_to receive(:debug).with(/Falling back to Puppet::Etc.group:/)
197
+ Puppet::Util::POSIX.groups_of(user)
198
+ end
199
+ end
200
+
201
+ describe 'when there are more than maximum expected number of groups' do
202
+ let(:user) { 'user1' }
203
+ let(:expected_groups) { ['group1', 'group3'] }
204
+
205
+ before(:each) do
206
+ allow(FFI::MemoryPointer).to receive(:new).with(:uint, Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS * 2).and_yield(groups_ptr)
207
+ allow(ngroups_ptr).to receive(:write_int).with(Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS * 2).and_return(ngroups_ptr)
208
+
209
+ allow(Puppet::FFI::POSIX::Functions).to receive(:getgrouplist).and_return(-1, 1)
210
+ end
211
+
212
+ it "should return the groups for given user, after two 'getgrouplist' calls" do
213
+ expect(Puppet::FFI::POSIX::Functions).to receive(:getgrouplist).twice
214
+ expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)
215
+ end
216
+
217
+ it 'should not print any debug message about falling back to Puppet::Etc.group' do
218
+ expect(Puppet).not_to receive(:debug).with(/Falling back to Puppet::Etc.group:/)
219
+ Puppet::Util::POSIX.groups_of(user)
220
+ end
221
+ end
35
222
  end
36
- allow(Puppet::Etc).to etc_stub
37
223
  end
38
224
 
39
- it 'returns the groups of the given user' do
40
- expect(Puppet::Util::POSIX.groups_of('user1')).to eql(
41
- ['group1', 'group3']
42
- )
225
+ describe 'when it falls back to Puppet::Etc.group method' do
226
+ before(:each) do
227
+ etc_stub = receive(:group)
228
+ mock_groups.each do |mock_group|
229
+ etc_stub = etc_stub.and_yield(mock_group)
230
+ end
231
+ allow(Puppet::Etc).to etc_stub
232
+
233
+ allow(Puppet::Etc).to receive(:getpwnam).with(user).and_raise(ArgumentError, "can't find user for #{user}")
234
+ allow(Puppet).to receive(:debug)
235
+
236
+ expect(Puppet::FFI::POSIX::Functions).not_to receive(:getgrouplist)
237
+ end
238
+
239
+ describe 'when there are groups' do
240
+ context 'for user1' do
241
+ let(:user) { 'user1' }
242
+ let(:expected_groups) { ['group1', 'group3'] }
243
+
244
+ it "should return the groups for given user" do
245
+ expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)
246
+ end
247
+
248
+ it 'logs a debug message' do
249
+ expect(Puppet).to receive(:debug).with("Falling back to Puppet::Etc.group: can't find user for #{user}")
250
+ Puppet::Util::POSIX.groups_of(user)
251
+ end
252
+ end
253
+
254
+ context 'for user2' do
255
+ let(:user) { 'user2' }
256
+ let(:expected_groups) { ['group1', 'group2', 'group4'] }
257
+
258
+ it "should return the groups for given user" do
259
+ expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)
260
+ end
261
+
262
+ it 'logs a debug message' do
263
+ expect(Puppet).to receive(:debug).with("Falling back to Puppet::Etc.group: can't find user for #{user}")
264
+ Puppet::Util::POSIX.groups_of(user)
265
+ end
266
+ end
267
+ end
268
+
269
+ describe 'when there are no groups' do
270
+ let(:user) { 'nomembers' }
271
+ let(:expected_groups) { [] }
272
+
273
+ it "should return no groups for given user" do
274
+ expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)
275
+ end
276
+
277
+ it 'logs a debug message' do
278
+ expect(Puppet).to receive(:debug).with("Falling back to Puppet::Etc.group: can't find user for #{user}")
279
+ Puppet::Util::POSIX.groups_of(user)
280
+ end
281
+ end
282
+
283
+ describe 'when primary group explicitly contains user' do
284
+ let(:user) { 'root' }
285
+ let(:expected_groups) { ['root'] }
286
+
287
+ it "should return the groups, including primary group, for given user" do
288
+ expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)
289
+ end
290
+
291
+ it 'logs a debug message' do
292
+ expect(Puppet).to receive(:debug).with("Falling back to Puppet::Etc.group: can't find user for #{user}")
293
+ Puppet::Util::POSIX.groups_of(user)
294
+ end
295
+ end
296
+
297
+ describe 'when primary group does not explicitly contain user' do
298
+ let(:user) { 'user1' }
299
+ let(:expected_groups) { ['group1', 'group3'] }
300
+
301
+ it "should not return primary group for given user" do
302
+ expect(Puppet::Util::POSIX.groups_of(user)).not_to include(user)
303
+ end
304
+
305
+ it 'logs a debug message' do
306
+ expect(Puppet).to receive(:debug).with("Falling back to Puppet::Etc.group: can't find user for #{user}")
307
+ Puppet::Util::POSIX.groups_of(user)
308
+ end
309
+ end
310
+
311
+ describe "when the 'getgrouplist' method is not available" do
312
+ let(:user) { 'user1' }
313
+ let(:expected_groups) { ['group1', 'group3'] }
314
+
315
+ before(:each) do
316
+ allow(Puppet::FFI::POSIX::Functions).to receive(:respond_to?).with(:getgrouplist).and_return(false)
317
+ end
318
+
319
+ it "should return the groups" do
320
+ expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)
321
+ end
322
+
323
+ it 'logs a debug message' do
324
+ expect(Puppet).to receive(:debug).with("Falling back to Puppet::Etc.group: The 'getgrouplist' method is not available")
325
+ Puppet::Util::POSIX.groups_of(user)
326
+ end
327
+ end
328
+
329
+
330
+ describe "when ffi is not available on the machine" do
331
+ let(:user) { 'user1' }
332
+ let(:expected_groups) { ['group1', 'group3'] }
333
+
334
+ before(:each) do
335
+ allow(Puppet::Util::POSIX).to receive(:require).with('puppet/ffi/posix').and_raise(LoadError, 'cannot load such file -- ffi')
336
+ end
337
+
338
+ it "should return the groups" do
339
+ expect(Puppet::Util::POSIX.groups_of(user)).to eql(expected_groups)
340
+ end
341
+
342
+ it 'logs a debug message' do
343
+ expect(Puppet).to receive(:debug).with("Falling back to Puppet::Etc.group: cannot load such file -- ffi")
344
+ Puppet::Util::POSIX.groups_of(user)
345
+ end
346
+ end
43
347
  end
44
348
  end
45
349
 
@@ -189,6 +493,25 @@ describe Puppet::Util::POSIX do
189
493
  expect(@posix.gid("asdf")).to eq(100)
190
494
  end
191
495
 
496
+ it "returns the id without full groups query if multiple groups have the same id" do
497
+ expect(@posix).to receive(:get_posix_field).with(:group, :gid, "asdf").and_return(100)
498
+ expect(@posix).to receive(:get_posix_field).with(:group, :name, 100).and_return("boo")
499
+ expect(@posix).to receive(:get_posix_field).with(:group, :gid, "boo").and_return(100)
500
+
501
+ expect(@posix).not_to receive(:search_posix_field)
502
+ expect(@posix.gid("asdf")).to eq(100)
503
+ end
504
+
505
+ it "returns the id with full groups query if name is nil" do
506
+ expect(@posix).to receive(:get_posix_field).with(:group, :gid, "asdf").and_return(100)
507
+ expect(@posix).to receive(:get_posix_field).with(:group, :name, 100).and_return(nil)
508
+ expect(@posix).not_to receive(:get_posix_field).with(:group, :gid, nil)
509
+
510
+
511
+ expect(@posix).to receive(:search_posix_field).with(:group, :gid, "asdf").and_return(100)
512
+ expect(@posix.gid("asdf")).to eq(100)
513
+ end
514
+
192
515
  it "should use :search_posix_field if the discovered name does not match the passed-in name" do
193
516
  expect(@posix).to receive(:get_posix_field).with(:group, :gid, "asdf").and_return(100)
194
517
  expect(@posix).to receive(:get_posix_field).with(:group, :name, 100).and_return("boo")
@@ -265,6 +588,25 @@ describe Puppet::Util::POSIX do
265
588
  expect(@posix.uid("asdf")).to eq(100)
266
589
  end
267
590
 
591
+ it "returns the id without full users query if multiple users have the same id" do
592
+ expect(@posix).to receive(:get_posix_field).with(:passwd, :uid, "asdf").and_return(100)
593
+ expect(@posix).to receive(:get_posix_field).with(:passwd, :name, 100).and_return("boo")
594
+ expect(@posix).to receive(:get_posix_field).with(:passwd, :uid, "boo").and_return(100)
595
+
596
+ expect(@posix).not_to receive(:search_posix_field)
597
+ expect(@posix.uid("asdf")).to eq(100)
598
+ end
599
+
600
+ it "returns the id with full users query if name is nil" do
601
+ expect(@posix).to receive(:get_posix_field).with(:passwd, :uid, "asdf").and_return(100)
602
+ expect(@posix).to receive(:get_posix_field).with(:passwd, :name, 100).and_return(nil)
603
+ expect(@posix).not_to receive(:get_posix_field).with(:passwd, :uid, nil)
604
+
605
+
606
+ expect(@posix).to receive(:search_posix_field).with(:passwd, :uid, "asdf").and_return(100)
607
+ expect(@posix.uid("asdf")).to eq(100)
608
+ end
609
+
268
610
  it "should use :search_posix_field if the discovered name does not match the passed-in name" do
269
611
  expect(@posix).to receive(:get_posix_field).with(:passwd, :uid, "asdf").and_return(100)
270
612
  expect(@posix).to receive(:get_posix_field).with(:passwd, :name, 100).and_return("boo")