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