Skip to content

Commit

Permalink
Make the proxy read from autoupdate_agent_rollout (#49380)
Browse files Browse the repository at this point in the history
* Add autoupdate_agenbt_rollout support

* fix ping proxy tests

* address creack's feedback

* Address sclevine's feedback

Co-authored-by: Stephen Levine <[email protected]>

* fix panic in tests

---------

Co-authored-by: Stephen Levine <[email protected]>
  • Loading branch information
hugoShaka and sclevine authored Dec 5, 2024
1 parent f7bf7eb commit a33473d
Show file tree
Hide file tree
Showing 7 changed files with 1,116 additions and 86 deletions.
13 changes: 11 additions & 2 deletions api/client/webclient/webclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ import (
"github.com/gravitational/teleport/api/utils/keys"
)

const (
// AgentUpdateGroupParameter is the parameter used to specify the updater
// group when doing a Ping() or Find() query.
// The proxy server will modulate the auto_update part of the PingResponse
// based on the specified group. e.g. some groups might need to update
// before others.
AgentUpdateGroupParameter = "group"
)

// Config specifies information when building requests with the
// webclient.
type Config struct {
Expand Down Expand Up @@ -183,7 +192,7 @@ func findWithClient(cfg *Config, clt *http.Client) (*PingResponse, error) {
}
if cfg.UpdateGroup != "" {
endpoint.RawQuery = url.Values{
"group": []string{cfg.UpdateGroup},
AgentUpdateGroupParameter: []string{cfg.UpdateGroup},
}.Encode()
}

Expand Down Expand Up @@ -232,7 +241,7 @@ func pingWithClient(cfg *Config, clt *http.Client) (*PingResponse, error) {
}
if cfg.UpdateGroup != "" {
endpoint.RawQuery = url.Values{
"group": []string{cfg.UpdateGroup},
AgentUpdateGroupParameter: []string{cfg.UpdateGroup},
}.Encode()
}
if cfg.ConnectorName != "" {
Expand Down
29 changes: 26 additions & 3 deletions lib/web/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ type Handler struct {
// rate-limits, each call must cause minimal work. The cached answer can be modulated after, for example if the
// caller specified its Automatic Updates UUID or group.
findEndpointCache *utils.FnCache

// clusterMaintenanceConfig is used to cache the cluster maintenance config from the AUth Service.
clusterMaintenanceConfigCache *utils.FnCache
}

// HandlerOption is a functional argument - an option that can be passed
Expand Down Expand Up @@ -480,6 +483,18 @@ func NewHandler(cfg Config, opts ...HandlerOption) (*APIHandler, error) {
}
h.findEndpointCache = findCache

// We create the cache after applying the options to make sure we use the fake clock if it was passed.
cmcCache, err := utils.NewFnCache(utils.FnCacheConfig{
TTL: findEndpointCacheTTL,
Clock: h.clock,
Context: cfg.Context,
ReloadOnErr: false,
})
if err != nil {
return nil, trace.Wrap(err, "creating /find cache")
}
h.clusterMaintenanceConfigCache = cmcCache

sessionLingeringThreshold := cachedSessionLingeringThreshold
if cfg.CachedSessionLingeringThreshold != nil {
sessionLingeringThreshold = *cfg.CachedSessionLingeringThreshold
Expand Down Expand Up @@ -1527,22 +1542,30 @@ func (h *Handler) ping(w http.ResponseWriter, r *http.Request, p httprouter.Para
return nil, trace.Wrap(err)
}

group := r.URL.Query().Get(webclient.AgentUpdateGroupParameter)

return webclient.PingResponse{
Auth: authSettings,
Proxy: *proxyConfig,
ServerVersion: teleport.Version,
MinClientVersion: teleport.MinClientVersion,
ClusterName: h.auth.clusterName,
AutomaticUpgrades: pr.ServerFeatures.GetAutomaticUpgrades(),
AutoUpdate: h.automaticUpdateSettings184(r.Context()),
AutoUpdate: h.automaticUpdateSettings184(r.Context(), group, "" /* updater UUID */),
Edition: modules.GetModules().BuildType(),
FIPS: modules.IsBoringBinary(),
}, nil
}

func (h *Handler) find(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
group := r.URL.Query().Get(webclient.AgentUpdateGroupParameter)
cacheKey := "find"
if group != "" {
cacheKey += "-" + group
}

// cache the generic answer to avoid doing work for each request
resp, err := utils.FnCacheGet[*webclient.PingResponse](r.Context(), h.findEndpointCache, "find", func(ctx context.Context) (*webclient.PingResponse, error) {
resp, err := utils.FnCacheGet[*webclient.PingResponse](r.Context(), h.findEndpointCache, cacheKey, func(ctx context.Context) (*webclient.PingResponse, error) {
proxyConfig, err := h.cfg.ProxySettings.GetProxySettings(ctx)
if err != nil {
return nil, trace.Wrap(err)
Expand All @@ -1561,7 +1584,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.automaticUpdateSettings184(ctx),
AutoUpdate: h.automaticUpdateSettings184(ctx, group, "" /* updater UUID */),
}, nil
})
if err != nil {
Expand Down
48 changes: 21 additions & 27 deletions lib/web/apiserver_ping_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ func TestPing_autoUpdateResources(t *testing.T) {
name string
config *autoupdatev1pb.AutoUpdateConfigSpec
version *autoupdatev1pb.AutoUpdateVersionSpec
rollout *autoupdatev1pb.AutoUpdateAgentRolloutSpec
cleanup bool
expected webclient.AutoUpdateSettings
}{
Expand Down Expand Up @@ -330,19 +331,12 @@ func TestPing_autoUpdateResources(t *testing.T) {
},
{
name: "enable agent auto update, immediate schedule",
config: &autoupdatev1pb.AutoUpdateConfigSpec{
Agents: &autoupdatev1pb.AutoUpdateConfigSpecAgents{
Mode: autoupdate.AgentsUpdateModeEnabled,
Strategy: autoupdate.AgentsStrategyHaltOnError,
},
},
version: &autoupdatev1pb.AutoUpdateVersionSpec{
Agents: &autoupdatev1pb.AutoUpdateVersionSpecAgents{
Mode: autoupdate.AgentsUpdateModeEnabled,
StartVersion: "1.2.3",
TargetVersion: "1.2.4",
Schedule: autoupdate.AgentsScheduleImmediate,
},
rollout: &autoupdatev1pb.AutoUpdateAgentRolloutSpec{
AutoupdateMode: autoupdate.AgentsUpdateModeEnabled,
Strategy: autoupdate.AgentsStrategyHaltOnError,
Schedule: autoupdate.AgentsScheduleImmediate,
StartVersion: "1.2.3",
TargetVersion: "1.2.4",
},
expected: webclient.AutoUpdateSettings{
ToolsVersion: api.Version,
Expand All @@ -354,20 +348,13 @@ func TestPing_autoUpdateResources(t *testing.T) {
cleanup: true,
},
{
name: "version enable agent auto update, but config disables them",
config: &autoupdatev1pb.AutoUpdateConfigSpec{
Agents: &autoupdatev1pb.AutoUpdateConfigSpecAgents{
Mode: autoupdate.AgentsUpdateModeDisabled,
Strategy: autoupdate.AgentsStrategyHaltOnError,
},
},
version: &autoupdatev1pb.AutoUpdateVersionSpec{
Agents: &autoupdatev1pb.AutoUpdateVersionSpecAgents{
Mode: autoupdate.AgentsUpdateModeEnabled,
StartVersion: "1.2.3",
TargetVersion: "1.2.4",
Schedule: autoupdate.AgentsScheduleImmediate,
},
name: "agent rollout present but AU mode is disabled",
rollout: &autoupdatev1pb.AutoUpdateAgentRolloutSpec{
AutoupdateMode: autoupdate.AgentsUpdateModeDisabled,
Strategy: autoupdate.AgentsStrategyHaltOnError,
Schedule: autoupdate.AgentsScheduleImmediate,
StartVersion: "1.2.3",
TargetVersion: "1.2.4",
},
expected: webclient.AutoUpdateSettings{
ToolsVersion: api.Version,
Expand Down Expand Up @@ -462,6 +449,12 @@ func TestPing_autoUpdateResources(t *testing.T) {
_, err = env.server.Auth().UpsertAutoUpdateVersion(ctx, version)
require.NoError(t, err)
}
if tc.rollout != nil {
rollout, err := autoupdate.NewAutoUpdateAgentRollout(tc.rollout)
require.NoError(t, err)
_, err = env.server.Auth().UpsertAutoUpdateAgentRollout(ctx, rollout)
require.NoError(t, err)
}

// expire the fn cache to force the next answer to be fresh
for _, proxy := range env.proxies {
Expand All @@ -480,6 +473,7 @@ func TestPing_autoUpdateResources(t *testing.T) {
if tc.cleanup {
require.NotErrorIs(t, env.server.Auth().DeleteAutoUpdateConfig(ctx), &trace.NotFoundError{})
require.NotErrorIs(t, env.server.Auth().DeleteAutoUpdateVersion(ctx), &trace.NotFoundError{})
require.NotErrorIs(t, env.server.Auth().DeleteAutoUpdateAgentRollout(ctx), &trace.NotFoundError{})
}
})
}
Expand Down
Loading

0 comments on commit a33473d

Please sign in to comment.