diff --git a/lib/autoupdate/agent/setup.go b/lib/autoupdate/agent/setup.go index 5fb67173ed669..6a47b51f082a2 100644 --- a/lib/autoupdate/agent/setup.go +++ b/lib/autoupdate/agent/setup.go @@ -55,6 +55,8 @@ const ( const ( // deprecatedTimerName is the timer for the deprecated upgrader should be disabled on setup. deprecatedTimerName = "teleport-upgrade.timer" + // deprecatedServiceName is the service for the deprecated upgrader should be disabled on setup. + deprecatedServiceName = "teleport-upgrade.service" ) const ( @@ -85,6 +87,13 @@ WantedBy={{.TeleportService}} [Service] Environment="TELEPORT_UPDATE_CONFIG_FILE={{escape .UpdaterConfigFile}}" Environment="TELEPORT_UPDATE_INSTALL_DIR={{escape .InstallDir}}" +` + + deprecatedDropInTemplate = `# teleport-update +# DO NOT EDIT THIS FILE +[Service] +ExecStart= +ExecStart=-/bin/echo "The teleport-upgrade script has been disabled by teleport-update. Please remove the teleport-ent-updater package." ` // This configuration sets the default value for needrestart-trigger automatic restarts for teleport.service to disabled. // Users may still choose to enable needrestart for teleport.service when installing packaging interactively (or via dpkg config), @@ -130,8 +139,10 @@ type Namespace struct { updaterServiceFile string // updaterTimerFile is the systemd timer path for the updater updaterTimerFile string - // dropInFile is the Teleport systemd drop-in path extending Teleport - dropInFile string + // teleportDropInFile is the Teleport systemd drop-in path extending Teleport + teleportDropInFile string + // deprecatedDropInFile is the deprecated upgrader's systemd drop-in path + deprecatedDropInFile string // needrestartConfFile is the path to needrestart configuration for Teleport needrestartConfFile string } @@ -157,19 +168,20 @@ func NewNamespace(ctx context.Context, log *slog.Logger, name, installDir string if name == "" { linkDir := defaultPathDir return &Namespace{ - log: log, - name: name, - installDir: installDir, - defaultPathDir: linkDir, - dataDir: defaults.DataDir, - serviceFile: filepath.Join("/", serviceDir, serviceName), - configFile: defaults.ConfigFilePath, - pidFile: filepath.Join(systemdPIDDir, "teleport.pid"), - updaterIDFile: filepath.Join(os.TempDir(), BinaryName+".id"), - updaterServiceFile: filepath.Join(systemdAdminDir, BinaryName+".service"), - updaterTimerFile: filepath.Join(systemdAdminDir, BinaryName+".timer"), - dropInFile: filepath.Join(systemdAdminDir, "teleport.service.d", BinaryName+".conf"), - needrestartConfFile: filepath.Join(needrestartConfDir, BinaryName+".conf"), + log: log, + name: name, + installDir: installDir, + defaultPathDir: linkDir, + dataDir: defaults.DataDir, + serviceFile: filepath.Join("/", serviceDir, serviceName), + configFile: defaults.ConfigFilePath, + pidFile: filepath.Join(systemdPIDDir, "teleport.pid"), + updaterIDFile: filepath.Join(os.TempDir(), BinaryName+".id"), + updaterServiceFile: filepath.Join(systemdAdminDir, BinaryName+".service"), + updaterTimerFile: filepath.Join(systemdAdminDir, BinaryName+".timer"), + teleportDropInFile: filepath.Join(systemdAdminDir, "teleport.service.d", BinaryName+".conf"), + deprecatedDropInFile: filepath.Join(systemdAdminDir, deprecatedServiceName+".d", BinaryName+".conf"), + needrestartConfFile: filepath.Join(needrestartConfDir, BinaryName+".conf"), }, nil } @@ -187,8 +199,9 @@ func NewNamespace(ctx context.Context, log *slog.Logger, name, installDir string updaterIDFile: filepath.Join(os.TempDir(), BinaryName+"_"+name+".id"), updaterServiceFile: filepath.Join(systemdAdminDir, BinaryName+"_"+name+".service"), updaterTimerFile: filepath.Join(systemdAdminDir, BinaryName+"_"+name+".timer"), - dropInFile: filepath.Join(systemdAdminDir, prefix+".service.d", BinaryName+"_"+name+".conf"), + teleportDropInFile: filepath.Join(systemdAdminDir, prefix+".service.d", BinaryName+"_"+name+".conf"), needrestartConfFile: filepath.Join(needrestartConfDir, BinaryName+"_"+name+".conf"), + // no deprecatedDropInFile, as teleport-upgrade does not conflict with namespaced installs }, nil } @@ -250,11 +263,12 @@ func (ns *Namespace) Setup(ctx context.Context, path string) error { } if present { if err := oldTimer.Disable(ctx, true); err != nil { - ns.log.ErrorContext(ctx, "The deprecated teleport-ent-updater package is installed on this server, and it cannot be disabled due to an error.", errorKey, err) + ns.log.ErrorContext(ctx, "The deprecated teleport-ent-updater package is installed on this server and cannot be disabled due to an error.", errorKey, err) ns.log.ErrorContext(ctx, "You must remove the teleport-ent-updater package after verifying that teleport-update is working.", errorKey, err) } else { ns.log.WarnContext(ctx, "The deprecated teleport-ent-updater package is installed on this server.") ns.log.WarnContext(ctx, "The systemd timer included in this package has been disabled to prevent conflicts.", "timer", deprecatedTimerName) + ns.log.WarnContext(ctx, "The systemd service included in this package will no longer perform updates.", "service", deprecatedServiceName) ns.log.WarnContext(ctx, "Please remove the teleport-ent-updater package after verifying that teleport-update is working.") } } @@ -283,9 +297,13 @@ func (ns *Namespace) Teardown(ctx context.Context) error { for _, p := range []string{ ns.updaterServiceFile, ns.updaterTimerFile, - ns.dropInFile, + ns.teleportDropInFile, + ns.deprecatedDropInFile, ns.needrestartConfFile, } { + if p == "" { + continue + } if err := os.Remove(p); err != nil && !errors.Is(err, fs.ErrNotExist) { return trace.Wrap(err, "failed to remove %s", filepath.Base(p)) } @@ -336,20 +354,25 @@ func (ns *Namespace) writeConfigFiles(ctx context.Context, path string) error { Path: path, UpdaterConfigFile: filepath.Join(ns.Dir(), updateConfigName), } - err := writeSystemTemplate(ns.updaterServiceFile, updateServiceTemplate, params) - if err != nil { - return trace.Wrap(err) - } - err = writeSystemTemplate(ns.updaterTimerFile, updateTimerTemplate, params) - if err != nil { - return trace.Wrap(err) - } - err = writeSystemTemplate(ns.dropInFile, teleportDropInTemplate, params) - if err != nil { - return trace.Wrap(err) + + for _, v := range []struct { + path, tmpl string + }{ + {ns.updaterServiceFile, updateServiceTemplate}, + {ns.updaterTimerFile, updateTimerTemplate}, + {ns.teleportDropInFile, teleportDropInTemplate}, + {ns.deprecatedDropInFile, deprecatedDropInTemplate}, + } { + if v.path == "" { + continue + } + err := writeSystemTemplate(v.path, v.tmpl, params) + if err != nil { + return trace.Wrap(err) + } } // Needrestart config is non-critical for updater functionality. - _, err = os.Stat(filepath.Dir(ns.needrestartConfFile)) + _, err := os.Stat(filepath.Dir(ns.needrestartConfFile)) if os.IsNotExist(err) { return nil // needrestart is not present } diff --git a/lib/autoupdate/agent/setup_test.go b/lib/autoupdate/agent/setup_test.go index 1a1b6778dfeeb..5a32b9e49a1fb 100644 --- a/lib/autoupdate/agent/setup_test.go +++ b/lib/autoupdate/agent/setup_test.go @@ -45,34 +45,36 @@ func TestNewNamespace(t *testing.T) { { name: "no namespace", ns: &Namespace{ - dataDir: "/var/lib/teleport", - installDir: "/opt/teleport", - defaultPathDir: "/usr/local/bin", - serviceFile: "/lib/systemd/system/teleport.service", - configFile: "/etc/teleport.yaml", - pidFile: "/run/teleport.pid", - updaterIDFile: "/TMP/teleport-update.id", - updaterServiceFile: "/etc/systemd/system/teleport-update.service", - updaterTimerFile: "/etc/systemd/system/teleport-update.timer", - dropInFile: "/etc/systemd/system/teleport.service.d/teleport-update.conf", - needrestartConfFile: "/etc/needrestart/conf.d/teleport-update.conf", + dataDir: "/var/lib/teleport", + installDir: "/opt/teleport", + defaultPathDir: "/usr/local/bin", + serviceFile: "/lib/systemd/system/teleport.service", + configFile: "/etc/teleport.yaml", + pidFile: "/run/teleport.pid", + updaterIDFile: "/TMP/teleport-update.id", + updaterServiceFile: "/etc/systemd/system/teleport-update.service", + updaterTimerFile: "/etc/systemd/system/teleport-update.timer", + teleportDropInFile: "/etc/systemd/system/teleport.service.d/teleport-update.conf", + deprecatedDropInFile: "/etc/systemd/system/teleport-upgrade.service.d/teleport-update.conf", + needrestartConfFile: "/etc/needrestart/conf.d/teleport-update.conf", }, }, { name: "no namespace with dirs", installDir: "/install", ns: &Namespace{ - dataDir: "/var/lib/teleport", - installDir: "/install", - defaultPathDir: "/usr/local/bin", - serviceFile: "/lib/systemd/system/teleport.service", - configFile: "/etc/teleport.yaml", - pidFile: "/run/teleport.pid", - updaterIDFile: "/TMP/teleport-update.id", - updaterServiceFile: "/etc/systemd/system/teleport-update.service", - updaterTimerFile: "/etc/systemd/system/teleport-update.timer", - dropInFile: "/etc/systemd/system/teleport.service.d/teleport-update.conf", - needrestartConfFile: "/etc/needrestart/conf.d/teleport-update.conf", + dataDir: "/var/lib/teleport", + installDir: "/install", + defaultPathDir: "/usr/local/bin", + serviceFile: "/lib/systemd/system/teleport.service", + configFile: "/etc/teleport.yaml", + pidFile: "/run/teleport.pid", + updaterIDFile: "/TMP/teleport-update.id", + updaterServiceFile: "/etc/systemd/system/teleport-update.service", + updaterTimerFile: "/etc/systemd/system/teleport-update.timer", + teleportDropInFile: "/etc/systemd/system/teleport.service.d/teleport-update.conf", + deprecatedDropInFile: "/etc/systemd/system/teleport-upgrade.service.d/teleport-update.conf", + needrestartConfFile: "/etc/needrestart/conf.d/teleport-update.conf", }, }, { @@ -89,7 +91,7 @@ func TestNewNamespace(t *testing.T) { updaterIDFile: "/TMP/teleport-update_test.id", updaterServiceFile: "/etc/systemd/system/teleport-update_test.service", updaterTimerFile: "/etc/systemd/system/teleport-update_test.timer", - dropInFile: "/etc/systemd/system/teleport_test.service.d/teleport-update_test.conf", + teleportDropInFile: "/etc/systemd/system/teleport_test.service.d/teleport-update_test.conf", needrestartConfFile: "/etc/needrestart/conf.d/teleport-update_test.conf", }, }, @@ -108,7 +110,7 @@ func TestNewNamespace(t *testing.T) { updaterIDFile: "/TMP/teleport-update_test.id", updaterServiceFile: "/etc/systemd/system/teleport-update_test.service", updaterTimerFile: "/etc/systemd/system/teleport-update_test.timer", - dropInFile: "/etc/systemd/system/teleport_test.service.d/teleport-update_test.conf", + teleportDropInFile: "/etc/systemd/system/teleport_test.service.d/teleport-update_test.conf", needrestartConfFile: "/etc/needrestart/conf.d/teleport-update_test.conf", }, }, @@ -161,10 +163,12 @@ func TestWriteConfigFiles(t *testing.T) { ctx := context.Background() ns, err := NewNamespace(ctx, log, p.namespace, "") require.NoError(t, err) - ns.updaterServiceFile = filepath.Join(linkDir, serviceDir, filepath.Base(ns.updaterServiceFile)) - ns.updaterTimerFile = filepath.Join(linkDir, serviceDir, filepath.Base(ns.updaterTimerFile)) - ns.dropInFile = filepath.Join(linkDir, serviceDir, filepath.Base(filepath.Dir(ns.dropInFile)), filepath.Base(ns.dropInFile)) - ns.needrestartConfFile = filepath.Join(linkDir, filepath.Base(ns.dropInFile)) + ns.updaterServiceFile = rebasePath(filepath.Join(linkDir, serviceDir), filepath.Base(ns.updaterServiceFile)) + ns.updaterServiceFile = rebasePath(filepath.Join(linkDir, serviceDir), ns.updaterServiceFile) + ns.updaterTimerFile = rebasePath(filepath.Join(linkDir, serviceDir), ns.updaterTimerFile) + ns.teleportDropInFile = rebasePath(filepath.Join(linkDir, serviceDir, filepath.Base(filepath.Dir(ns.teleportDropInFile))), ns.teleportDropInFile) + ns.deprecatedDropInFile = rebasePath(filepath.Join(linkDir, serviceDir, filepath.Base(filepath.Dir(ns.deprecatedDropInFile))), ns.deprecatedDropInFile) + ns.needrestartConfFile = rebasePath(linkDir, filepath.Base(ns.needrestartConfFile)) err = ns.writeConfigFiles(ctx, linkDir) require.NoError(t, err) @@ -174,9 +178,13 @@ func TestWriteConfigFiles(t *testing.T) { }{ {name: "service", path: ns.updaterServiceFile}, {name: "timer", path: ns.updaterTimerFile}, - {name: "dropin", path: ns.dropInFile}, + {name: "dropin", path: ns.teleportDropInFile}, + {name: "deprecated", path: ns.deprecatedDropInFile}, {name: "needrestart", path: ns.needrestartConfFile}, } { + if tt.path == "" { + continue + } t.Run(tt.name, func(t *testing.T) { data, err := os.ReadFile(tt.path) require.NoError(t, err) @@ -193,6 +201,13 @@ func TestWriteConfigFiles(t *testing.T) { } } +func rebasePath(newBase, oldPath string) string { + if oldPath == "" { + return "" + } + return filepath.Join(newBase, filepath.Base(oldPath)) +} + func replaceValues(data []byte, m map[string]string) []byte { for k, v := range m { data = bytes.ReplaceAll(data, []byte(v), []byte(k)) diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/no_namespace/deprecated.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/no_namespace/deprecated.golden new file mode 100644 index 0000000000000..3f18b9cdf3065 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/no_namespace/deprecated.golden @@ -0,0 +1,5 @@ +# teleport-update +# DO NOT EDIT THIS FILE +[Service] +ExecStart= +ExecStart=-/bin/echo "The teleport-upgrade script has been disabled by teleport-update. Please remove the teleport-ent-updater package." diff --git a/lib/service/service.go b/lib/service/service.go index 200826799a799..10249520a1a35 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -1164,23 +1164,7 @@ func NewTeleport(cfg *servicecfg.Config) (*TeleportProcess, error) { switch upgraderKind { case types.UpgraderKindTeleportUpdate: - isDefault, err := autoupdate.IsManagedAndDefault() - if err != nil { - return nil, trace.Wrap(err) - } - if !isDefault { - // Only write the nop schedule for the default updater. - // Suffixed installations of Teleport can coexist with the old upgrader system. - break - } - driver, err := uw.NewSystemdUnitDriver(uw.SystemdUnitDriverConfig{}) - if err != nil { - return nil, trace.Wrap(err) - } - if err := driver.ForceNop(process.ExitContext()); err != nil { - process.logger.WarnContext(process.ExitContext(), "Unable to disable the teleport-upgrade command provided by the deprecated teleport-ent-updater package.", "error", err) - process.logger.WarnContext(process.ExitContext(), "If the deprecated teleport-ent-updater package is installed, please ensure /etc/teleport-upgrade.d/schedule contains 'nop'.") - } + // Exports are not required for teleport-update case types.UpgraderKindSystemdUnit: process.RegisterFunc("autoupdates.endpoint.export", func() error { conn, err := waitForInstanceConnector(process, process.logger) diff --git a/lib/versioncontrol/upgradewindow/upgradewindow.go b/lib/versioncontrol/upgradewindow/upgradewindow.go index 8799b2acb09e5..5ce15c3f57a2e 100644 --- a/lib/versioncontrol/upgradewindow/upgradewindow.go +++ b/lib/versioncontrol/upgradewindow/upgradewindow.go @@ -45,9 +45,6 @@ const ( // unitScheduleFile is the name of the file to which the unit schedule is exported. unitScheduleFile = "schedule" - - // scheduleNop is the name of the no-op schedule. - scheduleNop = "nop" ) // ExportFunc represents the ExportUpgradeWindows rpc exposed by auth servers. @@ -317,12 +314,6 @@ type Driver interface { // called if teleport experiences prolonged loss of auth connectivity, which may be an indicator // that the control plane has been upgraded s.t. this agent is no longer compatible. Reset(ctx context.Context) error - - // ForceNop sets the NOP schedule, ensuring that updates do not happen. - // This schedule was originally only used for testing, but now it is also used by the - // teleport-update binary to protect against package updates that could interfere with - // the new update system. - ForceNop(ctx context.Context) error } // NewDriver sets up a new export driver corresponding to the specified upgrader kind. @@ -374,10 +365,6 @@ func (e *kubeDriver) Sync(ctx context.Context, rsp proto.ExportUpgradeWindowsRes return trace.Wrap(e.setSchedule(ctx, rsp.KubeControllerSchedule)) } -func (e *kubeDriver) ForceNop(ctx context.Context) error { - return trace.Wrap(e.setSchedule(ctx, scheduleNop)) -} - func (e *kubeDriver) setSchedule(ctx context.Context, schedule string) error { if schedule == "" { return e.Reset(ctx) @@ -428,10 +415,6 @@ func (e *systemdDriver) Sync(ctx context.Context, rsp proto.ExportUpgradeWindows return trace.Wrap(e.setSchedule(ctx, rsp.SystemdUnitSchedule)) } -func (e *systemdDriver) ForceNop(ctx context.Context) error { - return trace.Wrap(e.setSchedule(ctx, scheduleNop)) -} - func (e *systemdDriver) setSchedule(ctx context.Context, schedule string) error { if len(schedule) == 0 { // treat an empty schedule value as equivalent to a reset diff --git a/lib/versioncontrol/upgradewindow/upgradewindow_test.go b/lib/versioncontrol/upgradewindow/upgradewindow_test.go index 19aff9748d20c..c5c47c91b938b 100644 --- a/lib/versioncontrol/upgradewindow/upgradewindow_test.go +++ b/lib/versioncontrol/upgradewindow/upgradewindow_test.go @@ -27,7 +27,6 @@ import ( "testing" "time" - "github.com/gravitational/trace" "github.com/stretchr/testify/require" "github.com/gravitational/teleport/api/client/proto" @@ -183,37 +182,6 @@ func TestSystemdUnitDriver(t *testing.T) { require.Equal(t, "", string(sb)) } -// TestSystemdUnitDriverNop verifies the nop schedule behavior of the systemd unit export driver. -func TestSystemdUnitDriverNop(t *testing.T) { - t.Parallel() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - // use a sub-directory of a temp dir in order to verify that - // driver creates dir when needed. - dir := filepath.Join(t.TempDir(), "config") - - driver, err := NewSystemdUnitDriver(SystemdUnitDriverConfig{ - ConfigDir: dir, - }) - require.NoError(t, err) - - err = driver.Sync(ctx, proto.ExportUpgradeWindowsResponse{ - SystemdUnitSchedule: "fake-schedule", - }) - require.NoError(t, err) - - err = driver.ForceNop(ctx) - require.NoError(t, err) - - schedPath := filepath.Join(dir, "schedule") - sb, err := os.ReadFile(schedPath) - require.NoError(t, err) - - require.Equal(t, scheduleNop, string(sb)) -} - // fakeDriver is used to inject custom behavior into a dummy Driver instance. type fakeDriver struct { mu sync.Mutex @@ -241,10 +209,6 @@ func (d *fakeDriver) Sync(ctx context.Context, rsp proto.ExportUpgradeWindowsRes return nil } -func (d *fakeDriver) ForceNop(ctx context.Context) error { - return trace.NotImplemented("force-nop not used by exporter") -} - func (d *fakeDriver) Reset(ctx context.Context) error { d.mu.Lock() defer d.mu.Unlock()