diff options
Diffstat (limited to 'cmd/podman')
-rw-r--r-- | cmd/podman/build.go | 5 | ||||
-rw-r--r-- | cmd/podman/commit.go | 5 | ||||
-rw-r--r-- | cmd/podman/login.go | 18 | ||||
-rw-r--r-- | cmd/podman/logout.go | 12 | ||||
-rw-r--r-- | cmd/podman/logs.go | 2 | ||||
-rw-r--r-- | cmd/podman/pod_create.go | 20 | ||||
-rw-r--r-- | cmd/podman/ps.go | 10 | ||||
-rw-r--r-- | cmd/podman/service.go | 112 | ||||
-rw-r--r-- | cmd/podman/shared/container.go | 8 | ||||
-rw-r--r-- | cmd/podman/shared/create.go | 5 | ||||
-rw-r--r-- | cmd/podman/shared/create_cli.go | 11 | ||||
-rw-r--r-- | cmd/podman/shared/create_cli_test.go | 51 | ||||
-rw-r--r-- | cmd/podman/shared/parse/parse.go | 28 | ||||
-rw-r--r-- | cmd/podman/shared/parse/parse_test.go | 53 | ||||
-rw-r--r-- | cmd/podman/tree.go | 97 | ||||
-rw-r--r-- | cmd/podman/varlink/io.podman.varlink | 10 | ||||
-rw-r--r-- | cmd/podman/volume_create.go | 6 |
17 files changed, 225 insertions, 228 deletions
diff --git a/cmd/podman/build.go b/cmd/podman/build.go index 1fcb98a0e..12aedac37 100644 --- a/cmd/podman/build.go +++ b/cmd/podman/build.go @@ -84,7 +84,10 @@ func init() { } flag.DefValue = "true" - fromAndBugFlags := buildahcli.GetFromAndBudFlags(&fromAndBudValues, &userNSValues, &namespaceValues) + fromAndBugFlags, err := buildahcli.GetFromAndBudFlags(&fromAndBudValues, &userNSValues, &namespaceValues) + if err != nil { + logrus.Errorf("failed to setup podman build flags: %v", err) + } flags.AddFlagSet(&budFlags) flags.AddFlagSet(&fromAndBugFlags) diff --git a/cmd/podman/commit.go b/cmd/podman/commit.go index b4d249c66..7c35a4832 100644 --- a/cmd/podman/commit.go +++ b/cmd/podman/commit.go @@ -15,7 +15,7 @@ var ( commitDescription = `Create an image from a container's changes. Optionally tag the image created, set the author with the --author flag, set the commit message with the --message flag, and make changes to the instructions with the --change flag.` _commitCommand = &cobra.Command{ - Use: "commit [flags] CONTAINER IMAGE", + Use: "commit [flags] CONTAINER [IMAGE]", Short: "Create new image based on the changed container", Long: commitDescription, RunE: func(cmd *cobra.Command, args []string) error { @@ -26,7 +26,8 @@ var ( }, Example: `podman commit -q --message "committing container to image" reverent_golick image-committed podman commit -q --author "firstName lastName" reverent_golick image-committed - podman commit -q --pause=false containerID image-committed`, + podman commit -q --pause=false containerID image-committed + podman commit containerID`, } // ChangeCmds is the list of valid Changes commands to passed to the Commit call diff --git a/cmd/podman/login.go b/cmd/podman/login.go index 369e0da16..e09117833 100644 --- a/cmd/podman/login.go +++ b/cmd/podman/login.go @@ -12,6 +12,7 @@ import ( "github.com/containers/image/v5/types" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/registries" "github.com/docker/docker-credential-helpers/credentials" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -67,10 +68,23 @@ func loginCmd(c *cliconfig.LoginValues) error { if len(args) > 1 { return errors.Errorf("too many arguments, login takes only 1 argument") } + var server string if len(args) == 0 { - return errors.Errorf("please specify a registry to login to") + registriesFromFile, err := registries.GetRegistries() + if err != nil || len(registriesFromFile) == 0 { + return errors.Errorf("please specify a registry to login to") + } + + server = registriesFromFile[0] + logrus.Debugf("registry not specified, default to the first registry %q from registries.conf", server) + + } else { + server = registryFromFullName(scrubServer(args[0])) + } + + if c.Flag("password").Changed { + fmt.Fprintf(os.Stderr, "WARNING! Using --password via the cli is insecure. Please consider using --password-stdin\n") } - server := registryFromFullName(scrubServer(args[0])) sc := image.GetSystemContext("", c.Authfile, false) if c.Flag("tls-verify").Changed { diff --git a/cmd/podman/logout.go b/cmd/podman/logout.go index 4a113b1d0..dec6822cf 100644 --- a/cmd/podman/logout.go +++ b/cmd/podman/logout.go @@ -8,7 +8,9 @@ import ( "github.com/containers/image/v5/pkg/docker/config" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared" + "github.com/containers/libpod/pkg/registries" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -51,10 +53,16 @@ func logoutCmd(c *cliconfig.LogoutValues) error { if len(args) > 1 { return errors.Errorf("too many arguments, logout takes at most 1 argument") } + var server string if len(args) == 0 && !c.All { - return errors.Errorf("registry must be given") + registriesFromFile, err := registries.GetRegistries() + if err != nil || len(registriesFromFile) == 0 { + return errors.Errorf("no registries found in registries.conf, a registry must be provided") + } + + server = registriesFromFile[0] + logrus.Debugf("registry not specified, default to the first registry %q from registries.conf", server) } - var server string if len(args) == 1 { server = scrubServer(args[0]) } diff --git a/cmd/podman/logs.go b/cmd/podman/logs.go index a2594b5bf..ebc53ddf8 100644 --- a/cmd/podman/logs.go +++ b/cmd/podman/logs.go @@ -15,7 +15,7 @@ var ( logsCommand cliconfig.LogsValues logsDescription = `Retrieves logs for one or more containers. - This does not guarantee execution order when combined with podman run (i.e. your run may not have generated any logs at the time you execute podman logs. + This does not guarantee execution order when combined with podman run (i.e. your run may not have generated any logs at the time you execute podman logs). ` _logsCommand = &cobra.Command{ Use: "logs [flags] CONTAINER [CONTAINER...]", diff --git a/cmd/podman/pod_create.go b/cmd/podman/pod_create.go index cee6476ea..810f62f02 100644 --- a/cmd/podman/pod_create.go +++ b/cmd/podman/pod_create.go @@ -6,6 +6,7 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared" + "github.com/containers/libpod/cmd/podman/shared/parse" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/adapter" "github.com/containers/libpod/pkg/errorhandling" @@ -44,19 +45,7 @@ func init() { podCreateCommand.SetUsageTemplate(UsageTemplate()) flags := podCreateCommand.Flags() flags.SetInterspersed(false) - // When we are ready to add the network options to the create commmand, we need to uncomment - // the following - - //flags.AddFlagSet(getNetFlags()) - - // Once this is uncommented, then the publish option below needs to be removed because it - // conflicts with the publish in getNetFlags. Upon removal, the c.Publish will not work - // anymore and needs to be cleaned up. I suggest starting with removing the Publish attribute - // from PodCreateValues structure. Running make should then expose all areas that need to be - // addressed. To get the value of publish (and other flags in getNetFlags, use the syntax: - // c.<type>("<flag_name") or c.Bool("publish") - // Remember to do this safely by checking len, etc. - + flags.AddFlagSet(getNetFlags()) flags.StringVar(&podCreateCommand.CgroupParent, "cgroup-parent", "", "Set parent cgroup for the pod") flags.BoolVar(&podCreateCommand.Infra, "infra", true, "Create an infra container associated with the pod to share namespaces with") flags.StringVar(&podCreateCommand.InfraImage, "infra-image", define.DefaultInfraImage, "The image of the infra container to associate with the pod") @@ -66,7 +55,6 @@ func init() { flags.StringVarP(&podCreateCommand.Name, "name", "n", "", "Assign a name to the pod") flags.StringVarP(&podCreateCommand.Hostname, "hostname", "", "", "Set a hostname to the pod") flags.StringVar(&podCreateCommand.PodIDFile, "pod-id-file", "", "Write the pod ID to the file") - flags.StringSliceVarP(&podCreateCommand.Publish, "publish", "p", []string{}, "Publish a container's port, or a range of ports, to the host (default [])") flags.StringVar(&podCreateCommand.Share, "share", shared.DefaultKernelNamespaces, "A comma delimited list of kernel namespaces the pod will share") } @@ -82,7 +70,7 @@ func podCreateCmd(c *cliconfig.PodCreateValues) error { } defer runtime.DeferredShutdown(false) - if len(c.Publish) > 0 { + if len(c.StringSlice("publish")) > 0 { if !c.Infra { return errors.Errorf("you must have an infra container to publish port bindings to the host") } @@ -103,7 +91,7 @@ func podCreateCmd(c *cliconfig.PodCreateValues) error { defer errorhandling.SyncQuiet(podIdFile) } - labels, err := shared.GetAllLabels(c.LabelFile, c.Labels) + labels, err := parse.GetAllLabels(c.LabelFile, c.Labels) if err != nil { return errors.Wrapf(err, "unable to process labels") } diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index d93ccc24c..accd5b51a 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -205,9 +205,15 @@ func checkFlagsPassed(c *cliconfig.PsValues) error { if c.Last >= 0 && c.Latest { return errors.Errorf("last and latest are mutually exclusive") } - // Filter forces all + // Filter on status forces all if len(c.Filter) > 0 { - c.All = true + for _, filter := range c.Filter { + splitFilter := strings.SplitN(filter, "=", 2) + if strings.ToLower(splitFilter[0]) == "status" { + c.All = true + break + } + } } // Quiet conflicts with size and namespace and is overridden by a Go // template. diff --git a/cmd/podman/service.go b/cmd/podman/service.go index 4978b5d51..3e0ff927f 100644 --- a/cmd/podman/service.go +++ b/cmd/podman/service.go @@ -17,6 +17,7 @@ import ( "github.com/containers/libpod/pkg/adapter" api "github.com/containers/libpod/pkg/api/server" "github.com/containers/libpod/pkg/rootless" + "github.com/containers/libpod/pkg/systemd" "github.com/containers/libpod/pkg/util" "github.com/containers/libpod/pkg/varlinkapi" "github.com/containers/libpod/version" @@ -50,21 +51,52 @@ func init() { serviceCommand.SetHelpTemplate(HelpTemplate()) serviceCommand.SetUsageTemplate(UsageTemplate()) flags := serviceCommand.Flags() - flags.Int64VarP(&serviceCommand.Timeout, "timeout", "t", 1000, "Time until the service session expires in milliseconds. Use 0 to disable the timeout") + flags.Int64VarP(&serviceCommand.Timeout, "timeout", "t", 5, "Time until the service session expires in seconds. Use 0 to disable the timeout") flags.BoolVar(&serviceCommand.Varlink, "varlink", false, "Use legacy varlink service instead of REST") } func serviceCmd(c *cliconfig.ServiceValues) error { - // For V2, default to the REST socket - apiURI := adapter.DefaultAPIAddress + apiURI, err := resolveApiURI(c) + if err != nil { + return err + } + + // Create a single runtime api consumption + runtime, err := libpodruntime.GetRuntimeDisableFDs(getContext(), &c.PodmanCommand) + if err != nil { + return errors.Wrapf(err, "error creating libpod runtime") + } + defer func() { + if err := runtime.Shutdown(false); err != nil { + fmt.Fprintf(os.Stderr, "Failed to shutdown libpod runtime: %v", err) + } + }() + + timeout := time.Duration(c.Timeout) * time.Second if c.Varlink { - apiURI = adapter.DefaultVarlinkAddress + return runVarlink(runtime, apiURI, timeout, c) } + return runREST(runtime, apiURI, timeout) +} + +func resolveApiURI(c *cliconfig.ServiceValues) (string, error) { + var apiURI string - if rootless.IsRootless() { + // When determining _*THE*_ listening endpoint -- + // 1) User input wins always + // 2) systemd socket activation + // 3) rootless honors XDG_RUNTIME_DIR + // 4) if varlink -- adapter.DefaultVarlinkAddress + // 5) lastly adapter.DefaultAPIAddress + + if len(c.InputArgs) > 0 { + apiURI = c.InputArgs[0] + } else if ok := systemd.SocketActivated(); ok { + apiURI = "" + } else if rootless.IsRootless() { xdg, err := util.GetRuntimeDir() if err != nil { - return err + return "", err } socketName := "podman.sock" if c.Varlink { @@ -74,53 +106,59 @@ func serviceCmd(c *cliconfig.ServiceValues) error { if _, err := os.Stat(filepath.Dir(socketDir)); err != nil { if os.IsNotExist(err) { if err := os.Mkdir(filepath.Dir(socketDir), 0755); err != nil { - return err + return "", err } } else { - return err + return "", err } } - apiURI = fmt.Sprintf("unix:%s", socketDir) - } - - if len(c.InputArgs) > 0 { - apiURI = c.InputArgs[0] + apiURI = "unix:" + socketDir + } else if c.Varlink { + apiURI = adapter.DefaultVarlinkAddress + } else { + // For V2, default to the REST socket + apiURI = adapter.DefaultAPIAddress } - logrus.Infof("using API endpoint: %s", apiURI) - - // Create a single runtime api consumption - runtime, err := libpodruntime.GetRuntimeDisableFDs(getContext(), &c.PodmanCommand) - if err != nil { - return errors.Wrapf(err, "error creating libpod runtime") + if "" == apiURI { + logrus.Info("using systemd socket activation to determine API endpoint") + } else { + logrus.Infof("using API endpoint: %s", apiURI) } - defer runtime.DeferredShutdown(false) - - timeout := time.Duration(c.Timeout) * time.Millisecond - if c.Varlink { - return runVarlink(runtime, apiURI, timeout, c) - } - return runREST(runtime, apiURI, timeout) + return apiURI, nil } func runREST(r *libpod.Runtime, uri string, timeout time.Duration) error { logrus.Warn("This function is EXPERIMENTAL") fmt.Println("This function is EXPERIMENTAL.") - fields := strings.Split(uri, ":") - if len(fields) == 1 { - return errors.Errorf("%s is an invalid socket destination", uri) - } - address := strings.Join(fields[1:], ":") - l, err := net.Listen(fields[0], address) - if err != nil { - return errors.Wrapf(err, "unable to create socket %s", uri) + + var listener *net.Listener + if uri != "" { + fields := strings.Split(uri, ":") + if len(fields) == 1 { + return errors.Errorf("%s is an invalid socket destination", uri) + } + address := strings.Join(fields[1:], ":") + l, err := net.Listen(fields[0], address) + if err != nil { + return errors.Wrapf(err, "unable to create socket %s", uri) + } + defer l.Close() + listener = &l } - defer l.Close() - server, err := api.NewServerWithSettings(r, timeout, &l) + server, err := api.NewServerWithSettings(r, timeout, listener) if err != nil { return err } - return server.Serve() + defer func() { + if err := server.Shutdown(); err != nil { + fmt.Fprintf(os.Stderr, "Error when stopping service: %s", err) + } + }() + + err = server.Serve() + logrus.Debugf("%d/%d Active connections/Total connections\n", server.ActiveConnections, server.TotalConnections) + return err } func runVarlink(r *libpod.Runtime, uri string, timeout time.Duration, c *cliconfig.ServiceValues) error { diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go index ff3846e70..b5a1e7104 100644 --- a/cmd/podman/shared/container.go +++ b/cmd/podman/shared/container.go @@ -30,6 +30,7 @@ import ( const ( cidTruncLength = 12 podTruncLength = 12 + iidTruncLength = 12 cmdTruncLength = 17 ) @@ -66,6 +67,7 @@ type BatchContainerStruct struct { type PsContainerOutput struct { ID string Image string + ImageID string Command string Created string Ports string @@ -203,7 +205,7 @@ func NewBatchContainer(r *libpod.Runtime, ctr *libpod.Container, opts PsOptions) status = "Error" } - _, imageName := ctr.Image() + imageID, imageName := ctr.Image() cid := ctr.ID() podID := ctr.PodID() if !opts.NoTrunc { @@ -214,6 +216,9 @@ func NewBatchContainer(r *libpod.Runtime, ctr *libpod.Container, opts PsOptions) if len(command) > cmdTruncLength { command = command[0:cmdTruncLength] + "..." } + if len(imageID) > iidTruncLength { + imageID = imageID[0:iidTruncLength] + } } ports, err := ctr.PortMappings() @@ -223,6 +228,7 @@ func NewBatchContainer(r *libpod.Runtime, ctr *libpod.Container, opts PsOptions) pso.ID = cid pso.Image = imageName + pso.ImageID = imageID pso.Command = command pso.Created = created pso.Ports = portsToString(ports) diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go index be5adcccb..5b244699c 100644 --- a/cmd/podman/shared/create.go +++ b/cmd/podman/shared/create.go @@ -488,7 +488,7 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. } // LABEL VARIABLES - labels, err := GetAllLabels(c.StringSlice("label-file"), c.StringArray("label")) + labels, err := parse.GetAllLabels(c.StringSlice("label-file"), c.StringArray("label")) if err != nil { return nil, errors.Wrapf(err, "unable to process labels") } @@ -701,9 +701,6 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. Sysctl: sysctl, } - if err := secConfig.SetLabelOpts(runtime, pid, ipc); err != nil { - return nil, err - } if err := secConfig.SetSecurityOpts(runtime, c.StringArray("security-opt")); err != nil { return nil, err } diff --git a/cmd/podman/shared/create_cli.go b/cmd/podman/shared/create_cli.go index 00b83906d..10e27350b 100644 --- a/cmd/podman/shared/create_cli.go +++ b/cmd/podman/shared/create_cli.go @@ -4,7 +4,6 @@ import ( "fmt" "strings" - "github.com/containers/libpod/cmd/podman/shared/parse" "github.com/containers/libpod/pkg/cgroups" cc "github.com/containers/libpod/pkg/spec" "github.com/containers/libpod/pkg/sysinfo" @@ -12,16 +11,6 @@ import ( "github.com/sirupsen/logrus" ) -// GetAllLabels ... -func GetAllLabels(labelFile, inputLabels []string) (map[string]string, error) { - labels := make(map[string]string) - labelErr := parse.ReadKVStrings(labels, labelFile, inputLabels) - if labelErr != nil { - return labels, errors.Wrapf(labelErr, "unable to process labels from --label and label-file") - } - return labels, nil -} - // validateSysctl validates a sysctl and returns it. func validateSysctl(strSlice []string) (map[string]string, error) { sysctl := make(map[string]string) diff --git a/cmd/podman/shared/create_cli_test.go b/cmd/podman/shared/create_cli_test.go index fea1a2390..a045962cb 100644 --- a/cmd/podman/shared/create_cli_test.go +++ b/cmd/podman/shared/create_cli_test.go @@ -1,33 +1,11 @@ package shared import ( - "io/ioutil" - "os" "testing" "github.com/stretchr/testify/assert" ) -var ( - Var1 = []string{"ONE=1", "TWO=2"} -) - -func createTmpFile(content []byte) (string, error) { - tmpfile, err := ioutil.TempFile(os.TempDir(), "unittest") - if err != nil { - return "", err - } - - if _, err := tmpfile.Write(content); err != nil { - return "", err - - } - if err := tmpfile.Close(); err != nil { - return "", err - } - return tmpfile.Name(), nil -} - func TestValidateSysctl(t *testing.T) { strSlice := []string{"net.core.test1=4", "kernel.msgmax=2"} result, _ := validateSysctl(strSlice) @@ -39,32 +17,3 @@ func TestValidateSysctlBadSysctl(t *testing.T) { _, err := validateSysctl(strSlice) assert.Error(t, err) } - -func TestGetAllLabels(t *testing.T) { - fileLabels := []string{} - labels, _ := GetAllLabels(fileLabels, Var1) - assert.Equal(t, len(labels), 2) -} - -func TestGetAllLabelsBadKeyValue(t *testing.T) { - inLabels := []string{"=badValue", "="} - fileLabels := []string{} - _, err := GetAllLabels(fileLabels, inLabels) - assert.Error(t, err, assert.AnError) -} - -func TestGetAllLabelsBadLabelFile(t *testing.T) { - fileLabels := []string{"/foobar5001/be"} - _, err := GetAllLabels(fileLabels, Var1) - assert.Error(t, err, assert.AnError) -} - -func TestGetAllLabelsFile(t *testing.T) { - content := []byte("THREE=3") - tFile, err := createTmpFile(content) - defer os.Remove(tFile) - assert.NoError(t, err) - fileLabels := []string{tFile} - result, _ := GetAllLabels(fileLabels, Var1) - assert.Equal(t, len(result), 3) -} diff --git a/cmd/podman/shared/parse/parse.go b/cmd/podman/shared/parse/parse.go index 3a75ff7a8..79449029d 100644 --- a/cmd/podman/shared/parse/parse.go +++ b/cmd/podman/shared/parse/parse.go @@ -79,6 +79,34 @@ func ValidateDomain(val string) (string, error) { return "", fmt.Errorf("%s is not a valid domain", val) } +// GetAllLabels retrieves all labels given a potential label file and a number +// of labels provided from the command line. +func GetAllLabels(labelFile, inputLabels []string) (map[string]string, error) { + labels := make(map[string]string) + for _, file := range labelFile { + // Use of parseEnvFile still seems safe, as it's missing the + // extra parsing logic of parseEnv. + // There's an argument that we SHOULD be doing that parsing for + // all environment variables, even those sourced from files, but + // that would require a substantial rework. + if err := parseEnvFile(labels, file); err != nil { + return nil, err + } + } + for _, label := range inputLabels { + split := strings.SplitN(label, "=", 2) + if split[0] == "" { + return nil, errors.Errorf("invalid label format: %q", label) + } + value := "" + if len(split) > 1 { + value = split[1] + } + labels[split[0]] = value + } + return labels, nil +} + // reads a file of line terminated key=value pairs, and overrides any keys // present in the file with additional pairs specified in the override parameter // for env-file and labels-file flags diff --git a/cmd/podman/shared/parse/parse_test.go b/cmd/podman/shared/parse/parse_test.go index 1359076a0..a6ddc2be9 100644 --- a/cmd/podman/shared/parse/parse_test.go +++ b/cmd/podman/shared/parse/parse_test.go @@ -4,9 +4,33 @@ package parse import ( + "io/ioutil" + "os" "testing" + + "github.com/stretchr/testify/assert" +) + +var ( + Var1 = []string{"ONE=1", "TWO=2"} ) +func createTmpFile(content []byte) (string, error) { + tmpfile, err := ioutil.TempFile(os.TempDir(), "unittest") + if err != nil { + return "", err + } + + if _, err := tmpfile.Write(content); err != nil { + return "", err + + } + if err := tmpfile.Close(); err != nil { + return "", err + } + return tmpfile.Name(), nil +} + func TestValidateExtraHost(t *testing.T) { type args struct { val string @@ -97,3 +121,32 @@ func TestValidateFileName(t *testing.T) { }) } } + +func TestGetAllLabels(t *testing.T) { + fileLabels := []string{} + labels, _ := GetAllLabels(fileLabels, Var1) + assert.Equal(t, len(labels), 2) +} + +func TestGetAllLabelsBadKeyValue(t *testing.T) { + inLabels := []string{"=badValue", "="} + fileLabels := []string{} + _, err := GetAllLabels(fileLabels, inLabels) + assert.Error(t, err, assert.AnError) +} + +func TestGetAllLabelsBadLabelFile(t *testing.T) { + fileLabels := []string{"/foobar5001/be"} + _, err := GetAllLabels(fileLabels, Var1) + assert.Error(t, err, assert.AnError) +} + +func TestGetAllLabelsFile(t *testing.T) { + content := []byte("THREE=3") + tFile, err := createTmpFile(content) + defer os.Remove(tFile) + assert.NoError(t, err) + fileLabels := []string{tFile} + result, _ := GetAllLabels(fileLabels, Var1) + assert.Equal(t, len(result), 3) +} diff --git a/cmd/podman/tree.go b/cmd/podman/tree.go index 69b42639d..28c770f0c 100644 --- a/cmd/podman/tree.go +++ b/cmd/podman/tree.go @@ -1,23 +1,14 @@ package main import ( - "context" "fmt" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/adapter" - "github.com/docker/go-units" "github.com/pkg/errors" "github.com/spf13/cobra" ) -const ( - middleItem = "├── " - continueItem = "│ " - lastItem = "└── " -) - var ( treeCommand cliconfig.TreeValues @@ -56,95 +47,11 @@ func treeCmd(c *cliconfig.TreeValues) error { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.DeferredShutdown(false) - imageInfo, layerInfoMap, img, err := runtime.Tree(c.InputArgs[0]) - if err != nil { - return err - } - return printTree(imageInfo, layerInfoMap, img, c.WhatRequires) -} -func printTree(imageInfo *image.InfoImage, layerInfoMap map[string]*image.LayerInfo, img *adapter.ContainerImage, whatRequires bool) error { - size, err := img.Size(context.Background()) + tree, err := runtime.ImageTree(c.InputArgs[0], c.WhatRequires) if err != nil { return err } - - fmt.Printf("Image ID: %s\n", imageInfo.ID[:12]) - fmt.Printf("Tags:\t %s\n", imageInfo.Tags) - fmt.Printf("Size:\t %v\n", units.HumanSizeWithPrecision(float64(*size), 4)) - if img.TopLayer() != "" { - fmt.Printf("Image Layers\n") - } else { - fmt.Printf("No Image Layers\n") - } - - if !whatRequires { - // fill imageInfo with layers associated with image. - // the layers will be filled such that - // (Start)RootLayer->...intermediate Parent Layer(s)-> TopLayer(End) - // Build output from imageInfo into buffer - printImageHierarchy(imageInfo) - - } else { - // fill imageInfo with layers associated with image. - // the layers will be filled such that - // (Start)TopLayer->...intermediate Child Layer(s)-> Child TopLayer(End) - // (Forks)... intermediate Child Layer(s) -> Child Top Layer(End) - return printImageChildren(layerInfoMap, img.TopLayer(), "", true) - } - return nil -} - -// Stores all children layers which are created using given Image. -// Layers are stored as follows -// (Start)TopLayer->...intermediate Child Layer(s)-> Child TopLayer(End) -// (Forks)... intermediate Child Layer(s) -> Child Top Layer(End) -func printImageChildren(layerMap map[string]*image.LayerInfo, layerID string, prefix string, last bool) error { - if layerID == "" { - return nil - } - ll, ok := layerMap[layerID] - if !ok { - return fmt.Errorf("lookup error: layerid %s, not found", layerID) - } - fmt.Print(prefix) - - //initialize intend with middleItem to reduce middleItem checks. - intend := middleItem - if !last { - // add continueItem i.e. '|' for next iteration prefix - prefix += continueItem - } else if len(ll.ChildID) > 1 || len(ll.ChildID) == 0 { - // The above condition ensure, alignment happens for node, which has more then 1 children. - // If node is last in printing hierarchy, it should not be printed as middleItem i.e. ├── - intend = lastItem - prefix += " " - } - - var tags string - if len(ll.RepoTags) > 0 { - tags = fmt.Sprintf(" Top Layer of: %s", ll.RepoTags) - } - fmt.Printf("%sID: %s Size: %7v%s\n", intend, ll.ID[:12], units.HumanSizeWithPrecision(float64(ll.Size), 4), tags) - for count, childID := range ll.ChildID { - if err := printImageChildren(layerMap, childID, prefix, count == len(ll.ChildID)-1); err != nil { - return err - } - } + fmt.Print(tree) return nil } - -// prints the layers info of image -func printImageHierarchy(imageInfo *image.InfoImage) { - for count, l := range imageInfo.Layers { - var tags string - intend := middleItem - if len(l.RepoTags) > 0 { - tags = fmt.Sprintf(" Top Layer of: %s", l.RepoTags) - } - if count == len(imageInfo.Layers)-1 { - intend = lastItem - } - fmt.Printf("%s ID: %s Size: %7v%s\n", intend, l.ID[:12], units.HumanSizeWithPrecision(float64(l.Size), 4), tags) - } -} diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index a0227c48c..e9792fa8f 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -1188,6 +1188,16 @@ method GetPodsByStatus(statuses: []string) -> (pods: []string) # ~~~ method ImageExists(name: string) -> (exists: int) +# ImageTree returns the image tree for the provided image name or ID +# #### Example +# ~~~ +# $ varlink call -m unix:/run/podman/io.podman/io.podman.ImageTree '{"name": "alpine"}' +# { +# "tree": "Image ID: e7d92cdc71fe\nTags: [docker.io/library/alpine:latest]\nSize: 5.861MB\nImage Layers\n└── ID: 5216338b40a7 Size: 5.857MB Top Layer of: [docker.io/library/alpine:latest]\n" +# } +# ~~~ +method ImageTree(name: string, whatRequires: bool) -> (tree: string) + # ContainerExists takes a full or partial container ID or name and returns an int as to # whether the container exists in local storage. A result of 0 means the container does # exists; whereas a result of 1 means it could not be found. diff --git a/cmd/podman/volume_create.go b/cmd/podman/volume_create.go index e5a576749..52189657b 100644 --- a/cmd/podman/volume_create.go +++ b/cmd/podman/volume_create.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/shared" + "github.com/containers/libpod/cmd/podman/shared/parse" "github.com/containers/libpod/pkg/adapter" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -51,12 +51,12 @@ func volumeCreateCmd(c *cliconfig.VolumeCreateValues) error { return errors.Errorf("too many arguments, create takes at most 1 argument") } - labels, err := shared.GetAllLabels([]string{}, c.Label) + labels, err := parse.GetAllLabels([]string{}, c.Label) if err != nil { return errors.Wrapf(err, "unable to process labels") } - opts, err := shared.GetAllLabels([]string{}, c.Opt) + opts, err := parse.GetAllLabels([]string{}, c.Opt) if err != nil { return errors.Wrapf(err, "unable to process options") } |