diff options
-rw-r--r-- | cmd/podman/cp.go | 23 | ||||
-rw-r--r-- | cmd/podman/inspect.go | 8 | ||||
-rw-r--r-- | cmd/podman/main_local.go | 14 | ||||
-rw-r--r-- | docs/podman.1.md | 5 | ||||
-rw-r--r-- | libpod/container_inspect.go | 26 | ||||
-rw-r--r-- | libpod/kube.go | 5 | ||||
-rw-r--r-- | libpod/oci_linux.go | 6 | ||||
-rw-r--r-- | libpod/runtime.go | 91 | ||||
-rw-r--r-- | test/e2e/cp_test.go | 4 | ||||
-rw-r--r-- | test/e2e/generate_kube_test.go | 73 | ||||
-rw-r--r-- | test/e2e/inspect_test.go | 27 |
11 files changed, 228 insertions, 54 deletions
diff --git a/cmd/podman/cp.go b/cmd/podman/cp.go index a9418e6e0..efe343bfd 100644 --- a/cmd/podman/cp.go +++ b/cmd/podman/cp.go @@ -326,20 +326,6 @@ func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, ch } return nil } - if !archive.IsArchivePath(srcPath) { - // This srcPath is a file, and either it's not an - // archive, or we don't care whether or not it's an - // archive. - destfi, err := os.Stat(destPath) - if err != nil { - if !os.IsNotExist(err) { - return errors.Wrapf(err, "failed to get stat of dest path %s", destPath) - } - } - if destfi != nil && destfi.IsDir() { - destPath = filepath.Join(destPath, filepath.Base(srcPath)) - } - } if extract { // We're extracting an archive into the destination directory. @@ -350,9 +336,16 @@ func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, ch return nil } - if destDirIsExist || strings.HasSuffix(dest, string(os.PathSeparator)) { + destfi, err := os.Stat(destPath) + if err != nil { + if !os.IsNotExist(err) { + return errors.Wrapf(err, "failed to get stat of dest path %s", destPath) + } + } + if destfi != nil && destfi.IsDir() { destPath = filepath.Join(destPath, filepath.Base(srcPath)) } + // Copy the file, preserving attributes. logrus.Debugf("copying %q to %q", srcPath, destPath) if err = copyFileWithTar(srcPath, destPath); err != nil { diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go index 4303c149c..24edfcb68 100644 --- a/cmd/podman/inspect.go +++ b/cmd/podman/inspect.go @@ -98,6 +98,14 @@ func inspectCmd(c *cliconfig.InspectValues) error { if strings.Contains(outputFormat, "{{.Id}}") { outputFormat = strings.Replace(outputFormat, "{{.Id}}", formats.IDString, -1) } + // These fields were renamed, so we need to provide backward compat for + // the old names. + if strings.Contains(outputFormat, ".Src") { + outputFormat = strings.Replace(outputFormat, ".Src", ".Source", -1) + } + if strings.Contains(outputFormat, ".Dst") { + outputFormat = strings.Replace(outputFormat, ".Dst", ".Destination", -1) + } if latestContainer { lc, err := runtime.GetLatestContainer() if err != nil { diff --git a/cmd/podman/main_local.go b/cmd/podman/main_local.go index 132f35ab5..7a062cb4b 100644 --- a/cmd/podman/main_local.go +++ b/cmd/podman/main_local.go @@ -12,6 +12,7 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/tracing" "github.com/containers/libpod/pkg/util" @@ -25,8 +26,17 @@ import ( const remote = false func init() { - - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CGroupManager, "cgroup-manager", "", "Cgroup manager to use (cgroupfs or systemd, default systemd)") + cgroupManager := libpod.SystemdCgroupsManager + if runtimeConfig, err := libpod.DefaultRuntimeConfig(); err == nil { + cgroupManager = runtimeConfig.CgroupManager + } + cgroupHelp := "Cgroup manager to use (cgroupfs or systemd)" + cgroupv2, _ := util.IsCgroup2UnifiedMode() + if rootless.IsRootless() && !cgroupv2 { + cgroupManager = "" + cgroupHelp = "Cgroup manager is not supported in rootless mode" + } + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CGroupManager, "cgroup-manager", cgroupManager, cgroupHelp) // -c is deprecated due to conflict with -c on subcommands rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CpuProfile, "cpu-profile", "", "Path for the cpu profiling results") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Config, "config", "", "Path of a libpod config file detailing container server configuration options") diff --git a/docs/podman.1.md b/docs/podman.1.md index c23075718..022514a80 100644 --- a/docs/podman.1.md +++ b/docs/podman.1.md @@ -27,7 +27,10 @@ Print usage statement **--cgroup-manager**=*manager* -CGroup manager to use for container cgroups. Supported values are cgroupfs or systemd (default). Setting this flag can cause certain commands to break when called on containers created by the other CGroup manager type. +CGroup manager to use for container cgroups. Supported values are cgroupfs or systemd. Default is systemd unless overridden in the libpod.conf file. + +Note: Setting this flag can cause certain commands to break when called on containers previously created by the other CGroup manager type. +Note: CGroup manager is not supported in rootless mode when using CGroups Version V1. **--cpu-profile**=*path* diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go index 1d12b1b35..3ac774060 100644 --- a/libpod/container_inspect.go +++ b/libpod/container_inspect.go @@ -46,7 +46,7 @@ type InspectContainerData struct { GraphDriver *driver.Data `json:"GraphDriver"` SizeRw int64 `json:"SizeRw,omitempty"` SizeRootFs int64 `json:"SizeRootFs,omitempty"` - Mounts []*InspectMount `json:"Mounts"` + Mounts []InspectMount `json:"Mounts"` Dependencies []string `json:"Dependencies"` NetworkSettings *InspectNetworkSettings `json:"NetworkSettings"` //TODO ExitCommand []string `json:"ExitCommand"` @@ -111,10 +111,10 @@ type InspectMount struct { // The name of the volume. Empty for bind mounts. Name string `json:"Name,omptempty"` // The source directory for the volume. - Src string `json:"Source"` + Source string `json:"Source"` // The destination directory for the volume. Specified as a path within // the container, as it would be passed into the OCI runtime. - Dst string `json:"Destination"` + Destination string `json:"Destination"` // The driver used for the named volume. Empty for bind mounts. Driver string `json:"Driver"` // Contains SELinux :z/:Z mount options. Unclear what, if anything, else @@ -359,8 +359,8 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data) // Get inspect-formatted mounts list. // Only includes user-specified mounts. Only includes bind mounts and named // volumes, not tmpfs volumes. -func (c *Container) getInspectMounts(ctrSpec *spec.Spec) ([]*InspectMount, error) { - inspectMounts := []*InspectMount{} +func (c *Container) getInspectMounts(ctrSpec *spec.Spec) ([]InspectMount, error) { + inspectMounts := []InspectMount{} // No mounts, return early if len(c.config.UserVolumes) == 0 { @@ -384,9 +384,9 @@ func (c *Container) getInspectMounts(ctrSpec *spec.Spec) ([]*InspectMount, error // We need to look up the volumes. // First: is it a named volume? if volume, ok := namedVolumes[vol]; ok { - mountStruct := new(InspectMount) + mountStruct := InspectMount{} mountStruct.Type = "volume" - mountStruct.Dst = volume.Dest + mountStruct.Destination = volume.Dest mountStruct.Name = volume.Name // For src and driver, we need to look up the named @@ -396,9 +396,9 @@ func (c *Container) getInspectMounts(ctrSpec *spec.Spec) ([]*InspectMount, error return nil, errors.Wrapf(err, "error looking up volume %s in container %s config", volume.Name, c.ID()) } mountStruct.Driver = volFromDB.Driver() - mountStruct.Src = volFromDB.MountPoint() + mountStruct.Source = volFromDB.MountPoint() - parseMountOptionsForInspect(volume.Options, mountStruct) + parseMountOptionsForInspect(volume.Options, &mountStruct) inspectMounts = append(inspectMounts, mountStruct) } else if mount, ok := mounts[vol]; ok { @@ -408,12 +408,12 @@ func (c *Container) getInspectMounts(ctrSpec *spec.Spec) ([]*InspectMount, error continue } - mountStruct := new(InspectMount) + mountStruct := InspectMount{} mountStruct.Type = "bind" - mountStruct.Src = mount.Source - mountStruct.Dst = mount.Destination + mountStruct.Source = mount.Source + mountStruct.Destination = mount.Destination - parseMountOptionsForInspect(mount.Options, mountStruct) + parseMountOptionsForInspect(mount.Options, &mountStruct) inspectMounts = append(inspectMounts, mountStruct) } diff --git a/libpod/kube.go b/libpod/kube.go index c5fd9d75c..283662059 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -138,6 +138,11 @@ func (p *Pod) podWithContainers(containers []*Container, ports []v1.ContainerPor if err != nil { return nil, err } + + // Since port bindings for the pod are handled by the + // infra container, wipe them here. + result.Ports = nil + // We add the original port declarations from the libpod infra container // to the first kubernetes container description because otherwise we loose // the original container/port bindings. diff --git a/libpod/oci_linux.go b/libpod/oci_linux.go index 6e84c0759..b7efa742a 100644 --- a/libpod/oci_linux.go +++ b/libpod/oci_linux.go @@ -296,7 +296,11 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, res cmd.Env = append(cmd.Env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir)) cmd.Env = append(cmd.Env, fmt.Sprintf("_CONTAINERS_USERNS_CONFIGURED=%s", os.Getenv("_CONTAINERS_USERNS_CONFIGURED"))) cmd.Env = append(cmd.Env, fmt.Sprintf("_CONTAINERS_ROOTLESS_UID=%s", os.Getenv("_CONTAINERS_ROOTLESS_UID"))) - cmd.Env = append(cmd.Env, fmt.Sprintf("HOME=%s", os.Getenv("HOME"))) + home, err := homeDir() + if err != nil { + return err + } + cmd.Env = append(cmd.Env, fmt.Sprintf("HOME=%s", home)) if r.reservePorts && !ctr.config.NetMode.IsSlirp4netns() { ports, err := bindPorts(ctr.config.PortMappings) diff --git a/libpod/runtime.go b/libpod/runtime.go index 78fa22ec8..52ce8062b 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -5,6 +5,7 @@ import ( "fmt" "io/ioutil" "os" + "os/user" "path/filepath" "strings" "sync" @@ -374,6 +375,68 @@ func NewRuntimeFromConfig(ctx context.Context, userConfigPath string, options .. return newRuntimeFromConfig(ctx, userConfigPath, options...) } +func homeDir() (string, error) { + home := os.Getenv("HOME") + if home == "" { + usr, err := user.Current() + if err != nil { + return "", errors.Wrapf(err, "unable to resolve HOME directory") + } + home = usr.HomeDir + } + return home, nil +} + +func getRootlessConfigPath() (string, error) { + home, err := homeDir() + if err != nil { + return "", err + } + + return filepath.Join(home, ".config/containers/libpod.conf"), nil +} + +func getConfigPath() string { + if rootless.IsRootless() { + rootlessConfigPath, err := getRootlessConfigPath() + if err != nil { + if _, err := os.Stat(rootlessConfigPath); err == nil { + return rootlessConfigPath + } + } + } + if _, err := os.Stat(OverrideConfigPath); err == nil { + // Use the override configuration path + return OverrideConfigPath + } + if _, err := os.Stat(ConfigPath); err == nil { + return ConfigPath + } + return "" +} + +// DefaultRuntimeConfig reads default config path and returns the RuntimeConfig +func DefaultRuntimeConfig() (*RuntimeConfig, error) { + configPath := getConfigPath() + + contents, err := ioutil.ReadFile(configPath) + if err != nil { + return nil, errors.Wrapf(err, "error reading configuration file %s", configPath) + } + + // This is ugly, but we need to decode twice. + // Once to check if libpod static and tmp dirs were explicitly + // set (not enough to check if they're not the default value, + // might have been explicitly configured to the default). + // A second time to actually get a usable config. + tmpConfig := new(RuntimeConfig) + if _, err := toml.Decode(string(contents), tmpConfig); err != nil { + return nil, errors.Wrapf(err, "error decoding configuration file %s", + configPath) + } + return tmpConfig, nil +} + func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options ...RuntimeOption) (runtime *Runtime, err error) { runtime = new(Runtime) runtime.config = new(RuntimeConfig) @@ -402,11 +465,13 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options .. runtime.config.StaticDir = filepath.Join(storageConf.GraphRoot, "libpod") runtime.config.VolumePath = filepath.Join(storageConf.GraphRoot, "volumes") - configPath := ConfigPath - foundConfig := true + configPath := getConfigPath() rootlessConfigPath := "" if rootless.IsRootless() { - home := os.Getenv("HOME") + home, err := homeDir() + if err != nil { + return nil, err + } if runtime.config.SignaturePolicyPath == "" { newPath := filepath.Join(home, ".config/containers/policy.json") if _, err := os.Stat(newPath); err == nil { @@ -414,7 +479,10 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options .. } } - rootlessConfigPath = filepath.Join(home, ".config/containers/libpod.conf") + rootlessConfigPath, err = getRootlessConfigPath() + if err != nil { + return nil, err + } runtimeDir, err := util.GetRootlessRuntimeDir() if err != nil { @@ -436,21 +504,10 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options .. // when it doesn't exist return nil, errors.Wrapf(err, "cannot stat %s", configPath) } - } else if rootless.IsRootless() { - configPath = rootlessConfigPath - if _, err := os.Stat(configPath); err != nil { - foundConfig = false - } - } else if _, err := os.Stat(OverrideConfigPath); err == nil { - // Use the override configuration path - configPath = OverrideConfigPath - } else if _, err := os.Stat(ConfigPath); err != nil { - // Both stat checks failed, no config found - foundConfig = false } // If we have a valid configuration file, load it in - if foundConfig { + if configPath != "" { contents, err := ioutil.ReadFile(configPath) if err != nil { return nil, errors.Wrapf(err, "error reading configuration file %s", configPath) @@ -559,7 +616,7 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options .. } } - if !foundConfig { + if configPath != "" { os.MkdirAll(filepath.Dir(rootlessConfigPath), 0755) file, err := os.OpenFile(rootlessConfigPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) if err != nil && !os.IsExist(err) { diff --git a/test/e2e/cp_test.go b/test/e2e/cp_test.go index f7596d77d..5e98e73eb 100644 --- a/test/e2e/cp_test.go +++ b/test/e2e/cp_test.go @@ -58,6 +58,10 @@ var _ = Describe("Podman cp", func() { session = podmanTest.Podman([]string{"cp", name + ":foo", dstPath}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"start", name}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) }) It("podman cp file to dir", func() { diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index 40cc534c2..1df54f753 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -10,6 +10,7 @@ import ( "github.com/ghodss/yaml" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "k8s.io/api/core/v1" ) var _ = Describe("Podman generate kube", func() { @@ -57,8 +58,15 @@ var _ = Describe("Podman generate kube", func() { kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) - _, err := yaml.Marshal(kube.OutputToString()) + pod := new(v1.Pod) + err := yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).To(BeNil()) + + numContainers := 0 + for range pod.Spec.Containers { + numContainers = numContainers + 1 + } + Expect(numContainers).To(Equal(1)) }) It("podman generate service kube on container", func() { @@ -70,8 +78,11 @@ var _ = Describe("Podman generate kube", func() { kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) - _, err := yaml.Marshal(kube.OutputToString()) - Expect(err).To(BeNil()) + // TODO - test generated YAML - service produces multiple + // structs. + // pod := new(v1.Pod) + // err := yaml.Unmarshal([]byte(kube.OutputToString()), pod) + // Expect(err).To(BeNil()) }) It("podman generate kube on pod", func() { @@ -86,8 +97,15 @@ var _ = Describe("Podman generate kube", func() { kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) - _, err := yaml.Marshal(kube.OutputToString()) + pod := new(v1.Pod) + err := yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).To(BeNil()) + + numContainers := 0 + for range pod.Spec.Containers { + numContainers = numContainers + 1 + } + Expect(numContainers).To(Equal(1)) }) It("podman generate service kube on pod", func() { @@ -102,8 +120,53 @@ var _ = Describe("Podman generate kube", func() { kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) - _, err := yaml.Marshal(kube.OutputToString()) + // TODO: How do we test unmarshal with a service? We have two + // structs that need to be unmarshalled... + // _, err := yaml.Marshal(kube.OutputToString()) + // Expect(err).To(BeNil()) + }) + + It("podman generate kube on pod with ports", func() { + podName := "test" + podSession := podmanTest.Podman([]string{"pod", "create", "--name", podName, "-p", "4000:4000", "-p", "5000:5000"}) + podSession.WaitWithDefaultTimeout() + Expect(podSession.ExitCode()).To(Equal(0)) + + ctr1Name := "ctr1" + ctr1Session := podmanTest.Podman([]string{"create", "--name", ctr1Name, "--pod", podName, ALPINE, "top"}) + ctr1Session.WaitWithDefaultTimeout() + Expect(ctr1Session.ExitCode()).To(Equal(0)) + + ctr2Name := "ctr2" + ctr2Session := podmanTest.Podman([]string{"create", "--name", ctr2Name, "--pod", podName, ALPINE, "top"}) + ctr2Session.WaitWithDefaultTimeout() + Expect(ctr2Session.ExitCode()).To(Equal(0)) + + kube := podmanTest.Podman([]string{"generate", "kube", podName}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + pod := new(v1.Pod) + err := yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).To(BeNil()) + + foundPort4000 := 0 + foundPort5000 := 0 + foundOtherPort := 0 + for _, ctr := range pod.Spec.Containers { + for _, port := range ctr.Ports { + if port.HostPort == 4000 { + foundPort4000 = foundPort4000 + 1 + } else if port.HostPort == 5000 { + foundPort5000 = foundPort5000 + 1 + } else { + foundOtherPort = foundOtherPort + 1 + } + } + } + Expect(foundPort4000).To(Equal(1)) + Expect(foundPort5000).To(Equal(1)) + Expect(foundOtherPort).To(Equal(0)) }) It("podman generate and reimport kube on pod", func() { diff --git a/test/e2e/inspect_test.go b/test/e2e/inspect_test.go index ccd8602c4..790115133 100644 --- a/test/e2e/inspect_test.go +++ b/test/e2e/inspect_test.go @@ -107,4 +107,31 @@ var _ = Describe("Podman inspect", func() { Expect(result.ExitCode()).To(Equal(125)) }) + It("podman inspect with mount filters", func() { + SkipIfRemote() + + ctrSession := podmanTest.Podman([]string{"create", "-v", "/tmp:/test1", ALPINE, "top"}) + ctrSession.WaitWithDefaultTimeout() + Expect(ctrSession.ExitCode()).To(Equal(0)) + + inspectSource := podmanTest.Podman([]string{"inspect", "-l", "--format", "{{(index .Mounts 0).Source}}"}) + inspectSource.WaitWithDefaultTimeout() + Expect(inspectSource.ExitCode()).To(Equal(0)) + Expect(inspectSource.OutputToString()).To(Equal("/tmp")) + + inspectSrc := podmanTest.Podman([]string{"inspect", "-l", "--format", "{{(index .Mounts 0).Src}}"}) + inspectSrc.WaitWithDefaultTimeout() + Expect(inspectSrc.ExitCode()).To(Equal(0)) + Expect(inspectSrc.OutputToString()).To(Equal("/tmp")) + + inspectDestination := podmanTest.Podman([]string{"inspect", "-l", "--format", "{{(index .Mounts 0).Destination}}"}) + inspectDestination.WaitWithDefaultTimeout() + Expect(inspectDestination.ExitCode()).To(Equal(0)) + Expect(inspectDestination.OutputToString()).To(Equal("/test1")) + + inspectDst := podmanTest.Podman([]string{"inspect", "-l", "--format", "{{(index .Mounts 0).Dst}}"}) + inspectDst.WaitWithDefaultTimeout() + Expect(inspectDst.ExitCode()).To(Equal(0)) + Expect(inspectDst.OutputToString()).To(Equal("/test1")) + }) }) |