Skip to content

Commit b9ebae5

Browse files
committed
Add the ability for the loadjson() and loadyaml() functions to accept
an HTTP or HTTPS URI.
1 parent bd2d5c7 commit b9ebae5

5 files changed

Lines changed: 110 additions & 14 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1697,6 +1697,8 @@ Loads a JSON file containing an array, string, or hash, and returns the data in
16971697

16981698
For example:
16991699

1700+
The first parameter can be an absolute file path, or a URL.
1701+
17001702
```puppet
17011703
$myhash = loadjson('/etc/puppet/data/myhash.json')
17021704
```

lib/puppet/parser/functions/loadjson.rb

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,43 @@
11
#
22
# loadjson.rb
33
#
4+
45
module Puppet::Parser::Functions
56
newfunction(:loadjson, :type => :rvalue, :arity => -2, :doc => <<-'DOC') do |args|
67
Load a JSON file containing an array, string, or hash, and return the data
78
in the corresponding native data type.
9+
The first parameter can be a file path or a URL.
810
The second parameter is the default value. It will be returned if the file
911
was not found or could not be parsed.
1012
1113
For example:
1214
1315
$myhash = loadjson('/etc/puppet/data/myhash.json')
16+
$myhash = loadjson('https://example.local/my_hash.json')
1417
$myhash = loadjson('no-file.json', {'default' => 'value'})
1518
DOC
1619

1720
raise ArgumentError, 'Wrong number of arguments. 1 or 2 arguments should be provided.' unless args.length >= 1
18-
19-
if File.exists?(args[0]) # rubocop:disable Lint/DeprecatedClassMethods : Changing to .exist? breaks the code
20-
begin
21+
require 'open-uri'
22+
begin
23+
if args[0].start_with?('http://', 'https://')
24+
begin
25+
contents = OpenURI.open_uri(args[0])
26+
rescue OpenURI::HTTPError => err
27+
res = err.io
28+
warning("Can't load '#{args[0]}' HTTP Error Code: '#{res.status[0]}'")
29+
args[1]
30+
end
31+
PSON.load(contents) || args[1]
32+
elsif File.exists?(args[0]) # rubocop:disable Lint/DeprecatedClassMethods : Changing to .exist? breaks the code
2133
content = File.read(args[0])
2234
PSON.load(content) || args[1]
23-
rescue StandardError => e
24-
raise e unless args[1]
35+
else
36+
warning("Can't load '#{args[0]}' File does not exist!")
2537
args[1]
2638
end
27-
else
28-
warning("Can't load '#{args[0]}' File does not exist!")
39+
rescue StandardError => e
40+
raise e unless args[1]
2941
args[1]
3042
end
3143
end

lib/puppet/parser/functions/loadyaml.rb

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,38 @@ module Puppet::Parser::Functions
55
newfunction(:loadyaml, :type => :rvalue, :arity => -2, :doc => <<-'DOC') do |args|
66
Load a YAML file containing an array, string, or hash, and return the data
77
in the corresponding native data type.
8+
The first parameter can be a file path or a URL.
89
The second parameter is the default value. It will be returned if the file
910
was not found or could not be parsed.
1011
1112
For example:
1213
1314
$myhash = loadyaml('/etc/puppet/data/myhash.yaml')
15+
$myhash = loadyaml('https://example.local/my_hash.yaml')
1416
$myhash = loadyaml('no-file.yaml', {'default' => 'value'})
1517
DOC
1618

1719
raise ArgumentError, 'Wrong number of arguments. 1 or 2 arguments should be provided.' unless args.length >= 1
1820
require 'yaml'
19-
20-
if File.exists?(args[0]) # rubocop:disable Lint/DeprecatedClassMethods : Changing to .exist? breaks the code
21-
begin
21+
require 'open-uri'
22+
begin
23+
if args[0].start_with?('http://', 'https://')
24+
begin
25+
contents = OpenURI.open_uri(args[0])
26+
rescue OpenURI::HTTPError => err
27+
res = err.io
28+
warning("Can't load '#{args[0]}' HTTP Error Code: '#{res.status[0]}'")
29+
args[1]
30+
end
31+
YAML.safe_load(contents) || args[1]
32+
elsif File.exists?(args[0]) # rubocop:disable Lint/DeprecatedClassMethods : Changing to .exist? breaks the code
2233
YAML.load_file(args[0]) || args[1]
23-
rescue StandardError => e
24-
raise e unless args[1]
34+
else
35+
warning("Can't load '#{args[0]}' File does not exist!")
2536
args[1]
2637
end
27-
else
28-
warning("Can't load '#{args[0]}' File does not exist!")
38+
rescue StandardError => e
39+
raise e unless args[1]
2940
args[1]
3041
end
3142
end

