Add OS and ARCH info for client tools managed updates (#59891)

* Add OS and ARCH info for client tools managed updates

* Optimized initial tsh/tctl installation in tests to perform it once and re-use

* Add reading binary headers in order to identify OS and ARCH if it is not defined in configuration file.
This commit is contained in:
Vadym Popov
2025-10-20 19:03:49 +00:00
committed by GitHub
parent d5fc3431b1
commit a8ae574479
6 changed files with 162 additions and 143 deletions
+24
View File
@@ -41,6 +41,7 @@ import (
"github.com/gravitational/teleport/integration/autoupdate/tools/updater/tsh"
"github.com/gravitational/teleport/integration/helpers/archive"
"github.com/gravitational/teleport/lib/autoupdate"
"github.com/gravitational/teleport/lib/autoupdate/tools"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/modules/modulestest"
"github.com/gravitational/teleport/lib/utils"
@@ -63,6 +64,11 @@ var (
toolsDir string
baseURL string
// tshPath is the path to initial tsh binary version 1.0.0.
tshPath string
// tctlPath is the path to initial tctl binary version 1.0.0.
tctlPath string
)
func TestMain(m *testing.M) {
@@ -112,6 +118,24 @@ func TestMain(m *testing.M) {
log.Fatalf("failed to set base URL environment variable: %v", err)
}
// Initial fetch the updater binary un-archive and replace.
updater := tools.NewUpdater(
toolsDir,
testVersions[0],
tools.WithBaseURL(baseURL),
)
if err := updater.Update(ctx, testVersions[0]); err != nil {
log.Fatalf("failed to update: %v", err)
}
tshPath, err = updater.ToolPath(tools.DefaultClientTools()[0], testVersions[0])
if err != nil {
log.Fatalf("failed to get tsh path: %v", err)
}
tctlPath, err = updater.ToolPath(tools.DefaultClientTools()[1], testVersions[0])
if err != nil {
log.Fatalf("failed to get tctl path: %v", err)
}
// Run tests after binary is built.
code := m.Run()
+57 -51
View File
@@ -25,7 +25,9 @@ import (
"io"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strings"
"testing"
"time"
@@ -51,20 +53,6 @@ func TestUpdate(t *testing.T) {
t.Setenv(types.HomeEnvVar, t.TempDir())
ctx := context.Background()
// Fetch compiled test binary with updater logic and install to $TELEPORT_HOME.
updater := tools.NewUpdater(
toolsDir,
testVersions[0],
tools.WithBaseURL(baseURL),
)
err := updater.Update(ctx, testVersions[0])
require.NoError(t, err)
tshPath, err := updater.ToolPath(tools.DefaultClientTools()[0], testVersions[0])
require.NoError(t, err)
tctlPath, err := updater.ToolPath(tools.DefaultClientTools()[1], testVersions[0])
require.NoError(t, err)
// Verify that the installed version is equal to requested one.
cmd := exec.CommandContext(ctx, tctlPath, "version")
out, err := cmd.Output()
@@ -85,6 +73,59 @@ func TestUpdate(t *testing.T) {
matchVersion(t, string(out), testVersions[1])
}
// TestUpdateDifferentOSArch verifies the update logic for matching operating system
// and architecture. If they differ from the current system, a new download must be
// initiated even when the same version is already installed.
func TestUpdateDifferentOSArch(t *testing.T) {
home := t.TempDir()
t.Setenv(types.HomeEnvVar, home)
ctx := context.Background()
// Execute version command with setting the new version which must trigger update and
// re-execution of the same command after downloading requested version.
cmd := exec.CommandContext(ctx, tshPath, "version")
cmd.Env = append(
os.Environ(),
fmt.Sprintf("%s=%s", teleportToolsVersion, testVersions[1]),
)
out, err := cmd.Output()
require.NoError(t, err)
matchVersion(t, string(out), testVersions[1])
configPath := filepath.Join(home, "bin")
ctc, err := tools.GetToolsConfig(configPath)
require.NoError(t, err)
require.Len(t, ctc.Tools, 1)
require.Equal(t, runtime.GOOS, ctc.Tools[0].OS)
require.Equal(t, runtime.GOARCH, ctc.Tools[0].Arch)
// Update the architecture to a non-existing value.
err = tools.UpdateToolsConfig(configPath, func(ctc *tools.ClientToolsConfig) error {
ctc.Tools[0].Arch = "unknown"
return nil
})
require.NoError(t, err)
// After executing the version command, we should not match the architecture of the
// previously installed tool version. Since the package does not match, we must
// re-download the package for the required architecture and re-execute.
cmd = exec.CommandContext(ctx, tshPath, "version")
cmd.Env = append(
os.Environ(),
fmt.Sprintf("%s=%s", teleportToolsVersion, testVersions[1]),
)
out, err = cmd.Output()
require.NoError(t, err)
matchVersion(t, string(out), testVersions[1])
ctc, err = tools.GetToolsConfig(configPath)
require.NoError(t, err)
// The second call to the version command installs another package with the required
// OS and architecture, and we should then see two packages in the list.
require.Len(t, ctc.Tools, 2)
}
// TestParallelUpdate launches multiple updater commands in parallel while defining a new version.
// The first process should acquire a lock and block execution for the other processes. After the
// first update is complete, other processes should acquire the lock one by one and re-execute
@@ -93,18 +134,6 @@ func TestParallelUpdate(t *testing.T) {
t.Setenv(types.HomeEnvVar, t.TempDir())
ctx := context.Background()
// Initial fetch the updater binary un-archive and replace.
updater := tools.NewUpdater(
toolsDir,
testVersions[0],
tools.WithBaseURL(baseURL),
)
err := updater.Update(ctx, testVersions[0])
require.NoError(t, err)
tshPath, err := updater.ToolPath(tools.DefaultClientTools()[0], testVersions[0])
require.NoError(t, err)
tCtx, cancel := context.WithTimeout(ctx, time.Minute)
t.Cleanup(cancel)
@@ -121,7 +150,7 @@ func TestParallelUpdate(t *testing.T) {
os.Environ(),
fmt.Sprintf("%s=%s", teleportToolsVersion, testVersions[1]),
)
err = cmd.Start()
err := cmd.Start()
require.NoError(t, err, "failed to start updater")
go func(cmd *exec.Cmd) {
@@ -153,18 +182,6 @@ func TestParallelUpdate(t *testing.T) {
// TestUpdateInterruptSignal verifies the interrupt signal send to the process must stop downloading.
func TestUpdateInterruptSignal(t *testing.T) {
t.Setenv(types.HomeEnvVar, t.TempDir())
ctx := context.Background()
// Initial fetch the updater binary un-archive and replace.
updater := tools.NewUpdater(
toolsDir,
testVersions[0],
tools.WithBaseURL(baseURL),
)
err := updater.Update(ctx, testVersions[0])
require.NoError(t, err)
tshPath, err := updater.ToolPath(tools.DefaultClientTools()[0], testVersions[0])
require.NoError(t, err)
var output bytes.Buffer
multiOut := io.MultiWriter(&output, os.Stdout)
@@ -175,7 +192,7 @@ func TestUpdateInterruptSignal(t *testing.T) {
os.Environ(),
fmt.Sprintf("%s=%s", teleportToolsVersion, testVersions[1]),
)
err = cmd.Start()
err := cmd.Start()
if err != nil {
t.Log(output.String())
}
@@ -233,17 +250,6 @@ func TestUpdateForOSSBuild(t *testing.T) {
t.Setenv(updater.TestBuild, modules.BuildOSS)
t.Setenv(autoupdate.BaseURLEnvVar, "")
// Fetch compiled test binary with updater logic and install to $TELEPORT_HOME.
updater := tools.NewUpdater(
toolsDir,
testVersions[0],
tools.WithBaseURL(baseURL),
)
err := updater.Update(ctx, testVersions[0])
require.NoError(t, err)
tshPath, err := updater.ToolPath(tools.DefaultClientTools()[0], testVersions[0])
require.NoError(t, err)
// Verify that requested update is ignored by OSS build and version wasn't updated.
cmd := exec.CommandContext(ctx, tshPath, "version")
cmd.Env = append(
@@ -39,7 +39,6 @@ import (
"github.com/gravitational/teleport/api/types/autoupdate"
"github.com/gravitational/teleport/integration/autoupdate/tools/updater"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/autoupdate/tools"
"github.com/gravitational/teleport/lib/client"
"github.com/gravitational/teleport/lib/service"
"github.com/gravitational/teleport/lib/service/servicecfg"
@@ -59,21 +58,13 @@ import (
func TestAliasLoginWithUpdater(t *testing.T) {
ctx := context.Background()
rootServer, homeDir, installDir := bootstrapTestServer(t)
rootServer, homeDir := bootstrapTestServer(t)
setupManagedUpdates(t, rootServer.GetAuthServer(), autoupdate.ToolsUpdateModeEnabled, testVersions[1])
// Assign alias to the login command for test cluster.
proxyAddr, err := rootServer.ProxyWebAddr()
require.NoError(t, err)
// Fetch compiled test binary and install to tools dir [v1.0.0].
updater := tools.NewUpdater(installDir, testVersions[0], tools.WithBaseURL(baseURL))
require.NoError(t, updater.Update(ctx, testVersions[0]))
tshPath, err := updater.ToolPath("tsh", testVersions[0])
require.NoError(t, err)
tctlPath, err := updater.ToolPath("tctl", testVersions[0])
require.NoError(t, err)
configPath := filepath.Join(homeDir, client.TSHConfigPath)
require.NoError(t, os.MkdirAll(filepath.Dir(configPath), 0700))
out, err := yaml.Marshal(client.TSHConfig{
@@ -119,18 +110,12 @@ func TestAliasLoginWithUpdater(t *testing.T) {
func TestSequentialUpdate(t *testing.T) {
ctx := context.Background()
rootServer, _, installDir := bootstrapTestServer(t)
rootServer, _ := bootstrapTestServer(t)
// Assign alias to the login command for test cluster.
proxyAddr, err := rootServer.ProxyWebAddr()
require.NoError(t, err)
// Fetch compiled test binary and install to tools dir [v1.0.0].
updater := tools.NewUpdater(installDir, testVersions[0], tools.WithBaseURL(baseURL))
require.NoError(t, updater.Update(ctx, testVersions[0]))
tshPath, err := updater.ToolPath("tsh", testVersions[0])
require.NoError(t, err)
for _, testVersion := range testVersions[1:] {
// Set cluster version to be upgraded.
setupManagedUpdates(t, rootServer.GetAuthServer(), autoupdate.ToolsUpdateModeEnabled, testVersion)
@@ -162,18 +147,12 @@ func TestSequentialUpdate(t *testing.T) {
func TestLoginWithUpdaterAndProfile(t *testing.T) {
ctx := context.Background()
rootServer, _, installDir := bootstrapTestServer(t)
rootServer, _ := bootstrapTestServer(t)
setupManagedUpdates(t, rootServer.GetAuthServer(), autoupdate.ToolsUpdateModeDisabled, testVersions[1])
proxyAddr, err := rootServer.ProxyWebAddr()
require.NoError(t, err)
// Fetch compiled test binary and install to tools dir [v1.0.0].
updater := tools.NewUpdater(installDir, testVersions[0], tools.WithBaseURL(baseURL))
require.NoError(t, updater.Update(ctx, testVersions[0]))
tshPath, err := updater.ToolPath("tsh", testVersions[0])
require.NoError(t, err)
// First login with set version during login process
t.Setenv("TELEPORT_TOOLS_VERSION", testVersions[1])
cmd := exec.CommandContext(ctx, tshPath,
@@ -205,18 +184,12 @@ func TestLoginWithUpdaterAndProfile(t *testing.T) {
func TestLoginWithDisabledUpdateInProfile(t *testing.T) {
ctx := context.Background()
rootServer, _, installDir := bootstrapTestServer(t)
rootServer, _ := bootstrapTestServer(t)
setupManagedUpdates(t, rootServer.GetAuthServer(), autoupdate.ToolsUpdateModeDisabled, testVersions[1])
proxyAddr, err := rootServer.ProxyWebAddr()
require.NoError(t, err)
// Fetch compiled test binary and install to tools dir [v1.0.0].
updater := tools.NewUpdater(installDir, testVersions[0], tools.WithBaseURL(baseURL))
require.NoError(t, updater.Update(ctx, testVersions[0]))
tshPath, err := updater.ToolPath("tsh", testVersions[0])
require.NoError(t, err)
// Set env variable to forcibly request update on version command.
t.Setenv("TELEPORT_TOOLS_VERSION", testVersions[1])
cmd := exec.CommandContext(ctx, tshPath, "version")
@@ -257,18 +230,12 @@ func TestLoginWithDisabledUpdateInProfile(t *testing.T) {
func TestLoginWithDisabledUpdateForcedByEnv(t *testing.T) {
ctx := context.Background()
rootServer, _, installDir := bootstrapTestServer(t)
rootServer, _ := bootstrapTestServer(t)
setupManagedUpdates(t, rootServer.GetAuthServer(), autoupdate.ToolsUpdateModeDisabled, testVersions[1])
proxyAddr, err := rootServer.ProxyWebAddr()
require.NoError(t, err)
// Fetch compiled test binary and install to tools dir [v1.0.0].
updater := tools.NewUpdater(installDir, testVersions[0], tools.WithBaseURL(baseURL))
require.NoError(t, updater.Update(ctx, testVersions[0]))
tshPath, err := updater.ToolPath("tsh", testVersions[0])
require.NoError(t, err)
// Second login has to update profile and disable further managed updates.
cmd := exec.CommandContext(ctx, tshPath,
"login", "--proxy", proxyAddr.String(), "--insecure", "--user", "alice", "--auth", constants.LocalConnector)
@@ -304,18 +271,6 @@ func TestMigratedUpdateNotReExec(t *testing.T) {
t.Setenv(types.HomeEnvVar, testToolsDir)
ctx := context.Background()
// Fetch compiled test binary with updater logic and install to $TELEPORT_HOME.
updater := tools.NewUpdater(
testToolsDir,
testVersions[0],
tools.WithBaseURL(baseURL),
)
err := updater.Update(ctx, testVersions[0])
require.NoError(t, err)
tshPath, err := updater.ToolPath("tsh", testVersions[0])
require.NoError(t, err)
require.NoError(t, os.MkdirAll(filepath.Join(testToolsDir, "bin"), 0755))
require.NoError(t, os.WriteFile(filepath.Join(testToolsDir, "bin", "tsh"), []byte("#!/bin/sh\n echo 'Teleport v5.5.5 git'\n"), 0755))
@@ -335,24 +290,12 @@ func TestUpdateConfigReExecPath(t *testing.T) {
t.Setenv(types.HomeEnvVar, t.TempDir())
ctx := context.Background()
rootServer, _, _ := bootstrapTestServer(t)
rootServer, _ := bootstrapTestServer(t)
setupManagedUpdates(t, rootServer.GetAuthServer(), autoupdate.ToolsUpdateModeEnabled, testVersions[1])
proxyAddr, err := rootServer.ProxyWebAddr()
require.NoError(t, err)
// Fetch compiled test binary with updater logic and install to $TELEPORT_HOME.
updater := tools.NewUpdater(
toolsDir,
testVersions[0],
tools.WithBaseURL(baseURL),
)
err = updater.Update(ctx, testVersions[0])
require.NoError(t, err)
tshPath, err := updater.ToolPath("tsh", testVersions[0])
require.NoError(t, err)
cmd := exec.CommandContext(ctx, tshPath,
"login", "--proxy", proxyAddr.String(), "--insecure", "--user", "alice", "--auth", constants.LocalConnector)
cmd.Env = os.Environ()
@@ -379,12 +322,10 @@ func TestUpdateConfigReExecPath(t *testing.T) {
require.Contains(t, string(out), `ProxyCommand "`+tshPath+`" `)
}
func bootstrapTestServer(t *testing.T) (*service.TeleportProcess, string, string) {
func bootstrapTestServer(t *testing.T) (*service.TeleportProcess, string) {
t.Helper()
homeDir := filepath.Join(t.TempDir(), "home")
require.NoError(t, os.MkdirAll(homeDir, 0700))
installDir := filepath.Join(t.TempDir(), "local")
require.NoError(t, os.MkdirAll(installDir, 0700))
t.Setenv(types.HomeEnvVar, homeDir)
@@ -425,7 +366,7 @@ func bootstrapTestServer(t *testing.T) (*service.TeleportProcess, string, string
err = authService.UpsertPassword("alice", []byte(password))
require.NoError(t, err)
return rootServer, homeDir, installDir
return rootServer, homeDir
}
func setupManagedUpdates(t *testing.T, server *auth.Server, muMode string, muVersion string) {
+56 -13
View File
@@ -20,17 +20,20 @@ package tools
import (
"context"
"debug/buildinfo"
"encoding/json"
"errors"
"log/slog"
"maps"
"os"
"path/filepath"
"runtime"
"slices"
"strings"
"github.com/gravitational/trace"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/lib/utils"
)
@@ -66,9 +69,9 @@ type ClientToolsConfig struct {
// AddTool adds a tool to the collection in the configuration, always placing it at the top.
// The collection size is limited by the `defaultSizeStoredVersion` constant.
func (ctc *ClientToolsConfig) AddTool(tool Tool) {
func (ctc *ClientToolsConfig) AddTool(toolsDir string, tool Tool) {
for _, t := range ctc.Tools {
if t.Version == tool.Version {
if t.IsEqual(toolsDir, tool.Version, tool.OS, tool.Arch) {
maps.Copy(t.PathMap, tool.PathMap)
return
}
@@ -94,9 +97,9 @@ func (ctc *ClientToolsConfig) SetConfig(proxy string, version string, disabled b
}
// SelectVersion lookups the version and re-order by last recently used.
func (ctc *ClientToolsConfig) SelectVersion(version string) *Tool {
func (ctc *ClientToolsConfig) SelectVersion(toolsDir, version, os, arch string) *Tool {
for i, tool := range ctc.Tools {
if tool.Version == version {
if tool.IsEqual(toolsDir, version, os, arch) {
ctc.Tools = append([]Tool{tool}, append(ctc.Tools[:i], ctc.Tools[i+1:]...)...)
return &tool
}
@@ -105,9 +108,9 @@ func (ctc *ClientToolsConfig) SelectVersion(version string) *Tool {
}
// HasVersion check that specific version present in collection.
func (ctc *ClientToolsConfig) HasVersion(version string) bool {
return slices.ContainsFunc(ctc.Tools, func(s Tool) bool {
return version == s.Version
func (ctc *ClientToolsConfig) HasVersion(toolsDir, version, os, arch string) bool {
return slices.ContainsFunc(ctc.Tools, func(tool Tool) bool {
return tool.IsEqual(toolsDir, version, os, arch)
})
}
@@ -121,15 +124,19 @@ type ClusterConfig struct {
type Tool struct {
// Version is the version of the tools (tsh, tctl) as defined in the PathMap.
Version string `json:"version"`
// OS is the operating system of the installed package.
OS string `json:"os"`
// Arch is architecture of the installed package.
Arch string `json:"arch"`
// PathMap stores the relative path (within the tools directory) for each tool binary.
// For example: {"tctl": "package-id/tctl"}.
PathMap map[string]string `json:"path"`
}
// PackageNames returns the package names extracted from the tool path map.
func (c *Tool) PackageNames() []string {
func (t *Tool) PackageNames() []string {
var packageNames []string
for _, path := range c.PathMap {
for _, path := range t.PathMap {
dir := strings.SplitN(path, string(filepath.Separator), 2)
if len(dir) > 0 {
packageNames = append(packageNames, dir[0])
@@ -138,9 +145,45 @@ func (c *Tool) PackageNames() []string {
return packageNames
}
// getToolsConfig reads the configuration file for client tools managed updates,
// IsEqual verifies that specific tool matches version, operating system and architecture.
func (t *Tool) IsEqual(toolsDir, version, os, arch string) bool {
// If OS version is not defined in configuration we should check it by binary headers.
// TODO(vapopov): DELETE IN v21.0.0 - OS and Arch must be supported for all version.
if t.OS == "" {
path, ok := t.PathMap[DefaultClientTools()[0]]
if !ok {
return false
}
info, err := buildinfo.ReadFile(filepath.Join(toolsDir, path))
if err != nil {
slog.WarnContext(context.Background(), "Failed to read build info.", "error", err)
return version == t.Version
}
var binOS, binArch string
for _, s := range info.Settings {
switch s.Key {
case "GOOS":
binOS = s.Value
case "GOARCH":
binArch = s.Value
}
}
if binOS == "" || binArch == "" {
return version == t.Version
}
// macOS binaries are always built with universal architecture support (arm64, amd64).
if runtime.GOOS == constants.DarwinOS {
return version == t.Version && os == binOS
}
return version == t.Version && os == binOS && arch == binArch
}
return version == t.Version && os == t.OS && arch == t.Arch
}
// GetToolsConfig reads the configuration file for client tools managed updates,
// and acquires a filesystem lock until the configuration is read and deserialized.
func getToolsConfig(toolsDir string) (ctc *ClientToolsConfig, err error) {
func GetToolsConfig(toolsDir string) (ctc *ClientToolsConfig, err error) {
unlock, err := utils.FSWriteLock(filepath.Join(toolsDir, lockFileName))
if err != nil {
return nil, trace.Wrap(err)
@@ -167,9 +210,9 @@ func getToolsConfig(toolsDir string) (ctc *ClientToolsConfig, err error) {
return ctc, nil
}
// updateToolsConfig creates or opens the configuration file for client tools managed updates,
// UpdateToolsConfig creates or opens the configuration file for client tools managed updates,
// and acquires a filesystem lock until the configuration is written and closed.
func updateToolsConfig(toolsDir string, update func(ctc *ClientToolsConfig) error) (err error) {
func UpdateToolsConfig(toolsDir string, update func(ctc *ClientToolsConfig) error) (err error) {
unlock, err := utils.FSWriteLock(filepath.Join(toolsDir, lockFileName))
if err != nil {
return trace.Wrap(err)
+7 -2
View File
@@ -24,6 +24,7 @@ import (
"log/slog"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/google/uuid"
@@ -40,7 +41,7 @@ const (
// migrateV1AndUpdateConfig launches migration process and add migrated
// tools to configuration file.
func migrateV1AndUpdateConfig(toolsDir string, tools []string) error {
if err := updateToolsConfig(toolsDir, func(ctc *ClientToolsConfig) error {
if err := UpdateToolsConfig(toolsDir, func(ctc *ClientToolsConfig) error {
migratedTools, err := migrateV1(toolsDir, tools)
if err != nil {
return trace.Wrap(err)
@@ -50,7 +51,7 @@ func migrateV1AndUpdateConfig(toolsDir string, tools []string) error {
}
for _, tool := range migratedTools {
ctc.AddTool(tool)
ctc.AddTool(toolsDir, tool)
}
return nil
}); err != nil {
@@ -106,6 +107,8 @@ func migrateV1(toolsDir string, tools []string) (map[string]Tool, error) {
} else {
migratedTools[toolVersion] = Tool{
Version: toolVersion,
OS: runtime.GOOS,
Arch: runtime.GOARCH,
PathMap: map[string]string{tool: filepath.Join(newPkg, relPath)},
}
}
@@ -129,6 +132,8 @@ func migrateV1(toolsDir string, tools []string) (map[string]Tool, error) {
}
migratedTools[toolVersion] = Tool{
Version: toolVersion,
OS: runtime.GOOS,
Arch: runtime.GOARCH,
PathMap: map[string]string{tool: filepath.Join(newPkg, tool)},
}
}
+10 -10
View File
@@ -172,7 +172,7 @@ func (u *Updater) CheckLocal(ctx context.Context, profileName string) (resp *Upd
// We should acquire and release the lock before checking the version
// by executing the binary, as it might block tool execution until the version
// check is completed, which can take several seconds.
ctc, err := getToolsConfig(u.toolsDir)
ctc, err := GetToolsConfig(u.toolsDir)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -195,7 +195,7 @@ func (u *Updater) CheckLocal(ctx context.Context, profileName string) (resp *Upd
return nil, trace.Wrap(err)
}
if !ctc.HasVersion(toolsVersion) {
if !ctc.HasVersion(u.toolsDir, toolsVersion, runtime.GOOS, runtime.GOARCH) {
if err := migrateV1AndUpdateConfig(u.toolsDir, u.tools); err != nil {
// Execution should not be interrupted if migration fails. Instead, it's better to
// re-download the version that was supposed to be migrated but failed for some reason.
@@ -227,7 +227,7 @@ func (u *Updater) CheckRemote(ctx context.Context, proxyAddr string, insecure bo
return &UpdateResponse{Version: "", ReExec: false}, nil
// Requested version already the same as client version.
case u.localVersion:
if err := updateToolsConfig(u.toolsDir, func(ctc *ClientToolsConfig) error {
if err := UpdateToolsConfig(u.toolsDir, func(ctc *ClientToolsConfig) error {
ctc.SetConfig(proxyHost, requestedVersion, false)
return nil
}); err != nil {
@@ -244,7 +244,7 @@ func (u *Updater) CheckRemote(ctx context.Context, proxyAddr string, insecure bo
// If the environment variable is set during a remote check,
// prioritize this version for the current host and use it as the default
// for all commands under the current profile.
if err := updateToolsConfig(u.toolsDir, func(ctc *ClientToolsConfig) error {
if err := UpdateToolsConfig(u.toolsDir, func(ctc *ClientToolsConfig) error {
ctc.SetConfig(proxyHost, requestedVersion, false)
return nil
}); err != nil {
@@ -279,7 +279,7 @@ func (u *Updater) CheckRemote(ctx context.Context, proxyAddr string, insecure bo
updateResp = &UpdateResponse{Version: resp.AutoUpdate.ToolsVersion, ReExec: true}
}
if err := updateToolsConfig(u.toolsDir, func(ctc *ClientToolsConfig) error {
if err := UpdateToolsConfig(u.toolsDir, func(ctc *ClientToolsConfig) error {
ctc.SetConfig(proxyHost, updateResp.Version, updateResp.Disabled)
return nil
}); err != nil {
@@ -292,7 +292,7 @@ func (u *Updater) CheckRemote(ctx context.Context, proxyAddr string, insecure bo
// Update acquires filesystem lock, downloads requested version package, unarchive, replace
// existing one and cleanups the previous downloads with defined updater directory suffix.
func (u *Updater) Update(ctx context.Context, toolsVersion string) error {
err := updateToolsConfig(u.toolsDir, func(ctc *ClientToolsConfig) error {
err := UpdateToolsConfig(u.toolsDir, func(ctc *ClientToolsConfig) error {
// ignoreTools is the list of tools installed and tracked by the config.
// They should be preserved during cleanup. If we have more than [defaultSizeStoredVersion]
// versions, the updater will forget about the least used version.
@@ -301,7 +301,7 @@ func (u *Updater) Update(ctx context.Context, toolsVersion string) error {
// If the version of the running binary or the version downloaded to
// tools directory is the same as the requested version of client tools,
// nothing to be done, exit early.
if tool.Version == toolsVersion {
if tool.IsEqual(u.toolsDir, toolsVersion, runtime.GOOS, runtime.GOARCH) {
return nil
}
ignoreTools = append(ignoreTools, tool.PackageNames()...)
@@ -383,7 +383,7 @@ func (u *Updater) update(ctx context.Context, ctc *ClientToolsConfig, pkg packag
for key, val := range toolsMap {
toolsMap[key] = filepath.Join(pkgName, val)
}
ctc.AddTool(Tool{Version: pkg.Version, PathMap: toolsMap})
ctc.AddTool(u.toolsDir, Tool{Version: pkg.Version, OS: runtime.GOOS, Arch: runtime.GOARCH, PathMap: toolsMap})
return nil
}
@@ -391,8 +391,8 @@ func (u *Updater) update(ctx context.Context, ctc *ClientToolsConfig, pkg packag
// ToolPath loads full path from config file to specific tool and version.
func (u *Updater) ToolPath(toolName, toolVersion string) (path string, err error) {
var tool *Tool
if err := updateToolsConfig(u.toolsDir, func(ctc *ClientToolsConfig) error {
tool = ctc.SelectVersion(toolVersion)
if err := UpdateToolsConfig(u.toolsDir, func(ctc *ClientToolsConfig) error {
tool = ctc.SelectVersion(u.toolsDir, toolVersion, runtime.GOOS, runtime.GOARCH)
return nil
}); err != nil {
return "", trace.Wrap(err)