diff --git a/cmd/client/container.go b/cmd/client/container.go index 9b728459..eff55969 100644 --- a/cmd/client/container.go +++ b/cmd/client/container.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "log" "time" "github.com/urfave/cli" @@ -178,6 +179,21 @@ var listContainersCommand = cli.Command{ Name: "quiet", Usage: "list only container IDs", }, + cli.StringFlag{ + Name: "id", + Value: "", + Usage: "filter by container id", + }, + cli.StringFlag{ + Name: "pod", + Value: "", + Usage: "filter by container pod id", + }, + cli.StringFlag{ + Name: "state", + Value: "", + Usage: "filter by container state", + }, }, Action: func(context *cli.Context) error { // Set up a connection to the server. @@ -188,7 +204,7 @@ var listContainersCommand = cli.Command{ defer conn.Close() client := pb.NewRuntimeServiceClient(conn) - err = ListContainers(client, context.Bool("quiet")) + err = ListContainers(client, context.Bool("quiet"), context.String("id"), context.String("pod"), context.String("state")) if err != nil { return fmt.Errorf("listing containers failed: %v", err) } @@ -304,8 +320,33 @@ func ContainerStatus(client pb.RuntimeServiceClient, ID string) error { // ListContainers sends a ListContainerRequest to the server, and parses // the returned ListContainerResponse. -func ListContainers(client pb.RuntimeServiceClient, quiet bool) error { - r, err := client.ListContainers(context.Background(), &pb.ListContainersRequest{}) +func ListContainers(client pb.RuntimeServiceClient, quiet bool, id string, podID string, state string) error { + filter := &pb.ContainerFilter{} + if id != "" { + filter.Id = &id + } + if podID != "" { + filter.PodSandboxId = &podID + } + if state != "" { + st := pb.ContainerState_UNKNOWN + switch state { + case "created": + st = pb.ContainerState_CREATED + filter.State = &st + case "running": + st = pb.ContainerState_RUNNING + filter.State = &st + case "stopped": + st = pb.ContainerState_EXITED + filter.State = &st + default: + log.Fatalf("--state should be one of created, running or stopped") + } + } + r, err := client.ListContainers(context.Background(), &pb.ListContainersRequest{ + Filter: filter, + }) if err != nil { return err } diff --git a/server/container.go b/server/container.go index 08ed4b06..da8da6b5 100644 --- a/server/container.go +++ b/server/container.go @@ -411,10 +411,54 @@ func (s *Server) RemoveContainer(ctx context.Context, req *pb.RemoveContainerReq return &pb.RemoveContainerResponse{}, nil } +// filterContainer returns whether passed container matches filtering criteria +func filterContainer(c *pb.Container, filter *pb.ContainerFilter) bool { + if filter != nil { + if filter.State != nil { + if *c.State != *filter.State { + return false + } + } + // TODO(mrunalp): Add support for label filtering + } + return true +} + // ListContainers lists all containers by filters. func (s *Server) ListContainers(ctx context.Context, req *pb.ListContainersRequest) (*pb.ListContainersResponse, error) { var ctrs []*pb.Container - for _, ctr := range s.state.containers.List() { + filter := req.Filter + ctrList := s.state.containers.List() + + // Filter using container id and pod id first. + if filter != nil { + if filter.Id != nil { + c := s.state.containers.Get(*filter.Id) + if c != nil { + if filter.PodSandboxId != nil { + if c.Sandbox() == *filter.PodSandboxId { + ctrList = []*oci.Container{c} + } else { + ctrList = []*oci.Container{} + } + + } else { + ctrList = []*oci.Container{c} + } + } + } else { + if filter.PodSandboxId != nil { + pod := s.state.sandboxes[*filter.PodSandboxId] + if pod == nil { + ctrList = []*oci.Container{} + } else { + ctrList = pod.containers.List() + } + } + } + } + + for _, ctr := range ctrList { if err := s.runtime.UpdateStatus(ctr); err != nil { return nil, err } @@ -441,7 +485,10 @@ func (s *Server) ListContainers(ctx context.Context, req *pb.ListContainersReque } c.State = &rState - ctrs = append(ctrs, c) + // Filter by other criteria such as state and labels. + if filterContainer(c, req.Filter) { + ctrs = append(ctrs, c) + } } return &pb.ListContainersResponse{ diff --git a/test/ctr.bats b/test/ctr.bats index de300682..90444032 100644 --- a/test/ctr.bats +++ b/test/ctr.bats @@ -142,3 +142,100 @@ function teardown() { stop_ocid } +@test "ctr list filtering" { + # this test requires docker, thus it can't yet be run in a container + if [ "$TRAVIS" = "true" ]; then # instead of $TRAVIS, add a function is_containerized to skip here + skip "cannot yet run this test in a container, use sudo make localintegration" + fi + + start_ocid + run ocic pod create --config "$TESTDATA"/sandbox_config.json --name pod1 + echo "$output" + [ "$status" -eq 0 ] + pod1_id="$output" + run ocic ctr create --config "$TESTDATA"/container_redis.json --pod "$pod1_id" + echo "$output" + [ "$status" -eq 0 ] + ctr1_id="$output" + run ocic ctr start --id "$ctr1_id" + echo "$output" + [ "$status" -eq 0 ] + run ocic pod create --config "$TESTDATA"/sandbox_config.json --name pod2 + echo "$output" + [ "$status" -eq 0 ] + pod2_id="$output" + run ocic ctr create --config "$TESTDATA"/container_redis.json --pod "$pod2_id" + echo "$output" + [ "$status" -eq 0 ] + ctr2_id="$output" + run ocic pod create --config "$TESTDATA"/sandbox_config.json --name pod3 + echo "$output" + [ "$status" -eq 0 ] + pod3_id="$output" + run ocic ctr create --config "$TESTDATA"/container_redis.json --pod "$pod3_id" + echo "$output" + [ "$status" -eq 0 ] + ctr3_id="$output" + run ocic ctr start --id "$ctr3_id" + echo "$output" + [ "$status" -eq 0 ] + run ocic ctr stop --id "$ctr3_id" + echo "$output" + [ "$status" -eq 0 ] + run ocic ctr list --id "$ctr1_id" --quiet + echo "$output" + [ "$status" -eq 0 ] + [[ "$output" != "" ]] + [[ "$output" =~ "$ctr1_id" ]] + run ocic ctr list --id "$ctr2_id" --pod "$pod2_id" --quiet + echo "$output" + [ "$status" -eq 0 ] + [[ "$output" != "" ]] + [[ "$output" =~ "$ctr2_id" ]] + run ocic ctr list --id "$ctr2_id" --pod "$pod3_id" --quiet + echo "$output" + [ "$status" -eq 0 ] + [[ "$output" == "" ]] + run ocic ctr list --state created --quiet + echo "$output" + [ "$status" -eq 0 ] + [[ "$output" != "" ]] + [[ "$output" =~ "$ctr2_id" ]] + run ocic ctr list --state running --quiet + echo "$output" + [ "$status" -eq 0 ] + [[ "$output" != "" ]] + [[ "$output" =~ "$ctr1_id" ]] + run ocic ctr list --state stopped --quiet + echo "$output" + [ "$status" -eq 0 ] + [[ "$output" != "" ]] + [[ "$output" =~ "$ctr3_id" ]] + run ocic ctr list --pod "$pod1_id" --quiet + echo "$output" + [ "$status" -eq 0 ] + [[ "$output" != "" ]] + [[ "$output" =~ "$ctr1_id" ]] + run ocic ctr list --pod "$pod2_id" --quiet + echo "$output" + [ "$status" -eq 0 ] + [[ "$output" != "" ]] + [[ "$output" =~ "$ctr2_id" ]] + run ocic ctr list --pod "$pod3_id" --quiet + echo "$output" + [ "$status" -eq 0 ] + [[ "$output" != "" ]] + [[ "$output" =~ "$ctr3_id" ]] + run ocic pod remove --id "$pod1_id" + echo "$output" + [ "$status" -eq 0 ] + run ocic pod remove --id "$pod2_id" + echo "$output" + [ "$status" -eq 0 ] + run ocic pod remove --id "$pod3_id" + echo "$output" + [ "$status" -eq 0 ] + cleanup_ctrs + cleanup_pods + stop_ocid +}