spec/functions/loadjson_spec.rb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,43 @@
6565
end
6666
it { is_expected.to run.with_params(filename, 'default' => 'value').and_return('default' => 'value') }
6767
end
68+
69+
context 'when an existing URL is specified' do
70+
let(:filename) do
71+
'https://example.local/myhash.json'
72+
end
73+
let(:data) { { 'key' => 'value', 'ķęŷ' => 'νậŀųề', 'キー' => '値' } }
74+
let(:json) { '{"key":"value", {"ķęŷ":"νậŀųề" }, {"キー":"値" }' }
75+
76+
it {
77+
expect(OpenURI).to receive(:open_uri).with(filename).and_return(json)
78+
expect(PSON).to receive(:load).with(json).and_return(data).once
79+
is_expected.to run.with_params(filename).and_return(data)
80+
}
81+
end
82+
83+
context 'when the URL output could not be parsed, with default specified' do
84+
let(:filename) do
85+
'https://example.local/myhash.json'
86+
end
87+
let(:json) { ',;{"key":"value"}' }
88+
89+
it {
90+
expect(OpenURI).to receive(:open_uri).with(filename).and_return(json)
91+
expect(PSON).to receive(:load).with(json).once.and_raise StandardError, 'Something terrible have happened!'
92+
is_expected.to run.with_params(filename, 'default' => 'value').and_return('default' => 'value')
93+
}
94+
end
95+
96+
context 'when the URL does not exist, with default specified' do
97+
let(:filename) do
98+
'https://example.local/myhash.json'
99+
end
100+
101+
it {
102+
expect(OpenURI).to receive(:open_uri).with(filename).and_raise OpenURI::HTTPError, '404 File not Found'
103+
is_expected.to run.with_params(filename, 'default' => 'value').and_return('default' => 'value')
104+
}
105+
end
68106
end
69107
end

spec/functions/loadyaml_spec.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,37 @@
3636
end
3737
it { is_expected.to run.with_params(filename, 'default' => 'value').and_return('default' => 'value') }
3838
end
39+
40+
context 'when an existing URL is specified' do
41+
let(:filename) { 'https://example.local/myhash.yaml' }
42+
let(:yaml) { 'Dummy YAML' }
43+
let(:data) { { 'key' => 'value', 'ķęŷ' => 'νậŀųề', 'キー' => '値' } }
44+
45+
it {
46+
expect(OpenURI).to receive(:open_uri).with(filename).and_return(yaml)
47+
expect(YAML).to receive(:safe_load).with(yaml).and_return(data).once
48+
is_expected.to run.with_params(filename).and_return(data)
49+
}
50+
end
51+
52+
context 'when an existing URL could not be parsed, with default specified' do
53+
let(:filename) { 'https://example.local/myhash.yaml' }
54+
let(:yaml) { 'Dummy YAML' }
55+
56+
it {
57+
expect(OpenURI).to receive(:open_uri).with(filename).and_return(yaml)
58+
expect(YAML).to receive(:safe_load).with(yaml).and_raise StandardError, 'Cannot parse data'
59+
is_expected.to run.with_params(filename, 'default' => 'value').and_return('default' => 'value')
60+
}
61+
end
62+
63+
context 'when a URL does not exist, with default specified' do
64+
let(:filename) { 'https://example.local/myhash.yaml' }
65+
let(:yaml) { 'Dummy YAML' }
66+
67+
it {
68+
expect(OpenURI).to receive(:open_uri).with(filename).and_raise OpenURI::HTTPError, '404 File not Found'
69+
is_expected.to run.with_params(filename, 'default' => 'value').and_return('default' => 'value')
70+
}
71+
end
3972
end

0 commit comments

Comments
 (0)