diff --git a/REFERENCE.md b/REFERENCE.md
index fbf5f00..90fac7d 100644
--- a/REFERENCE.md
+++ b/REFERENCE.md
@@ -12,6 +12,10 @@
* [`vas::api_fetch`](#vas--api_fetch): Query a remote HTTP-based service for entries to be added to users_allow.
+### Data types
+
+* [`Vas::API::Config`](#Vas--API--Config): API configuration
+
## Classes
### `vas`
@@ -167,6 +171,7 @@ The following parameters are available in the `vas` class:
* [`api_users_allow_url`](#-vas--api_users_allow_url)
* [`api_token`](#-vas--api_token)
* [`api_ssl_verify`](#-vas--api_ssl_verify)
+* [`api_config`](#-vas--api_config)
##### `manage_nis`
@@ -1186,6 +1191,7 @@ Default value: `false`
Data type: `Optional[Stdlib::HTTPSUrl]`
The URL towards the API.
+Deprecated parameter, replaced by $api_config. Will be removed next major releaase.
Default value: `undef`
@@ -1194,6 +1200,7 @@ Default value: `undef`
Data type: `Optional[String[1]]`
Security token for authenticated access to the API.
+Deprecated parameter, replaced by $api_config. Will be removed next major releaase.
Default value: `undef`
@@ -1202,9 +1209,18 @@ Default value: `undef`
Data type: `Boolean`
Whether TLS connections should be verified or not.
+Deprecated parameter, replaced by $api_config. Will be removed next major releaase
Default value: `false`
+##### `api_config`
+
+Data type: `Optional[Vas::API::Config]`
+
+API configuration
+
+Default value: `undef`
+
## Functions
### `vas::api_fetch`
@@ -1218,10 +1234,19 @@ Query a remote HTTP-based service for entries to be added to users_allow.
##### Calling the function
```puppet
-vas::api_fetch("https://host.domain.tld/api/${facts['trusted.certname']}")
+vas::api_fetch([{'url' => "https://host.domain.tld/api/${facts['trusted.certname']}"}])
+```
+
+##### Multiple servers with different tokens, ssl_verify enabled
+
+```puppet
+vas::api_fetch([
+ {'url' => "https://host1.domain.tld/api/${facts['trusted.certname']}", 'token' => 'token123', 'ssl_verify' => true},
+ {'url' => "https://host2.domain.tld/api/${facts['trusted.certname']}", 'token' => 'token321', 'ssl_verify' => true},
+])
```
-#### `vas::api_fetch(Stdlib::HTTPUrl $url, String[1] $token, Optional[Boolean] $ssl_verify)`
+#### `vas::api_fetch(Vas::API::Config $config)`
Query a remote HTTP-based service for entries to be added to users_allow.
@@ -1232,24 +1257,37 @@ Returns: `Hash`
###### Calling the function
```puppet
-vas::api_fetch("https://host.domain.tld/api/${facts['trusted.certname']}")
+vas::api_fetch([{'url' => "https://host.domain.tld/api/${facts['trusted.certname']}"}])
```
-##### `url`
+###### Multiple servers with different tokens, ssl_verify enabled
-Data type: `Stdlib::HTTPUrl`
+```puppet
+vas::api_fetch([
+ {'url' => "https://host1.domain.tld/api/${facts['trusted.certname']}", 'token' => 'token123', 'ssl_verify' => true},
+ {'url' => "https://host2.domain.tld/api/${facts['trusted.certname']}", 'token' => 'token321', 'ssl_verify' => true},
+])
+```
-URL to connect to
+##### `config`
-##### `token`
+Data type: `Vas::API::Config`
-Data type: `String[1]`
+Hash with API configuration
-Token used for authentication
+## Data types
-##### `ssl_verify`
+### `Vas::API::Config`
-Data type: `Optional[Boolean]`
+API configuration
+
+Alias of
-Whether TLS connections should be verified or not
+```puppet
+Array[Struct[
+ url => Stdlib::HttpsUrl,
+ token => Optional[String[1]],
+ ssl_verify => Optional[Boolean],
+ ]]
+```
diff --git a/lib/puppet/functions/vas/api_fetch.rb b/lib/puppet/functions/vas/api_fetch.rb
index 3f5ab74..d5e56b2 100644
--- a/lib/puppet/functions/vas/api_fetch.rb
+++ b/lib/puppet/functions/vas/api_fetch.rb
@@ -3,52 +3,60 @@
require 'net/http'
require 'net/https'
require 'openssl'
- # @param url URL to connect to
- # @param token Token used for authentication
- # @param ssl_verify Whether TLS connections should be verified or not
+ # @param config Hash with API configuration
# @return Key 'content' with [Array] if API responds. Key 'errors' with [Array[String]] if errors happens.
# @example Calling the function
- # vas::api_fetch("https://host.domain.tld/api/${facts['trusted.certname']}")
+ # vas::api_fetch([{'url' => "https://host.domain.tld/api/${facts['trusted.certname']}"}])
+ # @example Multiple servers with different tokens, ssl_verify enabled
+ # vas::api_fetch([
+ # {'url' => "https://host1.domain.tld/api/${facts['trusted.certname']}", 'token' => 'token123', 'ssl_verify' => true},
+ # {'url' => "https://host2.domain.tld/api/${facts['trusted.certname']}", 'token' => 'token321', 'ssl_verify' => true},
+ # ])
+ #
dispatch :api_fetch do
- param 'Stdlib::HTTPUrl', :url
- param 'String[1]', :token
- optional_param 'Boolean', :ssl_verify
+ param 'Vas::API::Config', :config
return_type 'Hash'
end
- def api_fetch(url, token, ssl_verify = false)
- uri = URI.parse(url)
+ def api_fetch(config)
+ data = {}
- req = Net::HTTP::Get.new(uri.to_s)
- req['Authorization'] = "Bearer #{token}"
- req['Accept'] = 'text/plain'
+ config.shuffle.each do |entry|
+ url = entry['url']
+ uri = URI.parse(url)
- https = Net::HTTP.new(uri.host, uri.port)
- https.use_ssl = true
- unless ssl_verify
- https.verify_mode = OpenSSL::SSL::VERIFY_NONE
- end
- https.open_timeout = 2
- https.read_timeout = 2
+ req = Net::HTTP::Get.new(uri.to_s)
+ req['Authorization'] = "Bearer #{entry['token']}" if entry.key?('token')
+ req['Accept'] = 'text/plain'
- data = {}
- begin
- response = https.start do |cx|
- cx.request(req)
+ https = Net::HTTP.new(uri.host, uri.port)
+ https.use_ssl = true
+ unless entry.key?('ssl_verify')
+ https.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
+ https.open_timeout = 2
+ https.read_timeout = 2
+
+ begin
+ response = https.start do |cx|
+ cx.request(req)
+ end
- case response
- when Net::HTTPSuccess
- data['content'] = if response.body.empty?
- []
- else
- response.body.split("\n")
- end
- else
- (data['errors'] ||= []) << "#{url} returns HTTP code: #{response.code}"
+ case response
+ when Net::HTTPSuccess
+ data['content'] = if response.body.empty?
+ []
+ else
+ response.body.split("\n")
+ end
+ # Successful response received, break loop
+ break
+ else
+ (data['errors'] ||= []) << "#{url} returns HTTP code: #{response.code}"
+ end
+ rescue => error
+ (data['errors'] ||= []) << "#{url} connection failed: #{error.message}"
end
- rescue => error
- (data['errors'] ||= []) << "#{url} connection failed: #{error.message}"
end
data
diff --git a/manifests/init.pp b/manifests/init.pp
index f5d4e46..5a83900 100644
--- a/manifests/init.pp
+++ b/manifests/init.pp
@@ -465,12 +465,18 @@
#
# @param api_users_allow_url
# The URL towards the API.
+# Deprecated parameter, replaced by $api_config. Will be removed next major releaase.
#
# @param api_token
# Security token for authenticated access to the API.
+# Deprecated parameter, replaced by $api_config. Will be removed next major releaase.
#
# @param api_ssl_verify
# Whether TLS connections should be verified or not.
+# Deprecated parameter, replaced by $api_config. Will be removed next major releaase
+#
+# @param api_config
+# API configuration
class vas (
Boolean $manage_nis = true,
String[1] $package_version = 'installed',
@@ -587,6 +593,7 @@
Array[String[1]] $kpasswd_servers = [],
Stdlib::Port $kpasswd_server_port = 464,
Boolean $api_enable = false,
+ Optional[Vas::API::Config] $api_config = undef,
Optional[Stdlib::HTTPSUrl] $api_users_allow_url = undef,
Optional[String[1]] $api_token = undef,
Boolean $api_ssl_verify = false,
@@ -673,10 +680,22 @@
}
# functionality
- if $api_enable == true and ($api_users_allow_url == undef or $api_token == undef) {
- fail('vas::api_enable is set to true but required parameters vas::api_users_allow_url and/or vas::api_token missing')
- } elsif $api_enable == true {
- $api_users_allow_data = vas::api_fetch($api_users_allow_url, $api_token, $api_ssl_verify)
+ if $api_enable == true {
+ if $api_config {
+ $api_config_real = $api_config
+ } elsif $api_users_allow_url and $api_token {
+ warning('$api_users_allow_url and $api_token deprecated and will be removed next major release. Use $api_config')
+
+ $api_config_real = [{
+ 'url' => $api_users_allow_url,
+ 'token' => $api_token,
+ 'ssl_verify' => $api_ssl_verify,
+ }]
+ } else {
+ fail('vas::api_enable is set to true but required parameter $api_config missing')
+ }
+
+ $api_users_allow_data = vas::api_fetch($api_config_real)
if $api_users_allow_data['content'] {
$manage_users_allow = true
diff --git a/spec/classes/data_types_spec.rb b/spec/classes/data_types_spec.rb
index ee4fac6..0e4d647 100644
--- a/spec/classes/data_types_spec.rb
+++ b/spec/classes/data_types_spec.rb
@@ -13,13 +13,6 @@
],
}
- headers = {
- 'Accept' => 'text/plain',
- 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
- 'Authorization' => 'Bearer somesecret',
- 'User-Agent' => 'Ruby'
- }
-
on_supported_os(test_on).each do |_os, os_facts|
describe 'variable data type and content validations' do
let(:node) { 'data-types.example.com' }
@@ -49,7 +42,7 @@
},
'Boolean/API' => {
name: ['api_enable'],
- params: { api_users_allow_url: 'https://api.example.local', api_token: 'somesecret', },
+ params: { api_config: [{ 'url': 'https://api.example.local', 'token': 'somesecret' }] },
valid: [true, false],
invalid: ['true', 'false', ['array'], { 'ha' => 'sh' }, 3, 2.42, nil],
message: 'expects a Boolean',
@@ -172,7 +165,15 @@
invalid: ['true', 'false', 'string', ['array'], { 'ha' => 'sh' }, 3, 2.42],
message: 'expects a value of type Boolean or Enum',
},
-
+ 'Vas::API::Config' => {
+ name: ['api_config'],
+ params: { api_enable: true },
+ valid: [[{ 'url': 'https://test.ing' }],
+ [{ 'url': 'https://test.ing', 'token': 'mysecret', 'ssl_verify': true }],
+ [{ 'url': 'https://test.ing', 'token': 'mysecret', 'ssl_verify': true }, { 'url': 'https://test.ing' }]],
+ invalid: ['http://str.ing', 'string', ['array'], { 'ha' => 'sh' }, 3, 2.42, false],
+ message: "(parameter 'api_config' expects a Vas::API::Config|parameter 'api_config' index 0 expects a Struct value)",
+ },
}
validations.sort.each do |type, var|
mandatory_params = {} if mandatory_params.nil?
@@ -184,10 +185,8 @@
it do
stub_request(:get, 'https://test.ing/')
- .with(headers: headers)
stub_request(:get, 'https://api.example.local')
- .with(headers: headers)
is_expected.to compile
end
diff --git a/spec/classes/init_spec.rb b/spec/classes/init_spec.rb
index 9b52802..f631c42 100644
--- a/spec/classes/init_spec.rb
+++ b/spec/classes/init_spec.rb
@@ -279,45 +279,47 @@
end
it {
- is_expected.to compile.and_raise_error(%r{vas::api_enable is set to true but required parameters vas::api_users_allow_url and/or vas::api_token missing})
+ is_expected.to compile.and_raise_error(%r{vas::api_enable is set to true but required parameter \$api_config missing})
}
context 'param "api_users_allow_url" set' do
let(:params) do
- api_enabled.merge(
+ super().merge(
'api_users_allow_url': 'https://host.domain.tld',
)
end
it {
- is_expected.to compile.and_raise_error(%r{vas::api_enable is set to true but required parameters vas::api_users_allow_url and/or vas::api_token missing})
+ is_expected.to compile.and_raise_error(%r{vas::api_enable is set to true but required parameter \$api_config missing})
}
end
context 'param "api_token" set' do
let(:params) do
- api_enabled.merge(
+ super().merge(
'api_token': 'mytoken',
)
end
it {
- is_expected.to compile.and_raise_error(%r{vas::api_enable is set to true but required parameters vas::api_users_allow_url and/or vas::api_token missing})
+ is_expected.to compile.and_raise_error(%r{vas::api_enable is set to true but required parameter \$api_config missing})
}
end
context 'with required parameters' do
let(:params) do
- api_enabled.merge(
- 'api_users_allow_url': 'https://host.domain.tld',
- 'api_token': 'mytoken',
+ super().merge(
+ 'api_config': [{
+ 'url': 'https://host.domain.tld',
+ 'token': 'mytoken',
+ }],
)
end
context 'and queries successfully' do
context 'with no return entries' do
let(:pre_condition) do
- 'function vas::api_fetch($api_users_allow_url, $api_token, $api_ssl_verify) {
+ 'function vas::api_fetch($api_config) {
return { content => [] }
}'
end
@@ -333,9 +335,7 @@
context 'and users_allow parameter specified' do
let(:params) do
- api_enabled.merge(
- 'api_users_allow_url': 'https://host.domain.tld',
- 'api_token': 'mytoken',
+ super().merge(
'users_allow_entries': ['user1@example.com', 'user2@example.com'],
)
end
@@ -355,7 +355,7 @@
context 'with it returning "apiuser@example.com"' do
let(:pre_condition) do
- 'function vas::api_fetch($api_users_allow_url, $api_token, $api_ssl_verify) {
+ 'function vas::api_fetch($api_config) {
return { content => ["apiuser@example.com"]}
}'
end
@@ -372,9 +372,7 @@
context 'and users_allow parameter specified' do
let(:params) do
- api_enabled.merge(
- 'api_users_allow_url': 'https://host.domain.tld',
- 'api_token': 'mytoken',
+ super().merge(
'users_allow_entries': ['user1@example.com', 'user2@example.com'],
)
end
@@ -396,7 +394,7 @@
context 'and queries fails' do
let(:pre_condition) do
- 'function vas::api_fetch($api_users_allow_url, $api_token, $api_ssl_verify) {
+ 'function vas::api_fetch($api_data) {
return { error => ["https://host.domain.tld returns HTTP code: 502"] }
}'
end
diff --git a/spec/classes/parameter_spec.rb b/spec/classes/parameter_spec.rb
index 4a0f5b2..fdff0ce 100644
--- a/spec/classes/parameter_spec.rb
+++ b/spec/classes/parameter_spec.rb
@@ -905,7 +905,7 @@
end
it 'fails' do
- expect { is_expected.to contain_class('vas') }.to raise_error(Puppet::Error, %r{api_token missing})
+ expect { is_expected.to contain_class('vas') }.to raise_error(Puppet::Error, %r{api_config missing})
end
end
@@ -918,7 +918,7 @@
}
end
let(:pre_condition) do
- 'function vas::api_fetch($api_users_allow_url, $api_token, $api_ssl_verify) {
+ 'function vas::api_fetch($api_data) {
return { content => [] }
}'
end
@@ -937,7 +937,7 @@
}
end
let(:pre_condition) do
- 'function vas::api_fetch($api_users_allow_url, $api_token, $api_ssl_verify) {
+ 'function vas::api_fetch($api_data) {
return { content => ["apiuser@test.ing"] }
}'
end
@@ -957,7 +957,7 @@
}
end
let(:pre_condition) do
- 'function vas::api_fetch($api_users_allow_url, $api_token, $api_ssl_verify) {
+ 'function vas::api_fetch($api_data) {
return { content => [] }
}'
end
@@ -977,7 +977,7 @@
}
end
let(:pre_condition) do
- 'function vas::api_fetch($api_users_allow_url, $api_token, $api_ssl_verify) {
+ 'function vas::api_fetch($api_data) {
return { content => ["apiuser@test.ing"] }
}'
end
@@ -1002,7 +1002,7 @@
end
it 'fails' do
- expect { is_expected.to contain_class('vas') }.to raise_error(Puppet::Error, %r{api_users_allow_url .* missing})
+ expect { is_expected.to contain_class('vas') }.to raise_error(Puppet::Error, %r{api_config missing})
end
end
@@ -1015,7 +1015,7 @@
}
end
let(:pre_condition) do
- 'function vas::api_fetch($api_users_allow_url, $api_token, $api_ssl_verify) {
+ 'function vas::api_fetch($api_data) {
return { content => [] }
}'
end
@@ -1034,7 +1034,7 @@
}
end
let(:pre_condition) do
- 'function vas::api_fetch($api_users_allow_url, $api_token, $api_ssl_verify) {
+ 'function vas::api_fetch($api_data) {
return { content => "apiuser@test.ing" }
}'
end
diff --git a/spec/functions/api_fetch_spec.rb b/spec/functions/api_fetch_spec.rb
index ba2ca44..7ac8b9b 100644
--- a/spec/functions/api_fetch_spec.rb
+++ b/spec/functions/api_fetch_spec.rb
@@ -12,41 +12,33 @@
}
url = 'https://api.example.local/'
-
- describe 'raises an error when arguments are missing' do
+ params = [
+ {
+ 'url' => url,
+ 'token' => 'somesecret',
+ },
+ ]
+
+ describe 'raises an error when parameters are missing' do
describe 'no arguments' do
it do
is_expected.to run
.with_params
- .and_raise_error(ArgumentError, '\'vas::api_fetch\' expects between 2 and 3 arguments, got none')
+ .and_raise_error(ArgumentError, '\'vas::api_fetch\' expects 1 argument, got none')
end
end
- describe 'token argument is missing' do
+ describe 'when required key url is missing in $config' do
+ params_missing = [{}]
+
it do
is_expected.to run
- .with_params(url)
- .and_raise_error(ArgumentError, '\'vas::api_fetch\' expects between 2 and 3 arguments, got 1')
+ .with_params(params_missing)
+ .and_raise_error(ArgumentError, '\'vas::api_fetch\' parameter \'config\' index 0 expects size to be between 1 and 3, got 0')
end
end
end
- describe 'raises an error when url argument is not a string' do
- it do
- is_expected.to run
- .with_params(1, 'somesecret')
- .and_raise_error(ArgumentError, %r{'vas::api_fetch' parameter 'url' expects a match for Stdlib::HTTPUrl.* got Integer})
- end
- end
-
- describe 'raises an error when token argument is not a string' do
- it do
- is_expected.to run
- .with_params(url, 1)
- .and_raise_error(ArgumentError, '\'vas::api_fetch\' parameter \'token\' expects a String value, got Integer')
- end
- end
-
describe 'api call' do
it 'when request times out' do
stub_request(:get, url).with(
@@ -54,11 +46,11 @@
).to_timeout
is_expected.to run
- .with_params(url, 'somesecret')
+ .with_params(params)
.and_return({ 'errors' => ['https://api.example.local/ connection failed: execution expired'] })
end
- it 'returns an array containing http response code and body' do
+ it 'returns a hash containing key \'content\' with an array of contents' do
response_body = "line1\nline2"
stub_request(:get, url).with(
@@ -66,11 +58,11 @@
).to_return(body: response_body, status: 200)
is_expected.to run
- .with_params(url, 'somesecret')
+ .with_params(params)
.and_return({ 'content' => ['line1', 'line2'] })
end
- it 'returns an array containing http response code and an empty array when response body is empty' do
+ it 'returns a hash containing key \'content\' with an empty array' do
response_body = ''
stub_request(:get, url).with(
@@ -78,27 +70,27 @@
).to_return(body: response_body, status: 200)
is_expected.to run
- .with_params(url, 'somesecret')
+ .with_params(params)
.and_return({ 'content' => [] })
end
- it 'returns nil when http response code is not success' do
+ it 'returns a hash containing key \'errors\' when non-sucess http response code is received' do
stub_request(:get, url).with(
headers: headers,
).to_return(body: nil, status: 404)
is_expected.to run
- .with_params(url, 'somesecret')
+ .with_params(params)
.and_return({ 'errors' => ['https://api.example.local/ returns HTTP code: 404'] })
end
- it 'returns an array containing 0 and error when error occurs' do
+ it 'returns a hash containing key \'errors\' any other error occurs' do
stub_request(:get, url).with(
headers: headers,
).and_raise(StandardError.new('error'))
is_expected.to run
- .with_params(url, 'somesecret')
+ .with_params(params)
.and_return({ 'errors' => ['https://api.example.local/ connection failed: error'] })
end
end
diff --git a/types/api/config.pp b/types/api/config.pp
new file mode 100644
index 0000000..62b954a
--- /dev/null
+++ b/types/api/config.pp
@@ -0,0 +1,8 @@
+# @summary API configuration
+type Vas::API::Config = Array[
+ Struct[
+ url => Stdlib::HttpsUrl,
+ token => Optional[String[1]],
+ ssl_verify => Optional[Boolean],
+ ]
+]