diff --git a/changes/18534-support-rpm-upgrade b/changes/18534-support-rpm-upgrade new file mode 100644 index 0000000000..8294440820 --- /dev/null +++ b/changes/18534-support-rpm-upgrade @@ -0,0 +1 @@ +* Added support for upgrades to fleetd RPMs packages. diff --git a/orbit/pkg/packaging/linux_shared.go b/orbit/pkg/packaging/linux_shared.go index 8e0d67aff9..c7199b8163 100644 --- a/orbit/pkg/packaging/linux_shared.go +++ b/orbit/pkg/packaging/linux_shared.go @@ -15,6 +15,7 @@ import ( "github.com/fleetdm/fleet/v4/pkg/secure" "github.com/goreleaser/nfpm/v2" "github.com/goreleaser/nfpm/v2/files" + "github.com/goreleaser/nfpm/v2/rpm" "github.com/rs/zerolog/log" ) @@ -85,6 +86,8 @@ func buildNFPM(opt Options, pkger nfpm.Packager) (string, error) { // Write files + _, isRPM := pkger.(*rpm.RPM) + if err := writeSystemdUnit(opt, rootDir); err != nil { return "", fmt.Errorf("write systemd unit: %w", err) } @@ -110,9 +113,16 @@ func buildNFPM(opt Options, pkger nfpm.Packager) (string, error) { return "", fmt.Errorf("write preremove script: %w", err) } postRemovePath := filepath.Join(tmpDir, "postremove.sh") - if err := writePostRemove(opt, postRemovePath); err != nil { + if err := writePostRemove(postRemovePath); err != nil { return "", fmt.Errorf("write postremove script: %w", err) } + var postTransPath string + if isRPM { + postTransPath = filepath.Join(tmpDir, "posttrans.sh") + if err := writeRPMPostTrans(opt, postTransPath); err != nil { + return "", fmt.Errorf("write RPM posttrans script: %w", err) + } + } if opt.FleetCertificate != "" { if err := writeFleetServerCertificate(opt, orbitRoot); err != nil { @@ -194,6 +204,11 @@ func buildNFPM(opt Options, pkger nfpm.Packager) (string, error) { log.Debug().Interface("file", c).Msg("added file") } + rpmInfo := nfpm.RPM{} + if _, ok := pkger.(*rpm.RPM); ok { + rpmInfo.Scripts.PostTrans = postTransPath + } + // Build package info := &nfpm.Info{ Name: "fleet-osquery", @@ -211,6 +226,7 @@ func buildNFPM(opt Options, pkger nfpm.Packager) (string, error) { PreRemove: preRemovePath, PostRemove: postRemovePath, }, + RPM: rpmInfo, }, } filename := pkger.ConventionalFileName(info) @@ -367,7 +383,7 @@ pkill fleet-desktop || true return nil } -func writePostRemove(opt Options, path string) error { +func writePostRemove(path string) error { if err := os.WriteFile(path, []byte(`#!/bin/sh # For RPM during uninstall, $1 is 0 @@ -381,3 +397,39 @@ fi return nil } + +// postTransTemplate contains the template for RPM posttrans scriptlet (used when upgrading). +// See https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/. +// +// We cannot rely on "$1" because it's always "0" for RPM < 4.12 +// (see https://github.com/rpm-software-management/rpm/commit/ab069ec876639d46d12dd76dad54fd8fb762e43d) +// thus we check if orbit service is enabled, and if not we enable it (because posttrans +// will run both on "install" and "upgrade"). +var postTransTemplate = template.Must(template.New("posttrans").Parse(`#!/bin/sh + +# Exit on error +set -e + +if ! systemctl is-enabled orbit >/dev/null 2>&1; then + # If we have a systemd, daemon-reload away now + if command -v systemctl >/dev/null 2>&1; then + systemctl daemon-reload >/dev/null 2>&1 +{{ if .StartService -}} + systemctl restart orbit.service 2>&1 + systemctl enable orbit.service 2>&1 +{{- end}} + fi +fi +`)) + +// writeRPMPostTrans sets the posttrans scriptlets necessary to support RPM upgrades. +func writeRPMPostTrans(opt Options, path string) error { + var contents bytes.Buffer + if err := postTransTemplate.Execute(&contents, opt); err != nil { + return fmt.Errorf("execute template: %w", err) + } + if err := os.WriteFile(path, contents.Bytes(), constant.DefaultFileMode); err != nil { + return fmt.Errorf("write file: %w", err) + } + return nil +}