Skip to content

Commit 07e8b39

Browse files
committed
Merge pull request #462 from elyscape/fix/fqdn_rotate_seeds_with_argument
fqdn_rotate: Don't use the value itself as part of the random seed
2 parents a383705 + b436216 commit 07e8b39

File tree

4 files changed

+46
-17
lines changed

4 files changed

+46
-17
lines changed

README.markdown

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,20 @@ fqdn_rand_string(10, '', 'custom seed')
259259

260260
#### `fqdn_rotate`
261261

262-
Rotates an array a random number of times, based on a node's fqdn. *Type*: rvalue.
262+
Rotates an array or string a random number of times, combining the `$fqdn` fact and an optional seed for repeatable randomness.
263+
264+
*Usage:*
265+
~~~
266+
fqdn_rotate(VALUE, [SEED])
267+
~~~
268+
*Examples:*
269+
~~~
270+
fqdn_rotate(['a', 'b', 'c', 'd'])
271+
fqdn_rotate('abcd')
272+
fqdn_rotate([1, 2, 3], 'custom seed')
273+
~~~
274+
275+
*Type*: rvalue.
263276

264277
#### `get_module_path`
265278

lib/puppet/parser/functions/fqdn_rotate.rb

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,23 @@
22
# fqdn_rotate.rb
33
#
44

5-
module Puppet::Parser::Functions
6-
newfunction(:fqdn_rotate, :type => :rvalue, :doc => <<-EOS
7-
Rotates an array a random number of times based on a nodes fqdn.
8-
EOS
9-
) do |arguments|
5+
Puppet::Parser::Functions.newfunction(
6+
:fqdn_rotate,
7+
:type => :rvalue,
8+
:doc => "Usage: `fqdn_rotate(VALUE, [SEED])`. VALUE is required and
9+
must be an array or a string. SEED is optional and may be any number
10+
or string.
11+
12+
Rotates VALUE a random number of times, combining the `$fqdn` fact and
13+
the value of SEED for repeatable randomness. (That is, each node will
14+
get a different random rotation from this function, but a given node's
15+
result will be the same every time unless its hostname changes.) Adding
16+
a SEED can be useful if you need more than one unrelated rotation.") do |args|
1017

1118
raise(Puppet::ParseError, "fqdn_rotate(): Wrong number of arguments " +
12-
"given (#{arguments.size} for 1)") if arguments.size < 1
19+
"given (#{args.size} for 1)") if args.size < 1
1320

14-
value = arguments[0]
21+
value = args.shift
1522
require 'digest/md5'
1623

1724
unless value.is_a?(Array) || value.is_a?(String)
@@ -31,7 +38,7 @@ module Puppet::Parser::Functions
3138

3239
elements = result.size
3340

34-
seed = Digest::MD5.hexdigest([lookupvar('::fqdn'),arguments].join(':')).hex
41+
seed = Digest::MD5.hexdigest([lookupvar('::fqdn'),args].join(':')).hex
3542
# deterministic_rand() was added in Puppet 3.2.0; reimplement if necessary
3643
if Puppet::Util.respond_to?(:deterministic_rand)
3744
offset = Puppet::Util.deterministic_rand(seed, elements).to_i
@@ -51,7 +58,6 @@ module Puppet::Parser::Functions
5158
result = string ? result.join : result
5259

5360
return result
54-
end
5561
end
5662

5763
# vim: set ts=2 sw=2 et :

spec/acceptance/fqdn_rotate_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
EOS
3737

3838
apply_manifest(pp, :catch_failures => true) do |r|
39-
expect(r.stdout).to match(/fqdn_rotate is \["c", "d", "a", "b"\]/)
39+
expect(r.stdout).to match(/fqdn_rotate is \["d", "a", "b", "c"\]/)
4040
end
4141
end
4242
end

spec/functions/fqdn_rotate_spec.rb

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@
55
it { is_expected.to run.with_params().and_raise_error(Puppet::ParseError, /wrong number of arguments/i) }
66
it { is_expected.to run.with_params(0).and_raise_error(Puppet::ParseError, /Requires either array or string to work with/) }
77
it { is_expected.to run.with_params({}).and_raise_error(Puppet::ParseError, /Requires either array or string to work with/) }
8-
it {
9-
pending("Current implementation ignores parameters after the first.")
10-
is_expected.to run.with_params("one", "two").and_raise_error(Puppet::ParseError)
11-
}
128
it { is_expected.to run.with_params('').and_return('') }
139
it { is_expected.to run.with_params('a').and_return('a') }
1410

@@ -25,6 +21,18 @@
2521
expect(val1).to eq(val2)
2622
end
2723

24+
it "allows extra arguments to control the random rotation on a single host" do
25+
val1 = fqdn_rotate("abcdefg", :extra_identifier => [1, "different", "host"])
26+
val2 = fqdn_rotate("abcdefg", :extra_identifier => [2, "different", "host"])
27+
expect(val1).not_to eq(val2)
28+
end
29+
30+
it "considers the same host and same extra arguments to have the same random rotation" do
31+
val1 = fqdn_rotate("abcdefg", :extra_identifier => [1, "same", "host"])
32+
val2 = fqdn_rotate("abcdefg", :extra_identifier => [1, "same", "host"])
33+
expect(val1).to eq(val2)
34+
end
35+
2836
it "should rotate a string to give different values on different hosts" do
2937
val1 = fqdn_rotate("abcdefg", :host => 'one')
3038
val2 = fqdn_rotate("abcdefg", :host => 'two')
@@ -38,7 +46,7 @@
3846

3947
it "should use the Puppet::Util.deterministic_rand function" do
4048
if Puppet::Util.respond_to?(:deterministic_rand)
41-
Puppet::Util.expects(:deterministic_rand).with(113646079810780526294648115052177588845,4)
49+
Puppet::Util.expects(:deterministic_rand).with(44489829212339698569024999901561968770,4)
4250
fqdn_rotate("asdf")
4351
else
4452
skip 'Puppet::Util#deterministic_rand not available'
@@ -55,11 +63,13 @@
5563

5664
def fqdn_rotate(value, args = {})
5765
host = args[:host] || '127.0.0.1'
66+
extra = args[:extra_identifier] || []
5867

5968
# workaround not being able to use let(:facts) because some tests need
6069
# multiple different hostnames in one context
6170
scope.stubs(:lookupvar).with("::fqdn").returns(host)
6271

63-
scope.function_fqdn_rotate([value])
72+
function_args = [value] + extra
73+
scope.function_fqdn_rotate(function_args)
6474
end
6575
end

0 commit comments

Comments
 (0)