Skip to content
Merged
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
2 changes: 1 addition & 1 deletion integrations/access/common/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (a *BaseApp) checkTeleportVersion(ctx context.Context) (proto.PingResponse,
}
return pong, trace.Wrap(err, "Unable to get Teleport server version")
}
err = utils.CheckVersion(pong.ServerVersion, minServerVersion)
err = utils.CheckMinVersion(pong.ServerVersion, minServerVersion)
return pong, trace.Wrap(err)
}

Expand Down
2 changes: 1 addition & 1 deletion integrations/access/email/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ func (a *App) checkTeleportVersion(ctx context.Context) (proto.PingResponse, err
log.Error("Unable to get Teleport server version")
return pong, trace.Wrap(err)
}
err = utils.CheckVersion(pong.ServerVersion, minServerVersion)
err = utils.CheckMinVersion(pong.ServerVersion, minServerVersion)
return pong, trace.Wrap(err)
}

Expand Down
2 changes: 1 addition & 1 deletion integrations/access/jira/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ func (a *App) checkTeleportVersion(ctx context.Context) (proto.PingResponse, err
log.Error("Unable to get Teleport server version")
return pong, trace.Wrap(err)
}
err = utils.CheckVersion(pong.ServerVersion, minServerVersion)
err = utils.CheckMinVersion(pong.ServerVersion, minServerVersion)
return pong, trace.Wrap(err)
}

Expand Down
2 changes: 1 addition & 1 deletion integrations/access/msteams/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ func (a *App) checkTeleportVersion(ctx context.Context) (proto.PingResponse, err
return pong, trace.Wrap(err)
}

err = utils.CheckVersion(pong.ServerVersion, minServerVersion)
err = utils.CheckMinVersion(pong.ServerVersion, minServerVersion)

return pong, trace.Wrap(err)
}
Expand Down
2 changes: 1 addition & 1 deletion integrations/access/opsgenie/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ func (a *App) checkTeleportVersion(ctx context.Context) (proto.PingResponse, err
log.Error("Unable to get Teleport server version")
return pong, trace.Wrap(err)
}
err = utils.CheckVersion(pong.ServerVersion, minServerVersion)
err = utils.CheckMinVersion(pong.ServerVersion, minServerVersion)
return pong, trace.Wrap(err)
}

Expand Down
2 changes: 1 addition & 1 deletion integrations/access/pagerduty/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ func (a *App) checkTeleportVersion(ctx context.Context) (proto.PingResponse, err
log.Error("Unable to get Teleport server version")
return pong, trace.Wrap(err)
}
err = utils.CheckVersion(pong.ServerVersion, minServerVersion)
err = utils.CheckMinVersion(pong.ServerVersion, minServerVersion)
return pong, trace.Wrap(err)
}

Expand Down
2 changes: 1 addition & 1 deletion integrations/access/servicenow/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ func (a *App) checkTeleportVersion(ctx context.Context) (proto.PingResponse, err
log.Error("Unable to get Teleport server version")
return pong, trace.Wrap(err)
}
err = utils.CheckVersion(pong.ServerVersion, minServerVersion)
err = utils.CheckMinVersion(pong.ServerVersion, minServerVersion)
return pong, trace.Wrap(err)
}

