Skip to content

Commit

Permalink
Expose client tools auto update for find endpoint (#46785)
Browse files Browse the repository at this point in the history
* Expose client tools auto update for find endpoint

* Group auto update settings in find response
Log error instead returning error
Add tests auto update settings in find endpoint
Add check for not implemented error

* Add more test cases
  • Loading branch information
vapopov authored Oct 4, 2024
1 parent c4e8db6 commit eed38ad
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 2 deletions.
10 changes: 10 additions & 0 deletions api/client/webclient/webclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,8 @@ type PingResponse struct {
ServerVersion string `json:"server_version"`
// MinClientVersion is the minimum client version required by the server.
MinClientVersion string `json:"min_client_version"`
// AutoUpdateSettings contains the auto update settings.
AutoUpdate AutoUpdateSettings `json:"auto_update"`
// ClusterName contains the name of the Teleport cluster.
ClusterName string `json:"cluster_name"`

Expand Down Expand Up @@ -328,6 +330,14 @@ type ProxySettings struct {
TLSRoutingEnabled bool `json:"tls_routing_enabled"`
}

// AutoUpdateSettings contains information about the auto update requirements.
type AutoUpdateSettings struct {
// ToolsVersion defines the version of {tsh, tctl} for client auto update.
ToolsVersion string `json:"tools_version"`
// ToolsAutoUpdate enables client auto update feature.
ToolsAutoUpdate bool `json:"tools_auto_update"`
}

// KubeProxySettings is kubernetes proxy settings
type KubeProxySettings struct {
// Enabled is true when kubernetes proxy is enabled
Expand Down
22 changes: 20 additions & 2 deletions lib/web/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1520,7 +1520,7 @@ func (h *Handler) find(w http.ResponseWriter, r *http.Request, p httprouter.Para
if err != nil {
return nil, trace.Wrap(err)
}
return webclient.PingResponse{
response := webclient.PingResponse{
Auth: webclient.AuthenticationSettings{
// Nodes need the signature algorithm suite when joining to generate
// keys with the correct algorithm.
Expand All @@ -1530,7 +1530,25 @@ func (h *Handler) find(w http.ResponseWriter, r *http.Request, p httprouter.Para
ServerVersion: teleport.Version,
MinClientVersion: teleport.MinClientVersion,
ClusterName: h.auth.clusterName,
}, nil
}

autoUpdateConfig, err := h.cfg.AccessPoint.GetAutoUpdateConfig(r.Context())
// 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(r.Context(), "failed to receive AutoUpdateConfig", "error", err)
} else if err == nil {
response.AutoUpdate.ToolsAutoUpdate = autoUpdateConfig.GetSpec().GetToolsAutoupdate()
}

autoUpdateVersion, err := h.cfg.AccessPoint.GetAutoUpdateVersion(r.Context())
// 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(r.Context(), "failed to receive AutoUpdateVersion", "error", err)
} else if err == nil {
response.AutoUpdate.ToolsVersion = autoUpdateVersion.GetSpec().GetToolsVersion()
}

return response, nil
}

func (h *Handler) pingWithConnector(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
Expand Down
84 changes: 84 additions & 0 deletions lib/web/apiserver_ping_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@ import (
"testing"

"github.com/gravitational/roundtrip"
"github.com/gravitational/trace"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/gravitational/teleport/api/client/webclient"
"github.com/gravitational/teleport/api/constants"
autoupdatev1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/types/autoupdate"
"github.com/gravitational/teleport/lib/client"
"github.com/gravitational/teleport/lib/modules"
)
Expand Down Expand Up @@ -244,5 +247,86 @@ func TestPing_minimalAPI(t *testing.T) {
require.NoError(t, resp.Body.Close())
})
}
}

// TestPing_autoUpdateResources tests that find endpoint return correct data related to auto updates.
func TestPing_autoUpdateResources(t *testing.T) {
env := newWebPack(t, 1, func(cfg *proxyConfig) {
cfg.minimalHandler = true
})
proxy := env.proxies[0]

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

req, err := http.NewRequest(http.MethodGet, proxy.newClient(t).Endpoint("webapi", "find"), nil)
require.NoError(t, err)
req.Host = proxy.handler.handler.cfg.ProxyPublicAddrs[0].Host()

tests := []struct {
name string
config *autoupdatev1pb.AutoUpdateConfigSpec
version *autoupdatev1pb.AutoUpdateVersionSpec
cleanup bool
expected webclient.AutoUpdateSettings
}{
{
name: "resources not defined",
expected: webclient.AutoUpdateSettings{},
},
{
name: "enable auto update",
config: &autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true},
expected: webclient.AutoUpdateSettings{ToolsAutoUpdate: true},
cleanup: true,
},
{
name: "set auto update version",
version: &autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"},
expected: webclient.AutoUpdateSettings{ToolsVersion: "1.2.3"},
cleanup: true,
},
{
name: "enable auto update and set version",
config: &autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true},
version: &autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"},
expected: webclient.AutoUpdateSettings{ToolsAutoUpdate: true, ToolsVersion: "1.2.3"},
},
{
name: "modify auto update config and version",
config: &autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: false},
version: &autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "3.2.1"},
expected: webclient.AutoUpdateSettings{ToolsAutoUpdate: false, ToolsVersion: "3.2.1"},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if tc.config != nil {
config, err := autoupdate.NewAutoUpdateConfig(tc.config)
require.NoError(t, err)
_, err = env.server.Auth().UpsertAutoUpdateConfig(ctx, config)
require.NoError(t, err)
}
if tc.version != nil {
version, err := autoupdate.NewAutoUpdateVersion(tc.version)
require.NoError(t, err)
_, err = env.server.Auth().UpsertAutoUpdateVersion(ctx, version)
require.NoError(t, err)
}

resp, err := client.NewInsecureWebClient().Do(req)
require.NoError(t, err)

pr := &webclient.PingResponse{}
require.NoError(t, json.NewDecoder(resp.Body).Decode(pr))
require.NoError(t, resp.Body.Close())

assert.Equal(t, tc.expected, pr.AutoUpdate)

if tc.cleanup {
require.NotErrorIs(t, env.server.Auth().DeleteAutoUpdateConfig(ctx), &trace.NotFoundError{})
require.NotErrorIs(t, env.server.Auth().DeleteAutoUpdateVersion(ctx), &trace.NotFoundError{})
}
})
}
}

0 comments on commit eed38ad

Please sign in to comment.