diff --git a/resources/com/cadoles/podman/nfpm.yaml.gotmpl b/resources/com/cadoles/podman/nfpm.yaml.gotmpl new file mode 100644 index 0000000..cbb1f2d --- /dev/null +++ b/resources/com/cadoles/podman/nfpm.yaml.gotmpl @@ -0,0 +1,25 @@ +{{ $serviceName := index ( .Env.IMAGE_NAME | strings.Split "/" | coll.Reverse ) 0 }} +name: "cadoles-pod-{{ $serviceName }}" +arch: amd64 +platform: linux +version: "{{ strings.TrimPrefix "v" ( getenv "IMAGE_TAG" "latest" ) }}" +version_schema: none +version_metadata: git +section: "{{ getenv "PACKAGE_SECTION" "default" }}" +priority: "{{ getenv "PACKAGE_PRIORITY" "optional" }}" +maintainer: "{{ getenv "PACKAGE_MAINTAINER" "contact@cadoles.com" }}" +description: "{{ getenv "PACKAGE_DESCRIPTION" "" }}" +homepage: "{{ getenv "PACKAGE_HOMEPAGE" "https://forge.cadoles.com" }}" +license: "{{ getenv "PACKAGE_LICENCE" "GPL-3.0" }}" +depends: + - podman +scripts: + postinstall: post-install.sh +contents: + - packager: deb + src: pod.service + dst: "/usr/lib/systemd/system/cadoles-pod-{{ $serviceName }}.service" + - packager: deb + src: pod.conf + dst: /etc/cadoles-pod-{{ $serviceName }}.conf + type: config|noreplace \ No newline at end of file diff --git a/resources/com/cadoles/podman/pod.conf.gotmpl b/resources/com/cadoles/podman/pod.conf.gotmpl new file mode 100644 index 0000000..584ec25 --- /dev/null +++ b/resources/com/cadoles/podman/pod.conf.gotmpl @@ -0,0 +1 @@ +PODMAN_ARGS="{{ getenv "PODMAN_ARGS" "" }}" \ No newline at end of file diff --git a/resources/com/cadoles/podman/pod.service.gotmpl b/resources/com/cadoles/podman/pod.service.gotmpl new file mode 100644 index 0000000..012294b --- /dev/null +++ b/resources/com/cadoles/podman/pod.service.gotmpl @@ -0,0 +1,24 @@ +[Unit] +Description={{ .Env.IMAGE_NAME }} pod service +Wants=network-online.target +After=network-online.target +RequiresMountsFor=/run/containers/storage + +[Service] +Type=simple +Environment=PODMAN_SYSTEMD_UNIT=%n +EnvironmentFile=-/etc/cadoles-pod-{{ .Env.IMAGE_NAME }}.conf +Environment=IMAGE_NAME={{ .Env.IMAGE_NAME }} IMAGE_TAG={{ .Env.IMAGE_TAG }} +PassEnvironment=PODMAN_ARGS IMAGE_NAME IMAGE_TAG +Restart=on-failure +TimeoutStopSec=70 +{{ if getenv "SYSTEMD_EXEC_STARTPRE" "" }} +ExecStartPre={{ .Env.SYSTEMD_EXEC_STARTPRE }} +{{ end }} +ExecStart=/bin/sh -c "podman run ${PODMAN_ARGS} '${IMAGE_NAME}:${IMAGE_TAG}'" +{{ if getenv "SYSTEMD_EXEC_STARTPOST" "" }} +ExecStartPost={{ .Env.SYSTEMD_EXEC_STARTPOST }} +{{ end }} + +[Install] +WantedBy=default.target \ No newline at end of file diff --git a/resources/com/cadoles/podman/post-install.sh.gotmpl b/resources/com/cadoles/podman/post-install.sh.gotmpl new file mode 100644 index 0000000..d96ec94 --- /dev/null +++ b/resources/com/cadoles/podman/post-install.sh.gotmpl @@ -0,0 +1,79 @@ +#!/bin/sh + +# Adapted from https://nfpm.goreleaser.com/tips/ + +use_systemctl="True" +systemd_version=0 +if ! command -V systemctl >/dev/null 2>&1; then + use_systemctl="False" +else + systemd_version=$( systemctl --version | head -1 | sed 's/systemd //g' | cut -d' ' -f1 ) +fi + +SERVICE_NAME="cadoles-pod-{{ .Env.IMAGE_NAME }}" + +cleanup() { + if [ "${use_systemctl}" = "False" ]; then + rm -f /usr/lib/systemd/system/$SERVICE_NAME.service + else + rm -f /etc/chkconfig/$SERVICE_NAME + rm -f /etc/init.d/$SERVICE_NAME + fi +} + +cleanInstall() { + if [ "${use_systemctl}" = "False" ]; then + if command -V chkconfig >/dev/null 2>&1; then + chkconfig --add $SERVICE_NAME + fi + + service $SERVICE_NAME restart ||: + else + if [ "${systemd_version}" -lt 231 ]; then + printf "\033[31m systemd version %s is less then 231, fixing the service file \033[0m\n" "${systemd_version}" + sed -i "s/=+/=/g" /usr/lib/systemd/system/$SERVICE_NAME.service + fi + systemctl daemon-reload ||: + systemctl unmask $SERVICE_NAME ||: + systemctl preset $SERVICE_NAME ||: + systemctl enable $SERVICE_NAME ||: + systemctl restart $SERVICE_NAME ||: + fi +} + +upgrade() { + if [ "${use_systemctl}" = "False" ]; then + service $SERVICE_NAME restart ||: + else + if [ "${systemd_version}" -lt 231 ]; then + printf "\033[31m systemd version %s is less then 231, fixing the service file \033[0m\n" "${systemd_version}" + sed -i "s/=+/=/g" /usr/lib/systemd/system/$SERVICE_NAME.service + fi + systemctl daemon-reload ||: + systemctl restart $SERVICE_NAME ||: + fi + + echo 'Cleaning up unused images...' + podman image prune -f --filter "reference={{ .Env.IMAGE_NAME }}" +} + +action="$1" +if [ "$1" = "configure" ] && [ -z "$2" ]; then + action="install" +elif [ "$1" = "configure" ] && [ -n "$2" ]; then + action="upgrade" +fi + +case "$action" in + "1" | "install") + cleanInstall + ;; + "2" | "upgrade") + upgrade + ;; + *) + cleanInstall + ;; +esac + +cleanup \ No newline at end of file diff --git a/vars/gomplate.groovy b/vars/gomplate.groovy new file mode 100644 index 0000000..dacb236 --- /dev/null +++ b/vars/gomplate.groovy @@ -0,0 +1,46 @@ +void call(String sourceTemplate, String destFile, Map env = [:], Map options = [:]) { + String gomplateBin = getOrInstallGomplate(options) + + sh """ + ${exportEnvMap(env)} + ${gomplateBin} -f '${sourceTemplate}' > '${destFile}' + """ +} + +String exportEnvMap(Map env) { + String exports = '' + + env.each { item -> + exports = """ + ${exports} + export ${item.key}="${item.value}" + """ + } + + return exports +} + +String getOrInstallGomplate(Map options = [:]) { + String installDir = options.get('installDir', '/usr/local/bin') + String version = options.get('version', '3.10.0') + Boolean forceDownload = options.get('forceDownload', false) + String downloadUrl = options.get('downloadUrl', "https://github.com/hairyhenderson/gomplate/releases/download/v${version}/gomplate_linux-amd64") + + String gomplateBin = '' + + lock("${env.NODE_NAME}:gomplate-install") { + gomplateBin = sh(returnStdout: true, script: 'which gomplate || exit 0').trim() + + if (gomplateBin == '' || forceDownload) { + sh(""" + mkdir -p '${installDir}' + curl -o '${installDir}/gomplate' -sSL '${downloadUrl}' + chmod +x '${installDir}/gomplate' + """) + + gomplateBin = "${installDir}/gomplate" + } + } + + return gomplateBin +} diff --git a/vars/nfpm.groovy b/vars/nfpm.groovy index 011abce..3745431 100644 --- a/vars/nfpm.groovy +++ b/vars/nfpm.groovy @@ -1,25 +1,37 @@ -// Package project with nfpm -// See https://nfpm.goreleaser.com/ -def package(Map options = [:]) { - String installDir = options.get("nfpmInstallDir", "/usr/local/bin") - String nfpmVersion = options.get("nfpmVersion", "2.15.1") - String nfpmForceDownload = options.get("nfpmForceDownload", false) - String nfpmDownloadUrl = options.get("nfpmDownloadUrl", "https://github.com/goreleaser/nfpm/releases/download/${nfpmVersion}/nfpm_${nfpmVersion}_Linux_x86_64.tar.gz") - String nfpmConfig = options.get("nfpmConfig", "nfpm.yaml") - String nfpmTarget = options.get("nfpmTarget", ".") - String nfpmPackager = options.get("nfpmPackager", "") +/** + * Générer des paquets Debian, RPM, Alpine (ipk) via nfpm + * Voir See https://nfpm.goreleaser.com/ + * + * Options: + * - installDir - Répertoire d'installation du binaire nfpm, par défaut /usr/local/bin + * - version - Version de nfpm à installer, par défaut 2.15.1 + * - forceDownload - Forcer l'installation de nfpm, par défaut false + * - config - Fichier de configuration nfpm à utiliser, par défaut nfpm.yaml + * - target - Répertoire cible pour nfpm, par défaut ./dist + * - packager - Limiter l'exécution de nfpm à un packager spécifique, par défaut "deb" (i.e. pas de limitation) + */ +void call(Map options = [:]) { + String installDir = options.get('installDir', '/usr/local/bin') + String version = options.get('version', '2.20.0') + Boolean forceDownload = options.get('forceDownload', false) + String downloadUrl = options.get('downloadUrl', "https://github.com/goreleaser/nfpm/releases/download/v${version}/nfpm_${version}_Linux_x86_64.tar.gz") + String config = options.get('config', 'nfpm.yaml') + String target = options.get('target', env.WORKSPACE + '/dist') + String packager = options.get('packager', 'deb') - String nfpmBin = sh(returnStdout: true, script: 'which nfpm').trim("") - if (nfpmBin == "" || nfpmForceDownload) { + String nfpmBin = sh(returnStdout: true, script: 'which nfpm || exit 0').trim() + if (nfpmBin == '' || forceDownload) { sh(""" mkdir -p '${installDir}' - curl -L '${nfpmDownloadUrl}' > /tmp/nfpm.tar.gz + curl -L '${downloadUrl}' > /tmp/nfpm.tar.gz tar -C /usr/local/bin -xzf /tmp/nfpm.tar.gz """) + + nfpmBin = "${installDir}/nfpm" } sh(""" - export PATH='${installDir}:${env.PATH}' - nfpm --config '${nfpmConfig}' ${nfpmPackager ? "--packager "+nfpmPackager : ""} --target '${nfpmTarget}' + mkdir -p '${target}' + ${nfpmBin} package --config '${config}' ${packager ? '--packager ' + packager : ''} --target '${target}' """) } diff --git a/vars/podman.groovy b/vars/podman.groovy new file mode 100644 index 0000000..87c3241 --- /dev/null +++ b/vars/podman.groovy @@ -0,0 +1,44 @@ +void buildCadolesPodPackage(String imageName, String imageTag, Map options = [:]) { + String destDir = options.get('destDir', env.WORKSPACE + '/dist') + Map nfpmOptions = options.get('nfpmOptions', [:]) + + nfpmOptions['target'] = destDir + + Map env = options.get('env', [:]) + + env['IMAGE_NAME'] = imageName + env['IMAGE_TAG'] = imageTag + + withPodmanPackagingTempDir { + gomplate('post-install.sh.gotmpl', 'post-install.sh', env) + gomplate('pod.service.gotmpl', 'pod.service', env) + gomplate('pod.conf.gotmpl', 'pod.conf', env) + gomplate('nfpm.yaml.gotmpl', 'nfpm.yaml', env) + + nfpm(nfpmOptions) + } +} + +void withPodmanPackagingTempDir(Closure fn) { + File tempDir = File.createTempDir() + tempDir.deleteOnExit() + + tempDir.mkdirs() + + dir(tempDir.getAbsolutePath()) { + List resources = [ + 'com/cadoles/podman/nfpm.yaml.gotmpl', + 'com/cadoles/podman/pod.conf.gotmpl', + 'com/cadoles/podman/pod.service.gotmpl', + 'com/cadoles/podman/post-install.sh.gotmpl', + ] + + for (res in resources) { + String fileContent = libraryResource res + String fileName = res.substring(res.lastIndexOf('/') + 1) + writeFile file:fileName, text:fileContent + } + + fn() + } +}