Expand Down
2 changes: 1 addition & 1 deletion integrations/terraform/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ func (p *Provider) checkTeleportVersion(ctx context.Context, client *client.Clie
resp.Diagnostics.AddError("Unable to get Teleport server version!", "Unable to get Teleport server version!")
return false
}
err = utils.CheckVersion(pong.ServerVersion, minServerVersion)
err = utils.CheckMinVersion(pong.ServerVersion, minServerVersion)
if err != nil {
log.WithError(err).Debug("Teleport version check error!")
resp.Diagnostics.AddError("Teleport version check error!", err.Error())
Expand Down
2 changes: 1 addition & 1 deletion lib/auth/authclient/clt.go
Original file line number Diff line number Diff line change
Expand Up @@ -1768,7 +1768,7 @@ func TryCreateAppSessionForClientCertV15(ctx context.Context, client CreateAppSe

// If the auth server is v16+, the client does not need to provide a pre-created app session.
const minServerVersion = "16.0.0-aa" // "-aa" matches all development versions
if utils.MeetsVersion(pingResp.ServerVersion, minServerVersion) {
if utils.MeetsMinVersion(pingResp.ServerVersion, minServerVersion) {
return "", nil
}

Expand Down
20 changes: 18 additions & 2 deletions lib/client/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -4167,7 +4167,7 @@ func (tc *TeleportClient) Ping(ctx context.Context) (*webclient.PingResponse, er

// Verify server->client and client->server compatibility.
if tc.CheckVersions {
if !utils.MeetsVersion(teleport.Version, pr.MinClientVersion) {
if !utils.MeetsMinVersion(teleport.Version, pr.MinClientVersion) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know that the minimum version check will even work with a cluster >=v16.0.0 now that unsupported client connections are terminated by Auth. In my testing I'm seeing an EOF error during login prior to getting into this check.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, it's because webclient.Ping above returns an empty pr.MinClientVersion string (so utils.MeetsVersion returns true). Do you maybe know why it happens?

fmt.Fprintf(tc.Stderr, `
WARNING
Detected potentially incompatible client and server versions.
Expand All @@ -4179,9 +4179,25 @@ Future versions of tsh will fail when incompatible versions are detected.
pr.MinClientVersion, teleport.Version, pr.MinClientVersion)
}

if !utils.MeetsMaxVersion(teleport.Version, pr.ServerVersion) {
serverVersionWithWildcards, err := utils.MajorSemverWithWildcards(pr.ServerVersion)
if err != nil {
return nil, trace.Wrap(err)
}
fmt.Fprintf(tc.Stderr, `
WARNING
Detected potentially incompatible client and server versions.
Maximum client version supported by the server is %v but you are using %v.
Please downgrade tsh to %v or use the --skip-version-check flag to bypass this check.
Future versions of tsh will fail when incompatible versions are detected.

`,
serverVersionWithWildcards, teleport.Version, serverVersionWithWildcards)
}

// Recent `tsh mfa` changes require at least Teleport v15.
const minServerVersion = "15.0.0-aa" // "-aa" matches all development versions
if !utils.MeetsVersion(pr.ServerVersion, minServerVersion) {
if !utils.MeetsMinVersion(pr.ServerVersion, minServerVersion) {
fmt.Fprintf(tc.Stderr, `
WARNING
Detected incompatible client and server versions.
Expand Down
2 changes: 1 addition & 1 deletion lib/service/awsoidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ func (updater *AWSOIDCDeployServiceUpdater) updateAWSOIDCDeployServices(ctx cont
return trace.Wrap(err)
}

if !utils.MeetsVersion(updater.TeleportClusterVersion, minServerVersion) {
if !utils.MeetsMinVersion(updater.TeleportClusterVersion, minServerVersion) {
updater.Log.DebugContext(ctx, "Skipping update. AWS OIDC Deploy Service will not be compatible with cluster", "cluster_version", updater.TeleportClusterVersion, "stable_version", stableVersion)
return nil
}
Expand Down
45 changes: 39 additions & 6 deletions lib/utils/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ func TestRemoveFromSlice(t *testing.T) {
}
}

// TestVersions tests versions compatibility checking
func TestVersions(t *testing.T) {
// TestMinVersions tests versions compatibility checking
func TestMinVersions(t *testing.T) {
t.Parallel()

type tc struct {
Expand All @@ -149,8 +149,8 @@ func TestVersions(t *testing.T) {
}
for _, testCase := range successTestCases {
t.Run(testCase.info, func(t *testing.T) {
require.NoError(t, CheckVersion(testCase.client, testCase.minClient))
assert.True(t, MeetsVersion(testCase.client, testCase.minClient), "MeetsVersion expected to succeed")
require.NoError(t, CheckMinVersion(testCase.client, testCase.minClient))
assert.True(t, MeetsMinVersion(testCase.client, testCase.minClient), "MeetsMinVersion expected to succeed")
})
}

Expand All @@ -160,8 +160,41 @@ func TestVersions(t *testing.T) {
}
for _, testCase := range failTestCases {
t.Run(testCase.info, func(t *testing.T) {
fixtures.AssertBadParameter(t, CheckVersion(testCase.client, testCase.minClient))
assert.False(t, MeetsVersion(testCase.client, testCase.minClient), "MeetsVersion expected to fail")
fixtures.AssertBadParameter(t, CheckMinVersion(testCase.client, testCase.minClient))
assert.False(t, MeetsMinVersion(testCase.client, testCase.minClient), "MeetsMinVersion expected to fail")
})
}
}

// TestMaxVersions tests versions compatibility checking
func TestMaxVersions(t *testing.T) {
t.Parallel()

type tc struct {
info string
client string
maxClient string
}
successTestCases := []tc{
{info: "client same as max version", client: "1.0.0", maxClient: "1.0.0"},
{info: "client older than max version", client: "1.1.0", maxClient: "1.2.0"},
{info: "pre-releases clients are ok", client: "1.0.0-alpha.1", maxClient: "1.0.0"},
}
for _, testCase := range successTestCases {
t.Run(testCase.info, func(t *testing.T) {
require.NoError(t, CheckMaxVersion(testCase.client, testCase.maxClient))
assert.True(t, MeetsMaxVersion(testCase.client, testCase.maxClient), "MeetsMinVersion expected to succeed")
})
}

failTestCases := []tc{
{info: "client newer than max version", client: "1.3.0", maxClient: "1.1.0"},
{info: "newer pre-releases are no ok", client: "1.1.0", maxClient: "1.1.0-alpha.1"},
}
for _, testCase := range failTestCases {
t.Run(testCase.info, func(t *testing.T) {
fixtures.AssertBadParameter(t, CheckMaxVersion(testCase.client, testCase.maxClient))
assert.False(t, MeetsMaxVersion(testCase.client, testCase.maxClient), "MeetsMinVersion expected to fail")
})
}
}
Expand Down
46 changes: 41 additions & 5 deletions lib/utils/ver.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,32 @@ import (
"github.com/gravitational/trace"
)

// MeetsVersion returns true if gotVer is empty or at least minVer.
func MeetsVersion(gotVer, minVer string) bool {
// MeetsMinVersion returns true if gotVer is empty or at least minVer.
func MeetsMinVersion(gotVer, minVer string) bool {
if gotVer == "" {
return true // Ignore empty versions.
}

err := CheckVersion(gotVer, minVer)
err := CheckMinVersion(gotVer, minVer)

// Non BadParameter errors are semver parsing errors.
return !trace.IsBadParameter(err)
}

// CheckVersion compares a version with a minimum version supported.
func CheckVersion(currentVersion, minVersion string) error {
// MeetsMaxVersion returns true if gotVer is empty or at most maxVer.
func MeetsMaxVersion(gotVer, maxVer string) bool {
if gotVer == "" {
return true // Ignore empty versions.
}

err := CheckMaxVersion(gotVer, maxVer)

// Non BadParameter errors are semver parsing errors.
return !trace.IsBadParameter(err)
}

// CheckMinVersion compares a version with a minimum version supported.
func CheckMinVersion(currentVersion, minVersion string) error {
currentSemver, minSemver, err := versionStringToSemver(currentVersion, minVersion)
if err != nil {
return trace.Wrap(err)
Expand All @@ -51,6 +63,20 @@ func CheckVersion(currentVersion, minVersion string) error {
return nil
}

// CheckMaxVersion compares a version with a maximum version supported.
func CheckMaxVersion(currentVersion, maxVersion string) error {
currentSemver, maxSemver, err := versionStringToSemver(currentVersion, maxVersion)
if err != nil {
return trace.Wrap(err)
}

if maxSemver.LessThan(*currentSemver) {
return trace.BadParameter("incompatible versions: %v > %v", currentVersion, maxVersion)
}

return nil
}

// VersionBeforeAlpha appends "-aa" to the version so that it comes before <version>-alpha.
// This ban be used to make version checks work during development.
func VersionBeforeAlpha(version string) string {
Expand Down Expand Up @@ -82,6 +108,16 @@ func MajorSemver(version string) (string, error) {
return fmt.Sprintf("%d.0.0", ver.Major), nil
}

// MajorSemverWithWildcards returns the major version as a semver string.
// Ex: 13.4.3 -> 13.x.x
func MajorSemverWithWildcards(version string) (string, error) {
ver, err := semver.NewVersion(version)
if err != nil {
return "", trace.Wrap(err)
}
return fmt.Sprintf("%d.x.x", ver.Major), nil
}

func versionStringToSemver(ver1, ver2 string) (*semver.Version, *semver.Version, error) {
v1Semver, err := semver.NewVersion(ver1)
if err != nil {
Expand Down
31 changes: 23 additions & 8 deletions lib/utils/ver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,33 @@ import (
"github.com/stretchr/testify/require"
)

func TestMeetsVersion_emptyOrInvalid(t *testing.T) {
// See TestVersions for more comprehensive tests.
func TestMeetsMinVersion_emptyOrInvalid(t *testing.T) {
// See TestMinVersions for more comprehensive tests.

if !MeetsVersion("", "v1.2.3") {
t.Error("MeetsVersion with an empty gotVer should always succeed")
if !MeetsMinVersion("", "v1.2.3") {
t.Error("MeetsMinVersion with an empty gotVer should always succeed")
}

if !MeetsVersion("banana", "v1.2.3") {
t.Error("MeetsVersion with an invalid version should always succeed")
if !MeetsMinVersion("banana", "v1.2.3") {
t.Error("MeetsMinVersion with an invalid version should always succeed")
}
if !MeetsVersion("v1.2.3", "banana") {
t.Error("MeetsVersion with an invalid version should always succeed")
if !MeetsMinVersion("v1.2.3", "banana") {
t.Error("MeetsMinVersion with an invalid version should always succeed")
}
}

func TestMeetsMaxVersion_emptyOrInvalid(t *testing.T) {
// See TestMaxVersions for more comprehensive tests.

if !MeetsMaxVersion("", "v1.2.3") {
t.Error("MeetsMaxVersion with an empty gotVer should always succeed")
}

if !MeetsMaxVersion("banana", "v1.2.3") {
t.Error("banana with an invalid version should always succeed")
}
if !MeetsMaxVersion("v1.2.3", "banana") {
t.Error("MeetsMaxVersion with an invalid version should always succeed")
}
}

Expand Down