From 0dd4b4b998f318707e0a8c8a8bfeb8d4dc4821cc Mon Sep 17 00:00:00 2001 From: Joris29 Date: Tue, 16 Jan 2024 08:19:49 +0100 Subject: [PATCH] Add cli class, project define and type --- data/Debian.yaml | 2 + data/RedHat.yaml | 2 + files/rd_project_diff.sh | 15 ++++ manifests/cli.pp | 125 +++++++++++++++++++++++++++++++++ manifests/config/project.pp | 56 +++++++++++++++ spec/classes/cli_spec.rb | 133 ++++++++++++++++++++++++++++++++++++ types/project.pp | 5 ++ 7 files changed, 338 insertions(+) create mode 100644 files/rd_project_diff.sh create mode 100644 manifests/cli.pp create mode 100644 manifests/config/project.pp create mode 100644 spec/classes/cli_spec.rb create mode 100644 types/project.pp diff --git a/data/Debian.yaml b/data/Debian.yaml index 0f8bcbfad..665798d9c 100644 --- a/data/Debian.yaml +++ b/data/Debian.yaml @@ -9,3 +9,5 @@ rundeck::repo_config: key: name: rundeck source: https://packages.rundeck.com/pagerduty/rundeck/gpgkey + +rundeck::cli::repo_config: "%{alias('rundeck::repo_config')}" diff --git a/data/RedHat.yaml b/data/RedHat.yaml index ffc954725..e352544db 100644 --- a/data/RedHat.yaml +++ b/data/RedHat.yaml @@ -8,3 +8,5 @@ rundeck::repo_config: gpgcheck: 0 enabled: 1 gpgkey: https://packages.rundeck.com/pagerduty/rundeck/gpgkey + +rundeck::cli::repo_config: "%{alias('rundeck::repo_config')}" diff --git a/files/rd_project_diff.sh b/files/rd_project_diff.sh new file mode 100644 index 000000000..d64c8d602 --- /dev/null +++ b/files/rd_project_diff.sh @@ -0,0 +1,15 @@ +#!/usr/bin/bash + +project="$1" +update_method="$2" +config="$3" +keys="$4" + +if [[ $update_method == 'update' ]] +then + query="jq -S 'with_entries(select(.key as \$k | \"$keys\" | index(\$k)))'" +else + query='jq -S' +fi + +bash -c "diff <(rd projects configure get -p '$project' | $query) <(echo '$config' | jq -S)" diff --git a/manifests/cli.pp b/manifests/cli.pp new file mode 100644 index 000000000..74aa8394e --- /dev/null +++ b/manifests/cli.pp @@ -0,0 +1,125 @@ +# @summary Class to manage installation and configuration of Rundeck CLI. +# +# @example Use cli with token and project config. +# class { 'rundeck::cli': +# manage_repo => false, +# url => 'https://rundeck01.example.com', +# bypass_url => 'https://rundeck.example.com', +# token => 'very_secure', +# projects => { +# 'MyProject' => { +# 'update_method' => 'set', +# 'config' => { +# 'project.description' => 'This is My rundeck project', +# 'project.disable.executions' => 'false', +# }, +# }, +# 'TestProject' => { +# 'config' => { +# 'project.description' => 'This is a rundeck test project', +# 'project.disable.schedule' => 'false', +# }, +# }, +# }, +# } +# +# @param repo_config +# A hash of repository attributes for configuring the rundeck cli package repositories. +# Examples/defaults for yumrepo can be found at RedHat.yaml, and for apt at Debian.yaml +# @param manage_repo +# Whether to manage the cli package repository. +# @param version +# Ensure the state of the rundeck cli package, either present, absent or a specific version. +# @param url +# Rundeck instance/api url. +# @param bypass_url +# Rundeck external url to bypass. This will rewrite any redirect to $bypass_url as $url +# @param user +# Cli user to authenticate. +# @param password +# Cli password to authenticate. +# @param token +# Cli token to authenticate. +# @param projects +# Cli projects config. See example for structure and rundeck::config::project for available params. +# +class rundeck::cli ( + Hash $repo_config, + Boolean $manage_repo = true, + String[1] $version = 'installed', + Stdlib::HTTPUrl $url = 'http://localhost:4440', + Stdlib::HTTPUrl $bypass_url = 'http://localhost:4440', + String[1] $user = 'admin', + String[1] $password = 'admin', + Optional[String[8]] $token = undef, + Hash[String, Rundeck::Project] $projects = {}, +) { + ensure_resource('package', 'jq', { 'ensure' => 'present' }) + + case $facts['os']['family'] { + 'RedHat': { + if $manage_repo { + $repo_config.each | String $_repo_name, Hash $_attributes| { + yumrepo { $_repo_name: + * => $_attributes, + before => Package['rundeck-cli'], + } + } + } + } + 'Debian': { + if $manage_repo { + $repo_config.each | String $_repo_name, Hash $_attributes| { + apt::source { $_repo_name: + * => $_attributes, + before => Package['rundeck-cli'], + } + } + } + + Class['Apt::Update'] -> Package['rundeck-cli'] + } + default: { + err("The osfamily: ${facts['os']['family']} is not supported") + } + } + + package { 'rundeck-cli': + ensure => $version, + } + + file { '/usr/local/bin/rd_project_diff.sh': + ensure => file, + content => file('rundeck/rd_project_diff.sh'), + mode => '0755', + } + + $_default_env_vars = [ + 'RD_FORMAT=json', + "RD_URL=${url}", + "RD_BYPASS_URL=${bypass_url}", + ] + + if $token { + $environment = $_default_env_vars + ["RD_TOKEN=${token}"] + } else { + $environment = $_default_env_vars + ["RD_USER=${user}", "RD_PASSWORD=${password}"] + } + + exec { 'Check rundeck cli connection': + command => 'rd system info', + path => ['/bin', '/usr/bin', '/usr/local/bin'], + environment => $environment, + tries => 60, + try_sleep => 5, + unless => 'rd system info &> /dev/null', + require => Package['rundeck-cli'], + } + + $projects.each |$_name, $_attr| { + rundeck::config::project { $_name: + * => $_attr, + require => Exec['Check rundeck cli connection'], + } + } +} diff --git a/manifests/config/project.pp b/manifests/config/project.pp new file mode 100644 index 000000000..0328b815d --- /dev/null +++ b/manifests/config/project.pp @@ -0,0 +1,56 @@ +# @summary This define will create and manage a rundeck project. +# +# @example Basic usage. +# rundeck::config::project { 'MyProject': +# config => { +# 'project.description' => 'My test project', +# 'project.disable.schedule' => 'false', +# }, +# } +# +# @param update_method +# set: Overwrite all configuration properties for a project. Any config keys not included will be removed. +# update: Modify configuration properties for a project. Only the specified keys will be updated. +# @param config +# Configuration properties for a project. +# +define rundeck::config::project ( + Enum['set', 'update'] $update_method = 'update', + Hash[String, String] $config = { + 'project.description' => "${title} project", + 'project.disable.executions' => 'true', + 'project.disable.schedule' => 'true', + }, +) { + include rundeck::cli + + $_default_cfg = { + 'project.name' => $title, + } + + $_final_cfg = $config + $_default_cfg + + $_cmd_line_cfg = $_final_cfg.map |$_k, $_v| { + "--${_k}=${_v}" + } + + $_project_diff = $update_method ? { + 'set' => "rd_project_diff.sh '${title}' ${update_method} '${_final_cfg.to_json}'", + default => "rd_project_diff.sh '${title}' ${update_method} '${_final_cfg.to_json}' '${_final_cfg.keys}'", + } + + exec { + default: + path => ['/bin', '/usr/bin', '/usr/local/bin'], + environment => $rundeck::cli::environment, + ; + "Create rundeck project: ${title}": + command => "rd projects create -p ${title}", + unless => "rd projects info -p ${title}", + ; + "Manage rundeck project: ${title}": + command => "rd projects configure ${update_method} -p ${title} -- ${_cmd_line_cfg.shellquote}", + unless => $_project_diff, + ; + } +} diff --git a/spec/classes/cli_spec.rb b/spec/classes/cli_spec.rb new file mode 100644 index 000000000..da7787ce8 --- /dev/null +++ b/spec/classes/cli_spec.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'rundeck::cli' do + on_supported_os.each do |os, facts| + context "on #{os}" do + let :facts do + facts + end + + context 'with default parameters' do + it { is_expected.to compile } + + it { is_expected.to contain_package('jq').with(ensure: 'present') } + + case facts[:os]['family'] + when 'RedHat' + it { + is_expected.to contain_yumrepo('rundeck').with( + baseurl: 'https://packages.rundeck.com/pagerduty/rundeck/rpm_any/rpm_any/$basearch', + repo_gpgcheck: 1, + gpgcheck: 0, + enabled: 1, + gpgkey: 'https://packages.rundeck.com/pagerduty/rundeck/gpgkey' + ).that_comes_before('Package[rundeck-cli]') + } + when 'Debian' + it { + is_expected.to contain_apt__source('rundeck').with( + location: 'https://packages.rundeck.com/pagerduty/rundeck/any', + release: 'any', + repos: 'main', + key: { + 'name' => 'rundeck', + 'source' => 'https://packages.rundeck.com/pagerduty/rundeck/gpgkey', + } + ).that_comes_before('Package[rundeck-cli]') + } + + it { is_expected.to contain_class('apt::update').that_comes_before('Package[rundeck-cli]') } + it { is_expected.to contain_package('rundeck-cli').with(ensure: 'installed') } + it { is_expected.to contain_file('/usr/local/bin/rd_project_diff.sh').with(ensure: 'file', mode: '0755') } + + it { + is_expected.to contain_exec('Check rundeck cli connection').with( + command: 'rd system info', + path: ['/bin', '/usr/bin', '/usr/local/bin'], + environment: [ + 'RD_FORMAT=json', + 'RD_URL=http://localhost:4440', + 'RD_BYPASS_URL=http://localhost:4440', + 'RD_USER=admin', + 'RD_PASSWORD=admin', + ], + tries: 60, + try_sleep: 5, + unless: 'rd system info &> /dev/null' + ).that_requires('Package[rundeck-cli]') + } + end + end + + context 'with different urls and token auth' do + let(:params) do + { + url: 'http://rundeck01.example.com', + bypass_url: 'http://rundeck.example.com', + token: 'very_secure' + } + end + + it { + is_expected.to contain_exec('Check rundeck cli connection').with( + environment: [ + 'RD_FORMAT=json', + 'RD_URL=http://rundeck01.example.com', + 'RD_BYPASS_URL=http://rundeck.example.com', + 'RD_TOKEN=very_secure', + ] + ) + } + end + + context 'with projects config' do + let(:params) do + { + projects: { + 'MyProject' => { + 'update_method' => 'set', + 'config' => { + 'project.description' => 'This is My rundeck project', + 'project.disable.executions' => 'false', + }, + }, + 'TestProject' => { + 'config' => { + 'project.description' => 'This is a rundeck test project', + 'project.disable.schedule' => 'false', + }, + }, + }, + } + end + + it { + is_expected.to contain_rundeck__config__project('MyProject').with( + update_method: 'set', + config: { + 'project.description' => 'This is My rundeck project', + 'project.disable.executions' => 'false', + } + ) + } + + it { is_expected.to contain_exec('Create rundeck project: MyProject') } + it { is_expected.to contain_exec('Manage rundeck project: MyProject') } + + it { + is_expected.to contain_rundeck__config__project('TestProject').with( + config: { + 'project.description' => 'This is a rundeck test project', + 'project.disable.schedule' => 'false', + } + ) + } + + it { is_expected.to contain_exec('Create rundeck project: TestProject') } + it { is_expected.to contain_exec('Manage rundeck project: TestProject') } + end + end + end +end diff --git a/types/project.pp b/types/project.pp new file mode 100644 index 000000000..493f9e279 --- /dev/null +++ b/types/project.pp @@ -0,0 +1,5 @@ +# Rundeck project config type. +type Rundeck::Project = Struct[{ + Optional['config'] => Hash[String, String], + Optional['update_method'] => String, +}]