diff --git a/lib/autoupdate/agent/config.go b/lib/autoupdate/agent/config.go index 2fcc3b5404d0f..079d08ef57df0 100644 --- a/lib/autoupdate/agent/config.go +++ b/lib/autoupdate/agent/config.go @@ -19,7 +19,6 @@ package agent import ( - "encoding/json" "errors" "fmt" "io/fs" @@ -30,6 +29,8 @@ import ( "github.com/google/renameio/v2" "github.com/gravitational/trace" "gopkg.in/yaml.v3" + + "github.com/gravitational/teleport/lib/autoupdate" ) const ( @@ -59,8 +60,8 @@ type UpdateSpec struct { Proxy string `yaml:"proxy"` // Group specifies the update group identifier for the agent. Group string `yaml:"group,omitempty"` - // URLTemplate for the Teleport tgz download URL. - URLTemplate string `yaml:"url_template,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. @@ -84,13 +85,13 @@ 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"` + 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 InstallFlags) Revision { +func NewRevision(version string, flags autoupdate.InstallFlags) Revision { if version != "" { return Revision{ Version: version, @@ -113,16 +114,16 @@ func NewRevisionFromDir(dir string) (Revision, error) { } switch flags := parts[1:]; len(flags) { case 2: - if flags[1] != FlagFIPS.DirFlag() { + if flags[1] != autoupdate.FlagFIPS.DirFlag() { break } - out.Flags |= FlagFIPS + out.Flags |= autoupdate.FlagFIPS fallthrough case 1: - if flags[0] != FlagEnterprise.DirFlag() { + if flags[0] != autoupdate.FlagEnterprise.DirFlag() { break } - out.Flags |= FlagEnterprise + out.Flags |= autoupdate.FlagEnterprise fallthrough case 0: return out, nil @@ -135,11 +136,11 @@ 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&(autoupdate.FlagEnterprise|autoupdate.FlagFIPS) != 0 { + suffix += "_" + autoupdate.FlagEnterprise.DirFlag() } - if r.Flags&FlagFIPS != 0 { - suffix += "_" + FlagFIPS.DirFlag() + if r.Flags&autoupdate.FlagFIPS != 0 { + suffix += "_" + autoupdate.FlagFIPS.DirFlag() } return r.Version + suffix } @@ -203,16 +204,16 @@ func validateConfigSpec(spec *UpdateSpec, override OverrideConfig) error { if override.Group != "" { spec.Group = override.Group } - switch override.URLTemplate { + switch override.BaseURL { case "": case "default": - spec.URLTemplate = "" + spec.BaseURL = "" default: - spec.URLTemplate = override.URLTemplate + spec.BaseURL = override.BaseURL } - if spec.URLTemplate != "" && - !strings.HasPrefix(strings.ToLower(spec.URLTemplate), "https://") { - return trace.Errorf("Teleport download URL must use TLS (https://)") + 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 @@ -239,89 +240,3 @@ type FindResp struct { // Jitter duration before an automated install Jitter time.Duration `yaml:"jitter"` } - -// InstallFlags sets flags for the Teleport installation -type InstallFlags int - -const ( - // FlagEnterprise installs enterprise Teleport - FlagEnterprise InstallFlags = 1 << iota - // FlagFIPS installs FIPS Teleport - FlagFIPS -) - -// 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{ - FlagEnterprise, - FlagFIPS, - } { - if i&flag != 0 { - out = append(out, flag.String()) - } - } - return out -} - -// String returns the string representation of a single InstallFlag flag, or "Unknown". -func (i InstallFlags) String() string { - switch i { - case 0: - return "" - case FlagEnterprise: - return "Enterprise" - case FlagFIPS: - return "FIPS" - } - 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 index 0f3ce770ec7af..39d318cd6ee4c 100644 --- a/lib/autoupdate/agent/config_test.go +++ b/lib/autoupdate/agent/config_test.go @@ -22,7 +22,8 @@ import ( "testing" "github.com/stretchr/testify/require" - "gopkg.in/yaml.v3" + + "github.com/gravitational/teleport/lib/autoupdate" ) func TestNewRevisionFromDir(t *testing.T) { @@ -46,7 +47,7 @@ func TestNewRevisionFromDir(t *testing.T) { dir: "1.2.3_ent_fips", rev: Revision{ Version: "1.2.3", - Flags: FlagEnterprise | FlagFIPS, + Flags: autoupdate.FlagEnterprise | autoupdate.FlagFIPS, }, }, { @@ -54,7 +55,7 @@ func TestNewRevisionFromDir(t *testing.T) { dir: "1.2.3_ent", rev: Revision{ Version: "1.2.3", - Flags: FlagEnterprise, + Flags: autoupdate.FlagEnterprise, }, }, { @@ -124,72 +125,3 @@ func TestNewRevisionFromDir(t *testing.T) { }) } } - -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 f62da824c7c5b..370dcb58ad3df 100644 --- a/lib/autoupdate/agent/installer.go +++ b/lib/autoupdate/agent/installer.go @@ -31,15 +31,14 @@ import ( "os" "path" "path/filepath" - "runtime" "syscall" - "text/template" "time" "github.com/google/renameio/v2" "github.com/gravitational/trace" "github.com/gravitational/teleport" + "github.com/gravitational/teleport/lib/autoupdate" "github.com/gravitational/teleport/lib/utils" ) @@ -89,6 +88,8 @@ type LocalInstaller struct { // ValidateBinary returns true if a file is a linkable binary, or // false if a file should not be linked. ValidateBinary func(ctx context.Context, path string) (bool, error) + // Template is download URI Template of Teleport packages. + Template string } // Remove a Teleport version directory from InstallDir. @@ -127,15 +128,15 @@ func (li *LocalInstaller) Remove(ctx context.Context, rev Revision) 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, rev Revision, template string) (err error) { +func (li *LocalInstaller) Install(ctx context.Context, rev Revision, baseURL 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, rev) + // generate download URI from Template + uri, err := autoupdate.MakeURL(li.Template, baseURL, autoupdate.DefaultPackage, rev.Version, rev.Flags) if err != nil { return trace.Wrap(err) } @@ -229,30 +230,6 @@ func (li *LocalInstaller) Install(ctx context.Context, rev Revision, template st return nil } -// makeURL to download the Teleport tgz. -func makeURL(uriTmpl string, rev Revision) (string, error) { - tmpl, err := template.New("uri").Parse(uriTmpl) - if err != nil { - return "", trace.Wrap(err) - } - var uriBuf bytes.Buffer - params := struct { - OS, Version, Arch string - FIPS, Enterprise bool - }{ - OS: runtime.GOOS, - Version: rev.Version, - Arch: runtime.GOARCH, - FIPS: rev.Flags&FlagFIPS != 0, - Enterprise: rev.Flags&(FlagEnterprise|FlagFIPS) != 0, - } - err = tmpl.Execute(&uriBuf, params) - if err != nil { - return "", trace.Wrap(err) - } - return uriBuf.String(), nil -} - // readChecksum from the version directory. func readChecksum(path string) ([]byte, error) { f, err := os.Open(path) @@ -354,7 +331,7 @@ func (li *LocalInstaller) download(ctx context.Context, w io.Writer, max int64, return shaReader.Sum(nil), nil } -func (li *LocalInstaller) extract(ctx context.Context, dstDir string, src io.Reader, max int64, flags InstallFlags) error { +func (li *LocalInstaller) extract(ctx context.Context, dstDir string, src io.Reader, max int64, flags autoupdate.InstallFlags) error { if err := os.MkdirAll(dstDir, systemDirMode); err != nil { return trace.Wrap(err) } @@ -372,7 +349,7 @@ func (li *LocalInstaller) extract(ctx context.Context, dstDir string, src io.Rea } li.Log.InfoContext(ctx, "Extracting Teleport tarball.", "path", dstDir, "size", max) - err = utils.Extract(zr, dstDir, tgzExtractPaths(flags&(FlagEnterprise|FlagFIPS) != 0)...) + err = utils.Extract(zr, dstDir, tgzExtractPaths(flags&(autoupdate.FlagEnterprise|autoupdate.FlagFIPS) != 0)...) if err != nil { return trace.Wrap(err) } diff --git a/lib/autoupdate/agent/installer_test.go b/lib/autoupdate/agent/installer_test.go index 01dad06750cc6..ddc950eabcfde 100644 --- a/lib/autoupdate/agent/installer_test.go +++ b/lib/autoupdate/agent/installer_test.go @@ -39,6 +39,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/lib/autoupdate" ) func TestLocalInstaller_Install(t *testing.T) { @@ -52,7 +54,7 @@ func TestLocalInstaller_Install(t *testing.T) { reservedTmp uint64 reservedInstall uint64 existingSum string - flags InstallFlags + flags autoupdate.InstallFlags errMatch string }{ @@ -122,9 +124,10 @@ func TestLocalInstaller_Install(t *testing.T) { Log: slog.Default(), ReservedFreeTmpDisk: tt.reservedTmp, ReservedFreeInstallDisk: tt.reservedInstall, + Template: "{{.BaseURL}}/{{.Package}}-{{.OS}}/{{.Arch}}/{{.Version}}", } ctx := context.Background() - err := installer.Install(ctx, NewRevision(version, tt.flags), server.URL+"/{{.OS}}/{{.Arch}}/{{.Version}}") + err := installer.Install(ctx, NewRevision(version, tt.flags), server.URL) if tt.errMatch != "" { require.Error(t, err) assert.Contains(t, err.Error(), tt.errMatch) @@ -132,7 +135,7 @@ func TestLocalInstaller_Install(t *testing.T) { } require.NoError(t, err) - const expectedPath = "/" + runtime.GOOS + "/" + runtime.GOARCH + "/" + version + const expectedPath = "/teleport-" + runtime.GOOS + "/" + runtime.GOARCH + "/" + version require.Equal(t, expectedPath, dlPath) require.Equal(t, expectedPath+"."+checksumType, shaPath) @@ -406,6 +409,7 @@ func TestLocalInstaller_Link(t *testing.T) { return []byte("[transform]" + string(b)) }, ValidateBinary: validator.IsExecutable, + Template: autoupdate.DefaultCDNURITemplate, } ctx := context.Background() revert, err := installer.Link(ctx, NewRevision(version, 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 66e2695c2fd3a..490190b7416a0 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file.golden @@ -3,7 +3,7 @@ kind: update_config spec: proxy: localhost group: group - url_template: https://example.com + base_url: https://example.com enabled: true pinned: false status: 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 801e0b280f29c..96c6ded1a1488 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user.golden @@ -3,7 +3,7 @@ kind: update_config spec: proxy: localhost group: new-group - url_template: https://example.com/new + base_url: https://example.com/new enabled: true pinned: false status: diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL.golden index 61d99f3d18e5f..a519e9c172145 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL.golden @@ -2,7 +2,7 @@ version: v1 kind: update_config spec: proxy: "" - url_template: http://example.com + base_url: http://example.com enabled: false pinned: false status: 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 171b474129666..e56f19601af20 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 @@ -2,7 +2,7 @@ version: v1 kind: update_config spec: proxy: localhost - url_template: https://example.com + base_url: https://example.com enabled: true pinned: false status: 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 1a55f77e33fb8..211898d93399b 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 @@ -2,7 +2,7 @@ version: v1 kind: update_config spec: proxy: localhost - url_template: https://example.com + base_url: https://example.com enabled: true pinned: false status: 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 e5ccf529d19b9..e389e204ea1ca 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 @@ -2,7 +2,7 @@ version: v1 kind: update_config spec: proxy: localhost - url_template: https://example.com + base_url: https://example.com enabled: true pinned: false status: diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL.golden index 899b48118505e..dda01ea1b39b5 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL.golden @@ -2,7 +2,7 @@ version: v1 kind: update_config spec: proxy: localhost - url_template: http://example.com + base_url: http://example.com enabled: true pinned: false status: diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version.golden index dfc451f108d8c..7e0b873786ce5 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version.golden @@ -2,7 +2,7 @@ version: v1 kind: update_config spec: proxy: localhost - url_template: https://example.com + base_url: https://example.com enabled: true pinned: true status: diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/reload_fails.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/reload_fails.golden index 7efb7d9e3cf1f..dbd1cd855be45 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/reload_fails.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/reload_fails.golden @@ -2,7 +2,7 @@ version: v1 kind: update_config spec: proxy: localhost - url_template: https://example.com + base_url: https://example.com enabled: true pinned: false status: diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails.golden index 7efb7d9e3cf1f..dbd1cd855be45 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails.golden @@ -2,7 +2,7 @@ version: v1 kind: update_config spec: proxy: localhost - url_template: https://example.com + base_url: https://example.com enabled: true pinned: false status: diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version.golden index 7efb7d9e3cf1f..dbd1cd855be45 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version.golden @@ -2,7 +2,7 @@ version: v1 kind: update_config spec: proxy: localhost - url_template: https://example.com + base_url: https://example.com enabled: true pinned: false status: 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 b797b2509f748..59d65fb02feed 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 @@ -3,7 +3,7 @@ kind: update_config spec: proxy: localhost group: group - url_template: https://example.com + base_url: https://example.com enabled: false pinned: false status: 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 b797b2509f748..59d65fb02feed 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 @@ -3,7 +3,7 @@ kind: update_config spec: proxy: localhost group: group - url_template: https://example.com + base_url: https://example.com enabled: false pinned: false status: 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 66e2695c2fd3a..490190b7416a0 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 @@ -3,7 +3,7 @@ kind: update_config spec: proxy: localhost group: group - url_template: https://example.com + base_url: https://example.com enabled: true pinned: false status: diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now.golden index 66e2695c2fd3a..490190b7416a0 100644 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now.golden +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now.golden @@ -3,7 +3,7 @@ kind: update_config spec: proxy: localhost group: group - url_template: https://example.com + base_url: https://example.com enabled: true pinned: false status: 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 05b6b150d9796..1a4abb498286a 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 @@ -3,7 +3,7 @@ kind: update_config spec: proxy: localhost group: group - url_template: https://example.com + base_url: https://example.com enabled: true pinned: false status: 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 b1e895fb2c2e4..20af7de827742 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 @@ -2,7 +2,7 @@ version: v1 kind: update_config spec: proxy: localhost - url_template: https://example.com + base_url: https://example.com enabled: true pinned: false status: 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 b1e895fb2c2e4..20af7de827742 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 @@ -2,7 +2,7 @@ version: v1 kind: update_config spec: proxy: localhost - url_template: https://example.com + base_url: https://example.com enabled: true pinned: false status: diff --git a/lib/autoupdate/agent/updater.go b/lib/autoupdate/agent/updater.go index cc95048b11b50..f9c2f2bb22d32 100644 --- a/lib/autoupdate/agent/updater.go +++ b/lib/autoupdate/agent/updater.go @@ -36,6 +36,7 @@ import ( "github.com/gravitational/teleport/api/client/webclient" "github.com/gravitational/teleport/api/constants" + "github.com/gravitational/teleport/lib/autoupdate" libdefaults "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/modules" libutils "github.com/gravitational/teleport/lib/utils" @@ -51,8 +52,6 @@ const ( const ( // defaultSystemDir is the location where packaged Teleport binaries and services are installed. defaultSystemDir = "/opt/teleport/system" - // cdnURITemplate is the default template for the Teleport tgz download. - cdnURITemplate = "https://cdn.teleport.dev/teleport{{if .Enterprise}}-ent{{end}}-v{{.Version}}-{{.OS}}-{{.Arch}}{{if .FIPS}}-fips{{end}}-bin.tar.gz" // reservedFreeDisk is the minimum required free space left on disk during downloads. // TODO(sclevine): This value is arbitrary and could be replaced by, e.g., min(1%, 200mb) in the future // to account for a range of disk sizes. @@ -112,6 +111,7 @@ func NewLocalUpdater(cfg LocalUpdaterConfig, ns *Namespace) (*Updater, error) { ReservedFreeInstallDisk: reservedFreeDisk, TransformService: ns.replaceTeleportService, ValidateBinary: validator.IsBinary, + Template: autoupdate.DefaultCDNURITemplate, }, Process: &SystemdService{ ServiceName: filepath.Base(ns.serviceFile), @@ -183,9 +183,9 @@ type Updater struct { // Installer provides an API for installing Teleport agents. type Installer interface { - // Install the Teleport agent at revision from the download template. + // Install the Teleport agent at revision from the download Template. // Install must be idempotent. - Install(ctx context.Context, rev Revision, template string) error + Install(ctx context.Context, rev Revision, baseURL 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. @@ -262,7 +262,7 @@ type OverrideConfig struct { // ForceVersion to the specified version. ForceVersion string // ForceFlags in installed Teleport. - ForceFlags InstallFlags + ForceFlags autoupdate.InstallFlags } func deref[T any](ptr *T) T { @@ -604,16 +604,16 @@ func (u *Updater) find(ctx context.Context, cfg *UpdateConfig) (FindResp, error) if err != nil { return FindResp{}, trace.Wrap(err, "failed to request version from proxy") } - var flags InstallFlags + var flags autoupdate.InstallFlags switch resp.Edition { case modules.BuildEnterprise: - flags |= FlagEnterprise + flags |= autoupdate.FlagEnterprise case modules.BuildOSS, modules.BuildCommunity: default: u.Log.WarnContext(ctx, "Unknown edition detected, defaulting to community.", "edition", resp.Edition) } if resp.FIPS { - flags |= FlagFIPS + flags |= autoupdate.FlagFIPS } jitterSec := resp.AutoUpdate.AgentUpdateJitterSeconds return FindResp{ @@ -642,11 +642,11 @@ func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, target Revision // Install and link the desired version (or validate existing installation) - template := cfg.Spec.URLTemplate - if template == "" { - template = cdnURITemplate + baseURL := cfg.Spec.BaseURL + if baseURL == "" { + baseURL = autoupdate.DefaultBaseURL } - err := u.Installer.Install(ctx, target, template) + err := u.Installer.Install(ctx, target, baseURL) if err != nil { return trace.Wrap(err, "failed to install") } diff --git a/lib/autoupdate/agent/updater_test.go b/lib/autoupdate/agent/updater_test.go index d8981eb59fa17..f02c7773d5aa8 100644 --- a/lib/autoupdate/agent/updater_test.go +++ b/lib/autoupdate/agent/updater_test.go @@ -36,6 +36,7 @@ import ( "gopkg.in/yaml.v3" "github.com/gravitational/teleport/api/client/webclient" + "github.com/gravitational/teleport/lib/autoupdate" "github.com/gravitational/teleport/lib/utils/testutils/golden" ) @@ -221,7 +222,7 @@ func TestUpdater_Update(t *testing.T) { tests := []struct { name string cfg *UpdateConfig // nil -> file not present - flags InstallFlags + flags autoupdate.InstallFlags inWindow bool now bool installErr error @@ -230,7 +231,7 @@ func TestUpdater_Update(t *testing.T) { removedRevisions []Revision installedRevision Revision - installedTemplate string + installedBaseURL string linkedRevision Revision requestGroup string reloadCalls int @@ -244,9 +245,9 @@ func TestUpdater_Update(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Spec: UpdateSpec{ - Group: "group", - URLTemplate: "https://example.com", - Enabled: true, + Group: "group", + BaseURL: "https://example.com", + Enabled: true, }, Status: UpdateStatus{ Active: NewRevision("old-version", 0), @@ -256,7 +257,7 @@ func TestUpdater_Update(t *testing.T) { removedRevisions: []Revision{NewRevision("unknown-version", 0)}, installedRevision: NewRevision("16.3.0", 0), - installedTemplate: "https://example.com", + installedBaseURL: "https://example.com", linkedRevision: NewRevision("16.3.0", 0), requestGroup: "group", reloadCalls: 1, @@ -268,9 +269,9 @@ func TestUpdater_Update(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Spec: UpdateSpec{ - Group: "group", - URLTemplate: "https://example.com", - Enabled: true, + Group: "group", + BaseURL: "https://example.com", + Enabled: true, }, Status: UpdateStatus{ Active: NewRevision("old-version", 0), @@ -280,7 +281,7 @@ func TestUpdater_Update(t *testing.T) { removedRevisions: []Revision{NewRevision("unknown-version", 0)}, installedRevision: NewRevision("16.3.0", 0), - installedTemplate: "https://example.com", + installedBaseURL: "https://example.com", linkedRevision: NewRevision("16.3.0", 0), requestGroup: "group", reloadCalls: 1, @@ -292,9 +293,9 @@ func TestUpdater_Update(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Spec: UpdateSpec{ - Group: "group", - URLTemplate: "https://example.com", - Enabled: false, + Group: "group", + BaseURL: "https://example.com", + Enabled: false, }, Status: UpdateStatus{ Active: NewRevision("old-version", 0), @@ -308,9 +309,9 @@ func TestUpdater_Update(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Spec: UpdateSpec{ - Group: "group", - URLTemplate: "https://example.com", - Enabled: true, + Group: "group", + BaseURL: "https://example.com", + Enabled: true, }, Status: UpdateStatus{ Active: NewRevision("old-version", 0), @@ -324,9 +325,9 @@ func TestUpdater_Update(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Spec: UpdateSpec{ - Group: "group", - URLTemplate: "https://example.com", - Enabled: false, + Group: "group", + BaseURL: "https://example.com", + Enabled: false, }, Status: UpdateStatus{ Active: NewRevision("old-version", 0), @@ -339,13 +340,13 @@ func TestUpdater_Update(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Spec: UpdateSpec{ - URLTemplate: "http://example.com", - Enabled: true, + BaseURL: "http://example.com", + Enabled: true, }, }, inWindow: true, - errMatch: "URL must use TLS", + errMatch: "must use TLS", }, { name: "install error", @@ -360,7 +361,7 @@ func TestUpdater_Update(t *testing.T) { installErr: errors.New("install error"), installedRevision: NewRevision("16.3.0", 0), - installedTemplate: cdnURITemplate, + installedBaseURL: autoupdate.DefaultBaseURL, errMatch: "install error", }, { @@ -369,8 +370,8 @@ func TestUpdater_Update(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Spec: UpdateSpec{ - URLTemplate: "https://example.com", - Enabled: true, + BaseURL: "https://example.com", + Enabled: true, }, Status: UpdateStatus{ Active: NewRevision("16.3.0", 0), @@ -384,8 +385,8 @@ func TestUpdater_Update(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Spec: UpdateSpec{ - URLTemplate: "https://example.com", - Enabled: true, + BaseURL: "https://example.com", + Enabled: true, }, Status: UpdateStatus{ Active: NewRevision("16.3.0", 0), @@ -398,8 +399,8 @@ func TestUpdater_Update(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Spec: UpdateSpec{ - URLTemplate: "https://example.com", - Enabled: true, + BaseURL: "https://example.com", + Enabled: true, }, Status: UpdateStatus{ Active: NewRevision("old-version", 0), @@ -409,7 +410,7 @@ func TestUpdater_Update(t *testing.T) { inWindow: true, installedRevision: NewRevision("16.3.0", 0), - installedTemplate: "https://example.com", + installedBaseURL: "https://example.com", linkedRevision: NewRevision("16.3.0", 0), removedRevisions: []Revision{ NewRevision("backup-version", 0), @@ -424,8 +425,8 @@ func TestUpdater_Update(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Spec: UpdateSpec{ - URLTemplate: "https://example.com", - Enabled: true, + BaseURL: "https://example.com", + Enabled: true, }, Status: UpdateStatus{ Active: NewRevision("16.3.0", 0), @@ -443,22 +444,22 @@ func TestUpdater_Update(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Spec: UpdateSpec{ - URLTemplate: "https://example.com", - Enabled: true, + BaseURL: "https://example.com", + Enabled: true, }, Status: UpdateStatus{ - Active: NewRevision("old-version", FlagEnterprise|FlagFIPS), - Backup: toPtr(NewRevision("backup-version", FlagEnterprise|FlagFIPS)), + Active: NewRevision("old-version", autoupdate.FlagEnterprise|autoupdate.FlagFIPS), + Backup: toPtr(NewRevision("backup-version", autoupdate.FlagEnterprise|autoupdate.FlagFIPS)), }, }, inWindow: true, - flags: FlagEnterprise | FlagFIPS, + flags: autoupdate.FlagEnterprise | autoupdate.FlagFIPS, - installedRevision: NewRevision("16.3.0", FlagEnterprise|FlagFIPS), - installedTemplate: "https://example.com", - linkedRevision: NewRevision("16.3.0", FlagEnterprise|FlagFIPS), + installedRevision: NewRevision("16.3.0", autoupdate.FlagEnterprise|autoupdate.FlagFIPS), + installedBaseURL: "https://example.com", + linkedRevision: NewRevision("16.3.0", autoupdate.FlagEnterprise|autoupdate.FlagFIPS), removedRevisions: []Revision{ - NewRevision("backup-version", FlagEnterprise|FlagFIPS), + NewRevision("backup-version", autoupdate.FlagEnterprise|autoupdate.FlagFIPS), NewRevision("unknown-version", 0), }, reloadCalls: 1, @@ -475,8 +476,8 @@ func TestUpdater_Update(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Spec: UpdateSpec{ - URLTemplate: "https://example.com", - Enabled: true, + BaseURL: "https://example.com", + Enabled: true, }, Status: UpdateStatus{ Active: NewRevision("old-version", 0), @@ -487,7 +488,7 @@ func TestUpdater_Update(t *testing.T) { setupErr: errors.New("setup error"), installedRevision: NewRevision("16.3.0", 0), - installedTemplate: "https://example.com", + installedBaseURL: "https://example.com", linkedRevision: NewRevision("16.3.0", 0), removedRevisions: []Revision{ NewRevision("backup-version", 0), @@ -503,8 +504,8 @@ func TestUpdater_Update(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Spec: UpdateSpec{ - URLTemplate: "https://example.com", - Enabled: true, + BaseURL: "https://example.com", + Enabled: true, }, Status: UpdateStatus{ Active: NewRevision("old-version", 0), @@ -515,7 +516,7 @@ func TestUpdater_Update(t *testing.T) { reloadErr: errors.New("reload error"), installedRevision: NewRevision("16.3.0", 0), - installedTemplate: "https://example.com", + installedBaseURL: "https://example.com", linkedRevision: NewRevision("16.3.0", 0), removedRevisions: []Revision{ NewRevision("backup-version", 0), @@ -531,8 +532,8 @@ func TestUpdater_Update(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Spec: UpdateSpec{ - URLTemplate: "https://example.com", - Enabled: true, + BaseURL: "https://example.com", + Enabled: true, }, Status: UpdateStatus{ Active: NewRevision("old-version", 0), @@ -548,9 +549,9 @@ func TestUpdater_Update(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Spec: UpdateSpec{ - URLTemplate: "https://example.com", - Enabled: true, - Pinned: true, + BaseURL: "https://example.com", + Enabled: true, + Pinned: true, }, Status: UpdateStatus{ Active: NewRevision("old-version", 0), @@ -572,10 +573,10 @@ func TestUpdater_Update(t *testing.T) { AgentAutoUpdate: tt.inWindow, }, } - if tt.flags&FlagEnterprise != 0 { + if tt.flags&autoupdate.FlagEnterprise != 0 { config.Edition = "ent" } - config.FIPS = tt.flags&FlagFIPS != 0 + config.FIPS = tt.flags&autoupdate.FlagFIPS != 0 err := json.NewEncoder(w).Encode(config) require.NoError(t, err) })) @@ -603,7 +604,7 @@ func TestUpdater_Update(t *testing.T) { var ( installedRevision Revision - installedTemplate string + installedBaseURL string linkedRevision Revision removedRevisions []Revision revertFuncCalls int @@ -612,9 +613,9 @@ func TestUpdater_Update(t *testing.T) { reloadCalls int ) updater.Installer = &testInstaller{ - FuncInstall: func(_ context.Context, rev Revision, template string) error { + FuncInstall: func(_ context.Context, rev Revision, baseURL string) error { installedRevision = rev - installedTemplate = template + installedBaseURL = baseURL return tt.installErr }, FuncLink: func(_ context.Context, rev Revision) (revert func(context.Context) bool, err error) { @@ -660,7 +661,7 @@ func TestUpdater_Update(t *testing.T) { require.NoError(t, err) } require.Equal(t, tt.installedRevision, installedRevision) - require.Equal(t, tt.installedTemplate, installedTemplate) + require.Equal(t, tt.installedBaseURL, installedBaseURL) require.Equal(t, tt.linkedRevision, linkedRevision) require.Equal(t, tt.removedRevisions, removedRevisions) require.Equal(t, tt.flags, installedRevision.Flags) @@ -1085,14 +1086,14 @@ func TestUpdater_Install(t *testing.T) { name string cfg *UpdateConfig // nil -> file not present userCfg OverrideConfig - flags InstallFlags + flags autoupdate.InstallFlags installErr error setupErr error reloadErr error removedRevision Revision installedRevision Revision - installedTemplate string + installedBaseURL string linkedRevision Revision requestGroup string reloadCalls int @@ -1106,9 +1107,9 @@ func TestUpdater_Install(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Spec: UpdateSpec{ - Enabled: true, - Group: "group", - URLTemplate: "https://example.com", + Enabled: true, + Group: "group", + BaseURL: "https://example.com", }, Status: UpdateStatus{ Active: NewRevision("old-version", 0), @@ -1116,7 +1117,7 @@ func TestUpdater_Install(t *testing.T) { }, installedRevision: NewRevision("16.3.0", 0), - installedTemplate: "https://example.com", + installedBaseURL: "https://example.com", linkedRevision: NewRevision("16.3.0", 0), requestGroup: "group", reloadCalls: 1, @@ -1128,8 +1129,8 @@ func TestUpdater_Install(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Spec: UpdateSpec{ - Group: "old-group", - URLTemplate: "https://example.com/old", + Group: "old-group", + BaseURL: "https://example.com/old", }, Status: UpdateStatus{ Active: NewRevision("old-version", 0), @@ -1137,15 +1138,15 @@ func TestUpdater_Install(t *testing.T) { }, userCfg: OverrideConfig{ UpdateSpec: UpdateSpec{ - Enabled: true, - Group: "new-group", - URLTemplate: "https://example.com/new", + Enabled: true, + Group: "new-group", + BaseURL: "https://example.com/new", }, ForceVersion: "new-version", }, installedRevision: NewRevision("new-version", 0), - installedTemplate: "https://example.com/new", + installedBaseURL: "https://example.com/new", linkedRevision: NewRevision("new-version", 0), requestGroup: "new-group", reloadCalls: 1, @@ -1162,7 +1163,7 @@ func TestUpdater_Install(t *testing.T) { }, installedRevision: NewRevision("16.3.0", 0), - installedTemplate: cdnURITemplate, + installedBaseURL: autoupdate.DefaultBaseURL, linkedRevision: NewRevision("16.3.0", 0), reloadCalls: 1, setupCalls: 1, @@ -1179,7 +1180,7 @@ func TestUpdater_Install(t *testing.T) { }, installedRevision: NewRevision("16.3.0", 0), - installedTemplate: cdnURITemplate, + installedBaseURL: autoupdate.DefaultBaseURL, linkedRevision: NewRevision("16.3.0", 0), reloadCalls: 1, setupCalls: 1, @@ -1190,11 +1191,11 @@ func TestUpdater_Install(t *testing.T) { Version: updateConfigVersion, Kind: updateConfigKind, Spec: UpdateSpec{ - URLTemplate: "http://example.com", + BaseURL: "http://example.com", }, }, - errMatch: "URL must use TLS", + errMatch: "must use TLS", }, { name: "install error", @@ -1205,7 +1206,7 @@ func TestUpdater_Install(t *testing.T) { installErr: errors.New("install error"), installedRevision: NewRevision("16.3.0", 0), - installedTemplate: cdnURITemplate, + installedBaseURL: autoupdate.DefaultBaseURL, errMatch: "install error", }, { @@ -1219,7 +1220,7 @@ func TestUpdater_Install(t *testing.T) { }, installedRevision: NewRevision("16.3.0", 0), - installedTemplate: cdnURITemplate, + installedBaseURL: autoupdate.DefaultBaseURL, linkedRevision: NewRevision("16.3.0", 0), reloadCalls: 0, setupCalls: 1, @@ -1236,7 +1237,7 @@ func TestUpdater_Install(t *testing.T) { }, installedRevision: NewRevision("16.3.0", 0), - installedTemplate: cdnURITemplate, + installedBaseURL: autoupdate.DefaultBaseURL, linkedRevision: NewRevision("16.3.0", 0), removedRevision: NewRevision("backup-version", 0), reloadCalls: 1, @@ -1254,7 +1255,7 @@ func TestUpdater_Install(t *testing.T) { }, installedRevision: NewRevision("16.3.0", 0), - installedTemplate: cdnURITemplate, + installedBaseURL: autoupdate.DefaultBaseURL, linkedRevision: NewRevision("16.3.0", 0), reloadCalls: 0, setupCalls: 1, @@ -1263,17 +1264,17 @@ func TestUpdater_Install(t *testing.T) { name: "config does not exist", installedRevision: NewRevision("16.3.0", 0), - installedTemplate: cdnURITemplate, + installedBaseURL: autoupdate.DefaultBaseURL, linkedRevision: NewRevision("16.3.0", 0), reloadCalls: 1, setupCalls: 1, }, { name: "FIPS and Enterprise flags", - flags: FlagEnterprise | FlagFIPS, - installedRevision: NewRevision("16.3.0", FlagEnterprise|FlagFIPS), - installedTemplate: cdnURITemplate, - linkedRevision: NewRevision("16.3.0", FlagEnterprise|FlagFIPS), + flags: autoupdate.FlagEnterprise | autoupdate.FlagFIPS, + installedRevision: NewRevision("16.3.0", autoupdate.FlagEnterprise|autoupdate.FlagFIPS), + installedBaseURL: autoupdate.DefaultBaseURL, + linkedRevision: NewRevision("16.3.0", autoupdate.FlagEnterprise|autoupdate.FlagFIPS), reloadCalls: 1, setupCalls: 1, }, @@ -1287,7 +1288,7 @@ func TestUpdater_Install(t *testing.T) { setupErr: errors.New("setup error"), installedRevision: NewRevision("16.3.0", 0), - installedTemplate: cdnURITemplate, + installedBaseURL: autoupdate.DefaultBaseURL, linkedRevision: NewRevision("16.3.0", 0), reloadCalls: 0, revertCalls: 1, @@ -1299,7 +1300,7 @@ func TestUpdater_Install(t *testing.T) { reloadErr: errors.New("reload error"), installedRevision: NewRevision("16.3.0", 0), - installedTemplate: cdnURITemplate, + installedBaseURL: autoupdate.DefaultBaseURL, linkedRevision: NewRevision("16.3.0", 0), reloadCalls: 2, revertCalls: 1, @@ -1312,7 +1313,7 @@ func TestUpdater_Install(t *testing.T) { setupErr: ErrNotSupported, installedRevision: NewRevision("16.3.0", 0), - installedTemplate: cdnURITemplate, + installedBaseURL: autoupdate.DefaultBaseURL, linkedRevision: NewRevision("16.3.0", 0), reloadCalls: 1, setupCalls: 1, @@ -1322,7 +1323,7 @@ func TestUpdater_Install(t *testing.T) { reloadErr: ErrNotNeeded, installedRevision: NewRevision("16.3.0", 0), - installedTemplate: cdnURITemplate, + installedBaseURL: autoupdate.DefaultBaseURL, linkedRevision: NewRevision("16.3.0", 0), reloadCalls: 1, setupCalls: 1, @@ -1358,10 +1359,10 @@ func TestUpdater_Install(t *testing.T) { AgentVersion: "16.3.0", }, } - if tt.flags&FlagEnterprise != 0 { + if tt.flags&autoupdate.FlagEnterprise != 0 { config.Edition = "ent" } - config.FIPS = tt.flags&FlagFIPS != 0 + config.FIPS = tt.flags&autoupdate.FlagFIPS != 0 err := json.NewEncoder(w).Encode(config) require.NoError(t, err) })) @@ -1373,7 +1374,7 @@ func TestUpdater_Install(t *testing.T) { var ( installedRevision Revision - installedTemplate string + installedBaseURL string linkedRevision Revision removedRevision Revision revertFuncCalls int @@ -1382,9 +1383,9 @@ func TestUpdater_Install(t *testing.T) { revertSetupCalls int ) updater.Installer = &testInstaller{ - FuncInstall: func(_ context.Context, rev Revision, template string) error { + FuncInstall: func(_ context.Context, rev Revision, baseURL string) error { installedRevision = rev - installedTemplate = template + installedBaseURL = baseURL return tt.installErr }, FuncLink: func(_ context.Context, rev Revision) (revert func(context.Context) bool, err error) { @@ -1426,7 +1427,7 @@ func TestUpdater_Install(t *testing.T) { require.NoError(t, err) } require.Equal(t, tt.installedRevision, installedRevision) - require.Equal(t, tt.installedTemplate, installedTemplate) + require.Equal(t, tt.installedBaseURL, installedBaseURL) require.Equal(t, tt.linkedRevision, linkedRevision) require.Equal(t, tt.removedRevision, removedRevision) require.Equal(t, tt.flags, installedRevision.Flags) @@ -1461,7 +1462,7 @@ func blankTestAddr(s []byte) []byte { } type testInstaller struct { - FuncInstall func(ctx context.Context, rev Revision, template string) error + FuncInstall func(ctx context.Context, rev Revision, baseURL 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) @@ -1472,8 +1473,8 @@ type testInstaller struct { FuncList func(ctx context.Context) (revs []Revision, err error) } -func (ti *testInstaller) Install(ctx context.Context, rev Revision, template string) error { - return ti.FuncInstall(ctx, rev, template) +func (ti *testInstaller) Install(ctx context.Context, rev Revision, baseURL string) error { + return ti.FuncInstall(ctx, rev, baseURL) } func (ti *testInstaller) Remove(ctx context.Context, rev Revision) error { diff --git a/lib/autoupdate/package_url.go b/lib/autoupdate/package_url.go index 9b283c3da59c2..b00eb59fea5c9 100644 --- a/lib/autoupdate/package_url.go +++ b/lib/autoupdate/package_url.go @@ -20,10 +20,12 @@ package autoupdate import ( "bytes" + "encoding/json" "runtime" "text/template" "github.com/gravitational/trace" + "gopkg.in/yaml.v3" ) // InstallFlags sets flags for the Teleport installation. @@ -54,6 +56,82 @@ const ( BaseURLEnvVar = "TELEPORT_CDN_BASE_URL" ) +// 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{ + FlagEnterprise, + FlagFIPS, + } { + if i&flag != 0 { + out = append(out, flag.String()) + } + } + return out +} + +// String returns the string representation of a single InstallFlag flag, or "Unknown". +func (i InstallFlags) String() string { + switch i { + case 0: + return "" + case FlagEnterprise: + return "Enterprise" + case FlagFIPS: + return "FIPS" + } + 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 +} + // MakeURL constructs the package download URL from template, base URL and revision. func MakeURL(uriTmpl string, baseURL string, pkg string, version string, flags InstallFlags) (string, error) { tmpl, err := template.New("uri").Parse(uriTmpl) diff --git a/lib/autoupdate/package_url_test.go b/lib/autoupdate/package_url_test.go new file mode 100644 index 0000000000000..b3eca4be38d7e --- /dev/null +++ b/lib/autoupdate/package_url_test.go @@ -0,0 +1,95 @@ +/* + * Teleport + * Copyright (C) 2025 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 autoupdate + +import ( + "testing" + + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" +) + +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/tool/teleport-update/main.go b/tool/teleport-update/main.go index 7cdca6f8148bc..6bcb1deb9d1f0 100644 --- a/tool/teleport-update/main.go +++ b/tool/teleport-update/main.go @@ -31,6 +31,7 @@ import ( "gopkg.in/yaml.v3" "github.com/gravitational/teleport" + common "github.com/gravitational/teleport/lib/autoupdate" autoupdate "github.com/gravitational/teleport/lib/autoupdate/agent" libdefaults "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/modules" @@ -47,8 +48,6 @@ The Teleport Updater supports upgrade schedules and automated rollbacks. Find out more at https://goteleport.com/docs/updater` const ( - // templateEnvVar allows the template for the Teleport tgz to be specified via env var. - templateEnvVar = "TELEPORT_URL_TEMPLATE" // proxyServerEnvVar allows the proxy server address to be specified via env var. proxyServerEnvVar = "TELEPORT_PROXY" // updateGroupEnvVar allows the update group to be specified via env var. @@ -114,8 +113,8 @@ func Run(args []string) int { Short('p').Envar(proxyServerEnvVar).StringVar(&ccfg.Proxy) enableCmd.Flag("group", "Update group for this agent installation."). Short('g').Envar(updateGroupEnvVar).StringVar(&ccfg.Group) - enableCmd.Flag("template", "Go template used to override the Teleport download URL."). - Short('t').Envar(templateEnvVar).StringVar(&ccfg.URLTemplate) + enableCmd.Flag("base-url", "Base URL used to override the Teleport download URL."). + Short('b').Envar(common.BaseURLEnvVar).StringVar(&ccfg.BaseURL) enableCmd.Flag("force-version", "Force the provided version instead of using the version provided by the Teleport cluster."). Short('f').Envar(updateVersionEnvVar).Hidden().StringVar(&ccfg.ForceVersion) enableCmd.Flag("self-setup", "Use the current teleport-update binary to create systemd service config for auto-updates."). @@ -127,8 +126,8 @@ func Run(args []string) int { Short('p').Envar(proxyServerEnvVar).StringVar(&ccfg.Proxy) pinCmd.Flag("group", "Update group for this agent installation."). Short('g').Envar(updateGroupEnvVar).StringVar(&ccfg.Group) - pinCmd.Flag("template", "Go template used to override Teleport download URL."). - Short('t').Envar(templateEnvVar).StringVar(&ccfg.URLTemplate) + pinCmd.Flag("base-url", "Base URL used to override the Teleport download URL."). + Short('b').Envar(common.BaseURLEnvVar).StringVar(&ccfg.BaseURL) pinCmd.Flag("force-version", "Force the provided version instead of using the version provided by the Teleport cluster."). Short('f').Envar(updateVersionEnvVar).StringVar(&ccfg.ForceVersion) pinCmd.Flag("self-setup", "Use the current teleport-update binary to create systemd service config for auto-updates.").