1 change: 1 addition & 0 deletions .github/workflows/acceptance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
bundle config path vendor/bundle
bundle config without 'development test'
bundle install --jobs 4 --retry 3
acceptance:
needs: build_cache
runs-on: ubuntu-latest
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/cron.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Nightly tests

on:
schedule:
- cron: '4 * * * *'
- cron: '4 4 * * *'

jobs:
unit:
Expand Down Expand Up @@ -71,6 +71,7 @@ jobs:
bundle config path vendor/bundle
bundle config without 'development test'
bundle install --jobs 4 --retry 3
acceptance:
if: github.repository == 'theforeman/puppet-foreman'
needs: build_cache
Expand All @@ -81,6 +82,8 @@ jobs:
setfile:
- centos7-64{hostname=centos7-64.example.com}
- centos8-64{hostname=centos8-64.example.com}
- debian10-64{hostname=debian10-64.example.com}
- ubuntu1804-64{hostname=ubuntu1804-64.example.com}
puppet:
- "6"
- "5"
Expand Down
9 changes: 0 additions & 9 deletions .sync.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
---
.travis.yml:
beaker_sets:
- centos7-64
- centos8-64
- debian10-64
- ubuntu1804-64
env:
global:
- PARALLEL_TEST_PROCESSORS=8
Gemfile:
extra:
- gem: webmock
Expand Down
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# Changelog

## [16.1.0](https://github.com/theforeman/puppet-foreman/tree/16.1.0) (2021-01-28)

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

**Implemented enhancements:**

