diff --git a/main.go b/main.go index 9e14b6ad..3049e502 100644 --- a/main.go +++ b/main.go @@ -109,6 +109,7 @@ var runtimeCommands = []cli.Command{ killCLICommand, listCLICommand, pauseCLICommand, + psCLICommand, resumeCLICommand, runCLICommand, startCLICommand, diff --git a/ps.go b/ps.go new file mode 100644 index 00000000..0a1cd549 --- /dev/null +++ b/ps.go @@ -0,0 +1,89 @@ +// Copyright (c) 2014,2015,2016 Docker, Inc. +// Copyright (c) 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + + vc "github.com/containers/virtcontainers" + "github.com/urfave/cli" +) + +var psCLICommand = cli.Command{ + Name: "ps", + Usage: "ps displays the processes running inside a container", + ArgsUsage: ` [ps options]`, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "format, f", + Value: "table", + Usage: `select one of: ` + formatOptions, + }, + }, + Action: func(context *cli.Context) error { + if context.Args().Present() == false { + return fmt.Errorf("Missing container ID, should at least provide one") + } + + var args []string + if len(context.Args()) > 1 { + // [1:] is to remove container_id: + // context.Args(): [container_id ps_arg1 ps_arg2 ...] + // args: [ps_arg1 ps_arg2 ...] + args = context.Args()[1:] + } + + return ps(context.Args().First(), context.String("format"), args) + }, + SkipArgReorder: true, +} + +func ps(containerID, format string, args []string) error { + if containerID == "" { + return fmt.Errorf("Missing container ID") + } + + // Checks the MUST and MUST NOT from OCI runtime specification + status, podID, err := getExistingContainerInfo(containerID) + if err != nil { + return err + } + + containerID = status.ID + + // container MUST be running + if status.State.State != vc.StateRunning { + return fmt.Errorf("Container %s is not running", containerID) + } + + var options vc.ProcessListOptions + + options.Args = args + if len(options.Args) == 0 { + options.Args = []string{"-ef"} + } + + options.Format = format + + msg, err := vci.ProcessListContainer(containerID, podID, options) + if err != nil { + return err + } + + fmt.Print(string(msg)) + + return nil +} diff --git a/ps_test.go b/ps_test.go new file mode 100644 index 00000000..413e7791 --- /dev/null +++ b/ps_test.go @@ -0,0 +1,134 @@ +// Copyright (c) 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "flag" + "testing" + + vc "github.com/containers/virtcontainers" + "github.com/containers/virtcontainers/pkg/oci" + "github.com/containers/virtcontainers/pkg/vcMock" + "github.com/stretchr/testify/assert" + "github.com/urfave/cli" +) + +func TestPSCLIAction(t *testing.T) { + assert := assert.New(t) + + flagSet := flag.NewFlagSet("flag", flag.ContinueOnError) + flagSet.Parse([]string{"cc-runtime"}) + + // create a new fake context + ctx := cli.NewContext(&cli.App{Metadata: map[string]interface{}{}}, flagSet, nil) + + // get Action function + actionFunc, ok := psCLICommand.Action.(func(ctx *cli.Context) error) + assert.True(ok) + + err := actionFunc(ctx) + assert.Error(err, "Missing container ID") +} + +func TestPSFailure(t *testing.T) { + assert := assert.New(t) + + pod := &vcMock.Pod{ + MockID: testContainerID, + } + + pod.MockContainers = []*vcMock.Container{ + { + MockID: pod.ID(), + MockPod: pod, + }, + } + + testingImpl.ListPodFunc = func() ([]vc.PodStatus, error) { + // return a podStatus with the container status + return []vc.PodStatus{ + { + ID: pod.ID(), + ContainersStatus: []vc.ContainerStatus{ + { + ID: pod.ID(), + Annotations: map[string]string{ + oci.ContainerTypeKey: string(vc.PodContainer), + }, + }, + }, + }, + }, nil + } + + defer func() { + testingImpl.ListPodFunc = nil + }() + + // inexistent container + err := ps("xyz123abc", "json", []string{"-ef"}) + assert.Error(err) + + // container is not running + err = ps(pod.ID(), "json", []string{"-ef"}) + assert.Error(err) +} + +func TestPSSuccessful(t *testing.T) { + assert := assert.New(t) + + pod := &vcMock.Pod{ + MockID: testContainerID, + } + + pod.MockContainers = []*vcMock.Container{ + { + MockID: pod.ID(), + MockPod: pod, + }, + } + + testingImpl.ListPodFunc = func() ([]vc.PodStatus, error) { + // return a podStatus with the container status + return []vc.PodStatus{ + { + ID: pod.ID(), + ContainersStatus: []vc.ContainerStatus{ + { + State: vc.State{ + State: vc.StateRunning, + }, + ID: pod.ID(), + Annotations: map[string]string{ + oci.ContainerTypeKey: string(vc.PodContainer), + }, + }, + }, + }, + }, nil + } + + testingImpl.ProcessListContainerFunc = func(podID, containerID string, options vc.ProcessListOptions) (vc.ProcessList, error) { + return []byte("echo,sleep,grep"), nil + } + + defer func() { + testingImpl.ListPodFunc = nil + testingImpl.ProcessListContainerFunc = nil + }() + + err := ps(pod.ID(), "json", []string{}) + assert.NoError(err) +}