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