github.com/bigcommerce/nomad@v0.9.3-bc/e2e/nomadexec/exec.go (about) 1 package nomadexec 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io" 8 "reflect" 9 "regexp" 10 "testing" 11 "time" 12 13 "github.com/hashicorp/nomad/api" 14 "github.com/hashicorp/nomad/e2e/e2eutil" 15 "github.com/hashicorp/nomad/e2e/framework" 16 "github.com/hashicorp/nomad/helper/uuid" 17 dtestutils "github.com/hashicorp/nomad/plugins/drivers/testutils" 18 "github.com/stretchr/testify/assert" 19 "github.com/stretchr/testify/require" 20 ) 21 22 type NomadExecE2ETest struct { 23 framework.TC 24 25 name string 26 jobFilePath string 27 28 jobID string 29 alloc api.Allocation 30 } 31 32 func init() { 33 framework.AddSuites(&framework.TestSuite{ 34 Component: "Nomad exec", 35 CanRunLocal: true, 36 Cases: []framework.TestCase{ 37 newNomadExecE2eTest("docker", "./nomadexec/testdata/docker.nomad"), 38 }, 39 }) 40 } 41 42 func newNomadExecE2eTest(name, jobFilePath string) *NomadExecE2ETest { 43 return &NomadExecE2ETest{ 44 name: name, 45 jobFilePath: jobFilePath, 46 } 47 } 48 49 func (tc *NomadExecE2ETest) Name() string { 50 return fmt.Sprintf("%v (%v)", tc.TC.Name(), tc.name) 51 } 52 53 func (tc *NomadExecE2ETest) BeforeAll(f *framework.F) { 54 // Ensure cluster has leader before running tests 55 e2eutil.WaitForLeader(f.T(), tc.Nomad()) 56 e2eutil.WaitForNodesReady(f.T(), tc.Nomad(), 1) 57 58 // register a job for execing into 59 tc.jobID = "nomad-exec" + uuid.Generate()[:8] 60 allocs := e2eutil.RegisterAndWaitForAllocs(f.T(), tc.Nomad(), tc.jobFilePath, tc.jobID) 61 f.Len(allocs, 1) 62 63 e2eutil.WaitForAllocRunning(f.T(), tc.Nomad(), allocs[0].ID) 64 65 tc.alloc = api.Allocation{ 66 ID: allocs[0].ID, 67 Namespace: allocs[0].Namespace, 68 NodeID: allocs[0].NodeID, 69 } 70 } 71 72 func (tc *NomadExecE2ETest) TestExecBasicResponses(f *framework.F) { 73 for _, c := range dtestutils.ExecTaskStreamingBasicCases { 74 f.T().Run(c.Name, func(t *testing.T) { 75 76 stdin := newTestStdin(c.Tty, c.Stdin) 77 var stdout, stderr bytes.Buffer 78 79 resizeCh := make(chan api.TerminalSize) 80 go func() { 81 resizeCh <- api.TerminalSize{Height: 100, Width: 100} 82 }() 83 84 ctx, cancelFn := context.WithTimeout(context.Background(), 15*time.Second) 85 defer cancelFn() 86 87 exitCode, err := tc.Nomad().Allocations().Exec(ctx, 88 &tc.alloc, "task", c.Tty, 89 []string{"/bin/sh", "-c", c.Command}, 90 stdin, &stdout, &stderr, 91 resizeCh, nil) 92 93 require.NoError(t, err) 94 95 assert.Equal(t, c.ExitCode, exitCode) 96 97 switch s := c.Stdout.(type) { 98 case string: 99 require.Equal(t, s, stdout.String()) 100 case *regexp.Regexp: 101 require.Regexp(t, s, stdout.String()) 102 case nil: 103 require.Empty(t, stdout.String()) 104 default: 105 require.Fail(t, "unexpected stdout type", "found %v (%v), but expected string or regexp", s, reflect.TypeOf(s)) 106 } 107 108 switch s := c.Stderr.(type) { 109 case string: 110 require.Equal(t, s, stderr.String()) 111 case *regexp.Regexp: 112 require.Regexp(t, s, stderr.String()) 113 case nil: 114 require.Empty(t, stderr.String()) 115 default: 116 require.Fail(t, "unexpected stderr type", "found %v (%v), but expected string or regexp", s, reflect.TypeOf(s)) 117 } 118 }) 119 } 120 } 121 122 func (tc *NomadExecE2ETest) AfterAll(f *framework.F) { 123 jobs := tc.Nomad().Jobs() 124 if tc.jobID != "" { 125 jobs.Deregister(tc.jobID, true, nil) 126 } 127 tc.Nomad().System().GarbageCollect() 128 } 129 130 func newTestStdin(tty bool, d string) io.Reader { 131 pr, pw := io.Pipe() 132 go func() { 133 pw.Write([]byte(d)) 134 135 // when testing TTY, leave connection open for the entire duration of command 136 // closing stdin may cause TTY session prematurely before command completes 137 if !tty { 138 pw.Close() 139 } 140 141 }() 142 143 return pr 144 }