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  }