Skip to content

Commit a01dc94

Browse files
authored
Merge pull request #877 from pegasd/seeded_rand_string
seeded_rand_string() function
2 parents 664d6b9 + 5f8995e commit a01dc94

3 files changed

Lines changed: 67 additions & 0 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2176,6 +2176,10 @@ Takes an integer max value and a string seed value and returns a repeatable rand
21762176

21772177
*Type*: rvalue.
21782178

2179+
#### `seeded_rand_string`
2180+
2181+
Generates a consistent (based on seed value) random string. Useful for generating matching passwords for different hosts.
2182+
21792183
#### `shell_escape`
21802184

21812185
Escapes a string so that it can be safely used in a Bourne shell command line. Note that the resulting string should be used unquoted and is not intended for use in either double or single quotes. This function behaves the same as Ruby's `Shellwords.shellescape()` function; see the [Ruby documentation](http://ruby-doc.org/stdlib-2.3.0/libdoc/shellwords/rdoc/Shellwords.html#method-c-shellescape).
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Generates a consistent random string of specific length based on provided seed.
2+
#
3+
# @example Generate a consistently random string of length 8 with a seed:
4+
# seeded_rand_string(8, "${module_name}::redis_password")
5+
#
6+
# @example Generate a random string from a specific set of characters:
7+
# seeded_rand_string(5, '', 'abcdef')
8+
Puppet::Functions.create_function(:seeded_rand_string) do
9+
# @param length Length of string to be generated.
10+
# @param seed Seed string.
11+
# @param charset String that contains characters to use for the random string.
12+
#
13+
# @return [String] Random string.
14+
dispatch :rand_string do
15+
param 'Integer[1]', :length
16+
param 'String', :seed
17+
optional_param 'String[2]', :charset
18+
end
19+
20+
def rand_string(length, seed, charset = nil)
21+
require 'digest/sha2'
22+
23+
charset ||= '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
24+
25+
random_generator = Random.new(Digest::SHA256.hexdigest(seed).to_i(16))
26+
27+
Array.new(length) { charset[random_generator.rand(charset.size)] }.join
28+
end
29+
end
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
require 'spec_helper'
2+
3+
describe 'seeded_rand_string' do
4+
it { is_expected.not_to be(nil) }
5+
6+
# Test for erroneous params
7+
it { is_expected.to run.with_params.and_raise_error(ArgumentError, %r{expects between.+got none}i) }
8+
it { is_expected.to run.with_params(1).and_raise_error(ArgumentError, %r{expects between.+got 1}i) }
9+
it { is_expected.to run.with_params('1', 'hello').and_raise_error(ArgumentError, %r{parameter 'length' expects an Integer value}i) }
10+
it { is_expected.to run.with_params(1, 1).and_raise_error(ArgumentError, %r{parameter 'seed' expects a String value}i) }
11+
it { is_expected.to run.with_params(1, 'hello', 1).and_raise_error(ArgumentError, %r{parameter 'charset' expects a.+String}i) }
12+
13+
# Test regular run
14+
it { is_expected.to run.with_params(100, 'hello') }
15+
16+
# Test custom charsets
17+
it { is_expected.to run.with_params(100, 'hello', 'abcd').and_return(%r{[a-d]{100}}) }
18+
it { is_expected.to run.with_params(100, 'hello', 'abcdefgh').and_return(%r{[a-h]{100}}) }
19+
it { is_expected.to run.with_params(100, 'hello', 'ab,|').and_return(%r{[ab,|]{100}}) }
20+
21+
# Test behavior
22+
it 'generates the same string with the same seed' do
23+
rand_str_one = call_function(:seeded_rand_string, 300, 'my_seed')
24+
rand_str_two = call_function(:seeded_rand_string, 300, 'my_seed')
25+
26+
expect(rand_str_one).to eq(rand_str_two)
27+
end
28+
it 'generates different strings if seeded differently' do
29+
rand_str_one = call_function(:seeded_rand_string, 300, 'my_seed_one')
30+
rand_str_two = call_function(:seeded_rand_string, 300, 'my_seed_two')
31+
32+
expect(rand_str_one).not_to eq(rand_str_two)
33+
end
34+
end

0 commit comments

Comments
 (0)