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
13 changes: 12 additions & 1 deletion integration/proxy/teleterm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,10 @@ func testKubeGatewayCertRenewal(t *testing.T, suite *Suite, albAddr string, kube
kubeGateway, err := gateway.AsKube(gw)
require.NoError(t, err)

client = kubeClientForLocalProxy(t, kubeGateway.KubeconfigPath(), teleportCluster, kubeCluster)
kubeconfigPath := kubeGateway.KubeconfigPath()
checkKubeconfigPathInCommandEnv(t, gw, kubeconfigPath)

client = kubeClientForLocalProxy(t, kubeconfigPath, teleportCluster, kubeCluster)
})

mustGetKubePod(t, client, kubePodName)
Expand All @@ -350,3 +353,11 @@ func testKubeGatewayCertRenewal(t *testing.T, suite *Suite, albAddr string, kube
)

}

func checkKubeconfigPathInCommandEnv(t *testing.T, gw gateway.Gateway, wantKubeconfigPath string) {
t.Helper()

cmd, err := gw.CLICommand()
require.NoError(t, err)
require.Equal(t, cmd.Env, []string{"KUBECONFIG=" + wantKubeconfigPath})
}
5 changes: 4 additions & 1 deletion lib/teleterm/gateway/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,10 @@ func (b *base) CLICommand() (*api.GatewayCLICommand, error) {
if err != nil {
return nil, trace.Wrap(err)
}
return makeCLICommand(cmd), nil
}

func makeCLICommand(cmd *exec.Cmd) *api.GatewayCLICommand {
cmdString := strings.TrimSpace(
fmt.Sprintf("%s %s",
strings.Join(cmd.Env, " "),
Expand All @@ -204,7 +207,7 @@ func (b *base) CLICommand() (*api.GatewayCLICommand, error) {
Args: cmd.Args,
Env: cmd.Env,
Preview: cmdString,
}, nil
}
}

// ReloadCert loads the key pair from cfg.CertPath & cfg.KeyPath and updates the cert of the running
Expand Down
13 changes: 13 additions & 0 deletions lib/teleterm/gateway/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (

"github.com/gravitational/teleport/api/utils/keypaths"
"github.com/gravitational/teleport/api/utils/keys"
api "github.com/gravitational/teleport/gen/proto/go/teleport/lib/teleterm/v1"
"github.com/gravitational/teleport/lib/client"
"github.com/gravitational/teleport/lib/kube/kubeconfig"
"github.com/gravitational/teleport/lib/srv/alpnproxy"
Expand Down Expand Up @@ -209,3 +210,15 @@ func (k *kube) writeKubeconfig(key *keys.PrivateKey, cas map[string]tls.Certific
})
return nil
}

func (k *kube) CLICommand() (*api.GatewayCLICommand, error) {
// TODO(greedy52) currently kube must implement CLICommand in order to pass
// Kube to CLICommandProvider. We should revisit gateway design/flows like
Comment thread
greedy52 marked this conversation as resolved.
// this. For example, one alternative is to move gateway.CLICommand to
// daemon.GatewayCLICommand as daemon owns all CLICommandProvider.
cmd, err := k.cfg.CLICommandProvider.GetCommand(k)
if err != nil {
return nil, trace.Wrap(err)
}
return makeCLICommand(cmd), nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import { makeRuntimeSettings } from 'teleterm/mainProcess/fixtures/mocks';

import { GatewayCliClientCommand } from '../types';
import { ShellCommand, GatewayCliClientCommand } from '../types';

import { getPtyProcessOptions } from './buildPtyOptions';

Expand Down Expand Up @@ -50,4 +50,32 @@ describe('getPtyProcessOptions', () => {
expect(env.shared).toBe('fromCmd');
});
});

