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  }