Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6a245ab

Browse files
authored
test: Fix GPG test so it does not inherit parent parallelism (#5820)
* test: Fix GPG test so it does not inherit parent parallelism Running a subtest in a parent with `t.Parallel()` and using `t.Setenv` is not allowed in Go 1.20, so we move it to a separate test function. * Fix shadowed import
1 parent 73afdd7 commit 6a245ab

File tree

1 file changed

+154
-154
lines changed

1 file changed

+154
-154
lines changed

cli/ssh_test.go

+154-154
Original file line numberDiff line numberDiff line change
@@ -293,20 +293,21 @@ func TestSSH(t *testing.T) {
293293
pty.WriteLine("exit")
294294
<-cmdDone
295295
})
296+
}
296297

297-
//nolint:paralleltest // This test uses t.Setenv.
298-
t.Run("ForwardGPG", func(t *testing.T) {
299-
if runtime.GOOS == "windows" {
300-
// While GPG forwarding from a Windows client works, we currently do
301-
// not support forwarding to a Windows workspace. Our tests use the
302-
// same platform for the "client" and "workspace" as they run in the
303-
// same process.
304-
t.Skip("Test not supported on windows")
305-
}
298+
//nolint:paralleltest // This test uses t.Setenv, parent test MUST NOT be parallel.
299+
func TestSSH_ForwardGPG(t *testing.T) {
300+
if runtime.GOOS == "windows" {
301+
// While GPG forwarding from a Windows client works, we currently do
302+
// not support forwarding to a Windows workspace. Our tests use the
303+
// same platform for the "client" and "workspace" as they run in the
304+
// same process.
305+
t.Skip("Test not supported on windows")
306+
}
306307

307-
// This key is for [email protected].
308-
const randPublicKeyFingerprint = "7BDFBA0CC7F5A96537C806C427BC6335EB5117F1"
309-
const randPublicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
308+
// This key is for [email protected].
309+
const randPublicKeyFingerprint = "7BDFBA0CC7F5A96537C806C427BC6335EB5117F1"
310+
const randPublicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
310311
311312
mQINBF6SWkEBEADB8sAhBaT36VQ6HEhAmtKexLldu1HUdXNw16rdF+1wiBzSFfJN
312313
aPeX4Y9iFIZgC2wU0wOjJ04BpioyOLtJngbThI5WpeoQ/1yQZOpnDaCMPPLp+uJ+
@@ -359,40 +360,40 @@ p7KeSZdlk47pMBGOfnvEmoQ=
359360
=OxHv
360361
-----END PGP PUBLIC KEY BLOCK-----`
361362

362-
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
363-
defer cancel()
363+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
364+
defer cancel()
364365

365-
gpgPath, err := exec.LookPath("gpg")
366-
if err != nil {
367-
t.Skip("gpg not found")
368-
}
369-
gpgConfPath, err := exec.LookPath("gpgconf")
370-
if err != nil {
371-
t.Skip("gpgconf not found")
372-
}
373-
gpgAgentPath, err := exec.LookPath("gpg-agent")
374-
if err != nil {
375-
t.Skip("gpg-agent not found")
376-
}
366+
gpgPath, err := exec.LookPath("gpg")
367+
if err != nil {
368+
t.Skip("gpg not found")
369+
}
370+
gpgConfPath, err := exec.LookPath("gpgconf")
371+
if err != nil {
372+
t.Skip("gpgconf not found")
373+
}
374+
gpgAgentPath, err := exec.LookPath("gpg-agent")
375+
if err != nil {
376+
t.Skip("gpg-agent not found")
377+
}
377378

378-
// Setup GPG home directory on the "client".
379-
gnupgHomeClient := tempDirUnixSocket(t)
380-
t.Setenv("GNUPGHOME", gnupgHomeClient)
379+
// Setup GPG home directory on the "client".
380+
gnupgHomeClient := tempDirUnixSocket(t)
381+
t.Setenv("GNUPGHOME", gnupgHomeClient)
381382

382-
// Get the agent extra socket path.
383-
var (
384-
stdout = bytes.NewBuffer(nil)
385-
stderr = bytes.NewBuffer(nil)
386-
)
387-
c := exec.CommandContext(ctx, gpgConfPath, "--list-dir", "agent-extra-socket")
388-
c.Stdout = stdout
389-
c.Stderr = stderr
390-
err = c.Run()
391-
require.NoError(t, err, "get extra socket path failed: %s", stderr.String())
392-
extraSocketPath := strings.TrimSpace(stdout.String())
393-
394-
// Generate private key non-interactively.
395-
genKeyScript := `
383+
// Get the agent extra socket path.
384+
var (
385+
stdout = bytes.NewBuffer(nil)
386+
stderr = bytes.NewBuffer(nil)
387+
)
388+
c := exec.CommandContext(ctx, gpgConfPath, "--list-dir", "agent-extra-socket")
389+
c.Stdout = stdout
390+
c.Stderr = stderr
391+
err = c.Run()
392+
require.NoError(t, err, "get extra socket path failed: %s", stderr.String())
393+
extraSocketPath := strings.TrimSpace(stdout.String())
394+
395+
// Generate private key non-interactively.
396+
genKeyScript := `
396397
Key-Type: 1
397398
Key-Length: 2048
398399
Subkey-Type: 1
@@ -402,119 +403,118 @@ Name-Email: [email protected]
402403
Expire-Date: 0
403404
%no-protection
404405
`
405-
c = exec.CommandContext(ctx, gpgPath, "--batch", "--gen-key")
406-
c.Stdin = strings.NewReader(genKeyScript)
407-
out, err := c.CombinedOutput()
408-
require.NoError(t, err, "generate key failed: %s", out)
409-
410-
// Import a random public key.
411-
stdin := strings.NewReader(randPublicKey + "\n")
412-
c = exec.CommandContext(ctx, gpgPath, "--import", "-")
413-
c.Stdin = stdin
414-
out, err = c.CombinedOutput()
415-
require.NoError(t, err, "import key failed: %s", out)
416-
417-
// Set ultimate trust on imported key.
418-
stdin = strings.NewReader(randPublicKeyFingerprint + ":6:\n")
419-
c = exec.CommandContext(ctx, gpgPath, "--import-ownertrust")
420-
c.Stdin = stdin
421-
out, err = c.CombinedOutput()
422-
require.NoError(t, err, "import ownertrust failed: %s", out)
423-
424-
// Start the GPG agent.
425-
agentCmd := exec.CommandContext(ctx, gpgAgentPath, "--no-detach", "--extra-socket", extraSocketPath)
426-
agentCmd.Env = append(agentCmd.Env, "GNUPGHOME="+gnupgHomeClient)
427-
agentPTY, agentProc, err := pty.Start(agentCmd, pty.WithPTYOption(pty.WithGPGTTY()))
428-
require.NoError(t, err, "launch agent failed")
429-
defer func() {
430-
_ = agentProc.Kill()
431-
_ = agentPTY.Close()
432-
}()
433-
434-
// Get the agent socket path in the "workspace".
435-
gnupgHomeWorkspace := tempDirUnixSocket(t)
436-
437-
stdout = bytes.NewBuffer(nil)
438-
stderr = bytes.NewBuffer(nil)
439-
c = exec.CommandContext(ctx, gpgConfPath, "--list-dir", "agent-socket")
440-
c.Env = append(c.Env, "GNUPGHOME="+gnupgHomeWorkspace)
441-
c.Stdout = stdout
442-
c.Stderr = stderr
443-
err = c.Run()
444-
require.NoError(t, err, "get agent socket path in workspace failed: %s", stderr.String())
445-
workspaceAgentSocketPath := strings.TrimSpace(stdout.String())
446-
require.NotEqual(t, extraSocketPath, workspaceAgentSocketPath, "socket path should be different")
447-
448-
client, workspace, agentToken := setupWorkspaceForAgent(t, nil)
449-
450-
agentClient := codersdk.New(client.URL)
451-
agentClient.SetSessionToken(agentToken)
452-
agentCloser := agent.New(agent.Options{
453-
Client: agentClient,
454-
EnvironmentVariables: map[string]string{
455-
"GNUPGHOME": gnupgHomeWorkspace,
456-
},
457-
Logger: slogtest.Make(t, nil).Named("agent"),
458-
})
459-
defer agentCloser.Close()
460-
461-
cmd, root := clitest.New(t,
462-
"ssh",
463-
workspace.Name,
464-
"--forward-gpg",
465-
)
466-
clitest.SetupConfig(t, client, root)
467-
pty := ptytest.New(t)
468-
cmd.SetIn(pty.Input())
469-
cmd.SetOut(pty.Output())
470-
cmd.SetErr(pty.Output())
471-
cmdDone := tGo(t, func() {
472-
err := cmd.ExecuteContext(ctx)
473-
assert.NoError(t, err, "ssh command failed")
474-
})
475-
// Prevent the test from hanging if the asserts below kill the test
476-
// early. This will cause the command to exit with an error, which will
477-
// let the t.Cleanup'd `<-done` inside of `tGo` exit and not hang.
478-
// Without this, the test will hang forever on failure, preventing the
479-
// real error from being printed.
480-
t.Cleanup(cancel)
481-
482-
// Wait for the prompt or any output really to indicate the command has
483-
// started and accepting input on stdin.
484-
_ = pty.Peek(ctx, 1)
485-
486-
pty.WriteLine("echo hello 'world'")
487-
pty.ExpectMatch("hello world")
488-
489-
// Check the GNUPGHOME was correctly inherited via shell.
490-
pty.WriteLine("env && echo env-''-command-done")
491-
match := pty.ExpectMatch("env--command-done")
492-
require.Contains(t, match, "GNUPGHOME="+gnupgHomeWorkspace, match)
493-
494-
// Get the agent extra socket path in the "workspace" via shell.
495-
pty.WriteLine("gpgconf --list-dir agent-socket && echo gpgconf-''-agentsocket-command-done")
496-
pty.ExpectMatch(workspaceAgentSocketPath)
497-
pty.ExpectMatch("gpgconf--agentsocket-command-done")
498-
499-
// List the keys in the "workspace".
500-
pty.WriteLine("gpg --list-keys && echo gpg-''-listkeys-command-done")
501-
listKeysOutput := pty.ExpectMatch("gpg--listkeys-command-done")
502-
require.Contains(t, listKeysOutput, "[ultimate] Coder Test <[email protected]>")
503-
require.Contains(t, listKeysOutput, "[ultimate] Dean Sheather (work key) <[email protected]>")
504-
505-
// Try to sign something. This demonstrates that the forwarding is
506-
// working as expected, since the workspace doesn't have access to the
507-
// private key directly and must use the forwarded agent.
508-
pty.WriteLine("echo 'hello world' | gpg --clearsign && echo gpg-''-sign-command-done")
509-
pty.ExpectMatch("BEGIN PGP SIGNED MESSAGE")
510-
pty.ExpectMatch("Hash:")
511-
pty.ExpectMatch("hello world")
512-
pty.ExpectMatch("gpg--sign-command-done")
406+
c = exec.CommandContext(ctx, gpgPath, "--batch", "--gen-key")
407+
c.Stdin = strings.NewReader(genKeyScript)
408+
out, err := c.CombinedOutput()
409+
require.NoError(t, err, "generate key failed: %s", out)
410+
411+
// Import a random public key.
412+
stdin := strings.NewReader(randPublicKey + "\n")
413+
c = exec.CommandContext(ctx, gpgPath, "--import", "-")
414+
c.Stdin = stdin
415+
out, err = c.CombinedOutput()
416+
require.NoError(t, err, "import key failed: %s", out)
417+
418+
// Set ultimate trust on imported key.
419+
stdin = strings.NewReader(randPublicKeyFingerprint + ":6:\n")
420+
c = exec.CommandContext(ctx, gpgPath, "--import-ownertrust")
421+
c.Stdin = stdin
422+
out, err = c.CombinedOutput()
423+
require.NoError(t, err, "import ownertrust failed: %s", out)
424+
425+
// Start the GPG agent.
426+
agentCmd := exec.CommandContext(ctx, gpgAgentPath, "--no-detach", "--extra-socket", extraSocketPath)
427+
agentCmd.Env = append(agentCmd.Env, "GNUPGHOME="+gnupgHomeClient)
428+
agentPTY, agentProc, err := pty.Start(agentCmd, pty.WithPTYOption(pty.WithGPGTTY()))
429+
require.NoError(t, err, "launch agent failed")
430+
defer func() {
431+
_ = agentProc.Kill()
432+
_ = agentPTY.Close()
433+
}()
513434

514-
// And we're done.
515-
pty.WriteLine("exit")
516-
<-cmdDone
435+
// Get the agent socket path in the "workspace".
436+
gnupgHomeWorkspace := tempDirUnixSocket(t)
437+
438+
stdout = bytes.NewBuffer(nil)
439+
stderr = bytes.NewBuffer(nil)
440+
c = exec.CommandContext(ctx, gpgConfPath, "--list-dir", "agent-socket")
441+
c.Env = append(c.Env, "GNUPGHOME="+gnupgHomeWorkspace)
442+
c.Stdout = stdout
443+
c.Stderr = stderr
444+
err = c.Run()
445+
require.NoError(t, err, "get agent socket path in workspace failed: %s", stderr.String())
446+
workspaceAgentSocketPath := strings.TrimSpace(stdout.String())
447+
require.NotEqual(t, extraSocketPath, workspaceAgentSocketPath, "socket path should be different")
448+
449+
client, workspace, agentToken := setupWorkspaceForAgent(t, nil)
450+
451+
agentClient := codersdk.New(client.URL)
452+
agentClient.SetSessionToken(agentToken)
453+
agentCloser := agent.New(agent.Options{
454+
Client: agentClient,
455+
EnvironmentVariables: map[string]string{
456+
"GNUPGHOME": gnupgHomeWorkspace,
457+
},
458+
Logger: slogtest.Make(t, nil).Named("agent"),
459+
})
460+
defer agentCloser.Close()
461+
462+
cmd, root := clitest.New(t,
463+
"ssh",
464+
workspace.Name,
465+
"--forward-gpg",
466+
)
467+
clitest.SetupConfig(t, client, root)
468+
tpty := ptytest.New(t)
469+
cmd.SetIn(tpty.Input())
470+
cmd.SetOut(tpty.Output())
471+
cmd.SetErr(tpty.Output())
472+
cmdDone := tGo(t, func() {
473+
err := cmd.ExecuteContext(ctx)
474+
assert.NoError(t, err, "ssh command failed")
517475
})
476+
// Prevent the test from hanging if the asserts below kill the test
477+
// early. This will cause the command to exit with an error, which will
478+
// let the t.Cleanup'd `<-done` inside of `tGo` exit and not hang.
479+
// Without this, the test will hang forever on failure, preventing the
480+
// real error from being printed.
481+
t.Cleanup(cancel)
482+
483+
// Wait for the prompt or any output really to indicate the command has
484+
// started and accepting input on stdin.
485+
_ = tpty.Peek(ctx, 1)
486+
487+
tpty.WriteLine("echo hello 'world'")
488+
tpty.ExpectMatch("hello world")
489+
490+
// Check the GNUPGHOME was correctly inherited via shell.
491+
tpty.WriteLine("env && echo env-''-command-done")
492+
match := tpty.ExpectMatch("env--command-done")
493+
require.Contains(t, match, "GNUPGHOME="+gnupgHomeWorkspace, match)
494+
495+
// Get the agent extra socket path in the "workspace" via shell.
496+
tpty.WriteLine("gpgconf --list-dir agent-socket && echo gpgconf-''-agentsocket-command-done")
497+
tpty.ExpectMatch(workspaceAgentSocketPath)
498+
tpty.ExpectMatch("gpgconf--agentsocket-command-done")
499+
500+
// List the keys in the "workspace".
501+
tpty.WriteLine("gpg --list-keys && echo gpg-''-listkeys-command-done")
502+
listKeysOutput := tpty.ExpectMatch("gpg--listkeys-command-done")
503+
require.Contains(t, listKeysOutput, "[ultimate] Coder Test <[email protected]>")
504+
require.Contains(t, listKeysOutput, "[ultimate] Dean Sheather (work key) <[email protected]>")
505+
506+
// Try to sign something. This demonstrates that the forwarding is
507+
// working as expected, since the workspace doesn't have access to the
508+
// private key directly and must use the forwarded agent.
509+
tpty.WriteLine("echo 'hello world' | gpg --clearsign && echo gpg-''-sign-command-done")
510+
tpty.ExpectMatch("BEGIN PGP SIGNED MESSAGE")
511+
tpty.ExpectMatch("Hash:")
512+
tpty.ExpectMatch("hello world")
513+
tpty.ExpectMatch("gpg--sign-command-done")
514+
515+
// And we're done.
516+
tpty.WriteLine("exit")
517+
<-cmdDone
518518
}
519519

520520
// tGoContext runs fn in a goroutine passing a context that will be

0 commit comments

Comments
 (0)