Skip to content

Commit

Permalink
Move autoupdate code in proxy to make more sense (#49484)
Browse files Browse the repository at this point in the history
* Move autoupdate code in proxy to make more sense

* lint + godoc
  • Loading branch information
hugoShaka authored Nov 27, 2024
1 parent a73ce89 commit fb26137
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 94 deletions.
88 changes: 3 additions & 85 deletions lib/web/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,16 @@ import (
"google.golang.org/protobuf/types/known/timestamppb"

"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api"
apiclient "github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/client/webclient"
"github.com/gravitational/teleport/api/constants"
apidefaults "github.com/gravitational/teleport/api/defaults"
autoupdatepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1"
mfav1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/mfa/v1"
notificationsv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/notifications/v1"
"github.com/gravitational/teleport/api/mfa"
apitracing "github.com/gravitational/teleport/api/observability/tracing"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/types/autoupdate"
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/api/types/installers"
"github.com/gravitational/teleport/api/utils/keys"
Expand Down Expand Up @@ -1074,7 +1071,7 @@ func (h *Handler) bindDefaultEndpoints() {

// Implements the agent version server.
// Channel can contain "/", hence the use of a catch-all parameter
h.GET("/webapi/automaticupgrades/channel/*request", h.WithUnauthenticatedHighLimiter(h.automaticUpgrades))
h.GET("/webapi/automaticupgrades/channel/*request", h.WithUnauthenticatedHighLimiter(h.automaticUpgrades109))

// GET Machine ID bot by name
h.GET("/webapi/sites/:site/machine-id/bot/:name", h.WithClusterAuth(h.getBot))
Expand Down Expand Up @@ -1557,7 +1554,7 @@ func (h *Handler) ping(w http.ResponseWriter, r *http.Request, p httprouter.Para
MinClientVersion: teleport.MinClientVersion,
ClusterName: h.auth.clusterName,
AutomaticUpgrades: pr.ServerFeatures.GetAutomaticUpgrades(),
AutoUpdate: h.automaticUpdateSettings(r.Context()),
AutoUpdate: h.automaticUpdateSettings184(r.Context()),
Edition: modules.GetModules().BuildType(),
FIPS: modules.IsBoringBinary(),
}, nil
Expand All @@ -1584,7 +1581,7 @@ func (h *Handler) find(w http.ResponseWriter, r *http.Request, p httprouter.Para
ClusterName: h.auth.clusterName,
Edition: modules.GetModules().BuildType(),
FIPS: modules.IsBoringBinary(),
AutoUpdate: h.automaticUpdateSettings(ctx),
AutoUpdate: h.automaticUpdateSettings184(ctx),
}, nil
})
if err != nil {
Expand All @@ -1593,29 +1590,6 @@ func (h *Handler) find(w http.ResponseWriter, r *http.Request, p httprouter.Para
return resp, nil
}

// TODO: add the request as a parameter when we'll need to modulate the content based on the UUID and group
func (h *Handler) automaticUpdateSettings(ctx context.Context) webclient.AutoUpdateSettings {
autoUpdateConfig, err := h.cfg.AccessPoint.GetAutoUpdateConfig(ctx)
// TODO(vapopov) DELETE IN v18.0.0 check of IsNotImplemented, must be backported to all latest supported versions.
if err != nil && !trace.IsNotFound(err) && !trace.IsNotImplemented(err) {
h.logger.ErrorContext(ctx, "failed to receive AutoUpdateConfig", "error", err)
}

autoUpdateVersion, err := h.cfg.AccessPoint.GetAutoUpdateVersion(ctx)
// TODO(vapopov) DELETE IN v18.0.0 check of IsNotImplemented, must be backported to all latest supported versions.
if err != nil && !trace.IsNotFound(err) && !trace.IsNotImplemented(err) {
h.logger.ErrorContext(ctx, "failed to receive AutoUpdateVersion", "error", err)
}

return webclient.AutoUpdateSettings{
ToolsAutoUpdate: getToolsAutoUpdate(autoUpdateConfig),
ToolsVersion: getToolsVersion(autoUpdateVersion),
AgentUpdateJitterSeconds: DefaultAgentUpdateJitterSeconds,
AgentVersion: getAgentVersion(autoUpdateVersion),
AgentAutoUpdate: agentShouldUpdate(autoUpdateConfig, autoUpdateVersion),
}
}

func (h *Handler) pingWithConnector(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
authClient := h.cfg.ProxyClient
connectorName := p.ByName("connector")
Expand Down Expand Up @@ -5207,59 +5181,3 @@ func readEtagFromAppHash(fs http.FileSystem) (string, error) {

return etag, nil
}

func getToolsAutoUpdate(config *autoupdatepb.AutoUpdateConfig) bool {
// If we can't get the AU config or if AUs are not configured, we default to "disabled".
// This ensures we fail open and don't accidentally update agents if something is going wrong.
// If we want to enable AUs by default, it would be better to create a default "autoupdate_config" resource
// than changing this logic.
if config.GetSpec().GetTools() != nil {
return config.GetSpec().GetTools().GetMode() == autoupdate.ToolsUpdateModeEnabled
}
return false
}

func getToolsVersion(version *autoupdatepb.AutoUpdateVersion) string {
// If we can't get the AU version or tools AU version is not specified, we default to the current proxy version.
// This ensures we always advertise a version compatible with the cluster.
if version.GetSpec().GetTools() == nil {
return api.Version
}
return version.GetSpec().GetTools().GetTargetVersion()
}

func getAgentVersion(version *autoupdatepb.AutoUpdateVersion) string {
// If we can't get the AU version or tools AU version is not specified, we default to the current proxy version.
// This ensures we always advertise a version compatible with the cluster.
// TODO: read the version from the autoupdate_agent_rollout when the resource is implemented
if version.GetSpec().GetAgents() == nil {
return api.Version
}

return version.GetSpec().GetAgents().GetTargetVersion()
}

func agentShouldUpdate(config *autoupdatepb.AutoUpdateConfig, version *autoupdatepb.AutoUpdateVersion) bool {
// TODO: read the data from the autoupdate_agent_rollout when the resource is implemented

// If we can't get the AU config or if AUs are not configured, we default to "disabled".
// This ensures we fail open and don't accidentally update agents if something is going wrong.
// If we want to enable AUs by default, it would be better to create a default "autoupdate_config" resource
// than changing this logic.
if config.GetSpec().GetAgents() == nil {
return false
}
if version.GetSpec().GetAgents() == nil {
return false
}
configMode := config.GetSpec().GetAgents().GetMode()
versionMode := version.GetSpec().GetAgents().GetMode()

// We update only if both version and config agent modes are "enabled"
if configMode != autoupdate.AgentsUpdateModeEnabled || versionMode != autoupdate.AgentsUpdateModeEnabled {
return false
}

scheduleName := version.GetSpec().GetAgents().GetSchedule()
return scheduleName == autoupdate.AgentsScheduleImmediate
}
18 changes: 9 additions & 9 deletions lib/web/automaticupgrades.go → lib/web/autoupdate_rfd109.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
* Copyright (C) 2024 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
Expand Down Expand Up @@ -35,10 +35,10 @@ import (

const defaultChannelTimeout = 5 * time.Second

// automaticUpgrades implements a version server in the Teleport Proxy.
// automaticUpgrades implements a version server in the Teleport Proxy following the RFD 109 spec.
// It is configured through the Teleport Proxy configuration and tells agent updaters
// which version they should install.
func (h *Handler) automaticUpgrades(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
func (h *Handler) automaticUpgrades109(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
if h.cfg.AutomaticUpgradesChannels == nil {
return nil, trace.AccessDenied("This proxy is not configured to serve automatic upgrades channels.")
}
Expand Down Expand Up @@ -69,17 +69,17 @@ func (h *Handler) automaticUpgrades(w http.ResponseWriter, r *http.Request, p ht
switch requestType {
case "version":
h.log.Debugf("Agent requesting version for channel %s", channelName)
return h.automaticUpgradesVersion(w, r, channel)
return h.automaticUpgradesVersion109(w, r, channel)
case "critical":
h.log.Debugf("Agent requesting criticality for channel %s", channelName)
return h.automaticUpgradesCritical(w, r, channel)
return h.automaticUpgradesCritical109(w, r, channel)
default:
return nil, trace.BadParameter("requestType path must end with 'version' or 'critical'")
}
}

// automaticUpgradesVersion handles version requests from upgraders
func (h *Handler) automaticUpgradesVersion(w http.ResponseWriter, r *http.Request, channel *automaticupgrades.Channel) (interface{}, error) {
// automaticUpgradesVersion109 handles version requests from upgraders
func (h *Handler) automaticUpgradesVersion109(w http.ResponseWriter, r *http.Request, channel *automaticupgrades.Channel) (interface{}, error) {
ctx, cancel := context.WithTimeout(r.Context(), defaultChannelTimeout)
defer cancel()

Expand All @@ -100,8 +100,8 @@ func (h *Handler) automaticUpgradesVersion(w http.ResponseWriter, r *http.Reques
return nil, trace.Wrap(err)
}

// automaticUpgradesCritical handles criticality requests from upgraders
func (h *Handler) automaticUpgradesCritical(w http.ResponseWriter, r *http.Request, channel *automaticupgrades.Channel) (interface{}, error) {
// automaticUpgradesCritical109 handles criticality requests from upgraders
func (h *Handler) automaticUpgradesCritical109(w http.ResponseWriter, r *http.Request, channel *automaticupgrades.Channel) (interface{}, error) {
ctx, cancel := context.WithTimeout(r.Context(), defaultChannelTimeout)
defer cancel()

Expand Down
111 changes: 111 additions & 0 deletions lib/web/autoupdate_rfd184.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Teleport
* Copyright (C) 2024 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 <http://www.gnu.org/licenses/>.
*/

package web

import (
"context"

"github.com/gravitational/trace"

"github.com/gravitational/teleport/api"
"github.com/gravitational/teleport/api/client/webclient"
autoupdatepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1"
"github.com/gravitational/teleport/api/types/autoupdate"
)

// automaticUpdateSettings184 crafts the automatic updates part of the ping/find response
// as described in RFD-184 (agents) and RFD-144 (tools).
// TODO: add the request as a parameter when we'll need to modulate the content based on the UUID and group
func (h *Handler) automaticUpdateSettings184(ctx context.Context) webclient.AutoUpdateSettings {
autoUpdateConfig, err := h.cfg.AccessPoint.GetAutoUpdateConfig(ctx)
// TODO(vapopov) DELETE IN v18.0.0 check of IsNotImplemented, must be backported to all latest supported versions.
if err != nil && !trace.IsNotFound(err) && !trace.IsNotImplemented(err) {
h.logger.ErrorContext(ctx, "failed to receive AutoUpdateConfig", "error", err)
}

autoUpdateVersion, err := h.cfg.AccessPoint.GetAutoUpdateVersion(ctx)
// TODO(vapopov) DELETE IN v18.0.0 check of IsNotImplemented, must be backported to all latest supported versions.
if err != nil && !trace.IsNotFound(err) && !trace.IsNotImplemented(err) {
h.logger.ErrorContext(ctx, "failed to receive AutoUpdateVersion", "error", err)
}

return webclient.AutoUpdateSettings{
ToolsAutoUpdate: getToolsAutoUpdate(autoUpdateConfig),
ToolsVersion: getToolsVersion(autoUpdateVersion),
AgentUpdateJitterSeconds: DefaultAgentUpdateJitterSeconds,
AgentVersion: getAgentVersion184(autoUpdateVersion),
AgentAutoUpdate: agentShouldUpdate184(autoUpdateConfig, autoUpdateVersion),
}
}

func getToolsAutoUpdate(config *autoupdatepb.AutoUpdateConfig) bool {
// If we can't get the AU config or if AUs are not configured, we default to "disabled".
// This ensures we fail open and don't accidentally update agents if something is going wrong.
// If we want to enable AUs by default, it would be better to create a default "autoupdate_config" resource
// than changing this logic.
if config.GetSpec().GetTools() != nil {
return config.GetSpec().GetTools().GetMode() == autoupdate.ToolsUpdateModeEnabled
}
return false
}

func getToolsVersion(version *autoupdatepb.AutoUpdateVersion) string {
// If we can't get the AU version or tools AU version is not specified, we default to the current proxy version.
// This ensures we always advertise a version compatible with the cluster.
if version.GetSpec().GetTools() == nil {
return api.Version
}
return version.GetSpec().GetTools().GetTargetVersion()
}

func getAgentVersion184(version *autoupdatepb.AutoUpdateVersion) string {
// If we can't get the AU version or tools AU version is not specified, we default to the current proxy version.
// This ensures we always advertise a version compatible with the cluster.
// TODO: read the version from the autoupdate_agent_rollout when the resource is implemented
if version.GetSpec().GetAgents() == nil {
return api.Version
}

return version.GetSpec().GetAgents().GetTargetVersion()
}

func agentShouldUpdate184(config *autoupdatepb.AutoUpdateConfig, version *autoupdatepb.AutoUpdateVersion) bool {
// TODO: read the data from the autoupdate_agent_rollout when the resource is implemented

// If we can't get the AU config or if AUs are not configured, we default to "disabled".
// This ensures we fail open and don't accidentally update agents if something is going wrong.
// If we want to enable AUs by default, it would be better to create a default "autoupdate_config" resource
// than changing this logic.
if config.GetSpec().GetAgents() == nil {
return false
}
if version.GetSpec().GetAgents() == nil {
return false
}
configMode := config.GetSpec().GetAgents().GetMode()
versionMode := version.GetSpec().GetAgents().GetMode()

// We update only if both version and config agent modes are "enabled"
if configMode != autoupdate.AgentsUpdateModeEnabled || versionMode != autoupdate.AgentsUpdateModeEnabled {
return false
}

scheduleName := version.GetSpec().GetAgents().GetSchedule()
return scheduleName == autoupdate.AgentsScheduleImmediate
}

0 comments on commit fb26137

Please sign in to comment.