Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v14] Expose client tools auto update for find endpoint #47215

Open
wants to merge 2 commits into
base: branch/v14
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions api/client/webclient/webclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,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 @@ -329,6 +331,14 @@ type ProxySettings struct {
AssistEnabled bool `json:"assist_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"`
// ToolsMode defines mode client auto update feature `enabled|disabled`.
ToolsMode string `json:"tools_mode"`
}

// KubeProxySettings is kubernetes proxy settings
type KubeProxySettings struct {
// Enabled is true when kubernetes proxy is enabled
Expand Down
258 changes: 199 additions & 59 deletions api/gen/proto/go/teleport/autoupdate/v1/autoupdate.pb.go

Large diffs are not rendered by default.

23 changes: 19 additions & 4 deletions api/proto/teleport/autoupdate/v1/autoupdate.proto
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,15 @@ message AutoUpdateConfig {

// AutoUpdateConfigSpec encodes the parameters of the autoupdate config object.
message AutoUpdateConfigSpec {
// ToolsAutoupdate encodes the feature flag to enable/disable tools autoupdates.
bool tools_autoupdate = 1;
reserved 1;
reserved "tools_autoupdate"; // ToolsAutoupdate is replaced by tools.mode.
AutoUpdateConfigSpecTools tools = 2;
}

// AutoUpdateConfigSpecTools encodes the parameters for client tools auto updates.
message AutoUpdateConfigSpecTools {
// Mode defines state of the client tools auto update.
string mode = 1;
}

// AutoUpdateVersion is a resource singleton with version required for
Expand All @@ -50,6 +57,14 @@ message AutoUpdateVersion {

// AutoUpdateVersionSpec encodes the parameters of the autoupdate versions.
message AutoUpdateVersionSpec {
// ToolsVersion is the semantic version required for tools autoupdates.
string tools_version = 1;
reserved 1;
reserved "tools_version"; // ToolsVersion is replaced by tools.target_version.
AutoUpdateVersionSpecTools tools = 2;
}

// AutoUpdateVersionSpecTools encodes the parameters for client tools auto updates.
message AutoUpdateVersionSpecTools {
// TargetVersion specifies the semantic version required for tools to establish a connection with the cluster.
// Client tools after connection to the cluster going to be updated to this version automatically.
string target_version = 1;
}
12 changes: 12 additions & 0 deletions api/types/autoupdate/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ import (
"github.com/gravitational/teleport/api/types"
)

const (
// ToolsUpdateModeEnabled enables client tools automatic updates.
ToolsUpdateModeEnabled = "enabled"
// ToolsUpdateModeDisabled disables client tools automatic updates.
ToolsUpdateModeDisabled = "disabled"
)

// NewAutoUpdateConfig creates a new auto update configuration resource.
func NewAutoUpdateConfig(spec *autoupdate.AutoUpdateConfigSpec) (*autoupdate.AutoUpdateConfig, error) {
config := &autoupdate.AutoUpdateConfig{
Expand Down Expand Up @@ -58,6 +65,11 @@ func ValidateAutoUpdateConfig(c *autoupdate.AutoUpdateConfig) error {
if c.Spec == nil {
return trace.BadParameter("Spec is nil")
}
if c.Spec.Tools != nil {
if c.Spec.Tools.Mode != ToolsUpdateModeDisabled && c.Spec.Tools.Mode != ToolsUpdateModeEnabled {
return trace.BadParameter("ToolsMode is not valid")
}
}

return nil
}
27 changes: 23 additions & 4 deletions api/types/autoupdate/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ func TestNewAutoUpdateConfig(t *testing.T) {
{
name: "success tools autoupdate disabled",
spec: &autoupdate.AutoUpdateConfigSpec{
ToolsAutoupdate: false,
Tools: &autoupdate.AutoUpdateConfigSpecTools{
Mode: ToolsUpdateModeDisabled,
},
},
assertErr: func(t *testing.T, err error, a ...any) {
require.NoError(t, err)
Expand All @@ -53,14 +55,18 @@ func TestNewAutoUpdateConfig(t *testing.T) {
Name: types.MetaNameAutoUpdateConfig,
},
Spec: &autoupdate.AutoUpdateConfigSpec{
ToolsAutoupdate: false,
Tools: &autoupdate.AutoUpdateConfigSpecTools{
Mode: ToolsUpdateModeDisabled,
},
},
},
},
{
name: "success tools autoupdate enabled",
spec: &autoupdate.AutoUpdateConfigSpec{
ToolsAutoupdate: true,
Tools: &autoupdate.AutoUpdateConfigSpecTools{
Mode: ToolsUpdateModeEnabled,
},
},
assertErr: func(t *testing.T, err error, a ...any) {
require.NoError(t, err)
Expand All @@ -72,7 +78,9 @@ func TestNewAutoUpdateConfig(t *testing.T) {
Name: types.MetaNameAutoUpdateConfig,
},
Spec: &autoupdate.AutoUpdateConfigSpec{
ToolsAutoupdate: true,
Tools: &autoupdate.AutoUpdateConfigSpecTools{
Mode: ToolsUpdateModeEnabled,
},
},
},
},
Expand All @@ -83,6 +91,17 @@ func TestNewAutoUpdateConfig(t *testing.T) {
require.ErrorContains(t, err, "Spec is nil")
},
},
{
name: "invalid tools mode",
spec: &autoupdate.AutoUpdateConfigSpec{
Tools: &autoupdate.AutoUpdateConfigSpecTools{
Mode: "invalid-mode",
},
},
assertErr: func(t *testing.T, err error, a ...any) {
require.ErrorContains(t, err, "ToolsMode is not valid")
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
12 changes: 7 additions & 5 deletions api/types/autoupdate/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,13 @@ func ValidateAutoUpdateVersion(v *autoupdate.AutoUpdateVersion) error {
return trace.BadParameter("Spec is nil")
}

if v.Spec.ToolsVersion == "" {
return trace.BadParameter("ToolsVersion is unset")
}
if _, err := semver.NewVersion(v.Spec.ToolsVersion); err != nil {
return trace.BadParameter("ToolsVersion is not a valid semantic version")
if v.Spec.Tools != nil {
if v.Spec.Tools.TargetVersion == "" {
return trace.BadParameter("TargetVersion is unset")
}
if _, err := semver.NewVersion(v.Spec.Tools.TargetVersion); err != nil {
return trace.BadParameter("TargetVersion is not a valid semantic version")
}
}

return nil
Expand Down
20 changes: 14 additions & 6 deletions api/types/autoupdate/version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ func TestNewAutoUpdateVersion(t *testing.T) {
{
name: "success tools autoupdate version",
spec: &autoupdate.AutoUpdateVersionSpec{
ToolsVersion: "1.2.3-dev",
Tools: &autoupdate.AutoUpdateVersionSpecTools{
TargetVersion: "1.2.3-dev",
},
},
assertErr: func(t *testing.T, err error, a ...any) {
require.NoError(t, err)
Expand All @@ -53,26 +55,32 @@ func TestNewAutoUpdateVersion(t *testing.T) {
Name: types.MetaNameAutoUpdateVersion,
},
Spec: &autoupdate.AutoUpdateVersionSpec{
ToolsVersion: "1.2.3-dev",
Tools: &autoupdate.AutoUpdateVersionSpecTools{
TargetVersion: "1.2.3-dev",
},
},
},
},
{
name: "invalid empty tools version",
spec: &autoupdate.AutoUpdateVersionSpec{
ToolsVersion: "",
Tools: &autoupdate.AutoUpdateVersionSpecTools{
TargetVersion: "",
},
},
assertErr: func(t *testing.T, err error, a ...any) {
require.ErrorContains(t, err, "ToolsVersion is unset")
require.ErrorContains(t, err, "TargetVersion is unset")
},
},
{
name: "invalid semantic tools version",
spec: &autoupdate.AutoUpdateVersionSpec{
ToolsVersion: "17-0-0",
Tools: &autoupdate.AutoUpdateVersionSpecTools{
TargetVersion: "17-0-0",
},
},
assertErr: func(t *testing.T, err error, a ...any) {
require.ErrorContains(t, err, "ToolsVersion is not a valid semantic version")
require.ErrorContains(t, err, "TargetVersion is not a valid semantic version")
},
},
{
Expand Down
8 changes: 6 additions & 2 deletions lib/cache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3563,7 +3563,9 @@ func newAutoUpdateConfig(t *testing.T) *autoupdate.AutoUpdateConfig {
t.Helper()

r, err := update.NewAutoUpdateConfig(&autoupdate.AutoUpdateConfigSpec{
ToolsAutoupdate: true,
Tools: &autoupdate.AutoUpdateConfigSpecTools{
Mode: update.ToolsUpdateModeEnabled,
},
})
require.NoError(t, err)
return r
Expand All @@ -3573,7 +3575,9 @@ func newAutoUpdateVersion(t *testing.T) *autoupdate.AutoUpdateVersion {
t.Helper()

r, err := update.NewAutoUpdateVersion(&autoupdate.AutoUpdateVersionSpec{
ToolsVersion: "1.2.3",
Tools: &autoupdate.AutoUpdateVersionSpecTools{
TargetVersion: "1.2.3",
},
})
require.NoError(t, err)
return r
Expand Down
48 changes: 38 additions & 10 deletions lib/services/local/autoupdate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ func TestAutoUpdateServiceConfigCRUD(t *testing.T) {
Kind: types.KindAutoUpdateConfig,
Version: types.V1,
Metadata: &headerv1.Metadata{Name: types.MetaNameAutoUpdateConfig},
Spec: &autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true},
Spec: &autoupdatev1pb.AutoUpdateConfigSpec{
Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{
Mode: autoupdate.ToolsUpdateModeEnabled,
},
},
}

created, err := service.CreateAutoUpdateConfig(ctx, config)
Expand All @@ -71,10 +75,12 @@ func TestAutoUpdateServiceConfigCRUD(t *testing.T) {
require.Empty(t, diff)
require.Equal(t, created.GetMetadata().GetRevision(), got.GetMetadata().GetRevision())

config.Spec.ToolsAutoupdate = false
config.Spec.Tools = &autoupdatev1pb.AutoUpdateConfigSpecTools{
Mode: autoupdate.ToolsUpdateModeDisabled,
}
updated, err := service.UpdateAutoUpdateConfig(ctx, config)
require.NoError(t, err)
require.NotEqual(t, got.GetSpec().GetToolsAutoupdate(), updated.GetSpec().GetToolsAutoupdate())
require.NotEqual(t, got.GetSpec().GetTools(), updated.GetSpec().GetTools())

_, err = service.UpsertAutoUpdateConfig(ctx, config)
require.NoError(t, err)
Expand Down Expand Up @@ -106,7 +112,11 @@ func TestAutoUpdateServiceVersionCRUD(t *testing.T) {
Kind: types.KindAutoUpdateVersion,
Version: types.V1,
Metadata: &headerv1.Metadata{Name: types.MetaNameAutoUpdateVersion},
Spec: &autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"},
Spec: &autoupdatev1pb.AutoUpdateVersionSpec{
Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{
TargetVersion: "1.2.3",
},
},
}

created, err := service.CreateAutoUpdateVersion(ctx, version)
Expand All @@ -126,10 +136,12 @@ func TestAutoUpdateServiceVersionCRUD(t *testing.T) {
require.Empty(t, diff)
require.Equal(t, created.GetMetadata().GetRevision(), got.GetMetadata().GetRevision())

version.Spec.ToolsVersion = "3.2.1"
version.Spec.Tools = &autoupdatev1pb.AutoUpdateVersionSpecTools{
TargetVersion: "3.2.1",
}
updated, err := service.UpdateAutoUpdateVersion(ctx, version)
require.NoError(t, err)
require.NotEqual(t, got.GetSpec().GetToolsVersion(), updated.GetSpec().GetToolsVersion())
require.NotEqual(t, got.GetSpec().GetTools().GetTargetVersion(), updated.GetSpec().GetTools().GetTargetVersion())

_, err = service.UpsertAutoUpdateVersion(ctx, version)
require.NoError(t, err)
Expand Down Expand Up @@ -161,7 +173,11 @@ func TestAutoUpdateServiceInvalidNameCreate(t *testing.T) {
Kind: types.KindAutoUpdateConfig,
Version: types.V1,
Metadata: &headerv1.Metadata{Name: "invalid-auto-update-config-name"},
Spec: &autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true},
Spec: &autoupdatev1pb.AutoUpdateConfigSpec{
Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{
Mode: autoupdate.ToolsUpdateModeEnabled,
},
},
}

createdConfig, err := service.CreateAutoUpdateConfig(ctx, config)
Expand All @@ -172,7 +188,11 @@ func TestAutoUpdateServiceInvalidNameCreate(t *testing.T) {
Kind: types.KindAutoUpdateVersion,
Version: types.V1,
Metadata: &headerv1.Metadata{Name: "invalid-auto-update-version-name"},
Spec: &autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"},
Spec: &autoupdatev1pb.AutoUpdateVersionSpec{
Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{
TargetVersion: "1.2.3",
},
},
}

createdVersion, err := service.CreateAutoUpdateVersion(ctx, version)
Expand All @@ -194,7 +214,11 @@ func TestAutoUpdateServiceInvalidNameUpdate(t *testing.T) {
ctx := context.Background()

// Validate the config update restriction.
config, err := autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true})
config, err := autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{
Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{
Mode: autoupdate.ToolsUpdateModeEnabled,
},
})
require.NoError(t, err)

createdConfig, err := service.UpsertAutoUpdateConfig(ctx, config)
Expand All @@ -207,7 +231,11 @@ func TestAutoUpdateServiceInvalidNameUpdate(t *testing.T) {
require.Nil(t, createdConfig)

// Validate the version update restriction.
version, err := autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"})
version, err := autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{
Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{
TargetVersion: "1.2.3",
},
})
require.NoError(t, err)

createdVersion, err := service.UpsertAutoUpdateVersion(ctx, version)
Expand Down
22 changes: 20 additions & 2 deletions lib/web/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1415,12 +1415,30 @@ 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{
Proxy: *proxyConfig,
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.log.WithError(err).Error("failed to receive AutoUpdateConfig")
} else if err == nil {
response.AutoUpdate.ToolsMode = autoUpdateConfig.GetSpec().GetTools().GetMode()
}

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.log.WithError(err).Error("failed to receive AutoUpdateVersion")
} else if err == nil {
response.AutoUpdate.ToolsVersion = autoUpdateVersion.GetSpec().GetTools().GetTargetVersion()
}

return response, nil
}

func (h *Handler) pingWithConnector(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
Expand Down
Loading
Loading