mirror of
https://github.com/gravitational/teleport.git
synced 2026-09-24 16:17:11 +08:00
InstallScripts: pin teleport version using ServerVersion (#28149)
* InstallScripts: pin teleport version using ServerVersion When Automatic Upgrades are enabled and the current installation is an enterprise build, it will install teleport using: - stable/cloud repo channel (yum, apt) - pin the version to the one present at: https://updates.releases.teleport.dev/v1/stable/cloud/version * improve comments
This commit is contained in:
@@ -28,15 +28,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
|
||||
sudo apt-key add /tmp/teleport-pubkey.asc
|
||||
echo "deb https://apt.releases.teleport.dev/ubuntu ${VERSION_CODENAME?} {{ .RepoChannel }}" | sudo tee /etc/apt/sources.list.d/teleport.list
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -1702,20 +1702,20 @@ func (h *Handler) installer(w http.ResponseWriter, r *http.Request, p httprouter
|
||||
// By default, it uses the stable/v<majorVersion> 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)
|
||||
|
||||
+145
-35
@@ -2539,43 +2539,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) {
|
||||
@@ -2595,6 +2595,7 @@ echo AutomaticUpgrades: {{ .AutomaticUpgrades }}
|
||||
" 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)
|
||||
@@ -2613,19 +2614,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,
|
||||
}})
|
||||
|
||||
t.Run("cloud without automatic upgrades", func(t *testing.T) {
|
||||
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{})
|
||||
@@ -2633,7 +2668,82 @@ 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"+
|
||||
" # shellcheck disable=SC2050\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"+
|
||||
" # shellcheck disable=SC2050\n"+
|
||||
" if [[ \"false\" == \"true\" ]]; then\n"+
|
||||
" PACKAGE_LIST=\"${PACKAGE_LIST} teleport-updater\"\n"+
|
||||
" fi\n",
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
+37
-27
@@ -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<majorVersion>`, 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
|
||||
|
||||
+35
-22
@@ -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,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
+2
-1
@@ -32,6 +32,7 @@ import (
|
||||
|
||||
"github.com/gravitational/teleport"
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"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"
|
||||
@@ -179,7 +180,7 @@ func Run(args []string, stdout io.Writer) error {
|
||||
}
|
||||
|
||||
func onVersion() error {
|
||||
utils.PrintVersion()
|
||||
modules.GetModules().PrintVersion()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -501,7 +501,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)
|
||||
|
||||
@@ -1461,7 +1461,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)
|
||||
|
||||
Reference in New Issue
Block a user