From 8ecae42199f27693eec771513174ce8143c8ca83 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Wed, 14 Oct 2020 02:00:11 +0300 Subject: [PATCH 01/44] remove bin/cdist script --- bin/cdist | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100755 bin/cdist diff --git a/bin/cdist b/bin/cdist deleted file mode 100755 index 645020a1..00000000 --- a/bin/cdist +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh -# -*- coding: utf-8 -*- -# -# 2012 Nico Schottelius (nico-cdist at schottelius.org) -# -# This file is part of cdist. -# -# cdist is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# cdist is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with cdist. If not, see . -# -# - -# Wrapper for real script to allow execution from checkout -dir=${0%/*} - -# Ensure version is present - the bundled/shipped version contains a static version, -# the git version contains a dynamic version -"$dir/build-helper" version - -libdir=$(cd "${dir}/../" && pwd -P) -export PYTHONPATH="${libdir}" - -"$dir/../scripts/cdist" "$@" From 45d51c0e1553ce12fcea09b1b9e0d2fb9c0304c5 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Wed, 14 Oct 2020 02:00:44 +0300 Subject: [PATCH 02/44] rename build-helper -> cdist-build-helper --- bin/{build-helper => cdist-build-helper} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename bin/{build-helper => cdist-build-helper} (100%) diff --git a/bin/build-helper b/bin/cdist-build-helper similarity index 100% rename from bin/build-helper rename to bin/cdist-build-helper From 3f1939716f4c4c2cdf538a22170a67fe181c675c Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Wed, 14 Oct 2020 02:02:45 +0300 Subject: [PATCH 03/44] enable running scripts/cdist directly and symlinked --- scripts/cdist | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/cdist b/scripts/cdist index b1d782ab..2ce40ae0 100755 --- a/scripts/cdist +++ b/scripts/cdist @@ -22,7 +22,15 @@ # import logging +import os import sys + +cdist_bin = os.path.abspath(__file__) +if os.path.islink(cdist_bin): + cdist_bin = os.readlink(cdist_bin) +cdist_dir = os.path.abspath(os.path.join(os.path.dirname(cdist_bin), os.pardir)) +sys.path.insert(0, cdist_dir) + import cdist import cdist.argparse import cdist.banner From fdc1ab93e968575b1d1f6f3b2a6cefa6ed6bb850 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Wed, 14 Oct 2020 02:03:11 +0300 Subject: [PATCH 04/44] move scripts/* to bin/ --- {scripts => bin}/cdist | 0 {scripts => bin}/cdist-dump | 0 {scripts => bin}/cdist-new-type | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {scripts => bin}/cdist (100%) rename {scripts => bin}/cdist-dump (100%) rename {scripts => bin}/cdist-new-type (100%) diff --git a/scripts/cdist b/bin/cdist similarity index 100% rename from scripts/cdist rename to bin/cdist diff --git a/scripts/cdist-dump b/bin/cdist-dump similarity index 100% rename from scripts/cdist-dump rename to bin/cdist-dump diff --git a/scripts/cdist-new-type b/bin/cdist-new-type similarity index 100% rename from scripts/cdist-new-type rename to bin/cdist-new-type From 86057cef19b10859cd66ceb4e3ebade5986aff89 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Wed, 14 Oct 2020 02:05:17 +0300 Subject: [PATCH 05/44] don't die if there is no version.py --- cdist/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cdist/__init__.py b/cdist/__init__.py index be573170..26e7d071 100644 --- a/cdist/__init__.py +++ b/cdist/__init__.py @@ -24,10 +24,13 @@ import os import hashlib import cdist.log -import cdist.version -VERSION = cdist.version.VERSION +try: + import cdist.version + VERSION = cdist.version.VERSION +except ModuleNotFoundError: + VERSION = 'from git' BANNER = """ .. . .x+=:. s From fd04c036139f150be79801e5fd3b49086e5d7263 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Fri, 16 Oct 2020 13:42:16 +0300 Subject: [PATCH 06/44] add parent dir to module search path only when importing fails --- bin/cdist | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/bin/cdist b/bin/cdist index 2ce40ae0..66ccbb5a 100755 --- a/bin/cdist +++ b/bin/cdist @@ -25,13 +25,19 @@ import logging import os import sys -cdist_bin = os.path.abspath(__file__) -if os.path.islink(cdist_bin): - cdist_bin = os.readlink(cdist_bin) -cdist_dir = os.path.abspath(os.path.join(os.path.dirname(cdist_bin), os.pardir)) -sys.path.insert(0, cdist_dir) +# try to import cdist and if that fails, then add this file's parent dir to +# module search path and try again. additionally check if this file is +# symlinked, so user can symlink this file to, for example, ~/.local/bin. +try: + import cdist +except ModuleNotFoundError: + cdist_bin = os.path.abspath(__file__) + if os.path.islink(cdist_bin): + cdist_bin = os.readlink(cdist_bin) + cdist_dir = os.path.abspath(os.path.join(os.path.dirname(cdist_bin), os.pardir)) + sys.path.insert(0, cdist_dir) + import cdist -import cdist import cdist.argparse import cdist.banner import cdist.config From 1614b62f702a82731b3b9c636894268b4310821b Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Fri, 16 Oct 2020 13:48:28 +0300 Subject: [PATCH 07/44] fallback VERSION to "unknown version" --- cdist/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdist/__init__.py b/cdist/__init__.py index 26e7d071..350a2765 100644 --- a/cdist/__init__.py +++ b/cdist/__init__.py @@ -30,7 +30,7 @@ try: import cdist.version VERSION = cdist.version.VERSION except ModuleNotFoundError: - VERSION = 'from git' + VERSION = 'unknown version' BANNER = """ .. . .x+=:. s From 174aa7728065da611dc8887d9f33ead7e27c473a Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Fri, 16 Oct 2020 14:11:00 +0300 Subject: [PATCH 08/44] __file__ already is absolute --- bin/cdist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/cdist b/bin/cdist index 66ccbb5a..d7eae312 100755 --- a/bin/cdist +++ b/bin/cdist @@ -31,7 +31,7 @@ import sys try: import cdist except ModuleNotFoundError: - cdist_bin = os.path.abspath(__file__) + cdist_bin = __file__ if os.path.islink(cdist_bin): cdist_bin = os.readlink(cdist_bin) cdist_dir = os.path.abspath(os.path.join(os.path.dirname(cdist_bin), os.pardir)) From 65c8af4ba3751dddbfe07fd5077e5a99b5d240b8 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Fri, 16 Oct 2020 14:11:12 +0300 Subject: [PATCH 09/44] overengineered version discovery --- cdist/__init__.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/cdist/__init__.py b/cdist/__init__.py index 350a2765..f659506f 100644 --- a/cdist/__init__.py +++ b/cdist/__init__.py @@ -22,6 +22,7 @@ import os import hashlib +import subprocess import cdist.log @@ -30,7 +31,19 @@ try: import cdist.version VERSION = cdist.version.VERSION except ModuleNotFoundError: - VERSION = 'unknown version' + cdist_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) + if os.path.isdir(os.path.join(cdist_dir, '.git')): + run_git = subprocess.run( + ['git', 'describe', '--always'], + cwd=cdist_dir, + capture_output=True, + text=True) + if run_git.returncode == 0: + VERSION = str(run_git.stdout) + else: + VERSION = 'from git' + else: + VERSION = 'unknown version' BANNER = """ .. . .x+=:. s From 42d5d6c3e29f6500c3f6321b01c3b268e6886ec6 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Fri, 16 Oct 2020 14:12:39 +0300 Subject: [PATCH 10/44] redundant str() --- cdist/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdist/__init__.py b/cdist/__init__.py index f659506f..c16562e7 100644 --- a/cdist/__init__.py +++ b/cdist/__init__.py @@ -39,7 +39,7 @@ except ModuleNotFoundError: capture_output=True, text=True) if run_git.returncode == 0: - VERSION = str(run_git.stdout) + VERSION = run_git.stdout else: VERSION = 'from git' else: From b41d80075a616a1c7ac3a361079c748424409995 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Fri, 16 Oct 2020 14:16:04 +0300 Subject: [PATCH 11/44] update paths in setup.py --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 7b000041..002be19c 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ import subprocess # We have it only if it is a git cloned repo. -build_helper = os.path.join('bin', 'build-helper') +build_helper = os.path.join('bin', 'cdist-build-helper') # Version file path. version_file = os.path.join('cdist', 'version.py') # If we have build-helper we could be a git repo. @@ -56,7 +56,7 @@ setup( name="cdist", packages=["cdist", "cdist.core", "cdist.exec", "cdist.util", ], package_data={'cdist': package_data}, - scripts=["scripts/cdist", "scripts/cdist-dump", "scripts/cdist-new-type"], + scripts=["bin/cdist", "bin/cdist-dump", "bin/cdist-new-type"], version=cdist.version.VERSION, description="A Usable Configuration Management System", author="Nico Schottelius", From e55db1b427fcc7f750adfcb3646a3eeb446113de Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Fri, 16 Oct 2020 15:41:38 +0300 Subject: [PATCH 12/44] use check_output for git describe execution and define fallback VERSION earlier --- cdist/__init__.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/cdist/__init__.py b/cdist/__init__.py index c16562e7..1c60ae0f 100644 --- a/cdist/__init__.py +++ b/cdist/__init__.py @@ -27,23 +27,21 @@ import subprocess import cdist.log +VERSION = 'unknown version' + try: import cdist.version VERSION = cdist.version.VERSION except ModuleNotFoundError: cdist_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) if os.path.isdir(os.path.join(cdist_dir, '.git')): - run_git = subprocess.run( - ['git', 'describe', '--always'], - cwd=cdist_dir, - capture_output=True, - text=True) - if run_git.returncode == 0: - VERSION = run_git.stdout - else: - VERSION = 'from git' - else: - VERSION = 'unknown version' + try: + VERSION = subprocess.check_output( + ['git', 'describe', '--always'], + cwd=cdist_dir, + universal_newlines=True) + except: + pass BANNER = """ .. . .x+=:. s From 54d83a62118ed3f0c6328c4b25a3b9ee56adf27a Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Fri, 16 Oct 2020 15:50:50 +0300 Subject: [PATCH 13/44] there is no single author anymore, also remove www. --- setup.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 002be19c..858c2c17 100644 --- a/setup.py +++ b/setup.py @@ -59,9 +59,8 @@ setup( scripts=["bin/cdist", "bin/cdist-dump", "bin/cdist-new-type"], version=cdist.version.VERSION, description="A Usable Configuration Management System", - author="Nico Schottelius", - author_email="nico-cdist-pypi@schottelius.org", - url="https://www.cdi.st/", + author="cdist contributors", + url="https://cdi.st", classifiers=[ "Development Status :: 6 - Mature", "Environment :: Console", From d20fb743243f0341820a5b41a5725fb705eb5aae Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Sat, 17 Oct 2020 23:16:42 +0300 Subject: [PATCH 14/44] use os.path.realpath instead, because it eliminates any symbolic links encountered in the path --- bin/cdist | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bin/cdist b/bin/cdist index d7eae312..1f92f157 100755 --- a/bin/cdist +++ b/bin/cdist @@ -25,16 +25,16 @@ import logging import os import sys -# try to import cdist and if that fails, then add this file's parent dir to -# module search path and try again. additionally check if this file is -# symlinked, so user can symlink this file to, for example, ~/.local/bin. +# try to import cdist and if that fails, +# then add this file's parent dir to +# module search path and try again. try: import cdist except ModuleNotFoundError: - cdist_bin = __file__ - if os.path.islink(cdist_bin): - cdist_bin = os.readlink(cdist_bin) - cdist_dir = os.path.abspath(os.path.join(os.path.dirname(cdist_bin), os.pardir)) + cdist_dir = os.path.realpath( + os.path.join( + os.path.dirname(os.path.realpath(__file__)), + os.pardir)) sys.path.insert(0, cdist_dir) import cdist From 6964070282a6c5d09a458017623012fc17e3008d Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Sun, 18 Oct 2020 17:13:22 +0300 Subject: [PATCH 15/44] s/build-helper/cdist-build-helper/ --- .gitlab-ci.yml | 8 ++++---- README-maintainers | 2 +- bin/cdist-build-helper | 2 +- docs/src/cdist-install.rst | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e215652c..e48355ea 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,15 +6,15 @@ image: code.ungleich.ch:5050/ungleich-public/cdist/cdist-ci:latest unit_tests: stage: test script: - - ./bin/build-helper version - - ./bin/build-helper test + - ./bin/cdist-build-helper version + - ./bin/cdist-build-helper test pycodestyle: stage: test script: - - ./bin/build-helper pycodestyle + - ./bin/cdist-build-helper pycodestyle shellcheck: stage: test script: - - ./bin/build-helper shellcheck + - ./bin/cdist-build-helper shellcheck diff --git a/README-maintainers b/README-maintainers index af57f475..5766dd7d 100644 --- a/README-maintainers +++ b/README-maintainers @@ -1,4 +1,4 @@ -Maintainers should use ./bin/build-helper script. +Maintainers should use ./bin/cdist-build-helper script. Makefile is intended for end users. It can be used for non-maintaining targets that can be run from pure source (without git repository). diff --git a/bin/cdist-build-helper b/bin/cdist-build-helper index d4d603ed..bdef0dbb 100755 --- a/bin/cdist-build-helper +++ b/bin/cdist-build-helper @@ -495,7 +495,7 @@ eof ;; shellcheck-build-helper) - ${SHELLCHECKCMD} ./bin/build-helper + ${SHELLCHECKCMD} ./bin/cdist-build-helper ;; check-shellcheck) diff --git a/docs/src/cdist-install.rst b/docs/src/cdist-install.rst index 6f4f14d7..18863145 100644 --- a/docs/src/cdist-install.rst +++ b/docs/src/cdist-install.rst @@ -49,7 +49,7 @@ create version.py: .. code-block:: sh - ./bin/build-helper version + ./bin/cdist-build-helper version Then you install it with: @@ -70,7 +70,7 @@ Or directly with distutils: python setup.py install -Note that `bin/build-helper` script is intended for cdist maintainers. +Note that `bin/cdist-build-helper` script is intended for cdist maintainers. Available versions in git From a95eab77a5606f2552ac28c0b817945f72538c97 Mon Sep 17 00:00:00 2001 From: Matthias Stecher Date: Sun, 8 Nov 2020 15:18:04 +0100 Subject: [PATCH 16/44] __locale: add state explorer .. so it doesn't execute code all the time. --- cdist/conf/type/__locale/explorer/state | 36 +++++++++++++++++++++++++ cdist/conf/type/__locale/gencode-remote | 15 ++++++++--- 2 files changed, 47 insertions(+), 4 deletions(-) create mode 100755 cdist/conf/type/__locale/explorer/state diff --git a/cdist/conf/type/__locale/explorer/state b/cdist/conf/type/__locale/explorer/state new file mode 100755 index 00000000..4494fcbc --- /dev/null +++ b/cdist/conf/type/__locale/explorer/state @@ -0,0 +1,36 @@ +#!/bin/sh -e +# __locale/explorer/state +# +# 2020 Matthias Stecher (matthiasstecher at gmx.de) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# +# Check if the locale is already installed on the system. +# Outputs 'present' or 'absent' depending if the locale exists. +# + + +# Get user-defined locale +# locale name is echoed differently than the user propably set it (for UTF-8) +locale="$(echo "$__object_id" | sed 's/UTF-8/utf8/')" + +# Check if the given locale exists on the system +if localedef --list-archive | grep -qFx "$locale"; then + echo present +else + echo absent +fi diff --git a/cdist/conf/type/__locale/gencode-remote b/cdist/conf/type/__locale/gencode-remote index 1feb9884..4639cef8 100755 --- a/cdist/conf/type/__locale/gencode-remote +++ b/cdist/conf/type/__locale/gencode-remote @@ -23,6 +23,15 @@ locale="$__object_id" +state_is=$(cat "$__object/explorer/state") +state_should=$(cat "$__object/parameter/state") + +# short circuit if there is nothing to do +if [ "$state_is" = "$state_should" ]; then + exit 0 +fi + + # Hardcoded, create a pull request with # branching on $os in case it is at another location alias=/usr/share/locale/locale.alias @@ -35,8 +44,6 @@ charmap=$(echo "$locale" | cut -d . -f 2) # W-T-F! locale_remove=$(echo "$locale" | sed 's/UTF-8/utf8/') -state=$(cat "$__object/parameter/state") - os=$(cat "$__global/explorer/os") # Nothing to be done on alpine @@ -46,7 +53,7 @@ case "$os" in ;; esac -case "$state" in +case "$state_should" in present) echo localedef -A "$alias" -f "$charmap" -i "$input" "$locale" ;; @@ -54,7 +61,7 @@ case "$state" in echo localedef --delete-from-archive "$locale_remove" ;; *) - echo "Unsupported state: $state" >&2 + echo "Unsupported state: $state_should" >&2 exit 1 ;; esac From 792b4b10762c094d861282e78953dfd29b203a41 Mon Sep 17 00:00:00 2001 From: ssrq Date: Mon, 9 Nov 2020 12:08:54 +0100 Subject: [PATCH 17/44] Add missing 'config' command --- docs/src/cdist-best-practice.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/cdist-best-practice.rst b/docs/src/cdist-best-practice.rst index 39ec453e..f7677276 100644 --- a/docs/src/cdist-best-practice.rst +++ b/docs/src/cdist-best-practice.rst @@ -200,15 +200,15 @@ of cdist: .. code-block:: sh # Singleton type without parameter - echo __ungleich_munin_server | cdist --initial-manifest - munin.panter.ch + echo __ungleich_munin_server | cdist config --initial-manifest - munin.panter.ch # Singleton type with parameter echo __ungleich_munin_node --allow 1.2.3.4 | \ - cdist --initial-manifest - rails-19.panter.ch + cdist config --initial-manifest - rails-19.panter.ch # Normal type echo __file /tmp/stdintest --mode 0644 | \ - cdist --initial-manifest - cdist-dev-01.ungleich.ch + cdist config --initial-manifest - cdist-dev-01.ungleich.ch Other content in cdist repository From ba906510529549446952bb3abfd8b49b05da7ca0 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Wed, 11 Nov 2020 07:49:32 +0100 Subject: [PATCH 18/44] ++changelog --- docs/changelog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog b/docs/changelog index b6b8c22b..9fa0926c 100644 --- a/docs/changelog +++ b/docs/changelog @@ -1,6 +1,11 @@ Changelog --------- +next: + * Documentation: Fix examples in best practice (Dennis Camera) + * Type __locale: Add state explorer (Matthias Stecher) + * Core: Reorganize scripts, version generation (Ander Punnar) + 6.9.1: 2020-11-08 * Type __file: Fix state pre-exists (Dennis Camera) * Type __hostname: Add support for OpenWrt (Dennis Camera) From 3e48ef9e11795bf1c8a59ae202ed28f58cff1753 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Wed, 11 Nov 2020 14:09:26 +0100 Subject: [PATCH 19/44] [type/__hostname] Lint - Error if expected environment variables are unset - Always wrap variable expansions in {} --- cdist/conf/type/__hostname/gencode-remote | 82 ++++++------ cdist/conf/type/__hostname/manifest | 147 +++++++++++----------- 2 files changed, 114 insertions(+), 115 deletions(-) diff --git a/cdist/conf/type/__hostname/gencode-remote b/cdist/conf/type/__hostname/gencode-remote index 02afcbfb..9447daa9 100755 --- a/cdist/conf/type/__hostname/gencode-remote +++ b/cdist/conf/type/__hostname/gencode-remote @@ -20,26 +20,27 @@ # along with cdist. If not, see . # -os=$(cat "$__global/explorer/os") -name_running=$(cat "$__global/explorer/hostname") -has_hostnamectl=$(cat "$__object/explorer/has_hostnamectl") +os=$(cat "${__global:?}/explorer/os") +name_running=$(cat "${__global:?}/explorer/hostname") +has_hostnamectl=$(cat "${__object:?}/explorer/has_hostnamectl") -if test -s "$__object/parameter/name" +if test -s "${__object:?}/parameter/name" then - name_should=$(cat "$__object/parameter/name") + name_should=$(cat "${__object:?}/parameter/name") else - case $os + case ${os} in # RedHat-derivatives and BSDs - centos|fedora|redhat|scientific|freebsd|macosx|netbsd|openbsd) + (centos|fedora|redhat|scientific|freebsd|macosx|netbsd|openbsd) # Hostname is FQDN - name_should="${__target_host}" - ;; - *) + name_should=${__target_host:?} + ;; + (*) # Hostname is only first component of FQDN - name_should="${__target_host%%.*}" - ;; + name_should=${__target_host:?} + name_should=${name_should%%.*} + ;; esac fi @@ -47,46 +48,46 @@ fi ################################################################################ # Check if the (running) hostname is already correct # -test "$name_running" != "$name_should" || exit 0 +test "${name_running}" != "${name_should}" || exit 0 ################################################################################ # Setup hostname # -echo 'changed' >>"$__messages_out" +echo 'changed' >>"${__messages_out:?}" # Use the good old way to set the hostname. -case $os +case ${os} in - alpine|debian|devuan|ubuntu) + (alpine|debian|devuan|ubuntu) echo 'hostname -F /etc/hostname' - ;; - archlinux) + ;; + (archlinux) echo 'command -v hostnamectl >/dev/null 2>&1' \ - "&& hostnamectl set-hostname '$name_should'" \ - "|| hostname '$name_should'" - ;; - centos|fedora|redhat|scientific|freebsd|netbsd|openbsd|gentoo|void) - echo "hostname '$name_should'" - ;; - openwrt) - echo "echo '$name_should' >/proc/sys/kernel/hostname" - ;; - macosx) - echo "scutil --set HostName '$name_should'" - ;; - solaris) - echo "uname -S '$name_should'" - ;; - slackware|suse|opensuse-leap) + "&& hostnamectl set-hostname '${name_should}'" \ + "|| hostname '${name_should}'" + ;; + (centos|fedora|redhat|scientific|freebsd|netbsd|openbsd|gentoo|void) + echo "hostname '${name_should}'" + ;; + (openwrt) + echo "echo '${name_should}' >/proc/sys/kernel/hostname" + ;; + (macosx) + echo "scutil --set HostName '${name_should}'" + ;; + (solaris) + echo "uname -S '${name_should}'" + ;; + (slackware|suse|opensuse-leap) # We do not read from /etc/HOSTNAME, because the running # hostname is the first component only while the file contains # the FQDN. - echo "hostname '$name_should'" - ;; - *) + echo "hostname '${name_should}'" + ;; + (*) # Fall back to set the hostname using hostnamectl, if available. - if test -n "$has_hostnamectl" + if test -n "${has_hostnamectl}" then # Don't use hostnamectl as the primary means to set the hostname for # systemd systems, because it cannot be trusted to work reliably and @@ -97,7 +98,8 @@ in echo "test \"\$(hostname)\" = \"\$(cat /etc/hostname)\"" \ " || hostname -F /etc/hostname" else - printf "echo 'Unsupported OS: %s' >&2\nexit 1\n" "$os" + printf "echo 'Unsupported OS: %s' >&2\n" "${os}" + printf 'exit 1\n' fi - ;; + ;; esac diff --git a/cdist/conf/type/__hostname/manifest b/cdist/conf/type/__hostname/manifest index bf8a331c..125bb2fb 100755 --- a/cdist/conf/type/__hostname/manifest +++ b/cdist/conf/type/__hostname/manifest @@ -20,69 +20,65 @@ # along with cdist. If not, see . # -not_supported() { - echo "Your operating system ($os) is currently not supported by this type (${__type##*/})." >&2 - echo "Please contribute an implementation for it if you can." >&2 - exit 1 -} - set_hostname_systemd() { echo "$1" | __file /etc/hostname --source - } -os=$(cat "$__global/explorer/os") -os_version=$(cat "$__global/explorer/os_version") -os_major=$(echo "$os_version" | grep -o '^[0-9][0-9]*' || true) +os=$(cat "${__global:?}/explorer/os") +os_version=$(cat "${__global:?}/explorer/os_version") +os_major=$(echo "${os_version}" | grep -o '^[0-9][0-9]*' || true) -max_len=$(cat "$__object/explorer/max_len") -has_hostnamectl=$(cat "$__object/explorer/has_hostnamectl") +max_len=$(cat "${__object:?}/explorer/max_len") +has_hostnamectl=$(cat "${__object:?}/explorer/has_hostnamectl") -if test -s "$__object/parameter/name" +if test -s "${__object:?}/parameter/name" then - name_should=$(cat "$__object/parameter/name") + name_should=$(cat "${__object:?}/parameter/name") else - case $os + case ${os} in # RedHat-derivatives and BSDs - centos|fedora|redhat|scientific|freebsd|netbsd|openbsd|slackware) + (centos|fedora|redhat|scientific|freebsd|netbsd|openbsd|slackware) # Hostname is FQDN - name_should="${__target_host}" - ;; - suse|opensuse-leap) + name_should=${__target_host:?} + ;; + (suse|opensuse-leap) + name_should=${__target_host:?} + # Classic SuSE stores the FQDN in /etc/HOSTNAME, while # systemd does not. The running hostname is the first # component in both cases. # In versions before 15.x, the FQDN is stored in /etc/hostname. - if test -n "$has_hostnamectl" && test "$os_major" -ge 15 \ - && test "$os_major" -ne 42 + if test -n "${has_hostnamectl}" \ + && test "${os_major}" -ge 15 \ + && test "${os_major}" -ne 42 then - name_should="${__target_host%%.*}" - else - name_should="${__target_host}" + name_should=${name_should%%.*} fi - ;; + ;; *) # Hostname is only first component of FQDN on all other systems. - name_should="${__target_host%%.*}" - ;; + name_should=${__target_host:?} + name_should=${name_should%%.*} + ;; esac fi -if test -n "$max_len" && test "$(printf '%s' "$name_should" | wc -c)" -gt "$max_len" +if test -n "${max_len}" && test "$(printf '%s' "${name_should}" | wc -c)" -gt "${max_len}" then printf "Host name too long. Up to %u characters allowed.\n" "${max_len}" >&2 exit 1 fi -case $os +case ${os} in - alpine|debian|devuan|ubuntu|void) - echo "$name_should" | __file /etc/hostname --source - - ;; - archlinux) - if test -n "$has_hostnamectl" + (alpine|debian|devuan|ubuntu|void) + echo "${name_should}" | __file /etc/hostname --source - + ;; + (archlinux) + if test -n "${has_hostnamectl}" then - set_hostname_systemd "$name_should" + set_hostname_systemd "${name_should}" else echo 'Ancient ArchLinux variants without hostnamectl are not supported.' >&2 exit 1 @@ -97,8 +93,8 @@ in # --value "\"$name_should\"" fi ;; - centos|fedora|redhat|scientific) - if test -z "$has_hostnamectl" + (centos|fedora|redhat|scientific) + if test -z "${has_hostnamectl}" then # Only write to /etc/sysconfig/network on non-systemd versions. # On systemd-based versions this entry is ignored. @@ -106,63 +102,62 @@ in --file /etc/sysconfig/network \ --delimiter '=' --exact_delimiter \ --key HOSTNAME \ - --value "\"$name_should\"" + --value "\"${name_should}\"" else - set_hostname_systemd "$name_should" + set_hostname_systemd "${name_should}" fi - ;; - gentoo) + ;; + (gentoo) # Only write to /etc/conf.d/hostname on OpenRC-based installations. # On systemd use hostnamectl(1) in gencode-remote. - if test -z "$has_hostnamectl" + if test -z "${has_hostnamectl}" then __key_value '/etc/conf.d/hostname:hostname' \ --file /etc/conf.d/hostname \ --delimiter '=' --exact_delimiter \ --key 'hostname' \ - --value "\"$name_should\"" + --value "\"${name_should}\"" else set_hostname_systemd "$name_should" fi - ;; - freebsd) + ;; + (freebsd) __key_value '/etc/rc.conf:hostname' \ --file /etc/rc.conf \ --delimiter '=' --exact_delimiter \ --key 'hostname' \ - --value "\"$name_should\"" - ;; - macosx) + --value "\"${name_should}\"" + ;; + (macosx) # handled in gencode-remote - : - ;; - netbsd) + ;; + (netbsd) __key_value '/etc/rc.conf:hostname' \ --file /etc/rc.conf \ --delimiter '=' --exact_delimiter \ --key 'hostname' \ - --value "\"$name_should\"" + --value "\"${name_should}\"" # To avoid confusion, ensure that the hostname is only stored once. __file /etc/myname --state absent - ;; - openbsd) - echo "$name_should" | __file /etc/myname --source - - ;; - openwrt) - __uci system.@system[0].hostname --value "$name_should" + ;; + (openbsd) + echo "${name_should}" | __file /etc/myname --source - + ;; + (openwrt) + __uci system.@system[0].hostname --value "${name_should}" # --transaction hostname - ;; - slackware) + ;; + (slackware) # We write the FQDN into /etc/HOSTNAME. But /etc/rc.d/rc.M will only # read the first component from this file and set it as the running # hostname on boot. - echo "$name_should" | __file /etc/HOSTNAME --source - - ;; - solaris) - echo "$name_should" | __file /etc/nodename --source - - ;; - suse|opensuse-leap) + echo "${name_should}" | __file /etc/HOSTNAME --source - + ;; + (solaris) + echo "${name_should}" | __file /etc/nodename --source - + ;; + (suse|opensuse-leap) # Modern SuSE provides /etc/HOSTNAME as a symlink for # backwards-compatibility. Unfortunately it cannot be used # here as __file does not follow the symlink. @@ -171,23 +166,25 @@ in # not work correctly on openSUSE 12.x which provides # hostnamectl but not /etc/hostname. - if test -n "$has_hostnamectl" -a "$os_major" -gt 12 + if test -n "${has_hostnamectl}" -a "${os_major}" -gt 12 then - hostname_file='/etc/hostname' + hostname_file=/etc/hostname else - hostname_file='/etc/HOSTNAME' + hostname_file=/etc/HOSTNAME fi - echo "$name_should" | __file "$hostname_file" --source - - ;; - *) + echo "${name_should}" | __file "${hostname_file}" --source - + ;; + (*) # On other operating systems we fall back to systemd's # hostnamectl if available… - if test -n "$has_hostnamectl" + if test -n "${has_hostnamectl}" then - set_hostname_systemd "$name_should" + set_hostname_systemd "${name_should}" else - not_supported + echo "Your operating system (${os}) is currently not supported by this type (${__type##*/})." >&2 + echo "Please contribute an implementation for it if you can." >&2 + exit 1 fi - ;; + ;; esac From 702f3eba4f8f88b634a010b377d077d501a6f0e9 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Wed, 11 Nov 2020 14:15:02 +0100 Subject: [PATCH 20/44] [type/__hostname] Remove opensuse-leap OS string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit everything should be suse now… --- cdist/conf/type/__hostname/gencode-remote | 2 +- cdist/conf/type/__hostname/manifest | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cdist/conf/type/__hostname/gencode-remote b/cdist/conf/type/__hostname/gencode-remote index 9447daa9..c1a97ac8 100755 --- a/cdist/conf/type/__hostname/gencode-remote +++ b/cdist/conf/type/__hostname/gencode-remote @@ -79,7 +79,7 @@ in (solaris) echo "uname -S '${name_should}'" ;; - (slackware|suse|opensuse-leap) + (slackware|suse) # We do not read from /etc/HOSTNAME, because the running # hostname is the first component only while the file contains # the FQDN. diff --git a/cdist/conf/type/__hostname/manifest b/cdist/conf/type/__hostname/manifest index 125bb2fb..d044072b 100755 --- a/cdist/conf/type/__hostname/manifest +++ b/cdist/conf/type/__hostname/manifest @@ -42,7 +42,7 @@ else # Hostname is FQDN name_should=${__target_host:?} ;; - (suse|opensuse-leap) + (suse) name_should=${__target_host:?} # Classic SuSE stores the FQDN in /etc/HOSTNAME, while @@ -157,7 +157,7 @@ in (solaris) echo "${name_should}" | __file /etc/nodename --source - ;; - (suse|opensuse-leap) + (suse) # Modern SuSE provides /etc/HOSTNAME as a symlink for # backwards-compatibility. Unfortunately it cannot be used # here as __file does not follow the symlink. From 87a0d91587568594a616c7f3588bc74106312761 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Wed, 11 Nov 2020 14:20:18 +0100 Subject: [PATCH 21/44] [type/__hostname] Fix OS version detection for SuSE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit everything should be suse now… --- cdist/conf/type/__hostname/manifest | 39 ++++++++++++++++------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/cdist/conf/type/__hostname/manifest b/cdist/conf/type/__hostname/manifest index d044072b..b80aa2ef 100755 --- a/cdist/conf/type/__hostname/manifest +++ b/cdist/conf/type/__hostname/manifest @@ -25,8 +25,6 @@ set_hostname_systemd() { } os=$(cat "${__global:?}/explorer/os") -os_version=$(cat "${__global:?}/explorer/os_version") -os_major=$(echo "${os_version}" | grep -o '^[0-9][0-9]*' || true) max_len=$(cat "${__object:?}/explorer/max_len") has_hostnamectl=$(cat "${__object:?}/explorer/has_hostnamectl") @@ -38,24 +36,10 @@ else case ${os} in # RedHat-derivatives and BSDs - (centos|fedora|redhat|scientific|freebsd|netbsd|openbsd|slackware) + (centos|fedora|redhat|scientific|freebsd|netbsd|openbsd|slackware|suse) # Hostname is FQDN name_should=${__target_host:?} ;; - (suse) - name_should=${__target_host:?} - - # Classic SuSE stores the FQDN in /etc/HOSTNAME, while - # systemd does not. The running hostname is the first - # component in both cases. - # In versions before 15.x, the FQDN is stored in /etc/hostname. - if test -n "${has_hostnamectl}" \ - && test "${os_major}" -ge 15 \ - && test "${os_major}" -ne 42 - then - name_should=${name_should%%.*} - fi - ;; *) # Hostname is only first component of FQDN on all other systems. name_should=${__target_host:?} @@ -158,6 +142,27 @@ in echo "${name_should}" | __file /etc/nodename --source - ;; (suse) + if test -s "${__global:?}/explorer/os_release" + then + # shellcheck source=/dev/null + os_version=$(. "${__global:?}/explorer/os_release" && echo "${VERSION}") + else + os_version=$(sed -n 's/^VERSION\ *=\ *//p' "${__global:?}/explorer/os_version") + fi + os_major=$(expr "${os_version}" : '\([0-9]\{1,\}\)') + + # Classic SuSE stores the FQDN in /etc/HOSTNAME, while + # systemd does not. The running hostname is the first + # component in both cases. + # In versions before 15.x, the FQDN is stored in /etc/hostname. + if test -n "${has_hostnamectl}" \ + && test "${os_major}" -ge 15 \ + && test "${os_major}" -ne 42 + then + # strip away everything but the first part from $name_should + name_should=${name_should%%.*} + fi + # Modern SuSE provides /etc/HOSTNAME as a symlink for # backwards-compatibility. Unfortunately it cannot be used # here as __file does not follow the symlink. From 21dd500c05c91c4927b2a4166690da65b1c98f97 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Wed, 11 Nov 2020 14:44:44 +0100 Subject: [PATCH 22/44] Make pycodestyle pipeline happy --- cdist/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cdist/__init__.py b/cdist/__init__.py index 1c60ae0f..44366cd0 100644 --- a/cdist/__init__.py +++ b/cdist/__init__.py @@ -33,14 +33,15 @@ try: import cdist.version VERSION = cdist.version.VERSION except ModuleNotFoundError: - cdist_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) + cdist_dir = os.path.abspath( + os.path.join(os.path.dirname(__file__), os.pardir)) if os.path.isdir(os.path.join(cdist_dir, '.git')): try: VERSION = subprocess.check_output( ['git', 'describe', '--always'], cwd=cdist_dir, universal_newlines=True) - except: + except Exception: pass BANNER = """ From e2d4f8037ab7d30187e4d153aa25ef36e8afc91c Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Wed, 11 Nov 2020 14:45:05 +0100 Subject: [PATCH 23/44] [bin/cdist-build-helper] Fix paths to ex scripts/ scripts --- bin/cdist-build-helper | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/bin/cdist-build-helper b/bin/cdist-build-helper index bdef0dbb..fb8a67a4 100755 --- a/bin/cdist-build-helper +++ b/bin/cdist-build-helper @@ -45,7 +45,7 @@ usage() { shellcheck-manifests shellcheck-local-gencodes shellcheck-remote-gencodes - shellcheck-scripts + shellcheck-bin shellcheck-gencodes shellcheck-types shellcheck @@ -100,7 +100,7 @@ case "$option" in if (\$0 ~ /^$end/) { exit } else { - print \$0 + print \$0 } } }" "$basedir/docs/changelog" @@ -135,7 +135,7 @@ case "$option" in version=$1; shift - ( + ( cat << eof Subject: cdist $version has been released @@ -336,7 +336,7 @@ eof make docs-clean make docs - ############################################################# + ############################################################# # Everything green, let's do the release # Tag the current commit @@ -405,7 +405,7 @@ eof ;; pycodestyle|pep8) - pycodestyle "${basedir}" "${basedir}/scripts/cdist" + pycodestyle "${basedir}" "${basedir}/bin/cdist" ;; check-pycodestyle) @@ -460,9 +460,10 @@ eof test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; } ;; - shellcheck-scripts) + # NOTE: shellcheck-scripts is kept for compatibility + shellcheck-bin|shellcheck-scripts) # shellcheck disable=SC2086 - ${SHELLCHECKCMD} scripts/cdist-dump scripts/cdist-new-type > "${SHELLCHECKTMP}" + ${SHELLCHECKCMD} bin/cdist-dump bin/cdist-new-type > "${SHELLCHECKTMP}" test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; } ;; @@ -480,7 +481,7 @@ eof shellcheck) "$0" shellcheck-global-explorers || exit 1 "$0" shellcheck-types || exit 1 - "$0" shellcheck-scripts || exit 1 + "$0" shellcheck-bin || exit 1 ;; shellcheck-type-files) From f82e0167aaf295b8451a07fa931506a1290efcdc Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Wed, 11 Nov 2020 14:49:04 +0100 Subject: [PATCH 24/44] [.gitlab-ci.yml] Make version before other targets --- .gitlab-ci.yml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e48355ea..a4bc67aa 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,20 +1,23 @@ +--- +image: code.ungleich.ch:5050/ungleich-public/cdist/cdist-ci:latest + stages: - test -image: code.ungleich.ch:5050/ungleich-public/cdist/cdist-ci:latest +before_script: + - ./bin/cdist-build-helper version -unit_tests: +shellcheck: stage: test script: - - ./bin/cdist-build-helper version - - ./bin/cdist-build-helper test + - ./bin/cdist-build-helper shellcheck pycodestyle: stage: test script: - ./bin/cdist-build-helper pycodestyle -shellcheck: +unit_tests: stage: test script: - - ./bin/cdist-build-helper shellcheck + - ./bin/cdist-build-helper test From 0ee3fda94deff1461ac488d218a2af98057675c5 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Wed, 11 Nov 2020 15:04:51 +0100 Subject: [PATCH 25/44] Fix paths to cdist executable --- cdist/integration.py | 5 ++--- cdist/test/__init__.py | 2 +- cdist/test/config/__init__.py | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/cdist/integration.py b/cdist/integration.py index 03e4167d..17b65f09 100644 --- a/cdist/integration.py +++ b/cdist/integration.py @@ -54,13 +54,12 @@ _mydir = os.path.dirname(__file__) def find_cdist_exec(): """Search cdist executable starting from local lib directory. - Detect if ../scripts/cdist (from local lib direcotry) exists and + Detect if ../bin/cdist (from local lib directory) exists and if it is executable. If not then try to find cdist exec path in os.get_exec_path() entries. If no cdist path is found rasie cdist.Error. """ - cdist_path = os.path.abspath(os.path.join(_mydir, '..', 'scripts', - 'cdist')) + cdist_path = os.path.abspath(os.path.join(_mydir, '..', 'bin', 'cdist')) if os.access(cdist_path, os.X_OK): return cdist_path cdist_path = find_cdist_exec_in_path() diff --git a/cdist/test/__init__.py b/cdist/test/__init__.py index faa3686a..16a311e4 100644 --- a/cdist/test/__init__.py +++ b/cdist/test/__init__.py @@ -26,7 +26,7 @@ import tempfile cdist_base_path = os.path.abspath( os.path.join(os.path.dirname(os.path.realpath(__file__)), "../../")) -cdist_exec_path = os.path.join(cdist_base_path, "scripts/cdist") +cdist_exec_path = os.path.join(cdist_base_path, "bin/cdist") global_fixtures_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "fixtures")) diff --git a/cdist/test/config/__init__.py b/cdist/test/config/__init__.py index 0ed614b1..c405207c 100644 --- a/cdist/test/config/__init__.py +++ b/cdist/test/config/__init__.py @@ -202,7 +202,7 @@ class ConfigRunTestCase(test.CdistTestCase): host_dir_name=self.hostdir, # exec_path can not derivated from sys.argv in case of unittest exec_path=os.path.abspath(os.path.join( - my_dir, '../../../scripts/cdist')), + my_dir, '../../../bin/cdist')), initial_manifest=os.path.join(fixtures, 'manifest/dryrun_manifest'), add_conf_dirs=[fixtures]) @@ -219,7 +219,7 @@ class ConfigRunTestCase(test.CdistTestCase): base_root_path=self.host_base_path, host_dir_name=self.hostdir, exec_path=os.path.abspath(os.path.join( - my_dir, '../../../scripts/cdist')), + my_dir, '../../../bin/cdist')), initial_manifest=os.path.join( fixtures, 'manifest/init-deps-resolver'), add_conf_dirs=[fixtures]) From c39eb1dbce281ed6f64f804a16fb28e5c8dceccd Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Wed, 11 Nov 2020 15:16:33 +0100 Subject: [PATCH 26/44] [cdist.emulator] Fix setting of log level (tests OK) --- cdist/emulator.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cdist/emulator.py b/cdist/emulator.py index 60b94880..a2bdc3d4 100644 --- a/cdist/emulator.py +++ b/cdist/emulator.py @@ -120,17 +120,18 @@ class Emulator: level = logging.WARNING else: level = logging.WARNING + self.log = logging.getLogger(self.target_host[0]) try: logging.root.setLevel(level) + self.log.setLevel(level) except (ValueError, TypeError): # if invalid __cdist_log_level value logging.root.setLevel(logging.WARNING) + self.log.setLevel(logging.WARNING) colored_log = self.env.get('__cdist_colored_log', 'false') cdist.log.CdistFormatter.USE_COLORS = colored_log == 'true' - self.log = logging.getLogger(self.target_host[0]) - def commandline(self): """Parse command line""" From 2f70a8b540f8d4c283029207e98f7883557338f0 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Wed, 11 Nov 2020 15:25:46 +0100 Subject: [PATCH 27/44] [bin/cdist-build-helper] Keep going in shellcheck targets --- bin/cdist-build-helper | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/bin/cdist-build-helper b/bin/cdist-build-helper index fb8a67a4..0380b3f8 100755 --- a/bin/cdist-build-helper +++ b/bin/cdist-build-helper @@ -468,20 +468,26 @@ eof ;; shellcheck-gencodes) - "$0" shellcheck-local-gencodes || exit 1 - "$0" shellcheck-remote-gencodes || exit 1 + errors=false + "$0" shellcheck-local-gencodes || errors=true + "$0" shellcheck-remote-gencodes || errors=true + ! $errors || exit 1 ;; shellcheck-types) - "$0" shellcheck-type-explorers || exit 1 - "$0" shellcheck-manifests || exit 1 - "$0" shellcheck-gencodes || exit 1 + errors=false + "$0" shellcheck-type-explorers || errors=true + "$0" shellcheck-manifests || errors=true + "$0" shellcheck-gencodes || errors=true + ! $errors || exit 1 ;; shellcheck) - "$0" shellcheck-global-explorers || exit 1 - "$0" shellcheck-types || exit 1 - "$0" shellcheck-bin || exit 1 + errors=false + "$0" shellcheck-global-explorers || errors=true + "$0" shellcheck-types || errors=true + "$0" shellcheck-bin || errors=true + ! $errors || exit 1 ;; shellcheck-type-files) @@ -491,8 +497,10 @@ eof ;; shellcheck-with-files) - "$0" shellcheck || exit 1 - "$0" shellcheck-type-files || exit 1 + errors=false + "$0" shellcheck || errors=true + "$0" shellcheck-type-files || errors=true + ! $errors || exit 1 ;; shellcheck-build-helper) From ebf471e8d0216cc81928fcdff1fe159584187808 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Fri, 13 Nov 2020 02:32:45 +0100 Subject: [PATCH 28/44] [type/__hwclock] Add new type --- .../conf/type/__hwclock/explorer/adjtime_mode | 28 +++ .../__hwclock/explorer/timedatectl_localrtc | 27 +++ cdist/conf/type/__hwclock/gencode-remote | 62 +++++ cdist/conf/type/__hwclock/man.rst | 63 +++++ cdist/conf/type/__hwclock/manifest | 222 ++++++++++++++++++ cdist/conf/type/__hwclock/parameter/required | 1 + cdist/conf/type/__hwclock/singleton | 0 7 files changed, 403 insertions(+) create mode 100755 cdist/conf/type/__hwclock/explorer/adjtime_mode create mode 100755 cdist/conf/type/__hwclock/explorer/timedatectl_localrtc create mode 100755 cdist/conf/type/__hwclock/gencode-remote create mode 100644 cdist/conf/type/__hwclock/man.rst create mode 100755 cdist/conf/type/__hwclock/manifest create mode 100644 cdist/conf/type/__hwclock/parameter/required create mode 100644 cdist/conf/type/__hwclock/singleton diff --git a/cdist/conf/type/__hwclock/explorer/adjtime_mode b/cdist/conf/type/__hwclock/explorer/adjtime_mode new file mode 100755 index 00000000..2b27bedc --- /dev/null +++ b/cdist/conf/type/__hwclock/explorer/adjtime_mode @@ -0,0 +1,28 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# Prints the clock mode read from the /etc/adjtime file, if present. +# + +# not all operating systems use an adjfile +test -f /etc/adjtime || exit 0 + +# 3rd line is clock mode +# adjtime(5) https://man7.org/linux/man-pages/man5/adjtime.5.html +sed -n 3p /etc/adjtime diff --git a/cdist/conf/type/__hwclock/explorer/timedatectl_localrtc b/cdist/conf/type/__hwclock/explorer/timedatectl_localrtc new file mode 100755 index 00000000..8239122e --- /dev/null +++ b/cdist/conf/type/__hwclock/explorer/timedatectl_localrtc @@ -0,0 +1,27 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# Prints the LocalRTC property using timedatectl on systemd-based systems. +# + +command -v timedatectl >/dev/null 2>&1 || exit 0 + +# NOTE: Older versions of timedatectl do not support `timedatectl show' +timedatectl --no-pager status \ +| awk -F': ' '$1 ~ "RTC in local TZ$" { sub(/[ \t]*$/, "", $2); print $2 }' diff --git a/cdist/conf/type/__hwclock/gencode-remote b/cdist/conf/type/__hwclock/gencode-remote new file mode 100755 index 00000000..5995fb23 --- /dev/null +++ b/cdist/conf/type/__hwclock/gencode-remote @@ -0,0 +1,62 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +mode=$(cat "${__object:?}/parameter/mode") + +timedatectl_localrtc=$(cat "${__object:?}/explorer/timedatectl_localrtc") +adjtime_mode=$(cat "${__object:?}/explorer/adjtime_mode") + + +case ${mode} +in + (localtime) + adjtime_str=LOCAL + local_rtc_str=yes + ;; + (UTC|utc) + adjtime_str=UTC + local_rtc_str=no + ;; + (*) + printf 'Invalid value for --mode: %s\n' "${mode}" >&2 + printf 'Acceptable values are: localtime, utc.\n' >&2 + exit 1 +esac + + +if test -n "${timedatectl_localrtc}" +then + # systemd + timedatectl_should=${local_rtc_str} + if test "${timedatectl_localrtc}" != "${timedatectl_should}" + then + printf 'timedatectl set-local-rtc %s\n' "${timedatectl_should}" + fi +elif test -n "${adjtime_mode}" +then + # others (update /etc/adjtime if present) + if test "${adjtime_mode}" != "${adjtime_str}" + then + # Update /etc/adjtime (3rd line is clock mode) + # adjtime(5) https://man7.org/linux/man-pages/man5/adjtime.5.html + # FIXME: Should maybe add third line if adjfile only contains two lines + printf "sed -i '3c\\\\\\n%s\\n' /etc/adjtime\\n" "${adjtime_str}" + fi +fi diff --git a/cdist/conf/type/__hwclock/man.rst b/cdist/conf/type/__hwclock/man.rst new file mode 100644 index 00000000..65eb648f --- /dev/null +++ b/cdist/conf/type/__hwclock/man.rst @@ -0,0 +1,63 @@ +cdist-type__hwclock(7) +====================== + +NAME +---- +cdist-type__hwclock - Manage the hardware real time clock. + + +DESCRIPTION +----------- +This type can be used to control how the hardware clock is used by the operating +system. + + +REQUIRED PARAMETERS +------------------- +mode + What mode the hardware clock is in. + + Acceptable values: + + localtime + The hardware clock is set to local time (common for systems also running + Windows.) + UTC + The hardware clock is set to UTC (common on UNIX systems.) + + +OPTIONAL PARAMETERS +------------------- +None. + + +BOOLEAN PARAMETERS +------------------ +None. + + +EXAMPLES +-------- + +.. code-block:: sh + + # Make the operating system treat the time read from the hwclock as UTC. + __hwclock --mode UTC + + +SEE ALSO +-------- +:strong:`hwclock`\ (8) + + +AUTHORS +------- +Dennis Camera + + +COPYING +------- +Copyright \(C) 2020 Dennis Camera. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__hwclock/manifest b/cdist/conf/type/__hwclock/manifest new file mode 100755 index 00000000..7d9ab88f --- /dev/null +++ b/cdist/conf/type/__hwclock/manifest @@ -0,0 +1,222 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +# TODO: Consider supporting BADYEAR + +os=$(cat "${__global:?}/explorer/os") +mode=$(cat "${__object:?}/parameter/mode") + +has_systemd_timedatectl=$(test -s "${__object:?}/explorer/timedatectl_localrtc" && echo true || echo false) + + +case ${mode} +in + (localtime) + local_clock=true + ;; + (UTC|utc) + local_clock=false + ;; + (*) + printf 'Invalid value for --mode: %s\n' "${mode}" >&2 + printf 'Acceptable values are: UTC, localtime.\n' >&2 + exit 1 +esac + + +case ${os} +in + (alpine|gentoo) + if ! $has_systemd_timedatectl + then + # NOTE: Gentoo also supports systemd, in which case /etc/conf.d is + # not used. So we check for systemd presence here and only + # update /etc/conf.d if systemd is not installed. + # https://wiki.gentoo.org/wiki/System_time#Hardware_clock + + export CDIST_ORDER_DEPENDENCY=true + __file /etc/conf.d/hwclock --state present \ + --owner root --group root --mode 0644 + __key_value /etc/conf.d/hwclock:clock \ + --file /etc/conf.d/hwclock \ + --key clock \ + --delimiter '=' --exact_delimiter \ + --value "\"$($local_clock && echo local || echo UTC)\"" + unset CDIST_ORDER_DEPENDENCY + fi + ;; + (centos|fedora|redhat|scientific) + os_version=$(cat "${__global:?}/explorer/os_version") + os_major=$(expr "${os_version}" : '.* release \([0-9]*\)') + case ${os} + in + (centos|scientific) + update_sysconfig=$(test "${os_major}" -lt 6 && echo true || echo false) + ;; + (fedora) + update_sysconfig=$(test "${os_major}" -lt 10 && echo true || echo false) + ;; + (redhat|*) + case ${os_version} + in + ('Red Hat Enterprise Linux'*) + update_sysconfig=$(test "${os_major}" -lt 6 && echo true || echo false) + ;; + ('Red Hat Linux'*) + update_sysconfig=true + ;; + (*) + printf 'Could not determine Red Hat distribution.\n' >&2 + printf "Please contribute an implementation for it if you can.\n" >&2 + exit 1 + ;; + esac + ;; + esac + + if ${update_sysconfig:?} + then + export CDIST_ORDER_DEPENDENCY=true + __file /etc/sysconfig/clock --state present \ + --owner root --group root --mode 0644 + __key_value /etc/sysconfig/clock:UTC \ + --file /etc/sysconfig/clock \ + --key UTC \ + --delimiter '=' --exact_delimiter \ + --value "$($local_clock && echo false || echo true)" + unset CDIST_ORDER_DEPENDENCY + fi + ;; + (debian|devuan|ubuntu) + os_major=$(sed 's/[^0-9].*$//' "${__global:?}/explorer/os_version") + + case ${os} + in + (debian) + if test "${os_major}" -ge 7 + then + update_rcS=false + elif test "${os_major}" -ge 3 + then + update_rcS=true + else + # Debian 2.2 should be supportable using rcS. + # Debian 2.1 uses the ancient GMT key. + # Debian 1.3 does not have rcS. + printf "Your operating system (Debian %s) is currently not supported by this type (%s)\n" \ + "$(cat "${__global:?}/explorer/os_version")" "${__type##*/}" >&2 + printf "Please contribute an implementation for it if you can.\n" >&2 + exit 1 + fi + ;; + (devuan) + update_rcS=false + ;; + (ubuntu) + update_rcS=$(test "${os_major}" -lt 16 && echo true || echo false) + ;; + esac + + if ${update_rcS} + then + export CDIST_ORDER_DEPENDENCY=true + __file /etc/default/rcS --state present \ + --owner root --group root --mode 0644 + __key_value /etc/default/rcS:UTC \ + --file /etc/default/rcS \ + --key UTC \ + --delimiter '=' --exact_delimiter \ + --value "$($local_clock && echo no || echo yes)" + unset CDIST_ORDER_DEPENDENCY + fi + ;; + (freebsd) + # cf. adjkerntz(8) + __file /etc/wall_cmos_clock \ + --state "$($local_clock && echo present || echo absent)" \ + --owner root --group wheel --mode 0444 + ;; + (netbsd) + # https://wiki.netbsd.org/guide/boot/#index9h2 + __key_value /etc/rc.conf:rtclocaltime \ + --file /etc/rc.conf \ + --key rtclocaltime \ + --delimiter '=' --exact_delimiter \ + --value "$($local_clock && echo YES || echo NO)" + ;; + (slackware) + __file /etc/hardwareclock --owner root --group root --mode 0644 \ + --source - <<-EOF + # /etc/hardwareclock + # + # Tells how the hardware clock time is stored. + # This file is managed by cdist. + + $($local_clock && echo localtime || echo UTC) + EOF + ;; + (suse) + if test -s "${__global:?}/explorer/os_release" + then + # shellcheck source=/dev/null + os_version=$(. "${__global:?}/explorer/os_release" && echo "${VERSION}") + else + os_version=$(sed -n 's/^VERSION\ *=\ *//p' "${__global:?}/explorer/os_version") + fi + os_major=$(expr "${os_version}" : '\([0-9]\{1,\}\)') + + # TODO: Consider using `yast2 timezone set hwclock' instead + if expr "${os_major}" \< 12 + then + # Starting with SuSE 12 (first systemd-based version) + # /etc/sysconfig/clock does not contain the HWCLOCK line + # anymore. + # With SuSE 13, it has been reduced to TIMEZONE configuration. + __key_value /etc/sysconfig/clock:HWCLOCK \ + --file /etc/sysconfig/clock \ + --delimiter '=' --exact_delimiter \ + --key HWCLOCK \ + --value "$($local_clock && echo '"--localtime"' || echo '"-u"')" + fi + ;; + (void) + export CDIST_ORDER_DEPENDENCY=true + __file /etc/rc.conf \ + --owner root --group root --mode 0644 \ + --state present + __key_value /etc/rc.conf:HARDWARECLOCK \ + --file /etc/rc.conf \ + --delimiter '=' --exact_delimiter \ + --key HARDWARECLOCK \ + --value "\"$($local_clock && echo localtime || echo UTC)\"" + unset CDIST_ORDER_DEPENDENCY + ;; + (*) + if ! $has_systemd_timedatectl + then + printf "Your operating system (%s) is currently not supported by this type (%s)\n" "$os" "${__type##*/}" >&2 + printf "Please contribute an implementation for it if you can.\n" >&2 + exit 1 + fi + ;; +esac + +# NOTE: timedatectl set-local-rtc for systemd is in gencode-remote +# NOTE: /etc/adjtime is also updated in gencode-remote diff --git a/cdist/conf/type/__hwclock/parameter/required b/cdist/conf/type/__hwclock/parameter/required new file mode 100644 index 00000000..17ab372f --- /dev/null +++ b/cdist/conf/type/__hwclock/parameter/required @@ -0,0 +1 @@ +mode diff --git a/cdist/conf/type/__hwclock/singleton b/cdist/conf/type/__hwclock/singleton new file mode 100644 index 00000000..e69de29b From a07a45887136105cc8be316307805ceaecdd4cf3 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Fri, 13 Nov 2020 06:43:01 +0100 Subject: [PATCH 29/44] ++changelog --- docs/changelog | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changelog b/docs/changelog index 9fa0926c..ff411a46 100644 --- a/docs/changelog +++ b/docs/changelog @@ -4,7 +4,8 @@ Changelog next: * Documentation: Fix examples in best practice (Dennis Camera) * Type __locale: Add state explorer (Matthias Stecher) - * Core: Reorganize scripts, version generation (Ander Punnar) + * Core: Reorganize scripts, version generation (Ander Punnar, Dennis Camera) + * New type: __hwclock (Dennis Camera) 6.9.1: 2020-11-08 * Type __file: Fix state pre-exists (Dennis Camera) From 50927527863fd382f5200287a808d8b36028ff87 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Sat, 14 Nov 2020 09:59:30 +0100 Subject: [PATCH 30/44] Update build helper script in .gitattributes --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 45c10d7b..01d20f30 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,5 +4,5 @@ docs/speeches export-ignore docs/video export-ignore docs/src/man7 export-ignore -bin/build-helper export-ignore +bin/cdist-build-helper export-ignore README-maintainers export-ignore From 76aa00b12e9b675c797e997a06ec7597c53a85f3 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Sat, 14 Nov 2020 10:13:58 +0100 Subject: [PATCH 31/44] Fix importing cdist module Resolve #845. --- bin/cdist | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/bin/cdist b/bin/cdist index 1f92f157..ddaffa7f 100755 --- a/bin/cdist +++ b/bin/cdist @@ -25,25 +25,23 @@ import logging import os import sys -# try to import cdist and if that fails, -# then add this file's parent dir to -# module search path and try again. -try: - import cdist -except ModuleNotFoundError: - cdist_dir = os.path.realpath( - os.path.join( - os.path.dirname(os.path.realpath(__file__)), - os.pardir)) +# See if this file's parent is cdist module +# and if so add it to module search path. +cdist_dir = os.path.realpath( + os.path.join( + os.path.dirname(os.path.realpath(__file__)), + os.pardir)) +cdist_init_dir = os.path.join(cdist_dir, 'cdist', '__init__.py') +if os.path.exists(cdist_init_dir): sys.path.insert(0, cdist_dir) - import cdist -import cdist.argparse -import cdist.banner -import cdist.config -import cdist.install -import cdist.shell -import cdist.inventory +import cdist # noqa 402 +import cdist.argparse # noqa 402 +import cdist.banner # noqa 402 +import cdist.config # noqa 402 +import cdist.install # noqa 402 +import cdist.shell # noqa 402 +import cdist.inventory # noqa 402 def commandline(): From f75d4772090d3a9cfd798dd193cd324083a9b3a7 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sun, 8 Nov 2020 13:45:12 +0100 Subject: [PATCH 32/44] Deprecate __locale and replace with __localedef --- cdist/conf/type/__locale/deprecated | 1 + cdist/conf/type/__localedef/gencode-remote | 60 +++++++++++++++++++ cdist/conf/type/__localedef/man.rst | 55 +++++++++++++++++ cdist/conf/type/__localedef/manifest | 41 +++++++++++++ .../type/__localedef/parameter/default/state | 1 + .../conf/type/__localedef/parameter/optional | 1 + 6 files changed, 159 insertions(+) create mode 100644 cdist/conf/type/__locale/deprecated create mode 100755 cdist/conf/type/__localedef/gencode-remote create mode 100644 cdist/conf/type/__localedef/man.rst create mode 100755 cdist/conf/type/__localedef/manifest create mode 100644 cdist/conf/type/__localedef/parameter/default/state create mode 100644 cdist/conf/type/__localedef/parameter/optional diff --git a/cdist/conf/type/__locale/deprecated b/cdist/conf/type/__locale/deprecated new file mode 100644 index 00000000..5a06b28e --- /dev/null +++ b/cdist/conf/type/__locale/deprecated @@ -0,0 +1 @@ +This type is deprecated. Please use __localedef instead. diff --git a/cdist/conf/type/__localedef/gencode-remote b/cdist/conf/type/__localedef/gencode-remote new file mode 100755 index 00000000..1feb9884 --- /dev/null +++ b/cdist/conf/type/__localedef/gencode-remote @@ -0,0 +1,60 @@ +#!/bin/sh -e +# +# 2013-2019 Nico Schottelius (nico-cdist at schottelius.org) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# +# Let localedef do the magic +# + +locale="$__object_id" + +# Hardcoded, create a pull request with +# branching on $os in case it is at another location +alias=/usr/share/locale/locale.alias + +input=$(echo "$locale" | cut -d . -f 1) +charmap=$(echo "$locale" | cut -d . -f 2) + +# Adding locale? The name is de_CH.UTF-8 +# Removing locale? The name is de_CH.utf8. +# W-T-F! +locale_remove=$(echo "$locale" | sed 's/UTF-8/utf8/') + +state=$(cat "$__object/parameter/state") + +os=$(cat "$__global/explorer/os") + +# Nothing to be done on alpine +case "$os" in + alpine) + exit 0 + ;; +esac + +case "$state" in + present) + echo localedef -A "$alias" -f "$charmap" -i "$input" "$locale" + ;; + absent) + echo localedef --delete-from-archive "$locale_remove" + ;; + *) + echo "Unsupported state: $state" >&2 + exit 1 + ;; +esac diff --git a/cdist/conf/type/__localedef/man.rst b/cdist/conf/type/__localedef/man.rst new file mode 100644 index 00000000..0ccf484a --- /dev/null +++ b/cdist/conf/type/__localedef/man.rst @@ -0,0 +1,55 @@ +cdist-type__localedef(7) +======================== + +NAME +---- +cdist-type__localedef - Define and remove system locales + + +DESCRIPTION +----------- +This cdist type allows you to define locales on the system using +:strong:`localedef`\ (1) or remove them. +On systems that don't support definition of new locales, the type will raise an +error. + + +OPTIONAL PARAMETERS +------------------- +state + ``present`` or ``absent``. Defaults to ``present``. + + +EXAMPLES +-------- + +.. code-block:: sh + + # Add locale de_CH.UTF-8 + __localedef de_CH.UTF-8 + + # Same as above, but more explicit + __localedef de_CH.UTF-8 --state present + + # Remove colourful British English + __localedef en_GB.UTF-8 --state absent + + +SEE ALSO +-------- +:strong:`locale`\ (1), +:strong:`localedef`\ (1), +:strong:`cdist-type__locale_system`\ (7) + + +AUTHORS +------- +| Dennis Camera +| Nico Schottelius + + +COPYING +------- +Copyright \(C) 2013-2019 Nico Schottelius, 2020 Dennis Camera. Free use of this +software is granted under the terms of the GNU General Public License version 3 +or later (GPLv3+). diff --git a/cdist/conf/type/__localedef/manifest b/cdist/conf/type/__localedef/manifest new file mode 100755 index 00000000..9f1e17ac --- /dev/null +++ b/cdist/conf/type/__localedef/manifest @@ -0,0 +1,41 @@ +#!/bin/sh -e +# +# 2013-2019 Nico Schottelius (nico-cdist at schottelius.org) +# 2015 David Hürlimann (david at ungleich.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# +# Install required packages +# + +os=$(cat "$__global/explorer/os") + + +case "$os" in + debian|devuan) + # Debian needs a seperate package + __package locales --state present + ;; + archlinux|suse|ubuntu|scientific|centos|alpine) + : + ;; + *) + echo "Sorry, do not know how to handle os: $os" >&2 + echo "Please edit the type ${__type##*/} to fix this." >&2 + exit 1 + ;; +esac diff --git a/cdist/conf/type/__localedef/parameter/default/state b/cdist/conf/type/__localedef/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__localedef/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__localedef/parameter/optional b/cdist/conf/type/__localedef/parameter/optional new file mode 100644 index 00000000..ff72b5c7 --- /dev/null +++ b/cdist/conf/type/__localedef/parameter/optional @@ -0,0 +1 @@ +state From 54e689f7c2b3bc25aedb2aaf9f5aece289ec5b66 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sun, 8 Nov 2020 13:49:04 +0100 Subject: [PATCH 33/44] [type/__localedef] Add state explorer --- cdist/conf/type/__localedef/explorer/state | 71 ++++++++++++++++++++++ cdist/conf/type/__localedef/gencode-remote | 13 +++- 2 files changed, 82 insertions(+), 2 deletions(-) create mode 100755 cdist/conf/type/__localedef/explorer/state diff --git a/cdist/conf/type/__localedef/explorer/state b/cdist/conf/type/__localedef/explorer/state new file mode 100755 index 00000000..d8db29c5 --- /dev/null +++ b/cdist/conf/type/__localedef/explorer/state @@ -0,0 +1,71 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# This explorer determines if the locale is defined on the target system. +# Will print nothing on error. +# +# Possible output: +# present: +# the main locale (and possibly aliases) is present +# absent: +# neither the main locale nor any aliases are present +# + +command -v locale >/dev/null 2>&1 || exit 0 + +locales=$(locale -a) + +parse_locale() { + # This function will split locales into their parts. Locale strings are + # usually of the form: [language[_territory][.codeset][@modifier]] + # For simplicity, language and territory are not separated by this function. + # Old Linux systems were also using "english" or "german" as locale strings. + # Usage: parse_locale locale_str lang_var codeset_var modifier_var + eval "${2:?}"="$(expr "$1" : '\([^.@]*\)')" + eval "${3:?}"="$(expr "$1" : '[^.]*\.\([^@]*\)')" + eval "${4:?}"="$(expr "$1" : '.*@\(.*\)$')" +} + +format_locale() { + # Usage: format_locale language codeset modifier + printf '%s' "$1" + test -z "$2" || printf '.%s' "$2" + test -z "$3" || printf '@%s' "$3" + printf '\n' +} + +gnu_normalize_codeset() { + # reimplementation of glibc/locale/programs/localedef.c normalize_codeset() + echo "$*" | tr '[:upper:]' '[:lower:]' | tr -cd '[:alnum:]' +} + +locale_available() ( + echo "${locales}" | grep -qxF "$1" || { + # glibc uses "normalized" locale names in archives. + # If a locale is stored in an archive, the normalized name will be + # printed by locale, so that needs to be checked, too. + localename=$( + parse_locale "$1" _lang _codeset _modifier \ + && format_locale "${_lang:?}" "$(gnu_normalize_codeset "${_codeset?}")" \ + "${_modifier?}") + echo "${locales}" | grep -qxF "${localename}" + } +) + +locale_available "${__object_id:?}" && echo present || echo absent diff --git a/cdist/conf/type/__localedef/gencode-remote b/cdist/conf/type/__localedef/gencode-remote index 1feb9884..9ea3f6f1 100755 --- a/cdist/conf/type/__localedef/gencode-remote +++ b/cdist/conf/type/__localedef/gencode-remote @@ -35,7 +35,8 @@ charmap=$(echo "$locale" | cut -d . -f 2) # W-T-F! locale_remove=$(echo "$locale" | sed 's/UTF-8/utf8/') -state=$(cat "$__object/parameter/state") +state_is=$(cat "${__object:?}/explorer/state") +state_should=$(cat "${__object:?}/parameter/state") os=$(cat "$__global/explorer/os") @@ -46,7 +47,15 @@ case "$os" in ;; esac -case "$state" in +# NOTE: If state explorer fails (e.g. locale(1) missing), the following check +# will always fail and let definition/removal run. +if test "${state_is}" = "${state_should}" +then + exit 0 +fi + +case ${state_should} +in present) echo localedef -A "$alias" -f "$charmap" -i "$input" "$locale" ;; From cc29e54b851212568c3f6eddf4bf0a64c1eabb0d Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 23 Jul 2020 23:20:39 +0200 Subject: [PATCH 34/44] [type/__localedef] Differentiate between OSes and better handling of normalized locale names --- cdist/conf/type/__localedef/gencode-remote | 117 +++++++++++++++------ 1 file changed, 82 insertions(+), 35 deletions(-) diff --git a/cdist/conf/type/__localedef/gencode-remote b/cdist/conf/type/__localedef/gencode-remote index 9ea3f6f1..b9cc68e8 100755 --- a/cdist/conf/type/__localedef/gencode-remote +++ b/cdist/conf/type/__localedef/gencode-remote @@ -1,6 +1,7 @@ #!/bin/sh -e # # 2013-2019 Nico Schottelius (nico-cdist at schottelius.org) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -17,35 +18,16 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # +# Manage system locales using localedef(1). # -# Let localedef do the magic -# - -locale="$__object_id" - -# Hardcoded, create a pull request with -# branching on $os in case it is at another location -alias=/usr/share/locale/locale.alias - -input=$(echo "$locale" | cut -d . -f 1) -charmap=$(echo "$locale" | cut -d . -f 2) - -# Adding locale? The name is de_CH.UTF-8 -# Removing locale? The name is de_CH.utf8. -# W-T-F! -locale_remove=$(echo "$locale" | sed 's/UTF-8/utf8/') state_is=$(cat "${__object:?}/explorer/state") state_should=$(cat "${__object:?}/parameter/state") -os=$(cat "$__global/explorer/os") - -# Nothing to be done on alpine -case "$os" in - alpine) - exit 0 - ;; -esac +test "${state_should}" = 'present' -o "${state_should}" = 'absent' || { + printf 'Invalid state: %s\n' "${state_should}" >&2 + exit 1 +} # NOTE: If state explorer fails (e.g. locale(1) missing), the following check # will always fail and let definition/removal run. @@ -54,16 +36,81 @@ then exit 0 fi -case ${state_should} +locale=${__object_id:?} +os=$(cat "${__global:?}/explorer/os") + +if expr "${locale}" : '.*/' >/dev/null +then + printf 'Paths as locales are not supported.\n' >&2 + printf '__object_id is: %s\n' "${locale}" >&2 + exit 1 +fi + +parse_locale() { + # This function will split locales into their parts. Locale strings are + # usually of the form: [language[_territory][.codeset][@modifier]] + # For simplicity, language and territory are not separated by this function. + # Old Linux systems were also using "english" or "german" as locale strings. + # Usage: parse_locale locale_str lang_var codeset_var modifier_var + eval "${2:?}"="$(expr "$1" : '\([^.@]*\)')" + eval "${3:?}"="$(expr "$1" : '[^.]*\.\([^@]*\)')" + eval "${4:?}"="$(expr "$1" : '.*@\(.*\)$')" +} + +format_locale() { + # Usage: format_locale language codeset modifier + printf '%s' "$1" + test -z "$2" || printf '.%s' "$2" + test -z "$3" || printf '@%s' "$3" + printf '\n' +} + +gnu_normalize_codeset() { + echo "$*" | tr -cd '[:alnum:]' | tr '[:upper:]' '[:lower:]' +} + + +: "${lang=}" "${codeset=}" "${modifier=}" # declare variables for shellcheck +parse_locale "${locale}" lang codeset modifier + + +case ${os} in - present) - echo localedef -A "$alias" -f "$charmap" -i "$input" "$locale" - ;; - absent) - echo localedef --delete-from-archive "$locale_remove" - ;; - *) - echo "Unsupported state: $state" >&2 - exit 1 - ;; + (alpine|openwrt) + printf '%s does not support locales.\n' "${os}" >&2 + exit 1 + ;; + (archlinux|debian|devuan|ubuntu|suse|centos|fedora|redhat|scientific) + # FIXME: The code below only works for glibc-based installations. + + # NOTE: Hardcoded, create a pull request in case it is at another + # location for some opther distro. + # NOTE: locale.alias can be symlinked (e.g. Debian) + aliasfile='/usr/share/locale/locale.alias' + + case ${state_should} + in + (present) + input=$(format_locale "${lang}" '' "${modifier}") + cat <<-EOF + set -- + if test -e '${aliasfile}' + then + set -- -A '${aliasfile}' + fi + + localedef -i '${input}' -f '${codeset}' "\$@" '${locale}' + EOF + ;; + (absent) + localename=$(format_locale "${lang}" "$(gnu_normalize_codeset "${codeset}")" "${modifier}") + printf "localedef --delete-from-archive '%s'\n" "${localename}" + ;; + esac + ;; + (*) + echo "Your operating system (${os}) is currently not supported by this type (${__type##*/})." >&2 + echo "Please contribute an implementation for it if you can." >&2 + exit 1 + ;; esac From f44888f192d78d8ce1e3583f70f94b0c2039c1e2 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sun, 8 Nov 2020 14:02:08 +0100 Subject: [PATCH 35/44] [type/__localedef] Only install dependencies in manifest. OS checking moved to gencode-remote --- cdist/conf/type/__localedef/manifest | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/cdist/conf/type/__localedef/manifest b/cdist/conf/type/__localedef/manifest index 9f1e17ac..3ab3ad8c 100755 --- a/cdist/conf/type/__localedef/manifest +++ b/cdist/conf/type/__localedef/manifest @@ -2,6 +2,7 @@ # # 2013-2019 Nico Schottelius (nico-cdist at schottelius.org) # 2015 David Hürlimann (david at ungleich.ch) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -18,24 +19,12 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # -# -# Install required packages +# Install required packages. # -os=$(cat "$__global/explorer/os") - - -case "$os" in - debian|devuan) - # Debian needs a seperate package - __package locales --state present - ;; - archlinux|suse|ubuntu|scientific|centos|alpine) - : - ;; - *) - echo "Sorry, do not know how to handle os: $os" >&2 - echo "Please edit the type ${__type##*/} to fix this." >&2 - exit 1 - ;; +case $(cat "${__global:?}/explorer/os") +in + (debian|devuan) + __package_apt locales --state present + ;; esac From dcef2c19f5b87e3a9594f01fefab66cc7488dce7 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sun, 8 Nov 2020 14:07:42 +0100 Subject: [PATCH 36/44] [type/__localedef] Add support for FreeBSD --- cdist/conf/type/__localedef/gencode-remote | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/cdist/conf/type/__localedef/gencode-remote b/cdist/conf/type/__localedef/gencode-remote index b9cc68e8..80e7eb1c 100755 --- a/cdist/conf/type/__localedef/gencode-remote +++ b/cdist/conf/type/__localedef/gencode-remote @@ -108,6 +108,25 @@ in ;; esac ;; + (freebsd) + case ${state_should} + in + (present) + if expr "$(grep -oe '^[0-9]*' "${__global:?}/explorer/os_version")" '>=' 11 >/dev/null + then + # localedef(1) is available with FreeBSD >= 11 + printf "localedef -i '%s' -f '%s' '%s'\n" "${input}" "${codeset}" "${locale}" + else + printf 'localedef(1) was added to FreeBSD starting with version 11.\n' >&2 + printf 'Please upgrade your FreeBSD installation to use %s.\n' "${__type##*/}" >&2 + exit 1 + fi + ;; + (absent) + printf "rm -R '/usr/share/locale/%s'\n" "${locale}" + ;; + esac + ;; (*) echo "Your operating system (${os}) is currently not supported by this type (${__type##*/})." >&2 echo "Please contribute an implementation for it if you can." >&2 From c1c60e3374e0cb93ea182c4cbcb813150756c3e4 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sun, 8 Nov 2020 15:24:46 +0100 Subject: [PATCH 37/44] [type/__localedef] Blacklist OpenBSD and NetBSD --- cdist/conf/type/__localedef/gencode-remote | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cdist/conf/type/__localedef/gencode-remote b/cdist/conf/type/__localedef/gencode-remote index 80e7eb1c..af1a77f7 100755 --- a/cdist/conf/type/__localedef/gencode-remote +++ b/cdist/conf/type/__localedef/gencode-remote @@ -127,6 +127,12 @@ in ;; esac ;; + (netbsd|openbsd) + # NetBSD/OpenBSD are missing localedef(1). + # We also do not delete defined locales because they can't be recreated. + echo "${os} is lacking localedef(1). Locale management unavailable." >&2 + exit 1 + ;; (*) echo "Your operating system (${os}) is currently not supported by this type (${__type##*/})." >&2 echo "Please contribute an implementation for it if you can." >&2 From 575bb62dc507fb8c294319f8db20ba45595e6869 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Fri, 13 Nov 2020 18:42:04 +0100 Subject: [PATCH 38/44] [type/__localedef] Externalise functions to separate files --- .../conf/type/__localedef/files/lib/glibc.sh | 5 ++++ .../conf/type/__localedef/files/lib/locale.sh | 20 +++++++++++++ cdist/conf/type/__localedef/gencode-remote | 29 ++++--------------- 3 files changed, 30 insertions(+), 24 deletions(-) create mode 100644 cdist/conf/type/__localedef/files/lib/glibc.sh create mode 100644 cdist/conf/type/__localedef/files/lib/locale.sh diff --git a/cdist/conf/type/__localedef/files/lib/glibc.sh b/cdist/conf/type/__localedef/files/lib/glibc.sh new file mode 100644 index 00000000..6ace80d4 --- /dev/null +++ b/cdist/conf/type/__localedef/files/lib/glibc.sh @@ -0,0 +1,5 @@ +# -*- mode: sh; indent-tabs-mode: t -*- + +gnu_normalize_codeset() { + echo "$*" | tr -cd '[:alnum:]' | tr '[:upper:]' '[:lower:]' +} diff --git a/cdist/conf/type/__localedef/files/lib/locale.sh b/cdist/conf/type/__localedef/files/lib/locale.sh new file mode 100644 index 00000000..b5e61374 --- /dev/null +++ b/cdist/conf/type/__localedef/files/lib/locale.sh @@ -0,0 +1,20 @@ +# -*- mode: sh; indent-tabs-mode:t -*- + +parse_locale() { + # This function will split locales into their parts. Locale strings are + # usually of the form: [language[_territory][.codeset][@modifier]] + # For simplicity, language and territory are not separated by this function. + # Old Linux systems were also using "english" or "german" as locale strings. + # Usage: parse_locale locale_str lang_var codeset_var modifier_var + eval "${2:?}"="$(expr "$1" : '\([^.@]*\)')" + eval "${3:?}"="$(expr "$1" : '[^.]*\.\([^@]*\)')" + eval "${4:?}"="$(expr "$1" : '.*@\(.*\)$')" +} + +format_locale() { + # Usage: format_locale language codeset modifier + printf '%s' "$1" + test -z "$2" || printf '.%s' "$2" + test -z "$3" || printf '@%s' "$3" + printf '\n' +} diff --git a/cdist/conf/type/__localedef/gencode-remote b/cdist/conf/type/__localedef/gencode-remote index af1a77f7..17941c63 100755 --- a/cdist/conf/type/__localedef/gencode-remote +++ b/cdist/conf/type/__localedef/gencode-remote @@ -21,6 +21,11 @@ # Manage system locales using localedef(1). # +# shellcheck source=cdist/conf/type/__localedef/files/lib/locale.sh +. "${__type:?}/files/lib/locale.sh" +# shellcheck source=cdist/conf/type/__localedef/files/lib/glibc.sh +. "${__type:?}/files/lib/glibc.sh" + state_is=$(cat "${__object:?}/explorer/state") state_should=$(cat "${__object:?}/parameter/state") @@ -46,30 +51,6 @@ then exit 1 fi -parse_locale() { - # This function will split locales into their parts. Locale strings are - # usually of the form: [language[_territory][.codeset][@modifier]] - # For simplicity, language and territory are not separated by this function. - # Old Linux systems were also using "english" or "german" as locale strings. - # Usage: parse_locale locale_str lang_var codeset_var modifier_var - eval "${2:?}"="$(expr "$1" : '\([^.@]*\)')" - eval "${3:?}"="$(expr "$1" : '[^.]*\.\([^@]*\)')" - eval "${4:?}"="$(expr "$1" : '.*@\(.*\)$')" -} - -format_locale() { - # Usage: format_locale language codeset modifier - printf '%s' "$1" - test -z "$2" || printf '.%s' "$2" - test -z "$3" || printf '@%s' "$3" - printf '\n' -} - -gnu_normalize_codeset() { - echo "$*" | tr -cd '[:alnum:]' | tr '[:upper:]' '[:lower:]' -} - - : "${lang=}" "${codeset=}" "${modifier=}" # declare variables for shellcheck parse_locale "${locale}" lang codeset modifier From eeb98719197a09691af60fa26c0b64b3aaf8960e Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sat, 14 Nov 2020 09:54:01 +0100 Subject: [PATCH 39/44] [type/__localedef] glibc: Also delete aliases when removing a locale --- cdist/conf/type/__localedef/gencode-remote | 15 +++++++++++++-- cdist/conf/type/__localedef/man.rst | 5 +++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/cdist/conf/type/__localedef/gencode-remote b/cdist/conf/type/__localedef/gencode-remote index 17941c63..d7fd6942 100755 --- a/cdist/conf/type/__localedef/gencode-remote +++ b/cdist/conf/type/__localedef/gencode-remote @@ -84,8 +84,19 @@ in EOF ;; (absent) - localename=$(format_locale "${lang}" "$(gnu_normalize_codeset "${codeset}")" "${modifier}") - printf "localedef --delete-from-archive '%s'\n" "${localename}" + main_localename=$(format_locale "${lang}" "$(gnu_normalize_codeset "${codeset}")" "${modifier}") + + cat <<-EOF + while read -r _alias _localename + do + if test "\${_localename}" = '$(format_locale "${lang}" "${codeset}")' + then + localedef --delete-from-archive "\${_alias}" + fi + done <'${aliasfile}' + + localedef --delete-from-archive '${main_localename}' + EOF ;; esac ;; diff --git a/cdist/conf/type/__localedef/man.rst b/cdist/conf/type/__localedef/man.rst index 0ccf484a..454ce9d1 100644 --- a/cdist/conf/type/__localedef/man.rst +++ b/cdist/conf/type/__localedef/man.rst @@ -13,6 +13,11 @@ This cdist type allows you to define locales on the system using On systems that don't support definition of new locales, the type will raise an error. +**NB:** This type respects the glibc ``locale.alias`` file, +i.e. it defines alias locales or deletes aliases of a locale when it is removed. +It is not possible, however, to use alias names to define locales or only remove +certain aliases of a locale. + OPTIONAL PARAMETERS ------------------- From 87faffd8750cac94190707c2b37233229c3fbde2 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sat, 14 Nov 2020 10:41:50 +0100 Subject: [PATCH 40/44] [type/__localdef] Also check for aliases in state explorer --- cdist/conf/type/__localedef/explorer/state | 31 +++++++++++++++++++++- cdist/conf/type/__localedef/gencode-remote | 7 +++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/cdist/conf/type/__localedef/explorer/state b/cdist/conf/type/__localedef/explorer/state index d8db29c5..3ba57661 100755 --- a/cdist/conf/type/__localedef/explorer/state +++ b/cdist/conf/type/__localedef/explorer/state @@ -25,8 +25,14 @@ # the main locale (and possibly aliases) is present # absent: # neither the main locale nor any aliases are present +# alias-present: +# the main locale is absent, but at least one of its aliases is present # +# Hardcoded, create a pull request in case it is at another location for +# some other distro. (cf. gencode-remote) +aliasfile='/usr/share/locale/locale.alias' + command -v locale >/dev/null 2>&1 || exit 0 locales=$(locale -a) @@ -68,4 +74,27 @@ locale_available() ( } ) -locale_available "${__object_id:?}" && echo present || echo absent +if locale_available "${__object_id:?}" +then + echo present +else + # NOTE: locale.alias can be symlinked. + if test -e "${aliasfile}" + then + # Check if one of the aliases of the locale is defined + baselocale=$( + parse_locale "${__object_id:?}" _lang _codeset _modifiers \ + && format_locale "${_lang}" "${_codeset}") + while read -r _alias _localename + do + if test "${_localename}" = "${baselocale}" \ + && echo "${locales}" | grep -qxF "${_alias}" + then + echo alias-present + exit 0 + fi + done <"${aliasfile}" + fi + + echo absent +fi diff --git a/cdist/conf/type/__localedef/gencode-remote b/cdist/conf/type/__localedef/gencode-remote index d7fd6942..4538151f 100755 --- a/cdist/conf/type/__localedef/gencode-remote +++ b/cdist/conf/type/__localedef/gencode-remote @@ -94,9 +94,12 @@ in localedef --delete-from-archive "\${_alias}" fi done <'${aliasfile}' - - localedef --delete-from-archive '${main_localename}' EOF + + if test "${state_is}" = present + then + printf "localedef --delete-from-archive '%s'\n" "${main_localename}" + fi ;; esac ;; From 9d4f69250ea0e6bb733cde1e42183480f48b5d8f Mon Sep 17 00:00:00 2001 From: ssrq Date: Thu, 19 Nov 2020 19:33:47 +0100 Subject: [PATCH 41/44] __sshd config: New type --- cdist/conf/type/__sshd_config/explorer/state | 121 ++++++++ .../files/update_sshd_config.awk | 293 ++++++++++++++++++ cdist/conf/type/__sshd_config/gencode-remote | 97 ++++++ cdist/conf/type/__sshd_config/man.rst | 94 ++++++ cdist/conf/type/__sshd_config/manifest | 48 +++ .../type/__sshd_config/parameter/default/file | 1 + .../__sshd_config/parameter/default/state | 1 + .../type/__sshd_config/parameter/optional | 4 + .../__sshd_config/parameter/optional_multiple | 1 + 9 files changed, 660 insertions(+) create mode 100644 cdist/conf/type/__sshd_config/explorer/state create mode 100644 cdist/conf/type/__sshd_config/files/update_sshd_config.awk create mode 100755 cdist/conf/type/__sshd_config/gencode-remote create mode 100644 cdist/conf/type/__sshd_config/man.rst create mode 100755 cdist/conf/type/__sshd_config/manifest create mode 100644 cdist/conf/type/__sshd_config/parameter/default/file create mode 100644 cdist/conf/type/__sshd_config/parameter/default/state create mode 100644 cdist/conf/type/__sshd_config/parameter/optional create mode 100644 cdist/conf/type/__sshd_config/parameter/optional_multiple diff --git a/cdist/conf/type/__sshd_config/explorer/state b/cdist/conf/type/__sshd_config/explorer/state new file mode 100644 index 00000000..75c68b8a --- /dev/null +++ b/cdist/conf/type/__sshd_config/explorer/state @@ -0,0 +1,121 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# Determines the current state of the config option. +# Possible output: +# - present: "should" option present in config file +# - default: the "should" option is the default -> don’t know if present +# - absent: no such option present in config file +# + +joinlines() { sed -n -e H -e "\${x;s/^\\n//;s/\\n/${1:?}/g;p;}"; } +trlower() { tr '[:upper:]' '[:lower:]'; } +tolower() { printf '%s' "$*" | trlower; } + +default_value() { + sshd -T -f /dev/null -C "$(make_conn_spec)" \ + | sed -n -e 's/^'"$(tolower "${1:?}")"'[[:blank:]]\{1,\}//p' +} + +make_conn_spec() { + if test -s "${__object:?}/parameter/match" + then + _match_file="${__object:?}/parameter/match" + else + _match_file='/dev/null' + fi + + for _kw in \ + addr=Address \ + user=User \ + host=Host \ + laddr=LocalAddress \ + lport=LocalPort \ + rdomain=RDomain + do + _specname=${_kw%%=*} + _confname=$(tolower "${_kw#*=}") + while read -r _k _v + do + if test "$(tolower "${_k}")" = "${_confname}" + then + printf '%s=%s\n' "${_specname}" "${_v}" + continue 2 + fi + done <"${_match_file}" + + # NOTE: Print test spec even for empty keys to suppress errors like: + # 'Match User' in configuration but 'user' not in connection test specification. + # except lport: + # Invalid port '' in test mode specification lport= + test "${_specname}" = 'lport' || printf '%s=\n' "${_specname}" + done \ + | joinlines ',' + unset _match_file +} + +sshd_config_file=$(cat "${__object:?}/parameter/file") +state_should=$(cat "${__object:?}/parameter/state") + +if test -s "${__object:?}/parameter/option" +then + option_name=$(cat "${__object:?}/parameter/option") +else + option_name=${__object_id:?} +fi + +value_should=$(cat "${__object:?}/parameter/value" 2>/dev/null) \ +|| test "${state_should}" = absent || exit 0 # param optional if --state absent + +command -v sshd >/dev/null 2>&1 || { + echo 'Cannot find sshd.' >&2 + exit 1 +} + +test -e "${sshd_config_file}" || { + echo 'absent' + exit 0 +} + +value_is=$( + sshd -T -f "${sshd_config_file}" -C "$(make_conn_spec)" \ + | sed -n -e 's/^'"$(tolower "${option_name}")"'[[:blank:]]\{1,\}//p') + +if printf '%s\n' "${value_is}" | { + if test -n "${value_should}" + then + grep -q -x -F "${value_should}" + else + # if no value provided, assume "any" value + grep -q -e . + fi + } +then + if default_value "${option_name}" | grep -q -x -F "${value_is}" + then + # Might produce false positives for default values. + # TODO: Manual checking should be done, but for simplicity, this case is + # currently ignored here. + echo default + else + echo present + fi +else + echo absent +fi diff --git a/cdist/conf/type/__sshd_config/files/update_sshd_config.awk b/cdist/conf/type/__sshd_config/files/update_sshd_config.awk new file mode 100644 index 00000000..d0bc2b4b --- /dev/null +++ b/cdist/conf/type/__sshd_config/files/update_sshd_config.awk @@ -0,0 +1,293 @@ +# -*- mode: awk; indent-tabs-mode: t -*- + +function usage() { + print_err("Usage: awk -f update_sshd_config.awk -- -o set|unset [-m 'User git'] -l 'X11Forwarding no' /etc/ssh/sshd_config") +} + +function print_err(s) { print s | "cat >&2" } + +function alength(a, i) { + for (i = 0; (i + 1) in a; ++i); + return i +} + +function join(sep, a, i, s) { + for (i = i ? i : 1; i in a; i++) + s = s sep a[i] + return substr(s, 2) +} + +function getopt(opts, argv, target, files, i, c, lv, idx, nf) { + # trivial getopt(3) implementation; only basic functionality + if (argv[1] == "--") i++ + for (i += 1; i in argv; i++) { + if (lv) { target[c] = argv[i]; lv = 0; continue } + if (argv[i] ~ /^-/) { + c = substr(argv[i], 2, 1) + idx = index(opts, c) + if (!idx) { + print_err(sprintf("invalid option -%c\n", c)) + continue + } + if (substr(opts, idx + 1, 1) == ":") { + # option takes argument + if (length(argv[i]) > 2) + target[c] = substr(argv[i], 3) + else + lv = 1 + } else { + target[c] = 1 + } + } else + files[++nf] = argv[i] + } +} + +# tokenise configuration line +# this function mimics the counterpart in OpenSSH (misc.c) +# but it returns two (next token SUBSEP rest) because I didn’t want to have to +# simulate any pointer magic. +function strdelim_internal(s, split_equals, old) { + if (!s) + return "" + + old = s + + if (!match(s, WHITESPACE "|" QUOTE "" (split_equals ? "|" EQUALS : ""))) + return s + + s = substr(s, RSTART) + old = substr(old, 1, RSTART - 1) + + if (s ~ "^" QUOTE) { + old = substr(old, 2) + + # Find matching quote + if (match(s, QUOTE)) { + old = substr(old, 1, RSTART) + # s = substr() + if (match(s, "^" WHITESPACE "*")) + s = substr(s, RLENGTH) + return old + } else { + # no matching quote + return "" + } + } + + if (match(s, "^" WHITESPACE "+")) { + sub("^" WHITESPACE "+", "", s) + if (split_equals) + sub(EQUALS WHITESPACE "*", "", s) + } else if (s ~ "^" EQUALS) { + s = substr(s, 2) + } + + return old SUBSEP s +} +function strdelim(s) { return strdelim_internal(s, 1) } +function strdelimw(s) { return strdelim_internal(s, 0) } + +function singleton_option(opt) { + return tolower(opt) !~ /^(acceptenv|allowgroups|allowusers|authenticationmethods|authorizedkeysfile|denygroups|denyusers|hostcertificate|hostkey|listenaddress|logverbose|permitlisten|permitopen|port|setenv|subsystem)$/ +} + +function print_update() { + if (mode) { + if (match_only) printf "\t" + printf "%s\n", line_should + updated = 1 + } +} + +BEGIN { + FS = "\n" # disable field splitting + + WHITESPACE = "[ \t]" # servconf.c, misc.c:strdelim_internal (without line breaks, cf. bugs) + QUOTE = "[\"]" # misc.c:strdelim_internal + EQUALS = "[=]" + + split("", opts) + split("", files) + getopt("ho:l:m:", ARGV, opts, files) + + if (opts["h"]) { usage(); exit (e="0") } + + line_should = opts["l"] + match_only = opts["m"] + num_files = alength(files) + + if (num_files != 1 || !opts["o"] || !line_should) { + usage() + exit (e=126) + } + + if (opts["o"] == "set") { + mode = 1 + } else if (opts["o"] == "unset") { + mode = 0 + } else { + print_err(sprintf("invalid mode %s\n", mode)) + exit (e=1) + } + + if (mode) { + # loop over sshd_config twice! + ARGV[2] = ARGV[1] = files[1] + ARGC = 3 + } else { + # only loop once + ARGV[1] = files[1] + ARGC = 2 + } + + split(strdelim(line_should), should, SUBSEP) + option_should = tolower(should[1]) + value_should = should[2] +} + +{ + line = $0 + + # Strip trailing whitespace. Allow \f (form feed) at EOL only + sub("(" WHITESPACE "|\f)*$", "", line) + + # Strip leading whitespace + sub("^" WHITESPACE "*", "", line) + + if (match(line, "^#" WHITESPACE "*")) { + prefix = substr(line, RSTART, RLENGTH) + line = substr(line, RSTART + RLENGTH) + } else { + prefix = "" + } + + line_type = "invalid" + option_is = value_is = "" + + if (line) { + split(strdelim(line), toks, SUBSEP) + + if (tolower(toks[1]) == "match") { + MATCH = (prefix ~ /^#/ ? "#" : "") join(" ", toks, 2) + line_type = "match" + } else if (toks[1] ~ /^[A-Za-z][A-Za-z0-9]+$/) { + # This could be an option line + line_type = "option" + option_is = tolower(toks[1]) + value_is = toks[2] + } + } else { + line_type = "empty" + } +} + +# mode: unset + +!mode { + # delete matching config + if (prefix !~ /^#/) + if (MATCH == match_only && option_is == option_should) + if (!value_should || value_should == value_is) + next + + print + next +} + + +# mode: set + +mode && NR == FNR { + if (line_type == "option") { + if (MATCH !~ /^#/) { + if (prefix ~ /^#/) { + # comment line + last_occ[MATCH, "#" option_is] = FNR + } else { + # option line + last_occ[MATCH, option_is] = FNR + } + last_occ[MATCH] = FNR + } + } else if (line_type == "invalid" && !prefix) { + # INVALID LINE + print_err(sprintf("%s: syntax error on line %u\n", ARGV[0], FNR)) + } + + next +} + +# before second pass prepare hashes containing location information to be used +# in the second pass. +mode && NR > FNR && FNR == 1 { + # First we drop the locations of commented-out options if a non-commented + # option is available. If a non-commented option is available, we will + # append new config options there to have them all at one place. + for (k in last_occ) { + if (k ~ /^#/) { + # delete entries of commented out match blocks + delete last_occ[k] + continue + } + + split(k, parts, SUBSEP) + + if (parts[2] ~ /^#/ && ((parts[1], substr(parts[2], 2)) in last_occ)) + delete last_occ[k] + } + + # Reverse the option => line mapping. The line_map allows for easier lookups + # in the second pass. + # We only keep options, not top-level keywords, because we can only have + # one entry per line and there are conflicts with last lines of "sections". + for (k in last_occ) { + if (!index(k, SUBSEP)) continue + line_map[last_occ[k]] = k + } +} + +# Second pass +mode && line_map[FNR] == match_only SUBSEP option_should && !updated { + split(line_map[FNR], parts, SUBSEP) + + # If option allows multiple values, print current value + if (!singleton_option(parts[2])) { + if (value_should != value_is) + print + } + + print_update() + + next +} + +mode { print } + +# Is a comment option +mode && line_map[FNR] == match_only SUBSEP "#" option_should && !updated { + print_update() +} + +# Last line of the should match section +mode && last_occ[match_only] == FNR && !updated { + # NOTE: Inserting empty lines is only cosmetic. It is only done if + # different options are next to each other and not in a match block + # (match blocks are usually not in the default config and thus don’t + # contain commented blocks.) + if (line && option_is != option_should && !MATCH) + print "" + print_update() +} + +END { + if (e) exit e + + if (mode && !updated) { + if (match_only && MATCH != match_only) { + printf "\nMatch %s\n", match_only + } + + print_update() + } +} diff --git a/cdist/conf/type/__sshd_config/gencode-remote b/cdist/conf/type/__sshd_config/gencode-remote new file mode 100755 index 00000000..0b44dfa7 --- /dev/null +++ b/cdist/conf/type/__sshd_config/gencode-remote @@ -0,0 +1,97 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +joinlines() { sed -n -e H -e "\${x;s/^\\n//;s/\\n/${1:?}/g;p;}"; } + +state_is=$(cat "${__object:?}/explorer/state") +state_should=$(cat "${__object:?}/parameter/state") + +if test "${state_is}" = "${state_should}" -o "${state_is}" = 'default' +then + # nothing to do (if the value is the default, ignore its state) + exit 0 +fi + +case ${state_should} +in + (present) + mode='set' + ;; + (absent) + mode='unset' + ;; + (*) + printf 'Invalid --state: %s\n' "${state_should}" >&2 + exit 1 + ;; +esac + +sshd_config_file=$(cat "${__object:?}/parameter/file") + +quote() { printf "'%s'" "$(printf '%s' "$*" | sed -e "s/'/'\\\\''/g")"; } +drop_awk_comments() { quote "$(sed '/^[[:blank:]]*#.*$/d;/^$/d' "$@")"; } + +# Ensure the sshd_config file is there +cat <$(quote "${sshd_config_file}") + chown 0:0 $(quote "${sshd_config_file}") + chmod 0644 $(quote "${sshd_config_file}") +} + +EOF + +match_only= +if test -s "${__object:?}/parameter/match" +then + match_only=$(joinlines ' ' <"${__object:?}/parameter/match") +fi + +if test -s "${__object:?}/parameter/option" +then + option_line=$(cat "${__object:?}/parameter/option") +else + option_line=${__object_id:?} +fi + +if test -s "${__object:?}/parameter/value" +then + option_line="${option_line} $(cat "${__object:?}/parameter/value")" +fi + +# Send message on config update +printf '%s%s %s\n' "${mode}" "${match_only:+ [${match_only}]}" \ + "${option_line}" >>"${__messages_out:?}" + +# Update sshd_config (remote code) +cat <$(quote "${sshd_config_file}.tmp") \\ +|| exit + +cmp -s $(quote "${sshd_config_file}") $(quote "${sshd_config_file}.tmp") || { + sshd -t -f $(quote "${sshd_config_file}.tmp") \\ + && cat $(quote "${sshd_config_file}.tmp") >$(quote "${sshd_config_file}") +} +rm -f $(quote "${sshd_config_file}.tmp") +EOF diff --git a/cdist/conf/type/__sshd_config/man.rst b/cdist/conf/type/__sshd_config/man.rst new file mode 100644 index 00000000..8b0069ac --- /dev/null +++ b/cdist/conf/type/__sshd_config/man.rst @@ -0,0 +1,94 @@ +cdist-type__sshd_config(7) +========================== + +NAME +---- +cdist-type__sshd_config - Manage options in sshd_config + + +DESCRIPTION +----------- +This space intentionally left blank. + + +REQUIRED PARAMETERS +------------------- +None. + + +OPTIONAL PARAMETERS +------------------- +file + The path to the sshd_config file to edit. + Defaults to ``/etc/ssh/sshd_config``. +match + Restrict this option to apply only for certain connections. + Allowed values are what would be allowed to be written after a ``Match`` + keyword in ``sshd_config``, e.g. ``--match 'User anoncvs'``. + + Can be used multiple times. All of the values are ANDed together. +option + The name of the option to manipulate. Defaults to ``__object_id``. +state + Can be: + + - ``present``: ensure a matching config line is present (or the default + value). + - ``absent``: ensure no matching config line is present. +value + The option's value to be assigned to the option (if ``--state present``) or + removed (if ``--state absent``). + + This option is required if ``--state present``. If not specified and + ``--state absent``, all values for the given option are removed. + + +BOOLEAN PARAMETERS +------------------ +None. + + +EXAMPLES +-------- + +.. code-block:: sh + + # Disallow root logins with password + __sshd_config PermitRootLogin --value without-password + + # Disallow password-based authentication + __sshd_config PasswordAuthentication --value no + + # Accept the EDITOR environment variable + __sshd_config AcceptEnv:EDITOR --option AcceptEnv --value EDITOR + + # Force command for connections as git user + __sshd_config git@ForceCommand --match 'User git' --option ForceCommand \ + --value 'cd ~git && exec git-shell ${SSH_ORIGINAL_COMMAND:+-c "${SSH_ORIGINAL_COMMAND}"}' + + +SEE ALSO +-------- +:strong:`sshd_config`\ (5) + + +BUGS +---- +- This type assumes a nicely formatted config file, + i.e. no config options spanning multiple lines. +- ``Include`` directives are ignored. +- Config options are not added/removed to/from the config file if their value is + the default value. + + +AUTHORS +------- +Dennis Camera + + +COPYING +------- +Copyright \(C) 2020 Dennis Camera. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__sshd_config/manifest b/cdist/conf/type/__sshd_config/manifest new file mode 100755 index 00000000..566bde90 --- /dev/null +++ b/cdist/conf/type/__sshd_config/manifest @@ -0,0 +1,48 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +os=$(cat "${__global:?}/explorer/os") + +state_should=$(cat "${__object:?}/parameter/state") + +case ${os} +in + (alpine|centos|fedora|redhat|scientific|debian|devuan|ubuntu) + if test "${state_should}" != 'absent' + then + __package openssh-server --state present + fi + ;; + (archlinux|gentoo|slackware|suse) + if test "${state_should}" != 'absent' + then + __package openssh --state present + fi + ;; + (freebsd|netbsd|openbsd) + # whitelist + ;; + (*) + printf 'Your operating system (%s) is currently not supported by this type (%s)\n' \ + "${os}" "${__type##*/}" >&2 + printf 'Please contribute an implementation for it if you can.\n' >&2 + exit 1 + ;; +esac diff --git a/cdist/conf/type/__sshd_config/parameter/default/file b/cdist/conf/type/__sshd_config/parameter/default/file new file mode 100644 index 00000000..d8ea5dfc --- /dev/null +++ b/cdist/conf/type/__sshd_config/parameter/default/file @@ -0,0 +1 @@ +/etc/ssh/sshd_config diff --git a/cdist/conf/type/__sshd_config/parameter/default/state b/cdist/conf/type/__sshd_config/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__sshd_config/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__sshd_config/parameter/optional b/cdist/conf/type/__sshd_config/parameter/optional new file mode 100644 index 00000000..922ab093 --- /dev/null +++ b/cdist/conf/type/__sshd_config/parameter/optional @@ -0,0 +1,4 @@ +file +option +state +value diff --git a/cdist/conf/type/__sshd_config/parameter/optional_multiple b/cdist/conf/type/__sshd_config/parameter/optional_multiple new file mode 100644 index 00000000..02b1d1a9 --- /dev/null +++ b/cdist/conf/type/__sshd_config/parameter/optional_multiple @@ -0,0 +1 @@ +match From 82eadb69948e631041263938994be5aeae161ab0 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Thu, 19 Nov 2020 19:34:43 +0100 Subject: [PATCH 42/44] ++changelog --- docs/changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog b/docs/changelog index ff411a46..331acbeb 100644 --- a/docs/changelog +++ b/docs/changelog @@ -6,6 +6,8 @@ next: * Type __locale: Add state explorer (Matthias Stecher) * Core: Reorganize scripts, version generation (Ander Punnar, Dennis Camera) * New type: __hwclock (Dennis Camera) + * Type __hostname: Fix guessing SuSE OS version (Dennis Camera) + * New type: __sshd_config (Dennis Camera) 6.9.1: 2020-11-08 * Type __file: Fix state pre-exists (Dennis Camera) From 803a9d62a7a6b7fd11844f6ea273f4b692c2334e Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Fri, 20 Nov 2020 19:46:03 +0100 Subject: [PATCH 43/44] ++changelog --- docs/changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog b/docs/changelog index 331acbeb..cd5dbb4f 100644 --- a/docs/changelog +++ b/docs/changelog @@ -8,6 +8,8 @@ next: * New type: __hwclock (Dennis Camera) * Type __hostname: Fix guessing SuSE OS version (Dennis Camera) * New type: __sshd_config (Dennis Camera) + * New type: __localedef (Dennis Camera) + * Type __locale: Deprecate in favor of __localedef (Dennis Camera) 6.9.1: 2020-11-08 * Type __file: Fix state pre-exists (Dennis Camera) From 23e0da521c785715357b27d66007bd03fe173bf3 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Fri, 20 Nov 2020 19:46:55 +0100 Subject: [PATCH 44/44] Release 6.9.2 --- docs/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog b/docs/changelog index cd5dbb4f..9a91bf57 100644 --- a/docs/changelog +++ b/docs/changelog @@ -1,7 +1,7 @@ Changelog --------- -next: +6.9.2: 2020-11-20 * Documentation: Fix examples in best practice (Dennis Camera) * Type __locale: Add state explorer (Matthias Stecher) * Core: Reorganize scripts, version generation (Ander Punnar, Dennis Camera)