- Fixes [\#31670](https://projects.theforeman.org/issues/31670) - don't timeout when running db:migrate [\#915](https://github.com/theforeman/puppet-foreman/pull/915) ([evgeni](https://github.com/evgeni))
- Fixes [\#30284](https://projects.theforeman.org/issues/30284) - Improve smartproxy registration failure error messages [\#912](https://github.com/theforeman/puppet-foreman/pull/912) ([wbclark](https://github.com/wbclark))
- Set the reverse proxy host to the name of the service [\#909](https://github.com/theforeman/puppet-foreman/pull/909) ([ehelms](https://github.com/ehelms))
- Use apache::mod::auth\_openidc [\#906](https://github.com/theforeman/puppet-foreman/pull/906) ([ekohl](https://github.com/ekohl))
- CLI: Allow to configure use\_sessions setting [\#905](https://github.com/theforeman/puppet-foreman/pull/905) ([neomilium](https://github.com/neomilium))
- CLI: make refresh\_cache and request\_timeout params global [\#884](https://github.com/theforeman/puppet-foreman/pull/884) ([neomilium](https://github.com/neomilium))
- Fixes [\#30803](https://projects.theforeman.org/issues/30803): Bind to socket for Puma and Apache [\#883](https://github.com/theforeman/puppet-foreman/pull/883) ([ehelms](https://github.com/ehelms))

**Fixed bugs:**

- Fix URI.escape deprecation warning [\#911](https://github.com/theforeman/puppet-foreman/pull/911) ([ekohl](https://github.com/ekohl))

**Merged pull requests:**

- Drop Puppet \< 3.7.5 version check [\#907](https://github.com/theforeman/puppet-foreman/pull/907) ([ekohl](https://github.com/ekohl))

## [16.0.0](https://github.com/theforeman/puppet-foreman/tree/16.0.0) (2020-10-30)

[Full Changelog](https://github.com/theforeman/puppet-foreman/compare/15.1.1...16.0.0)
Expand Down
17 changes: 9 additions & 8 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@

source 'https://rubygems.org'

gem 'puppet', ENV.key?('PUPPET_VERSION') ? "~> #{ENV['PUPPET_VERSION']}" : '>= 5.5'
gem 'puppet', ENV.key?('PUPPET_VERSION') ? "~> #{ENV['PUPPET_VERSION']}" : '>= 5.5', groups: ['development', 'test']
gem 'rake'

gem 'kafo_module_lint'
gem 'puppet-lint-empty_string-check'
gem 'puppet-lint-file_ensure-check'
gem 'puppet-lint-param-docs', '>= 1.3.0'
gem 'puppet-lint-spaceship_operator_without_tag-check'
gem 'puppet-lint-strict_indent-check'
gem 'puppet-lint-undef_in_function-check'
gem 'kafo_module_lint', {"groups"=>["test"]}
gem 'puppet-lint-empty_string-check', {"groups"=>["test"]}
gem 'puppet-lint-file_ensure-check', {"groups"=>["test"]}
gem 'puppet-lint-param-docs', '>= 1.3.0', {"groups"=>["test"]}
gem 'puppet-lint-spaceship_operator_without_tag-check', {"groups"=>["test"]}
gem 'puppet-lint-strict_indent-check', {"groups"=>["test"]}
gem 'puppet-lint-undef_in_function-check', {"groups"=>["test"]}
gem 'voxpupuli-test', '~> 1.4'
gem 'github_changelog_generator', '>= 1.15.0', {"groups"=>["development"]}
gem 'puppet-blacksmith', '>= 6.0.0', {"groups"=>["development"]}
Expand Down
3 changes: 2 additions & 1 deletion lib/puppet/functions/foreman/foreman.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#
# Happy Foreman API-ing!

require "cgi"
require "yaml"
require "net/http"
require "net/https"
Expand All @@ -61,7 +62,7 @@ def foreman(item, search, per_page = "20", foreman_url = "https://localhost", fo
raise Puppet::ParseError, "Foreman: Invalid filter_result: #{filter_result}, must not be boolean true" if filter_result == true

begin
path = URI.escape("/api/#{item}?search=#{search}&per_page=#{per_page}")
path = "/api/#{CGI.escape(item)}?search=#{CGI.escape(search)}&per_page=#{CGI.escape(per_page)}"

req = Net::HTTP::Get.new(path)
req['Content-Type'] = 'application/json'
Expand Down
5 changes: 3 additions & 2 deletions lib/puppet/functions/foreman/smartvar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Foreman holds all the value names and their possible values,
# this function simply ask foreman for the right value for this host.

require "cgi"
require "net/http"
require "net/https"
require "uri"
Expand All @@ -15,15 +16,15 @@
optional_param 'String', :foreman_pass
end

def smartvar(var, foreman_url = "http://foreman", foreman_user = "admin", foreman_pass = "changeme")
def smartvar(var, foreman_url = "http://foreman", foreman_user = "admin", foreman_pass = "changeme")
scope = closure_scope
fqdn = scope['facts']['fqdn']

uri = URI.parse(foreman_url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true if uri.scheme == 'https'

path = URI.escape("/hosts/#{fqdn}/lookup_keys/#{var}")
path = "/hosts/#{CGI.escape(fqdn)}/lookup_keys/#{CGI.escape(var)}"
req = Net::HTTP::Get.new(path)
req.basic_auth(foreman_user, foreman_pass)
req['Content-Type'] = 'application/json'
Expand Down
3 changes: 2 additions & 1 deletion lib/puppet/parser/functions/foreman.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#
# Happy Foreman API-ing!

require "cgi"
require "yaml"
require "net/http"
require "net/https"
Expand Down Expand Up @@ -65,7 +66,7 @@ module Puppet::Parser::Functions
raise Puppet::ParseError, "Foreman: Invalid filter_result: #{filter_result}, must be a String or an Array" unless filter_result.is_a? String or filter_result.is_a? Array or filter_result.is_a? Hash or filter_result == false

begin
path = URI.escape("/api/#{item}?search=#{search}&per_page=#{per_page}")
path = "/api/#{CGI.escape(item)}?search=#{CGI.escape(search)}&per_page=#{CGI.escape(per_page)}"

req = Net::HTTP::Get.new(path)
req['Content-Type'] = 'application/json'
Expand Down
3 changes: 2 additions & 1 deletion lib/puppet/parser/functions/smartvar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# this function simply ask foreman for the right value for this host.


require "cgi"
require "net/http"
require "net/https"
require "uri"
Expand All @@ -24,7 +25,7 @@ module Puppet::Parser::Functions
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true if uri.scheme == 'https'

path = URI.escape("/hosts/#{fqdn}/lookup_keys/#{var}")
path = "/hosts/#{CGI.escape(fqdn)}/lookup_keys/#{CGI.escape(var)}"
req = Net::HTTP::Get.new(path)
req.basic_auth(foreman_user, foreman_pass)
req['Content-Type'] = 'application/json'
Expand Down
30 changes: 24 additions & 6 deletions lib/puppet/provider/foreman_resource/rest_v3.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Base provider for other Puppet types managing Foreman resources
#
# This provider uses Net::HTTP from Ruby stdlib, JSON (stdlib on 1.9+ or the
# gem on 1.8) and the oauth gem for auth, so requiring minimal dependencies.
# This provider uses Net::HTTP from Ruby stdlib, JSON and the oauth gem for
# auth, so requiring minimal dependencies.

require 'cgi'
require 'uri'

Puppet::Type.type(:foreman_resource).provide(:rest_v3) do
Expand Down Expand Up @@ -54,12 +55,15 @@ def generate_token
OAuth::AccessToken.new(oauth_consumer)
end

def request(method, path, params = {}, data = nil, headers = {})
def request_uri(path)
base_url = resource[:base_url]
base_url += '/' unless base_url.end_with?('/')
URI.join(base_url, path)
end

uri = URI.join(base_url, path)
uri.query = params.map { |p,v| "#{URI.escape(p.to_s)}=#{URI.escape(v.to_s)}" }.join('&') unless params.empty?
def request(method, path, params = {}, data = nil, headers = {})
uri = request_uri(path)
uri.query = params.map { |p,v| "#{CGI.escape(p.to_s)}=#{CGI.escape(v.to_s)}" }.join('&') unless params.empty?

headers = {
'Accept' => 'application/json',
Expand Down Expand Up @@ -95,6 +99,20 @@ def success?(response)
end

def error_message(response)
JSON.parse(response.body)['error']['full_messages'].join(' ') rescue "unknown error (response #{response.code})"
explanations = {
'400' => 'Something is wrong with the data sent to Foreman server',
'401' => 'Often this is caused by invalid Oauth credentials',
'404' => 'The requested resource was not found',
'500' => 'Check /var/log/foreman/production.log on Foreman server for detailed information',
'502' => 'The webserver received an invalid response from the backend service. Was Foreman unable to handle the request?',
'503' => 'The webserver was unable to reach the backend service. Is foreman.service running?',
'504' => 'The webserver timed out waiting for a response from the backend service. Is Foreman under unusually heavy load?'
}

if (explanation = explanations[response.code.to_str])
"Response: #{response.code} #{response.message}: #{explanation}"
else
JSON.parse(response.body)['error']['full_messages'].join(' ') rescue "Response: #{response.code} #{response.message}"
end
end
end
48 changes: 38 additions & 10 deletions lib/puppet/provider/foreman_smartproxy/rest_v3.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@

def proxy
@proxy ||= begin
r = request(:get, 'api/v2/smart_proxies', :search => %{name="#{resource[:name]}"})
raise Puppet::Error.new("Proxy #{resource[:name]} cannot be retrieved: #{error_message(r)}") unless success?(r)
path = 'api/v2/smart_proxies'
r = request(:get, path, :search => %{name="#{resource[:name]}"})

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

JSON.load(r.body)['results'][0]
end
end
Expand All @@ -19,14 +25,26 @@ def exists?

def create
post_data = {:smart_proxy => {:name => resource[:name], :url => resource[:url]}}.to_json
r = request(:post, 'api/v2/smart_proxies', {}, post_data)
raise Puppet::Error.new("Proxy #{resource[:name]} cannot be registered: #{error_message(r)}") unless success?(r)
path = 'api/v2/smart_proxies'
r = request(:post, path, {}, post_data)

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

validate_features!(resource[:features], features_list(JSON.load(r.body)))
end

def destroy
r = request(:delete, "api/v2/smart_proxies/#{id}")
raise Puppet::Error.new("Proxy #{resource[:name]} cannot be removed: #{error_message(r)}") unless success?(r)
path = "api/v2/smart_proxies/#{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.new(error_string)
end

@proxy = nil
end

Expand All @@ -36,8 +54,13 @@ def url

def url=(value)
post_data = {:smart_proxy => {:url => value}}.to_json
r = request(:put, "api/v2/smart_proxies/#{id}", {}, post_data)
raise Puppet::Error.new("Proxy #{resource[:name]} cannot be updated: #{error_message(r)}") unless success?(r)
path = "api/v2/smart_proxies/#{id}"
r = request(:put, path, {}, post_data)

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

def features
Expand All @@ -49,8 +72,13 @@ def features=(expected_features)
end

def refresh_features!
r = request(:put, "api/v2/smart_proxies/#{id}/refresh")
raise Puppet::Error.new("Proxy #{resource[:name]} cannot be refreshed: #{error_message(r)}") unless success?(r)
path = "api/v2/smart_proxies/#{id}/refresh"
r = request(:put, path)

unless success?(r)
error_string = "Error making PUT request to #{request_uri(path)}: #{error_message(r)}"
raise Puppet::Error.new(error_string)
end

body = JSON.load(r.body)
# Replace proxy/feature list cache: pre-#19476 versions have limited responses, clear cache and re-fetch for them
Expand Down
8 changes: 4 additions & 4 deletions manifests/cli.pp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#
# $manage_root_config:: Whether to manage /root/.hammer configuration.
#
# $use_sessions:: Enable using sessions
#
# $refresh_cache:: Check API documentation cache status on each request
#
# $request_timeout:: API request timeout, set -1 for infinity
Expand All @@ -31,16 +33,14 @@
Boolean $manage_root_config = $foreman::cli::params::manage_root_config,
Optional[String] $username = $foreman::cli::params::username,
Optional[String] $password = $foreman::cli::params::password,
Boolean $use_sessions = $foreman::cli::params::use_sessions,
Boolean $refresh_cache = $foreman::cli::params::refresh_cache,
Integer[-1] $request_timeout = $foreman::cli::params::request_timeout,
Optional[Stdlib::Absolutepath] $ssl_ca_file = $foreman::cli::params::ssl_ca_file,
String $hammer_plugin_prefix = $foreman::cli::params::hammer_plugin_prefix,
) inherits foreman::cli::params {
# Inherit URL & auth parameters from foreman class if possible
#
# The parameter existence must be checked in case strict variables is enabled, but this will only
# work since PUP-4072 (3.7.5+) due to a bug resolving variables outside of this class.
if versioncmp($::puppetversion, '3.7.5') < 0 or defined('$foreman::foreman_url') {
if defined('$foreman::foreman_url') {
$foreman_url_real = pick($foreman_url, $foreman::foreman_url)
$username_real = pick($username, $foreman::initial_admin_username)
$password_real = pick($password, $foreman::initial_admin_password)
Expand Down
1 change: 1 addition & 0 deletions manifests/cli/params.pp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
$password = undef
$refresh_cache = false
$request_timeout = 120
$use_sessions = false
$ssl_ca_file = undef

# OS specific paths
Expand Down
13 changes: 7 additions & 6 deletions manifests/config.pp
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,13 @@
ensure => absent,
}

$listen_socket = $foreman::foreman_service_bind ? {
Stdlib::IP::Address::V6 => "[${foreman::foreman_service_bind}]:${foreman::foreman_service_port}",
default => "${foreman::foreman_service_bind}:${foreman::foreman_service_port}",
}

if $foreman::use_foreman_service {
if $foreman::apache {
include apache
}

$listen_stream = regsubst($foreman::foreman_service_bind, 'unix://|tcp://', '')

systemd::dropin_file { 'foreman-socket':
filename => 'installer.conf',
unit => "${foreman::foreman_service}.socket",
Expand Down Expand Up @@ -125,7 +126,7 @@
serveraliases => $foreman::serveraliases,
server_port => $foreman::server_port,
server_ssl_port => $foreman::server_ssl_port,
proxy_backend => "http://${listen_socket}/",
proxy_backend => $foreman::foreman_service_bind,
ssl => $foreman::ssl,
ssl_ca => $foreman::server_ssl_ca,
ssl_chain => $foreman::server_ssl_chain,
Expand Down
Loading