aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/common/create_opts.go5
-rw-r--r--docs/source/markdown/podman-save.1.md3
-rw-r--r--docs/tutorials/basic_networking.md2
-rw-r--r--libpod/container_internal_linux.go19
-rw-r--r--libpod/pod_internal.go7
-rw-r--r--libpod/runtime_ctr.go5
-rw-r--r--libpod/runtime_pod_linux.go25
-rw-r--r--pkg/bindings/images/build.go7
-rw-r--r--pkg/bindings/images/build_test.go17
-rw-r--r--pkg/channel/writer.go20
-rw-r--r--test/compose/ipam_set_ip/docker-compose.yml17
-rw-r--r--test/compose/ipam_set_ip/tests.sh4
12 files changed, 96 insertions, 35 deletions
diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go
index ca36d751e..77ac781a5 100644
--- a/cmd/podman/common/create_opts.go
+++ b/cmd/podman/common/create_opts.go
@@ -302,6 +302,11 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup
staticIP := net.ParseIP(ep.IPAddress)
netInfo.StaticIP = &staticIP
}
+ // if IPAMConfig.IPv4Address is provided
+ if ep.IPAMConfig != nil && ep.IPAMConfig.IPv4Address != "" {
+ staticIP := net.ParseIP(ep.IPAMConfig.IPv4Address)
+ netInfo.StaticIP = &staticIP
+ }
// If MAC address is provided
if len(ep.MacAddress) > 0 {
staticMac, err := net.ParseMAC(ep.MacAddress)
diff --git a/docs/source/markdown/podman-save.1.md b/docs/source/markdown/podman-save.1.md
index 0036a9379..cae0c4b05 100644
--- a/docs/source/markdown/podman-save.1.md
+++ b/docs/source/markdown/podman-save.1.md
@@ -45,6 +45,7 @@ Save image to **oci-archive, oci-dir** (directory with oci manifest type), or **
#### **\-\-multi-image-archive**, **-m**
Allow for creating archives with more than one image. Additional names will be interpreted as images instead of tags. Only supported for **docker-archive**.
+The default for this option can be modified via the `multi_image_archive="true"|"false"` flag in containers.conf.
#### **\-\-quiet**, **-q**
@@ -99,7 +100,7 @@ Storing signatures
```
## SEE ALSO
-podman(1), podman-load(1)
+podman(1), podman-load(1), containers.conf(5)
## HISTORY
July 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>
diff --git a/docs/tutorials/basic_networking.md b/docs/tutorials/basic_networking.md
index 7544c1cfd..51dfa7564 100644
--- a/docs/tutorials/basic_networking.md
+++ b/docs/tutorials/basic_networking.md
@@ -151,7 +151,7 @@ host. This interface can configure multiple subinterfaces. And each subinterfac
is capable of having its own MAC and IP address. In the case of Podman containers,
the container will present itself as if it is on the same network as the host.
-![macvlan_network](podman_bridge.png)
+![macvlan_network](podman_macvlan.png)
In the illustration, outside clients will be able to access the web container by
its IP address directly. Usually the network information, including IP address,
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index f87e845cb..f0608e2b2 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -2216,6 +2216,17 @@ func (c *Container) generatePasswdAndGroup() (string, string, error) {
return passwdPath, groupPath, nil
}
+func isRootlessCgroupSet(cgroup string) bool {
+ // old versions of podman were setting the CgroupParent to CgroupfsDefaultCgroupParent
+ // by default. Avoid breaking these versions and check whether the cgroup parent is
+ // set to the default and in this case enable the old behavior. It should not be a real
+ // problem because the default CgroupParent is usually owned by root so rootless users
+ // cannot access it.
+ // This check might be lifted in a future version of Podman.
+ // Check both that the cgroup or its parent is set to the default value (used by pods).
+ return cgroup != CgroupfsDefaultCgroupParent && filepath.Dir(cgroup) != CgroupfsDefaultCgroupParent
+}
+
// Get cgroup path in a format suitable for the OCI spec
func (c *Container) getOCICgroupPath() (string, error) {
unified, err := cgroups.IsCgroup2UnifiedMode()
@@ -2227,13 +2238,7 @@ func (c *Container) getOCICgroupPath() (string, error) {
case c.config.NoCgroups:
return "", nil
case (rootless.IsRootless() && (cgroupManager == config.CgroupfsCgroupsManager || !unified)):
- if c.config.CgroupParent == CgroupfsDefaultCgroupParent {
- // old versions of podman were setting the CgroupParent to CgroupfsDefaultCgroupParent
- // by default. Avoid breaking these versions and check whether the cgroup parent is
- // set to the default and in this case enable the old behavior. It should not be a real
- // problem because the default CgroupParent is usually owned by root so rootless users
- // cannot access it.
- // This check might be lifted in a future version of Podman.
+ if !isRootlessCgroupSet(c.config.CgroupParent) {
return "", nil
}
return c.config.CgroupParent, nil
diff --git a/libpod/pod_internal.go b/libpod/pod_internal.go
index 31b4ba443..e81bd7b16 100644
--- a/libpod/pod_internal.go
+++ b/libpod/pod_internal.go
@@ -7,6 +7,7 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v3/libpod/define"
+ "github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/storage/pkg/stringid"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -74,9 +75,11 @@ func (p *Pod) refresh() error {
}
p.state.CgroupPath = cgroupPath
case config.CgroupfsCgroupsManager:
- p.state.CgroupPath = filepath.Join(p.config.CgroupParent, p.ID())
+ if rootless.IsRootless() && isRootlessCgroupSet(p.config.CgroupParent) {
+ p.state.CgroupPath = filepath.Join(p.config.CgroupParent, p.ID())
- logrus.Debugf("setting pod cgroup to %s", p.state.CgroupPath)
+ logrus.Debugf("setting pod cgroup to %s", p.state.CgroupPath)
+ }
default:
return errors.Wrapf(define.ErrInvalidArg, "unknown cgroups manager %s specified", p.runtime.config.Engine.CgroupManager)
}
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 328f47c12..7d31e392f 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -295,7 +295,10 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
if podCgroup == "" {
return nil, errors.Wrapf(define.ErrInternal, "pod %s cgroup is not set", pod.ID())
}
- ctr.config.CgroupParent = podCgroup
+ canUseCgroup := !rootless.IsRootless() || isRootlessCgroupSet(podCgroup)
+ if canUseCgroup {
+ ctr.config.CgroupParent = podCgroup
+ }
} else if !rootless.IsRootless() {
ctr.config.CgroupParent = CgroupfsDefaultCgroupParent
}
diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go
index cf48a9453..4ede23cac 100644
--- a/libpod/runtime_pod_linux.go
+++ b/libpod/runtime_pod_linux.go
@@ -75,17 +75,20 @@ func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (_ *Po
// Check CGroup parent sanity, and set it if it was not set
switch r.config.Engine.CgroupManager {
case config.CgroupfsCgroupsManager:
- if pod.config.CgroupParent == "" {
- pod.config.CgroupParent = CgroupfsDefaultCgroupParent
- } else if strings.HasSuffix(path.Base(pod.config.CgroupParent), ".slice") {
- return nil, errors.Wrapf(define.ErrInvalidArg, "systemd slice received as cgroup parent when using cgroupfs")
- }
- // If we are set to use pod cgroups, set the cgroup parent that
- // all containers in the pod will share
- // No need to create it with cgroupfs - the first container to
- // launch should do it for us
- if pod.config.UsePodCgroup {
- pod.state.CgroupPath = filepath.Join(pod.config.CgroupParent, pod.ID())
+ canUseCgroup := !rootless.IsRootless() || isRootlessCgroupSet(pod.config.CgroupParent)
+ if canUseCgroup {
+ if pod.config.CgroupParent == "" {
+ pod.config.CgroupParent = CgroupfsDefaultCgroupParent
+ } else if strings.HasSuffix(path.Base(pod.config.CgroupParent), ".slice") {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "systemd slice received as cgroup parent when using cgroupfs")
+ }
+ // If we are set to use pod cgroups, set the cgroup parent that
+ // all containers in the pod will share
+ // No need to create it with cgroupfs - the first container to
+ // launch should do it for us
+ if pod.config.UsePodCgroup {
+ pod.state.CgroupPath = filepath.Join(pod.config.CgroupParent, pod.ID())
+ }
}
case config.SystemdCgroupsManager:
if pod.config.CgroupParent == "" {
diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go
index c0e5706a5..6acfcc1c8 100644
--- a/pkg/bindings/images/build.go
+++ b/pkg/bindings/images/build.go
@@ -28,6 +28,10 @@ import (
"github.com/sirupsen/logrus"
)
+var (
+ iidRegex = regexp.MustCompile(`^[0-9a-f]{12}`)
+)
+
// Build creates an image using a containerfile reference
func Build(ctx context.Context, containerFiles []string, options entities.BuildOptions) (*entities.BuildReport, error) {
params := url.Values{}
@@ -337,7 +341,6 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
}
dec := json.NewDecoder(body)
- re := regexp.MustCompile(`[0-9a-f]{12}`)
var id string
var mErr error
@@ -366,7 +369,7 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
switch {
case s.Stream != "":
stdout.Write([]byte(s.Stream))
- if re.Match([]byte(s.Stream)) {
+ if iidRegex.Match([]byte(s.Stream)) {
id = strings.TrimSuffix(s.Stream, "\n")
}
case s.Error != "":
diff --git a/pkg/bindings/images/build_test.go b/pkg/bindings/images/build_test.go
new file mode 100644
index 000000000..e4035d5f8
--- /dev/null
+++ b/pkg/bindings/images/build_test.go
@@ -0,0 +1,17 @@
+package images
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestBuildMatchIID(t *testing.T) {
+ assert.True(t, iidRegex.MatchString("a883dafc480d466ee04e0d6da986bd78eb1fdd2178d04693723da3a8f95d42f4"))
+ assert.True(t, iidRegex.MatchString("3da3a8f95d42"))
+ assert.False(t, iidRegex.MatchString("3da3"))
+}
+
+func TestBuildNotMatchStatusMessage(t *testing.T) {
+ assert.False(t, iidRegex.MatchString("Copying config a883dafc480d466ee04e0d6da986bd78eb1fdd2178d04693723da3a8f95d42f4"))
+}
diff --git a/pkg/channel/writer.go b/pkg/channel/writer.go
index 28c9d7de5..ecb68e906 100644
--- a/pkg/channel/writer.go
+++ b/pkg/channel/writer.go
@@ -1,7 +1,6 @@
package channel
import (
- "fmt"
"io"
"sync"
@@ -34,20 +33,17 @@ func (w *writeCloser) Chan() <-chan []byte {
// Write method for WriteCloser
func (w *writeCloser) Write(b []byte) (bLen int, err error) {
- // https://github.com/containers/podman/issues/7896
- // when podman-remote pull image, if it was killed, the server will panic: send on closed channel
- // so handle it
- defer func() {
- if rErr := recover(); rErr != nil {
- err = fmt.Errorf("%s", rErr)
- }
- }()
- if w == nil || w.ch == nil {
+ if w == nil {
return 0, errors.New("use channel.NewWriter() to initialize a WriteCloser")
}
w.mux.Lock()
defer w.mux.Unlock()
+
+ if w.ch == nil {
+ return 0, errors.New("the channel is closed for Write")
+ }
+
buf := make([]byte, len(b))
copy(buf, b)
w.ch <- buf
@@ -57,6 +53,10 @@ func (w *writeCloser) Write(b []byte) (bLen int, err error) {
// Close method for WriteCloser
func (w *writeCloser) Close() error {
+ w.mux.Lock()
+ defer w.mux.Unlock()
+
close(w.ch)
+ w.ch = nil
return nil
}
diff --git a/test/compose/ipam_set_ip/docker-compose.yml b/test/compose/ipam_set_ip/docker-compose.yml
new file mode 100644
index 000000000..d220c02c0
--- /dev/null
+++ b/test/compose/ipam_set_ip/docker-compose.yml
@@ -0,0 +1,17 @@
+version: "3.2"
+services:
+ test:
+ image: alpine
+ networks:
+ net1:
+ ipv4_address: 10.123.0.253
+ tty: true
+ command: ["top"]
+
+networks:
+ net1:
+ driver: bridge
+ ipam:
+ driver: default
+ config:
+ - subnet: 10.123.0.0/24
diff --git a/test/compose/ipam_set_ip/tests.sh b/test/compose/ipam_set_ip/tests.sh
new file mode 100644
index 000000000..ecaf3167e
--- /dev/null
+++ b/test/compose/ipam_set_ip/tests.sh
@@ -0,0 +1,4 @@
+# -*- bash -*-
+
+podman container inspect ipam_set_ip_test_1 --format '{{ .NetworkSettings.Networks.ipam_set_ip_net1.IPAddress }}'
+like "$output" "10.123.0.253" "$testname : ip address is set"