diff --git a/api/client/webclient/webconfig.go b/api/client/webclient/webconfig.go index eeea0bd41a88b..91b51128c1bce 100644 --- a/api/client/webclient/webconfig.go +++ b/api/client/webclient/webconfig.go @@ -65,6 +65,10 @@ type WebConfig struct { IsUsageBasedBilling bool `json:"isUsageBasedBilling,omitempty"` // AutomaticUpgrades describes whether agents should automatically upgrade. AutomaticUpgrades bool `json:"automaticUpgrades"` + // AutomaticUpgradesTargetVersion is the agents version (eg kube agent helm chart) that should be installed. + // Eg, v13.4.3 + // Only present when AutomaticUpgrades are enabled. + AutomaticUpgradesTargetVersion string `json:"automaticUpgradesTargetVersion,omitempty"` // AssistEnabled is true when Teleport Assist is enabled. AssistEnabled bool `json:"assistEnabled"` // HideInaccessibleFeatures is true when features should be undiscoverable to users without the necessary permissions. diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index 67e38ea3d9269..aa1a38b833eb1 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -68,6 +68,7 @@ import ( apisshutils "github.com/gravitational/teleport/api/utils/sshutils" "github.com/gravitational/teleport/lib/auth" wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" + "github.com/gravitational/teleport/lib/automaticupgrades" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/defaults" dtconfig "github.com/gravitational/teleport/lib/devicetrust/config" @@ -78,6 +79,7 @@ import ( "github.com/gravitational/teleport/lib/limiter" "github.com/gravitational/teleport/lib/modules" "github.com/gravitational/teleport/lib/multiplexer" + "github.com/gravitational/teleport/lib/observability/tracing" "github.com/gravitational/teleport/lib/plugin" "github.com/gravitational/teleport/lib/proxy" "github.com/gravitational/teleport/lib/reversetunnelclient" @@ -266,6 +268,22 @@ type Config struct { // NodeWatcher is a services.NodeWatcher used by Assist to lookup nodes from // the proxy's cache and get nodes in real time. NodeWatcher *services.NodeWatcher + + // AutomaticUpgradesVersionURL is the URL which returns the target agent version. + // This URL must returns a valid version string. + // Eg, v13.4.3 + // Optional: uses cloud/stable channel when omitted. + AutomaticUpgradesVersionURL string +} + +// SetDefaults ensures proper default values are set if +// not provided. +func (c *Config) SetDefaults() { + c.ProxyClient = auth.WithGithubConnectorConversions(c.ProxyClient) + + if c.TracerProvider == nil { + c.TracerProvider = tracing.NoopProvider() + } } type APIHandler struct { @@ -1455,19 +1473,29 @@ func (h *Handler) getWebConfig(w http.ResponseWriter, r *http.Request, p httprou canJoinSessions = !services.IsRecordAtProxy(recCfg.GetMode()) } + automaticUpgradesEnabled := clusterFeatures.GetAutomaticUpgrades() + var automaticUpgradesTargetVersion string + if automaticUpgradesEnabled { + automaticUpgradesTargetVersion, err = automaticupgrades.Version(r.Context(), h.cfg.AutomaticUpgradesVersionURL) + if err != nil { + return nil, trace.Wrap(err) + } + } + webCfg := webclient.WebConfig{ - Auth: authSettings, - CanJoinSessions: canJoinSessions, - IsCloud: clusterFeatures.GetCloud(), - TunnelPublicAddress: tunnelPublicAddr, - RecoveryCodesEnabled: clusterFeatures.GetRecoveryCodes(), - UI: h.getUIConfig(r.Context()), - IsDashboard: isDashboard(clusterFeatures), - IsUsageBasedBilling: clusterFeatures.GetIsUsageBased(), - AutomaticUpgrades: clusterFeatures.GetAutomaticUpgrades(), - AssistEnabled: assistEnabled, - HideInaccessibleFeatures: clusterFeatures.GetFeatureHiding(), - CustomTheme: clusterFeatures.GetCustomTheme(), + Auth: authSettings, + CanJoinSessions: canJoinSessions, + IsCloud: clusterFeatures.GetCloud(), + TunnelPublicAddress: tunnelPublicAddr, + RecoveryCodesEnabled: clusterFeatures.GetRecoveryCodes(), + UI: h.getUIConfig(r.Context()), + IsDashboard: isDashboard(clusterFeatures), + IsUsageBasedBilling: clusterFeatures.GetIsUsageBased(), + AutomaticUpgrades: automaticUpgradesEnabled, + AutomaticUpgradesTargetVersion: automaticUpgradesTargetVersion, + AssistEnabled: assistEnabled, + HideInaccessibleFeatures: clusterFeatures.GetFeatureHiding(), + CustomTheme: clusterFeatures.GetCustomTheme(), } resource, err := h.cfg.ProxyClient.GetClusterName() diff --git a/lib/web/apiserver_test.go b/lib/web/apiserver_test.go index 0136bc220e600..149add495f31e 100644 --- a/lib/web/apiserver_test.go +++ b/lib/web/apiserver_test.go @@ -4588,9 +4588,20 @@ func TestGetWebConfig(t *testing.T) { } env.proxies[0].handler.handler.cfg.ProxySettings = mockProxySetting + 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("v99.0.1")) + })) + defer httpTestServer.Close() + versionURL, err := url.JoinPath(httpTestServer.URL, "/v1/stable/cloud/version") + require.NoError(t, err) + env.proxies[0].handler.handler.cfg.AutomaticUpgradesVersionURL = versionURL + expectedCfg.IsCloud = true expectedCfg.IsUsageBasedBilling = true expectedCfg.AutomaticUpgrades = true + expectedCfg.AutomaticUpgradesTargetVersion = "v99.0.1" expectedCfg.AssistEnabled = true // request and verify enabled features are enabled. diff --git a/web/packages/teleport/src/Discover/Kubernetes/HelmChart/HelmChart.tsx b/web/packages/teleport/src/Discover/Kubernetes/HelmChart/HelmChart.tsx index b2db0262073f9..90409d978a0d0 100644 --- a/web/packages/teleport/src/Discover/Kubernetes/HelmChart/HelmChart.tsx +++ b/web/packages/teleport/src/Discover/Kubernetes/HelmChart/HelmChart.tsx @@ -318,8 +318,10 @@ const generateCmd = (data: { isEnterprise: boolean; isCloud: boolean; automaticUpgradesEnabled: boolean; + automaticUpgradesTargetVersion: string; }) => { let extraYAMLConfig = ''; + let deployVersion = data.clusterVersion; if (data.isEnterprise) { extraYAMLConfig += 'enterprise: true\n'; @@ -334,6 +336,11 @@ const generateCmd = (data: { extraYAMLConfig += ' podDisruptionBudget:\n'; extraYAMLConfig += ' enabled: true\n'; extraYAMLConfig += ' minAvailable: 1\n'; + + // Replace the helm version to deploy with the one coming from the AutomaticUpgrades Version URL. + // AutomaticUpgradesTargetVersion contains a v, eg, v13.4.2. + // However, helm chart expects no 'v', eg, 13.4.2. + deployVersion = data.automaticUpgradesTargetVersion.replace(/^v/, ''); } return `cat << EOF > prod-cluster-values.yaml @@ -345,7 +352,7 @@ labels: teleport.internal/resource-id: ${data.resourceId} ${extraYAMLConfig}EOF -helm install teleport-agent teleport/teleport-kube-agent -f prod-cluster-values.yaml --version ${data.clusterVersion} --create-namespace --namespace ${data.namespace}`; +helm install teleport-agent teleport/teleport-kube-agent -f prod-cluster-values.yaml --version ${deployVersion} --create-namespace --namespace ${data.namespace}`; }; const InstallHelmChart = ({ @@ -440,6 +447,7 @@ const InstallHelmChart = ({ isEnterprise: ctx.isEnterprise, isCloud: ctx.isCloud, automaticUpgradesEnabled: ctx.automaticUpgradesEnabled, + automaticUpgradesTargetVersion: ctx.automaticUpgradesTargetVersion, }); return ( diff --git a/web/packages/teleport/src/config.ts b/web/packages/teleport/src/config.ts index 7b112eb133965..5b9bf51502c71 100644 --- a/web/packages/teleport/src/config.ts +++ b/web/packages/teleport/src/config.ts @@ -39,6 +39,7 @@ const cfg = { isCloud: false, assistEnabled: false, automaticUpgrades: false, + automaticUpgradesTargetVersion: '', isDashboard: false, tunnelPublicAddress: '', recoveryCodesEnabled: false, diff --git a/web/packages/teleport/src/teleportContext.tsx b/web/packages/teleport/src/teleportContext.tsx index babefbe3329de..f2c0f6628dbaf 100644 --- a/web/packages/teleport/src/teleportContext.tsx +++ b/web/packages/teleport/src/teleportContext.tsx @@ -60,6 +60,7 @@ class TeleportContext implements types.Context { isEnterprise = cfg.isEnterprise; isCloud = cfg.isCloud; automaticUpgradesEnabled = cfg.automaticUpgrades; + automaticUpgradesTargetVersion = cfg.automaticUpgradesTargetVersion; assistEnabled = cfg.assistEnabled; agentService = agentService;