From 7772a729ab26048fa587c10c192a0f9429aaf82a Mon Sep 17 00:00:00 2001 From: Tulip Blossom Date: Wed, 11 Dec 2024 18:24:54 -0300 Subject: [PATCH] refactor: separate rpm-ostree and bootc drivers w/ new format --- cmd/update.go | 75 ++++++++++++++++----- drv/rpm-ostree.go | 121 ++++++++++++++++++++++++++++++++++ drv/system.go | 164 +++++++++------------------------------------- 3 files changed, 209 insertions(+), 151 deletions(-) create mode 100644 drv/rpm-ostree.go diff --git a/cmd/update.go b/cmd/update.go index 9ee060f..74a5818 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -62,20 +62,6 @@ func Update(cmd *cobra.Command, args []string) { initConfiguration.DryRun = dryRun initConfiguration.Verbose = verboseRun - systemUpdater, err := drv.SystemUpdater{}.New(*initConfiguration) - if err != nil { - systemUpdater.Config.Enabled = false - } else { - enableUpd, err := systemUpdater.Check() - if err != nil { - slog.Error("Failed checking for updates") - } - systemUpdater.Config.Enabled = enableUpd - if !enableUpd { - slog.Debug("No system update found, disabiling module") - } - } - brewUpdater, err := drv.BrewUpdater{}.New(*initConfiguration) brewUpdater.Config.Enabled = err == nil @@ -87,7 +73,45 @@ func Update(cmd *cobra.Command, args []string) { distroboxUpdater.Config.Enabled = err == nil distroboxUpdater.SetUsers(users) - totalSteps := brewUpdater.Steps() + systemUpdater.Steps() + flatpakUpdater.Steps() + distroboxUpdater.Steps() + var enableUpd bool = true + var systemOutdated bool = false + + rpmOstreeUpdater, err := drv.RpmOstreeUpdater{}.New(*initConfiguration) + if err != nil { + enableUpd = false + } + + systemUpdater, err := drv.SystemUpdater{}.New(*initConfiguration) + if err != nil { + enableUpd = false + } + + // FIXME: Bootc can also only be used whenever there are no layered packages, we should account for that! + isBootc, err := drv.BootcCompatible(systemUpdater.BinaryPath) + if err != nil { + isBootc = false + } + + if !isBootc { + slog.Debug("Using rpm-ostree fallback as system driver") + } + + systemUpdater.Config.Enabled = enableUpd && isBootc + rpmOstreeUpdater.Config.Enabled = enableUpd && !isBootc + + if systemUpdater.Config.Enabled { + enableUpd, err = systemUpdater.Check() + } else { + enableUpd, err = rpmOstreeUpdater.Check() + } + if err != nil { + slog.Error("Failed checking for updates") + } + if !enableUpd { + slog.Debug("No system update found, disabiling module") + } + + totalSteps := brewUpdater.Steps() + systemUpdater.Steps() + rpmOstreeUpdater.Steps() + flatpakUpdater.Steps() + distroboxUpdater.Steps() pw := lib.NewProgressWriter() pw.SetNumTrackersExpected(1) pw.SetAutoStop(false) @@ -119,7 +143,17 @@ func Update(cmd *cobra.Command, args []string) { var outputs = []drv.CommandOutput{} - if systemUpdater.Outdated { + if systemUpdater.Config.Enabled { + systemOutdated, err = systemUpdater.Outdated() + } else { + systemOutdated, err = rpmOstreeUpdater.Outdated() + } + + if err != nil { + slog.Error("Failed checking if system is out of date") + } + + if systemOutdated { const OUTDATED_WARNING = "There hasn't been an update in over a month. Consider rebooting or running updates manually" err := lib.Notify("System Warning", OUTDATED_WARNING) if err != nil { @@ -128,9 +162,14 @@ func Update(cmd *cobra.Command, args []string) { slog.Warn(OUTDATED_WARNING) } - if systemUpdater.Config.Enabled { + if enableUpd { lib.ChangeTrackerMessageFancy(pw, tracker, progressEnabled, lib.TrackerMessage{Title: systemUpdater.Config.Title, Description: systemUpdater.Config.Description}) - out, err := systemUpdater.Update() + var out *[]drv.CommandOutput + if rpmOstreeUpdater.Config.Enabled { + out, err = rpmOstreeUpdater.Update() + } else { + out, err = systemUpdater.Update() + } outputs = append(outputs, *out...) tracker.IncrementSection(err) } diff --git a/drv/rpm-ostree.go b/drv/rpm-ostree.go new file mode 100644 index 0000000..468850e --- /dev/null +++ b/drv/rpm-ostree.go @@ -0,0 +1,121 @@ +package drv + +// Temporary: WILL get removed at some point. +// FIXME: Remove this on Spring 2025 when we all move to dnf5 and bootc ideally + +import ( + "encoding/json" + "os/exec" + "strings" + "time" +) + +type rpmOstreeStatus struct { + Deployments []struct { + Timestamp int64 `json:"timestamp"` + } `json:"deployments"` +} + +type RpmOstreeUpdater struct { + Config DriverConfiguration + BinaryPath string +} + +func (dr RpmOstreeUpdater) Outdated() (bool, error) { + if dr.Config.DryRun { + return false, nil + } + oneMonthAgo := time.Now().AddDate(0, -1, 0) + var timestamp time.Time + + cmd := exec.Command(dr.BinaryPath, "status", "--json", "--booted") + out, err := cmd.CombinedOutput() + if err != nil { + return false, err + } + var status rpmOstreeStatus + err = json.Unmarshal(out, &status) + if err != nil { + return false, err + } + timestamp = time.Unix(status.Deployments[0].Timestamp, 0).UTC() + + return timestamp.Before(oneMonthAgo), nil +} + +func (dr RpmOstreeUpdater) Update() (*[]CommandOutput, error) { + var finalOutput = []CommandOutput{} + var cmd *exec.Cmd = nil + binaryPath := dr.BinaryPath + cli := []string{binaryPath, "upgrade"} + cmd = exec.Command(cli[0], cli[1:]...) + out, err := cmd.CombinedOutput() + tmpout := CommandOutput{}.New(out, err) + tmpout.Cli = cli + tmpout.Failure = err != nil + tmpout.Context = "System Update" + finalOutput = append(finalOutput, *tmpout) + return &finalOutput, err +} + +func (dr RpmOstreeUpdater) UpdateAvailable() (bool, error) { + // This function may or may not be accurate, rpm-ostree updgrade --check has issues... https://github.com/coreos/rpm-ostree/issues/1579 + // Not worried because we will end up removing rpm-ostree from the equation soon + cmd := exec.Command(dr.BinaryPath, "upgrade", "--check") + out, err := cmd.CombinedOutput() + if err != nil { + return true, err + } + return strings.Contains(string(out), "AvailableUpdate"), nil +} + +func (up RpmOstreeUpdater) Steps() int { + if up.Config.Enabled { + return 1 + } + return 0 +} + +func BootcCompatible(binaryPath string) (bool, error) { + cmd := exec.Command(binaryPath, "status", "--format=json") + out, err := cmd.CombinedOutput() + if err != nil { + return false, nil + } + var status bootcStatus + err = json.Unmarshal(out, &status) + if err != nil { + return false, nil + } + return !(status.Status.Booted.Incompatible || status.Status.Staged.Incompatible), nil +} + +func (up RpmOstreeUpdater) New(config UpdaterInitConfiguration) (RpmOstreeUpdater, error) { + up.Config = DriverConfiguration{ + Title: "System", + Description: "System Updates", + Enabled: !config.Ci, + DryRun: config.DryRun, + } + + if up.Config.DryRun { + return up, nil + } + + binaryPath, exists := config.Environment["UUPD_RPMOSTREE_BINARY"] + if !exists || binaryPath == "" { + up.BinaryPath = "/usr/bin/rpm-ostree" + } else { + up.BinaryPath = binaryPath + } + + return up, nil +} + +func (up RpmOstreeUpdater) Check() (bool, error) { + if up.Config.DryRun { + return true, nil + } + + return up.UpdateAvailable() +} diff --git a/drv/system.go b/drv/system.go index 0188194..6970858 100644 --- a/drv/system.go +++ b/drv/system.go @@ -24,79 +24,39 @@ type bootcStatus struct { } `json:"status"` } -// TODO: Temporary: WILL get removed at some point. -type rpmOstreeStatus struct { - Deployments []struct { - Timestamp int64 `json:"timestamp"` - } `json:"deployments"` -} - type SystemUpdater struct { - Config DriverConfiguration - SystemDriver SystemDriver - Outdated bool - UpdateAvailable bool -} - -type SystemVariation int - -const ( - Bootc SystemVariation = iota - RpmOstree -) - -type SystemDriver struct { - Variation SystemVariation - bootcBinaryPath string - rpmOstreeBinaryPath string + Config DriverConfiguration + BinaryPath string } -func (dr SystemDriver) Outdated() (bool, error) { +func (dr SystemUpdater) Outdated() (bool, error) { + if dr.Config.DryRun { + return false, nil + } oneMonthAgo := time.Now().AddDate(0, -1, 0) var timestamp time.Time - switch dr.Variation { - case Bootc: - cmd := exec.Command(dr.bootcBinaryPath, "status", "--format=json") - out, err := cmd.CombinedOutput() - if err != nil { - return false, err - } - var status bootcStatus - err = json.Unmarshal(out, &status) - if err != nil { - return false, err - } - timestamp, err = time.Parse(time.RFC3339Nano, status.Status.Booted.Image.Timestamp) - if err != nil { - return false, nil - } - case RpmOstree: - cmd := exec.Command(dr.rpmOstreeBinaryPath, "status", "--json", "--booted") - out, err := cmd.CombinedOutput() - if err != nil { - return false, err - } - var status rpmOstreeStatus - err = json.Unmarshal(out, &status) - if err != nil { - return false, err - } - timestamp = time.Unix(status.Deployments[0].Timestamp, 0).UTC() + cmd := exec.Command(dr.BinaryPath, "status", "--format=json") + out, err := cmd.CombinedOutput() + if err != nil { + return false, err + } + var status bootcStatus + err = json.Unmarshal(out, &status) + if err != nil { + return false, err + } + timestamp, err = time.Parse(time.RFC3339Nano, status.Status.Booted.Image.Timestamp) + if err != nil { + return false, nil } - return timestamp.Before(oneMonthAgo), nil } -func (dr SystemDriver) Update() (*[]CommandOutput, error) { +func (dr SystemUpdater) Update() (*[]CommandOutput, error) { var finalOutput = []CommandOutput{} var cmd *exec.Cmd = nil var binaryPath string - switch dr.Variation { - case Bootc: - binaryPath = dr.bootcBinaryPath - case RpmOstree: - binaryPath = dr.rpmOstreeBinaryPath - } + binaryPath = dr.BinaryPath cli := []string{binaryPath, "upgrade"} cmd = exec.Command(cli[0], cli[1:]...) out, err := cmd.CombinedOutput() @@ -108,27 +68,13 @@ func (dr SystemDriver) Update() (*[]CommandOutput, error) { return &finalOutput, err } -func (dr SystemDriver) UpdateAvailable() (bool, error) { - switch dr.Variation { - case Bootc: - cmd := exec.Command(dr.bootcBinaryPath, "upgrade", "--check") - out, err := cmd.CombinedOutput() - if err != nil { - return true, err - } - return !strings.Contains(string(out), "No changes in:"), nil - case RpmOstree: - // This function may or may not be accurate, rpm-ostree updgrade --check has issues... https://github.com/coreos/rpm-ostree/issues/1579 - // Not worried because we will end up removing rpm-ostree from the equation soon - cmd := exec.Command(dr.rpmOstreeBinaryPath, "upgrade", "--check") - out, err := cmd.CombinedOutput() - if err != nil { - return true, err - } - return strings.Contains(string(out), "AvailableUpdate"), nil +func (dr SystemUpdater) UpdateAvailable() (bool, error) { + cmd := exec.Command(dr.BinaryPath, "upgrade", "--check") + out, err := cmd.CombinedOutput() + if err != nil { + return true, err } - - return false, nil + return !strings.Contains(string(out), "No changes in:"), nil } func (up SystemUpdater) Steps() int { @@ -138,20 +84,6 @@ func (up SystemUpdater) Steps() int { return 0 } -func (dr SystemDriver) BootcCompatible() (bool, error) { - cmd := exec.Command(dr.bootcBinaryPath, "status", "--format=json") - out, err := cmd.CombinedOutput() - if err != nil { - return false, nil - } - var status bootcStatus - err = json.Unmarshal(out, &status) - if err != nil { - return false, nil - } - return !(status.Status.Booted.Incompatible || status.Status.Staged.Incompatible), nil -} - func (up SystemUpdater) New(config UpdaterInitConfiguration) (SystemUpdater, error) { up.Config = DriverConfiguration{ Title: "System", @@ -161,57 +93,23 @@ func (up SystemUpdater) New(config UpdaterInitConfiguration) (SystemUpdater, err } if up.Config.DryRun { - up.Outdated = false return up, nil } - up.SystemDriver = SystemDriver{} bootcBinaryPath, exists := config.Environment["UUPD_BOOTC_BINARY"] if !exists || bootcBinaryPath == "" { - up.SystemDriver.bootcBinaryPath = "/usr/bin/bootc" - } else { - up.SystemDriver.bootcBinaryPath = bootcBinaryPath - } - rpmOstreeBinaryPath, exists := config.Environment["UUPD_RPMOSTREE_BINARY"] - if !exists || rpmOstreeBinaryPath == "" { - up.SystemDriver.rpmOstreeBinaryPath = "/usr/bin/rpm-ostree" + up.BinaryPath = "/usr/bin/bootc" } else { - up.SystemDriver.rpmOstreeBinaryPath = rpmOstreeBinaryPath - } - - isBootc, err := up.SystemDriver.BootcCompatible() - if err != nil { - return up, err - } - if isBootc { - up.SystemDriver.Variation = Bootc - } else { - up.SystemDriver.Variation = RpmOstree - } - - outdated, err := up.SystemDriver.Outdated() - if err != nil { - return up, err + up.BinaryPath = bootcBinaryPath } - up.Outdated = outdated return up, nil } -func (up *SystemUpdater) Check() (bool, error) { +func (up SystemUpdater) Check() (bool, error) { if up.Config.DryRun { return true, nil } - updateAvailable, err := up.SystemDriver.UpdateAvailable() - return updateAvailable, err -} - -func (up SystemUpdater) Update() (*[]CommandOutput, error) { - if up.Config.DryRun { - return &[]CommandOutput{}, nil - } - - out, err := up.SystemDriver.Update() - return out, err + return up.UpdateAvailable() }