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  }