aboutsummaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/container.go25
-rw-r--r--libpod/container_api.go26
-rw-r--r--libpod/container_config.go8
-rw-r--r--libpod/container_internal_linux.go13
-rw-r--r--libpod/container_internal_unsupported.go4
-rw-r--r--libpod/kube.go28
-rw-r--r--libpod/networking_linux.go92
-rw-r--r--libpod/networking_unsupported.go9
-rw-r--r--libpod/runtime.go2
-rw-r--r--libpod/shutdown/handler.go6
-rw-r--r--libpod/stats.go2
11 files changed, 191 insertions, 24 deletions
diff --git a/libpod/container.go b/libpod/container.go
index 4e0687318..96a21736c 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -921,13 +921,33 @@ func (c *Container) CgroupManager() string {
return cgroupManager
}
-// CGroupPath returns a cgroups "path" for a given container.
+// CGroupPath returns a cgroups "path" for the given container.
+// Note that the container must be running. Otherwise, an error
+// is returned.
func (c *Container) CGroupPath() (string, error) {
+ if !c.batched {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+ if err := c.syncContainer(); err != nil {
+ return "", errors.Wrapf(err, "error updating container %s state", c.ID())
+ }
+ }
+ return c.cGroupPath()
+}
+
+// cGroupPath returns a cgroups "path" for the given container.
+// Note that the container must be running. Otherwise, an error
+// is returned.
+// NOTE: only call this when owning the container's lock.
+func (c *Container) cGroupPath() (string, error) {
if c.config.NoCgroups || c.config.CgroupsMode == "disabled" {
return "", errors.Wrapf(define.ErrNoCgroups, "this container is not creating cgroups")
}
+ if c.state.State != define.ContainerStateRunning && c.state.State != define.ContainerStatePaused {
+ return "", errors.Wrapf(define.ErrCtrStopped, "cannot get cgroup path unless container %s is running", c.ID())
+ }
- // Read /proc/[PID]/cgroup and find the *longest* cgroup entry. That's
+ // Read /proc/{PID}/cgroup and find the *longest* cgroup entry. That's
// needed to account for hacks in cgroups v1, where each line in the
// file could potentially point to a cgroup. The longest one, however,
// is the libpod-specific one we're looking for.
@@ -952,7 +972,6 @@ func (c *Container) CGroupPath() (string, error) {
if len(path) > len(cgroupPath) {
cgroupPath = path
}
-
}
if len(cgroupPath) == 0 {
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 6a7ddc421..1b33f16b4 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -639,6 +639,32 @@ func (c *Container) Sync() error {
return nil
}
+// ReloadNetwork reconfigures the container's network.
+// Technically speaking, it will tear down and then reconfigure the container's
+// network namespace, which will result in all firewall rules being recreated.
+// It is mostly intended to be used in cases where the system firewall has been
+// reloaded, and existing rules have been wiped out. It is expected that some
+// downtime will result, as the rules are destroyed as part of this process.
+// At present, this only works on root containers; it may be expanded to restart
+// slirp4netns in the future to work with rootless containers as well.
+// Requires that the container must be running or created.
+func (c *Container) ReloadNetwork() error {
+ if !c.batched {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ if err := c.syncContainer(); err != nil {
+ return err
+ }
+ }
+
+ if !c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning) {
+ return errors.Wrapf(define.ErrCtrStateInvalid, "cannot reload network unless container network has been configured")
+ }
+
+ return c.reloadNetwork()
+}
+
// Refresh is DEPRECATED and REMOVED.
func (c *Container) Refresh(ctx context.Context) error {
// This has been deprecated for a long while, and is in the process of
diff --git a/libpod/container_config.go b/libpod/container_config.go
index cc3ad25ea..c95be9b55 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -135,7 +135,13 @@ type ContainerRootFSConfig struct {
// OverlayVolumes lists the overlay volumes to mount into the container.
OverlayVolumes []*ContainerOverlayVolume `json:"overlayVolumes,omitempty"`
// ImageVolumes lists the image volumes to mount into the container.
- ImageVolumes []*ContainerImageVolume `json:"imageVolumes,omitempty"`
+ // Please note that this is named ctrImageVolumes in JSON to
+ // distinguish between these and the old `imageVolumes` field in Podman
+ // pre-1.8, which was used in very old Podman versions to determine how
+ // image volumes were handled in Libpod (support for these eventually
+ // moved out of Libpod into pkg/specgen).
+ // Please DO NOT re-use the `imageVolumes` name in container JSON again.
+ ImageVolumes []*ContainerImageVolume `json:"ctrImageVolumes,omitempty"`
// CreateWorkingDir indicates that Libpod should create the container's
// working directory if it does not exist. Some OCI runtimes do this by
// default, but others do not.
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 72eaeac8e..1bf044f9d 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -231,6 +231,19 @@ func (c *Container) cleanupNetwork() error {
return nil
}
+// reloadNetwork reloads the network for the given container, recreating
+// firewall rules.
+func (c *Container) reloadNetwork() error {
+ result, err := c.runtime.reloadContainerNetwork(c)
+ if err != nil {
+ return err
+ }
+
+ c.state.NetworkStatus = result
+
+ return c.save()
+}
+
func (c *Container) getUserOverrides() *lookup.Overrides {
var hasPasswdFile, hasGroupFile bool
overrides := lookup.Overrides{}
diff --git a/libpod/container_internal_unsupported.go b/libpod/container_internal_unsupported.go
index c22e9a4a4..7f6fc9ec9 100644
--- a/libpod/container_internal_unsupported.go
+++ b/libpod/container_internal_unsupported.go
@@ -50,6 +50,10 @@ func (c *Container) cleanupOverlayMounts() error {
return nil
}
+func (c *Container) reloadNetwork() error {
+ return define.ErrNotImplemented
+}
+
func (c *Container) getUserOverrides() *lookup.Overrides {
return nil
}
diff --git a/libpod/kube.go b/libpod/kube.go
index 067e7827d..bf041112a 100644
--- a/libpod/kube.go
+++ b/libpod/kube.go
@@ -21,9 +21,9 @@ import (
// GenerateForKube takes a slice of libpod containers and generates
// one v1.Pod description that includes just a single container.
-func (c *Container) GenerateForKube() (*v1.Pod, error) {
+func GenerateForKube(ctrs []*Container) (*v1.Pod, error) {
// Generate the v1.Pod yaml description
- return simplePodWithV1Container(c)
+ return simplePodWithV1Containers(ctrs)
}
// GenerateForKube takes a slice of libpod containers and generates
@@ -236,14 +236,20 @@ func addContainersAndVolumesToPodObject(containers []v1.Container, volumes []v1.
return &p
}
-// simplePodWithV1Container is a function used by inspect when kube yaml needs to be generated
+// simplePodWithV1Containers is a function used by inspect when kube yaml needs to be generated
// for a single container. we "insert" that container description in a pod.
-func simplePodWithV1Container(ctr *Container) (*v1.Pod, error) {
- kubeCtr, kubeVols, err := containerToV1Container(ctr)
- if err != nil {
- return nil, err
+func simplePodWithV1Containers(ctrs []*Container) (*v1.Pod, error) {
+ kubeCtrs := make([]v1.Container, 0, len(ctrs))
+ kubeVolumes := make([]v1.Volume, 0)
+ for _, ctr := range ctrs {
+ kubeCtr, kubeVols, err := containerToV1Container(ctr)
+ if err != nil {
+ return nil, err
+ }
+ kubeCtrs = append(kubeCtrs, kubeCtr)
+ kubeVolumes = append(kubeVolumes, kubeVols...)
}
- return addContainersAndVolumesToPodObject([]v1.Container{kubeCtr}, kubeVols, ctr.Name()), nil
+ return addContainersAndVolumesToPodObject(kubeCtrs, kubeVolumes, strings.ReplaceAll(ctrs[0].Name(), "_", "")), nil
}
@@ -294,6 +300,12 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, error) {
_, image := c.Image()
kubeContainer.Image = image
kubeContainer.Stdin = c.Stdin()
+
+ // prepend the entrypoint of the container to command
+ if ep := c.Entrypoint(); len(c.Entrypoint()) > 0 {
+ ep = append(ep, containerCommands...)
+ containerCommands = ep
+ }
kubeContainer.Command = containerCommands
// TODO need to figure out how we handle command vs entry point. Kube appears to prefer entrypoint.
// right now we just take the container's command
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 463378af7..bf27989bf 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -13,6 +13,7 @@ import (
"os"
"os/exec"
"path/filepath"
+ "regexp"
"sort"
"strings"
"syscall"
@@ -741,8 +742,9 @@ func (r *Runtime) closeNetNS(ctr *Container) error {
return nil
}
-// Tear down a network namespace, undoing all state associated with it.
-func (r *Runtime) teardownNetNS(ctr *Container) error {
+// Tear down a container's CNI network configuration, but do not tear down the
+// namespace itself.
+func (r *Runtime) teardownCNI(ctr *Container) error {
if ctr.state.NetNS == nil {
// The container has no network namespace, we're set
return nil
@@ -781,6 +783,19 @@ func (r *Runtime) teardownNetNS(ctr *Container) error {
return errors.Wrapf(err, "error tearing down CNI namespace configuration for container %s", ctr.ID())
}
}
+ return nil
+}
+
+// Tear down a network namespace, undoing all state associated with it.
+func (r *Runtime) teardownNetNS(ctr *Container) error {
+ if err := r.teardownCNI(ctr); err != nil {
+ return err
+ }
+
+ networks, _, err := ctr.networks()
+ if err != nil {
+ return err
+ }
// CNI-in-slirp4netns
if rootless.IsRootless() && len(networks) != 0 {
@@ -821,12 +836,73 @@ func getContainerNetNS(ctr *Container) (string, error) {
return "", nil
}
+// Reload only works with containers with a configured network.
+// It will tear down, and then reconfigure, the network of the container.
+// This is mainly used when a reload of firewall rules wipes out existing
+// firewall configuration.
+// Efforts will be made to preserve MAC and IP addresses, but this only works if
+// the container only joined a single CNI network, and was only assigned a
+// single MAC or IP.
+// Only works on root containers at present, though in the future we could
+// extend this to stop + restart slirp4netns
+func (r *Runtime) reloadContainerNetwork(ctr *Container) ([]*cnitypes.Result, error) {
+ if ctr.state.NetNS == nil {
+ return nil, errors.Wrapf(define.ErrCtrStateInvalid, "container %s network is not configured, refusing to reload", ctr.ID())
+ }
+ if rootless.IsRootless() || ctr.config.NetMode.IsSlirp4netns() {
+ return nil, errors.Wrapf(define.ErrRootless, "network reload only supported for root containers")
+ }
+
+ logrus.Infof("Going to reload container %s network", ctr.ID())
+
+ var requestedIP net.IP
+ var requestedMAC net.HardwareAddr
+ // Set requested IP and MAC address, if possible.
+ if len(ctr.state.NetworkStatus) == 1 {
+ result := ctr.state.NetworkStatus[0]
+ if len(result.IPs) == 1 {
+ resIP := result.IPs[0]
+
+ requestedIP = resIP.Address.IP
+ ctr.requestedIP = requestedIP
+ logrus.Debugf("Going to preserve container %s IP address %s", ctr.ID(), ctr.requestedIP.String())
+
+ if resIP.Interface != nil && *resIP.Interface < len(result.Interfaces) && *resIP.Interface >= 0 {
+ var err error
+ requestedMAC, err = net.ParseMAC(result.Interfaces[*resIP.Interface].Mac)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error parsing container %s MAC address %s", ctr.ID(), result.Interfaces[*resIP.Interface].Mac)
+ }
+ ctr.requestedMAC = requestedMAC
+ logrus.Debugf("Going to preserve container %s MAC address %s", ctr.ID(), ctr.requestedMAC.String())
+ }
+ }
+ }
+
+ err := r.teardownCNI(ctr)
+ if err != nil {
+ // teardownCNI will error if the iptables rules do not exists and this is the case after
+ // a firewall reload. The purpose of network reload is to recreate the rules if they do
+ // not exists so we should not log this specific error as error. This would confuse users otherwise.
+ b, rerr := regexp.MatchString("Couldn't load target `CNI-[a-f0-9]{24}':No such file or directory", err.Error())
+ if rerr == nil && !b {
+ logrus.Error(err)
+ } else {
+ logrus.Info(err)
+ }
+ }
+
+ // teardownCNI will clean the requested IP and MAC so we need to set them again
+ ctr.requestedIP = requestedIP
+ ctr.requestedMAC = requestedMAC
+ return r.configureNetNS(ctr, ctr.state.NetNS)
+}
+
func getContainerNetIO(ctr *Container) (*netlink.LinkStatistics, error) {
var netStats *netlink.LinkStatistics
- // rootless v2 cannot seem to resolve its network connection to
- // collect statistics. For now, we allow stats to at least run
- // by returning nil
- if rootless.IsRootless() {
+ // With slirp4netns, we can't collect statistics at present.
+ // For now, we allow stats to at least run by returning nil
+ if rootless.IsRootless() || ctr.config.NetMode.IsSlirp4netns() {
return netStats, nil
}
netNSPath, netPathErr := getContainerNetNS(ctr)
@@ -984,12 +1060,12 @@ func resultToBasicNetworkConfig(result *cnitypes.Result) (define.InspectBasicNet
config.IPAddress = ctrIP.Address.IP.String()
config.IPPrefixLen = size
config.Gateway = ctrIP.Gateway.String()
- if ctrIP.Interface != nil && *ctrIP.Interface < len(result.Interfaces) && *ctrIP.Interface > 0 {
+ if ctrIP.Interface != nil && *ctrIP.Interface < len(result.Interfaces) && *ctrIP.Interface >= 0 {
config.MacAddress = result.Interfaces[*ctrIP.Interface].Mac
}
case ctrIP.Version == "4" && config.IPAddress != "":
config.SecondaryIPAddresses = append(config.SecondaryIPAddresses, ctrIP.Address.String())
- if ctrIP.Interface != nil && *ctrIP.Interface < len(result.Interfaces) && *ctrIP.Interface > 0 {
+ if ctrIP.Interface != nil && *ctrIP.Interface < len(result.Interfaces) && *ctrIP.Interface >= 0 {
config.AdditionalMacAddresses = append(config.AdditionalMacAddresses, result.Interfaces[*ctrIP.Interface].Mac)
}
case ctrIP.Version == "6" && config.IPAddress == "":
diff --git a/libpod/networking_unsupported.go b/libpod/networking_unsupported.go
index 76bb01424..9e5c4adde 100644
--- a/libpod/networking_unsupported.go
+++ b/libpod/networking_unsupported.go
@@ -2,7 +2,10 @@
package libpod
-import "github.com/containers/podman/v2/libpod/define"
+import (
+ cnitypes "github.com/containernetworking/cni/pkg/types/current"
+ "github.com/containers/podman/v2/libpod/define"
+)
func (r *Runtime) setupRootlessNetNS(ctr *Container) error {
return define.ErrNotImplemented
@@ -28,6 +31,10 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
return nil, define.ErrNotImplemented
}
+func (r *Runtime) reloadContainerNetwork(ctr *Container) ([]*cnitypes.Result, error) {
+ return nil, define.ErrNotImplemented
+}
+
func getCNINetworksDir() (string, error) {
return "", define.ErrNotImplemented
}
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 72bd34a5e..1004e4fa7 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -190,7 +190,7 @@ func newRuntimeFromConfig(ctx context.Context, conf *config.Config, options ...R
if err := shutdown.Register("libpod", func(sig os.Signal) error {
os.Exit(1)
return nil
- }); err != nil {
+ }); err != nil && errors.Cause(err) != shutdown.ErrHandlerExists {
logrus.Errorf("Error registering shutdown handler for libpod: %v", err)
}
diff --git a/libpod/shutdown/handler.go b/libpod/shutdown/handler.go
index 87538dec9..f0f228b19 100644
--- a/libpod/shutdown/handler.go
+++ b/libpod/shutdown/handler.go
@@ -11,6 +11,10 @@ import (
)
var (
+ ErrHandlerExists error = errors.New("handler with given name already exists")
+)
+
+var (
stopped bool
sigChan chan os.Signal
cancelChan chan bool
@@ -98,7 +102,7 @@ func Register(name string, handler func(os.Signal) error) error {
}
if _, ok := handlers[name]; ok {
- return errors.Errorf("handler with name %s already exists", name)
+ return ErrHandlerExists
}
handlers[name] = handler
diff --git a/libpod/stats.go b/libpod/stats.go
index e34739626..09d990017 100644
--- a/libpod/stats.go
+++ b/libpod/stats.go
@@ -34,7 +34,7 @@ func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*de
return stats, define.ErrCtrStateInvalid
}
- cgroupPath, err := c.CGroupPath()
+ cgroupPath, err := c.cGroupPath()
if err != nil {
return nil, err
}