From a423169685b783873b3cb879255cfcc65f59d2d2 Mon Sep 17 00:00:00 2001 From: hugoShaka Date: Mon, 24 Feb 2025 17:27:48 -0500 Subject: [PATCH] Allow prerelease Teleport to install official artifacts --- lib/utils/teleportassets/teleportassets.go | 21 +++++++-- .../teleportassets/teleportassets_test.go | 47 +++++++++++++++++++ lib/web/scripts.go | 12 +++-- lib/web/scripts/install.go | 6 ++- lib/web/scripts/install_test.go | 18 +++++++ 5 files changed, 97 insertions(+), 7 deletions(-) diff --git a/lib/utils/teleportassets/teleportassets.go b/lib/utils/teleportassets/teleportassets.go index e396798a5477b..302977a8bd9ef 100644 --- a/lib/utils/teleportassets/teleportassets.go +++ b/lib/utils/teleportassets/teleportassets.go @@ -28,9 +28,9 @@ import ( ) const ( - // teleportReleaseCDN is the Teleport CDN URL for release builds. + // TeleportReleaseCDN is the Teleport CDN URL for release builds. // This can be used to download the Teleport binary for release builds. - teleportReleaseCDN = "https://cdn.teleport.dev" + TeleportReleaseCDN = "https://cdn.teleport.dev" // teleportPreReleaseCDN is the Teleport CDN URL for pre-release builds. // This can be used to download the Teleport binary for pre-release builds. teleportPreReleaseCDN = "https://cdn.cloud.gravitational.io" @@ -48,7 +48,22 @@ func cdnBaseURL(version semver.Version) string { if version.PreRelease != "" { return teleportPreReleaseCDN } - return teleportReleaseCDN + return TeleportReleaseCDN +} + +// CDNBaseURLForVersion returns the CDN base URL for a given artifact version. +// This function ensures that a Teleport production build cannot download from +// the pre-release CDN while Teleport pre-release builds can download both form +// the production and pre-release CDN. +func CDNBaseURLForVersion(artifactVersion *semver.Version) string { + return cdnBaseURLForVersion(artifactVersion, teleport.SemVersion) +} + +func cdnBaseURLForVersion(artifactVersion, teleportVersion *semver.Version) string { + if teleportVersion.PreRelease != "" && artifactVersion.PreRelease != "" { + return teleportPreReleaseCDN + } + return TeleportReleaseCDN } const ( diff --git a/lib/utils/teleportassets/teleportassets_test.go b/lib/utils/teleportassets/teleportassets_test.go index 22dee4c13061c..ffc3a9471891c 100644 --- a/lib/utils/teleportassets/teleportassets_test.go +++ b/lib/utils/teleportassets/teleportassets_test.go @@ -80,3 +80,50 @@ func TestDistrolessTeleportImageRepo(t *testing.T) { }) } } + +func Test_cdnBaseURLForVersion(t *testing.T) { + t.Parallel() + tests := []struct { + name string + artifactVersion string + teleportVersion string + want string + }{ + { + name: "both official releases", + artifactVersion: "16.3.2", + teleportVersion: "16.1.0", + want: TeleportReleaseCDN, + }, + { + name: "both pre-releases", + artifactVersion: "16.3.2-dev.1", + teleportVersion: "16.1.0-foo.25", + want: teleportPreReleaseCDN, + }, + { + name: "official teleport should not be able to install pre-release artifacts", + artifactVersion: "16.3.2-dev.1", + teleportVersion: "16.1.0", + want: TeleportReleaseCDN, + }, + { + name: "pre-release teleport should be able to install official artifacts", + artifactVersion: "16.3.2", + teleportVersion: "16.1.0-dev.1", + want: TeleportReleaseCDN, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Test setup: parse version. + av, err := semver.NewVersion(tt.artifactVersion) + require.NoError(t, err) + tv, err := semver.NewVersion(tt.teleportVersion) + require.NoError(t, err) + + // Test execution and validation. + require.Equal(t, tt.want, cdnBaseURLForVersion(av, tv)) + }) + } +} diff --git a/lib/web/scripts.go b/lib/web/scripts.go index d5e89bec59e1d..aee4b7342fc1a 100644 --- a/lib/web/scripts.go +++ b/lib/web/scripts.go @@ -24,6 +24,7 @@ import ( "net/http" "os" + "github.com/coreos/go-semver/semver" "github.com/gravitational/trace" "github.com/julienschmidt/httprouter" @@ -108,7 +109,7 @@ func (h *Handler) installScriptOptions(ctx context.Context) (scripts.InstallScri teleportFlavor = types.PackageNameOSS } - cdnBaseURL, err := getCDNBaseURL() + cdnBaseURL, err := getCDNBaseURL(version) if err != nil { h.logger.WarnContext(ctx, "Failed to get CDN base URL", "error", err) return scripts.InstallScriptOptions{}, trace.Wrap(err) @@ -133,12 +134,17 @@ func (h *Handler) installScriptOptions(ctx context.Context) (scripts.InstallScri // - "https://cdn.cloud.gravitational.io" (dev builds/staging) const EnvVarCDNBaseURL = "TELEPORT_CDN_BASE_URL" -func getCDNBaseURL() (string, error) { +func getCDNBaseURL(version string) (string, error) { // If the user explicitly overrides the CDN base URL, we use it. if override := os.Getenv(EnvVarCDNBaseURL); override != "" { return override, nil } + v, err := semver.NewVersion(version) + if err != nil { + return "", trace.Wrap(err) + } + // If this is an AGPL build, we don't want to automatically install binaries distributed under a more restrictive // license so we error and ask the user set the CDN URL, either to: // - the official Teleport CDN if they agree with the community license and meet its requirements @@ -150,5 +156,5 @@ func getCDNBaseURL() (string, error) { teleportassets.CDNBaseURL()) } - return teleportassets.CDNBaseURL(), nil + return teleportassets.CDNBaseURLForVersion(v), nil } diff --git a/lib/web/scripts/install.go b/lib/web/scripts/install.go index 25a8970e58563..92fbc77ee2144 100644 --- a/lib/web/scripts/install.go +++ b/lib/web/scripts/install.go @@ -27,6 +27,7 @@ import ( "github.com/google/safetext/shsprintf" "github.com/gravitational/trace" + "github.com/gravitational/teleport/lib/utils/teleportassets" "github.com/gravitational/teleport/lib/web/scripts/oneoff" ) @@ -45,6 +46,8 @@ const ( // updater). // See RFD-184 for more details: https://github.com/gravitational/teleport/blob/master/rfd/0184-agent-auto-updates.md UpdaterBinaryAutoupdate + + teleportUpdateDefaultCDN = teleportassets.TeleportReleaseCDN ) // InstallScriptOptions contains the Teleport installation options used to generate installation scripts. @@ -112,7 +115,8 @@ func (o *InstallScriptOptions) oneOffParams() (params oneoff.OneOffScriptParams) } args := []string{"enable", "--proxy", shsprintf.EscapeDefaultContext(o.ProxyAddr)} - if o.CDNBaseURL != "" { + // Pass the base-url override if the base url is set and is not the default one. + if o.CDNBaseURL != "" && o.CDNBaseURL != teleportUpdateDefaultCDN { args = append(args, "--base-url", shsprintf.EscapeDefaultContext(o.CDNBaseURL)) } diff --git a/lib/web/scripts/install_test.go b/lib/web/scripts/install_test.go index 5606858d4aac8..4c7e51ea4eb50 100644 --- a/lib/web/scripts/install_test.go +++ b/lib/web/scripts/install_test.go @@ -88,6 +88,24 @@ func TestGetInstallScript(t *testing.T) { require.Contains(t, script, "packageSuffix='bin.tar.gz'") }, }, + { + name: "Oneoff install default CDN", + opts: InstallScriptOptions{ + AutoupdateStyle: UpdaterBinaryAutoupdate, + TeleportVersion: testVersion, + ProxyAddr: testProxyAddr, + TeleportFlavor: types.PackageNameOSS, + CDNBaseURL: teleportassets.TeleportReleaseCDN, + }, + assertFn: func(t *testing.T, script string) { + require.Contains(t, script, "entrypoint='teleport-update'") + require.Contains(t, script, fmt.Sprintf("teleportVersion='v%s'", testVersion)) + require.Contains(t, script, fmt.Sprintf("teleportFlavor='%s'", types.PackageNameOSS)) + require.Contains(t, script, fmt.Sprintf("cdnBaseURL='%s'", teleportassets.TeleportReleaseCDN)) + require.Contains(t, script, fmt.Sprintf("entrypointArgs='enable --proxy %s'", testProxyAddr)) + require.Contains(t, script, "packageSuffix='bin.tar.gz'") + }, + }, { name: "Oneoff enterprise install", opts: InstallScriptOptions{