describe('pty.shell', () => {
it('merges process env with the env from cmd', () => {
const processEnv = {
processExclusive: 'process',
shared: 'fromProcess',
};
const cmd: ShellCommand = {
kind: 'pty.shell',
clusterName: 'bar',
proxyHost: 'baz',
env: {
cmdExclusive: 'cmd',
shared: 'fromCmd',
},
};

const { env } = getPtyProcessOptions(
makeRuntimeSettings(),
cmd,
processEnv
);

expect(env.processExclusive).toBe('process');
expect(env.cmdExclusive).toBe('cmd');
expect(env.shared).toBe('fromCmd');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ export function getPtyProcessOptions(
path: settings.defaultShell,
args: [],
cwd: cmd.cwd,
env,
env: { ...env, ...cmd.env },
initMessage: cmd.initMessage,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ export function createPtyHostClient(
if (ptyOptions.cwd) {
request.setCwd(ptyOptions.cwd);
}
if (ptyOptions.initMessage) {
request.setInitMessage(ptyOptions.initMessage);
}

return new Promise<string>((resolve, reject) => {
client.createPtyProcess(request, (error, response) => {
Expand Down
9 changes: 9 additions & 0 deletions web/packages/teleterm/src/services/pty/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ export type PtyServiceClient = {
export type ShellCommand = PtyCommandBase & {
kind: 'pty.shell';
cwd?: string;
// env is a record of additional env variables that need to be set for the shell terminal and it
// will be merged with process env.
env?: Record<string, string>;
// initMessage is a help message presented to the user at the beginning of
// the shell to provide extra context.
//
// The initMessage is rendered on the terminal UI without being written or
// read by the underlying PTY.
initMessage?: string;
};

export type TshLoginCommand = PtyCommandBase & {
Expand Down
25 changes: 24 additions & 1 deletion web/packages/teleterm/src/services/tshd/testHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const makeServer = (props: Partial<tsh.Server> = {}): tsh.Server => ({
});

export const databaseUri = '/clusters/teleport-local/dbs/foo';
export const kubeUri = '/clusters/teleport-local/kubes/foo';

export const makeDatabase = (
props: Partial<tsh.Database> = {}
Expand Down Expand Up @@ -74,7 +75,9 @@ export const makeRootCluster = (
...props,
});

export const makeGateway = (props: Partial<tsh.Gateway> = {}): tsh.Gateway => ({
export const makeDatabaseGateway = (
props: Partial<tsh.Gateway> = {}
): tsh.Gateway => ({
uri: '/gateways/foo',
targetName: 'sales-production',
targetUri: databaseUri,
Expand All @@ -91,3 +94,23 @@ export const makeGateway = (props: Partial<tsh.Gateway> = {}): tsh.Gateway => ({
targetSubresourceName: 'bar',
...props,
});

export const makeKubeGateway = (
props: Partial<tsh.Gateway> = {}
): tsh.Gateway => ({
uri: '/gateways/foo',
targetName: 'foo',
targetUri: kubeUri,
targetUser: '',
localAddress: 'localhost',
localPort: '1337',
protocol: '',
gatewayCliCommand: {
path: '/bin/kubectl',
argsList: ['version'],
envList: ['KUBECONFIG=/path/to/kubeconfig'],
preview: 'KUBECONFIG=/path/to/kubeconfig /bin/kubectl version',
},
targetSubresourceName: '',
...props,
});
4 changes: 2 additions & 2 deletions web/packages/teleterm/src/services/tshd/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export interface Server extends apiServer.Server.AsObject {

export interface Gateway extends apiGateway.Gateway.AsObject {
uri: uri.GatewayUri;
targetUri: uri.DatabaseUri;
targetUri: uri.DatabaseUri | uri.KubeUri;
Comment thread
ravicious marked this conversation as resolved.
// The type of gatewayCliCommand was repeated here just to refer to the type with the JSDoc.
gatewayCliCommand: GatewayCLICommand;
}
Expand Down Expand Up @@ -272,7 +272,7 @@ export interface LoginPasswordlessParams extends LoginParamsBase {
}

export type CreateGatewayParams = {
targetUri: uri.DatabaseUri;
targetUri: uri.DatabaseUri | uri.KubeUri;
port?: string;
user: string;
subresource_name?: string;
Expand Down
34 changes: 19 additions & 15 deletions web/packages/teleterm/src/services/tshdNotifications.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/**
/*
* Copyright 2023 Gravitational, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -31,27 +31,31 @@ export class TshdNotificationsService {
request.cannotProxyGatewayConnection;
const gateway = this.clustersService.findGateway(gatewayUri);
const clusterName = routing.parseClusterName(targetUri);
let shortTargetDesc: string;
let longTargetDesc: string;
let targetName: string;
let targetUser: string;
let targetDesc: string;

// Try to get target name and user from gateway object.
if (gateway) {
shortTargetDesc = `${gateway.targetName} as ${gateway.targetUser}`;
longTargetDesc = shortTargetDesc;
targetName = gateway.targetName;
targetUser = gateway.targetUser;
} else {
const targetName = routing.parseDbUri(targetUri)?.params['dbId'];
// Try to get target name from target URI.
targetName =
routing.parseDbUri(targetUri)?.params['dbId'] ||
routing.parseKubeUri(targetUri)?.params['kubeId'] ||
targetUri;
}

if (targetName) {
shortTargetDesc = targetName;
longTargetDesc = shortTargetDesc;
} else {
shortTargetDesc = 'a database server';
longTargetDesc = `a database server under ${targetUri}`;
}
if (targetUser) {
targetDesc = `${targetName} as ${targetUser}`;
} else {
targetDesc = targetName;
}

const notificationContent = {
title: `Cannot connect to ${shortTargetDesc} (${clusterName})`,
description: `You tried to connect to ${longTargetDesc} but we encountered an unexpected error: ${error}`,
title: `Cannot connect to ${targetDesc} (${clusterName})`,
description: `You tried to connect to ${targetDesc} but we encountered an unexpected error: ${error}`,
};

this.notificationsService.notifyError(notificationContent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ message PtyCreate {
reserved 6;
reserved "init_command";
google.protobuf.Struct env = 7;
string init_message = 8;
}

message PtyClientEvent {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export function createPtyHostService(): IPtyHostServer {
cwd: ptyOptions.cwd,
ptyId,
env: call.request.getEnv()?.toJavaScript() as Record<string, string>,
initMessage: ptyOptions.initMessage,
});
ptyProcesses.set(ptyId, ptyProcess);
} catch (error) {
Expand Down
7 changes: 7 additions & 0 deletions web/packages/teleterm/src/sharedProcess/ptyHost/ptyProcess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ export class PtyProcess extends EventEmitter implements IPtyProcess {
this._setStatus('open');
this.emit(TermEventEnum.Open);

// Emit the init/help message before registering data handler. This ensures
// the message is printed first and will not conflict with data coming from
// the PTY.
if (this.options.initMessage) {
this.emit(TermEventEnum.Data, this.options.initMessage);
}

this._process.onData(data => this._handleData(data));
this._process.onExit(ev => this._handleExit(ev));
}
Expand Down
1 change: 1 addition & 0 deletions web/packages/teleterm/src/sharedProcess/ptyHost/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export type PtyProcessOptions = {
path: string;
args: string[];
cwd?: string;
initMessage?: string;
};

export type IPtyProcess = {
Expand Down
Loading