Skip to content

Commit

Permalink
Client auto updates integration for tctl/tsh
Browse files Browse the repository at this point in the history
  • Loading branch information
vapopov committed Oct 22, 2024
1 parent 37ed182 commit cc3662d
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 42 deletions.
17 changes: 9 additions & 8 deletions integration/autoupdate/tools/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ import (

"github.com/gravitational/trace"

"github.com/gravitational/teleport/integration/helpers"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/integration/helpers/archive"
)

const (
Expand Down Expand Up @@ -133,25 +134,25 @@ func buildAndArchiveApps(ctx context.Context, path string, toolsDir string, vers
for _, app := range []string{"tsh", "tctl"} {
output := filepath.Join(versionPath, app)
switch runtime.GOOS {
case "windows":
case constants.WindowsOS:
output = filepath.Join(versionPath, app+".exe")
case "darwin":
case constants.DarwinOS:
output = filepath.Join(versionPath, app+".app", "Contents", "MacOS", app)
}
if err := buildBinary(output, toolsDir, version, baseURL); err != nil {
return trace.Wrap(err)
}
}
switch runtime.GOOS {
case "darwin":
case constants.DarwinOS:
archivePath := filepath.Join(path, fmt.Sprintf("teleport-%s.pkg", version))
return trace.Wrap(helpers.CompressDirToPkgFile(ctx, versionPath, archivePath, "com.example.pkgtest"))
case "windows":
return trace.Wrap(archive.CompressDirToPkgFile(ctx, versionPath, archivePath, "com.example.pkgtest"))
case constants.WindowsOS:
archivePath := filepath.Join(path, fmt.Sprintf("teleport-v%s-windows-amd64-bin.zip", version))
return trace.Wrap(helpers.CompressDirToZipFile(ctx, versionPath, archivePath))
return trace.Wrap(archive.CompressDirToZipFile(ctx, versionPath, archivePath))
default:
archivePath := filepath.Join(path, fmt.Sprintf("teleport-v%s-linux-%s-bin.tar.gz", version, runtime.GOARCH))
return trace.Wrap(helpers.CompressDirToTarGzFile(ctx, versionPath, archivePath))
return trace.Wrap(archive.CompressDirToTarGzFile(ctx, versionPath, archivePath))
}
}

Expand Down
14 changes: 1 addition & 13 deletions integration/autoupdate/tools/updater/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,9 @@ import (
"log"
"os"
"os/signal"
"runtime"
"syscall"
"time"

"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/lib/autoupdate/tools"
)

Expand All @@ -45,7 +43,7 @@ func main() {
ctx, _ = signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM)

updater := tools.NewUpdater(
clientTools(),
tools.DefaultClientTools(),
toolsDir,
version,
tools.WithBaseURL(baseURL),
Expand Down Expand Up @@ -76,13 +74,3 @@ func main() {
fmt.Printf("Teleport v%v git\n", version)
}
}

// clientTools list of the client tools needs to be updated.
func clientTools() []string {
switch runtime.GOOS {
case constants.WindowsOS:
return []string{"tsh.exe", "tctl.exe"}
default:
return []string{"tsh", "tctl"}
}
}
17 changes: 3 additions & 14 deletions integration/autoupdate/tools/updater_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,13 @@ import (
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strings"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/lib/autoupdate/tools"
)

Expand All @@ -51,7 +49,7 @@ func TestUpdate(t *testing.T) {

// Fetch compiled test binary with updater logic and install to $TELEPORT_HOME.
updater := tools.NewUpdater(
clientTools(),
tools.DefaultClientTools(),
toolsDir,
testVersions[0],
tools.WithBaseURL(baseURL),
Expand Down Expand Up @@ -93,7 +91,7 @@ func TestParallelUpdate(t *testing.T) {

// Initial fetch the updater binary un-archive and replace.
updater := tools.NewUpdater(
clientTools(),
tools.DefaultClientTools(),
toolsDir,
testVersions[0],
tools.WithBaseURL(baseURL),
Expand Down Expand Up @@ -167,7 +165,7 @@ func TestUpdateInterruptSignal(t *testing.T) {

// Initial fetch the updater binary un-archive and replace.
updater := tools.NewUpdater(
clientTools(),
tools.DefaultClientTools(),
toolsDir,
testVersions[0],
tools.WithBaseURL(baseURL),
Expand Down Expand Up @@ -220,12 +218,3 @@ func TestUpdateInterruptSignal(t *testing.T) {
}
assert.Contains(t, output.String(), "Update progress:")
}

func clientTools() []string {
switch runtime.GOOS {
case constants.WindowsOS:
return []string{"tsh.exe", "tctl.exe"}
default:
return []string{"tsh", "tctl"}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package helpers
package archive

import (
"archive/tar"
Expand Down
11 changes: 11 additions & 0 deletions lib/autoupdate/tools/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"github.com/coreos/go-semver/semver"
"github.com/gravitational/trace"

"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/utils"
Expand All @@ -52,6 +53,16 @@ func Dir() (string, error) {
return filepath.Join(home, ".tsh", "bin"), nil
}

// DefaultClientTools list of the client tools needs to be updated by default.
func DefaultClientTools() []string {
switch runtime.GOOS {
case constants.WindowsOS:
return []string{"tsh.exe", "tctl.exe"}
default:
return []string{"tsh", "tctl"}
}
}

func checkToolVersion(toolsDir string) (string, error) {
// Find the path to the current executable.
path, err := toolName(toolsDir)
Expand Down
31 changes: 31 additions & 0 deletions lib/client/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import (
"github.com/gravitational/teleport/lib/auth/touchid"
wancli "github.com/gravitational/teleport/lib/auth/webauthncli"
"github.com/gravitational/teleport/lib/authz"
"github.com/gravitational/teleport/lib/autoupdate/tools"
libmfa "github.com/gravitational/teleport/lib/client/mfa"
"github.com/gravitational/teleport/lib/client/sso"
"github.com/gravitational/teleport/lib/client/terminal"
Expand Down Expand Up @@ -699,6 +700,36 @@ func RetryWithRelogin(ctx context.Context, tc *TeleportClient, fn func() error,
return trace.Wrap(err)
}

// The user has typed a command like `tsh ssh ...` without being logged in,
// if the running binary needs to be updated, update and re-exec.
//
// If needed, download the new version of {tsh, tctl} and re-exec. Make
// sure to exit this process with the same exit code as the child process.
//
toolsDir, err := tools.Dir()
if err != nil {
return trace.Wrap(err)
}
updater := tools.NewUpdater(tools.DefaultClientTools(), toolsDir, teleport.Version)
toolsVersion, reExec, err := updater.CheckRemote(ctx, tc.WebProxyAddr)
if err != nil {
return trace.Wrap(err)
}
if reExec {
// Download the version of client tools required by the cluster.
err := updater.UpdateWithLock(ctx, toolsVersion)
if err != nil {
return trace.Wrap(err)
}

// Re-execute client tools with the correct version of client tools.
code, err := updater.Exec()
if err != nil {
return trace.Wrap(err)
}
os.Exit(code)
}

if opt.afterLoginHook != nil {
if err := opt.afterLoginHook(); err != nil {
return trace.Wrap(err)
Expand Down
33 changes: 31 additions & 2 deletions tool/tctl/common/tctl.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import (
"github.com/gravitational/teleport/lib/auth/authclient"
"github.com/gravitational/teleport/lib/auth/state"
"github.com/gravitational/teleport/lib/auth/storage"
"github.com/gravitational/teleport/lib/autoupdate/tools"
"github.com/gravitational/teleport/lib/client"
"github.com/gravitational/teleport/lib/client/identityfile"
libmfa "github.com/gravitational/teleport/lib/client/mfa"
Expand Down Expand Up @@ -103,8 +104,36 @@ type CLICommand interface {
// "distributions" like OSS or Enterprise
//
// distribution: name of the Teleport distribution
func Run(commands []CLICommand) {
err := TryRun(commands, os.Args[1:])
func Run(ctx context.Context, commands []CLICommand) {
// The user has typed a command like `tsh ssh ...` without being logged in,
// if the running binary needs to be updated, update and re-exec.
//
// If needed, download the new version of {tsh, tctl} and re-exec. Make
// sure to exit this process with the same exit code as the child process.
//
toolsDir, err := tools.Dir()
if err != nil {
utils.FatalError(err)
}
updater := tools.NewUpdater([]string{"tctl", "tsh"}, toolsDir, teleport.Version)
toolsVersion, reExec := updater.CheckLocal()
if reExec {
// Download the version of client tools required by the cluster. This
// is required if the user passed in the TELEPORT_TOOLS_VERSION
// explicitly.
err := updater.UpdateWithLock(ctx, toolsVersion)
if err != nil {
utils.FatalError(err)
}
// Re-execute client tools with the correct version of client tools.
code, err := updater.Exec()
if err != nil {
utils.FatalError(err)
}
os.Exit(code)
}

err = TryRun(commands, os.Args[1:])
if err != nil {
var exitError *common.ExitCodeError
if errors.As(err, &exitError) {
Expand Down
9 changes: 8 additions & 1 deletion tool/tctl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,16 @@
package main

import (
"context"
"os/signal"
"syscall"

"github.com/gravitational/teleport/tool/tctl/common"
)

func main() {
common.Run(common.Commands())
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer cancel()

common.Run(ctx, common.Commands())
}
Loading

0 comments on commit cc3662d

Please sign in to comment.