From d1b5f6c9bc011b9704729740efa657e93823d282 Mon Sep 17 00:00:00 2001 From: Stephen Levine Date: Tue, 3 Dec 2024 21:19:22 -0500 Subject: [PATCH] [teleport-update] Support for Enterprise/FIPS migration (#49451) * store ent/fips data cleanup formatting revert updater rename cleanup Update lib/autoupdate/agent/config.go Co-authored-by: Zac Bergquist feedback * feedback * feedback * lint --- constants.go | 2 +- lib/autoupdate/agent/config.go | 150 +++++++- lib/autoupdate/agent/config_test.go | 195 +++++++++++ lib/autoupdate/agent/installer.go | 66 ++-- lib/autoupdate/agent/installer_test.go | 19 +- lib/autoupdate/agent/process.go | 8 +- lib/autoupdate/agent/setup.go | 4 +- .../already_disabled.golden | 4 +- .../TestUpdater_Disable/enabled.golden | 4 +- .../FIPS_and_Enterprise_flags.golden | 5 +- .../backup_version_kept_for_validation.golden | 6 +- .../backup_version_removed_on_install.golden | 6 +- .../config_does_not_exist.golden | 4 +- .../config_from_file.golden | 6 +- .../config_from_user.golden | 6 +- .../TestUpdater_Install/defaults.golden | 6 +- .../TestUpdater_Install/insecure_URL.golden | 4 +- .../TestUpdater_Install/install_error.golden | 4 +- .../invalid_metadata.golden | 4 +- .../no_need_to_reload.golden | 4 +- .../TestUpdater_Install/no_systemd.golden | 4 +- .../TestUpdater_Install/override_skip.golden | 6 +- .../version_already_installed.golden | 4 +- .../TestUpdater_Unpin/not_pinned.golden | 4 +- .../testdata/TestUpdater_Unpin/pinned.golden | 4 +- .../FIPS_and_Enterprise_flags.golden | 8 +- .../backup_version_kept_when_no_change.golden | 6 +- .../backup_version_removed_on_install.golden | 6 +- .../TestUpdater_Update/insecure_URL.golden | 4 +- .../TestUpdater_Update/install_error.golden | 4 +- .../invalid_metadata.golden | 4 +- .../TestUpdater_Update/pinned_version.golden | 6 +- .../TestUpdater_Update/reload_fails.golden | 9 +- .../TestUpdater_Update/setup_fails.golden | 9 +- .../TestUpdater_Update/skip_version.golden | 9 +- .../updates_disabled_during_window.golden | 4 +- .../updates_disabled_outside_of_window.golden | 4 +- .../updates_enabled_during_window.golden | 6 +- .../updates_enabled_outside_of_window.golden | 4 +- ...version_already_installed_in_window.golden | 4 +- ...already_installed_outside_of_window.golden | 4 +- lib/autoupdate/agent/updater.go | 209 ++++++----- lib/autoupdate/agent/updater_test.go | 331 +++++++++--------- 43 files changed, 766 insertions(+), 394 deletions(-) create mode 100644 lib/autoupdate/agent/config_test.go diff --git a/constants.go b/constants.go index 803a58d44fbbd..0f07d97b89bd5 100644 --- a/constants.go +++ b/constants.go @@ -283,7 +283,7 @@ const ( // ComponentProxySecureGRPC represents a secure gRPC server running on Proxy (used for Kube). ComponentProxySecureGRPC = "proxy:secure-grpc" - // ComponentUpdater represents the agent updater. + // ComponentUpdater represents the teleport-update binary. ComponentUpdater = "updater" // VerboseLogsEnvVar forces all logs to be verbose (down to DEBUG level) diff --git a/lib/autoupdate/agent/config.go b/lib/autoupdate/agent/config.go index 247392f881c57..2fcc3b5404d0f 100644 --- a/lib/autoupdate/agent/config.go +++ b/lib/autoupdate/agent/config.go @@ -19,7 +19,9 @@ package agent import ( + "encoding/json" "errors" + "fmt" "io/fs" "os" "strings" @@ -67,12 +69,87 @@ type UpdateSpec struct { // UpdateStatus describes the status field in update.yaml. type UpdateStatus struct { - // ActiveVersion is the currently active Teleport version. - ActiveVersion string `yaml:"active_version"` - // BackupVersion is the last working version of Teleport. - BackupVersion string `yaml:"backup_version"` - // SkipVersion is the last reverted version of Teleport. - SkipVersion string `yaml:"skip_version,omitempty"` + // Active is the currently active revision of Teleport. + Active Revision `yaml:"active"` + // Backup is the last working revision of Teleport. + Backup *Revision `yaml:"backup,omitempty"` + // Skip is the skipped revision of Teleport. + // Skipped revisions are not applied because they + // are known to crash. + Skip *Revision `yaml:"skip,omitempty"` +} + +// Revision is a version and edition of Teleport. +type Revision struct { + // Version is the version of Teleport. + Version string `yaml:"version" json:"version"` + // Flags describe the edition of Teleport. + Flags InstallFlags `yaml:"flags,flow,omitempty" json:"flags,omitempty"` +} + +// NewRevision create a Revision. +// If version is not set, no flags are returned. +// This ensures that all Revisions without versions are zero-valued. +func NewRevision(version string, flags InstallFlags) Revision { + if version != "" { + return Revision{ + Version: version, + Flags: flags, + } + } + return Revision{} +} + +// NewRevisionFromDir translates a directory path containing Teleport into a Revision. +func NewRevisionFromDir(dir string) (Revision, error) { + parts := strings.Split(dir, "_") + var out Revision + if len(parts) == 0 { + return out, trace.Errorf("dir name empty") + } + out.Version = parts[0] + if out.Version == "" { + return out, trace.Errorf("version missing in dir %s", dir) + } + switch flags := parts[1:]; len(flags) { + case 2: + if flags[1] != FlagFIPS.DirFlag() { + break + } + out.Flags |= FlagFIPS + fallthrough + case 1: + if flags[0] != FlagEnterprise.DirFlag() { + break + } + out.Flags |= FlagEnterprise + fallthrough + case 0: + return out, nil + } + return out, trace.Errorf("invalid flag in %s", dir) +} + +// Dir returns the directory path name of a Revision. +func (r Revision) Dir() string { + // Do not change the order of these statements. + // Otherwise, installed versions will no longer match update.yaml. + var suffix string + if r.Flags&(FlagEnterprise|FlagFIPS) != 0 { + suffix += "_" + FlagEnterprise.DirFlag() + } + if r.Flags&FlagFIPS != 0 { + suffix += "_" + FlagFIPS.DirFlag() + } + return r.Version + suffix +} + +// String returns a human-readable description of a Teleport revision. +func (r Revision) String() string { + if flags := r.Flags.Strings(); len(flags) > 0 { + return fmt.Sprintf("%s+%s", r.Version, strings.Join(flags, "+")) + } + return r.Version } // readConfig reads UpdateConfig from a file. @@ -93,10 +170,10 @@ func readConfig(path string) (*UpdateConfig, error) { return nil, trace.Wrap(err, "failed to parse") } if k := cfg.Kind; k != updateConfigKind { - return nil, trace.Errorf("invalid kind %q", k) + return nil, trace.Errorf("invalid kind %s", k) } if v := cfg.Version; v != updateConfigVersion { - return nil, trace.Errorf("invalid version %q", v) + return nil, trace.Errorf("invalid version %s", v) } return &cfg, nil } @@ -155,10 +232,8 @@ type Status struct { // FindResp summarizes the auto-update status response from cluster. type FindResp struct { - // Version of Teleport to install - TargetVersion string `yaml:"target_version"` - // Flags describing the edition of Teleport - Flags InstallFlags `yaml:"flags"` + // Target revision of Teleport to install + Target Revision `yaml:"target"` // InWindow is true when the install should happen now. InWindow bool `yaml:"in_window"` // Jitter duration before an automated install @@ -175,10 +250,23 @@ const ( FlagFIPS ) -func (i InstallFlags) MarshalYAML() (any, error) { - return i.Strings(), nil +// NewInstallFlagsFromStrings returns InstallFlags given a slice of human-readable strings. +func NewInstallFlagsFromStrings(s []string) InstallFlags { + var out InstallFlags + for _, f := range s { + for _, flag := range []InstallFlags{ + FlagEnterprise, + FlagFIPS, + } { + if f == flag.String() { + out |= flag + } + } + } + return out } +// Strings converts InstallFlags to a slice of human-readable strings. func (i InstallFlags) Strings() []string { var out []string for _, flag := range []InstallFlags{ @@ -192,6 +280,7 @@ func (i InstallFlags) Strings() []string { return out } +// String returns the string representation of a single InstallFlag flag, or "Unknown". func (i InstallFlags) String() string { switch i { case 0: @@ -203,3 +292,36 @@ func (i InstallFlags) String() string { } return "Unknown" } + +// DirFlag returns the directory path representation of a single InstallFlag flag, or "unknown". +func (i InstallFlags) DirFlag() string { + switch i { + case 0: + return "" + case FlagEnterprise: + return "ent" + case FlagFIPS: + return "fips" + } + return "unknown" +} + +func (i InstallFlags) MarshalYAML() (any, error) { + return i.Strings(), nil +} + +func (i InstallFlags) MarshalJSON() ([]byte, error) { + return json.Marshal(i.Strings()) +} + +func (i *InstallFlags) UnmarshalYAML(n *yaml.Node) error { + var s []string + if err := n.Decode(&s); err != nil { + return trace.Wrap(err) + } + if i == nil { + return trace.BadParameter("nil install flags while parsing YAML") + } + *i = NewInstallFlagsFromStrings(s) + return nil +} diff --git a/lib/autoupdate/agent/config_test.go b/lib/autoupdate/agent/config_test.go new file mode 100644 index 0000000000000..0f3ce770ec7af --- /dev/null +++ b/lib/autoupdate/agent/config_test.go @@ -0,0 +1,195 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package agent + +import ( + "testing" + + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" +) + +func TestNewRevisionFromDir(t *testing.T) { + t.Parallel() + + for _, tt := range []struct { + name string + dir string + rev Revision + errMatch string + }{ + { + name: "version", + dir: "1.2.3", + rev: Revision{ + Version: "1.2.3", + }, + }, + { + name: "full", + dir: "1.2.3_ent_fips", + rev: Revision{ + Version: "1.2.3", + Flags: FlagEnterprise | FlagFIPS, + }, + }, + { + name: "ent", + dir: "1.2.3_ent", + rev: Revision{ + Version: "1.2.3", + Flags: FlagEnterprise, + }, + }, + { + name: "empty", + errMatch: "missing", + }, + { + name: "trailing", + dir: "1.2.3_", + errMatch: "invalid", + }, + { + name: "more trailing", + dir: "1.2.3___", + errMatch: "invalid", + }, + { + name: "no version", + dir: "_fips", + errMatch: "missing", + }, + { + name: "fips no ent", + dir: "1.2.3_fips", + errMatch: "invalid", + }, + { + name: "unknown start fips", + dir: "1.2.3_test_fips", + errMatch: "invalid", + }, + { + name: "unknown start ent", + dir: "1.2.3_test_ent", + errMatch: "invalid", + }, + { + name: "unknown end fips", + dir: "1.2.3_fips_test", + errMatch: "invalid", + }, + { + name: "unknown end ent", + dir: "1.2.3_ent_test", + errMatch: "invalid", + }, + { + name: "bad order", + dir: "1.2.3_fips_ent", + errMatch: "invalid", + }, + { + name: "underscore", + dir: "_", + errMatch: "missing", + }, + } { + t.Run(tt.name, func(t *testing.T) { + rev, err := NewRevisionFromDir(tt.dir) + if tt.errMatch != "" { + require.ErrorContains(t, err, tt.errMatch) + return + } + require.NoError(t, err) + require.Equal(t, tt.rev, rev) + require.Equal(t, tt.dir, rev.Dir()) + }) + } +} + +func TestInstallFlagsYAML(t *testing.T) { + t.Parallel() + + for _, tt := range []struct { + name string + yaml string + flags InstallFlags + skipYAML bool + }{ + { + name: "both", + yaml: `["Enterprise", "FIPS"]`, + flags: FlagEnterprise | FlagFIPS, + }, + { + name: "order", + yaml: `["FIPS", "Enterprise"]`, + flags: FlagEnterprise | FlagFIPS, + skipYAML: true, + }, + { + name: "extra", + yaml: `["FIPS", "Enterprise", "bad"]`, + flags: FlagEnterprise | FlagFIPS, + skipYAML: true, + }, + { + name: "enterprise", + yaml: `["Enterprise"]`, + flags: FlagEnterprise, + }, + { + name: "fips", + yaml: `["FIPS"]`, + flags: FlagFIPS, + }, + { + name: "empty", + yaml: `[]`, + }, + { + name: "nil", + skipYAML: true, + }, + } { + t.Run(tt.name, func(t *testing.T) { + var flags InstallFlags + err := yaml.Unmarshal([]byte(tt.yaml), &flags) + require.NoError(t, err) + require.Equal(t, tt.flags, flags) + + // verify test YAML + var v any + err = yaml.Unmarshal([]byte(tt.yaml), &v) + require.NoError(t, err) + res, err := yaml.Marshal(v) + require.NoError(t, err) + + // compare verified YAML to flag output + out, err := yaml.Marshal(flags) + require.NoError(t, err) + + if !tt.skipYAML { + require.Equal(t, string(res), string(out)) + } + }) + } +} diff --git a/lib/autoupdate/agent/installer.go b/lib/autoupdate/agent/installer.go index 9fc05804f5e56..6de16e17cb4c6 100644 --- a/lib/autoupdate/agent/installer.go +++ b/lib/autoupdate/agent/installer.go @@ -91,12 +91,12 @@ type LocalInstaller struct { // Remove a Teleport version directory from InstallDir. // This function is idempotent. // See Installer interface for additional specs. -func (li *LocalInstaller) Remove(ctx context.Context, version string) error { +func (li *LocalInstaller) Remove(ctx context.Context, rev Revision) error { // os.RemoveAll is dangerous because it can remove an entire directory tree. // We must validate the version to ensure that we remove only a single path // element under the InstallDir, and not InstallDir or its parents. - // versionDir performs these validations. - versionDir, err := li.versionDir(version) + // revisionDir performs these validations. + versionDir, err := li.revisionDir(rev) if err != nil { return trace.Wrap(err) } @@ -124,15 +124,15 @@ func (li *LocalInstaller) Remove(ctx context.Context, version string) error { // Install a Teleport version directory in InstallDir. // This function is idempotent. // See Installer interface for additional specs. -func (li *LocalInstaller) Install(ctx context.Context, version, template string, flags InstallFlags) (err error) { - versionDir, err := li.versionDir(version) +func (li *LocalInstaller) Install(ctx context.Context, rev Revision, template string) (err error) { + versionDir, err := li.revisionDir(rev) if err != nil { return trace.Wrap(err) } sumPath := filepath.Join(versionDir, checksumType) // generate download URI from template - uri, err := makeURL(template, version, flags) + uri, err := makeURL(template, rev) if err != nil { return trace.Wrap(err) } @@ -147,16 +147,16 @@ func (li *LocalInstaller) Install(ctx context.Context, version, template string, oldSum, err := readChecksum(sumPath) if err == nil { if bytes.Equal(oldSum, newSum) { - li.Log.InfoContext(ctx, "Version already present.", "version", version) + li.Log.InfoContext(ctx, "Version already present.", "version", rev) return nil } - li.Log.WarnContext(ctx, "Removing version that does not match checksum.", "version", version) - if err := li.Remove(ctx, version); err != nil { + li.Log.WarnContext(ctx, "Removing version that does not match checksum.", "version", rev) + if err := li.Remove(ctx, rev); err != nil { return trace.Wrap(err) } } else if !errors.Is(err, os.ErrNotExist) { - li.Log.WarnContext(ctx, "Removing version with unreadable checksum.", "version", version, "error", err) - if err := li.Remove(ctx, version); err != nil { + li.Log.WarnContext(ctx, "Removing version with unreadable checksum.", "version", rev, "error", err) + if err := li.Remove(ctx, rev); err != nil { return trace.Wrap(err) } } @@ -215,7 +215,7 @@ func (li *LocalInstaller) Install(ctx context.Context, version, template string, }() // Extract tgz into version directory. - if err := li.extract(ctx, versionDir, f, n, flags); err != nil { + if err := li.extract(ctx, versionDir, f, n, rev.Flags); err != nil { return trace.Wrap(err, "failed to extract teleport") } // Write the checksum last. This marks the version directory as valid. @@ -227,7 +227,7 @@ func (li *LocalInstaller) Install(ctx context.Context, version, template string, } // makeURL to download the Teleport tgz. -func makeURL(uriTmpl, version string, flags InstallFlags) (string, error) { +func makeURL(uriTmpl string, rev Revision) (string, error) { tmpl, err := template.New("uri").Parse(uriTmpl) if err != nil { return "", trace.Wrap(err) @@ -238,10 +238,10 @@ func makeURL(uriTmpl, version string, flags InstallFlags) (string, error) { FIPS, Enterprise bool }{ OS: runtime.GOOS, - Version: version, + Version: rev.Version, Arch: runtime.GOARCH, - FIPS: flags&FlagFIPS != 0, - Enterprise: flags&(FlagEnterprise|FlagFIPS) != 0, + FIPS: rev.Flags&FlagFIPS != 0, + Enterprise: rev.Flags&(FlagEnterprise|FlagFIPS) != 0, } err = tmpl.Execute(&uriBuf, params) if err != nil { @@ -346,11 +346,11 @@ func (li *LocalInstaller) extract(ctx context.Context, dstDir string, src io.Rea } free, err := utils.FreeDiskWithReserve(dstDir, li.ReservedFreeInstallDisk) if err != nil { - return trace.Wrap(err, "failed to calculate free disk in %q", dstDir) + return trace.Wrap(err, "failed to calculate free disk in %s", dstDir) } // Bail if there's not enough free disk space at the target if d := int64(free) - max; d < 0 { - return trace.Errorf("%q needs %d additional bytes of disk space for decompression", dstDir, -d) + return trace.Errorf("%s needs %d additional bytes of disk space for decompression", dstDir, -d) } zr, err := gzip.NewReader(src) if err != nil { @@ -401,7 +401,7 @@ func uncompressedSize(f io.Reader) (int64, error) { } // List installed versions of Teleport. -func (li *LocalInstaller) List(ctx context.Context) (versions []string, err error) { +func (li *LocalInstaller) List(ctx context.Context) (revs []Revision, err error) { entries, err := os.ReadDir(li.InstallDir) if err != nil { return nil, trace.Wrap(err) @@ -410,17 +410,21 @@ func (li *LocalInstaller) List(ctx context.Context) (versions []string, err erro if !entry.IsDir() { continue } - versions = append(versions, entry.Name()) + rev, err := NewRevisionFromDir(entry.Name()) + if err != nil { + return nil, trace.Wrap(err) + } + revs = append(revs, rev) } - return versions, nil + return revs, nil } // Link the specified version into the system LinkBinDir and CopyServiceFile. // The revert function restores the previous linking. // See Installer interface for additional specs. -func (li *LocalInstaller) Link(ctx context.Context, version string) (revert func(context.Context) bool, err error) { +func (li *LocalInstaller) Link(ctx context.Context, rev Revision) (revert func(context.Context) bool, err error) { revert = func(context.Context) bool { return true } - versionDir, err := li.versionDir(version) + versionDir, err := li.revisionDir(rev) if err != nil { return revert, trace.Wrap(err) } @@ -445,8 +449,8 @@ func (li *LocalInstaller) LinkSystem(ctx context.Context) (revert func(context.C // TryLink links the specified version, but only in the case that // no installation of Teleport is already linked or partially linked. // See Installer interface for additional specs. -func (li *LocalInstaller) TryLink(ctx context.Context, version string) error { - versionDir, err := li.versionDir(version) +func (li *LocalInstaller) TryLink(ctx context.Context, revision Revision) error { + versionDir, err := li.revisionDir(revision) if err != nil { return trace.Wrap(err) } @@ -465,8 +469,8 @@ func (li *LocalInstaller) TryLinkSystem(ctx context.Context) error { // Unlink unlinks a version from LinkBinDir and CopyServiceFile. // See Installer interface for additional specs. -func (li *LocalInstaller) Unlink(ctx context.Context, version string) error { - versionDir, err := li.versionDir(version) +func (li *LocalInstaller) Unlink(ctx context.Context, rev Revision) error { + versionDir, err := li.revisionDir(rev) if err != nil { return trace.Wrap(err) } @@ -833,15 +837,15 @@ func needsLink(oldname, newname string) (ok bool, err error) { return false, nil } -// versionDir returns the storage directory for a Teleport version. -// versionDir will fail if the version cannot be used to construct the directory name. +// revisionDir returns the storage directory for a Teleport revision. +// revisionDir will fail if the revision cannot be used to construct the directory name. // For example, it ensures that ".." cannot be provided to return a system directory. -func (li *LocalInstaller) versionDir(version string) (string, error) { +func (li *LocalInstaller) revisionDir(rev Revision) (string, error) { installDir, err := filepath.Abs(li.InstallDir) if err != nil { return "", trace.Wrap(err) } - versionDir := filepath.Join(installDir, version) + versionDir := filepath.Join(installDir, rev.Dir()) if filepath.Dir(versionDir) != filepath.Clean(installDir) { return "", trace.Errorf("refusing to link directory outside of version directory") } diff --git a/lib/autoupdate/agent/installer_test.go b/lib/autoupdate/agent/installer_test.go index 22c983fdbfeb0..576e2ddacc9af 100644 --- a/lib/autoupdate/agent/installer_test.go +++ b/lib/autoupdate/agent/installer_test.go @@ -124,7 +124,7 @@ func TestLocalInstaller_Install(t *testing.T) { ReservedFreeInstallDisk: tt.reservedInstall, } ctx := context.Background() - err := installer.Install(ctx, version, server.URL+"/{{.OS}}/{{.Arch}}/{{.Version}}", tt.flags) + err := installer.Install(ctx, NewRevision(version, tt.flags), server.URL+"/{{.OS}}/{{.Arch}}/{{.Version}}") if tt.errMatch != "" { require.Error(t, err) assert.Contains(t, err.Error(), tt.errMatch) @@ -406,7 +406,7 @@ func TestLocalInstaller_Link(t *testing.T) { }, } ctx := context.Background() - revert, err := installer.Link(ctx, version) + revert, err := installer.Link(ctx, NewRevision(version, 0)) if tt.errMatch != "" { require.Error(t, err) assert.Contains(t, err.Error(), tt.errMatch) @@ -659,7 +659,7 @@ func TestLocalInstaller_TryLink(t *testing.T) { }, } ctx := context.Background() - err = installer.TryLink(ctx, version) + err = installer.TryLink(ctx, NewRevision(version, 0)) if tt.errMatch != "" { require.Error(t, err) assert.Contains(t, err.Error(), tt.errMatch) @@ -809,10 +809,10 @@ func TestLocalInstaller_Remove(t *testing.T) { ctx := context.Background() if tt.linkedVersion != "" { - _, err = installer.Link(ctx, tt.linkedVersion) + _, err = installer.Link(ctx, NewRevision(tt.linkedVersion, 0)) require.NoError(t, err) } - err = installer.Remove(ctx, tt.removeVersion) + err = installer.Remove(ctx, NewRevision(tt.removeVersion, 0)) if tt.errMatch != "" { require.Error(t, err) assert.Contains(t, err.Error(), tt.errMatch) @@ -981,7 +981,7 @@ func TestLocalInstaller_Unlink(t *testing.T) { }, } ctx := context.Background() - err = installer.Unlink(ctx, version) + err = installer.Unlink(ctx, NewRevision(version, 0)) if tt.errMatch != "" { require.Error(t, err) assert.Contains(t, err.Error(), tt.errMatch) @@ -1024,7 +1024,10 @@ func TestLocalInstaller_List(t *testing.T) { Log: slog.Default(), } ctx := context.Background() - versions, err := installer.List(ctx) + revisions, err := installer.List(ctx) require.NoError(t, err) - require.Equal(t, []string{"v1", "v2"}, versions) + require.Equal(t, []Revision{ + NewRevision("v1", 0), + NewRevision("v2", 0), + }, revisions) } diff --git a/lib/autoupdate/agent/process.go b/lib/autoupdate/agent/process.go index 77455b24eb1dd..d2b2b081ad7fe 100644 --- a/lib/autoupdate/agent/process.go +++ b/lib/autoupdate/agent/process.go @@ -34,9 +34,9 @@ import ( ) const ( - // crashMonitorInterval is the polling interval for determining restart times from LastRestartPath. + // crashMonitorInterval is the polling interval for determining restart times from PIDFile. crashMonitorInterval = 2 * time.Second - // minRunningIntervalsBeforeStable is the number of consecutive intervals with the same running PID detect + // minRunningIntervalsBeforeStable is the number of consecutive intervals with the same running PID detected // before the service is determined stable. minRunningIntervalsBeforeStable = 6 // maxCrashesBeforeFailure is the number of total crashes detected before the service is marked as crash-looping. @@ -298,14 +298,14 @@ func (s SystemdService) IsEnabled(ctx context.Context) (bool, error) { code := s.systemctl(ctx, slog.LevelDebug, "is-enabled", "--quiet", s.ServiceName) switch { case code < 0: - return false, trace.Errorf("unable to determine if systemd service %q is enabled", s.ServiceName) + return false, trace.Errorf("unable to determine if systemd service %s is enabled", s.ServiceName) case code == 0: return true, nil } code = s.systemctl(ctx, slog.LevelDebug, "is-active", "--quiet", s.ServiceName) switch { case code < 0: - return false, trace.Errorf("unable to determine if systemd service %q is active", s.ServiceName) + return false, trace.Errorf("unable to determine if systemd service %s is active", s.ServiceName) case code == 0: return true, nil } diff --git a/lib/autoupdate/agent/setup.go b/lib/autoupdate/agent/setup.go index 4caa0a3d95a27..b51e378208715 100644 --- a/lib/autoupdate/agent/setup.go +++ b/lib/autoupdate/agent/setup.go @@ -107,10 +107,10 @@ var alphanum = regexp.MustCompile("^[a-zA-Z0-9-]*$") func NewNamespace(log *slog.Logger, name, dataDir, linkDir string) (*Namespace, error) { if name == defaultNamespace || name == systemNamespace { - return nil, trace.Errorf("namespace %q is reserved", name) + return nil, trace.Errorf("namespace %s is reserved", name) } if !alphanum.MatchString(name) { - return nil, trace.Errorf("invalid namespace name %q, must be alphanumeric", name) + return nil, trace.Errorf("invalid namespace name %s, must be alphanumeric", name) } if name == "" { if dataDir == "" { diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Disable/already_disabled.golden b/lib/autoupdate/agent/testdata/TestUpdater_Disable/already_disabled.golden index b05a76e619cdc..7c30a0f7fa0c8 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Disable/already_disabled.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Disable/already_disabled.golden @@ -5,5 +5,5 @@ spec: enabled: false pinned: false status: - active_version: "" - backup_version: "" + active: + version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Disable/enabled.golden b/lib/autoupdate/agent/testdata/TestUpdater_Disable/enabled.golden index b05a76e619cdc..7c30a0f7fa0c8 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Disable/enabled.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Disable/enabled.golden @@ -5,5 +5,5 @@ spec: enabled: false pinned: false status: - active_version: "" - backup_version: "" + active: + version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/FIPS_and_Enterprise_flags.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/FIPS_and_Enterprise_flags.golden index 9c2b8ef209bbd..a14c9c32bcb12 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/FIPS_and_Enterprise_flags.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/FIPS_and_Enterprise_flags.golden @@ -5,5 +5,6 @@ spec: enabled: false pinned: false status: - active_version: 16.3.0 - backup_version: "" + active: + version: 16.3.0 + flags: [Enterprise, FIPS] diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_kept_for_validation.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_kept_for_validation.golden index 715d8d3aae94d..28eb97f2534e0 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_kept_for_validation.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_kept_for_validation.golden @@ -5,5 +5,7 @@ spec: enabled: false pinned: false status: - active_version: 16.3.0 - backup_version: backup-version + active: + version: 16.3.0 + backup: + version: backup-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_removed_on_install.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_removed_on_install.golden index ec6bf5a101df8..0b6105717e924 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_removed_on_install.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_removed_on_install.golden @@ -5,5 +5,7 @@ spec: enabled: false pinned: false status: - active_version: 16.3.0 - backup_version: old-version + active: + version: 16.3.0 + backup: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_does_not_exist.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_does_not_exist.golden index 9c2b8ef209bbd..cfd3745bbd833 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_does_not_exist.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_does_not_exist.golden @@ -5,5 +5,5 @@ spec: enabled: false pinned: false status: - active_version: 16.3.0 - backup_version: "" + active: + version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file.golden index d63e09b2fa155..66e2695c2fd3a 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file.golden @@ -7,5 +7,7 @@ spec: enabled: true pinned: false status: - active_version: 16.3.0 - backup_version: old-version + active: + version: 16.3.0 + backup: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user.golden index 52b33b0111955..801e0b280f29c 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user.golden @@ -7,5 +7,7 @@ spec: enabled: true pinned: false status: - active_version: new-version - backup_version: old-version + active: + version: new-version + backup: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/defaults.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/defaults.golden index ec6bf5a101df8..0b6105717e924 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/defaults.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/defaults.golden @@ -5,5 +5,7 @@ spec: enabled: false pinned: false status: - active_version: 16.3.0 - backup_version: old-version + active: + version: 16.3.0 + backup: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL.golden index 4de09b7eeb217..61d99f3d18e5f 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL.golden @@ -6,5 +6,5 @@ spec: enabled: false pinned: false status: - active_version: "" - backup_version: "" + active: + version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/install_error.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/install_error.golden index b05a76e619cdc..7c30a0f7fa0c8 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/install_error.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/install_error.golden @@ -5,5 +5,5 @@ spec: enabled: false pinned: false status: - active_version: "" - backup_version: "" + active: + version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/invalid_metadata.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/invalid_metadata.golden index 91cb5de367475..4f1323d0e41f4 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/invalid_metadata.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/invalid_metadata.golden @@ -5,5 +5,5 @@ spec: enabled: false pinned: false status: - active_version: "" - backup_version: "" + active: + version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/no_need_to_reload.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/no_need_to_reload.golden index 9c2b8ef209bbd..cfd3745bbd833 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/no_need_to_reload.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/no_need_to_reload.golden @@ -5,5 +5,5 @@ spec: enabled: false pinned: false status: - active_version: 16.3.0 - backup_version: "" + active: + version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/no_systemd.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/no_systemd.golden index 9c2b8ef209bbd..cfd3745bbd833 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/no_systemd.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/no_systemd.golden @@ -5,5 +5,5 @@ spec: enabled: false pinned: false status: - active_version: 16.3.0 - backup_version: "" + active: + version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip.golden index ec6bf5a101df8..0b6105717e924 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip.golden @@ -5,5 +5,7 @@ spec: enabled: false pinned: false status: - active_version: 16.3.0 - backup_version: old-version + active: + version: 16.3.0 + backup: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed.golden index 9c2b8ef209bbd..cfd3745bbd833 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed.golden @@ -5,5 +5,5 @@ spec: enabled: false pinned: false status: - active_version: 16.3.0 - backup_version: "" + active: + version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Unpin/not_pinned.golden b/lib/autoupdate/agent/testdata/TestUpdater_Unpin/not_pinned.golden index b05a76e619cdc..7c30a0f7fa0c8 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Unpin/not_pinned.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Unpin/not_pinned.golden @@ -5,5 +5,5 @@ spec: enabled: false pinned: false status: - active_version: "" - backup_version: "" + active: + version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Unpin/pinned.golden b/lib/autoupdate/agent/testdata/TestUpdater_Unpin/pinned.golden index b05a76e619cdc..7c30a0f7fa0c8 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Unpin/pinned.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Unpin/pinned.golden @@ -5,5 +5,5 @@ spec: enabled: false pinned: false status: - active_version: "" - backup_version: "" + active: + version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/FIPS_and_Enterprise_flags.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/FIPS_and_Enterprise_flags.golden index 0f0663cdaffc8..171b474129666 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/FIPS_and_Enterprise_flags.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/FIPS_and_Enterprise_flags.golden @@ -6,5 +6,9 @@ spec: enabled: true pinned: false status: - active_version: 16.3.0 - backup_version: old-version + active: + version: 16.3.0 + flags: [Enterprise, FIPS] + backup: + version: old-version + flags: [Enterprise, FIPS] diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_kept_when_no_change.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_kept_when_no_change.golden index c56612e230890..1a55f77e33fb8 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_kept_when_no_change.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_kept_when_no_change.golden @@ -6,5 +6,7 @@ spec: enabled: true pinned: false status: - active_version: 16.3.0 - backup_version: backup-version + active: + version: 16.3.0 + backup: + version: backup-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_removed_on_install.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_removed_on_install.golden index 0f0663cdaffc8..e5ccf529d19b9 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_removed_on_install.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_removed_on_install.golden @@ -6,5 +6,7 @@ spec: enabled: true pinned: false status: - active_version: 16.3.0 - backup_version: old-version + active: + version: 16.3.0 + backup: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL.golden index 1078bb7378840..899b48118505e 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL.golden @@ -6,5 +6,5 @@ spec: enabled: true pinned: false status: - active_version: "" - backup_version: "" + active: + version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/install_error.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/install_error.golden index 85c50cbb4b1e0..0323d25821cb2 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/install_error.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/install_error.golden @@ -5,5 +5,5 @@ spec: enabled: true pinned: false status: - active_version: "" - backup_version: "" + active: + version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/invalid_metadata.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/invalid_metadata.golden index 99be1fccf2fd7..37cfc7fc89d10 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/invalid_metadata.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/invalid_metadata.golden @@ -5,5 +5,5 @@ spec: enabled: false pinned: false status: - active_version: "" - backup_version: "" + active: + version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version.golden index ec80fb388511c..dfc451f108d8c 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version.golden @@ -6,5 +6,7 @@ spec: enabled: true pinned: true status: - active_version: old-version - backup_version: backup-version + active: + version: old-version + backup: + version: backup-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/reload_fails.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/reload_fails.golden index 8451c7aae551b..7efb7d9e3cf1f 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/reload_fails.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/reload_fails.golden @@ -6,6 +6,9 @@ spec: enabled: true pinned: false status: - active_version: old-version - backup_version: backup-version - skip_version: 16.3.0 + active: + version: old-version + backup: + version: backup-version + skip: + version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails.golden index 8451c7aae551b..7efb7d9e3cf1f 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails.golden @@ -6,6 +6,9 @@ spec: enabled: true pinned: false status: - active_version: old-version - backup_version: backup-version - skip_version: 16.3.0 + active: + version: old-version + backup: + version: backup-version + skip: + version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version.golden index 8451c7aae551b..7efb7d9e3cf1f 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version.golden @@ -6,6 +6,9 @@ spec: enabled: true pinned: false status: - active_version: old-version - backup_version: backup-version - skip_version: 16.3.0 + active: + version: old-version + backup: + version: backup-version + skip: + version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_during_window.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_during_window.golden index 41bab44393b8d..b797b2509f748 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_during_window.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_during_window.golden @@ -7,5 +7,5 @@ spec: enabled: false pinned: false status: - active_version: old-version - backup_version: "" + active: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window.golden index 41bab44393b8d..b797b2509f748 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window.golden @@ -7,5 +7,5 @@ spec: enabled: false pinned: false status: - active_version: old-version - backup_version: "" + active: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_during_window.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_during_window.golden index d63e09b2fa155..66e2695c2fd3a 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_during_window.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_during_window.golden @@ -7,5 +7,7 @@ spec: enabled: true pinned: false status: - active_version: 16.3.0 - backup_version: old-version + active: + version: 16.3.0 + backup: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_outside_of_window.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_outside_of_window.golden index 3d0ead7bbfe4d..05b6b150d9796 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_outside_of_window.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_outside_of_window.golden @@ -7,5 +7,5 @@ spec: enabled: true pinned: false status: - active_version: old-version - backup_version: "" + active: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_in_window.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_in_window.golden index 09cfd0264e561..b1e895fb2c2e4 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_in_window.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_in_window.golden @@ -6,5 +6,5 @@ spec: enabled: true pinned: false status: - active_version: 16.3.0 - backup_version: "" + active: + version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window.golden index 09cfd0264e561..b1e895fb2c2e4 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window.golden @@ -6,5 +6,5 @@ spec: enabled: true pinned: false status: - active_version: 16.3.0 - backup_version: "" + active: + version: 16.3.0 diff --git a/lib/autoupdate/agent/updater.go b/lib/autoupdate/agent/updater.go index c1a35c09c8866..f9053da822511 100644 --- a/lib/autoupdate/agent/updater.go +++ b/lib/autoupdate/agent/updater.go @@ -61,10 +61,10 @@ const ( // Log keys const ( - targetVersionKey = "target_version" - activeVersionKey = "active_version" - backupVersionKey = "backup_version" - errorKey = "error" + targetKey = "target_version" + activeKey = "active_version" + backupKey = "backup_version" + errorKey = "error" ) // NewLocalUpdater returns a new Updater that auto-updates local @@ -181,37 +181,37 @@ type Updater struct { // Installer provides an API for installing Teleport agents. type Installer interface { - // Install the Teleport agent at version from the download template. + // Install the Teleport agent at revision from the download template. // Install must be idempotent. - Install(ctx context.Context, version, template string, flags InstallFlags) error - // Link the Teleport agent at the specified version of Teleport into the linking locations. + Install(ctx context.Context, rev Revision, template string) error + // Link the Teleport agent at the specified revision of Teleport into the linking locations. // The revert function must restore the previous linking, returning false on any failure. // Link must be idempotent. Link's revert function must be idempotent. - Link(ctx context.Context, version string) (revert func(context.Context) bool, err error) + Link(ctx context.Context, rev Revision) (revert func(context.Context) bool, err error) // LinkSystem links the system installation of Teleport into the linking locations. // The revert function must restore the previous linking, returning false on any failure. // LinkSystem must be idempotent. LinkSystem's revert function must be idempotent. LinkSystem(ctx context.Context) (revert func(context.Context) bool, err error) - // TryLink links the specified version of Teleport into the linking locations. + // TryLink links the specified revision of Teleport into the linking locations. // Unlike Link, TryLink will fail if existing links to other locations are present. // TryLink must be idempotent. - TryLink(ctx context.Context, version string) error + TryLink(ctx context.Context, rev Revision) error // TryLinkSystem links the system (package) installation of Teleport into the linking locations. // Unlike LinkSystem, TryLinkSystem will fail if existing links to other locations are present. // TryLinkSystem must be idempotent. TryLinkSystem(ctx context.Context) error - // Unlink unlinks the specified version of Teleport from the linking locations. + // Unlink unlinks the specified revision of Teleport from the linking locations. // Unlink must be idempotent. - Unlink(ctx context.Context, version string) error + Unlink(ctx context.Context, rev Revision) error // UnlinkSystem unlinks the system (package) installation of Teleport from the linking locations. // UnlinkSystem must be idempotent. UnlinkSystem(ctx context.Context) error - // List the installed versions of Teleport. - List(ctx context.Context) (versions []string, err error) - // Remove the Teleport agent at version. + // List the installed revisions of Teleport. + List(ctx context.Context) (revisions []Revision, err error) + // Remove the Teleport agent at revision. // Must return ErrLinked if unable to remove due to being linked. // Remove must be idempotent. - Remove(ctx context.Context, version string) error + Remove(ctx context.Context, rev Revision) error } var ( @@ -263,6 +263,18 @@ type OverrideConfig struct { ForceFlags InstallFlags } +func deref[T any](ptr *T) T { + if ptr != nil { + return *ptr + } + var t T + return t +} + +func toPtr[T any](t T) *T { + return &t +} + // Install attempts an initial installation of Teleport. // If the initial installation succeeds, the override configuration is persisted. // Otherwise, the configuration is not changed. @@ -277,8 +289,8 @@ func (u *Updater) Install(ctx context.Context, override OverrideConfig) error { return trace.Wrap(err) } - activeVersion := cfg.Status.ActiveVersion - skipVersion := cfg.Status.SkipVersion + active := cfg.Status.Active + skip := deref(cfg.Status.Skip) // Lookup target version from the proxy. @@ -286,27 +298,28 @@ func (u *Updater) Install(ctx context.Context, override OverrideConfig) error { if err != nil { return trace.Wrap(err) } - targetVersion := resp.TargetVersion - flags := resp.Flags - flags |= override.ForceFlags + targetVersion := resp.Target.Version + targetFlags := resp.Target.Flags + targetFlags |= override.ForceFlags if override.ForceVersion != "" { targetVersion = override.ForceVersion } + target := NewRevision(targetVersion, targetFlags) - switch targetVersion { + switch target.Version { case "": return trace.Errorf("agent version not available from Teleport cluster") - case skipVersion: - u.Log.WarnContext(ctx, "Target version was previously marked as broken. Retrying update.", targetVersionKey, targetVersion, activeVersionKey, activeVersion) + case skip.Version: + u.Log.WarnContext(ctx, "Target version was previously marked as broken. Retrying update.", targetKey, target, activeKey, active) default: - u.Log.InfoContext(ctx, "Initiating installation.", targetVersionKey, targetVersion, activeVersionKey, activeVersion) + u.Log.InfoContext(ctx, "Initiating installation.", targetKey, target, activeKey, active) } - if err := u.update(ctx, cfg, targetVersion, flags); err != nil { + if err := u.update(ctx, cfg, target); err != nil { return trace.Wrap(err) } - if targetVersion == cfg.Status.SkipVersion { - cfg.Status.SkipVersion = "" + if target.Version == skip.Version { + cfg.Status.Skip = nil } // Only write the configuration file if the initial update succeeds. @@ -330,8 +343,8 @@ func (u *Updater) Remove(ctx context.Context) error { if err := validateConfigSpec(&cfg.Spec, OverrideConfig{}); err != nil { return trace.Wrap(err) } - activeVersion := cfg.Status.ActiveVersion - if activeVersion == "" { + active := cfg.Status.Active + if active.Version == "" { u.Log.InfoContext(ctx, "No installation of Teleport managed by the updater. Removing updater configuration.") if err := u.Teardown(ctx); err != nil { return trace.Wrap(err) @@ -350,10 +363,10 @@ func (u *Updater) Remove(ctx context.Context) error { if ok { return trace.Errorf("refusing to remove active installation of Teleport, please disable Teleport first") } - if err := u.Installer.Unlink(ctx, activeVersion); err != nil { + if err := u.Installer.Unlink(ctx, active); err != nil { return trace.Wrap(err) } - u.Log.InfoContext(ctx, "Teleport uninstalled.", "version", activeVersion) + u.Log.InfoContext(ctx, "Teleport uninstalled.", "version", active) if err := u.Teardown(ctx); err != nil { return trace.Wrap(err) } @@ -416,7 +429,7 @@ func (u *Updater) Remove(ctx context.Context) error { } return trace.Wrap(err, "failed to start system package version of Teleport") } - u.Log.InfoContext(ctx, "Auto-updating Teleport removed and replaced by Teleport packaged.", "version", activeVersion) + u.Log.InfoContext(ctx, "Auto-updating Teleport removed and replaced by Teleport packaged.", "version", active) if err := u.Teardown(ctx); err != nil { return trace.Wrap(err) } @@ -473,7 +486,7 @@ func (u *Updater) Unpin(ctx context.Context) error { return trace.Wrap(err, "failed to read %s", updateConfigName) } if !cfg.Spec.Pinned { - u.Log.InfoContext(ctx, "Current version not pinned.", activeVersionKey, cfg.Status.ActiveVersion) + u.Log.InfoContext(ctx, "Current version not pinned.", activeKey, cfg.Status.Active) return nil } cfg.Spec.Pinned = false @@ -497,10 +510,10 @@ func (u *Updater) Update(ctx context.Context) error { if err := validateConfigSpec(&cfg.Spec, OverrideConfig{}); err != nil { return trace.Wrap(err) } - activeVersion := cfg.Status.ActiveVersion - skipVersion := cfg.Status.SkipVersion + active := cfg.Status.Active + skip := deref(cfg.Status.Skip) if !cfg.Spec.Enabled { - u.Log.InfoContext(ctx, "Automatic updates disabled.", activeVersionKey, activeVersion) + u.Log.InfoContext(ctx, "Automatic updates disabled.", activeKey, active) return nil } @@ -508,48 +521,51 @@ func (u *Updater) Update(ctx context.Context) error { if err != nil { return trace.Wrap(err) } - targetVersion := resp.TargetVersion + target := resp.Target if cfg.Spec.Pinned { - switch targetVersion { - case activeVersion: - u.Log.InfoContext(ctx, "Teleport is up-to-date. Installation is pinned to prevent future updates.", activeVersionKey, activeVersion) + switch target { + case active: + u.Log.InfoContext(ctx, "Teleport is up-to-date. Installation is pinned to prevent future updates.", activeKey, active) default: - u.Log.InfoContext(ctx, "Teleport version is pinned. Skipping update.", targetVersionKey, targetVersion, activeVersionKey, activeVersion) + u.Log.InfoContext(ctx, "Teleport version is pinned. Skipping update.", targetKey, target, activeKey, active) } return nil } + // If a version fails and is marked skip, we ignore any edition changes as well. + // If a cluster is broadcasting a version that failed to start, changing ent/fips is unlikely to fix the issue. + if !resp.InWindow { - switch targetVersion { - case "": + switch { + case target.Version == "": u.Log.WarnContext(ctx, "Cannot determine target agent version. Waiting for both version and update window.") - case activeVersion: - u.Log.InfoContext(ctx, "Teleport is up-to-date. Update window is not active.", activeVersionKey, activeVersion) - case skipVersion: - u.Log.InfoContext(ctx, "Update available, but the new version is marked as broken. Update window is not active.", targetVersionKey, targetVersion, activeVersionKey, activeVersion) + case target == active: + u.Log.InfoContext(ctx, "Teleport is up-to-date. Update window is not active.", activeKey, active) + case target.Version == skip.Version: + u.Log.InfoContext(ctx, "Update available, but the new version is marked as broken. Update window is not active.", targetKey, target, activeKey, active) default: - u.Log.InfoContext(ctx, "Update available, but update window is not active.", targetVersionKey, targetVersion, activeVersionKey, activeVersion) + u.Log.InfoContext(ctx, "Update available, but update window is not active.", targetKey, target, activeKey, active) } return nil } - switch targetVersion { - case "": - u.Log.ErrorContext(ctx, "Update window is active, but target version is not available.", activeVersionKey, activeVersion) + switch { + case target.Version == "": + u.Log.ErrorContext(ctx, "Update window is active, but target version is not available.", activeKey, active) return trace.Errorf("target version missing") - case activeVersion: - u.Log.InfoContext(ctx, "Teleport is up-to-date. Update window is active, but no action is needed.", activeVersionKey, activeVersion) + case target == active: + u.Log.InfoContext(ctx, "Teleport is up-to-date. Update window is active, but no action is needed.", activeKey, active) return nil - case skipVersion: - u.Log.InfoContext(ctx, "Update available, but the new version is marked as broken. Skipping update during the update window.", targetVersionKey, targetVersion, activeVersionKey, activeVersion) + case target.Version == skip.Version: + u.Log.InfoContext(ctx, "Update available, but the new version is marked as broken. Skipping update during the update window.", targetKey, target, activeKey, active) return nil default: - u.Log.InfoContext(ctx, "Update available. Initiating update.", targetVersionKey, targetVersion, activeVersionKey, activeVersion) + u.Log.InfoContext(ctx, "Update available. Initiating update.", targetKey, target, activeKey, active) } time.Sleep(resp.Jitter) - updateErr := u.update(ctx, cfg, targetVersion, resp.Flags) + updateErr := u.update(ctx, cfg, target) writeErr := writeConfig(u.ConfigPath, cfg) if writeErr != nil { writeErr = trace.Wrap(writeErr, "failed to write %s", updateConfigName) @@ -591,27 +607,26 @@ func (u *Updater) find(ctx context.Context, cfg *UpdateConfig) (FindResp, error) } jitterSec := resp.AutoUpdate.AgentUpdateJitterSeconds return FindResp{ - TargetVersion: resp.AutoUpdate.AgentVersion, - Flags: flags, - InWindow: resp.AutoUpdate.AgentAutoUpdate, - Jitter: time.Duration(jitterSec) * time.Second, + Target: NewRevision(resp.AutoUpdate.AgentVersion, flags), + InWindow: resp.AutoUpdate.AgentAutoUpdate, + Jitter: time.Duration(jitterSec) * time.Second, }, nil } -func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, targetVersion string, flags InstallFlags) error { - activeVersion := cfg.Status.ActiveVersion - backupVersion := cfg.Status.BackupVersion - switch backupVersion { - case "", targetVersion, activeVersion: +func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, target Revision) error { + active := cfg.Status.Active + backup := deref(cfg.Status.Backup) + switch backup { + case Revision{}, target, active: default: - if targetVersion == activeVersion { + if target == active { // Keep backup version if we are only verifying active version break } - err := u.Installer.Remove(ctx, backupVersion) + err := u.Installer.Remove(ctx, backup) if err != nil { // this could happen if it was already removed due to a failed installation - u.Log.WarnContext(ctx, "Failed to remove backup version of Teleport before new install.", errorKey, err, backupVersionKey, backupVersion) + u.Log.WarnContext(ctx, "Failed to remove backup version of Teleport before new install.", errorKey, err, backupKey, backup) } } @@ -621,7 +636,7 @@ func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, targetVersion s if template == "" { template = cdnURITemplate } - err := u.Installer.Install(ctx, targetVersion, template, flags) + err := u.Installer.Install(ctx, target, template) if err != nil { return trace.Wrap(err, "failed to install") } @@ -631,7 +646,7 @@ func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, targetVersion s // Cleanup logic at the end of this function will ensure that they are removed // eventually. - revert, err := u.Installer.Link(ctx, targetVersion) + revert, err := u.Installer.Link(ctx, target) if err != nil { return trace.Wrap(err, "failed to link") } @@ -640,7 +655,9 @@ func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, targetVersion s // fix the link to restore the active version. revertConfig := func(ctx context.Context) bool { - cfg.Status.SkipVersion = targetVersion + if target.Version != "" { + cfg.Status.Skip = toPtr(target) + } if ok := revert(ctx); !ok { u.Log.ErrorContext(ctx, "Failed to revert Teleport symlinks. Installation likely broken.") return false @@ -665,13 +682,13 @@ func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, targetVersion s if ok := revertConfig(ctx); ok { u.Log.WarnContext(ctx, "Teleport updater encountered a configuration error and successfully reverted the installation.") } - return trace.Wrap(err, "failed to validate configuration for new version %q of Teleport", targetVersion) + return trace.Wrap(err, "failed to validate configuration for new version %s of Teleport", target) } // Restart Teleport if necessary. - if cfg.Status.ActiveVersion != targetVersion { - u.Log.InfoContext(ctx, "Target version successfully installed.", targetVersionKey, targetVersion) + if cfg.Status.Active != target { + u.Log.InfoContext(ctx, "Target version successfully installed.", targetKey, target) err = u.Process.Reload(ctx) if errors.Is(err, context.Canceled) { return trace.Errorf("reload canceled") @@ -689,37 +706,37 @@ func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, targetVersion s u.Log.WarnContext(ctx, "Teleport updater detected an error with the new installation and successfully reverted it.") } } - return trace.Wrap(err, "failed to start new version %q of Teleport", targetVersion) + return trace.Wrap(err, "failed to start new version %s of Teleport", target) + } + if r := cfg.Status.Active; r.Version != "" { + cfg.Status.Backup = toPtr(r) } - cfg.Status.BackupVersion = cfg.Status.ActiveVersion - cfg.Status.ActiveVersion = targetVersion + cfg.Status.Active = target } else { - u.Log.InfoContext(ctx, "Target version successfully validated.", targetVersionKey, targetVersion) + u.Log.InfoContext(ctx, "Target version successfully validated.", targetKey, target) } - if v := cfg.Status.BackupVersion; v != "" { - u.Log.InfoContext(ctx, "Backup version set.", backupVersionKey, v) + if r := deref(cfg.Status.Backup); r.Version != "" { + u.Log.InfoContext(ctx, "Backup version set.", backupKey, r) } - return trace.Wrap(u.cleanup(ctx, []string{ - targetVersion, - activeVersion, - backupVersion, + return trace.Wrap(u.cleanup(ctx, []Revision{ + target, active, backup, })) } // cleanup orphan installations -func (u *Updater) cleanup(ctx context.Context, keep []string) error { - versions, err := u.Installer.List(ctx) +func (u *Updater) cleanup(ctx context.Context, keep []Revision) error { + revs, err := u.Installer.List(ctx) if err != nil { u.Log.ErrorContext(ctx, "Failed to read installed versions.", errorKey, err) return nil } - if len(versions) < 3 { + if len(revs) < 3 { return nil } - u.Log.WarnContext(ctx, "More than two versions of Teleport are installed. Removing unused versions.", "count", len(versions)) - for _, v := range versions { - if v == "" || slices.Contains(keep, v) { + u.Log.WarnContext(ctx, "More than two versions of Teleport are installed. Removing unused versions.", "count", len(revs)) + for _, v := range revs { + if v.Version == "" || slices.Contains(keep, v) { continue } err := u.Installer.Remove(ctx, v) @@ -748,20 +765,20 @@ func (u *Updater) LinkPackage(ctx context.Context) error { if err := validateConfigSpec(&cfg.Spec, OverrideConfig{}); err != nil { return trace.Wrap(err) } - activeVersion := cfg.Status.ActiveVersion + active := cfg.Status.Active if cfg.Spec.Enabled { - u.Log.InfoContext(ctx, "Automatic updates is enabled. Skipping system package link.", activeVersionKey, activeVersion) + u.Log.InfoContext(ctx, "Automatic updates is enabled. Skipping system package link.", activeKey, active) return nil } if cfg.Spec.Pinned { - u.Log.InfoContext(ctx, "Automatic update version is pinned. Skipping system package link.", activeVersionKey, activeVersion) + u.Log.InfoContext(ctx, "Automatic update version is pinned. Skipping system package link.", activeKey, active) return nil } // If an active version is set, but auto-updates is disabled, try to link the system installation in case the config is stale. // If any links are present, this will return ErrLinked and not create any system links. // This state is important to log as a warning, if err := u.Installer.TryLinkSystem(ctx); errors.Is(err, ErrLinked) { - u.Log.WarnContext(ctx, "Automatic updates is disabled, but a non-package version of Teleport is linked.", activeVersionKey, activeVersion) + u.Log.WarnContext(ctx, "Automatic updates is disabled, but a non-package version of Teleport is linked.", activeKey, active) return nil } else if err != nil { return trace.Wrap(err, "failed to link system package installation") diff --git a/lib/autoupdate/agent/updater_test.go b/lib/autoupdate/agent/updater_test.go index e4afd63740a76..db6ffdd930c64 100644 --- a/lib/autoupdate/agent/updater_test.go +++ b/lib/autoupdate/agent/updater_test.go @@ -227,10 +227,10 @@ func TestUpdater_Update(t *testing.T) { setupErr error reloadErr error - removedVersions []string - installedVersion string + removedRevisions []Revision + installedRevision Revision installedTemplate string - linkedVersion string + linkedRevision Revision requestGroup string reloadCalls int revertCalls int @@ -248,15 +248,15 @@ func TestUpdater_Update(t *testing.T) { Enabled: true, }, Status: UpdateStatus{ - ActiveVersion: "old-version", + Active: NewRevision("old-version", 0), }, }, inWindow: true, - removedVersions: []string{"unknown-version"}, - installedVersion: "16.3.0", + removedRevisions: []Revision{NewRevision("unknown-version", 0)}, + installedRevision: NewRevision("16.3.0", 0), installedTemplate: "https://example.com", - linkedVersion: "16.3.0", + linkedRevision: NewRevision("16.3.0", 0), requestGroup: "group", reloadCalls: 1, setupCalls: 1, @@ -272,7 +272,7 @@ func TestUpdater_Update(t *testing.T) { Enabled: false, }, Status: UpdateStatus{ - ActiveVersion: "old-version", + Active: NewRevision("old-version", 0), }, }, inWindow: true, @@ -288,7 +288,7 @@ func TestUpdater_Update(t *testing.T) { Enabled: true, }, Status: UpdateStatus{ - ActiveVersion: "old-version", + Active: NewRevision("old-version", 0), }, }, requestGroup: "group", @@ -304,7 +304,7 @@ func TestUpdater_Update(t *testing.T) { Enabled: false, }, Status: UpdateStatus{ - ActiveVersion: "old-version", + Active: NewRevision("old-version", 0), }, }, }, @@ -334,7 +334,7 @@ func TestUpdater_Update(t *testing.T) { inWindow: true, installErr: errors.New("install error"), - installedVersion: "16.3.0", + installedRevision: NewRevision("16.3.0", 0), installedTemplate: cdnURITemplate, errMatch: "install error", }, @@ -348,7 +348,7 @@ func TestUpdater_Update(t *testing.T) { Enabled: true, }, Status: UpdateStatus{ - ActiveVersion: "16.3.0", + Active: NewRevision("16.3.0", 0), }, }, inWindow: true, @@ -363,7 +363,7 @@ func TestUpdater_Update(t *testing.T) { Enabled: true, }, Status: UpdateStatus{ - ActiveVersion: "16.3.0", + Active: NewRevision("16.3.0", 0), }, }, }, @@ -377,18 +377,21 @@ func TestUpdater_Update(t *testing.T) { Enabled: true, }, Status: UpdateStatus{ - ActiveVersion: "old-version", - BackupVersion: "backup-version", + Active: NewRevision("old-version", 0), + Backup: toPtr(NewRevision("backup-version", 0)), }, }, inWindow: true, - installedVersion: "16.3.0", + installedRevision: NewRevision("16.3.0", 0), installedTemplate: "https://example.com", - linkedVersion: "16.3.0", - removedVersions: []string{"backup-version", "unknown-version"}, - reloadCalls: 1, - setupCalls: 1, + linkedRevision: NewRevision("16.3.0", 0), + removedRevisions: []Revision{ + NewRevision("backup-version", 0), + NewRevision("unknown-version", 0), + }, + reloadCalls: 1, + setupCalls: 1, }, { name: "backup version kept when no change", @@ -400,8 +403,8 @@ func TestUpdater_Update(t *testing.T) { Enabled: true, }, Status: UpdateStatus{ - ActiveVersion: "16.3.0", - BackupVersion: "backup-version", + Active: NewRevision("16.3.0", 0), + Backup: toPtr(NewRevision("backup-version", 0)), }, }, inWindow: true, @@ -419,19 +422,22 @@ func TestUpdater_Update(t *testing.T) { Enabled: true, }, Status: UpdateStatus{ - ActiveVersion: "old-version", - BackupVersion: "backup-version", + Active: NewRevision("old-version", FlagEnterprise|FlagFIPS), + Backup: toPtr(NewRevision("backup-version", FlagEnterprise|FlagFIPS)), }, }, inWindow: true, flags: FlagEnterprise | FlagFIPS, - installedVersion: "16.3.0", + installedRevision: NewRevision("16.3.0", FlagEnterprise|FlagFIPS), installedTemplate: "https://example.com", - linkedVersion: "16.3.0", - removedVersions: []string{"backup-version", "unknown-version"}, - reloadCalls: 1, - setupCalls: 1, + linkedRevision: NewRevision("16.3.0", FlagEnterprise|FlagFIPS), + removedRevisions: []Revision{ + NewRevision("backup-version", FlagEnterprise|FlagFIPS), + NewRevision("unknown-version", 0), + }, + reloadCalls: 1, + setupCalls: 1, }, { name: "invalid metadata", @@ -448,21 +454,23 @@ func TestUpdater_Update(t *testing.T) { Enabled: true, }, Status: UpdateStatus{ - ActiveVersion: "old-version", - BackupVersion: "backup-version", + Active: NewRevision("old-version", 0), + Backup: toPtr(NewRevision("backup-version", 0)), }, }, inWindow: true, setupErr: errors.New("setup error"), - installedVersion: "16.3.0", + installedRevision: NewRevision("16.3.0", 0), installedTemplate: "https://example.com", - linkedVersion: "16.3.0", - removedVersions: []string{"backup-version"}, - reloadCalls: 0, - revertCalls: 1, - setupCalls: 1, - errMatch: "setup error", + linkedRevision: NewRevision("16.3.0", 0), + removedRevisions: []Revision{ + NewRevision("backup-version", 0), + }, + reloadCalls: 0, + revertCalls: 1, + setupCalls: 1, + errMatch: "setup error", }, { name: "reload fails", @@ -474,21 +482,23 @@ func TestUpdater_Update(t *testing.T) { Enabled: true, }, Status: UpdateStatus{ - ActiveVersion: "old-version", - BackupVersion: "backup-version", + Active: NewRevision("old-version", 0), + Backup: toPtr(NewRevision("backup-version", 0)), }, }, inWindow: true, reloadErr: errors.New("reload error"), - installedVersion: "16.3.0", + installedRevision: NewRevision("16.3.0", 0), installedTemplate: "https://example.com", - linkedVersion: "16.3.0", - removedVersions: []string{"backup-version"}, - reloadCalls: 2, - revertCalls: 1, - setupCalls: 1, - errMatch: "reload error", + linkedRevision: NewRevision("16.3.0", 0), + removedRevisions: []Revision{ + NewRevision("backup-version", 0), + }, + reloadCalls: 2, + revertCalls: 1, + setupCalls: 1, + errMatch: "reload error", }, { name: "skip version", @@ -500,9 +510,9 @@ func TestUpdater_Update(t *testing.T) { Enabled: true, }, Status: UpdateStatus{ - ActiveVersion: "old-version", - BackupVersion: "backup-version", - SkipVersion: "16.3.0", + Active: NewRevision("old-version", 0), + Backup: toPtr(NewRevision("backup-version", 0)), + Skip: toPtr(NewRevision("16.3.0", 0)), }, }, inWindow: true, @@ -518,8 +528,8 @@ func TestUpdater_Update(t *testing.T) { Pinned: true, }, Status: UpdateStatus{ - ActiveVersion: "old-version", - BackupVersion: "backup-version", + Active: NewRevision("old-version", 0), + Backup: toPtr(NewRevision("backup-version", 0)), }, }, inWindow: true, @@ -567,39 +577,37 @@ func TestUpdater_Update(t *testing.T) { } var ( - installedVersion string + installedRevision Revision installedTemplate string - linkedVersion string - removedVersions []string - installedFlags InstallFlags + linkedRevision Revision + removedRevisions []Revision revertFuncCalls int setupCalls int revertSetupCalls int reloadCalls int ) updater.Installer = &testInstaller{ - FuncInstall: func(_ context.Context, version, template string, flags InstallFlags) error { - installedVersion = version + FuncInstall: func(_ context.Context, rev Revision, template string) error { + installedRevision = rev installedTemplate = template - installedFlags = flags return tt.installErr }, - FuncLink: func(_ context.Context, version string) (revert func(context.Context) bool, err error) { - linkedVersion = version + FuncLink: func(_ context.Context, rev Revision) (revert func(context.Context) bool, err error) { + linkedRevision = rev return func(_ context.Context) bool { revertFuncCalls++ return true }, nil }, - FuncList: func(_ context.Context) (versions []string, err error) { - return slices.Compact([]string{ - installedVersion, - tt.cfg.Status.ActiveVersion, - "unknown-version", + FuncList: func(_ context.Context) (revs []Revision, err error) { + return slices.Compact([]Revision{ + installedRevision, + tt.cfg.Status.Active, + NewRevision("unknown-version", 0), }), nil }, - FuncRemove: func(_ context.Context, version string) error { - removedVersions = append(removedVersions, version) + FuncRemove: func(_ context.Context, rev Revision) error { + removedRevisions = append(removedRevisions, rev) return nil }, } @@ -626,11 +634,11 @@ func TestUpdater_Update(t *testing.T) { } else { require.NoError(t, err) } - require.Equal(t, tt.installedVersion, installedVersion) + require.Equal(t, tt.installedRevision, installedRevision) require.Equal(t, tt.installedTemplate, installedTemplate) - require.Equal(t, tt.linkedVersion, linkedVersion) - require.Equal(t, tt.removedVersions, removedVersions) - require.Equal(t, tt.flags, installedFlags) + require.Equal(t, tt.linkedRevision, linkedRevision) + require.Equal(t, tt.removedRevisions, removedRevisions) + require.Equal(t, tt.flags, installedRevision.Flags) require.Equal(t, tt.requestGroup, requestedGroup) require.Equal(t, tt.reloadCalls, reloadCalls) require.Equal(t, tt.revertCalls, revertSetupCalls) @@ -835,14 +843,7 @@ func TestUpdater_Remove(t *testing.T) { errMatch string }{ { - name: "no config", - cfg: &UpdateConfig{ - Version: updateConfigVersion, - Kind: updateConfigKind, - Status: UpdateStatus{ - ActiveVersion: "", - }, - }, + name: "no config", teardownCalls: 1, }, { @@ -859,7 +860,7 @@ func TestUpdater_Remove(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Status: UpdateStatus{ - ActiveVersion: version, + Active: NewRevision(version, 0), }, }, linkSystemErr: ErrNoBinaries, @@ -873,7 +874,7 @@ func TestUpdater_Remove(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Status: UpdateStatus{ - ActiveVersion: version, + Active: NewRevision(version, 0), }, }, linkSystemErr: ErrNoBinaries, @@ -887,7 +888,7 @@ func TestUpdater_Remove(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Status: UpdateStatus{ - ActiveVersion: version, + Active: NewRevision(version, 0), }, }, linkSystemErr: ErrNoBinaries, @@ -902,7 +903,7 @@ func TestUpdater_Remove(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Status: UpdateStatus{ - ActiveVersion: version, + Active: NewRevision(version, 0), }, }, linkSystemCalls: 1, @@ -916,7 +917,7 @@ func TestUpdater_Remove(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Status: UpdateStatus{ - ActiveVersion: version, + Active: NewRevision(version, 0), }, }, linkSystemCalls: 1, @@ -932,7 +933,7 @@ func TestUpdater_Remove(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Status: UpdateStatus{ - ActiveVersion: version, + Active: NewRevision(version, 0), }, }, linkSystemCalls: 1, @@ -947,7 +948,7 @@ func TestUpdater_Remove(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Status: UpdateStatus{ - ActiveVersion: version, + Active: NewRevision(version, 0), }, }, linkSystemCalls: 1, @@ -962,7 +963,7 @@ func TestUpdater_Remove(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Status: UpdateStatus{ - ActiveVersion: version, + Active: NewRevision(version, 0), }, }, linkSystemCalls: 1, @@ -1011,8 +1012,8 @@ func TestUpdater_Remove(t *testing.T) { return true }, tt.linkSystemErr }, - FuncUnlink: func(_ context.Context, version string) error { - unlinkedVersion = version + FuncUnlink: func(_ context.Context, rev Revision) error { + unlinkedVersion = rev.Version return nil }, } @@ -1064,10 +1065,10 @@ func TestUpdater_Install(t *testing.T) { setupErr error reloadErr error - removedVersion string - installedVersion string + removedRevision Revision + installedRevision Revision installedTemplate string - linkedVersion string + linkedRevision Revision requestGroup string reloadCalls int revertCalls int @@ -1085,13 +1086,13 @@ func TestUpdater_Install(t *testing.T) { URLTemplate: "https://example.com", }, Status: UpdateStatus{ - ActiveVersion: "old-version", + Active: NewRevision("old-version", 0), }, }, - installedVersion: "16.3.0", + installedRevision: NewRevision("16.3.0", 0), installedTemplate: "https://example.com", - linkedVersion: "16.3.0", + linkedRevision: NewRevision("16.3.0", 0), requestGroup: "group", reloadCalls: 1, setupCalls: 1, @@ -1106,7 +1107,7 @@ func TestUpdater_Install(t *testing.T) { URLTemplate: "https://example.com/old", }, Status: UpdateStatus{ - ActiveVersion: "old-version", + Active: NewRevision("old-version", 0), }, }, userCfg: OverrideConfig{ @@ -1118,9 +1119,9 @@ func TestUpdater_Install(t *testing.T) { ForceVersion: "new-version", }, - installedVersion: "new-version", + installedRevision: NewRevision("new-version", 0), installedTemplate: "https://example.com/new", - linkedVersion: "new-version", + linkedRevision: NewRevision("new-version", 0), requestGroup: "new-group", reloadCalls: 1, setupCalls: 1, @@ -1131,13 +1132,13 @@ func TestUpdater_Install(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Status: UpdateStatus{ - ActiveVersion: "old-version", + Active: NewRevision("old-version", 0), }, }, - installedVersion: "16.3.0", + installedRevision: NewRevision("16.3.0", 0), installedTemplate: cdnURITemplate, - linkedVersion: "16.3.0", + linkedRevision: NewRevision("16.3.0", 0), reloadCalls: 1, setupCalls: 1, }, @@ -1147,14 +1148,14 @@ func TestUpdater_Install(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Status: UpdateStatus{ - ActiveVersion: "old-version", - SkipVersion: "16.3.0", + Active: NewRevision("old-version", 0), + Skip: toPtr(NewRevision("16.3.0", 0)), }, }, - installedVersion: "16.3.0", + installedRevision: NewRevision("16.3.0", 0), installedTemplate: cdnURITemplate, - linkedVersion: "16.3.0", + linkedRevision: NewRevision("16.3.0", 0), reloadCalls: 1, setupCalls: 1, }, @@ -1178,8 +1179,7 @@ func TestUpdater_Install(t *testing.T) { }, installErr: errors.New("install error"), - installedVersion: "16.3.0", - linkedVersion: "", + installedRevision: NewRevision("16.3.0", 0), installedTemplate: cdnURITemplate, errMatch: "install error", }, @@ -1189,13 +1189,13 @@ func TestUpdater_Install(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Status: UpdateStatus{ - ActiveVersion: "16.3.0", + Active: NewRevision("16.3.0", 0), }, }, - installedVersion: "16.3.0", + installedRevision: NewRevision("16.3.0", 0), installedTemplate: cdnURITemplate, - linkedVersion: "16.3.0", + linkedRevision: NewRevision("16.3.0", 0), reloadCalls: 0, setupCalls: 1, }, @@ -1205,15 +1205,15 @@ func TestUpdater_Install(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Status: UpdateStatus{ - ActiveVersion: "old-version", - BackupVersion: "backup-version", + Active: NewRevision("old-version", 0), + Backup: toPtr(NewRevision("backup-version", 0)), }, }, - installedVersion: "16.3.0", + installedRevision: NewRevision("16.3.0", 0), installedTemplate: cdnURITemplate, - linkedVersion: "16.3.0", - removedVersion: "backup-version", + linkedRevision: NewRevision("16.3.0", 0), + removedRevision: NewRevision("backup-version", 0), reloadCalls: 1, setupCalls: 1, }, @@ -1223,33 +1223,32 @@ func TestUpdater_Install(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Status: UpdateStatus{ - ActiveVersion: "16.3.0", - BackupVersion: "backup-version", + Active: NewRevision("16.3.0", 0), + Backup: toPtr(NewRevision("backup-version", 0)), }, }, - installedVersion: "16.3.0", + installedRevision: NewRevision("16.3.0", 0), installedTemplate: cdnURITemplate, - linkedVersion: "16.3.0", - removedVersion: "", + linkedRevision: NewRevision("16.3.0", 0), reloadCalls: 0, setupCalls: 1, }, { name: "config does not exist", - installedVersion: "16.3.0", + installedRevision: NewRevision("16.3.0", 0), installedTemplate: cdnURITemplate, - linkedVersion: "16.3.0", + linkedRevision: NewRevision("16.3.0", 0), reloadCalls: 1, setupCalls: 1, }, { name: "FIPS and Enterprise flags", flags: FlagEnterprise | FlagFIPS, - installedVersion: "16.3.0", + installedRevision: NewRevision("16.3.0", FlagEnterprise|FlagFIPS), installedTemplate: cdnURITemplate, - linkedVersion: "16.3.0", + linkedRevision: NewRevision("16.3.0", FlagEnterprise|FlagFIPS), reloadCalls: 1, setupCalls: 1, }, @@ -1262,9 +1261,9 @@ func TestUpdater_Install(t *testing.T) { name: "setup fails", setupErr: errors.New("setup error"), - installedVersion: "16.3.0", + installedRevision: NewRevision("16.3.0", 0), installedTemplate: cdnURITemplate, - linkedVersion: "16.3.0", + linkedRevision: NewRevision("16.3.0", 0), reloadCalls: 0, revertCalls: 1, setupCalls: 1, @@ -1274,9 +1273,9 @@ func TestUpdater_Install(t *testing.T) { name: "reload fails", reloadErr: errors.New("reload error"), - installedVersion: "16.3.0", + installedRevision: NewRevision("16.3.0", 0), installedTemplate: cdnURITemplate, - linkedVersion: "16.3.0", + linkedRevision: NewRevision("16.3.0", 0), reloadCalls: 2, revertCalls: 1, setupCalls: 1, @@ -1287,9 +1286,9 @@ func TestUpdater_Install(t *testing.T) { reloadErr: ErrNotSupported, setupErr: ErrNotSupported, - installedVersion: "16.3.0", + installedRevision: NewRevision("16.3.0", 0), installedTemplate: cdnURITemplate, - linkedVersion: "16.3.0", + linkedRevision: NewRevision("16.3.0", 0), reloadCalls: 1, setupCalls: 1, }, @@ -1297,9 +1296,9 @@ func TestUpdater_Install(t *testing.T) { name: "no need to reload", reloadErr: ErrNotNeeded, - installedVersion: "16.3.0", + installedRevision: NewRevision("16.3.0", 0), installedTemplate: cdnURITemplate, - linkedVersion: "16.3.0", + linkedRevision: NewRevision("16.3.0", 0), reloadCalls: 1, setupCalls: 1, }, @@ -1348,35 +1347,33 @@ func TestUpdater_Install(t *testing.T) { } var ( - installedVersion string + installedRevision Revision installedTemplate string - linkedVersion string - removedVersion string - installedFlags InstallFlags + linkedRevision Revision + removedRevision Revision revertFuncCalls int reloadCalls int setupCalls int revertSetupCalls int ) updater.Installer = &testInstaller{ - FuncInstall: func(_ context.Context, version, template string, flags InstallFlags) error { - installedVersion = version + FuncInstall: func(_ context.Context, rev Revision, template string) error { + installedRevision = rev installedTemplate = template - installedFlags = flags return tt.installErr }, - FuncLink: func(_ context.Context, version string) (revert func(context.Context) bool, err error) { - linkedVersion = version + FuncLink: func(_ context.Context, rev Revision) (revert func(context.Context) bool, err error) { + linkedRevision = rev return func(_ context.Context) bool { revertFuncCalls++ return true }, nil }, - FuncList: func(_ context.Context) (versions []string, err error) { - return []string{}, nil + FuncList: func(_ context.Context) (revs []Revision, err error) { + return []Revision{}, nil }, - FuncRemove: func(_ context.Context, version string) error { - removedVersion = version + FuncRemove: func(_ context.Context, rev Revision) error { + removedRevision = rev return nil }, } @@ -1403,11 +1400,11 @@ func TestUpdater_Install(t *testing.T) { } else { require.NoError(t, err) } - require.Equal(t, tt.installedVersion, installedVersion) + require.Equal(t, tt.installedRevision, installedRevision) require.Equal(t, tt.installedTemplate, installedTemplate) - require.Equal(t, tt.linkedVersion, linkedVersion) - require.Equal(t, tt.removedVersion, removedVersion) - require.Equal(t, tt.flags, installedFlags) + require.Equal(t, tt.linkedRevision, linkedRevision) + require.Equal(t, tt.removedRevision, removedRevision) + require.Equal(t, tt.flags, installedRevision.Flags) require.Equal(t, tt.requestGroup, requestedGroup) require.Equal(t, tt.reloadCalls, reloadCalls) require.Equal(t, tt.revertCalls, revertSetupCalls) @@ -1439,50 +1436,50 @@ func blankTestAddr(s []byte) []byte { } type testInstaller struct { - FuncInstall func(ctx context.Context, version, template string, flags InstallFlags) error - FuncRemove func(ctx context.Context, version string) error - FuncLink func(ctx context.Context, version string) (revert func(context.Context) bool, err error) + FuncInstall func(ctx context.Context, rev Revision, template string) error + FuncRemove func(ctx context.Context, rev Revision) error + FuncLink func(ctx context.Context, rev Revision) (revert func(context.Context) bool, err error) FuncLinkSystem func(ctx context.Context) (revert func(context.Context) bool, err error) - FuncTryLink func(ctx context.Context, version string) error + FuncTryLink func(ctx context.Context, rev Revision) error FuncTryLinkSystem func(ctx context.Context) error - FuncUnlink func(ctx context.Context, version string) error + FuncUnlink func(ctx context.Context, rev Revision) error FuncUnlinkSystem func(ctx context.Context) error - FuncList func(ctx context.Context) (versions []string, err error) + FuncList func(ctx context.Context) (revs []Revision, err error) } -func (ti *testInstaller) Install(ctx context.Context, version, template string, flags InstallFlags) error { - return ti.FuncInstall(ctx, version, template, flags) +func (ti *testInstaller) Install(ctx context.Context, rev Revision, template string) error { + return ti.FuncInstall(ctx, rev, template) } -func (ti *testInstaller) Remove(ctx context.Context, version string) error { - return ti.FuncRemove(ctx, version) +func (ti *testInstaller) Remove(ctx context.Context, rev Revision) error { + return ti.FuncRemove(ctx, rev) } -func (ti *testInstaller) Link(ctx context.Context, version string) (revert func(context.Context) bool, err error) { - return ti.FuncLink(ctx, version) +func (ti *testInstaller) Link(ctx context.Context, rev Revision) (revert func(context.Context) bool, err error) { + return ti.FuncLink(ctx, rev) } func (ti *testInstaller) LinkSystem(ctx context.Context) (revert func(context.Context) bool, err error) { return ti.FuncLinkSystem(ctx) } -func (ti *testInstaller) TryLink(ctx context.Context, version string) error { - return ti.FuncTryLink(ctx, version) +func (ti *testInstaller) TryLink(ctx context.Context, rev Revision) error { + return ti.FuncTryLink(ctx, rev) } func (ti *testInstaller) TryLinkSystem(ctx context.Context) error { return ti.FuncTryLinkSystem(ctx) } -func (ti *testInstaller) Unlink(ctx context.Context, version string) error { - return ti.FuncUnlink(ctx, version) +func (ti *testInstaller) Unlink(ctx context.Context, rev Revision) error { + return ti.FuncUnlink(ctx, rev) } func (ti *testInstaller) UnlinkSystem(ctx context.Context) error { return ti.FuncUnlinkSystem(ctx) } -func (ti *testInstaller) List(ctx context.Context) (versions []string, err error) { +func (ti *testInstaller) List(ctx context.Context) (revs []Revision, err error) { return ti.FuncList(ctx) }