4 changes: 4 additions & 0 deletions .sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,9 @@ Gemfile:
spec/spec_helper.rb:
requires:
- webmock/rspec
custom_facts:
- name: service_provider
value: systemd
source: puppetlabs-stdlib
spec/spec_helper_acceptance.rb:
locale_workaround: true
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## [20.2.0](https://github.com/theforeman/puppet-foreman/tree/20.2.0) (2022-06-21)

[Full Changelog](https://github.com/theforeman/puppet-foreman/compare/20.1.0...20.2.0)

**Implemented enhancements:**

- add foreman\_global\_parameter type [\#1054](https://github.com/theforeman/puppet-foreman/pull/1054) ([jhoblitt](https://github.com/jhoblitt))
- derive base\_url from foreman-proxy/settings.yml by default [\#1053](https://github.com/theforeman/puppet-foreman/pull/1053) ([jhoblitt](https://github.com/jhoblitt))

## [20.1.0](https://github.com/theforeman/puppet-foreman/tree/20.1.0) (2022-05-24)

[Full Changelog](https://github.com/theforeman/puppet-foreman/compare/20.0.0...20.1.0)
Expand Down
137 changes: 137 additions & 0 deletions lib/puppet/provider/foreman_global_parameter/rest_v3.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# frozen_string_literal: true

Puppet::Type.type(:foreman_global_parameter)
.provide(
:rest_v3,
parent: Puppet::Type.type(:foreman_resource).provider(:rest_v3)
) do
confine feature: %i[json oauth]

mk_resource_methods

# Note that #request is an instance method and as such prevents
# self.instances and self.prefetch from being implimented.

# The foreman api has both `hidden_value` and `hidden_value?` fields. The
# later is the boolean value.
def hidden_value
@property_hash[:hidden_value?]
end

def hidden_value=(value)
@property_hash[:hidden_value?] = value
end

def create
@property_hash[:ensure] = :present
end

def exists?
get_common_parameter!
@property_hash[:ensure] == :present
end

def destroy
@property_hash[:ensure] = :absent
end

def flush
case self.ensure
when :present
# if there is already an id, this is an update
if id.nil?
create_common_parameter
else
update_common_parameter
end
when :absent
destroy_common_parameter
else
raise Puppet::Error, "invalid :ensure value: #{self.ensure}"
end
end

private

def id
@property_hash[:id]
end

def base_path
'api/v2/common_parameters'
end

def get_common_parameter!
path = base_path
params = {
search: "name=#{resource[:name]}",
show_hidden: true
}
r = request(:get, base_path, params)

raise Puppet::Error, "Error making GET request to Foreman at #{request_uri(path)}: #{error_message(r)}" unless success?(r)

begin
results = JSON.parse(r.body)['results'].first
rescue JSON::ParserError
raise Puppet::Error, "unable to parse as JSON: #{r.body}"
end

if results.nil?
@property_hash[:ensure] = :absent
else
@property_hash = results.transform_keys(&:to_sym).transform_values do |v|
resource.munge_boolean_to_symbol(v)
end
@property_hash[:ensure] = :present
end
end

def create_common_parameter
path = base_path
data = {
common_parameter: {
name: resource[:name],
value: resource[:value],
parameter_type: resource[:parameter_type]
}
}
data[:hidden_value] = resource[:hidden_value] unless resource[:hidden_value].nil?
data.transform_values! { |v| resource.munge_symbol_to_boolean(v) }
r = request(:post, path, {}, data.to_json)

return if success?(r)

raise Puppet::Error, "Error making POST request to Foreman at #{request_uri(path)}: #{error_message(r)}"
end

def update_common_parameter
path = "#{base_path}/#{id}"
data = {
common_parameter: {
name: resource[:name],
value: resource[:value],
parameter_type: resource[:parameter_type]
}
}
data[:hidden_value] = resource[:hidden_value] unless resource[:hidden_value].nil?
data.transform_values! { |v| resource.munge_symbol_to_boolean(v) }
r = request(:put, path, {}, data.to_json)

return if success?(r)

raise Puppet::Error, "Error making PUT request to Foreman at #{request_uri(path)}: #{error_message(r)}"
end

def destroy_common_parameter
path = "#{base_path}/#{id}"
r = request(:delete, path)

unless success?(r)
error_string = "Error making DELETE request to Foreman at #{request_uri(path)}: #{error_message(r)}"
raise Puppet::Error, error_string
end

@property_hash.clear
end
end
20 changes: 17 additions & 3 deletions lib/puppet/provider/foreman_resource/rest_v3.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,23 @@ def oauth_consumer_secret
end
end

def foreman_url
@foreman_url ||= begin
if resource[:base_url]
resource[:base_url]
else
begin
YAML.load_file('/etc/foreman-proxy/settings.yml')[:foreman_url]
rescue
fail "Resource #{resource[:name]} cannot be managed: No base_url available"
end
end
end
end

def oauth_consumer
@consumer ||= OAuth::Consumer.new(oauth_consumer_key, oauth_consumer_secret, {
:site => resource[:base_url],
:site => foreman_url,
:request_token_path => '',
:authorize_path => '',
:access_token_path => '',
Expand All @@ -56,7 +70,7 @@ def generate_token
end

def request_uri(path)
base_url = resource[:base_url]
base_url = foreman_url
base_url += '/' unless base_url.end_with?('/')
URI.join(base_url, path)
end
Expand Down Expand Up @@ -99,7 +113,7 @@ def success?(response)
end

def error_message(response)
fqdn = URI::parse(resource[:base_url]).host
fqdn = URI::parse(foreman_url).host

explanations = {
'400' => "Something is wrong with the data sent to Foreman at #{fqdn}",
Expand Down
65 changes: 65 additions & 0 deletions lib/puppet/type/foreman_global_parameter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# frozen_string_literal: true

require_relative '../../puppet_x/foreman/common'

Puppet::Type.newtype(:foreman_global_parameter) do
desc 'foreman_global_parameter Manipulate global parameters'

instance_eval(&PuppetX::Foreman::Common::REST_API_COMMON_PARAMS)

newparam(:name, namevar: true) do
desc 'Parameter name'
end

newproperty(:parameter_type) do
desc 'Type of the parameter'

munge { |v| v.to_s }
end

newproperty(:value) do
desc 'Parameter value'

munge { |v| @resource.munge_boolean_to_symbol(v) }
end

newproperty(:hidden_value) do
desc 'Should the value be hidden'

munge { |v| @resource.munge_boolean_to_symbol(v) }

validate do |v|
unless [TrueClass, FalseClass].include?(v.class)
raise Puppet::ResourceError, 'hidden_value expected a boolean value.'
end
end
end

# Convert booleans into symbols to work around parameter/property handling of
# booleans being broken.
#
# See:
# https://tickets.puppetlabs.com/browse/PUP-2368
# https://tickets.puppetlabs.com/browse/PUP-8442
def munge_boolean_to_symbol(value)
case value
when TrueClass
:true
when FalseClass
:false
else
value
end
end

def munge_symbol_to_boolean(value)
case value
when :true
true
when :false
false
else
value
end
end
end
2 changes: 1 addition & 1 deletion metadata.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "theforeman-foreman",
"version": "20.1.0",
"version": "20.2.0",
"author": "theforeman",
"summary": "Foreman server configuration",
"license": "GPL-3.0+",
Expand Down
76 changes: 76 additions & 0 deletions spec/acceptance/foreman_global_parameter_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
require 'spec_helper_acceptance'

shared_examples 'global-parameter' do |name, type, v|
describe command("hammer global-parameter list --search name=#{name} --fields type") do
its(:stdout) { is_expected.to include(type.to_s) }
end
describe command("hammer global-parameter list --search name=#{name} --fields value") do
its(:stdout) { is_expected.to include(v.to_s) }
end
end

describe 'Scenario: install foreman and manage some global parameters', order: :defined do
before(:context) { purge_foreman }

it_behaves_like 'an idempotent resource' do
let(:manifest) do
<<-PUPPET
include foreman
include foreman::cli
Foreman_global_parameter {
ensure => present,
base_url => $foreman::foreman_url,
consumer_key => $foreman::oauth_consumer_key,
consumer_secret => $foreman::oauth_consumer_secret,
ssl_ca => $foreman::server_ssl_ca,
timeout => 5,
}
foreman_global_parameter { 'boolean-true-test':
parameter_type => 'boolean',
value => true,
}
foreman_global_parameter { 'boolean-false-test':
parameter_type => 'boolean',
value => false,
}
PUPPET
end
end

it_behaves_like 'the foreman application'
it_behaves_like 'hammer'

it_behaves_like 'global-parameter', 'boolean-true-test', 'boolean', true
it_behaves_like 'global-parameter', 'boolean-false-test', 'boolean', false

describe 'removing a hostgroup' do
it_behaves_like 'an idempotent resource' do
let(:manifest) do
<<-PUPPET
include foreman
Foreman_global_parameter {
ensure => present,
base_url => $foreman::foreman_url,
consumer_key => $foreman::oauth_consumer_key,
consumer_secret => $foreman::oauth_consumer_secret,
ssl_ca => $foreman::server_ssl_ca,
timeout => 5,
}
foreman_global_parameter { 'boolean-true-test':
ensure => absent,
}
PUPPET
end
end

describe command('hammer global-parameter list --search name=boolean-true-test') do
its(:stdout) { is_expected.not_to include('boolean-true-test') }
end
end
end
2 changes: 2 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

require 'voxpupuli/test/spec_helper'

add_custom_fact :service_provider, "systemd" # puppetlabs-stdlib

def get_content(subject, title)
is_expected.to contain_file(title)
content = subject.resource('file', title).send(:parameters)[:content]
Expand Down
Loading