diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/api/handlers/types.go | 27 | ||||
-rw-r--r-- | pkg/domain/infra/abi/containers.go | 21 | ||||
-rw-r--r-- | pkg/domain/infra/abi/system.go | 33 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/containers.go | 12 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/pods.go | 5 | ||||
-rw-r--r-- | pkg/parallel/parallel.go | 44 | ||||
-rw-r--r-- | pkg/parallel/parallel_linux.go | 57 | ||||
-rw-r--r-- | pkg/specgen/config_linux.go | 93 | ||||
-rw-r--r-- | pkg/specgen/container_validate.go | 2 | ||||
-rw-r--r-- | pkg/specgen/generate/container.go | 2 | ||||
-rw-r--r-- | pkg/specgen/generate/container_create.go | 40 | ||||
-rw-r--r-- | pkg/specgen/generate/namespaces.go | 4 | ||||
-rw-r--r-- | pkg/specgen/generate/oci.go | 4 |
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 |