diff --git a/lib/autoupdate/agent/config.go b/lib/autoupdate/agent/config.go index 2fcc3b5404d0f..95e7fd591ce8a 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 ( @@ -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 } @@ -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..14b9b92af81c8 100644 --- a/lib/autoupdate/agent/config_test.go +++ b/lib/autoupdate/agent/config_test.go @@ -21,8 +21,8 @@ package agent import ( "testing" + "github.com/gravitational/teleport/lib/autoupdate" "github.com/stretchr/testify/require" - "gopkg.in/yaml.v3" ) func TestNewRevisionFromDir(t *testing.T) { @@ -46,7 +46,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 +54,7 @@ func TestNewRevisionFromDir(t *testing.T) { dir: "1.2.3_ent", rev: Revision{ Version: "1.2.3", - Flags: FlagEnterprise, + Flags: autoupdate.FlagEnterprise, }, }, { @@ -124,72 +124,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..d4b92bd416de8 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" ) @@ -135,7 +134,7 @@ func (li *LocalInstaller) Install(ctx context.Context, rev Revision, template st sumPath := filepath.Join(versionDir, checksumType) // generate download URI from template - uri, err := makeURL(template, rev) + uri, err := autoupdate.MakeURL(template, autoupdate.DefaultBaseURL, autoupdate.DefaultPackage, rev.Version, rev.Flags) if err != nil { return trace.Wrap(err) } @@ -229,30 +228,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 +329,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 +347,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..f039a4dc06c36 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 }{ diff --git a/lib/autoupdate/agent/updater.go b/lib/autoupdate/agent/updater.go index cc95048b11b50..447008cade1f1 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. @@ -262,7 +261,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 +603,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{ @@ -644,7 +643,7 @@ func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, target Revision template := cfg.Spec.URLTemplate if template == "" { - template = cdnURITemplate + template = autoupdate.DefaultCDNURITemplate } err := u.Installer.Install(ctx, target, template) if err != nil { diff --git a/lib/autoupdate/agent/updater_test.go b/lib/autoupdate/agent/updater_test.go index d8981eb59fa17..4b54d1b89b83a 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 @@ -360,7 +361,7 @@ func TestUpdater_Update(t *testing.T) { installErr: errors.New("install error"), installedRevision: NewRevision("16.3.0", 0), - installedTemplate: cdnURITemplate, + installedTemplate: autoupdate.DefaultCDNURITemplate, errMatch: "install error", }, { @@ -447,18 +448,18 @@ func TestUpdater_Update(t *testing.T) { 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), + installedRevision: NewRevision("16.3.0", autoupdate.FlagEnterprise|autoupdate.FlagFIPS), installedTemplate: "https://example.com", - linkedRevision: NewRevision("16.3.0", FlagEnterprise|FlagFIPS), + 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, @@ -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) })) @@ -1085,7 +1086,7 @@ 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 @@ -1162,7 +1163,7 @@ func TestUpdater_Install(t *testing.T) { }, installedRevision: NewRevision("16.3.0", 0), - installedTemplate: cdnURITemplate, + installedTemplate: autoupdate.DefaultCDNURITemplate, 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, + installedTemplate: autoupdate.DefaultCDNURITemplate, linkedRevision: NewRevision("16.3.0", 0), reloadCalls: 1, setupCalls: 1, @@ -1205,7 +1206,7 @@ func TestUpdater_Install(t *testing.T) { installErr: errors.New("install error"), installedRevision: NewRevision("16.3.0", 0), - installedTemplate: cdnURITemplate, + installedTemplate: autoupdate.DefaultCDNURITemplate, errMatch: "install error", }, { @@ -1219,7 +1220,7 @@ func TestUpdater_Install(t *testing.T) { }, installedRevision: NewRevision("16.3.0", 0), - installedTemplate: cdnURITemplate, + installedTemplate: autoupdate.DefaultCDNURITemplate, 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, + installedTemplate: autoupdate.DefaultCDNURITemplate, 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, + installedTemplate: autoupdate.DefaultCDNURITemplate, 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, + installedTemplate: autoupdate.DefaultCDNURITemplate, 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), + installedTemplate: autoupdate.DefaultCDNURITemplate, + 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, + installedTemplate: autoupdate.DefaultCDNURITemplate, 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, + installedTemplate: autoupdate.DefaultCDNURITemplate, 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, + installedTemplate: autoupdate.DefaultCDNURITemplate, 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, + installedTemplate: autoupdate.DefaultCDNURITemplate, 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) })) diff --git a/lib/autoupdate/package_url.go b/lib/autoupdate/package_url.go index 0b2afa3a60620..27c4f6618ac52 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. @@ -52,6 +54,82 @@ const ( {{- end }}` ) +// 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)) + } + }) + } +}