github.com/djenriquez/nomad-1@v0.8.1/client/driver/rkt_test.go (about)

     1  // +build linux
     2  
     3  package driver
     4  
     5  import (
     6  	"bytes"
     7  	"context"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"reflect"
    14  	"strings"
    15  	"syscall"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/hashicorp/nomad/client/config"
    20  	cstructs "github.com/hashicorp/nomad/client/structs"
    21  	"github.com/hashicorp/nomad/nomad/structs"
    22  	"github.com/hashicorp/nomad/testutil"
    23  	"github.com/stretchr/testify/assert"
    24  
    25  	ctestutils "github.com/hashicorp/nomad/client/testutil"
    26  )
    27  
    28  func TestRktVersionRegex(t *testing.T) {
    29  	t.Parallel()
    30  	if os.Getenv("NOMAD_TEST_RKT") == "" {
    31  		t.Skip("NOMAD_TEST_RKT unset, skipping")
    32  	}
    33  
    34  	inputRkt := "rkt version 0.8.1"
    35  	inputAppc := "appc version 1.2.0"
    36  	expectedRkt := "0.8.1"
    37  	expectedAppc := "1.2.0"
    38  	rktMatches := reRktVersion.FindStringSubmatch(inputRkt)
    39  	appcMatches := reAppcVersion.FindStringSubmatch(inputAppc)
    40  	if rktMatches[1] != expectedRkt {
    41  		fmt.Printf("Test failed; got %q; want %q\n", rktMatches[1], expectedRkt)
    42  	}
    43  	if appcMatches[1] != expectedAppc {
    44  		fmt.Printf("Test failed; got %q; want %q\n", appcMatches[1], expectedAppc)
    45  	}
    46  }
    47  
    48  // The fingerprinter test should always pass, even if rkt is not installed.
    49  func TestRktDriver_Fingerprint(t *testing.T) {
    50  	t.Parallel()
    51  	if os.Getenv("NOMAD_TEST_RKT") == "" {
    52  		t.Skip("skipping rkt tests")
    53  	}
    54  
    55  	ctestutils.RktCompatible(t)
    56  	ctx := testDriverContexts(t, &structs.Task{Name: "foo", Driver: "rkt"})
    57  	d := NewRktDriver(ctx.DriverCtx)
    58  	node := &structs.Node{
    59  		Attributes: make(map[string]string),
    60  	}
    61  
    62  	request := &cstructs.FingerprintRequest{Config: &config.Config{}, Node: node}
    63  	var response cstructs.FingerprintResponse
    64  	err := d.Fingerprint(request, &response)
    65  	if err != nil {
    66  		t.Fatalf("err: %v", err)
    67  	}
    68  
    69  	if !response.Detected {
    70  		t.Fatalf("expected response to be applicable")
    71  	}
    72  
    73  	attributes := response.Attributes
    74  	if attributes == nil {
    75  		t.Fatalf("expected attributes to not equal nil")
    76  	}
    77  	if attributes["driver.rkt"] != "1" {
    78  		t.Fatalf("Missing Rkt driver")
    79  	}
    80  	if attributes["driver.rkt.version"] == "" {
    81  		t.Fatalf("Missing Rkt driver version")
    82  	}
    83  	if attributes["driver.rkt.appc.version"] == "" {
    84  		t.Fatalf("Missing appc version for the Rkt driver")
    85  	}
    86  }
    87  
    88  func TestRktDriver_Start_DNS(t *testing.T) {
    89  	if !testutil.IsTravis() {
    90  		t.Parallel()
    91  	}
    92  	if os.Getenv("NOMAD_TEST_RKT") == "" {
    93  		t.Skip("skipping rkt tests")
    94  	}
    95  
    96  	ctestutils.RktCompatible(t)
    97  	// TODO: use test server to load from a fixture
    98  	task := &structs.Task{
    99  		Name:   "etcd",
   100  		Driver: "rkt",
   101  		Config: map[string]interface{}{
   102  			"trust_prefix":       "coreos.com/etcd",
   103  			"image":              "coreos.com/etcd:v2.0.4",
   104  			"command":            "/etcd",
   105  			"dns_servers":        []string{"8.8.8.8", "8.8.4.4"},
   106  			"dns_search_domains": []string{"example.com", "example.org", "example.net"},
   107  		},
   108  		LogConfig: &structs.LogConfig{
   109  			MaxFiles:      10,
   110  			MaxFileSizeMB: 10,
   111  		},
   112  		Resources: &structs.Resources{
   113  			MemoryMB: 128,
   114  			CPU:      100,
   115  		},
   116  	}
   117  
   118  	ctx := testDriverContexts(t, task)
   119  	defer ctx.AllocDir.Destroy()
   120  	d := NewRktDriver(ctx.DriverCtx)
   121  
   122  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   123  		t.Fatalf("error in prestart: %v", err)
   124  	}
   125  	resp, err := d.Start(ctx.ExecCtx, task)
   126  	if err != nil {
   127  		t.Fatalf("err: %v", err)
   128  	}
   129  	defer resp.Handle.Kill()
   130  
   131  	// Attempt to open
   132  	handle2, err := d.Open(ctx.ExecCtx, resp.Handle.ID())
   133  	if err != nil {
   134  		t.Fatalf("err: %v", err)
   135  	}
   136  	if handle2 == nil {
   137  		t.Fatalf("missing handle")
   138  	}
   139  	handle2.Kill()
   140  }
   141  
   142  func TestRktDriver_Start_Wait(t *testing.T) {
   143  	if !testutil.IsTravis() {
   144  		t.Parallel()
   145  	}
   146  	if os.Getenv("NOMAD_TEST_RKT") == "" {
   147  		t.Skip("skipping rkt tests")
   148  	}
   149  
   150  	ctestutils.RktCompatible(t)
   151  	task := &structs.Task{
   152  		Name:   "etcd",
   153  		Driver: "rkt",
   154  		Config: map[string]interface{}{
   155  			"trust_prefix": "coreos.com/etcd",
   156  			"image":        "coreos.com/etcd:v2.0.4",
   157  			"command":      "/etcd",
   158  			"args":         []string{"--version"},
   159  		},
   160  		LogConfig: &structs.LogConfig{
   161  			MaxFiles:      10,
   162  			MaxFileSizeMB: 10,
   163  		},
   164  		Resources: &structs.Resources{
   165  			MemoryMB: 128,
   166  			CPU:      100,
   167  		},
   168  	}
   169  
   170  	ctx := testDriverContexts(t, task)
   171  	defer ctx.AllocDir.Destroy()
   172  	d := NewRktDriver(ctx.DriverCtx)
   173  
   174  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   175  		t.Fatalf("error in prestart: %v", err)
   176  	}
   177  	resp, err := d.Start(ctx.ExecCtx, task)
   178  	if err != nil {
   179  		t.Fatalf("err: %v", err)
   180  	}
   181  	handle := resp.Handle.(*rktHandle)
   182  	defer handle.Kill()
   183  
   184  	// Update should be a no-op
   185  	if err := handle.Update(task); err != nil {
   186  		t.Fatalf("err: %v", err)
   187  	}
   188  
   189  	// Signal should be an error
   190  	if err := resp.Handle.Signal(syscall.SIGTERM); err == nil {
   191  		t.Fatalf("err: %v", err)
   192  	}
   193  
   194  	select {
   195  	case res := <-resp.Handle.WaitCh():
   196  		if !res.Successful() {
   197  			t.Fatalf("err: %v", res)
   198  		}
   199  	case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second):
   200  		t.Fatalf("timeout")
   201  	}
   202  
   203  	// Make sure pod was removed #3561
   204  	var stderr bytes.Buffer
   205  	cmd := exec.Command(rktCmd, "status", handle.uuid)
   206  	cmd.Stdout = ioutil.Discard
   207  	cmd.Stderr = &stderr
   208  	if err := cmd.Run(); err == nil {
   209  		t.Fatalf("expected error running 'rkt status %s' on removed container", handle.uuid)
   210  	}
   211  	if out := stderr.String(); !strings.Contains(out, "no matches found") {
   212  		t.Fatalf("expected 'no matches found' but received: %s", out)
   213  	}
   214  }
   215  
   216  func TestRktDriver_Start_Wait_Skip_Trust(t *testing.T) {
   217  	if !testutil.IsTravis() {
   218  		t.Parallel()
   219  	}
   220  	if os.Getenv("NOMAD_TEST_RKT") == "" {
   221  		t.Skip("skipping rkt tests")
   222  	}
   223  
   224  	ctestutils.RktCompatible(t)
   225  	task := &structs.Task{
   226  		Name:   "etcd",
   227  		Driver: "rkt",
   228  		Config: map[string]interface{}{
   229  			"image":   "coreos.com/etcd:v2.0.4",
   230  			"command": "/etcd",
   231  			"args":    []string{"--version"},
   232  		},
   233  		LogConfig: &structs.LogConfig{
   234  			MaxFiles:      10,
   235  			MaxFileSizeMB: 10,
   236  		},
   237  		Resources: &structs.Resources{
   238  			MemoryMB: 128,
   239  			CPU:      100,
   240  		},
   241  	}
   242  
   243  	ctx := testDriverContexts(t, task)
   244  	defer ctx.AllocDir.Destroy()
   245  	d := NewRktDriver(ctx.DriverCtx)
   246  
   247  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   248  		t.Fatalf("error in prestart: %v", err)
   249  	}
   250  	resp, err := d.Start(ctx.ExecCtx, task)
   251  	if err != nil {
   252  		t.Fatalf("err: %v", err)
   253  	}
   254  	defer resp.Handle.Kill()
   255  
   256  	// Update should be a no-op
   257  	err = resp.Handle.Update(task)
   258  	if err != nil {
   259  		t.Fatalf("err: %v", err)
   260  	}
   261  
   262  	select {
   263  	case res := <-resp.Handle.WaitCh():
   264  		if !res.Successful() {
   265  			t.Fatalf("err: %v", res)
   266  		}
   267  	case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second):
   268  		t.Fatalf("timeout")
   269  	}
   270  }
   271  
   272  func TestRktDriver_Start_Wait_AllocDir(t *testing.T) {
   273  	if !testutil.IsTravis() {
   274  		t.Parallel()
   275  	}
   276  	if os.Getenv("NOMAD_TEST_RKT") == "" {
   277  		t.Skip("skipping rkt tests")
   278  	}
   279  
   280  	ctestutils.RktCompatible(t)
   281  
   282  	exp := []byte{'w', 'i', 'n'}
   283  	file := "output.txt"
   284  	tmpvol, err := ioutil.TempDir("", "nomadtest_rktdriver_volumes")
   285  	if err != nil {
   286  		t.Fatalf("error creating temporary dir: %v", err)
   287  	}
   288  	defer os.RemoveAll(tmpvol)
   289  	hostpath := filepath.Join(tmpvol, file)
   290  
   291  	task := &structs.Task{
   292  		Name:   "rkttest_alpine",
   293  		Driver: "rkt",
   294  		Config: map[string]interface{}{
   295  			"image":   "docker://alpine",
   296  			"command": "/bin/sh",
   297  			"args": []string{
   298  				"-c",
   299  				fmt.Sprintf(`echo -n %s > foo/%s`, string(exp), file),
   300  			},
   301  			"net":     []string{"none"},
   302  			"volumes": []string{fmt.Sprintf("%s:/foo", tmpvol)},
   303  		},
   304  		LogConfig: &structs.LogConfig{
   305  			MaxFiles:      10,
   306  			MaxFileSizeMB: 10,
   307  		},
   308  		Resources: &structs.Resources{
   309  			MemoryMB: 128,
   310  			CPU:      100,
   311  		},
   312  	}
   313  
   314  	ctx := testDriverContexts(t, task)
   315  	defer ctx.AllocDir.Destroy()
   316  	d := NewRktDriver(ctx.DriverCtx)
   317  
   318  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   319  		t.Fatalf("error in prestart: %v", err)
   320  	}
   321  	resp, err := d.Start(ctx.ExecCtx, task)
   322  	if err != nil {
   323  		t.Fatalf("err: %v", err)
   324  	}
   325  	defer resp.Handle.Kill()
   326  
   327  	select {
   328  	case res := <-resp.Handle.WaitCh():
   329  		if !res.Successful() {
   330  			t.Fatalf("err: %v", res)
   331  		}
   332  	case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second):
   333  		t.Fatalf("timeout")
   334  	}
   335  
   336  	// Check that data was written to the shared alloc directory.
   337  	act, err := ioutil.ReadFile(hostpath)
   338  	if err != nil {
   339  		t.Fatalf("Couldn't read expected output: %v", err)
   340  	}
   341  
   342  	if !reflect.DeepEqual(act, exp) {
   343  		t.Fatalf("Command output is %v; expected %v", act, exp)
   344  	}
   345  }
   346  
   347  // TestRktDriver_UserGroup asserts tasks may override the user and group of the
   348  // rkt image.
   349  func TestRktDriver_UserGroup(t *testing.T) {
   350  	if !testutil.IsTravis() {
   351  		t.Parallel()
   352  	}
   353  	if os.Getenv("NOMAD_TEST_RKT") == "" {
   354  		t.Skip("skipping rkt tests")
   355  	}
   356  	ctestutils.RktCompatible(t)
   357  	require := assert.New(t)
   358  
   359  	task := &structs.Task{
   360  		Name:   "etcd",
   361  		Driver: "rkt",
   362  		User:   "nobody",
   363  		Config: map[string]interface{}{
   364  			"image": "docker://redis:3.2",
   365  			"group": "nogroup",
   366  		},
   367  		LogConfig: &structs.LogConfig{
   368  			MaxFiles:      10,
   369  			MaxFileSizeMB: 10,
   370  		},
   371  		Resources: &structs.Resources{
   372  			MemoryMB: 128,
   373  			CPU:      100,
   374  		},
   375  	}
   376  
   377  	tctx := testDriverContexts(t, task)
   378  	defer tctx.AllocDir.Destroy()
   379  	d := NewRktDriver(tctx.DriverCtx)
   380  
   381  	_, err := d.Prestart(tctx.ExecCtx, task)
   382  	require.Nil(err)
   383  	resp, err := d.Start(tctx.ExecCtx, task)
   384  	require.Nil(err)
   385  	defer resp.Handle.Kill()
   386  
   387  	timeout := time.Duration(testutil.TestMultiplier()*15) * time.Second
   388  
   389  	ctx, cancel := context.WithTimeout(context.Background(), timeout)
   390  	defer cancel()
   391  
   392  	// WaitUntil we can determine the user/group redis is running as
   393  	expected := []byte("redis-server *:6379         nobody   nogroup\n")
   394  	testutil.WaitForResult(func() (bool, error) {
   395  		raw, code, err := resp.Handle.Exec(ctx, "/bin/bash", []string{"-c", "ps -eo args,user,group | grep ^redis"})
   396  		if err != nil {
   397  			return false, err
   398  		}
   399  		if code != 0 {
   400  			return false, fmt.Errorf("unexpected exit code: %d", code)
   401  		}
   402  		return bytes.Equal(expected, raw), fmt.Errorf("expected %q but found %q", expected, raw)
   403  	}, func(err error) {
   404  		t.Fatalf("err: %v", err)
   405  	})
   406  
   407  	require.Nil(resp.Handle.Kill())
   408  }
   409  
   410  func TestRktTrustPrefix(t *testing.T) {
   411  	if !testutil.IsTravis() {
   412  		t.Parallel()
   413  	}
   414  	if os.Getenv("NOMAD_TEST_RKT") == "" {
   415  		t.Skip("skipping rkt tests")
   416  	}
   417  	ctestutils.RktCompatible(t)
   418  	task := &structs.Task{
   419  		Name:   "etcd",
   420  		Driver: "rkt",
   421  		Config: map[string]interface{}{
   422  			"trust_prefix": "example.com/invalid",
   423  			"image":        "coreos.com/etcd:v2.0.4",
   424  			"command":      "/etcd",
   425  			"args":         []string{"--version"},
   426  		},
   427  		LogConfig: &structs.LogConfig{
   428  			MaxFiles:      10,
   429  			MaxFileSizeMB: 10,
   430  		},
   431  		Resources: &structs.Resources{
   432  			MemoryMB: 128,
   433  			CPU:      100,
   434  		},
   435  	}
   436  	ctx := testDriverContexts(t, task)
   437  	defer ctx.AllocDir.Destroy()
   438  	d := NewRktDriver(ctx.DriverCtx)
   439  
   440  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   441  		t.Fatalf("error in prestart: %v", err)
   442  	}
   443  	resp, err := d.Start(ctx.ExecCtx, task)
   444  	if err == nil {
   445  		resp.Handle.Kill()
   446  		t.Fatalf("Should've failed")
   447  	}
   448  	msg := "Error running rkt trust"
   449  	if !strings.Contains(err.Error(), msg) {
   450  		t.Fatalf("Expecting '%v' in '%v'", msg, err)
   451  	}
   452  }
   453  
   454  func TestRktTaskValidate(t *testing.T) {
   455  	t.Parallel()
   456  	ctestutils.RktCompatible(t)
   457  	task := &structs.Task{
   458  		Name:   "etcd",
   459  		Driver: "rkt",
   460  		Config: map[string]interface{}{
   461  			"trust_prefix":       "coreos.com/etcd",
   462  			"image":              "coreos.com/etcd:v2.0.4",
   463  			"command":            "/etcd",
   464  			"args":               []string{"--version"},
   465  			"dns_servers":        []string{"8.8.8.8", "8.8.4.4"},
   466  			"dns_search_domains": []string{"example.com", "example.org", "example.net"},
   467  		},
   468  		Resources: basicResources,
   469  	}
   470  	ctx := testDriverContexts(t, task)
   471  	defer ctx.AllocDir.Destroy()
   472  	d := NewRktDriver(ctx.DriverCtx)
   473  
   474  	if err := d.Validate(task.Config); err != nil {
   475  		t.Fatalf("Validation error in TaskConfig : '%v'", err)
   476  	}
   477  }
   478  
   479  // TODO: Port Mapping test should be ran with proper ACI image and test the port access.
   480  func TestRktDriver_PortsMapping(t *testing.T) {
   481  	if !testutil.IsTravis() {
   482  		t.Parallel()
   483  	}
   484  	if os.Getenv("NOMAD_TEST_RKT") == "" {
   485  		t.Skip("skipping rkt tests")
   486  	}
   487  
   488  	ctestutils.RktCompatible(t)
   489  	task := &structs.Task{
   490  		Name:   "etcd",
   491  		Driver: "rkt",
   492  		Config: map[string]interface{}{
   493  			"image": "docker://redis:3.2",
   494  			"port_map": []map[string]string{
   495  				{
   496  					"main": "6379-tcp",
   497  				},
   498  			},
   499  			"debug": "true",
   500  		},
   501  		LogConfig: &structs.LogConfig{
   502  			MaxFiles:      10,
   503  			MaxFileSizeMB: 10,
   504  		},
   505  		Resources: &structs.Resources{
   506  			MemoryMB: 256,
   507  			CPU:      512,
   508  			Networks: []*structs.NetworkResource{
   509  				{
   510  					IP:            "127.0.0.1",
   511  					ReservedPorts: []structs.Port{{Label: "main", Value: 8080}},
   512  				},
   513  			},
   514  		},
   515  	}
   516  
   517  	ctx := testDriverContexts(t, task)
   518  	defer ctx.AllocDir.Destroy()
   519  	d := NewRktDriver(ctx.DriverCtx)
   520  
   521  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   522  		t.Fatalf("error in prestart: %v", err)
   523  	}
   524  	resp, err := d.Start(ctx.ExecCtx, task)
   525  	if err != nil {
   526  		t.Fatalf("err: %v", err)
   527  	}
   528  	if resp.Network == nil {
   529  		t.Fatalf("Expected driver to set a DriverNetwork, but it did not!")
   530  	}
   531  
   532  	failCh := make(chan error, 1)
   533  	go func() {
   534  		time.Sleep(1 * time.Second)
   535  		if err := resp.Handle.Kill(); err != nil {
   536  			failCh <- err
   537  		}
   538  	}()
   539  
   540  	select {
   541  	case err := <-failCh:
   542  		t.Fatalf("failed to kill handle: %v", err)
   543  	case <-resp.Handle.WaitCh():
   544  	case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second):
   545  		t.Fatalf("timeout")
   546  	}
   547  }
   548  
   549  // TestRktDriver_PortsMapping_Host asserts that port_map isn't required when
   550  // host networking is used.
   551  func TestRktDriver_PortsMapping_Host(t *testing.T) {
   552  	if !testutil.IsTravis() {
   553  		t.Parallel()
   554  	}
   555  	if os.Getenv("NOMAD_TEST_RKT") == "" {
   556  		t.Skip("skipping rkt tests")
   557  	}
   558  
   559  	ctestutils.RktCompatible(t)
   560  	task := &structs.Task{
   561  		Name:   "etcd",
   562  		Driver: "rkt",
   563  		Config: map[string]interface{}{
   564  			"image": "docker://redis:latest",
   565  			"net":   []string{"host"},
   566  		},
   567  		LogConfig: &structs.LogConfig{
   568  			MaxFiles:      10,
   569  			MaxFileSizeMB: 10,
   570  		},
   571  		Resources: &structs.Resources{
   572  			MemoryMB: 256,
   573  			CPU:      512,
   574  			Networks: []*structs.NetworkResource{
   575  				{
   576  					IP:            "127.0.0.1",
   577  					ReservedPorts: []structs.Port{{Label: "main", Value: 8080}},
   578  				},
   579  			},
   580  		},
   581  	}
   582  
   583  	ctx := testDriverContexts(t, task)
   584  	defer ctx.AllocDir.Destroy()
   585  	d := NewRktDriver(ctx.DriverCtx)
   586  
   587  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   588  		t.Fatalf("error in prestart: %v", err)
   589  	}
   590  	resp, err := d.Start(ctx.ExecCtx, task)
   591  	if err != nil {
   592  		t.Fatalf("err: %v", err)
   593  	}
   594  	if resp.Network != nil {
   595  		t.Fatalf("No network should be returned with --net=host but found: %#v", resp.Network)
   596  	}
   597  
   598  	failCh := make(chan error, 1)
   599  	go func() {
   600  		time.Sleep(1 * time.Second)
   601  		if err := resp.Handle.Kill(); err != nil {
   602  			failCh <- err
   603  		}
   604  	}()
   605  
   606  	select {
   607  	case err := <-failCh:
   608  		t.Fatalf("failed to kill handle: %v", err)
   609  	case <-resp.Handle.WaitCh():
   610  	case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second):
   611  		t.Fatalf("timeout")
   612  	}
   613  }
   614  
   615  func TestRktDriver_HandlerExec(t *testing.T) {
   616  	if !testutil.IsTravis() {
   617  		t.Parallel()
   618  	}
   619  	if os.Getenv("NOMAD_TEST_RKT") == "" {
   620  		t.Skip("skipping rkt tests")
   621  	}
   622  
   623  	ctestutils.RktCompatible(t)
   624  	task := &structs.Task{
   625  		Name:   "etcd",
   626  		Driver: "rkt",
   627  		Config: map[string]interface{}{
   628  			"trust_prefix": "coreos.com/etcd",
   629  			"image":        "coreos.com/etcd:v2.0.4",
   630  			"command":      "/etcd",
   631  		},
   632  		LogConfig: &structs.LogConfig{
   633  			MaxFiles:      10,
   634  			MaxFileSizeMB: 10,
   635  		},
   636  		Resources: &structs.Resources{
   637  			MemoryMB: 128,
   638  			CPU:      100,
   639  		},
   640  	}
   641  
   642  	ctx := testDriverContexts(t, task)
   643  	defer ctx.AllocDir.Destroy()
   644  	d := NewRktDriver(ctx.DriverCtx)
   645  
   646  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   647  		t.Fatalf("error in prestart: %v", err)
   648  	}
   649  	resp, err := d.Start(ctx.ExecCtx, task)
   650  	if err != nil {
   651  		t.Fatalf("err: %v", err)
   652  	}
   653  
   654  	// Give the pod a second to start
   655  	time.Sleep(time.Second)
   656  
   657  	// Exec a command that should work
   658  	out, code, err := resp.Handle.Exec(context.TODO(), "/etcd", []string{"--version"})
   659  	if err != nil {
   660  		t.Fatalf("error exec'ing etcd --version: %v", err)
   661  	}
   662  	if code != 0 {
   663  		t.Fatalf("expected `etcd --version` to succeed but exit code was: %d\n%s", code, string(out))
   664  	}
   665  	if expected := []byte("etcd version "); !bytes.HasPrefix(out, expected) {
   666  		t.Fatalf("expected output to start with %q but found:\n%q", expected, out)
   667  	}
   668  
   669  	// Exec a command that should fail
   670  	out, code, err = resp.Handle.Exec(context.TODO(), "/etcd", []string{"--kaljdshf"})
   671  	if err != nil {
   672  		t.Fatalf("error exec'ing bad command: %v", err)
   673  	}
   674  	if code == 0 {
   675  		t.Fatalf("expected `stat` to fail but exit code was: %d", code)
   676  	}
   677  	if expected := "flag provided but not defined"; !bytes.Contains(out, []byte(expected)) {
   678  		t.Fatalf("expected output to contain %q but found: %q", expected, out)
   679  	}
   680  
   681  	if err := resp.Handle.Kill(); err != nil {
   682  		t.Fatalf("error killing handle: %v", err)
   683  	}
   684  }
   685  
   686  func TestRktDriver_Remove_Error(t *testing.T) {
   687  	if !testutil.IsTravis() {
   688  		t.Parallel()
   689  	}
   690  	if os.Getenv("NOMAD_TEST_RKT") == "" {
   691  		t.Skip("skipping rkt tests")
   692  	}
   693  
   694  	ctestutils.RktCompatible(t)
   695  
   696  	// Removing a nonexistent pod should return an error
   697  	if err := rktRemove("00000000-0000-0000-0000-000000000000"); err == nil {
   698  		t.Fatalf("expected an error")
   699  	}
   700  
   701  	if err := rktRemove("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"); err == nil {
   702  		t.Fatalf("expected an error")
   703  	}
   704  }