diff --git a/api/types/installers/installer.sh.tmpl b/api/types/installers/installer.sh.tmpl index 864e85bf04745..8633b6c4fa323 100644 --- a/api/types/installers/installer.sh.tmpl +++ b/api/types/installers/installer.sh.tmpl @@ -27,15 +27,10 @@ on_azure() { PACKAGE_LIST="${PACKAGE_LIST} {{ .TeleportPackage }}-updater" fi - # old versions of ubuntu require that keys get added by `apt-key add`, without - # adding the key apt shows a key signing error when installing teleport. - LEGACY_UBUNTU=false - if [ "$VERSION_CODENAME" = "xenial" ] || [ "$VERSION_CODENAME" = "trusty" ]; then - LEGACY_UBUNTU=true - fi - if [ "$ID" = "debian" ] || [ "$ID" = "ubuntu" ]; then - if [ "$LEGACY_UBUNTU" = true ]; then + # old versions of ubuntu require that keys get added by `apt-key add`, without + # adding the key apt shows a key signing error when installing teleport. + if [ "$VERSION_CODENAME" = "xenial" ] || [ "$VERSION_CODENAME" = "trusty" ]; then curl -o /tmp/teleport-pubkey.asc https://apt.releases.teleport.dev/gpg cat /tmp/teleport-pubkey.asc | sudo apt-key add - echo "deb https://apt.releases.teleport.dev/ubuntu ${VERSION_CODENAME?} {{ .RepoChannel }}" | sudo tee /etc/apt/sources.list.d/teleport.list diff --git a/lib/automaticupgrades/version.go b/lib/automaticupgrades/version.go new file mode 100644 index 0000000000000..607d32ef9bbd9 --- /dev/null +++ b/lib/automaticupgrades/version.go @@ -0,0 +1,75 @@ +/* +Copyright 2023 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package automaticupgrades + +import ( + "context" + "net/http" + "net/url" + "strings" + + "github.com/gravitational/trace" + + "github.com/gravitational/teleport" + "github.com/gravitational/teleport/lib/utils" +) + +const ( + // stableCloudVersionBaseURL is the base URL for the server that returns the current stable/cloud version. + stableCloudVersionBaseURL = "https://updates.releases.teleport.dev" + + // stableCloudVersionPath is the URL path that returns the current stable/cloud version. + stableCloudVersionPath = "/v1/stable/cloud/version" +) + +// Version returns the version that should be used for installing Teleport Services +// This is used when installing agents using scripts. +// Even when Teleport Auth/Proxy is using vX, the agents must always respect this version. +func Version(ctx context.Context, baseURL string) (string, error) { + if baseURL == "" { + baseURL = stableCloudVersionBaseURL + } + + fullURL, err := url.JoinPath(baseURL, stableCloudVersionPath) + if err != nil { + return "", trace.Wrap(err) + } + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, fullURL, nil) + if err != nil { + return "", trace.Wrap(err) + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return "", trace.Wrap(err) + } + defer resp.Body.Close() + + body, err := utils.ReadAtMost(resp.Body, teleport.MaxHTTPResponseSize) + if err != nil { + return "", trace.Wrap(err) + } + + if resp.StatusCode != http.StatusOK { + return "", trace.BadParameter("invalid status code %d, body: %s", resp.StatusCode, string(body)) + } + + versionString := strings.TrimSpace(string(body)) + + return versionString, trace.Wrap(err) +} diff --git a/lib/automaticupgrades/version_test.go b/lib/automaticupgrades/version_test.go new file mode 100644 index 0000000000000..7433e6af84ae5 --- /dev/null +++ b/lib/automaticupgrades/version_test.go @@ -0,0 +1,86 @@ +/* +Copyright 2023 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package automaticupgrades + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gravitational/trace" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestVersion(t *testing.T) { + ctx := context.Background() + + isBadParameterErr := func(tt require.TestingT, err error, i ...interface{}) { + require.True(tt, trace.IsBadParameter(err), "expected bad parameter, got %v", err) + } + + for _, tt := range []struct { + name string + mockStatusCode int + mockResponseString string + errCheck require.ErrorAssertionFunc + expectedVersion string + }{ + { + name: "real response", + mockStatusCode: http.StatusOK, + mockResponseString: "v13.1.1\n", + errCheck: require.NoError, + expectedVersion: "v13.1.1", + }, + { + name: "invalid status code (500)", + mockStatusCode: http.StatusInternalServerError, + errCheck: isBadParameterErr, + }, + { + name: "invalid status code (403)", + mockStatusCode: http.StatusForbidden, + errCheck: isBadParameterErr, + }, + { + name: "valid but has spaces", + mockStatusCode: http.StatusOK, + mockResponseString: " v13.1.1 \n \r\n", + errCheck: require.NoError, + expectedVersion: "v13.1.1", + }, + } { + t.Run(tt.name, func(t *testing.T) { + httpTestServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.URL.Path, "/v1/stable/cloud/version") + w.WriteHeader(tt.mockStatusCode) + w.Write([]byte(tt.mockResponseString)) + })) + defer httpTestServer.Close() + + v, err := Version(ctx, httpTestServer.URL) + tt.errCheck(t, err) + if err != nil { + return + } + + require.Equal(t, v, tt.expectedVersion) + }) + } +} diff --git a/lib/utils/utils.go b/lib/utils/utils.go index b211cb1b23302..0acea1524393e 100644 --- a/lib/utils/utils.go +++ b/lib/utils/utils.go @@ -42,7 +42,6 @@ import ( "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/constants" apiutils "github.com/gravitational/teleport/api/utils" - "github.com/gravitational/teleport/lib/modules" ) // WriteContextCloser provides close method with context @@ -506,11 +505,6 @@ func ReadOrMakeHostUUID(dataDir string) (string, error) { return id, nil } -// PrintVersion prints human readable version -func PrintVersion() { - modules.GetModules().PrintVersion() -} - // StringSliceSubset returns true if b is a subset of a. func StringSliceSubset(a []string, b []string) error { aset := make(map[string]bool) diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index 4ec2993ab7854..325f83c9506e4 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -1680,20 +1680,20 @@ func (h *Handler) installer(w http.ResponseWriter, r *http.Request, p httprouter // By default, it uses the stable/v channel. repoChannel := fmt.Sprintf("stable/%s", version) - // For Teleport Cloud installations, use the `stable/cloud` channel. - if feats.Cloud { + // If the updater must be installed, then change the repo to stable/cloud + // It must also install the version specified in + // https://updates.releases.teleport.dev/v1/stable/cloud/version + installUpdater := automaticUpgrades(h.ClusterFeatures) + if installUpdater { repoChannel = stableCloudChannelRepo } - // TODO(marco): remove BuildType check when teleport-upgrade (oss) package is available in apt/yum repos. - automaticUpgrades := feats.AutomaticUpgrades && modules.GetModules().BuildType() == modules.BuildEnterprise - tmpl := installers.Template{ PublicProxyAddr: h.PublicProxyAddr(), MajorVersion: version, TeleportPackage: teleportPackage, RepoChannel: repoChannel, - AutomaticUpgrades: strconv.FormatBool(automaticUpgrades), + AutomaticUpgrades: strconv.FormatBool(installUpdater), } err = instTmpl.Execute(w, tmpl) return nil, trace.Wrap(err) diff --git a/lib/web/apiserver_test.go b/lib/web/apiserver_test.go index 19260a121fa65..983baa04e22fc 100644 --- a/lib/web/apiserver_test.go +++ b/lib/web/apiserver_test.go @@ -2444,43 +2444,43 @@ func TestPingAutomaticUpgrades(t *testing.T) { // TestInstallerRepoChannel ensures the returned installer script has the proper repo channel func TestInstallerRepoChannel(t *testing.T) { - s := newWebSuiteWithConfig(t, webSuiteConfig{ - authPreferenceSpec: &types.AuthPreferenceSpecV2{ - Type: constants.Local, - SecondFactor: constants.SecondFactorOn, - Webauthn: &types.Webauthn{RPID: "localhost"}, - }, - }) + t.Run("cloud with automatic upgrades", func(t *testing.T) { + modules.SetTestModules(t, &modules.TestModules{ + TestFeatures: modules.Features{ + Cloud: true, + AutomaticUpgrades: true, + }, + }) - wc := s.client(t) - t.Run("documented variables are injected", func(t *testing.T) { - // Variables documented here: https://goteleport.com/docs/server-access/guides/ec2-discovery/#step-67-optional-customize-the-default-installer-script - err := s.server.Auth().SetInstaller(s.ctx, types.MustNewInstallerV1("custom", `#!/usr/bin/env bash + s := newWebSuiteWithConfig(t, webSuiteConfig{ + authPreferenceSpec: &types.AuthPreferenceSpecV2{ + Type: constants.Local, + SecondFactor: constants.SecondFactorOn, + Webauthn: &types.Webauthn{RPID: "localhost"}, + }, + }) + + wc := s.client(t) + t.Run("documented variables are injected", func(t *testing.T) { + // Variables documented here: https://goteleport.com/docs/server-access/guides/ec2-discovery/#step-67-optional-customize-the-default-installer-script + err := s.server.Auth().SetInstaller(s.ctx, types.MustNewInstallerV1("custom", `#!/usr/bin/env bash echo {{ .PublicProxyAddr }} echo Teleport-{{ .MajorVersion }} echo Repository Channel: {{ .RepoChannel }} echo AutomaticUpgrades: {{ .AutomaticUpgrades }} `)) - require.NoError(t, err) + require.NoError(t, err) - re, err := wc.Get(s.ctx, wc.Endpoint("webapi", "scripts", "installer", "custom"), url.Values{}) - require.NoError(t, err) + re, err := wc.Get(s.ctx, wc.Endpoint("webapi", "scripts", "installer", "custom"), url.Values{}) + require.NoError(t, err) - responseString := string(re.Bytes()) + responseString := string(re.Bytes()) - // Variables must be injected - require.Contains(t, responseString, "echo Teleport-v") - require.Contains(t, responseString, "echo Repository Channel: stable/v") - require.NotContains(t, responseString, "echo Repository Channel: stable/cloud") - require.Contains(t, responseString, "echo AutomaticUpgrades: false") - }) - t.Run("cloud with automatic upgrades", func(t *testing.T) { - modules.SetTestModules(t, &modules.TestModules{ - TestBuildType: modules.BuildEnterprise, - TestFeatures: modules.Features{ - Cloud: true, - AutomaticUpgrades: true, - }, + // Variables must be injected + require.Contains(t, responseString, "echo Teleport-v") + require.NotContains(t, responseString, "echo Repository Channel: stable/v") + require.Contains(t, responseString, "echo Repository Channel: stable/cloud") + require.Contains(t, responseString, "echo AutomaticUpgrades: true") }) t.Run("default-installer", func(t *testing.T) { @@ -2499,6 +2499,7 @@ echo AutomaticUpgrades: {{ .AutomaticUpgrades }} " fi\n", ) }) + t.Run("default-agentless-installer", func(t *testing.T) { re, err := wc.Get(s.ctx, wc.Endpoint("webapi", "scripts", "installer", "default-agentless-installer"), url.Values{}) require.NoError(t, err) @@ -2516,19 +2517,53 @@ echo AutomaticUpgrades: {{ .AutomaticUpgrades }} ) }) }) + t.Run("cloud without automatic upgrades", func(t *testing.T) { - modules.SetTestModules(t, &modules.TestModules{TestFeatures: modules.Features{ - Cloud: true, - AutomaticUpgrades: false, - }}) + modules.SetTestModules(t, &modules.TestModules{ + TestFeatures: modules.Features{ + Cloud: true, + AutomaticUpgrades: false, + }, + }) + s := newWebSuiteWithConfig(t, webSuiteConfig{ + authPreferenceSpec: &types.AuthPreferenceSpecV2{ + Type: constants.Local, + SecondFactor: constants.SecondFactorOn, + Webauthn: &types.Webauthn{RPID: "localhost"}, + }, + }) + + wc := s.client(t) + + t.Run("documented variables are injected", func(t *testing.T) { + // Variables documented here: https://goteleport.com/docs/server-access/guides/ec2-discovery/#step-67-optional-customize-the-default-installer-script + err := s.server.Auth().SetInstaller(s.ctx, types.MustNewInstallerV1("custom", `#!/usr/bin/env bash + echo {{ .PublicProxyAddr }} + echo Teleport-{{ .MajorVersion }} + echo Repository Channel: {{ .RepoChannel }} + echo AutomaticUpgrades: {{ .AutomaticUpgrades }} + `)) + require.NoError(t, err) + + re, err := wc.Get(s.ctx, wc.Endpoint("webapi", "scripts", "installer", "custom"), url.Values{}) + require.NoError(t, err) + + responseString := string(re.Bytes()) + + // Variables must be injected + require.Contains(t, responseString, "echo Teleport-v") + require.Contains(t, responseString, "echo Repository Channel: stable/v") + require.NotContains(t, responseString, "echo Repository Channel: stable/cloud") + require.Contains(t, responseString, "echo AutomaticUpgrades: false") + }) t.Run("default-installer", func(t *testing.T) { re, err := wc.Get(s.ctx, wc.Endpoint("webapi", "scripts", "installer", "default-installer"), url.Values{}) require.NoError(t, err) responseString := string(re.Bytes()) - require.Contains(t, responseString, "stable/cloud") + require.NotContains(t, responseString, "stable/cloud") }) t.Run("default-agentless-installer", func(t *testing.T) { re, err := wc.Get(s.ctx, wc.Endpoint("webapi", "scripts", "installer", "default-agentless-installer"), url.Values{}) @@ -2536,7 +2571,80 @@ echo AutomaticUpgrades: {{ .AutomaticUpgrades }} responseString := string(re.Bytes()) - require.Contains(t, responseString, "stable/cloud") + require.NotContains(t, responseString, "stable/cloud") + }) + }) + + t.Run("oss or enterprise with automatic upgrades", func(t *testing.T) { + modules.SetTestModules(t, &modules.TestModules{ + TestBuildType: modules.BuildOSS, + TestFeatures: modules.Features{ + Cloud: false, + AutomaticUpgrades: true, + }, + }) + + s := newWebSuiteWithConfig(t, webSuiteConfig{ + authPreferenceSpec: &types.AuthPreferenceSpecV2{ + Type: constants.Local, + SecondFactor: constants.SecondFactorOn, + Webauthn: &types.Webauthn{RPID: "localhost"}, + }, + }) + + wc := s.client(t) + t.Run("documented variables are injected", func(t *testing.T) { + // Variables documented here: https://goteleport.com/docs/server-access/guides/ec2-discovery/#step-67-optional-customize-the-default-installer-script + err := s.server.Auth().SetInstaller(s.ctx, types.MustNewInstallerV1("custom", `#!/usr/bin/env bash +echo {{ .PublicProxyAddr }} +echo Teleport-{{ .MajorVersion }} +echo Repository Channel: {{ .RepoChannel }} +echo AutomaticUpgrades: {{ .AutomaticUpgrades }} + `)) + require.NoError(t, err) + + re, err := wc.Get(s.ctx, wc.Endpoint("webapi", "scripts", "installer", "custom"), url.Values{}) + require.NoError(t, err) + + responseString := string(re.Bytes()) + + // Variables must be injected + require.Contains(t, responseString, "echo Teleport-v") + require.Contains(t, responseString, "echo Repository Channel: stable/v") + require.NotContains(t, responseString, "echo Repository Channel: stable/cloud") + require.Contains(t, responseString, "echo AutomaticUpgrades: false") + }) + t.Run("default-installer", func(t *testing.T) { + re, err := wc.Get(s.ctx, wc.Endpoint("webapi", "scripts", "installer", "default-installer"), url.Values{}) + require.NoError(t, err) + + responseString := string(re.Bytes()) + + // The repo's channel to use is stable/cloud + require.NotContains(t, responseString, "stable/cloud") + require.Contains(t, responseString, "stable/v") + require.Contains(t, responseString, ""+ + " PACKAGE_LIST=\"teleport jq\"\n"+ + " if [[ \"false\" == \"true\" ]]; then\n"+ + " PACKAGE_LIST=\"${PACKAGE_LIST} teleport-updater\"\n"+ + " fi", + ) + }) + t.Run("default-agentless-installer", func(t *testing.T) { + re, err := wc.Get(s.ctx, wc.Endpoint("webapi", "scripts", "installer", "default-agentless-installer"), url.Values{}) + require.NoError(t, err) + + responseString := string(re.Bytes()) + + // The repo's channel to use is stable/cloud + require.NotContains(t, responseString, "stable/cloud") + require.Contains(t, responseString, "stable/v") + require.Contains(t, responseString, ""+ + " PACKAGE_LIST=\"jq teleport\"\n"+ + " if [[ \"false\" == \"true\" ]]; then\n"+ + " PACKAGE_LIST=\"${PACKAGE_LIST} teleport-updater\"\n"+ + " fi\n", + ) }) }) } diff --git a/lib/web/join_tokens.go b/lib/web/join_tokens.go index 74d6a82fb995c..5bd30a60f5a58 100644 --- a/lib/web/join_tokens.go +++ b/lib/web/join_tokens.go @@ -40,6 +40,7 @@ import ( "github.com/gravitational/teleport/api/types" apiutils "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/lib/auth" + "github.com/gravitational/teleport/lib/automaticupgrades" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/httplib" "github.com/gravitational/teleport/lib/modules" @@ -71,20 +72,22 @@ type nodeJoinToken struct { // scriptSettings is used to hold values which are passed into the function that // generates the join script. type scriptSettings struct { - token string - appInstallMode bool - appName string - appURI string - joinMethod string - databaseInstallMode bool - stableCloudChannelRepo bool - installUpdater bool + token string + appInstallMode bool + appName string + appURI string + joinMethod string + databaseInstallMode bool + installUpdater bool + + // automaticUpgradesVersionBaseURL is the base URL for getting the version when using the cloud/stable channel. + // Optional. + automaticUpgradesVersionBaseURL string } // automaticUpgrades returns whether automaticUpgrades should be enabled. func automaticUpgrades(features proto.Features) bool { - // TODO(marco): remove BuildType check when teleport-updater (oss) package is available in apt/yum repos. - return features.AutomaticUpgrades && modules.GetModules().BuildType() == modules.BuildEnterprise + return features.AutomaticUpgrades && features.Cloud } func (h *Handler) createTokenHandle(w http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) { @@ -210,11 +213,10 @@ func (h *Handler) getNodeJoinScriptHandle(w http.ResponseWriter, r *http.Request scripts.SetScriptHeaders(w.Header()) settings := scriptSettings{ - token: params.ByName("token"), - appInstallMode: false, - joinMethod: r.URL.Query().Get("method"), - stableCloudChannelRepo: h.ClusterFeatures.Cloud, - installUpdater: automaticUpgrades(h.ClusterFeatures), + token: params.ByName("token"), + appInstallMode: false, + joinMethod: r.URL.Query().Get("method"), + installUpdater: automaticUpgrades(h.ClusterFeatures), } script, err := getJoinScript(r.Context(), settings, h.GetProxyClient()) @@ -252,12 +254,11 @@ func (h *Handler) getAppJoinScriptHandle(w http.ResponseWriter, r *http.Request, } settings := scriptSettings{ - token: params.ByName("token"), - appInstallMode: true, - appName: name, - appURI: uri, - stableCloudChannelRepo: h.ClusterFeatures.Cloud, - installUpdater: automaticUpgrades(h.ClusterFeatures), + token: params.ByName("token"), + appInstallMode: true, + appName: name, + appURI: uri, + installUpdater: automaticUpgrades(h.ClusterFeatures), } script, err := getJoinScript(r.Context(), settings, h.GetProxyClient()) @@ -280,10 +281,9 @@ func (h *Handler) getDatabaseJoinScriptHandle(w http.ResponseWriter, r *http.Req scripts.SetScriptHeaders(w.Header()) settings := scriptSettings{ - token: params.ByName("token"), - databaseInstallMode: true, - stableCloudChannelRepo: h.ClusterFeatures.Cloud, - installUpdater: automaticUpgrades(h.ClusterFeatures), + token: params.ByName("token"), + databaseInstallMode: true, + installUpdater: automaticUpgrades(h.ClusterFeatures), } script, err := getJoinScript(r.Context(), settings, h.GetProxyClient()) @@ -392,9 +392,19 @@ func getJoinScript(ctx context.Context, settings scriptSettings, m nodeAPIGetter // By default, it will use `stable/v`, eg stable/v12 repoChannel := "" - // For Teleport Cloud installations, use the `stable/cloud` channel. - if settings.stableCloudChannelRepo { + // The install script will install the updater (teleport-ent-updater) for Cloud customers enrolled in Automatic Upgrades. + // The repo channel used must be `stable/cloud` which has the available packages for the Cloud Customer's agents. + // It pins the teleport version to the one specified by https://updates.releases.teleport.dev/v1/stable/cloud/version + // This ensures the initial installed version is the same as the `teleport-ent-updater` would install. + if settings.installUpdater { repoChannel = stableCloudChannelRepo + cloudStableVersion, err := automaticupgrades.Version(ctx, settings.automaticUpgradesVersionBaseURL) + if err != nil { + return "", trace.Wrap(err) + } + + // cloudStableVersion has vX.Y.Z format, however the script expects the version to not include the `v` + version = strings.TrimPrefix(cloudStableVersion, "v") } // This section relies on Go's default zero values to make sure that the settings diff --git a/lib/web/join_tokens_test.go b/lib/web/join_tokens_test.go index 48fa851f3a3b0..dd11cb98add82 100644 --- a/lib/web/join_tokens_test.go +++ b/lib/web/join_tokens_test.go @@ -20,12 +20,16 @@ import ( "context" "encoding/hex" "fmt" + "net/http" + "net/http/httptest" "regexp" "testing" "github.com/gravitational/trace" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/utils" @@ -873,7 +877,10 @@ func TestJoinScript(t *testing.T) { mockGetProxyServers: func() ([]types.Server, error) { return []types.Server{ &types.ServerV2{ - Spec: types.ServerSpecV2{PublicAddrs: []string{"test-host:12345678"}}, + Spec: types.ServerSpecV2{ + PublicAddrs: []string{"test-host:12345678"}, + Version: teleport.Version, + }, }, }, nil }, @@ -926,10 +933,20 @@ func TestJoinScript(t *testing.T) { }) t.Run("using repo", func(t *testing.T) { - t.Run("installUpdater set to true, list of packages must include updater package", func(t *testing.T) { - script, err := getJoinScript(context.Background(), scriptSettings{token: validToken, installUpdater: true}, m) + t.Run("installUpdater is true", func(t *testing.T) { + currentStableCloudVersion := "v99.1.1" + + httpTestServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.URL.Path, "/v1/stable/cloud/version") + w.WriteHeader(http.StatusOK) + w.Write([]byte(currentStableCloudVersion)) + })) + defer httpTestServer.Close() + + script, err := getJoinScript(context.Background(), scriptSettings{token: validToken, installUpdater: true, automaticUpgradesVersionBaseURL: httpTestServer.URL}, m) require.NoError(t, err) + // list of packages must include the updater require.Contains(t, script, ""+ " PACKAGE_LIST=${TELEPORT_PACKAGE_PIN_VERSION}\n"+ " # (warning): This expression is constant. Did you forget the $ on a variable?\n"+ @@ -939,11 +956,14 @@ func TestJoinScript(t *testing.T) { " PACKAGE_LIST+=\" ${TELEPORT_UPDATER_PIN_VERSION}\"\n"+ " fi\n", ) + // Repo channel is stable/cloud + require.Contains(t, script, "REPO_CHANNEL='stable/cloud'") + // TELEPORT_VERSION is the one provided by https://updates.releases.teleport.dev/v1/stable/cloud/version + require.Contains(t, script, "TELEPORT_VERSION='99.1.1'") }) - t.Run("installUpdater set to false, list of packages must not include updater package", func(t *testing.T) { + t.Run("installUpdater is false", func(t *testing.T) { script, err := getJoinScript(context.Background(), scriptSettings{token: validToken, installUpdater: false}, m) require.NoError(t, err) - require.Contains(t, script, ""+ " PACKAGE_LIST=${TELEPORT_PACKAGE_PIN_VERSION}\n"+ " # (warning): This expression is constant. Did you forget the $ on a variable?\n"+ @@ -953,26 +973,19 @@ func TestJoinScript(t *testing.T) { " PACKAGE_LIST+=\" ${TELEPORT_UPDATER_PIN_VERSION}\"\n"+ " fi\n", ) - }) - t.Run("using stable/cloud channel", func(t *testing.T) { - script, err := getJoinScript(context.Background(), scriptSettings{token: validToken, stableCloudChannelRepo: true}, m) - require.NoError(t, err) - require.Contains(t, script, "REPO_CHANNEL='stable/cloud'") - }) - t.Run("not hard-coding a particular channel", func(t *testing.T) { - script, err := getJoinScript(context.Background(), scriptSettings{token: validToken, stableCloudChannelRepo: false}, m) - require.NoError(t, err) + // Default based on current version is used instead require.Contains(t, script, "REPO_CHANNEL=''") + // Current version must be used + require.Contains(t, script, fmt.Sprintf("TELEPORT_VERSION='%s'", teleport.Version)) }) }) - } func TestAutomaticUpgrades(t *testing.T) { - t.Run("enterprise and automatic upgrades enabled", func(t *testing.T) { + t.Run("cloud and automatic upgrades enabled", func(t *testing.T) { modules.SetTestModules(t, &modules.TestModules{ - TestBuildType: modules.BuildEnterprise, TestFeatures: modules.Features{ + Cloud: true, AutomaticUpgrades: true, }, }) @@ -980,10 +993,10 @@ func TestAutomaticUpgrades(t *testing.T) { got := automaticUpgrades(*modules.GetModules().Features().ToProto()) require.True(t, got) }) - t.Run("enterprise but automatic upgrades disabled", func(t *testing.T) { + t.Run("cloud but automatic upgrades disabled", func(t *testing.T) { modules.SetTestModules(t, &modules.TestModules{ - TestBuildType: modules.BuildEnterprise, TestFeatures: modules.Features{ + Cloud: true, AutomaticUpgrades: false, }, }) @@ -992,12 +1005,12 @@ func TestAutomaticUpgrades(t *testing.T) { require.False(t, got) }) - // There's no `teleport-updater` (oss) package yet, so even if automatic upgrades are enabled it must return false. - t.Run("automatic upgrades enabled but build is OSS", func(t *testing.T) { + t.Run("automatic upgrades enabled but is not cloud", func(t *testing.T) { modules.SetTestModules(t, &modules.TestModules{ TestBuildType: modules.BuildEnterprise, TestFeatures: modules.Features{ - AutomaticUpgrades: false, + Cloud: false, + AutomaticUpgrades: true, }, }) diff --git a/tool/tbot/main.go b/tool/tbot/main.go index 33b8a8a5acb94..8486b20487a56 100644 --- a/tool/tbot/main.go +++ b/tool/tbot/main.go @@ -31,6 +31,7 @@ import ( "gopkg.in/yaml.v2" "github.com/gravitational/teleport" + "github.com/gravitational/teleport/lib/modules" "github.com/gravitational/teleport/lib/tbot" "github.com/gravitational/teleport/lib/tbot/config" "github.com/gravitational/teleport/lib/utils" @@ -182,7 +183,7 @@ func Run(args []string, stdout io.Writer) error { } func onVersion() error { - utils.PrintVersion() + modules.GetModules().PrintVersion() return nil } diff --git a/tool/tctl/common/tctl.go b/tool/tctl/common/tctl.go index 3c8c783cd3f79..89c2e890fcd45 100644 --- a/tool/tctl/common/tctl.go +++ b/tool/tctl/common/tctl.go @@ -39,6 +39,7 @@ import ( "github.com/gravitational/teleport/lib/client/identityfile" "github.com/gravitational/teleport/lib/config" "github.com/gravitational/teleport/lib/defaults" + "github.com/gravitational/teleport/lib/modules" "github.com/gravitational/teleport/lib/service/servicecfg" "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/tool/common" @@ -176,7 +177,7 @@ func TryRun(commands []CLICommand, args []string) error { // "version" command? if selectedCmd == ver.FullCommand() { - utils.PrintVersion() + modules.GetModules().PrintVersion() return nil } diff --git a/tool/teleport/common/teleport.go b/tool/teleport/common/teleport.go index 7276d0c88469a..b30fb92cc8c13 100644 --- a/tool/teleport/common/teleport.go +++ b/tool/teleport/common/teleport.go @@ -500,7 +500,7 @@ func Run(options Options) (app *kingpin.Application, executedCommand string, con // by the systemd unit upgrader). fmt.Printf("%s\n", teleport.Version) } else { - utils.PrintVersion() + modules.GetModules().PrintVersion() } case dbConfigureCreate.FullCommand(): err = onDumpDatabaseConfig(dbConfigCreateFlags) diff --git a/tool/tsh/tsh.go b/tool/tsh/tsh.go index 82f7211f9c393..4dbe99c4de5b7 100644 --- a/tool/tsh/tsh.go +++ b/tool/tsh/tsh.go @@ -1440,7 +1440,7 @@ func onVersion(cf *CLIConf) error { format := strings.ToLower(cf.Format) switch format { case teleport.Text, "": - utils.PrintVersion() + modules.GetModules().PrintVersion() if proxyVersion != "" { fmt.Printf("Proxy version: %s\n", proxyVersion) fmt.Printf("Proxy: %s\n", proxyPublicAddr)