github.com/bigcommerce/nomad@v0.9.3-bc/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  	"github.com/stretchr/testify/require"
    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  			require.NoError(t, err)
    94  
    95  			assert.Equal(t, c.ExitCode, exitCode)
    96  
    97  			switch s := c.Stdout.(type) {
    98  			case string:
    99  				require.Equal(t, s, stdout.String())
   100  			case *regexp.Regexp:
   101  				require.Regexp(t, s, stdout.String())
   102  			case nil:
   103  				require.Empty(t, stdout.String())
   104  			default:
   105  				require.Fail(t, "unexpected stdout type", "found %v (%v), but expected string or regexp", s, reflect.TypeOf(s))
   106  			}
   107  
   108  			switch s := c.Stderr.(type) {
   109  			case string:
   110  				require.Equal(t, s, stderr.String())
   111  			case *regexp.Regexp:
   112  				require.Regexp(t, s, stderr.String())
   113  			case nil:
   114  				require.Empty(t, stderr.String())
   115  			default:
   116  				require.Fail(t, "unexpected stderr type", "found %v (%v), but expected string or regexp", s, reflect.TypeOf(s))
   117  			}
   118  		})
   119  	}
   120  }
   121  
   122  func (tc *NomadExecE2ETest) AfterAll(f *framework.F) {
   123  	jobs := tc.Nomad().Jobs()
   124  	if tc.jobID != "" {
   125  		jobs.Deregister(tc.jobID, true, nil)
   126  	}
   127  	tc.Nomad().System().GarbageCollect()
   128  }
   129  
   130  func newTestStdin(tty bool, d string) io.Reader {
   131  	pr, pw := io.Pipe()
   132  	go func() {
   133  		pw.Write([]byte(d))
   134  
   135  		// when testing TTY, leave connection open for the entire duration of command
   136  		// closing stdin may cause TTY session prematurely before command completes
   137  		if !tty {
   138  			pw.Close()
   139  		}
   140  
   141  	}()
   142  
   143  	return pr
   144  }