diff --git a/.config/make/tests.mak b/.config/make/tests.mak index 56d5d79df..ef288b904 100644 --- a/.config/make/tests.mak +++ b/.config/make/tests.mak @@ -29,6 +29,7 @@ script-tests: ## Run shell script tests ./scripts/test_entrypoint_credentials.sh ./scripts/test_internode_grpc_ab_bench.sh ./scripts/test_object_batch_bench_enhanced.sh + ./scripts/test_package_service_scripts.sh ./scripts/test_hotpath_warp_ab_gate.sh ./scripts/test_hotpath_warp_abba.sh ./scripts/test_scanner_validation_harness.sh diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index 485ebea28..72a50a904 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -24,7 +24,9 @@ on: - '.github/actions/**' - '.github/workflows/**' - 'scripts/release/create_or_update_release.sh' + - 'scripts/release/package_service_scripts.sh' - 'scripts/release/package_versions.sh' + - 'scripts/test_package_service_scripts.sh' - 'scripts/test_package_versions.sh' - 'scripts/security/check_performance_ab_workflow.sh' - 'scripts/security/check_preview_release_workflow.sh' @@ -40,7 +42,9 @@ on: - '.github/actions/**' - '.github/workflows/**' - 'scripts/release/create_or_update_release.sh' + - 'scripts/release/package_service_scripts.sh' - 'scripts/release/package_versions.sh' + - 'scripts/test_package_service_scripts.sh' - 'scripts/test_package_versions.sh' - 'scripts/security/check_performance_ab_workflow.sh' - 'scripts/security/check_preview_release_workflow.sh' @@ -158,6 +162,9 @@ jobs: - name: Check package version contract run: ./scripts/test_package_versions.sh + - name: Check package service lifecycle contract + run: ./scripts/test_package_service_scripts.sh + dependency-review: name: Dependency Review runs-on: ubuntu-latest diff --git a/.github/workflows/nightly-gnu.yml b/.github/workflows/nightly-gnu.yml index d283c31f0..bc7b73a33 100644 --- a/.github/workflows/nightly-gnu.yml +++ b/.github/workflows/nightly-gnu.yml @@ -117,10 +117,15 @@ jobs: cat > "${PKG_DIR}/etc/default/rustfs" << 'ENVEOF' # RustFS Environment Configuration - # See https://rustfs.com/docs/ for more information + # See https://docs.rustfs.com for more information + # RUSTFS_ACCESS_KEY= + # RUSTFS_SECRET_KEY= # RUSTFS_VOLUMES="" - # RUSTFS_ROOT_USER="" - # RUSTFS_ROOT_PASSWORD="" + # RUSTFS_ADDRESS=":9000" + # RUSTFS_CONSOLE_ADDRESS=":9001" + # RUSTFS_CONSOLE_ENABLE=true + # RUSTFS_OBS_LOGGER_LEVEL=error + # RUSTFS_OBS_LOG_DIRECTORY="/var/log/rustfs/" ENVEOF # dpkg versions must start with a digit and cannot contain hyphens; @@ -146,37 +151,13 @@ jobs: /etc/default/rustfs CONFFILES - cat > "${PKG_DIR}/DEBIAN/postinst" << 'POSTINST' - #!/bin/bash - set -e - if ! getent passwd rustfs > /dev/null 2>&1; then - useradd -r -s /bin/false -d /opt/rustfs rustfs - fi - mkdir -p /opt/rustfs /data/rustfs /var/log/rustfs - chown rustfs:rustfs /opt/rustfs /data/rustfs /var/log/rustfs - if [ -d /run/systemd/system ]; then - systemctl daemon-reload - fi - echo "RustFS installed. Configure /etc/default/rustfs then: systemctl start rustfs" - POSTINST + ./scripts/release/package_service_scripts.sh after-install > "${PKG_DIR}/DEBIAN/postinst" chmod 755 "${PKG_DIR}/DEBIAN/postinst" - cat > "${PKG_DIR}/DEBIAN/prerm" << 'PRERM' - #!/bin/bash - set -e - if [ -d /run/systemd/system ] && systemctl is-active --quiet rustfs; then - systemctl stop rustfs - fi - PRERM + ./scripts/release/package_service_scripts.sh before-remove > "${PKG_DIR}/DEBIAN/prerm" chmod 755 "${PKG_DIR}/DEBIAN/prerm" - cat > "${PKG_DIR}/DEBIAN/postrm" << 'POSTRM' - #!/bin/bash - set -e - if [ -d /run/systemd/system ]; then - systemctl daemon-reload - fi - POSTRM + ./scripts/release/package_service_scripts.sh after-remove > "${PKG_DIR}/DEBIAN/postrm" chmod 755 "${PKG_DIR}/DEBIAN/postrm" cp LICENSE "${PKG_DIR}/usr/share/doc/rustfs/" @@ -214,10 +195,15 @@ jobs: mkdir -p ./tmp-pkg/etc/default cat > ./tmp-pkg/etc/default/rustfs << 'ENVEOF' # RustFS Environment Configuration - # See https://rustfs.com/docs/ for more information + # See https://docs.rustfs.com for more information + # RUSTFS_ACCESS_KEY= + # RUSTFS_SECRET_KEY= # RUSTFS_VOLUMES="" - # RUSTFS_ROOT_USER="" - # RUSTFS_ROOT_PASSWORD="" + # RUSTFS_ADDRESS=":9000" + # RUSTFS_CONSOLE_ADDRESS=":9001" + # RUSTFS_CONSOLE_ENABLE=true + # RUSTFS_OBS_LOGGER_LEVEL=error + # RUSTFS_OBS_LOG_DIRECTORY="/var/log/rustfs/" ENVEOF fpm -s dir -t rpm \ @@ -231,35 +217,13 @@ jobs: --description "High-performance distributed object storage" \ --url "https://rustfs.com" \ --license "Apache-2.0" \ - --after-install <(cat << 'POSTINST' - #!/bin/bash - set -e - if ! getent passwd rustfs > /dev/null 2>&1; then - useradd -r -s /bin/false -d /opt/rustfs rustfs - fi - mkdir -p /opt/rustfs /data/rustfs /var/log/rustfs - chown rustfs:rustfs /opt/rustfs /data/rustfs /var/log/rustfs - if [ -d /run/systemd/system ]; then - systemctl daemon-reload - fi - POSTINST - ) \ - --before-remove <(cat << 'PRERM' - #!/bin/bash - set -e - if [ -d /run/systemd/system ] && systemctl is-active --quiet rustfs; then - systemctl stop rustfs - fi - PRERM - ) \ - --after-remove <(cat << 'POSTRM' - #!/bin/bash - set -e - if [ -d /run/systemd/system ]; then - systemctl daemon-reload - fi - POSTRM - ) \ + --before-install <(./scripts/release/package_service_scripts.sh rpm-before-install) \ + --before-upgrade <(./scripts/release/package_service_scripts.sh rpm-before-upgrade) \ + --after-install <(./scripts/release/package_service_scripts.sh after-install) \ + --after-upgrade <(./scripts/release/package_service_scripts.sh after-install) \ + --before-remove <(./scripts/release/package_service_scripts.sh before-remove) \ + --after-remove <(./scripts/release/package_service_scripts.sh after-remove) \ + --rpm-posttrans <(./scripts/release/package_service_scripts.sh rpm-posttrans) \ --config-files /etc/default/rustfs \ "rustfs-nightly-${DEB_DATE}/usr/bin/rustfs=/usr/bin/rustfs" \ ./tmp-pkg/etc/default/rustfs=/etc/default/rustfs \ diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 9c0b4eb9d..faa46ed64 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -328,10 +328,15 @@ jobs: cat > "${PKG_DIR}/etc/default/rustfs" << 'ENVEOF' # RustFS Environment Configuration - # See https://rustfs.com/docs/ for more information + # See https://docs.rustfs.com for more information + # RUSTFS_ACCESS_KEY= + # RUSTFS_SECRET_KEY= # RUSTFS_VOLUMES="" - # RUSTFS_ROOT_USER="" - # RUSTFS_ROOT_PASSWORD="" + # RUSTFS_ADDRESS=":9000" + # RUSTFS_CONSOLE_ADDRESS=":9001" + # RUSTFS_CONSOLE_ENABLE=true + # RUSTFS_OBS_LOGGER_LEVEL=error + # RUSTFS_OBS_LOG_DIRECTORY="/var/log/rustfs/" ENVEOF cat > "${PKG_DIR}/DEBIAN/control" << EOF @@ -354,37 +359,13 @@ jobs: /etc/default/rustfs CONFFILES - cat > "${PKG_DIR}/DEBIAN/postinst" << 'POSTINST' - #!/bin/bash - set -e - if ! getent passwd rustfs > /dev/null 2>&1; then - useradd -r -s /bin/false -d /opt/rustfs rustfs - fi - mkdir -p /opt/rustfs /data/rustfs /var/log/rustfs - chown rustfs:rustfs /opt/rustfs /data/rustfs /var/log/rustfs - if [ -d /run/systemd/system ]; then - systemctl daemon-reload - fi - echo "RustFS installed. Configure /etc/default/rustfs then: systemctl start rustfs" - POSTINST + ./scripts/release/package_service_scripts.sh after-install > "${PKG_DIR}/DEBIAN/postinst" chmod 755 "${PKG_DIR}/DEBIAN/postinst" - cat > "${PKG_DIR}/DEBIAN/prerm" << 'PRERM' - #!/bin/bash - set -e - if [ -d /run/systemd/system ] && systemctl is-active --quiet rustfs; then - systemctl stop rustfs - fi - PRERM + ./scripts/release/package_service_scripts.sh before-remove > "${PKG_DIR}/DEBIAN/prerm" chmod 755 "${PKG_DIR}/DEBIAN/prerm" - cat > "${PKG_DIR}/DEBIAN/postrm" << 'POSTRM' - #!/bin/bash - set -e - if [ -d /run/systemd/system ]; then - systemctl daemon-reload - fi - POSTRM + ./scripts/release/package_service_scripts.sh after-remove > "${PKG_DIR}/DEBIAN/postrm" chmod 755 "${PKG_DIR}/DEBIAN/postrm" cp LICENSE "${PKG_DIR}/usr/share/doc/rustfs/" @@ -422,10 +403,15 @@ jobs: mkdir -p ./tmp-pkg/etc/default cat > ./tmp-pkg/etc/default/rustfs << 'ENVEOF' # RustFS Environment Configuration - # See https://rustfs.com/docs/ for more information + # See https://docs.rustfs.com for more information + # RUSTFS_ACCESS_KEY= + # RUSTFS_SECRET_KEY= # RUSTFS_VOLUMES="" - # RUSTFS_ROOT_USER="" - # RUSTFS_ROOT_PASSWORD="" + # RUSTFS_ADDRESS=":9000" + # RUSTFS_CONSOLE_ADDRESS=":9001" + # RUSTFS_CONSOLE_ENABLE=true + # RUSTFS_OBS_LOGGER_LEVEL=error + # RUSTFS_OBS_LOG_DIRECTORY="/var/log/rustfs/" ENVEOF fpm -s dir -t rpm \ @@ -439,35 +425,13 @@ jobs: --description "High-performance distributed object storage" \ --url "https://rustfs.com" \ --license "Apache-2.0" \ - --after-install <(cat <<'POSTINST' - #!/bin/bash - set -e - if ! getent passwd rustfs > /dev/null 2>&1; then - useradd -r -s /bin/false -d /opt/rustfs rustfs - fi - mkdir -p /opt/rustfs /data/rustfs /var/log/rustfs - chown rustfs:rustfs /opt/rustfs /data/rustfs /var/log/rustfs - if [ -d /run/systemd/system ]; then - systemctl daemon-reload - fi - POSTINST - ) \ - --before-remove <(cat <<'PRERM' - #!/bin/bash - set -e - if [ -d /run/systemd/system ] && systemctl is-active --quiet rustfs; then - systemctl stop rustfs - fi - PRERM - ) \ - --after-remove <(cat <<'POSTRM' - #!/bin/bash - set -e - if [ -d /run/systemd/system ]; then - systemctl daemon-reload - fi - POSTRM - ) \ + --before-install <(./scripts/release/package_service_scripts.sh rpm-before-install) \ + --before-upgrade <(./scripts/release/package_service_scripts.sh rpm-before-upgrade) \ + --after-install <(./scripts/release/package_service_scripts.sh after-install) \ + --after-upgrade <(./scripts/release/package_service_scripts.sh after-install) \ + --before-remove <(./scripts/release/package_service_scripts.sh before-remove) \ + --after-remove <(./scripts/release/package_service_scripts.sh after-remove) \ + --rpm-posttrans <(./scripts/release/package_service_scripts.sh rpm-posttrans) \ --config-files /etc/default/rustfs \ ./bin/rustfs=/usr/bin/rustfs \ ./tmp-pkg/etc/default/rustfs=/etc/default/rustfs \ diff --git a/scripts/README.md b/scripts/README.md index a802401a9..922f9dfe7 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -90,6 +90,7 @@ their issue closes. | `test_build_rustfs_options.sh` | dev-tool | Shell test for rustfs build-option wiring | `make test` (script-tests) | | `test_entrypoint_credentials.sh` | dev-tool | Container entrypoint credential-handling test | `make test` (script-tests) | | `test_helm_chart_version.sh` | dev-tool | Test for `helm_chart_version.sh` | — | +| `test_package_service_scripts.sh` | ci-gate | Verifies DEB/RPM install, upgrade, removal, and service restart scriptlets | audit.yml `workflow-pin-report` | | `test_package_versions.sh` | ci-gate | Exact-output and fail-closed tests for DEB/RPM package version normalization | audit.yml `workflow-pin-report`; package.yml RPM build | | `windows-sftp-listener-smoke.sh` | dev-tool | Confirms `rustfs.exe --features sftp` binds an SFTP listener on Windows | — | diff --git a/scripts/release/package_service_scripts.sh b/scripts/release/package_service_scripts.sh new file mode 100755 index 000000000..7dcf2fb8f --- /dev/null +++ b/scripts/release/package_service_scripts.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash + +set -euo pipefail + +case ${1:-} in + after-install) + cat <<'SCRIPT' +#!/bin/sh +set -e +if ! getent passwd rustfs > /dev/null 2>&1; then + useradd -r -s /bin/false -d /opt/rustfs rustfs +fi +mkdir -p /opt/rustfs /data/rustfs /var/log/rustfs +chown rustfs:rustfs /opt/rustfs /data/rustfs /var/log/rustfs +if [ -d /run/systemd/system ]; then + systemctl daemon-reload + if [ "${1:-}" = configure ]; then + marker=/run/rustfs-package-upgrade-was-active + if [ -z "${2:-}" ]; then + rm -f "$marker" + else + # Versions published before this fix had no upgrade-aware prerm: + # the old prerm stopped the service and left no marker, so the + # marker check below cannot see it. Fall back to restarting for + # upgrades from those versions. NOTE: these are dpkg control-file + # versions - package_versions.sh maps the SemVer prerelease "-" + # to "~", so 1.0.0-rc.5 was published as 1.0.0~rc.5. Drop this list once + # upgrades from <= 1.0.0 no longer need support. + case $2 in + 1.0.0|1.0.0~rc.[1-6]|1.0.0~rc.[1-6]~preview.*) legacy_upgrade=true ;; + *) legacy_upgrade=false ;; + esac + if [ -f "$marker" ] || [ "$legacy_upgrade" = true ]; then + rm -f "$marker" + if command -v deb-systemd-invoke > /dev/null 2>&1; then + deb-systemd-invoke restart rustfs.service + else + systemctl restart rustfs.service + fi + fi + fi + fi +fi +SCRIPT + ;; + before-remove) + cat <<'SCRIPT' +#!/bin/sh +set -e +case ${1:-} in + upgrade) + marker=/run/rustfs-package-upgrade-was-active + rm -f "$marker" + if [ -d /run/systemd/system ] && systemctl is-active --quiet rustfs; then + touch "$marker" + fi + exit 0 + ;; + 0|remove|deconfigure) ;; + *) exit 0 ;; +esac +if [ -d /run/systemd/system ] && systemctl is-active --quiet rustfs; then + systemctl stop rustfs +fi +SCRIPT + ;; + after-remove) + cat <<'SCRIPT' +#!/bin/sh +set -e +if [ -d /run/systemd/system ]; then + systemctl daemon-reload +fi +if [ "${1:-}" != upgrade ]; then + rm -f /run/rustfs-package-upgrade-was-active +fi +SCRIPT + ;; + rpm-before-install) + cat <<'SCRIPT' +#!/bin/sh +set -e +rm -f /run/rustfs-package-upgrade-was-active +SCRIPT + ;; + rpm-before-upgrade) + cat <<'SCRIPT' +#!/bin/sh +set -e +marker=/run/rustfs-package-upgrade-was-active +rm -f "$marker" +if [ -d /run/systemd/system ] && systemctl is-active --quiet rustfs; then + touch "$marker" +fi +SCRIPT + ;; + rpm-posttrans) + cat <<'SCRIPT' +#!/bin/sh +set -e +marker=/run/rustfs-package-upgrade-was-active +if [ -f "$marker" ]; then + rm -f "$marker" + systemctl restart rustfs +fi +SCRIPT + ;; + *) + echo "usage: $0 {after-install|before-remove|after-remove|rpm-before-install|rpm-before-upgrade|rpm-posttrans}" >&2 + exit 2 + ;; +esac diff --git a/scripts/test_package_service_scripts.sh b/scripts/test_package_service_scripts.sh new file mode 100755 index 000000000..3ae599271 --- /dev/null +++ b/scripts/test_package_service_scripts.sh @@ -0,0 +1,116 @@ +#!/usr/bin/env bash + +set -euo pipefail + +repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +generator="${repo_root}/scripts/release/package_service_scripts.sh" +test_tmp=$(mktemp -d) +trap 'rm -rf "$test_tmp"' EXIT + +mkdir -p "${test_tmp}/bin" +mkdir -p "${test_tmp}/run/systemd/system" +cat > "${test_tmp}/bin/getent" <<'SCRIPT' +#!/bin/sh +exit 0 +SCRIPT +cat > "${test_tmp}/bin/mkdir" <<'SCRIPT' +#!/bin/sh +exit 0 +SCRIPT +cat > "${test_tmp}/bin/chown" <<'SCRIPT' +#!/bin/sh +exit 0 +SCRIPT +cat > "${test_tmp}/bin/systemctl" <<'SCRIPT' +#!/bin/sh +printf 'systemctl %s\n' "$*" >> "$PACKAGE_SCRIPT_LOG" +case "$1" in + is-active) exit "${SYSTEMCTL_ACTIVE_EXIT:-0}" ;; +esac +SCRIPT +cat > "${test_tmp}/bin/deb-systemd-invoke" <<'SCRIPT' +#!/bin/sh +printf 'deb-systemd-invoke %s\n' "$*" >> "$PACKAGE_SCRIPT_LOG" +SCRIPT +chmod +x "${test_tmp}/bin/"* + +export PATH="${test_tmp}/bin:${PATH}" +export PACKAGE_SCRIPT_LOG="${test_tmp}/calls" + +run_script() { + local name=$1 + shift + "$generator" "$name" | + sed \ + -e "s#/run/systemd/system#${test_tmp}/run/systemd/system#g" \ + -e "s#/run/rustfs-package-upgrade-was-active#${test_tmp}/run/rustfs-package-upgrade-was-active#g" \ + > "${test_tmp}/${name}" + chmod +x "${test_tmp}/${name}" + "${test_tmp}/${name}" "$@" +} + +assert_log() { + local expected=$1 + printf '%s\n' "$expected" > "${test_tmp}/expected" + diff -u "${test_tmp}/expected" "$PACKAGE_SCRIPT_LOG" +} + +: > "$PACKAGE_SCRIPT_LOG" +run_script before-remove upgrade 1.0.0 +assert_log 'systemctl is-active --quiet rustfs' +marker="${test_tmp}/run/rustfs-package-upgrade-was-active" +[[ -f $marker ]] + +: > "$PACKAGE_SCRIPT_LOG" +run_script after-remove upgrade 1.0.1 +[[ -f $marker ]] +run_script after-install configure 1.0.1 +[[ ! -e $marker ]] +assert_log $'systemctl daemon-reload\nsystemctl daemon-reload\ndeb-systemd-invoke restart rustfs.service' + +: > "$PACKAGE_SCRIPT_LOG" +touch "$marker" +SYSTEMCTL_ACTIVE_EXIT=1 run_script before-remove upgrade 1.0.1 +[[ ! -e $marker ]] +run_script after-remove upgrade 1.0.2 +run_script after-install configure 1.0.1 +assert_log $'systemctl is-active --quiet rustfs\nsystemctl daemon-reload\nsystemctl daemon-reload' + +: > "$PACKAGE_SCRIPT_LOG" +run_script after-install configure 1.0.0~rc.5 +assert_log $'systemctl daemon-reload\ndeb-systemd-invoke restart rustfs.service' + +: > "$PACKAGE_SCRIPT_LOG" +run_script after-install configure 0~dev.9000.0123456789abcdef +assert_log 'systemctl daemon-reload' + +: > "$PACKAGE_SCRIPT_LOG" +run_script before-remove remove +assert_log $'systemctl is-active --quiet rustfs\nsystemctl stop rustfs' + +: > "$PACKAGE_SCRIPT_LOG" +touch "$marker" +run_script after-remove remove +[[ ! -e $marker ]] +assert_log 'systemctl daemon-reload' + +: > "$PACKAGE_SCRIPT_LOG" +touch "$marker" +run_script after-install configure +[[ ! -e $marker ]] +assert_log 'systemctl daemon-reload' + +: > "$PACKAGE_SCRIPT_LOG" +run_script rpm-before-upgrade +[[ -f $marker ]] +run_script rpm-posttrans +[[ ! -e $marker ]] +assert_log $'systemctl is-active --quiet rustfs\nsystemctl restart rustfs' + +: > "$PACKAGE_SCRIPT_LOG" +SYSTEMCTL_ACTIVE_EXIT=1 run_script rpm-before-upgrade +[[ ! -e $marker ]] +run_script rpm-posttrans +assert_log 'systemctl is-active --quiet rustfs' + +echo "PASS package service script contract"