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