summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/api/handlers/types.go27
-rw-r--r--pkg/domain/infra/abi/containers.go21
-rw-r--r--pkg/domain/infra/abi/system.go33
-rw-r--r--pkg/domain/infra/tunnel/containers.go12
-rw-r--r--pkg/domain/infra/tunnel/pods.go5
-rw-r--r--pkg/parallel/parallel.go44
-rw-r--r--pkg/parallel/parallel_linux.go57
-rw-r--r--pkg/specgen/config_linux.go93
-rw-r--r--pkg/specgen/container_validate.go2
-rw-r--r--pkg/specgen/generate/container.go2
-rw-r--r--pkg/specgen/generate/container_create.go40
-rw-r--r--pkg/specgen/generate/namespaces.go4
-rw-r--r--pkg/specgen/generate/oci.go4
13 files changed, 222 insertions, 122 deletions
diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go
index aa3d0fe91..79aeff2f8 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -334,16 +334,25 @@ func ImageDataToImageInspect(ctx context.Context, l *libpodImage.Image) (*ImageI
func portsToPortSet(input map[string]struct{}) (nat.PortSet, error) {
ports := make(nat.PortSet)
for k := range input {
- npTCP, err := nat.NewPort("tcp", k)
- if err != nil {
- return nil, errors.Wrapf(err, "unable to create tcp port from %s", k)
- }
- npUDP, err := nat.NewPort("udp", k)
- if err != nil {
- return nil, errors.Wrapf(err, "unable to create udp port from %s", k)
+ proto, port := nat.SplitProtoPort(k)
+ switch proto {
+ // See the OCI image spec for details:
+ // https://github.com/opencontainers/image-spec/blob/e562b04403929d582d449ae5386ff79dd7961a11/config.md#properties
+ case "tcp", "":
+ p, err := nat.NewPort("tcp", port)
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to create tcp port from %s", k)
+ }
+ ports[p] = struct{}{}
+ case "udp":
+ p, err := nat.NewPort("udp", port)
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to create tcp port from %s", k)
+ }
+ ports[p] = struct{}{}
+ default:
+ return nil, errors.Errorf("invalid port proto %q in %q", proto, k)
}
- ports[npTCP] = struct{}{}
- ports[npUDP] = struct{}{}
}
return ports, nil
}
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index 19232eff1..eb45d4630 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -23,6 +23,7 @@ import (
"github.com/containers/libpod/pkg/checkpoint"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/domain/infra/abi/terminal"
+ "github.com/containers/libpod/pkg/parallel"
"github.com/containers/libpod/pkg/ps"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/signal"
@@ -321,21 +322,25 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
return reports, nil
}
- for _, c := range ctrs {
- report := entities.RmReport{Id: c.ID()}
+ errMap, err := parallel.ParallelContainerOp(ctx, ctrs, func(c *libpod.Container) error {
err := ic.Libpod.RemoveContainer(ctx, c, options.Force, options.Volumes)
if err != nil {
if options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr {
logrus.Debugf("Ignoring error (--allow-missing): %v", err)
- reports = append(reports, &report)
- continue
+ return nil
}
logrus.Debugf("Failed to remove container %s: %s", c.ID(), err.Error())
- report.Err = err
- reports = append(reports, &report)
- continue
}
- reports = append(reports, &report)
+ return err
+ })
+ if err != nil {
+ return nil, err
+ }
+ for ctr, err := range errMap {
+ report := new(entities.RmReport)
+ report.Id = ctr.ID()
+ report.Err = err
+ reports = append(reports, report)
}
return reports, nil
}
diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go
index 52dfaba7d..9b538b301 100644
--- a/pkg/domain/infra/abi/system.go
+++ b/pkg/domain/infra/abi/system.go
@@ -25,7 +25,38 @@ import (
)
func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) {
- return ic.Libpod.Info()
+ info, err := ic.Libpod.Info()
+ if err != nil {
+ return nil, err
+ }
+ xdg, err := util.GetRuntimeDir()
+ if err != nil {
+ return nil, err
+ }
+ if len(xdg) == 0 {
+ // If no xdg is returned, assume root socket
+ xdg = "/run"
+ }
+
+ // Glue the socket path together
+ socketPath := filepath.Join(xdg, "podman", "podman.sock")
+ rs := define.RemoteSocket{
+ Path: socketPath,
+ Exists: false,
+ }
+
+ // Check if the socket exists
+ if fi, err := os.Stat(socketPath); err == nil {
+ if fi.Mode()&os.ModeSocket != 0 {
+ rs.Exists = true
+ }
+ }
+ // TODO
+ // it was suggested future versions of this could perform
+ // a ping on the socket for greater confidence the socket is
+ // actually active.
+ info.Host.RemoteSocket = &rs
+ return info, err
}
func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) error {
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index 97b98eec2..36b7bf535 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -86,8 +86,16 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin
var (
reports []*entities.StopReport
)
+ for _, cidFile := range options.CIDFiles {
+ content, err := ioutil.ReadFile(cidFile)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error reading CIDFile %s", cidFile)
+ }
+ id := strings.Split(string(content), "\n")[0]
+ namesOrIds = append(namesOrIds, id)
+ }
ctrs, err := getContainersByContext(ic.ClientCxt, options.All, namesOrIds)
- if err != nil {
+ if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) {
return nil, err
}
for _, c := range ctrs {
@@ -172,7 +180,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
namesOrIds = append(namesOrIds, id)
}
ctrs, err := getContainersByContext(ic.ClientCxt, options.All, namesOrIds)
- if err != nil {
+ if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) {
return nil, err
}
// TODO there is no endpoint for container eviction. Need to discuss
diff --git a/pkg/domain/infra/tunnel/pods.go b/pkg/domain/infra/tunnel/pods.go
index c193c6752..b93c48aab 100644
--- a/pkg/domain/infra/tunnel/pods.go
+++ b/pkg/domain/infra/tunnel/pods.go
@@ -3,6 +3,7 @@ package tunnel
import (
"context"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/bindings/pods"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/specgen"
@@ -89,7 +90,7 @@ func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, opt
timeout int = -1
)
foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds)
- if err != nil {
+ if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchPod) {
return nil, err
}
if options.Timeout != -1 {
@@ -155,7 +156,7 @@ func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, op
func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, options entities.PodRmOptions) ([]*entities.PodRmReport, error) {
var reports []*entities.PodRmReport
foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds)
- if err != nil {
+ if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchPod) {
return nil, err
}
for _, p := range foundPods {
diff --git a/pkg/parallel/parallel.go b/pkg/parallel/parallel.go
new file mode 100644
index 000000000..c9e4da50d
--- /dev/null
+++ b/pkg/parallel/parallel.go
@@ -0,0 +1,44 @@
+package parallel
+
+import (
+ "sync"
+
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/sync/semaphore"
+)
+
+var (
+ // Maximum number of jobs that will be used.
+ // Set a low, but non-zero, default. We'll be overriding it by default
+ // anyways.
+ numThreads uint = 8
+ // Semaphore to control thread creation and ensure numThreads is
+ // respected.
+ jobControl *semaphore.Weighted
+ // Lock to control changing the semaphore - we don't want to do it
+ // while anyone is using it.
+ jobControlLock sync.RWMutex
+)
+
+// SetMaxThreads sets the number of threads that will be used for parallel jobs.
+func SetMaxThreads(threads uint) error {
+ if threads == 0 {
+ return errors.New("must give a non-zero number of threads to execute with")
+ }
+
+ jobControlLock.Lock()
+ defer jobControlLock.Unlock()
+
+ numThreads = threads
+ jobControl = semaphore.NewWeighted(int64(threads))
+ logrus.Infof("Setting parallel job count to %d", threads)
+
+ return nil
+}
+
+// GetMaxThreads returns the current number of threads that will be used for
+// parallel jobs.
+func GetMaxThreads() uint {
+ return numThreads
+}
diff --git a/pkg/parallel/parallel_linux.go b/pkg/parallel/parallel_linux.go
new file mode 100644
index 000000000..e3f086c0e
--- /dev/null
+++ b/pkg/parallel/parallel_linux.go
@@ -0,0 +1,57 @@
+package parallel
+
+import (
+ "context"
+ "sync"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+// ParallelContainerOp performs the given function on the given set of
+// containers, using a number of parallel threads.
+// If no error is returned, each container specified in ctrs will have an entry
+// in the resulting map; containers with no error will be set to nil.
+func ParallelContainerOp(ctx context.Context, ctrs []*libpod.Container, applyFunc func(*libpod.Container) error) (map[*libpod.Container]error, error) {
+ jobControlLock.RLock()
+ defer jobControlLock.RUnlock()
+
+ // We could use a sync.Map but given Go's lack of generic I'd rather
+ // just use a lock on a normal map...
+ // The expectation is that most of the time is spent in applyFunc
+ // anyways.
+ var (
+ errMap map[*libpod.Container]error = make(map[*libpod.Container]error)
+ errLock sync.Mutex
+ allDone sync.WaitGroup
+ )
+
+ for _, ctr := range ctrs {
+ // Block until a thread is available
+ if err := jobControl.Acquire(ctx, 1); err != nil {
+ return nil, errors.Wrapf(err, "error acquiring job control semaphore")
+ }
+
+ allDone.Add(1)
+
+ c := ctr
+ go func() {
+ logrus.Debugf("Launching job on container %s", c.ID())
+
+ err := applyFunc(c)
+ errLock.Lock()
+ errMap[c] = err
+ errLock.Unlock()
+
+ allDone.Done()
+ jobControl.Release(1)
+ }()
+ }
+
+ allDone.Wait()
+
+ return errMap, nil
+}
+
+// TODO: Add an Enqueue() function that returns a promise
diff --git a/pkg/specgen/config_linux.go b/pkg/specgen/config_linux.go
deleted file mode 100644
index 82a371492..000000000
--- a/pkg/specgen/config_linux.go
+++ /dev/null
@@ -1,93 +0,0 @@
-package specgen
-
-//func createBlockIO() (*spec.LinuxBlockIO, error) {
-// var ret *spec.LinuxBlockIO
-// bio := &spec.LinuxBlockIO{}
-// if c.Resources.BlkioWeight > 0 {
-// ret = bio
-// bio.Weight = &c.Resources.BlkioWeight
-// }
-// if len(c.Resources.BlkioWeightDevice) > 0 {
-// var lwds []spec.LinuxWeightDevice
-// ret = bio
-// for _, i := range c.Resources.BlkioWeightDevice {
-// wd, err := ValidateweightDevice(i)
-// if err != nil {
-// return ret, errors.Wrapf(err, "invalid values for blkio-weight-device")
-// }
-// wdStat, err := GetStatFromPath(wd.Path)
-// if err != nil {
-// return ret, errors.Wrapf(err, "error getting stat from path %q", wd.Path)
-// }
-// lwd := spec.LinuxWeightDevice{
-// Weight: &wd.Weight,
-// }
-// lwd.Major = int64(unix.Major(wdStat.Rdev))
-// lwd.Minor = int64(unix.Minor(wdStat.Rdev))
-// lwds = append(lwds, lwd)
-// }
-// bio.WeightDevice = lwds
-// }
-// if len(c.Resources.DeviceReadBps) > 0 {
-// ret = bio
-// readBps, err := makeThrottleArray(c.Resources.DeviceReadBps, bps)
-// if err != nil {
-// return ret, err
-// }
-// bio.ThrottleReadBpsDevice = readBps
-// }
-// if len(c.Resources.DeviceWriteBps) > 0 {
-// ret = bio
-// writeBpds, err := makeThrottleArray(c.Resources.DeviceWriteBps, bps)
-// if err != nil {
-// return ret, err
-// }
-// bio.ThrottleWriteBpsDevice = writeBpds
-// }
-// if len(c.Resources.DeviceReadIOps) > 0 {
-// ret = bio
-// readIOps, err := makeThrottleArray(c.Resources.DeviceReadIOps, iops)
-// if err != nil {
-// return ret, err
-// }
-// bio.ThrottleReadIOPSDevice = readIOps
-// }
-// if len(c.Resources.DeviceWriteIOps) > 0 {
-// ret = bio
-// writeIOps, err := makeThrottleArray(c.Resources.DeviceWriteIOps, iops)
-// if err != nil {
-// return ret, err
-// }
-// bio.ThrottleWriteIOPSDevice = writeIOps
-// }
-// return ret, nil
-//}
-
-//func makeThrottleArray(throttleInput []string, rateType int) ([]spec.LinuxThrottleDevice, error) {
-// var (
-// ltds []spec.LinuxThrottleDevice
-// t *throttleDevice
-// err error
-// )
-// for _, i := range throttleInput {
-// if rateType == bps {
-// t, err = validateBpsDevice(i)
-// } else {
-// t, err = validateIOpsDevice(i)
-// }
-// if err != nil {
-// return []spec.LinuxThrottleDevice{}, err
-// }
-// ltdStat, err := GetStatFromPath(t.path)
-// if err != nil {
-// return ltds, errors.Wrapf(err, "error getting stat from path %q", t.path)
-// }
-// ltd := spec.LinuxThrottleDevice{
-// Rate: t.rate,
-// }
-// ltd.Major = int64(unix.Major(ltdStat.Rdev))
-// ltd.Minor = int64(unix.Minor(ltdStat.Rdev))
-// ltds = append(ltds, ltd)
-// }
-// return ltds, nil
-//}
diff --git a/pkg/specgen/container_validate.go b/pkg/specgen/container_validate.go
index 75da38c0e..2c5891f9a 100644
--- a/pkg/specgen/container_validate.go
+++ b/pkg/specgen/container_validate.go
@@ -38,7 +38,7 @@ func (s *SpecGenerator) Validate() error {
}
// systemd values must be true, false, or always
if len(s.ContainerBasicConfig.Systemd) > 0 && !util.StringInSlice(strings.ToLower(s.ContainerBasicConfig.Systemd), SystemDValues) {
- return errors.Wrapf(ErrInvalidSpecConfig, "SystemD values must be one of %s", strings.Join(SystemDValues, ","))
+ return errors.Wrapf(ErrInvalidSpecConfig, "--systemd values must be one of %q", strings.Join(SystemDValues, ", "))
}
//
diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go
index a217125f4..3d70571d5 100644
--- a/pkg/specgen/generate/container.go
+++ b/pkg/specgen/generate/container.go
@@ -194,7 +194,7 @@ func finishThrottleDevices(s *specgen.SpecGenerator) error {
s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice, v)
}
}
- if iops := s.ThrottleWriteBpsDevice; len(iops) > 0 {
+ if iops := s.ThrottleWriteIOPSDevice; len(iops) > 0 {
for k, v := range iops {
statT := unix.Stat_t{}
if err := unix.Stat(k, &statT); err != nil {
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index 7ddfed339..74ae848af 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -3,12 +3,14 @@ package generate
import (
"context"
"os"
+ "path/filepath"
"github.com/containers/common/pkg/config"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/specgen"
+ "github.com/containers/libpod/pkg/util"
"github.com/containers/storage"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -114,7 +116,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
}
options = append(options, libpod.WithExitCommand(exitCommandArgs))
- runtimeSpec, err := SpecGenToOCI(ctx, s, rt, rtc, newImage, finalMounts)
+ runtimeSpec, err := SpecGenToOCI(ctx, s, rt, rtc, newImage, finalMounts, pod)
if err != nil {
return nil, err
}
@@ -128,7 +130,41 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
if s.Stdin {
options = append(options, libpod.WithStdin())
}
- if len(s.Systemd) > 0 {
+
+ useSystemd := false
+ switch s.Systemd {
+ case "always":
+ useSystemd = true
+ case "false":
+ break
+ case "", "true":
+ command := s.Command
+ if len(command) == 0 {
+ command, err = img.Cmd(ctx)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if len(command) > 0 {
+ if command[0] == "/usr/sbin/init" || command[0] == "/sbin/init" || (filepath.Base(command[0]) == "systemd") {
+ useSystemd = true
+ }
+ }
+ default:
+ return nil, errors.Wrapf(err, "invalid value %q systemd option requires 'true, false, always'", s.Systemd)
+ }
+ if useSystemd {
+ // is StopSignal was not set by the user then set it to systemd
+ // expected StopSigal
+ if s.StopSignal == nil {
+ stopSignal, err := util.ParseSignal("RTMIN+3")
+ if err != nil {
+ return nil, errors.Wrapf(err, "error parsing systemd signal")
+ }
+ s.StopSignal = &stopSignal
+ }
+
options = append(options, libpod.WithSystemd())
}
if len(s.Name) > 0 {
diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go
index 138d9e0cd..ffa96a5cf 100644
--- a/pkg/specgen/generate/namespaces.go
+++ b/pkg/specgen/generate/namespaces.go
@@ -265,7 +265,7 @@ func GenerateNamespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt
return toReturn, nil
}
-func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt *libpod.Runtime) error {
+func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt *libpod.Runtime, pod *libpod.Pod) error {
// PID
switch s.PidNS.NSMode {
case specgen.Path:
@@ -326,6 +326,8 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt
hostname := s.Hostname
if hostname == "" {
switch {
+ case s.UtsNS.NSMode == specgen.FromPod:
+ hostname = pod.Hostname()
case s.UtsNS.NSMode == specgen.FromContainer:
utsCtr, err := rt.LookupContainer(s.UtsNS.Value)
if err != nil {
diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go
index 11b18e2d0..266abd28d 100644
--- a/pkg/specgen/generate/oci.go
+++ b/pkg/specgen/generate/oci.go
@@ -118,7 +118,7 @@ func makeCommand(ctx context.Context, s *specgen.SpecGenerator, img *image.Image
return finalCommand, nil
}
-func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, newImage *image.Image, mounts []spec.Mount) (*spec.Spec, error) {
+func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, newImage *image.Image, mounts []spec.Mount, pod *libpod.Pod) (*spec.Spec, error) {
var (
inUserNS bool
)
@@ -300,7 +300,7 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt
}
// NAMESPACES
- if err := specConfigureNamespaces(s, &g, rt); err != nil {
+ if err := specConfigureNamespaces(s, &g, rt, pod); err != nil {
return nil, err
}
configSpec := g.Config