From 23251149aba5965e06bc35ddbd15717b2bb7b43b Mon Sep 17 00:00:00 2001 From: Brent Baude Date: Thu, 20 Aug 2020 09:52:53 -0500 Subject: error when adding container to pod with network information because a pod's network information is dictated by the infra container at creation, a container cannot be created with network attributes. this has been difficult for users to understand. we now return an error when a container is being created inside a pod and passes any of the following attributes: * static IP (v4 and v6) * static mac * ports -p (i.e. -p 8080:80) * exposed ports (i.e. 222-225) * publish ports from image -P Signed-off-by: Brent Baude Signed-off-by: Matthew Heon --- pkg/specgen/container_validate.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'pkg/specgen') diff --git a/pkg/specgen/container_validate.go b/pkg/specgen/container_validate.go index 4dd2ab0b3..c4449ba3a 100644 --- a/pkg/specgen/container_validate.go +++ b/pkg/specgen/container_validate.go @@ -3,6 +3,7 @@ package specgen import ( "strings" + "github.com/containers/libpod/v2/libpod/define" "github.com/containers/libpod/v2/pkg/rootless" "github.com/containers/libpod/v2/pkg/util" "github.com/pkg/errors" @@ -34,6 +35,23 @@ func (s *SpecGenerator) Validate() error { } } + // Containers being added to a pod cannot have certain network attributes + // associated with them because those should be on the infra container. + if len(s.Pod) > 0 && s.NetNS.NSMode == FromPod { + if s.StaticIP != nil || s.StaticIPv6 != nil { + return errors.Wrap(define.ErrNetworkOnPodContainer, "static ip addresses must be defined when the pod is created") + } + if s.StaticMAC != nil { + return errors.Wrap(define.ErrNetworkOnPodContainer, "MAC addresses must be defined when the pod is created") + } + if len(s.CNINetworks) > 0 { + return errors.Wrap(define.ErrNetworkOnPodContainer, "networks must be defined when the pod is created") + } + if len(s.PortMappings) > 0 || s.PublishExposedPorts { + return errors.Wrap(define.ErrNetworkOnPodContainer, "published or exposed ports must be defined when the pod is created") + } + } + // // ContainerBasicConfig // -- cgit v1.2.3-54-g00ecf From 484bd0af1dc90848be4bb4d729d5913b65fa7bc6 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Mon, 10 Aug 2020 15:00:42 -0400 Subject: Ensure pod infra containers have an exit command Most Libpod containers are made via `pkg/specgen/generate` which includes code to generate an appropriate exit command which will handle unmounting the container's storage, cleaning up the container's network, etc. There is one notable exception: pod infra containers, which are made entirely within Libpod and do not touch pkg/specgen. As such, no cleanup process, network never cleaned up, bad things can happen. There is good news, though - it's not that difficult to add this, and it's done in this PR. Generally speaking, we don't allow passing options directly to the infra container at create time, but we do (optionally) proxy a pre-approved set of options into it when we create it. Add ExitCommand to these options, and set it at time of pod creation using the same code we use to generate exit commands for normal containers. Fixes #7103 Signed-off-by: Matthew Heon Signed-off-by: Matthew Heon --- libpod/options.go | 20 ++++++++++++++++++++ libpod/pod.go | 11 ++++++++++- libpod/runtime_pod_infra_linux.go | 6 ++++++ pkg/bindings/test/pods_test.go | 6 +++--- pkg/specgen/generate/pod_create.go | 16 ++++++++++++++-- 5 files changed, 53 insertions(+), 6 deletions(-) (limited to 'pkg/specgen') diff --git a/libpod/options.go b/libpod/options.go index a4e4b99e9..45775e73b 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -2036,3 +2036,23 @@ func WithPodHostNetwork() PodCreateOption { return nil } } + +// WithPodInfraExitCommand sets an exit command for the pod's infra container. +// Semantics are identical to WithExitCommand() above - the ID of the container +// will be appended to the end of the provided command (note that this will +// specifically be the ID of the infra container *and not the pod's id*. +func WithPodInfraExitCommand(exitCmd []string) PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + + if !pod.config.InfraContainer.HasInfraContainer { + return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod infra container exit command as no infra container is being created") + } + + pod.config.InfraContainer.ExitCommand = exitCmd + + return nil + } +} diff --git a/libpod/pod.go b/libpod/pod.go index 00ba5d53c..4ce66b751 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -81,7 +81,15 @@ type podState struct { InfraContainerID string } -// InfraContainerConfig is the configuration for the pod's infra container +// InfraContainerConfig is the configuration for the pod's infra container. +// Generally speaking, these are equivalent to container configuration options +// you will find in container_config.go (and even named identically), save for +// HasInfraContainer (which determines if an infra container is even created - +// if it is false, no other options in this struct will be used) and HostNetwork +// (this involves the created OCI spec, and as such is not represented directly +// in container_config.go). +// Generally speaking, aside from those two exceptions, these options will set +// the equivalent field in the container's configuration. type InfraContainerConfig struct { ConmonPidFile string `json:"conmonPidFile"` HasInfraContainer bool `json:"makeInfraContainer"` @@ -96,6 +104,7 @@ type InfraContainerConfig struct { UseImageHosts bool `json:"useImageHosts,omitempty"` HostAdd []string `json:"hostsAdd,omitempty"` Networks []string `json:"networks,omitempty"` + ExitCommand []string `json:"exitCommand,omitempty"` } // ID retrieves the pod's ID diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index 24802f89e..faa627ff1 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -83,6 +83,9 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm return nil, errors.Wrapf(err, "error removing network namespace from pod %s infra container", p.ID()) } + // For each option in InfraContainerConfig - if set, pass into + // the infra container we're creating with the appropriate + // With... option. if p.config.InfraContainer.StaticIP != nil { options = append(options, WithStaticIP(p.config.InfraContainer.StaticIP)) } @@ -107,6 +110,9 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm if len(p.config.InfraContainer.HostAdd) > 0 { options = append(options, WithHosts(p.config.InfraContainer.HostAdd)) } + if len(p.config.InfraContainer.ExitCommand) > 0 { + options = append(options, WithExitCommand(p.config.InfraContainer.ExitCommand)) + } } g.SetRootReadonly(true) diff --git a/pkg/bindings/test/pods_test.go b/pkg/bindings/test/pods_test.go index 2ad6f38c1..65336f1f1 100644 --- a/pkg/bindings/test/pods_test.go +++ b/pkg/bindings/test/pods_test.go @@ -244,7 +244,7 @@ var _ = Describe("Podman pods", func() { Expect(response.State).To(Equal(define.PodStateExited)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). - To(Equal(define.ContainerStateStopped)) + To(Equal(define.ContainerStateExited)) } // Stop an already stopped pod @@ -309,7 +309,7 @@ var _ = Describe("Podman pods", func() { Expect(response.State).To(Equal(define.PodStateExited)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). - To(Equal(define.ContainerStateStopped)) + To(Equal(define.ContainerStateExited)) } _, err = pods.Stop(bt.conn, newpod2, nil) Expect(err).To(BeNil()) @@ -318,7 +318,7 @@ var _ = Describe("Podman pods", func() { Expect(response.State).To(Equal(define.PodStateExited)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). - To(Equal(define.ContainerStateStopped)) + To(Equal(define.ContainerStateExited)) } _, err = pods.Prune(bt.conn) Expect(err).To(BeNil()) diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index 4fe1b6435..09972da4a 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -13,14 +13,14 @@ func MakePod(p *specgen.PodSpecGenerator, rt *libpod.Runtime) (*libpod.Pod, erro if err := p.Validate(); err != nil { return nil, err } - options, err := createPodOptions(p) + options, err := createPodOptions(p, rt) if err != nil { return nil, err } return rt.NewPod(context.Background(), options...) } -func createPodOptions(p *specgen.PodSpecGenerator) ([]libpod.PodCreateOption, error) { +func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime) ([]libpod.PodCreateOption, error) { var ( options []libpod.PodCreateOption ) @@ -31,6 +31,18 @@ func createPodOptions(p *specgen.PodSpecGenerator) ([]libpod.PodCreateOption, er return nil, err } options = append(options, nsOptions...) + + // Make our exit command + storageConfig := rt.StorageConfig() + runtimeConfig, err := rt.GetConfig() + if err != nil { + return nil, err + } + exitCommand, err := CreateExitCommandArgs(storageConfig, runtimeConfig, logrus.IsLevelEnabled(logrus.DebugLevel), false, false) + if err != nil { + return nil, errors.Wrapf(err, "error creating infra container exit command") + } + options = append(options, libpod.WithPodInfraExitCommand(exitCommand)) } if len(p.CgroupParent) > 0 { options = append(options, libpod.WithPodCgroupParent(p.CgroupParent)) -- cgit v1.2.3-54-g00ecf