github.com/hspak/nomad@v0.7.2-0.20180309000617-bc4ae22a39a5/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  func TestRktDriverUser(t *testing.T) {
   348  	assert := assert.New(t)
   349  	if !testutil.IsTravis() {
   350  		t.Parallel()
   351  	}
   352  	if os.Getenv("NOMAD_TEST_RKT") == "" {
   353  		t.Skip("skipping rkt tests")
   354  	}
   355  
   356  	ctestutils.RktCompatible(t)
   357  	task := &structs.Task{
   358  		Name:   "etcd",
   359  		Driver: "rkt",
   360  		User:   "alice",
   361  		Config: map[string]interface{}{
   362  			"trust_prefix": "coreos.com/etcd",
   363  			"image":        "coreos.com/etcd:v2.0.4",
   364  			"command":      "/etcd",
   365  			"args":         []string{"--version"},
   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  	ctx := testDriverContexts(t, task)
   378  	defer ctx.AllocDir.Destroy()
   379  	d := NewRktDriver(ctx.DriverCtx)
   380  
   381  	_, err := d.Prestart(ctx.ExecCtx, task)
   382  	assert.Nil(err)
   383  	resp, err := d.Start(ctx.ExecCtx, task)
   384  	assert.Nil(err)
   385  	defer resp.Handle.Kill()
   386  
   387  	select {
   388  	case res := <-resp.Handle.WaitCh():
   389  		assert.False(res.Successful())
   390  	case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second):
   391  		t.Fatalf("timeout")
   392  	}
   393  
   394  }
   395  
   396  func TestRktTrustPrefix(t *testing.T) {
   397  	if !testutil.IsTravis() {
   398  		t.Parallel()
   399  	}
   400  	if os.Getenv("NOMAD_TEST_RKT") == "" {
   401  		t.Skip("skipping rkt tests")
   402  	}
   403  	ctestutils.RktCompatible(t)
   404  	task := &structs.Task{
   405  		Name:   "etcd",
   406  		Driver: "rkt",
   407  		Config: map[string]interface{}{
   408  			"trust_prefix": "example.com/invalid",
   409  			"image":        "coreos.com/etcd:v2.0.4",
   410  			"command":      "/etcd",
   411  			"args":         []string{"--version"},
   412  		},
   413  		LogConfig: &structs.LogConfig{
   414  			MaxFiles:      10,
   415  			MaxFileSizeMB: 10,
   416  		},
   417  		Resources: &structs.Resources{
   418  			MemoryMB: 128,
   419  			CPU:      100,
   420  		},
   421  	}
   422  	ctx := testDriverContexts(t, task)
   423  	defer ctx.AllocDir.Destroy()
   424  	d := NewRktDriver(ctx.DriverCtx)
   425  
   426  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   427  		t.Fatalf("error in prestart: %v", err)
   428  	}
   429  	resp, err := d.Start(ctx.ExecCtx, task)
   430  	if err == nil {
   431  		resp.Handle.Kill()
   432  		t.Fatalf("Should've failed")
   433  	}
   434  	msg := "Error running rkt trust"
   435  	if !strings.Contains(err.Error(), msg) {
   436  		t.Fatalf("Expecting '%v' in '%v'", msg, err)
   437  	}
   438  }
   439  
   440  func TestRktTaskValidate(t *testing.T) {
   441  	t.Parallel()
   442  	ctestutils.RktCompatible(t)
   443  	task := &structs.Task{
   444  		Name:   "etcd",
   445  		Driver: "rkt",
   446  		Config: map[string]interface{}{
   447  			"trust_prefix":       "coreos.com/etcd",
   448  			"image":              "coreos.com/etcd:v2.0.4",
   449  			"command":            "/etcd",
   450  			"args":               []string{"--version"},
   451  			"dns_servers":        []string{"8.8.8.8", "8.8.4.4"},
   452  			"dns_search_domains": []string{"example.com", "example.org", "example.net"},
   453  		},
   454  		Resources: basicResources,
   455  	}
   456  	ctx := testDriverContexts(t, task)
   457  	defer ctx.AllocDir.Destroy()
   458  	d := NewRktDriver(ctx.DriverCtx)
   459  
   460  	if err := d.Validate(task.Config); err != nil {
   461  		t.Fatalf("Validation error in TaskConfig : '%v'", err)
   462  	}
   463  }
   464  
   465  // TODO: Port Mapping test should be ran with proper ACI image and test the port access.
   466  func TestRktDriver_PortsMapping(t *testing.T) {
   467  	if !testutil.IsTravis() {
   468  		t.Parallel()
   469  	}
   470  	if os.Getenv("NOMAD_TEST_RKT") == "" {
   471  		t.Skip("skipping rkt tests")
   472  	}
   473  
   474  	ctestutils.RktCompatible(t)
   475  	task := &structs.Task{
   476  		Name:   "etcd",
   477  		Driver: "rkt",
   478  		Config: map[string]interface{}{
   479  			"image": "docker://redis:latest",
   480  			"port_map": []map[string]string{
   481  				{
   482  					"main": "6379-tcp",
   483  				},
   484  			},
   485  			"debug": "true",
   486  		},
   487  		LogConfig: &structs.LogConfig{
   488  			MaxFiles:      10,
   489  			MaxFileSizeMB: 10,
   490  		},
   491  		Resources: &structs.Resources{
   492  			MemoryMB: 256,
   493  			CPU:      512,
   494  			Networks: []*structs.NetworkResource{
   495  				{
   496  					IP:            "127.0.0.1",
   497  					ReservedPorts: []structs.Port{{Label: "main", Value: 8080}},
   498  				},
   499  			},
   500  		},
   501  	}
   502  
   503  	ctx := testDriverContexts(t, task)
   504  	defer ctx.AllocDir.Destroy()
   505  	d := NewRktDriver(ctx.DriverCtx)
   506  
   507  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   508  		t.Fatalf("error in prestart: %v", err)
   509  	}
   510  	resp, err := d.Start(ctx.ExecCtx, task)
   511  	if err != nil {
   512  		t.Fatalf("err: %v", err)
   513  	}
   514  	if resp.Network == nil {
   515  		t.Fatalf("Expected driver to set a DriverNetwork, but it did not!")
   516  	}
   517  
   518  	failCh := make(chan error, 1)
   519  	go func() {
   520  		time.Sleep(1 * time.Second)
   521  		if err := resp.Handle.Kill(); err != nil {
   522  			failCh <- err
   523  		}
   524  	}()
   525  
   526  	select {
   527  	case err := <-failCh:
   528  		t.Fatalf("failed to kill handle: %v", err)
   529  	case <-resp.Handle.WaitCh():
   530  	case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second):
   531  		t.Fatalf("timeout")
   532  	}
   533  }
   534  
   535  // TestRktDriver_PortsMapping_Host asserts that port_map isn't required when
   536  // host networking is used.
   537  func TestRktDriver_PortsMapping_Host(t *testing.T) {
   538  	if !testutil.IsTravis() {
   539  		t.Parallel()
   540  	}
   541  	if os.Getenv("NOMAD_TEST_RKT") == "" {
   542  		t.Skip("skipping rkt tests")
   543  	}
   544  
   545  	ctestutils.RktCompatible(t)
   546  	task := &structs.Task{
   547  		Name:   "etcd",
   548  		Driver: "rkt",
   549  		Config: map[string]interface{}{
   550  			"image": "docker://redis:latest",
   551  			"net":   []string{"host"},
   552  		},
   553  		LogConfig: &structs.LogConfig{
   554  			MaxFiles:      10,
   555  			MaxFileSizeMB: 10,
   556  		},
   557  		Resources: &structs.Resources{
   558  			MemoryMB: 256,
   559  			CPU:      512,
   560  			Networks: []*structs.NetworkResource{
   561  				{
   562  					IP:            "127.0.0.1",
   563  					ReservedPorts: []structs.Port{{Label: "main", Value: 8080}},
   564  				},
   565  			},
   566  		},
   567  	}
   568  
   569  	ctx := testDriverContexts(t, task)
   570  	defer ctx.AllocDir.Destroy()
   571  	d := NewRktDriver(ctx.DriverCtx)
   572  
   573  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   574  		t.Fatalf("error in prestart: %v", err)
   575  	}
   576  	resp, err := d.Start(ctx.ExecCtx, task)
   577  	if err != nil {
   578  		t.Fatalf("err: %v", err)
   579  	}
   580  	if resp.Network != nil {
   581  		t.Fatalf("No network should be returned with --net=host but found: %#v", resp.Network)
   582  	}
   583  
   584  	failCh := make(chan error, 1)
   585  	go func() {
   586  		time.Sleep(1 * time.Second)
   587  		if err := resp.Handle.Kill(); err != nil {
   588  			failCh <- err
   589  		}
   590  	}()
   591  
   592  	select {
   593  	case err := <-failCh:
   594  		t.Fatalf("failed to kill handle: %v", err)
   595  	case <-resp.Handle.WaitCh():
   596  	case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second):
   597  		t.Fatalf("timeout")
   598  	}
   599  }
   600  
   601  func TestRktDriver_HandlerExec(t *testing.T) {
   602  	if !testutil.IsTravis() {
   603  		t.Parallel()
   604  	}
   605  	if os.Getenv("NOMAD_TEST_RKT") == "" {
   606  		t.Skip("skipping rkt tests")
   607  	}
   608  
   609  	ctestutils.RktCompatible(t)
   610  	task := &structs.Task{
   611  		Name:   "etcd",
   612  		Driver: "rkt",
   613  		Config: map[string]interface{}{
   614  			"trust_prefix": "coreos.com/etcd",
   615  			"image":        "coreos.com/etcd:v2.0.4",
   616  			"command":      "/etcd",
   617  		},
   618  		LogConfig: &structs.LogConfig{
   619  			MaxFiles:      10,
   620  			MaxFileSizeMB: 10,
   621  		},
   622  		Resources: &structs.Resources{
   623  			MemoryMB: 128,
   624  			CPU:      100,
   625  		},
   626  	}
   627  
   628  	ctx := testDriverContexts(t, task)
   629  	defer ctx.AllocDir.Destroy()
   630  	d := NewRktDriver(ctx.DriverCtx)
   631  
   632  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   633  		t.Fatalf("error in prestart: %v", err)
   634  	}
   635  	resp, err := d.Start(ctx.ExecCtx, task)
   636  	if err != nil {
   637  		t.Fatalf("err: %v", err)
   638  	}
   639  
   640  	// Give the pod a second to start
   641  	time.Sleep(time.Second)
   642  
   643  	// Exec a command that should work
   644  	out, code, err := resp.Handle.Exec(context.TODO(), "/etcd", []string{"--version"})
   645  	if err != nil {
   646  		t.Fatalf("error exec'ing etcd --version: %v", err)
   647  	}
   648  	if code != 0 {
   649  		t.Fatalf("expected `etcd --version` to succeed but exit code was: %d\n%s", code, string(out))
   650  	}
   651  	if expected := []byte("etcd version "); !bytes.HasPrefix(out, expected) {
   652  		t.Fatalf("expected output to start with %q but found:\n%q", expected, out)
   653  	}
   654  
   655  	// Exec a command that should fail
   656  	out, code, err = resp.Handle.Exec(context.TODO(), "/etcd", []string{"--kaljdshf"})
   657  	if err != nil {
   658  		t.Fatalf("error exec'ing bad command: %v", err)
   659  	}
   660  	if code == 0 {
   661  		t.Fatalf("expected `stat` to fail but exit code was: %d", code)
   662  	}
   663  	if expected := "flag provided but not defined"; !bytes.Contains(out, []byte(expected)) {
   664  		t.Fatalf("expected output to contain %q but found: %q", expected, out)
   665  	}
   666  
   667  	if err := resp.Handle.Kill(); err != nil {
   668  		t.Fatalf("error killing handle: %v", err)
   669  	}
   670  }
   671  
   672  func TestRktDriver_Remove_Error(t *testing.T) {
   673  	if !testutil.IsTravis() {
   674  		t.Parallel()
   675  	}
   676  	if os.Getenv("NOMAD_TEST_RKT") == "" {
   677  		t.Skip("skipping rkt tests")
   678  	}
   679  
   680  	ctestutils.RktCompatible(t)
   681  
   682  	// Removing a non-existent pod should return an error
   683  	if err := rktRemove("00000000-0000-0000-0000-000000000000"); err == nil {
   684  		t.Fatalf("expected an error")
   685  	}
   686  
   687  	if err := rktRemove("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"); err == nil {
   688  		t.Fatalf("expected an error")
   689  	}
   690  }