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