summaryrefslogtreecommitdiff
path: root/cmd/podman
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/podman')
-rw-r--r--cmd/podman/build.go5
-rw-r--r--cmd/podman/commit.go5
-rw-r--r--cmd/podman/login.go18
-rw-r--r--cmd/podman/logout.go12
-rw-r--r--cmd/podman/logs.go2
-rw-r--r--cmd/podman/pod_create.go20
-rw-r--r--cmd/podman/ps.go10
-rw-r--r--cmd/podman/service.go112
-rw-r--r--cmd/podman/shared/container.go8
-rw-r--r--cmd/podman/shared/create.go5
-rw-r--r--cmd/podman/shared/create_cli.go11
-rw-r--r--cmd/podman/shared/create_cli_test.go51
-rw-r--r--cmd/podman/shared/parse/parse.go28
-rw-r--r--cmd/podman/shared/parse/parse_test.go53
-rw-r--r--cmd/podman/tree.go97
-rw-r--r--cmd/podman/varlink/io.podman.varlink10
-rw-r--r--cmd/podman/volume_create.go6
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")
}