github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/e2e/nomadexec/exec.go (about) 1 package nomadexec 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io" 8 "reflect" 9 "regexp" 10 "strings" 11 "testing" 12 "time" 13 14 "github.com/hashicorp/nomad/api" 15 "github.com/hashicorp/nomad/e2e/e2eutil" 16 "github.com/hashicorp/nomad/e2e/framework" 17 "github.com/hashicorp/nomad/helper/uuid" 18 dtestutils "github.com/hashicorp/nomad/plugins/drivers/testutils" 19 "github.com/stretchr/testify/assert" 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 // TODO: Occasionally, we get "Unexpected EOF" error, but with the correct output. 94 // investigate why 95 if err != nil && strings.Contains(err.Error(), io.ErrUnexpectedEOF.Error()) { 96 f.T().Logf("got unexpected EOF error, ignoring: %v", err) 97 } else { 98 assert.NoError(t, err) 99 } 100 101 assert.Equal(t, c.ExitCode, exitCode) 102 103 switch s := c.Stdout.(type) { 104 case string: 105 assert.Equal(t, s, stdout.String()) 106 case *regexp.Regexp: 107 assert.Regexp(t, s, stdout.String()) 108 case nil: 109 assert.Empty(t, stdout.String()) 110 default: 111 assert.Fail(t, "unexpected stdout type", "found %v (%v), but expected string or regexp", s, reflect.TypeOf(s)) 112 } 113 114 switch s := c.Stderr.(type) { 115 case string: 116 assert.Equal(t, s, stderr.String()) 117 case *regexp.Regexp: 118 assert.Regexp(t, s, stderr.String()) 119 case nil: 120 assert.Empty(t, stderr.String()) 121 default: 122 assert.Fail(t, "unexpected stderr type", "found %v (%v), but expected string or regexp", s, reflect.TypeOf(s)) 123 } 124 }) 125 } 126 } 127 128 func (tc *NomadExecE2ETest) AfterAll(f *framework.F) { 129 jobs := tc.Nomad().Jobs() 130 if tc.jobID != "" { 131 jobs.Deregister(tc.jobID, true, nil) 132 } 133 tc.Nomad().System().GarbageCollect() 134 } 135 136 func newTestStdin(tty bool, d string) io.Reader { 137 pr, pw := io.Pipe() 138 go func() { 139 pw.Write([]byte(d)) 140 141 // when testing TTY, leave connection open for the entire duration of command 142 // closing stdin may cause TTY session prematurely before command completes 143 if !tty { 144 pw.Close() 145 } 146 147 }() 148 149 return pr 150 }