From 4b0e6bd70a125f076a2adee70929c56e67ba4d73 Mon Sep 17 00:00:00 2001 From: Lucas Yamanishi Date: Fri, 27 Jan 2017 23:25:29 -0500 Subject: [PATCH] Manage yumrepos via data This commit adds parameters to manage Yumrepo resources via data. The primary parameter, `repos`, is simply a hash of Yumrepo resources for digestion by an each loop similar to an old `create_resources()` call. It does nothing on its own, acting only as a source of data. The other parameters control the behavior of the resource instantiation loop. The parameters `managed_repos`, `os_default_repos`, and `repo_exclusions` each take an array of key names from the `repos` hash. They are then combined to select a set of repos from the `repos` hash for instantiation. The formula takes one of the following two forms: `managed_repos` - `repo_exclusions` OR, if `manage_os_default_repos` is set to `true` `managed_repos` + `os_default_repos` - `repo_exclusions` Data is included for most of the CentOS default repositories, with the exception of the "archive" repos. --- .sync.yml | 2 + data/common.yaml | 11 ++ data/os/RedHat/CentOS.yaml | 106 +++++++++++++++ data/os/RedHat/CentOS/6.yaml | 3 + data/os/RedHat/CentOS/7.yaml | 3 + functions/bool2num_hash_recursive.pp | 13 ++ hiera.yaml | 16 ++- manifests/init.pp | 121 +++++++++++++++++- spec/classes/init_spec.rb | 58 +++++++++ spec/fixtures/data/default.yaml | 1 + spec/fixtures/data/repo_exclusions.yaml | 3 + spec/fixtures/hiera.yaml | 9 ++ .../functions/bool2num_hash_recursive_spec.rb | 39 ++++++ spec/spec_helper.rb | 1 + 14 files changed, 373 insertions(+), 13 deletions(-) create mode 100644 data/os/RedHat/CentOS/6.yaml create mode 100644 data/os/RedHat/CentOS/7.yaml create mode 100644 functions/bool2num_hash_recursive.pp create mode 100644 spec/fixtures/data/default.yaml create mode 100644 spec/fixtures/data/repo_exclusions.yaml create mode 100644 spec/fixtures/hiera.yaml create mode 100644 spec/functions/bool2num_hash_recursive_spec.rb diff --git a/.sync.yml b/.sync.yml index d84d0fa9..3eb2969e 100644 --- a/.sync.yml +++ b/.sync.yml @@ -11,3 +11,5 @@ sudo: required bundler_args: --without development secure: "iItNJ/PlMvdSiYjtXWJkS69mF/4XUriTRC6axFoiTgX8BzGNWA1U/anENNyzmhRKcH/Nc0erVeau+RX8vyJ0HmIJOCvYfq5Q/SQWex1fDXLe/UYJkAEWwmeIOVSF2nTEUPDvDn/d6bNEdULw5yrNn1dT8eLqIIXl6/nThdpiS917BX6CeYdojr/mISrLsvihuB5DQRdVzH+hK1bXcECihnOfNH9lQ0lZ2v2ohJiLJL0DadDg0YMMeJMlP7CnBZzRs7fhTPdLMjzCvysef9nqBYRlGBRUn+CaQ4VoQZlWB1JchJup4qCGeU9ANkb8gdKYTy1kFkBrEDuqlUUuuTTMhDpQ+2fGF32zgnXCSnVY8AIriFfO9c1ljxL6k6vaHpfnsPcMrxuQXNeOPGYpVjNGi/Hz8OjuZ3IT07c8SmZgmGaNp+ZIKErJQV0eob0NeA/1P7HheRS5aPEiN8vj/ZGuIGa+BhbTp2riJ599urrSqGDcJ0YzNeW2BvBZQoXs953X4N4yROz4xKMNqPz/jhyGM9w5SBJ/uLiIvKTu+bSsJ2VNyrOOu25eYqzH1zKc71fKiWa1ZOTHKVM24chlmoq3tZTSpSn6OxpptKLxAYZG0IUdFSMy66m8nss1AxL2djScAptugsqpfLqziMArAoN9iWXCeGiWz1qLRl+5AlMrmMY=" +spec/spec_helper.rb: + hiera_config: "File.expand_path(File.join(__FILE__, '../fixtures/hiera.yaml'))" diff --git a/data/common.yaml b/data/common.yaml index 8d9df2bd..71d14720 100644 --- a/data/common.yaml +++ b/data/common.yaml @@ -2,3 +2,14 @@ lookup_options: yum::config_options: merge: 'hash' + yum::repos: + merge: + strategy: 'deep' + knockout_prefix: '--' + merge_hash_arrays: true + yum::managed_repos: + merge: 'unique' + yum::os_default_repos: + merge: 'unique' + yum::repo_exclusions: + merge: 'unique' diff --git a/data/os/RedHat/CentOS.yaml b/data/os/RedHat/CentOS.yaml index ed97d539..1f7f79e1 100644 --- a/data/os/RedHat/CentOS.yaml +++ b/data/os/RedHat/CentOS.yaml @@ -1 +1,107 @@ --- +yum::os_default_repos: + - 'base' + - 'updates' + - 'extras' + - 'centosplus' + - 'base-source' + - 'updates-source' + - 'extras-source' + - 'base-debuginfo' + - 'centos-media' +yum::repos: + base: + enabled: true + descr: 'CentOS-$releasever - Base' + mirrorlist: 'http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os&infra=$infra' + gpgcheck: true + gpgkey: "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-%{facts.os.release.major}" + target: '/etc/yum.repos.d/CentOS-Base.repo' + + updates: + enabled: true + descr: 'CentOS-$releasever - Updates' + mirrorlist: 'http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=updates&infra=$infra' + gpgcheck: true + gpgkey: "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-%{facts.os.release.major}" + target: '/etc/yum.repos.d/CentOS-Base.repo' + + extras: + enabled: false + descr: 'CentOS-$releasever - Extras' + mirrorlist: 'http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=extras&infra=$infra' + gpgcheck: true + gpgkey: "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-%{facts.os.release.major}" + target: '/etc/yum.repos.d/CentOS-Base.repo' + + centosplus: + enabled: false + descr: 'CentOS-$releasever - Plus' + mirrorlist: 'http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=centosplus&infra=$infra' + gpgcheck: true + gpgkey: "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-%{facts.os.release.major}" + target: '/etc/yum.repos.d/CentOS-Base.repo' + + contrib: + enabled: false + descr: 'CentOS-$releasever - Contrib' + mirrorlist: 'http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=contrib&infra=$infra' + gpgcheck: true + gpgkey: "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-%{facts.os.release.major}" + target: '/etc/yum.repos.d/CentOS-Base.repo' + + cr: + enabled: false + descr: 'CentOS-$releasever - cr' + baseurl: 'http://mirror.centos.org/centos/$releasever/cr/$basearch/' + gpgcheck: true + gpgkey: "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-%{facts.os.release.major}" + target: '/etc/yum.repos.d/CentOS-CR.repo' + + fasttrack: + enabled: false + descr: 'CentOS-$releasever - fasttrack' + mirrorlist: 'http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=fasttrack&infra=$infra' + gpgcheck: true + gpgkey: "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-%{facts.os.release.major}" + target: '/etc/yum.repos.d/CentOS-fasttrack.repo' + + base-source: + enabled: false + descr: 'CentOS-$releasever - Base Sources' + baseurl: 'http://vault.centos.org/centos/$releasever/os/Source/' + gpgcheck: true + gpgkey: "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-%{facts.os.release.major}" + target: '/etc/yum.repos.d/CentOS-Sources.repo' + + updates-source: + enabled: false + descr: 'CentOS-$releasever - Updates Sources' + baseurl: 'http://vault.centos.org/centos/$releasever/updates/Source/' + gpgcheck: true + gpgkey: "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-%{facts.os.release.major}" + target: '/etc/yum.repos.d/CentOS-Sources.repo' + + extras-source: + enabled: false + descr: 'CentOS-$releasever - Extras Sources' + baseurl: 'http://vault.centos.org/centos/$releasever/extras/Source/' + gpgcheck: true + gpgkey: "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-%{facts.os.release.major}" + target: '/etc/yum.repos.d/CentOS-Sources.repo' + + base-debuginfo: + enabled: false + descr: 'CentOS-$releasever - Debuginfo' + baseurl: 'http://debuginfo.centos.org/$releasever/$basearch/' + gpgcheck: true + gpgkey: "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-Debug-%{facts.os.release.major}" + target: '/etc/yum.repos.d/CentOS-Debuginfo.repo' + + centos-media: + enabled: false + descr: 'CentOS-$releasever - Media' + baseurl: 'file:///media/CentOS/ file:///media/cdrom/ file:///media/cdrecorder/' + gpgcheck: true + gpgkey: "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-%{facts.os.release.major}" + target: '/etc/yum.repos.d/CentOS-Media.repo' diff --git a/data/os/RedHat/CentOS/6.yaml b/data/os/RedHat/CentOS/6.yaml new file mode 100644 index 00000000..93345db9 --- /dev/null +++ b/data/os/RedHat/CentOS/6.yaml @@ -0,0 +1,3 @@ +--- +yum::os_default_repos: + - 'contrib' diff --git a/data/os/RedHat/CentOS/7.yaml b/data/os/RedHat/CentOS/7.yaml new file mode 100644 index 00000000..18889b09 --- /dev/null +++ b/data/os/RedHat/CentOS/7.yaml @@ -0,0 +1,3 @@ +--- +yum::os_default_repos: + - 'cr' diff --git a/functions/bool2num_hash_recursive.pp b/functions/bool2num_hash_recursive.pp new file mode 100644 index 00000000..26d48b91 --- /dev/null +++ b/functions/bool2num_hash_recursive.pp @@ -0,0 +1,13 @@ +function yum::bool2num_hash_recursive($arg) { + assert_type(Hash, $arg) + $arg.map |$key, $value| { + $return_value = $value ? { + Boolean => bool2num($value), + Hash => yum::bool2num_hash_recursive($value), + default => $value, + } + Hash({ $key => $return_value }) + }.reduce |$attrs_memo, $kv| { + merge($attrs_memo, $kv) + } +} diff --git a/hiera.yaml b/hiera.yaml index 883d7952..d60cd94d 100644 --- a/hiera.yaml +++ b/hiera.yaml @@ -2,13 +2,15 @@ version: 4 datadir: data hierarchy: - - name: 'Operating System' + - name: 'OS Data' backend: yaml - path: "os/%{facts.os.family}/%{facts.os}" + paths: + - "os/%{facts.os.family}/%{facts.os.name}/%{facts.os.release.full}" + - "os/%{facts.os.family}/%{facts.os.name}/%{facts.os.release.major}/%{facts.os.release.minor}" + - "os/%{facts.os.family}/%{facts.os.name}/%{facts.os.release.major}" + - "os/%{facts.os.family}/%{facts.os.name}" + - "os/%{facts.os.family}" - - name: 'OS family' - backend: yaml - path: "os/%{facts.os.family}" - - - name: 'common' + - name: 'Common Data' backend: yaml + path: 'common' diff --git a/manifests/init.pp b/manifests/init.pp index 1130cf73..c0500f0a 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -1,16 +1,108 @@ # A class to install and manage Yum configuration. # -# @param clean_old_kernels Whether or not to purge old kernel version beyond the `keeponly_limit`. -# @param keep_kernel_devel Whether or not to keep kernel devel packages on old kernel purge. -# @param config_options A Hash where keys are the names of `Yum::Config` resources and the values +# @param clean_old_kernels [Boolean] +# Whether or not to purge old kernel version beyond the `keeponly_limit`. +# +# @param keep_kernel_devel [Boolean] +# Whether or not to keep kernel devel packages on old kernel purge. +# +# @param config_options [Hash] +# A Hash where keys are the names of `Yum::Config` resources and the values # are either the direct `ensure` value, or a Hash of the resource's attributes. -# @note Boolean values will be converted to either a `1` or `0`; use a quoted string to get a -# literal `true` or `false`. +# +# @note Boolean parameter values will be converted to either a `1` or `0`; use a quoted string to +# get a literal `true` or `false`. +# +# @param repos [Hash] +# A hash where keys are the names of `Yumrepo` resources and each value represents its respective +# Yumrepo's resource parameters. This is used in conjunction with the `managed_repos` parameter +# to create `Yumrepo` resources en masse. Some default data is provided for this using module +# data. It is configured to deep merge with a `knockout_prefix` of `--` by default, so individual +# parameters may be overriden or removed via global or environment Hiera data. +# +# @note Boolean parameter values will be converted to either a `1` or `0`; use a quoted string to +# get a literal `true` or `false`. +# +# @param managed_repos [Array] +# An array of first-level keys from the `repos` hash to include in the catalog. The module uses +# this list to select `Yumrepo` resources from the `repos` hash for instantiation. Defaults are +# set in the module's Hiera data. +# +# @note This only indicates the *managed* state of the repos, the `ensure` state must be managed +# in the `repos` data. +# +# @param manage_os_default_repos [Boolean] +# Whether or not to add an operating system's default repos to the `managed_repos` array. +# +# @note Currently only valid on CentOS. +# +# @param os_default_repos [Array] +# A list of default repos to add to `managed_repos` if `manage_os_default_repos` is enabled. +# Normally this should not be modified. +# +# @param repo_exclusions [Array] +# An array of first-level keys from the `repos` hash to exclude from management via this module. +# Values in this array will be subtracted from the `managed_repos` array as a last step before +# instantiation. +# +# @example Enable management of the default repos for a supported OS: +# ```yaml +# --- +# yum::manage_os_default_repos: true +# ``` +# +# @example Add Hiera data to disable *management* of the CentOS Base repo: +# ```yaml +# --- +# yum::manage_os_default_repos: true +# yum::repo_exclusions: +# - 'base' +# ``` +# +# @example Ensure the CentOS base repo is removed from the agent system(s): +# ```yaml +# --- +# yum::manage_os_default_repos: true +# yum::repos: +# base: +# ensure: 'absent' +# ``` +# +# @example Add a custom repo: +# ```yaml +# --- +# yum::managed_repos: +# - 'example_repo' +# yum::repos: +# example_repo: +# ensure: 'present' +# enabled: true +# descr: 'Example Repo' +# baseurl: 'https://repos.example.com/example/' +# gpgcheck: true +# gpgkey: 'file:///etc/pki/gpm-gpg/RPM-GPG-KEY-Example' +# target: '/etc/yum.repos.d/example.repo' +# ``` +# +# @example Use a custom `baseurl` for the CentOS Base repo: +# ```yaml +# --- +# yum::manage_os_default_repos: true +# yum::repos: +# base: +# baseurl: 'https://repos.example.com/CentOS/base/' +# mirrorlist: '--' +# ``` # class yum ( Boolean $clean_old_kernels = true, Boolean $keep_kernel_devel = false, Hash[String, Variant[String, Integer, Boolean, Hash[String, Variant[String, Integer, Boolean]]]] $config_options = { }, + Optional[Hash[String, Optional[Hash[String, Variant[String, Integer, Boolean]]]]] $repos = {}, + Array[String] $managed_repos = [], + Boolean $manage_os_default_repos = false, + Array[String] $os_default_repos = [], + Array[String] $repo_exclusions = [], ) { $module_metadata = load_module_metadata($module_name) @@ -23,8 +115,25 @@ fail("${::os['name']} not supported") } - unless empty($config_options) { + $_managed_repos = $manage_os_default_repos ? { + true => $managed_repos + $os_default_repos, + default => $managed_repos, + } + unless empty($_managed_repos) or empty($repos) { + $_managed_repos_minus_exclusions = $_managed_repos - $repo_exclusions + $normalized_repos = yum::bool2num_hash_recursive($repos) + + $normalized_repos.each |$yumrepo, $attributes| { + if member($_managed_repos_minus_exclusions, $yumrepo) { + Resource['yumrepo'] { + $yumrepo: * => $attributes, + } + } + } + } + + unless empty($config_options) { if has_key($config_options, 'installonly_limit') { assert_type(Variant[Integer, Hash[String, Integer]], $config_options['installonly_limit']) |$expected, $actual| { fail("The value or ensure for `\$yum::config_options[installonly_limit]` must be an Integer, but it is not.") diff --git a/spec/classes/init_spec.rb b/spec/classes/init_spec.rb index 0b5f2cda..b33f9967 100644 --- a/spec/classes/init_spec.rb +++ b/spec/classes/init_spec.rb @@ -12,6 +12,12 @@ end end +shared_examples 'a catalog containing repos' do |repos| + repos.each do |repo| + it { is_expected.to contain_yumrepo(repo) } + end +end + describe 'yum' do on_supported_os.each do |os, facts| context "on #{os}" do @@ -26,6 +32,58 @@ let(:params) { {} } it_behaves_like 'a Yum class' + it { is_expected.to have_yumrepo_resource_count(0) } + end + + context 'when `manage_os_default_repos` is enabled' do + let(:params) { { 'manage_os_default_repos' => true } } + + case facts[:os]['name'] + when 'CentOS' + it { is_expected.to have_yumrepo_resource_count(10) } + it_behaves_like 'a catalog containing repos', [ + 'base', + 'updates', + 'extras', + 'base-source', + 'updates-source', + 'extras-source', + 'base-debuginfo', + 'centosplus', + 'centos-media' + ] + case facts[:os]['release']['major'] + when '7' + it { is_expected.to contain_yumrepo('cr') } + it { is_expected.not_to contain_yumrepo('contrib') } + when '6' + it { is_expected.to contain_yumrepo('contrib') } + it { is_expected.not_to contain_yumrepo('cr') } + end + else + it { is_expected.to have_yumrepo_resource_count(0) } + end + + context 'and the CentOS base repo is negated' do + let(:facts) { facts.merge(hiera_fixture: 'repo_exclusions') } + + case facts[:os]['name'] + when 'CentOS' + it { is_expected.not_to contain_yumrepo('base') } + it_behaves_like 'a catalog containing repos', [ + 'updates', + 'extras', + 'base-source', + 'updates-source', + 'extras-source', + 'base-debuginfo', + 'centosplus', + 'centos-media' + ] + else + it { is_expected.to have_yumrepo_resource_count(0) } + end + end end context 'when `config_options[installonly_limit]` is modified' do diff --git a/spec/fixtures/data/default.yaml b/spec/fixtures/data/default.yaml new file mode 100644 index 00000000..ed97d539 --- /dev/null +++ b/spec/fixtures/data/default.yaml @@ -0,0 +1 @@ +--- diff --git a/spec/fixtures/data/repo_exclusions.yaml b/spec/fixtures/data/repo_exclusions.yaml new file mode 100644 index 00000000..77d085db --- /dev/null +++ b/spec/fixtures/data/repo_exclusions.yaml @@ -0,0 +1,3 @@ +--- +yum::repo_exclusions: + - 'base' diff --git a/spec/fixtures/hiera.yaml b/spec/fixtures/hiera.yaml new file mode 100644 index 00000000..075b1d9b --- /dev/null +++ b/spec/fixtures/hiera.yaml @@ -0,0 +1,9 @@ +--- +:version: 3 +:backends: + - yaml +:yaml: + :datadir: './spec/fixtures/data' +:hierarchy: + - "%{facts.hiera_fixture}" + - 'default' diff --git a/spec/functions/bool2num_hash_recursive_spec.rb b/spec/functions/bool2num_hash_recursive_spec.rb new file mode 100644 index 00000000..8f0a4a02 --- /dev/null +++ b/spec/functions/bool2num_hash_recursive_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe 'yum::bool2num_hash_recursive' do + let(:flat_hash) { { 'a' => true, 'b' => false, 'c' => 'c' } } + let(:nested_hash) do + { + 'a' => { + 'aa' => true, 'ab' => false, 'ac' => 'c', + 'aaa' => true, 'aab' => false, 'aac' => 'c' + }, + 'b' => { 'ba' => true, 'bb' => false, 'bc' => 'c' }, + 'c' => true, + 'd' => false, + 'e' => 5 + } + end + + it 'appropriately modifies a simple, flat hash' do + is_expected.to run.with_params(flat_hash).and_return('a' => 1, 'b' => 0, 'c' => 'c') + end + + it 'appropriately modifies a nested hash' do + is_expected.to run.with_params(nested_hash). + and_return( + 'a' => { + 'aa' => 1, 'ab' => 0, 'ac' => 'c', + 'aaa' => 1, 'aab' => 0, 'aac' => 'c' + }, + 'b' => { 'ba' => 1, 'bb' => 0, 'bc' => 'c' }, + 'c' => 1, + 'd' => 0, + 'e' => 5 + ) + end + + it 'fails on an array' do + is_expected.to run.with_params([true, false]).and_raise_error(Puppet::Error) + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2aa9da74..373c667e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -27,6 +27,7 @@ default_facts.merge!(YAML.load(File.read(File.expand_path('../default_facts.yml', __FILE__)))) if File.exist?(File.expand_path('../default_facts.yml', __FILE__)) default_facts.merge!(YAML.load(File.read(File.expand_path('../default_module_facts.yml', __FILE__)))) if File.exist?(File.expand_path('../default_module_facts.yml', __FILE__)) c.default_facts = default_facts + c.hiera_config = File.expand_path(File.join(__FILE__, '../fixtures/hiera.yaml')) end # vim: syntax=ruby