Skip to content

Commit

Permalink
[v17] RFD 184: Agent Automatic Updates, teleport-update (#52372)
Browse files Browse the repository at this point in the history
* [teleport-update] Add linking into /usr/local (#47879)

* clean up download logic

* Finish installer tests

* fips and ent support

* feedback

* move enterprise/fips to webapi

* wip

* wip2

* add cleanup

* fix extract

* wip

* fix tests

* remove safety

* cleanup

* cleanup extract

* cleanup

* cleanup

* fix bugs

* cleanup

* [teleport-update] Use new webapi fields to find version (#47961)

* Adapt teleport-update to new webapi endpoints

* feedback

* [teleport-update] Add support for reloading the agent & reverting symlinks on failed reload (#47929)

* wip

* cleanup

* comments

* test wip

* test link revert

* tests

* cleanup

* cleanup more

* comments

* comments

* errors

* comments

* linting

* fix bugs

* fix typo

* cleanup

* cleanup

* fix revert

* lint

* feedback

* fix

* fix test

* clarify comment

* use afterfunc

* [teleport-update] Add update subcommand (#48244)

* Add update subcommand

* fix

* lint

* add command

* warn on known edition

* warn on unknown edition for update

* [teleport-update] Add link subcommand (#48712)

* wip

* refactor

* docs

* updater

* add link command

* test LinkPackage

* cleanup

* fix enterprise paths

* fix systemd linking

* typo

* comment

* comments

* typo

* feedback

* adjust systemd service locations

* cleanup tests, adjust service link path

* [teleport-update] PID-based failure detection and rollback (#49175)

* Extract from other PR

* comments

* string

* [teleport-update] Add systemd setup (#49174)

* service and timer

* comments

* feedback

* feedback

* [teleport-update] Add unlink-package command (#49250)

* unlink

* test

* lock type

* comments

* cleanup

* Update lib/autoupdate/agent/installer.go

Co-authored-by: Hugo Shaka <[email protected]>

---------

Co-authored-by: Hugo Shaka <[email protected]>

* [teleport-update] Add support for version pinning (#49307)

* pinning

* cleanup

* unskip

* cleanup

* unpin

* typo

* [teleport-update] status subcommand (#49308)

* status

* cleanup

* comments

* cleanup output by removing optional fields

* rebase fix

* [teleport-update] Uninstall subcommand (#49341)

* Uninstall

* tests

* comment

* Short-circuit link package on pinned

* log

* move error

* Update lib/autoupdate/agent/process.go

Co-authored-by: Hugo Shaka <[email protected]>

* Update lib/autoupdate/agent/process.go

Co-authored-by: Hugo Shaka <[email protected]>

* Update lib/autoupdate/agent/process.go

Co-authored-by: Hugo Shaka <[email protected]>

* Update lib/autoupdate/agent/process.go

Co-authored-by: Hugo Shaka <[email protected]>

* fix

---------

Co-authored-by: Hugo Shaka <[email protected]>

* [teleport-update] Protect against disk space leaks (#49309)

* cleanup unused

* cleanup

* cleanup

* [teleport-update] Show warning instead of return error for link/unlink (#49334)

* Add warning instead of return error for link/unlink

* Add test for sync call with ErrNotSupported

* Change warning message

* [teleport-update] Isolated installation suffix (#49364)

* namespacing

* words

* cli

* fix

* err

* use structured logs consistently

* comments

* bugs

* test

* switch to new paths

* test

* adjust

* reserved

* cleanup

* cleanup

* docs

* fix uninstall

* test

* simplify init

* cleanup

* namespace -> install-suffix

* log

* [teleport-update] Fix usage of trace (#49388)

* fix trace

* rebase

* [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 <[email protected]>

feedback

* feedback

* feedback

* lint

* [teleport-update] Display download progress and stats (#49805)

* download progress

* typo

* sub -> since

* time -> duration

* [teleport-update] update --now (#49807)

* update --now

* testdata

* [teleport-update] Adjust download progress log output (#49845)

* adjust logger

* fix

* fix

* Extended binary validations (#49748)

* [teleport-update] needrestart and systemd drop-in (#49806)

* wip

* Add more config

* nit

* feedback

* Fix duplicate teleport-update short command (#50304)

* [teleport-update] Version reporting and deprecated upgrader management (#50266)

* wip

* telemetry

* abs

* fix

* tests

* Disable deprecated timer

* keep schedule on non-suffixed

* Update maintenance.go

* Update lib/autoupdate/agent/setup.go

* update warnings

* feedback pt 1

* feedback pt 2

* headers

* [teleport-update] Remove warning when running Teleport on platforms without systemd (#51465)

* improve detection logic on non-systemd platforms

* adjust

* remove OS check

* [teleport-update] common MakeURL with ability to override BaseURL (#51383)

* Add templates for client tools auto-update download url

* Change to base url setting by env

MakeURL moved to common function to be general for both, agent and client tools

* Reuse MakeURL moved to common package

* Fix linter warning

* Add common env variable to override base url

* Remove template from interface

* Make template exported
Change a stale comment

* Remove unused code

* [teleport-update] Adjustments for SELinux (#51474)

* selinux fixes

* extra checks

* lint

* lint

* cleanup

* better cleanup

* fix rebase

* [teleport-update] Add --overwrite flag to replace tarball installations (#51579)

* add --overwrite flag

* extra warning

* [teleport-update] Only use CDN for community / enterprise editions (#51726)

* Only use CDN for community / enterprise

* wording

* [teleport-update] Warn instead of erroring when disabling the deprecated updater (#51759)

* Warn instead of erroring when disabling old updater

* Update lib/service/service.go

* Update lib/service/service.go

* [teleport-update] Adjust non-critical SELinux contexts (#51793)

* correct selinux contexts

* Update lib/autoupdate/agent/installer.go

Co-authored-by: rosstimothy <[email protected]>

* Update lib/autoupdate/agent/installer.go

---------

Co-authored-by: rosstimothy <[email protected]>

* [teleport-update] Add proper healthcheck for agents (#51613)

* Add socket readiness monitor

* cleanup

* add 404 check

* check

* better cleanup

* fix bug

* typo

* fix 404

* improve logging

* cleanup

* disable socket redirect

* avoid race condition with socket removal

* verify PID

* cleanup

* Update lib/autoupdate/agent/process.go

Co-authored-by: Edoardo Spadolini <[email protected]>

* feedback

* fix subtle race condition

* debugging

---------

Co-authored-by: Edoardo Spadolini <[email protected]>

* [teleport-update] Allow teleport-update uninstall to succeed with non-packaged installs (#51576)

* Treat missing source bin dir same as missing binaries

* prevent linking package outside /usr/local/bin

* Apply suggestions from code review

* [teleport-update] use new updater to reload and verify Teleport (#51734)

* wip

* finish implementation

* fix tests

* test setup

* remove stale data

* bug

* spelling

* pass log format and debug through

* feedback

* [teleport-update] Read proxy from teleport.yaml to improve UX (#51633)

* derive proxy from config

* fix parsing

* cleanup

* require force for uninstall (#51973)

* [teleport-update] add insecure flag for testing (#52019)

* insecure flag

* fmt

* [teleport-update] skip updater setup when systemd is missing (#52022)

* skip updater installation when systemd is missing

* test

* wording

* [teleport-update] Ensure stable interface between versions of teleport-update (#52152)

* refactor data dir

* finish refactor

* fix path

* cleanup

* more tests

* lint

* prevent notice failure without systemd

* feedback

* url

* revert log level change (#52416)

---------

Co-authored-by: Hugo Shaka <[email protected]>
Co-authored-by: Vadym Popov <[email protected]>
Co-authored-by: rosstimothy <[email protected]>
Co-authored-by: Edoardo Spadolini <[email protected]>
  • Loading branch information
5 people authored Feb 27, 2025
1 parent 4c6cee5 commit 5ab4ff6
Show file tree
Hide file tree
Showing 82 changed files with 8,383 additions and 523 deletions.
6 changes: 5 additions & 1 deletion api/types/maintenance.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,17 @@ import (
)

const (
// UpgraderKindKuberController is a short name used to identify the kube-controller-based
// UpgraderKindKubeController is a short name used to identify the kube-controller-based
// external upgrader variant.
UpgraderKindKubeController = "kube"

// UpgraderKindSystemdUnit is a short name used to identify the systemd-unit-based
// external upgrader variant.
UpgraderKindSystemdUnit = "unit"

// UpgraderKindTeleportUpdate is a short name used to identify the teleport-update
// external upgrader variant.
UpgraderKindTeleportUpdate = "binary"
)

var validWeekdays = [7]time.Weekday{
Expand Down
2 changes: 1 addition & 1 deletion constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,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"

// ComponentGit represents git proxy related services.
Expand Down
2 changes: 1 addition & 1 deletion lib/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -6858,7 +6858,7 @@ func (a *Server) ExportUpgradeWindows(ctx context.Context, req proto.ExportUpgra
}

switch req.UpgraderKind {
case "":
case "", types.UpgraderKindTeleportUpdate:
rsp.CanonicalSchedule = cached.CanonicalSchedule.Clone()
case types.UpgraderKindKubeController:
rsp.KubeControllerSchedule = cached.KubeControllerSchedule
Expand Down
251 changes: 251 additions & 0 deletions lib/autoupdate/agent/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/

package agent

import (
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
"time"

"github.com/google/renameio/v2"
"github.com/gravitational/trace"
"gopkg.in/yaml.v3"

"github.com/gravitational/teleport/lib/autoupdate"
)

const (
// updateConfigName specifies the name of the file inside versionsDirName containing configuration for the teleport update.
updateConfigName = "update.yaml"

// UpdateConfig metadata
updateConfigVersion = "v1"
updateConfigKind = "update_config"
)

// UpdateConfig describes the update.yaml file schema.
type UpdateConfig struct {
// Version of the configuration file
Version string `yaml:"version"`
// Kind of configuration file (always "update_config")
Kind string `yaml:"kind"`
// Spec contains user-specified configuration.
Spec UpdateSpec `yaml:"spec"`
// Status contains state configuration.
Status UpdateStatus `yaml:"status"`
}

// UpdateSpec describes the spec field in update.yaml.
type UpdateSpec struct {
// Proxy address
Proxy string `yaml:"proxy"`
// Path is the location the Teleport binaries are linked into.
Path string `yaml:"path"`
// Group specifies the update group identifier for the agent.
Group string `yaml:"group,omitempty"`
// BaseURL is CDN base URL used for the Teleport tgz download URL.
BaseURL string `yaml:"base_url,omitempty"`
// Enabled controls whether auto-updates are enabled.
Enabled bool `yaml:"enabled"`
// Pinned controls whether the active_version is pinned.
Pinned bool `yaml:"pinned"`
}

// UpdateStatus describes the status field in update.yaml.
type UpdateStatus struct {
// 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 autoupdate.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 autoupdate.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] != autoupdate.FlagFIPS.DirFlag() {
break
}
out.Flags |= autoupdate.FlagFIPS
fallthrough
case 1:
if flags[0] != autoupdate.FlagEnterprise.DirFlag() {
break
}
out.Flags |= autoupdate.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&(autoupdate.FlagEnterprise|autoupdate.FlagFIPS) != 0 {
suffix += "_" + autoupdate.FlagEnterprise.DirFlag()
}
if r.Flags&autoupdate.FlagFIPS != 0 {
suffix += "_" + autoupdate.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.
func readConfig(path string) (*UpdateConfig, error) {
f, err := os.Open(path)
if errors.Is(err, fs.ErrNotExist) {
return &UpdateConfig{
Version: updateConfigVersion,
Kind: updateConfigKind,
}, nil
}
if err != nil {
return nil, trace.Wrap(err, "failed to open")
}
defer f.Close()
var cfg UpdateConfig
if err := yaml.NewDecoder(f).Decode(&cfg); err != nil {
return nil, trace.Wrap(err, "failed to parse")
}
if k := cfg.Kind; k != updateConfigKind {
return nil, trace.Errorf("invalid kind %s", k)
}
if v := cfg.Version; v != updateConfigVersion {
return nil, trace.Errorf("invalid version %s", v)
}
return &cfg, nil
}

// writeConfig writes UpdateConfig to a file atomically, ensuring the file cannot be corrupted.
func writeConfig(filename string, cfg *UpdateConfig) error {
opts := []renameio.Option{
renameio.WithPermissions(configFileMode),
renameio.WithExistingPermissions(),
renameio.WithTempDir(filepath.Dir(filename)),
}
t, err := renameio.NewPendingFile(filename, opts...)
if err != nil {
return trace.Wrap(err)
}
defer t.Cleanup()
err = yaml.NewEncoder(t).Encode(cfg)
if err != nil {
return trace.Wrap(err)
}
return trace.Wrap(t.CloseAtomicallyReplace())
}

func validateConfigSpec(spec *UpdateSpec, override OverrideConfig) error {
if override.Proxy != "" {
spec.Proxy = override.Proxy
}
if override.Path != "" {
spec.Path = override.Path
}
if override.Group != "" {
spec.Group = override.Group
}
switch override.BaseURL {
case "":
case "default":
spec.BaseURL = ""
default:
spec.BaseURL = override.BaseURL
}
if spec.BaseURL != "" &&
!strings.HasPrefix(strings.ToLower(spec.BaseURL), "https://") {
return trace.Errorf("Teleport download base URL %s must use TLS (https://)", spec.BaseURL)
}
if override.Enabled {
spec.Enabled = true
}
if override.Pinned {
spec.Pinned = true
}
return nil
}

// Status of the agent auto-updates system.
type Status struct {
UpdateSpec `yaml:",inline"`
UpdateStatus `yaml:",inline"`
FindResp `yaml:",inline"`
}

// FindResp summarizes the auto-update status response from cluster.
type FindResp struct {
// 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
Jitter time.Duration `yaml:"jitter"`
// AGPL installations cannot use the official CDN.
AGPL bool `yaml:"agpl,omitempty"`
}
Loading

0 comments on commit 5ab4ff6

Please sign in to comment.