-
Notifications
You must be signed in to change notification settings - Fork 891
test(agent): use afero for motd tests to allow parallel execution #8329
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -192,7 +192,7 @@ func TestAgent_Stats_Magic(t *testing.T) { | |
|
||
func TestAgent_SessionExec(t *testing.T) { | ||
t.Parallel() | ||
session := setupSSHSession(t, agentsdk.Manifest{}, codersdk.ServiceBannerConfig{}) | ||
session := setupSSHSession(t, agentsdk.Manifest{}, codersdk.ServiceBannerConfig{}, nil) | ||
|
||
command := "echo test" | ||
if runtime.GOOS == "windows" { | ||
|
@@ -205,7 +205,7 @@ func TestAgent_SessionExec(t *testing.T) { | |
|
||
func TestAgent_GitSSH(t *testing.T) { | ||
t.Parallel() | ||
session := setupSSHSession(t, agentsdk.Manifest{}, codersdk.ServiceBannerConfig{}) | ||
session := setupSSHSession(t, agentsdk.Manifest{}, codersdk.ServiceBannerConfig{}, nil) | ||
command := "sh -c 'echo $GIT_SSH_COMMAND'" | ||
if runtime.GOOS == "windows" { | ||
command = "cmd.exe /c echo %GIT_SSH_COMMAND%" | ||
|
@@ -225,7 +225,7 @@ func TestAgent_SessionTTYShell(t *testing.T) { | |
// it seems like it could be either. | ||
t.Skip("ConPTY appears to be inconsistent on Windows.") | ||
} | ||
session := setupSSHSession(t, agentsdk.Manifest{}, codersdk.ServiceBannerConfig{}) | ||
session := setupSSHSession(t, agentsdk.Manifest{}, codersdk.ServiceBannerConfig{}, nil) | ||
command := "sh" | ||
if runtime.GOOS == "windows" { | ||
command = "cmd.exe" | ||
|
@@ -248,7 +248,7 @@ func TestAgent_SessionTTYShell(t *testing.T) { | |
|
||
func TestAgent_SessionTTYExitCode(t *testing.T) { | ||
t.Parallel() | ||
session := setupSSHSession(t, agentsdk.Manifest{}, codersdk.ServiceBannerConfig{}) | ||
session := setupSSHSession(t, agentsdk.Manifest{}, codersdk.ServiceBannerConfig{}, nil) | ||
command := "areallynotrealcommand" | ||
err := session.RequestPty("xterm", 128, 128, ssh.TerminalModes{}) | ||
require.NoError(t, err) | ||
|
@@ -268,25 +268,22 @@ func TestAgent_SessionTTYExitCode(t *testing.T) { | |
} | ||
} | ||
|
||
//nolint:paralleltest // This test sets an environment variable. | ||
func TestAgent_Session_TTY_MOTD(t *testing.T) { | ||
t.Parallel() | ||
if runtime.GOOS == "windows" { | ||
// This might be our implementation, or ConPTY itself. | ||
// It's difficult to find extensive tests for it, so | ||
// it seems like it could be either. | ||
t.Skip("ConPTY appears to be inconsistent on Windows.") | ||
} | ||
|
||
wantMOTD := "Welcome to your Coder workspace!" | ||
wantServiceBanner := "Service banner text goes here" | ||
u, err := user.Current() | ||
require.NoError(t, err, "get current user") | ||
|
||
tmpdir := t.TempDir() | ||
name := filepath.Join(tmpdir, "motd") | ||
err := os.WriteFile(name, []byte(wantMOTD), 0o600) | ||
require.NoError(t, err, "write motd file") | ||
name := filepath.Join(u.HomeDir, "motd") | ||
|
||
// Set HOME so we can ensure no ~/.hushlogin is present. | ||
t.Setenv("HOME", tmpdir) | ||
wantMOTD := "Welcome to your Coder workspace!" | ||
wantServiceBanner := "Service banner text goes here" | ||
|
||
tests := []struct { | ||
name string | ||
|
@@ -362,14 +359,20 @@ func TestAgent_Session_TTY_MOTD(t *testing.T) { | |
for _, test := range tests { | ||
test := test | ||
t.Run(test.name, func(t *testing.T) { | ||
session := setupSSHSession(t, test.manifest, test.banner) | ||
t.Parallel() | ||
session := setupSSHSession(t, test.manifest, test.banner, func(fs afero.Fs) { | ||
err := fs.MkdirAll(filepath.Dir(name), 0o700) | ||
require.NoError(t, err) | ||
err = afero.WriteFile(fs, name, []byte(wantMOTD), 0o600) | ||
require.NoError(t, err) | ||
}) | ||
testSessionOutput(t, session, test.expected, test.unexpected, test.expectedRe) | ||
}) | ||
} | ||
} | ||
|
||
//nolint:paralleltest // This test sets an environment variable. | ||
func TestAgent_Session_TTY_MOTD_Update(t *testing.T) { | ||
t.Parallel() | ||
if runtime.GOOS == "windows" { | ||
// This might be our implementation, or ConPTY itself. | ||
// It's difficult to find extensive tests for it, so | ||
|
@@ -380,11 +383,6 @@ func TestAgent_Session_TTY_MOTD_Update(t *testing.T) { | |
// Only the banner updates dynamically; the MOTD file does not. | ||
wantServiceBanner := "Service banner text goes here" | ||
|
||
tmpdir := t.TempDir() | ||
|
||
// Set HOME so we can ensure no ~/.hushlogin is present. | ||
t.Setenv("HOME", tmpdir) | ||
|
||
tests := []struct { | ||
banner codersdk.ServiceBannerConfig | ||
expected []string | ||
|
@@ -424,22 +422,25 @@ func TestAgent_Session_TTY_MOTD_Update(t *testing.T) { | |
}, | ||
} | ||
|
||
const updateInterval = 100 * time.Millisecond | ||
|
||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) | ||
defer cancel() | ||
//nolint:dogsled // Allow the blank identifiers. | ||
conn, client, _, _, _ := setupAgent(t, &client{}, 0) | ||
for _, test := range tests { | ||
test := test | ||
// Set new banner func and wait for the agent to call it to update the | ||
// banner. | ||
|
||
ready := make(chan struct{}, 2) | ||
client.mu.Lock() | ||
client.getServiceBanner = func() (codersdk.ServiceBannerConfig, error) { | ||
select { | ||
case ready <- struct{}{}: | ||
default: | ||
} | ||
return test.banner, nil | ||
} | ||
client.mu.Unlock() | ||
time.Sleep(updateInterval) | ||
<-ready | ||
<-ready // Wait for two updates to ensure the value has propagated. | ||
|
||
sshClient, err := conn.SSHClient(ctx) | ||
require.NoError(t, err) | ||
|
@@ -466,50 +467,51 @@ func TestAgent_Session_TTY_QuietLogin(t *testing.T) { | |
} | ||
|
||
wantNotMOTD := "Welcome to your Coder workspace!" | ||
wantServiceBanner := "Service banner text goes here" | ||
wantMaybeServiceBanner := "Service banner text goes here" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 I like the use of |
||
|
||
tmpdir := t.TempDir() | ||
name := filepath.Join(tmpdir, "motd") | ||
err := os.WriteFile(name, []byte(wantNotMOTD), 0o600) | ||
require.NoError(t, err, "write motd file") | ||
u, err := user.Current() | ||
require.NoError(t, err, "get current user") | ||
|
||
// Set HOME so we can ensure ~/.hushlogin is present. | ||
t.Setenv("HOME", tmpdir) | ||
name := filepath.Join(u.HomeDir, "motd") | ||
|
||
// Neither banner nor MOTD should show if not a login shell. | ||
t.Run("NotLogin", func(t *testing.T) { | ||
wantEcho := "foobar" | ||
session := setupSSHSession(t, agentsdk.Manifest{ | ||
MOTDFile: name, | ||
}, codersdk.ServiceBannerConfig{ | ||
Enabled: true, | ||
Message: wantServiceBanner, | ||
Message: wantMaybeServiceBanner, | ||
}, func(fs afero.Fs) { | ||
err := afero.WriteFile(fs, name, []byte(wantNotMOTD), 0o600) | ||
require.NoError(t, err, "write motd file") | ||
}) | ||
err = session.RequestPty("xterm", 128, 128, ssh.TerminalModes{}) | ||
require.NoError(t, err) | ||
|
||
wantEcho := "foobar" | ||
command := "echo " + wantEcho | ||
output, err := session.Output(command) | ||
require.NoError(t, err) | ||
|
||
require.Contains(t, string(output), wantEcho, "should show echo") | ||
require.NotContains(t, string(output), wantNotMOTD, "should not show motd") | ||
require.NotContains(t, string(output), wantServiceBanner, "should not show service banner") | ||
require.NotContains(t, string(output), wantMaybeServiceBanner, "should not show service banner") | ||
}) | ||
|
||
// Only the MOTD should be silenced. | ||
// Only the MOTD should be silenced when hushlogin is present. | ||
t.Run("Hushlogin", func(t *testing.T) { | ||
// Create hushlogin to silence motd. | ||
f, err := os.Create(filepath.Join(tmpdir, ".hushlogin")) | ||
require.NoError(t, err, "create .hushlogin file") | ||
err = f.Close() | ||
require.NoError(t, err, "close .hushlogin file") | ||
|
||
session := setupSSHSession(t, agentsdk.Manifest{ | ||
MOTDFile: name, | ||
}, codersdk.ServiceBannerConfig{ | ||
Enabled: true, | ||
Message: wantServiceBanner, | ||
Message: wantMaybeServiceBanner, | ||
}, func(fs afero.Fs) { | ||
err := afero.WriteFile(fs, name, []byte(wantNotMOTD), 0o600) | ||
require.NoError(t, err, "write motd file") | ||
|
||
// Create hushlogin to silence motd. | ||
err = afero.WriteFile(fs, name, []byte{}, 0o600) | ||
require.NoError(t, err, "write hushlogin file") | ||
}) | ||
err = session.RequestPty("xterm", 128, 128, ssh.TerminalModes{}) | ||
require.NoError(t, err) | ||
|
@@ -527,7 +529,7 @@ func TestAgent_Session_TTY_QuietLogin(t *testing.T) { | |
require.NoError(t, err) | ||
|
||
require.NotContains(t, stdout.String(), wantNotMOTD, "should not show motd") | ||
require.Contains(t, stdout.String(), wantServiceBanner, "should show service banner") | ||
require.Contains(t, stdout.String(), wantMaybeServiceBanner, "should show service banner") | ||
}) | ||
} | ||
|
||
|
@@ -975,7 +977,7 @@ func TestAgent_EnvironmentVariables(t *testing.T) { | |
EnvironmentVariables: map[string]string{ | ||
key: value, | ||
}, | ||
}, codersdk.ServiceBannerConfig{}) | ||
}, codersdk.ServiceBannerConfig{}, nil) | ||
command := "sh -c 'echo $" + key + "'" | ||
if runtime.GOOS == "windows" { | ||
command = "cmd.exe /c echo %" + key + "%" | ||
|
@@ -992,7 +994,7 @@ func TestAgent_EnvironmentVariableExpansion(t *testing.T) { | |
EnvironmentVariables: map[string]string{ | ||
key: "$SOMETHINGNOTSET", | ||
}, | ||
}, codersdk.ServiceBannerConfig{}) | ||
}, codersdk.ServiceBannerConfig{}, nil) | ||
command := "sh -c 'echo $" + key + "'" | ||
if runtime.GOOS == "windows" { | ||
command = "cmd.exe /c echo %" + key + "%" | ||
|
@@ -1015,7 +1017,7 @@ func TestAgent_CoderEnvVars(t *testing.T) { | |
t.Run(key, func(t *testing.T) { | ||
t.Parallel() | ||
|
||
session := setupSSHSession(t, agentsdk.Manifest{}, codersdk.ServiceBannerConfig{}) | ||
session := setupSSHSession(t, agentsdk.Manifest{}, codersdk.ServiceBannerConfig{}, nil) | ||
command := "sh -c 'echo $" + key + "'" | ||
if runtime.GOOS == "windows" { | ||
command = "cmd.exe /c echo %" + key + "%" | ||
|
@@ -1038,7 +1040,7 @@ func TestAgent_SSHConnectionEnvVars(t *testing.T) { | |
t.Run(key, func(t *testing.T) { | ||
t.Parallel() | ||
|
||
session := setupSSHSession(t, agentsdk.Manifest{}, codersdk.ServiceBannerConfig{}) | ||
session := setupSSHSession(t, agentsdk.Manifest{}, codersdk.ServiceBannerConfig{}, nil) | ||
command := "sh -c 'echo $" + key + "'" | ||
if runtime.GOOS == "windows" { | ||
command = "cmd.exe /c echo %" + key + "%" | ||
|
@@ -1876,16 +1878,20 @@ func setupSSHSession( | |
t *testing.T, | ||
options agentsdk.Manifest, | ||
serviceBanner codersdk.ServiceBannerConfig, | ||
prepareFS func(fs afero.Fs), | ||
) *ssh.Session { | ||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) | ||
defer cancel() | ||
//nolint:dogsled | ||
conn, _, _, _, _ := setupAgent(t, &client{ | ||
conn, _, _, fs, _ := setupAgent(t, &client{ | ||
manifest: options, | ||
getServiceBanner: func() (codersdk.ServiceBannerConfig, error) { | ||
return serviceBanner, nil | ||
}, | ||
}, 0) | ||
if prepareFS != nil { | ||
prepareFS(fs) | ||
} | ||
sshClient, err := conn.SSHClient(ctx) | ||
require.NoError(t, err) | ||
t.Cleanup(func() { | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review: This is the race fix, if we only wait for one
ready
, the service banner may not yet be stored in the agents atomic pointer.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh good find! Thank you for fixing!