github.com/coreos/rocket@v1.30.1-0.20200224141603-171c416fac02/tests/rkt_api_service_test.go (about)

     1  // Copyright 2015 The rkt Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // +build host coreos src kvm
    16  
    17  package main
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"fmt"
    23  	"os"
    24  	"path/filepath"
    25  	"reflect"
    26  	"strings"
    27  	"syscall"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/appc/spec/schema"
    32  	"github.com/appc/spec/schema/types"
    33  	"github.com/coreos/gexpect"
    34  	"github.com/rkt/rkt/api/v1alpha"
    35  	"github.com/rkt/rkt/common"
    36  	"github.com/rkt/rkt/tests/testutils"
    37  	"golang.org/x/net/context"
    38  )
    39  
    40  func startAPIService(t *testing.T, ctx *testutils.RktRunCtx) *gexpect.ExpectSubprocess {
    41  	noRktGid := false
    42  	rktGid, err := common.LookupGid(common.RktGroup)
    43  	if err != nil {
    44  		t.Logf("no %q group, will run api service with root, ONLY DO THIS FOR TESTING!", common.RktGroup)
    45  		noRktGid = true
    46  	} else {
    47  		if err := ctx.SetupDataDir(); err != nil {
    48  			t.Fatalf("failed to setup data directory: %v", err)
    49  		}
    50  	}
    51  
    52  	uid, _ := ctx.GetUidGidRktBinOwnerNotRoot()
    53  
    54  	t.Logf("Running rkt api service")
    55  	apisvcCmd := fmt.Sprintf("%s api-service", ctx.Cmd())
    56  
    57  	if noRktGid {
    58  		return startRktAndCheckOutput(t, apisvcCmd, "API service running")
    59  	}
    60  	return startRktAsUidGidAndCheckOutput(t, apisvcCmd, "API service running", false, uid, rktGid)
    61  }
    62  
    63  func stopAPIService(t *testing.T, svc *gexpect.ExpectSubprocess) {
    64  	if err := svc.Cmd.Process.Signal(syscall.SIGINT); err != nil {
    65  		t.Fatalf("Failed to stop the api service: %v", err)
    66  	}
    67  	waitOrFail(t, svc, 0)
    68  }
    69  
    70  func checkPodState(t *testing.T, rawState string, apiState v1alpha.PodState) {
    71  	switch rawState {
    72  	case "embryo":
    73  		if apiState == v1alpha.PodState_POD_STATE_EMBRYO {
    74  			return
    75  		}
    76  	case "preparing":
    77  		if apiState == v1alpha.PodState_POD_STATE_PREPARING {
    78  			return
    79  		}
    80  	case "aborted prepare":
    81  		if apiState == v1alpha.PodState_POD_STATE_ABORTED_PREPARE {
    82  			return
    83  		}
    84  	case "running":
    85  		if apiState == v1alpha.PodState_POD_STATE_RUNNING {
    86  			return
    87  		}
    88  	case "deleting":
    89  		if apiState == v1alpha.PodState_POD_STATE_DELETING {
    90  			return
    91  		}
    92  	case "exited":
    93  		if apiState == v1alpha.PodState_POD_STATE_EXITED {
    94  			return
    95  		}
    96  	case "garbage", "exited garbage":
    97  		if apiState == v1alpha.PodState_POD_STATE_GARBAGE {
    98  			return
    99  		}
   100  	default:
   101  		t.Fatalf("Unexpected state: %v", rawState)
   102  	}
   103  	t.Errorf("Pod state returned by api-service (%q) is not equivalent to the state returned by 'rkt status' (%q)", apiState, rawState)
   104  }
   105  
   106  func checkPodApps(t *testing.T, rawPod *podInfo, apiApps []*v1alpha.App, hasAppState bool) {
   107  	rawApps := rawPod.apps
   108  	if len(rawApps) != len(apiApps) {
   109  		t.Errorf("Expected %d apps, saw %d apps returned by api service %v", len(rawApps), len(apiApps), apiApps)
   110  	}
   111  
   112  	for _, app := range apiApps {
   113  		appInfo, ok := rawApps[app.Name]
   114  		if !ok {
   115  			t.Errorf("Expected app (name: %q) in the app list", app.Name)
   116  			continue
   117  		}
   118  
   119  		appACName := types.MustACName(app.Name)
   120  		runtimeApp := rawPod.manifest.Apps.Get(*appACName)
   121  		if runtimeApp == nil {
   122  			t.Errorf("Expected app (name: %q) in the pod manifest", app.Name)
   123  		}
   124  
   125  		if hasAppState && appInfo.exitCode != int(app.ExitCode) {
   126  			t.Errorf("Expected %v, saw %v", appInfo.exitCode, app.ExitCode)
   127  		}
   128  		// Image hash in the pod manifest can be partial hash.
   129  		if !strings.HasPrefix(app.Image.Id, appInfo.image.id) {
   130  			t.Errorf("Expected partial hash of %q, saw %q", appInfo.image.id, app.Image.Id)
   131  		}
   132  
   133  		// Check app annotations.
   134  		checkAnnotations(t, runtimeApp.Annotations, app.Annotations)
   135  	}
   136  }
   137  
   138  func checkPodNetworks(t *testing.T, rawNets map[string]*networkInfo, apiNets []*v1alpha.Network) {
   139  	if len(rawNets) != len(apiNets) {
   140  		t.Errorf("Expected %d networks, saw %d networks returned by api service", len(rawNets), len(apiNets))
   141  	}
   142  
   143  	// Each network should have a unique name, so iteration over one list is enough given
   144  	// the lengths of the two lists are equal.
   145  	for _, net := range apiNets {
   146  		if netInfo, ok := rawNets[net.Name]; ok {
   147  			if netInfo.ipv4 != net.Ipv4 {
   148  				t.Errorf("Expected %q, saw %q", netInfo.ipv4, net.Ipv4)
   149  			}
   150  		} else {
   151  			t.Errorf("Expected network (name: %q, ipv4: %q) in networks", netInfo.name, netInfo.ipv4)
   152  		}
   153  	}
   154  }
   155  
   156  // Check the pod's information by 'rkt status'.
   157  func checkPod(t *testing.T, ctx *testutils.RktRunCtx, p *v1alpha.Pod, hasAppState, hasManifest bool, expectedGCTime time.Time) {
   158  	t.Logf("API Pod info: %v", p)
   159  
   160  	podInfo := getPodInfo(t, ctx, p.Id)
   161  	t.Logf("Pod info: %+v", podInfo)
   162  
   163  	if podInfo.id != p.Id {
   164  		t.Errorf("Expected %q, saw %q", podInfo.id, p.Id)
   165  	}
   166  	if podInfo.pid != int(p.Pid) {
   167  		t.Errorf("Expected %d, saw %d", podInfo.pid, p.Pid)
   168  	}
   169  	// The time accuracy returned by 'rkt status' stops at milliseconds.
   170  	accuracy := time.Millisecond.Nanoseconds()
   171  	if podInfo.createdAt/accuracy != p.CreatedAt/accuracy {
   172  		t.Errorf("Expected %d, saw %d", podInfo.createdAt, p.CreatedAt)
   173  	}
   174  	if podInfo.startedAt/accuracy != p.StartedAt/accuracy {
   175  		t.Errorf("Expected %d, saw %d", podInfo.startedAt, p.StartedAt)
   176  	}
   177  
   178  	// If expectedGCTime.IsZero() == true, then p.GcMarkedAt should also be zero.
   179  	actualTime := time.Unix(0, p.GcMarkedAt)
   180  	if !compareTime(expectedGCTime, actualTime) {
   181  		t.Errorf("API service returned an incorrect GC marked time. Got %q, Expect: %q", actualTime, expectedGCTime)
   182  	}
   183  	checkPodState(t, podInfo.state, p.State)
   184  	checkPodApps(t, podInfo, p.Apps, hasAppState)
   185  	checkPodNetworks(t, podInfo.networks, p.Networks)
   186  
   187  	expectedCgroupSuffix := ""
   188  	if podInfo.state == "running" {
   189  		machineID := fmt.Sprintf("rkt-%s", p.Id)
   190  		escapedmID := strings.Replace(machineID, "-", "\\x2d", -1)
   191  		expectedCgroupSuffix = fmt.Sprintf("/machine-%s.scope", escapedmID)
   192  	}
   193  
   194  	if !strings.HasSuffix(p.Cgroup, expectedCgroupSuffix) {
   195  		t.Errorf("Expected the cgroup suffix to have %q, but saw %q", expectedCgroupSuffix, p.Cgroup)
   196  	}
   197  
   198  	if hasManifest && podInfo.manifest.Annotations != nil {
   199  		checkAnnotations(t, podInfo.manifest.Annotations, p.Annotations)
   200  	}
   201  
   202  	msft, err := json.Marshal(podInfo.manifest)
   203  	if err != nil {
   204  		t.Errorf("Cannot marshal manifest: %v", err)
   205  	}
   206  
   207  	if hasManifest && !bytes.Equal(msft, p.Manifest) {
   208  		t.Errorf("Expected %q, saw %q", string(msft), string(p.Manifest))
   209  	} else if !hasManifest && p.Manifest != nil {
   210  		t.Errorf("Expected nil manifest")
   211  	}
   212  }
   213  
   214  func checkPodBasicsWithGCTime(t *testing.T, ctx *testutils.RktRunCtx, p *v1alpha.Pod, expectedGCTime time.Time) {
   215  	checkPod(t, ctx, p, false, false, expectedGCTime)
   216  }
   217  
   218  func checkPodBasics(t *testing.T, ctx *testutils.RktRunCtx, p *v1alpha.Pod) {
   219  	checkPod(t, ctx, p, false, false, time.Time{})
   220  }
   221  
   222  func checkPodDetails(t *testing.T, ctx *testutils.RktRunCtx, p *v1alpha.Pod) {
   223  	checkPod(t, ctx, p, true, true, time.Time{})
   224  }
   225  
   226  // Check the image's information by 'rkt image list'.
   227  func checkImage(t *testing.T, ctx *testutils.RktRunCtx, m *v1alpha.Image, hasManifest bool) {
   228  	imgInfo := getImageInfo(t, ctx, m.Id)
   229  	if imgInfo.id != m.Id {
   230  		t.Errorf("Expected %q, saw %q", imgInfo.id, m.Id)
   231  	}
   232  	if imgInfo.name != m.Name {
   233  		t.Errorf("Expected %q, saw %q", imgInfo.name, m.Name)
   234  	}
   235  	if imgInfo.version != m.Version {
   236  		t.Errorf("Expected %q, saw %q", imgInfo.version, m.Version)
   237  	}
   238  	if imgInfo.importTime != m.ImportTimestamp {
   239  		t.Errorf("Expected %q, saw %q", imgInfo.importTime, m.ImportTimestamp)
   240  	}
   241  	if imgInfo.size != m.Size {
   242  		t.Errorf("Expected size %d, saw %d", imgInfo.size, m.Size)
   243  	}
   244  
   245  	if hasManifest {
   246  		var mfst schema.ImageManifest
   247  		err := json.Unmarshal(imgInfo.manifest, &mfst)
   248  		if err != nil {
   249  			t.Fatal(err)
   250  		}
   251  		if mfst.Annotations != nil {
   252  			checkAnnotations(t, mfst.Annotations, m.Annotations)
   253  		}
   254  	}
   255  
   256  	if hasManifest && !bytes.Equal(imgInfo.manifest, m.Manifest) {
   257  		t.Errorf("Expected %q, saw %q", string(imgInfo.manifest), string(m.Manifest))
   258  	} else if !hasManifest && m.Manifest != nil {
   259  		t.Errorf("Expected nil manifest")
   260  	}
   261  }
   262  
   263  func checkAnnotations(t *testing.T, expected types.Annotations, actual []*v1alpha.KeyValue) {
   264  	if len(expected) != len(actual) {
   265  		t.Fatalf("Expected annotation counts to equal, expected %d, got %d", len(expected), len(actual))
   266  	}
   267  	for _, a := range actual {
   268  		val, ok := expected.Get(a.Key)
   269  		if !ok {
   270  			t.Fatalf("Expected annotation for key %q, got nothing", a.Key)
   271  		}
   272  		if val != a.Value {
   273  			t.Fatalf("Incorrect Annotation value, expected %q, got %q", val, a.Value)
   274  		}
   275  	}
   276  }
   277  
   278  func checkImageBasics(t *testing.T, ctx *testutils.RktRunCtx, m *v1alpha.Image) {
   279  	checkImage(t, ctx, m, false)
   280  }
   281  
   282  func checkImageDetails(t *testing.T, ctx *testutils.RktRunCtx, m *v1alpha.Image) {
   283  	checkImage(t, ctx, m, true)
   284  }
   285  
   286  func TestAPIServiceGetInfo(t *testing.T) {
   287  	ctx := testutils.NewRktRunCtx()
   288  	defer ctx.Cleanup()
   289  
   290  	svc := startAPIService(t, ctx)
   291  	defer stopAPIService(t, svc)
   292  
   293  	c, conn := newAPIClientOrFail(t, "localhost:15441")
   294  	defer conn.Close()
   295  
   296  	resp, err := c.GetInfo(context.Background(), &v1alpha.GetInfoRequest{})
   297  	if err != nil {
   298  		t.Fatalf("Unexpected error: %v", err)
   299  	}
   300  
   301  	expectedAPIVersion := "1.0.0-alpha"
   302  	if resp.Info.ApiVersion != expectedAPIVersion {
   303  		t.Errorf("Expected api version to be %q, but saw %q", expectedAPIVersion, resp.Info.ApiVersion)
   304  	}
   305  
   306  	expectedGlobalFlags := &v1alpha.GlobalFlags{
   307  		Dir:             ctx.DataDir(),
   308  		SystemConfigDir: ctx.SystemDir(),
   309  		LocalConfigDir:  ctx.LocalDir(),
   310  		UserConfigDir:   ctx.UserDir(),
   311  		InsecureFlags:   "none",
   312  	}
   313  	if !reflect.DeepEqual(resp.Info.GlobalFlags, expectedGlobalFlags) {
   314  		t.Errorf("Expected global flags to be %v, but saw %v", expectedGlobalFlags, resp.Info.GlobalFlags)
   315  	}
   316  }
   317  
   318  func NewAPIServiceListInspectPodsTest() testutils.Test {
   319  	return testutils.TestFunc(func(t *testing.T) {
   320  		ctx := testutils.NewRktRunCtx()
   321  		defer ctx.Cleanup()
   322  
   323  		svc := startAPIService(t, ctx)
   324  		defer stopAPIService(t, svc)
   325  
   326  		c, conn := newAPIClientOrFail(t, "localhost:15441")
   327  		defer conn.Close()
   328  
   329  		resp, err := c.ListPods(context.Background(), &v1alpha.ListPodsRequest{})
   330  		if err != nil {
   331  			t.Fatalf("Unexpected error: %v", err)
   332  		}
   333  
   334  		if len(resp.Pods) != 0 {
   335  			t.Errorf("Unexpected result: %v, should see zero pods", resp.Pods)
   336  		}
   337  
   338  		patches := []string{"--exec=/inspect --print-msg=HELLO_API --exit-code=0"}
   339  		imageHash, err := patchImportAndFetchHash("rkt-inspect-print.aci", patches, t, ctx)
   340  		if err != nil {
   341  			t.Fatalf("%v", err)
   342  		}
   343  		imgID, err := types.NewHash(imageHash)
   344  		if err != nil {
   345  			t.Fatalf("Cannot generate types.Hash from %v: %v", imageHash, err)
   346  		}
   347  
   348  		podManifests := []struct {
   349  			mfst             schema.PodManifest
   350  			net              string
   351  			expectedExitCode int
   352  		}{
   353  			{
   354  				// 1, Good pod.
   355  				schema.PodManifest{
   356  					ACKind:    schema.PodManifestKind,
   357  					ACVersion: schema.AppContainerVersion,
   358  					Apps: []schema.RuntimeApp{
   359  						{
   360  							Name: types.ACName("rkt-inspect"),
   361  							Image: schema.RuntimeImage{
   362  								Name: types.MustACIdentifier("coreos.com/rkt-inspect"),
   363  								ID:   *imgID,
   364  							},
   365  							Annotations: []types.Annotation{{Name: types.ACIdentifier("app-test"), Value: "app-test"}},
   366  						},
   367  					},
   368  					Annotations: []types.Annotation{
   369  						{Name: types.ACIdentifier("test"), Value: "test"},
   370  					},
   371  				},
   372  				"default",
   373  				0,
   374  			},
   375  			{
   376  				// 2, Bad pod, won't be launched correctly.
   377  				schema.PodManifest{
   378  					ACKind:    schema.PodManifestKind,
   379  					ACVersion: schema.AppContainerVersion,
   380  					Apps: []schema.RuntimeApp{
   381  						{
   382  							Name: types.ACName("rkt-inspect"),
   383  							Image: schema.RuntimeImage{
   384  								Name: types.MustACIdentifier("coreos.com/rkt-inspect"),
   385  								ID:   *imgID,
   386  							},
   387  						},
   388  					},
   389  				},
   390  				"non-existent-network",
   391  				254,
   392  			},
   393  		}
   394  
   395  		// Launch the pods.
   396  		for _, entry := range podManifests {
   397  			manifestFile := generatePodManifestFile(t, &entry.mfst)
   398  			defer os.Remove(manifestFile)
   399  
   400  			runCmd := fmt.Sprintf("%s run --net=%s --pod-manifest=%s", ctx.Cmd(), entry.net, manifestFile)
   401  			waitOrFail(t, spawnOrFail(t, runCmd), entry.expectedExitCode)
   402  		}
   403  
   404  		time.Sleep(delta)
   405  
   406  		gcCmd := fmt.Sprintf("%s gc --mark-only=true", ctx.Cmd())
   407  		waitOrFail(t, spawnOrFail(t, gcCmd), 0)
   408  
   409  		gcTime := time.Now()
   410  
   411  		// ListPods(detail=false).
   412  		resp, err = c.ListPods(context.Background(), &v1alpha.ListPodsRequest{})
   413  		if err != nil {
   414  			t.Fatalf("Unexpected error: %v", err)
   415  		}
   416  
   417  		if len(resp.Pods) != len(podManifests) {
   418  			t.Errorf("Unexpected result: %v, should see %v pods", len(resp.Pods), len(podManifests))
   419  		}
   420  
   421  		for _, p := range resp.Pods {
   422  			checkPodBasicsWithGCTime(t, ctx, p, gcTime)
   423  
   424  			// Test InspectPod().
   425  			inspectResp, err := c.InspectPod(context.Background(), &v1alpha.InspectPodRequest{Id: p.Id})
   426  			if err != nil {
   427  				t.Fatalf("Unexpected error: %v", err)
   428  			}
   429  			checkPodDetails(t, ctx, inspectResp.Pod)
   430  		}
   431  
   432  		// ListPods(detail=true).
   433  		resp, err = c.ListPods(context.Background(), &v1alpha.ListPodsRequest{Detail: true})
   434  		if err != nil {
   435  			t.Fatalf("Unexpected error: %v", err)
   436  		}
   437  
   438  		if len(resp.Pods) != len(podManifests) {
   439  			t.Errorf("Unexpected result: %v, should see %v pods", len(resp.Pods), len(podManifests))
   440  		}
   441  
   442  		for _, p := range resp.Pods {
   443  			checkPodDetails(t, ctx, p)
   444  		}
   445  
   446  		// ListPods with corrupt pod directory
   447  		// Note that we don't checkPodDetails here, the failure this is testing is
   448  		// the api server panicking, which results in a list call hanging for ages
   449  		// and then failing.
   450  		// TODO: do further validation on the partial pods returned
   451  		for _, p := range resp.Pods {
   452  			numRemoved := 0
   453  			podDir := getPodDir(t, ctx, p.Id)
   454  			filepath.Walk(filepath.Join(podDir, "appsinfo"), filepath.WalkFunc(func(path string, info os.FileInfo, err error) error {
   455  				if err != nil {
   456  					return err
   457  				}
   458  				if info.Name() == "manifest" {
   459  					os.Remove(path)
   460  					numRemoved++
   461  				}
   462  				return nil
   463  			}))
   464  			if numRemoved == 0 {
   465  				t.Fatalf("Expected to remove at least one app manifest for pod %v", p)
   466  			}
   467  		}
   468  
   469  		// ListPods(detail=true).
   470  		resp, err = c.ListPods(context.Background(), &v1alpha.ListPodsRequest{Detail: true})
   471  		if err != nil {
   472  			t.Fatalf("Unexpected error: %v", err)
   473  		}
   474  		if len(resp.Pods) != len(podManifests) {
   475  			t.Fatalf("Expected %v pods, got %v pods", len(podManifests), len(resp.Pods))
   476  		}
   477  	})
   478  }
   479  
   480  func TestAPIServiceListInspectImages(t *testing.T) {
   481  	ctx := testutils.NewRktRunCtx()
   482  	defer ctx.Cleanup()
   483  
   484  	svc := startAPIService(t, ctx)
   485  	defer stopAPIService(t, svc)
   486  
   487  	c, conn := newAPIClientOrFail(t, "localhost:15441")
   488  	defer conn.Close()
   489  
   490  	resp, err := c.ListImages(context.Background(), &v1alpha.ListImagesRequest{})
   491  	if err != nil {
   492  		t.Fatalf("Unexpected error: %v", err)
   493  	}
   494  
   495  	if len(resp.Images) != 0 {
   496  		t.Errorf("Unexpected result: %v, should see zero images", resp.Images)
   497  	}
   498  
   499  	_, err = patchImportAndFetchHash("rkt-inspect-sleep.aci", []string{"--exec=/inspect"}, t, ctx)
   500  	if err != nil {
   501  		t.Fatalf("%v", err)
   502  	}
   503  
   504  	// ListImages(detail=false).
   505  	resp, err = c.ListImages(context.Background(), &v1alpha.ListImagesRequest{})
   506  	if err != nil {
   507  		t.Fatalf("Unexpected error: %v", err)
   508  	}
   509  
   510  	if len(resp.Images) == 0 {
   511  		t.Errorf("Unexpected result: %v, should see non-zero images", resp.Images)
   512  	}
   513  
   514  	for _, m := range resp.Images {
   515  		checkImageBasics(t, ctx, m)
   516  
   517  		// Test InspectImage().
   518  		inspectResp, err := c.InspectImage(context.Background(), &v1alpha.InspectImageRequest{Id: m.Id})
   519  		if err != nil {
   520  			t.Fatalf("Unexpected error: %v", err)
   521  		}
   522  		checkImageDetails(t, ctx, inspectResp.Image)
   523  	}
   524  
   525  	// ListImages(detail=true).
   526  	resp, err = c.ListImages(context.Background(), &v1alpha.ListImagesRequest{Detail: true})
   527  	if err != nil {
   528  		t.Fatalf("Unexpected error: %v", err)
   529  	}
   530  
   531  	if len(resp.Images) == 0 {
   532  		t.Errorf("Unexpected result: %v, should see non-zero images", resp.Images)
   533  	}
   534  
   535  	for _, m := range resp.Images {
   536  		checkImageDetails(t, ctx, m)
   537  	}
   538  }
   539  
   540  func NewAPIServiceCgroupTest() testutils.Test {
   541  	return testutils.TestFunc(func(t *testing.T) {
   542  		ctx := testutils.NewRktRunCtx()
   543  		defer ctx.Cleanup()
   544  
   545  		svc := startAPIService(t, ctx)
   546  		defer stopAPIService(t, svc)
   547  
   548  		c, conn := newAPIClientOrFail(t, "localhost:15441")
   549  		defer conn.Close()
   550  
   551  		aciFileName := patchTestACI("rkt-inspect-interactive.aci", "--exec=/inspect --read-stdin")
   552  		defer os.Remove(aciFileName)
   553  
   554  		runCmd := fmt.Sprintf("%s --insecure-options=image run --interactive %s", ctx.Cmd(), aciFileName)
   555  		child := spawnOrFail(t, runCmd)
   556  
   557  		var resp *v1alpha.ListPodsResponse
   558  		var err error
   559  		done := make(chan struct{})
   560  
   561  		// Wait the pods to be running.
   562  		go func() {
   563  			for {
   564  				// ListPods(detail=false).
   565  				resp, err = c.ListPods(context.Background(), &v1alpha.ListPodsRequest{})
   566  				if err != nil {
   567  					t.Fatalf("Unexpected error: %v", err)
   568  				}
   569  
   570  				if len(resp.Pods) != 0 {
   571  					allRunning := true
   572  					for _, p := range resp.Pods {
   573  						if p.State != v1alpha.PodState_POD_STATE_RUNNING || p.Pid == -1 {
   574  							allRunning = false
   575  							break
   576  						}
   577  					}
   578  					if allRunning {
   579  						t.Logf("Pods are running")
   580  						close(done)
   581  						return
   582  					}
   583  				}
   584  				t.Logf("Pods are not in RUNNING state")
   585  				time.Sleep(time.Second)
   586  			}
   587  		}()
   588  
   589  		testutils.WaitOrTimeout(t, time.Second*60, done)
   590  
   591  		var cgroups []string
   592  		var subcgroups []string
   593  
   594  		for _, p := range resp.Pods {
   595  			checkPodBasics(t, ctx, p)
   596  
   597  			// Test InspectPod().
   598  			inspectResp, err := c.InspectPod(context.Background(), &v1alpha.InspectPodRequest{Id: p.Id})
   599  			if err != nil {
   600  				t.Fatalf("Unexpected error: %v", err)
   601  			}
   602  			checkPodDetails(t, ctx, inspectResp.Pod)
   603  			if p.Cgroup != "" {
   604  				cgroups = append(cgroups, p.Cgroup)
   605  				subcgroups = append(subcgroups, filepath.Join(p.Cgroup, "system.slice"))
   606  			}
   607  		}
   608  
   609  		// ListPods(detail=true). Filter according to the cgroup.
   610  		t.Logf("Calling ListPods with cgroup filter %v", cgroups)
   611  		resp, err = c.ListPods(context.Background(), &v1alpha.ListPodsRequest{
   612  			Detail:  true,
   613  			Filters: []*v1alpha.PodFilter{{Cgroups: cgroups}},
   614  		})
   615  		if err != nil {
   616  			t.Fatalf("Unexpected error: %v", err)
   617  		}
   618  
   619  		if len(resp.Pods) == 0 {
   620  			t.Errorf("Unexpected result: %v, should see non-zero pods", resp.Pods)
   621  		}
   622  
   623  		for _, p := range resp.Pods {
   624  			checkPodDetails(t, ctx, p)
   625  		}
   626  
   627  		t.Logf("Calling ListPods with subcgroup filter %v", subcgroups)
   628  		resp, err = c.ListPods(context.Background(), &v1alpha.ListPodsRequest{
   629  			Detail:  true,
   630  			Filters: []*v1alpha.PodFilter{{PodSubCgroups: subcgroups}},
   631  		})
   632  		if err != nil {
   633  			t.Fatalf("Unexpected error: %v", err)
   634  		}
   635  
   636  		if len(resp.Pods) == 0 {
   637  			t.Errorf("Unexpected result: %v, should see non-zero pods", resp.Pods)
   638  		}
   639  
   640  		for _, p := range resp.Pods {
   641  			checkPodDetails(t, ctx, p)
   642  		}
   643  
   644  		// Terminate the pod.
   645  		if err := child.SendLine("Good bye"); err != nil {
   646  			t.Fatalf("Failed to send message to the pod: %v", err)
   647  		}
   648  		waitOrFail(t, child, 0)
   649  
   650  		// Check that there's no cgroups returned for non-running pods.
   651  		cgroups = []string{}
   652  		resp, err = c.ListPods(context.Background(), &v1alpha.ListPodsRequest{})
   653  		for _, p := range resp.Pods {
   654  			checkPodBasics(t, ctx, p)
   655  			if p.Cgroup != "" {
   656  				cgroups = append(cgroups, p.Cgroup)
   657  			}
   658  		}
   659  		if len(cgroups) != 0 {
   660  			t.Errorf("Unexpected cgroup returned by pods: %v", cgroups)
   661  		}
   662  	})
   663  }