Merge pull request #14591 from mheon/bump_411

Bump to v4.1.1
This commit is contained in:
OpenShift Merge Robot 2022-06-15 03:13:37 -04:00 committed by GitHub
commit 932822ab4b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
218 changed files with 3130 additions and 2756 deletions

View file

@ -44,7 +44,6 @@ linters:
- goconst
- gocyclo
- lll
- unconvert
- gosec
- maligned
- gomoddirectives

View file

@ -134,7 +134,8 @@ ifeq ($(GOBIN),)
GOBIN := $(FIRST_GOPATH)/bin
endif
export PATH := $(PATH):$(GOBIN):$(CURDIR)/hack
# This must never include the 'hack' directory
export PATH := $(PATH):$(GOBIN)
GOMD2MAN ?= $(shell command -v go-md2man || echo '$(GOBIN)/go-md2man')
@ -571,7 +572,7 @@ remoteintegration: test-binaries ginkgo-remote
.PHONY: localbenchmarks
localbenchmarks: test-binaries
ACK_GINKGO_RC=true $(GOBIN)/ginkgo \
PATH=$(PATH):$(shell pwd)/hack ACK_GINKGO_RC=true $(GOBIN)/ginkgo \
-focus "Podman Benchmark Suite" \
-tags "$(BUILDTAGS) benchmarks" -noColor \
-noisySkippings=false -noisyPendings=false \

View file

@ -1,5 +1,50 @@
# Release Notes
## 4.1.1
### Features
- Podman machine events are now supported on Windows.
### Changes
- The output of the `podman load` command now mirrors that of `docker load`.
### Bugfixes
- Fixed a bug where the `podman play kube` command could panic if the `--log-opt` option was used ([#13356](https://github.com/containers/podman/issues/13356)).
- Fixed a bug where Podman could, under some circumstances, fail to parse container cgroup paths ([#14146](https://github.com/containers/podman/issues/14146)).
- Fixed a bug where containers created with the `--sdnotify=conmon` option could send `MAINPID` twice.
- Fixed a bug where the `podman info` command could fail when run inside an LXC container.
- Fixed a bug where the pause image of a Pod with a custom ID mappings could not be built ([BZ 2083997](https://bugzilla.redhat.com/show_bug.cgi?id=2083997)).
- Fixed a bug where, on `podman machine` VMs on Windows, containers could be prematurely terminated with API forwarding was not running ([#13965](https://github.com/containers/podman/issues/13965)).
- Fixed a bug where removing a container with a zombie exec session would fail the first time, but succeed for subsequent calls ([#14252](https://github.com/containers/podman/issues/14252)).
- Fixed a bug where a dangling ID in the database could render Podman unusable.
- Fixed a bug where containers with memory limits could not be created when Podman was run in a root cgroup ([#14236](https://github.com/containers/podman/issues/14236)).
- Fixed a bug where the `--security-opt` option to `podman run` and `podman create` did not support the `no-new-privileges:true` and `no-new-privileges:false` options (the only supported separator was `=`, not `:`) ([#14133](https://github.com/containers/podman/issues/14133)).
- Fixed a bug where containers that did not create a network namespace (e.g. containers created with `--network none` or `--network ns:/path/to/ns`) could not be restored from checkpoints ([#14389](https://github.com/containers/podman/issues/14389)).
- Fixed a bug where `podman-restart.service` could, if enabled, cause system shutdown to hang for 90 seconds ([#14434](https://github.com/containers/podman/issues/14434)).
- Fixed a bug where the `podman stats` command would, when run as root on a container that had the `podman network disconnect` command run on it or that set a custom network interface name, return an error ([#13824](https://github.com/containers/podman/issues/13824)).
- Fixed a bug where the remote Podman client's `podman pod create` command would error when the `--uidmap` option was used ([#14233](https://github.com/containers/podman/issues/14233)).
- Fixed a bug where cleaning up systemd units and timers related to healthchecks was subject to race conditions and could fail.
- Fixed a bug where the default network mode of containers created by the remote Podman client was assigned by the client, not the server ([#14368](https://github.com/containers/podman/issues/14368)).
- Fixed a bug where containers joining a pod that was created with `--network=host` would receive a private network namespace ([#13763](https://github.com/containers/podman/issues/13763)).
- Fixed a bug where `podman machine rm --force` would remove files related to the VM before stopping it, causing issues if removal was interrupted.
- Fixed a bug where `podman logs` would omit the last line of a container's logs if the log did not end in a newline ([#14458](https://github.com/containers/podman/issues/14458)).
- Fixed a bug where network cleanup was nonfunctional for containers which used a custom user namespace and were initialized via API ([#14465](https://github.com/containers/podman/issues/14465)).
- Fixed a bug where some options (including volumes) for containers that joined pods were overwritten by the infra container ([#14454](https://github.com/containers/podman/issues/14454)).
- Fixed a bug where the `--file-locks` option to `podman container restore` was ignored, such that file locks checkpointed by `podman container checkpoint --file-locks` were not restored.
- Fixed a bug where signals sent to a Podman attach session with `--sig-proxy` enabled at the exact moment the container that was attached to exited could cause error messages to be printed.
- Fixed a bug where running the `podman machine start` command more than once (simultaneously) on the same machine would cause errors.
- Fixed a bug where the `podman stats` command could not be run on containers that were not running (it now reports all-0s statistics for Docker compatibility) ([#14498](https://github.com/containers/podman/issues/14498)).
### API
- Fixed a bug where images pulled from a private registry could not be accessed via shortname using the Compat API endpoints ([#14291](https://github.com/containers/podman/issues/14291)).
- Fixed a bug where the Compat Delete API for Images would return an incorrect status code (500) when attempting to delete images that are in use ([#14208](https://github.com/containers/podman/issues/14208)).
- Fixed a bug where the Compat Build API for Images would include the build's `STDERR` output even if the `quiet` parameter was true.
- Fixed a bug where the Libpod Play Kube API would overwrite any log driver specified by query parameter with the system default.
### Misc
- The `podman auto-update` command now creates an event when it is run.
- Error messages printed when Podman's temporary files directory is not writable have been improved.
- Units for memory limits accepted by Podman commands were incorrectly stated by documentation as megabytes, instead of mebibytes; this has now been corrected ([#14187](https://github.com/containers/podman/issues/14187)).
## 4.1.0
### Features
- Podman now supports Docker Compose v2.2 and higher ([#11822](https://github.com/containers/podman/issues/11822)). Please note that it may be necessary to disable the use of Buildkit by setting the environment variable `DOCKER_BUILDKIT=0`.

View file

@ -12,7 +12,7 @@ import (
"github.com/spf13/cobra"
)
const sizeWithUnitFormat = "(format: `<number>[<unit>]`, where unit = b (bytes), k (kilobytes), m (megabytes), or g (gigabytes))"
const sizeWithUnitFormat = "(format: `<number>[<unit>]`, where unit = b (bytes), k (kibibytes), m (mebibytes), or g (gibibytes))"
var containerConfig = registry.PodmanConfig()

View file

@ -1,472 +1,9 @@
package common
import (
"fmt"
"net"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/containers/common/libnetwork/types"
"github.com/containers/common/pkg/cgroups"
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/api/handlers"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/rootless"
"github.com/containers/podman/v4/pkg/specgen"
"github.com/docker/docker/api/types/mount"
"github.com/pkg/errors"
)
func stringMaptoArray(m map[string]string) []string {
a := make([]string, 0, len(m))
for k, v := range m {
a = append(a, fmt.Sprintf("%s=%s", k, v))
}
return a
}
// ContainerCreateToContainerCLIOpts converts a compat input struct to cliopts so it can be converted to
// a specgen spec.
func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *config.Config) (*entities.ContainerCreateOptions, []string, error) {
var (
capAdd []string
cappDrop []string
entrypoint *string
init bool
specPorts []types.PortMapping
)
if cc.HostConfig.Init != nil {
init = *cc.HostConfig.Init
}
// Iterate devices and convert back to string
devices := make([]string, 0, len(cc.HostConfig.Devices))
for _, dev := range cc.HostConfig.Devices {
devices = append(devices, fmt.Sprintf("%s:%s:%s", dev.PathOnHost, dev.PathInContainer, dev.CgroupPermissions))
}
// iterate blkreaddevicebps
readBps := make([]string, 0, len(cc.HostConfig.BlkioDeviceReadBps))
for _, dev := range cc.HostConfig.BlkioDeviceReadBps {
readBps = append(readBps, dev.String())
}
// iterate blkreaddeviceiops
readIops := make([]string, 0, len(cc.HostConfig.BlkioDeviceReadIOps))
for _, dev := range cc.HostConfig.BlkioDeviceReadIOps {
readIops = append(readIops, dev.String())
}
// iterate blkwritedevicebps
writeBps := make([]string, 0, len(cc.HostConfig.BlkioDeviceWriteBps))
for _, dev := range cc.HostConfig.BlkioDeviceWriteBps {
writeBps = append(writeBps, dev.String())
}
// iterate blkwritedeviceiops
writeIops := make([]string, 0, len(cc.HostConfig.BlkioDeviceWriteIOps))
for _, dev := range cc.HostConfig.BlkioDeviceWriteIOps {
writeIops = append(writeIops, dev.String())
}
// entrypoint
// can be a string or slice. if it is a slice, we need to
// marshall it to json; otherwise it should just be the string
// value
if len(cc.Config.Entrypoint) > 0 {
entrypoint = &cc.Config.Entrypoint[0]
if len(cc.Config.Entrypoint) > 1 {
b, err := json.Marshal(cc.Config.Entrypoint)
if err != nil {
return nil, nil, err
}
var jsonString = string(b)
entrypoint = &jsonString
}
}
// expose ports
expose := make([]string, 0, len(cc.Config.ExposedPorts))
for p := range cc.Config.ExposedPorts {
expose = append(expose, fmt.Sprintf("%s/%s", p.Port(), p.Proto()))
}
// mounts type=tmpfs/bind,source=...,target=...=,opt=val
volSources := make(map[string]bool)
volDestinations := make(map[string]bool)
mounts := make([]string, 0, len(cc.HostConfig.Mounts))
var builder strings.Builder
for _, m := range cc.HostConfig.Mounts {
addField(&builder, "type", string(m.Type))
addField(&builder, "source", m.Source)
addField(&builder, "target", m.Target)
// Store source/dest so we don't add duplicates if a volume is
// also mentioned in cc.Volumes.
// Which Docker Compose v2.0 does, for unclear reasons...
volSources[m.Source] = true
volDestinations[m.Target] = true
if m.ReadOnly {
addField(&builder, "ro", "true")
}
addField(&builder, "consistency", string(m.Consistency))
// Map any specialized mount options that intersect between *Options and cli options
switch m.Type {
case mount.TypeBind:
if m.BindOptions != nil {
addField(&builder, "bind-propagation", string(m.BindOptions.Propagation))
addField(&builder, "bind-nonrecursive", strconv.FormatBool(m.BindOptions.NonRecursive))
}
case mount.TypeTmpfs:
if m.TmpfsOptions != nil {
addField(&builder, "tmpfs-size", strconv.FormatInt(m.TmpfsOptions.SizeBytes, 10))
addField(&builder, "tmpfs-mode", strconv.FormatUint(uint64(m.TmpfsOptions.Mode), 8))
}
case mount.TypeVolume:
// All current VolumeOpts are handled above
// See vendor/github.com/containers/common/pkg/parse/parse.go:ValidateVolumeOpts()
}
mounts = append(mounts, builder.String())
builder.Reset()
}
// dns
dns := make([]net.IP, 0, len(cc.HostConfig.DNS))
for _, d := range cc.HostConfig.DNS {
dns = append(dns, net.ParseIP(d))
}
// publish
for port, pbs := range cc.HostConfig.PortBindings {
for _, pb := range pbs {
var hostport int
var err error
if pb.HostPort != "" {
hostport, err = strconv.Atoi(pb.HostPort)
}
if err != nil {
return nil, nil, err
}
tmpPort := types.PortMapping{
HostIP: pb.HostIP,
ContainerPort: uint16(port.Int()),
HostPort: uint16(hostport),
Range: 0,
Protocol: port.Proto(),
}
specPorts = append(specPorts, tmpPort)
}
}
// special case for NetworkMode, the podman default is slirp4netns for
// rootless but for better docker compat we want bridge.
netmode := string(cc.HostConfig.NetworkMode)
if netmode == "" || netmode == "default" {
netmode = "bridge"
}
nsmode, networks, netOpts, err := specgen.ParseNetworkFlag([]string{netmode})
if err != nil {
return nil, nil, err
}
// network
// Note: we cannot emulate compat exactly here. we only allow specifics of networks to be
// defined when there is only one network.
netInfo := entities.NetOptions{
AddHosts: cc.HostConfig.ExtraHosts,
DNSOptions: cc.HostConfig.DNSOptions,
DNSSearch: cc.HostConfig.DNSSearch,
DNSServers: dns,
Network: nsmode,
PublishPorts: specPorts,
NetworkOptions: netOpts,
NoHosts: rtc.Containers.NoHosts,
}
// network names
switch {
case len(cc.NetworkingConfig.EndpointsConfig) > 0:
endpointsConfig := cc.NetworkingConfig.EndpointsConfig
networks := make(map[string]types.PerNetworkOptions, len(endpointsConfig))
for netName, endpoint := range endpointsConfig {
netOpts := types.PerNetworkOptions{}
if endpoint != nil {
netOpts.Aliases = endpoint.Aliases
// if IP address is provided
if len(endpoint.IPAddress) > 0 {
staticIP := net.ParseIP(endpoint.IPAddress)
if staticIP == nil {
return nil, nil, errors.Errorf("failed to parse the ip address %q", endpoint.IPAddress)
}
netOpts.StaticIPs = append(netOpts.StaticIPs, staticIP)
}
if endpoint.IPAMConfig != nil {
// if IPAMConfig.IPv4Address is provided
if len(endpoint.IPAMConfig.IPv4Address) > 0 {
staticIP := net.ParseIP(endpoint.IPAMConfig.IPv4Address)
if staticIP == nil {
return nil, nil, errors.Errorf("failed to parse the ipv4 address %q", endpoint.IPAMConfig.IPv4Address)
}
netOpts.StaticIPs = append(netOpts.StaticIPs, staticIP)
}
// if IPAMConfig.IPv6Address is provided
if len(endpoint.IPAMConfig.IPv6Address) > 0 {
staticIP := net.ParseIP(endpoint.IPAMConfig.IPv6Address)
if staticIP == nil {
return nil, nil, errors.Errorf("failed to parse the ipv6 address %q", endpoint.IPAMConfig.IPv6Address)
}
netOpts.StaticIPs = append(netOpts.StaticIPs, staticIP)
}
}
// If MAC address is provided
if len(endpoint.MacAddress) > 0 {
staticMac, err := net.ParseMAC(endpoint.MacAddress)
if err != nil {
return nil, nil, errors.Errorf("failed to parse the mac address %q", endpoint.MacAddress)
}
netOpts.StaticMAC = types.HardwareAddr(staticMac)
}
}
networks[netName] = netOpts
}
netInfo.Networks = networks
case len(cc.HostConfig.NetworkMode) > 0:
netInfo.Networks = networks
}
parsedTmp := make([]string, 0, len(cc.HostConfig.Tmpfs))
for path, options := range cc.HostConfig.Tmpfs {
finalString := path
if options != "" {
finalString += ":" + options
}
parsedTmp = append(parsedTmp, finalString)
}
// Note: several options here are marked as "don't need". this is based
// on speculation by Matt and I. We think that these come into play later
// like with start. We believe this is just a difference in podman/compat
cliOpts := entities.ContainerCreateOptions{
// Attach: nil, // don't need?
Authfile: "",
CapAdd: append(capAdd, cc.HostConfig.CapAdd...),
CapDrop: append(cappDrop, cc.HostConfig.CapDrop...),
CgroupParent: cc.HostConfig.CgroupParent,
CIDFile: cc.HostConfig.ContainerIDFile,
CPUPeriod: uint64(cc.HostConfig.CPUPeriod),
CPUQuota: cc.HostConfig.CPUQuota,
CPURTPeriod: uint64(cc.HostConfig.CPURealtimePeriod),
CPURTRuntime: cc.HostConfig.CPURealtimeRuntime,
CPUShares: uint64(cc.HostConfig.CPUShares),
// CPUS: 0, // don't need?
CPUSetCPUs: cc.HostConfig.CpusetCpus,
CPUSetMems: cc.HostConfig.CpusetMems,
// Detach: false, // don't need
// DetachKeys: "", // don't need
Devices: devices,
DeviceCgroupRule: nil,
DeviceReadBPs: readBps,
DeviceReadIOPs: readIops,
DeviceWriteBPs: writeBps,
DeviceWriteIOPs: writeIops,
Entrypoint: entrypoint,
Env: cc.Config.Env,
Expose: expose,
GroupAdd: cc.HostConfig.GroupAdd,
Hostname: cc.Config.Hostname,
ImageVolume: "bind",
Init: init,
Interactive: cc.Config.OpenStdin,
IPC: string(cc.HostConfig.IpcMode),
Label: stringMaptoArray(cc.Config.Labels),
LogDriver: cc.HostConfig.LogConfig.Type,
LogOptions: stringMaptoArray(cc.HostConfig.LogConfig.Config),
Name: cc.Name,
OOMScoreAdj: &cc.HostConfig.OomScoreAdj,
Arch: "",
OS: "",
Variant: "",
PID: string(cc.HostConfig.PidMode),
PIDsLimit: cc.HostConfig.PidsLimit,
Privileged: cc.HostConfig.Privileged,
PublishAll: cc.HostConfig.PublishAllPorts,
Quiet: false,
ReadOnly: cc.HostConfig.ReadonlyRootfs,
ReadOnlyTmpFS: true, // podman default
Rm: cc.HostConfig.AutoRemove,
SecurityOpt: cc.HostConfig.SecurityOpt,
StopSignal: cc.Config.StopSignal,
StorageOpts: stringMaptoArray(cc.HostConfig.StorageOpt),
Sysctl: stringMaptoArray(cc.HostConfig.Sysctls),
Systemd: "true", // podman default
TmpFS: parsedTmp,
TTY: cc.Config.Tty,
UnsetEnv: cc.UnsetEnv,
UnsetEnvAll: cc.UnsetEnvAll,
User: cc.Config.User,
UserNS: string(cc.HostConfig.UsernsMode),
UTS: string(cc.HostConfig.UTSMode),
Mount: mounts,
VolumesFrom: cc.HostConfig.VolumesFrom,
Workdir: cc.Config.WorkingDir,
Net: &netInfo,
HealthInterval: define.DefaultHealthCheckInterval,
HealthRetries: define.DefaultHealthCheckRetries,
HealthTimeout: define.DefaultHealthCheckTimeout,
HealthStartPeriod: define.DefaultHealthCheckStartPeriod,
}
if !rootless.IsRootless() {
var ulimits []string
if len(cc.HostConfig.Ulimits) > 0 {
for _, ul := range cc.HostConfig.Ulimits {
ulimits = append(ulimits, ul.String())
}
cliOpts.Ulimit = ulimits
}
}
if cc.HostConfig.Resources.NanoCPUs > 0 {
if cliOpts.CPUPeriod != 0 || cliOpts.CPUQuota != 0 {
return nil, nil, errors.Errorf("NanoCpus conflicts with CpuPeriod and CpuQuota")
}
cliOpts.CPUPeriod = 100000
cliOpts.CPUQuota = cc.HostConfig.Resources.NanoCPUs / 10000
}
// volumes
for _, vol := range cc.HostConfig.Binds {
cliOpts.Volume = append(cliOpts.Volume, vol)
// Extract the destination so we don't add duplicate mounts in
// the volumes phase.
splitVol := specgen.SplitVolumeString(vol)
switch len(splitVol) {
case 1:
volDestinations[vol] = true
default:
volSources[splitVol[0]] = true
volDestinations[splitVol[1]] = true
}
}
// Anonymous volumes are added differently from other volumes, in their
// own special field, for reasons known only to Docker. Still use the
// format of `-v` so we can just append them in there.
// Unfortunately, these may be duplicates of existing mounts in Binds.
// So... We need to catch that.
// This also handles volumes duplicated between cc.HostConfig.Mounts and
// cc.Volumes, as seen in compose v2.0.
for vol := range cc.Volumes {
if _, ok := volDestinations[filepath.Clean(vol)]; ok {
continue
}
cliOpts.Volume = append(cliOpts.Volume, vol)
}
// Make mount points for compat volumes
for vol := range volSources {
// This might be a named volume.
// Assume it is if it's not an absolute path.
if !filepath.IsAbs(vol) {
continue
}
// If volume already exists, there is nothing to do
if _, err := os.Stat(vol); err == nil {
continue
}
if err := os.MkdirAll(vol, 0755); err != nil {
if !os.IsExist(err) {
return nil, nil, errors.Wrapf(err, "error making volume mountpoint for volume %s", vol)
}
}
}
if len(cc.HostConfig.BlkioWeightDevice) > 0 {
devices := make([]string, 0, len(cc.HostConfig.BlkioWeightDevice))
for _, d := range cc.HostConfig.BlkioWeightDevice {
devices = append(devices, d.String())
}
cliOpts.BlkIOWeightDevice = devices
}
if cc.HostConfig.BlkioWeight > 0 {
cliOpts.BlkIOWeight = strconv.Itoa(int(cc.HostConfig.BlkioWeight))
}
if cc.HostConfig.Memory > 0 {
cliOpts.Memory = strconv.Itoa(int(cc.HostConfig.Memory))
}
if cc.HostConfig.MemoryReservation > 0 {
cliOpts.MemoryReservation = strconv.Itoa(int(cc.HostConfig.MemoryReservation))
}
cgroupsv2, err := cgroups.IsCgroup2UnifiedMode()
if err != nil {
return nil, nil, err
}
if cc.HostConfig.MemorySwap > 0 && (!rootless.IsRootless() || (rootless.IsRootless() && cgroupsv2)) {
cliOpts.MemorySwap = strconv.Itoa(int(cc.HostConfig.MemorySwap))
}
if cc.Config.StopTimeout != nil {
cliOpts.StopTimeout = uint(*cc.Config.StopTimeout)
}
if cc.HostConfig.ShmSize > 0 {
cliOpts.ShmSize = strconv.Itoa(int(cc.HostConfig.ShmSize))
}
if len(cc.HostConfig.RestartPolicy.Name) > 0 {
policy := cc.HostConfig.RestartPolicy.Name
// only add restart count on failure
if cc.HostConfig.RestartPolicy.IsOnFailure() {
policy += fmt.Sprintf(":%d", cc.HostConfig.RestartPolicy.MaximumRetryCount)
}
cliOpts.Restart = policy
}
if cc.HostConfig.MemorySwappiness != nil && (!rootless.IsRootless() || rootless.IsRootless() && cgroupsv2 && rtc.Engine.CgroupManager == "systemd") {
cliOpts.MemorySwappiness = *cc.HostConfig.MemorySwappiness
} else {
cliOpts.MemorySwappiness = -1
}
if cc.HostConfig.OomKillDisable != nil {
cliOpts.OOMKillDisable = *cc.HostConfig.OomKillDisable
}
if cc.Config.Healthcheck != nil {
finCmd := ""
for _, str := range cc.Config.Healthcheck.Test {
finCmd = finCmd + str + " "
}
if len(finCmd) > 1 {
finCmd = finCmd[:len(finCmd)-1]
}
cliOpts.HealthCmd = finCmd
if cc.Config.Healthcheck.Interval > 0 {
cliOpts.HealthInterval = cc.Config.Healthcheck.Interval.String()
}
if cc.Config.Healthcheck.Retries > 0 {
cliOpts.HealthRetries = uint(cc.Config.Healthcheck.Retries)
}
if cc.Config.Healthcheck.StartPeriod > 0 {
cliOpts.HealthStartPeriod = cc.Config.Healthcheck.StartPeriod.String()
}
if cc.Config.Healthcheck.Timeout > 0 {
cliOpts.HealthTimeout = cc.Config.Healthcheck.Timeout.String()
}
}
// specgen assumes the image name is arg[0]
cmd := []string{cc.Config.Image}
cmd = append(cmd, cc.Config.Cmd...)
return &cliOpts, cmd, nil
}
func ulimits() []string {
if !registry.IsRemote() {
return containerConfig.Ulimits()
@ -536,17 +73,3 @@ func LogDriver() string {
}
return ""
}
// addField is a helper function to populate mount options
func addField(b *strings.Builder, name string, value string) {
if value == "" {
return
}
if b.Len() > 0 {
b.WriteRune(',')
}
b.WriteString(name)
b.WriteRune('=')
b.WriteString(value)
}

View file

@ -28,8 +28,8 @@ func TestPodOptions(t *testing.T) {
for j := 0; j < cc.NumField(); j++ {
containerField := cc.FieldByIndex([]int{j})
containerType := reflect.TypeOf(exampleOptions).Field(j)
tagPod := strings.Split(string(podType.Tag.Get("json")), ",")[0]
tagContainer := strings.Split(string(containerType.Tag.Get("json")), ",")[0]
tagPod := strings.Split(podType.Tag.Get("json"), ",")[0]
tagContainer := strings.Split(containerType.Tag.Get("json"), ",")[0]
if tagPod == tagContainer && (tagPod != "" && tagContainer != "") {
areEqual := true
if containerField.Kind() == podField.Kind() {

View file

@ -31,7 +31,7 @@ var (
Long: checkpointDescription,
RunE: checkpoint,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
ValidArgsFunction: common.AutocompleteContainersRunning,
Example: `podman container checkpoint --keep ctrID

View file

@ -27,7 +27,7 @@ var (
Long: cleanupDescription,
RunE: cleanup,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
ValidArgsFunction: common.AutocompleteContainersExited,
Example: `podman container cleanup --latest

View file

@ -9,6 +9,7 @@ import (
"github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/config"
cutil "github.com/containers/common/pkg/util"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v4/cmd/podman/common"
@ -116,7 +117,7 @@ func create(cmd *cobra.Command, args []string) error {
if !cmd.Flags().Changed("pod") {
return errors.New("must specify pod value with init-ctr")
}
if !util.StringInSlice(initctr, []string{define.AlwaysInitContainer, define.OneShotInitContainer}) {
if !cutil.StringInSlice(initctr, []string{define.AlwaysInitContainer, define.OneShotInitContainer}) {
return errors.Errorf("init-ctr value must be '%s' or '%s'", define.AlwaysInitContainer, define.OneShotInitContainer)
}
cliVals.InitContainerType = initctr

View file

@ -21,7 +21,7 @@ var (
Long: initDescription,
RunE: initContainer,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
ValidArgsFunction: common.AutocompleteContainersCreated,
Example: `podman init --latest

View file

@ -25,7 +25,7 @@ var (
Long: killDescription,
RunE: kill,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, false, true)
return validate.CheckAllLatestAndIDFile(cmd, args, false, "cidfile")
},
ValidArgsFunction: common.AutocompleteContainersRunning,
Example: `podman kill mywebserver
@ -35,7 +35,7 @@ var (
containerKillCommand = &cobra.Command{
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, false, true)
return validate.CheckAllLatestAndIDFile(cmd, args, false, "cidfile")
},
Use: killCommand.Use,
Short: killCommand.Short,
@ -95,7 +95,7 @@ func kill(_ *cobra.Command, args []string) error {
return errors.New("valid signals are 1 through 64")
}
for _, cidFile := range cidFiles {
content, err := ioutil.ReadFile(string(cidFile))
content, err := ioutil.ReadFile(cidFile)
if err != nil {
return errors.Wrap(err, "error reading CIDFile")
}

View file

@ -33,7 +33,7 @@ var (
Long: mountDescription,
RunE: mount,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, true, false)
return validate.CheckAllLatestAndIDFile(cmd, args, true, "")
},
ValidArgsFunction: common.AutocompleteContainers,
}

View file

@ -23,7 +23,7 @@ var (
Long: portDescription,
RunE: port,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, true, false)
return validate.CheckAllLatestAndIDFile(cmd, args, true, "")
},
ValidArgsFunction: common.AutocompleteContainerOneArg,
Example: `podman port --all
@ -37,7 +37,7 @@ var (
Long: portDescription,
RunE: portCommand.RunE,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, true, false)
return validate.CheckAllLatestAndIDFile(cmd, args, true, "")
},
ValidArgsFunction: portCommand.ValidArgsFunction,
Example: `podman container port --all

View file

@ -26,7 +26,7 @@ var (
Long: restartDescription,
RunE: restart,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
ValidArgsFunction: common.AutocompleteContainers,
Example: `podman restart ctrID

View file

@ -28,7 +28,7 @@ var (
Long: restoreDescription,
RunE: restore,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, true, false)
return validate.CheckAllLatestAndIDFile(cmd, args, true, "")
},
ValidArgsFunction: common.AutocompleteContainersAndImages,
Example: `podman container restore ctrID

View file

@ -28,7 +28,7 @@ var (
Long: rmDescription,
RunE: rm,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, false, true)
return validate.CheckAllLatestAndIDFile(cmd, args, false, "cidfile")
},
ValidArgsFunction: common.AutocompleteContainers,
Example: `podman rm imageID
@ -102,7 +102,7 @@ func rm(cmd *cobra.Command, args []string) error {
rmOptions.Timeout = &stopTimeout
}
for _, cidFile := range cidFiles {
content, err := ioutil.ReadFile(string(cidFile))
content, err := ioutil.ReadFile(cidFile)
if err != nil {
return errors.Wrap(err, "error reading CIDFile")
}
@ -123,9 +123,7 @@ func rm(cmd *cobra.Command, args []string) error {
// removeContainers will set the exit code according to the `podman-rm` man
// page.
func removeContainers(namesOrIDs []string, rmOptions entities.RmOptions, setExit bool) error {
var (
errs utils.OutputErrors
)
var errs utils.OutputErrors
responses, err := registry.ContainerEngine().ContainerRm(context.Background(), namesOrIDs, rmOptions)
if err != nil {
if setExit {
@ -135,8 +133,9 @@ func removeContainers(namesOrIDs []string, rmOptions entities.RmOptions, setExit
}
for _, r := range responses {
if r.Err != nil {
// TODO this will not work with the remote client
if errors.Cause(err) == define.ErrWillDeadlock {
// When using the API, errors.Cause(err) will never equal constant define.ErrWillDeadLock
if errors.Cause(r.Err) == define.ErrWillDeadlock ||
errors.Cause(r.Err).Error() == define.ErrWillDeadlock.Error() {
logrus.Errorf("Potential deadlock detected - please run 'podman system renumber' to resolve")
}
if setExit {

View file

@ -214,10 +214,6 @@ func (s *containerStats) BlockIO() string {
}
func (s *containerStats) PIDS() string {
if s.PIDs == 0 {
// If things go bazinga, return a safe value
return "--"
}
return fmt.Sprintf("%d", s.PIDs)
}
@ -231,7 +227,7 @@ func (s *containerStats) MemUsageBytes() string {
func floatToPercentString(f float64) string {
strippedFloat, err := utils.RemoveScientificNotationFromFloat(f)
if err != nil || strippedFloat == 0 {
if err != nil {
// If things go bazinga, return a safe value
return "--"
}
@ -239,16 +235,10 @@ func floatToPercentString(f float64) string {
}
func combineHumanValues(a, b uint64) string {
if a == 0 && b == 0 {
return "-- / --"
}
return fmt.Sprintf("%s / %s", units.HumanSize(float64(a)), units.HumanSize(float64(b)))
}
func combineBytesValues(a, b uint64) string {
if a == 0 && b == 0 {
return "-- / --"
}
return fmt.Sprintf("%s / %s", units.BytesSize(float64(a)), units.BytesSize(float64(b)))
}

View file

@ -26,7 +26,7 @@ var (
Long: stopDescription,
RunE: stop,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, false, true)
return validate.CheckAllLatestAndIDFile(cmd, args, false, "cidfile")
},
ValidArgsFunction: common.AutocompleteContainersRunning,
Example: `podman stop ctrID
@ -40,7 +40,7 @@ var (
Long: stopCommand.Long,
RunE: stopCommand.RunE,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, false, true)
return validate.CheckAllLatestAndIDFile(cmd, args, false, "cidfile")
},
ValidArgsFunction: stopCommand.ValidArgsFunction,
Example: `podman container stop ctrID
@ -100,7 +100,7 @@ func stop(cmd *cobra.Command, args []string) error {
}
for _, cidFile := range cidFiles {
content, err := ioutil.ReadFile(string(cidFile))
content, err := ioutil.ReadFile(cidFile)
if err != nil {
return errors.Wrap(err, "error reading CIDFile")
}

View file

@ -27,7 +27,7 @@ var (
Long: description,
RunE: unmount,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
ValidArgsFunction: common.AutocompleteContainers,
Example: `podman unmount ctrID
@ -43,7 +43,7 @@ var (
Long: unmountCommand.Long,
RunE: unmountCommand.RunE,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
ValidArgsFunction: common.AutocompleteContainers,
Example: `podman container unmount ctrID

View file

@ -110,6 +110,6 @@ func load(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
fmt.Println("Loaded image(s): " + strings.Join(response.Names, ","))
fmt.Println("Loaded image: " + strings.Join(response.Names, "\nLoaded image: "))
return nil
}

View file

@ -6,12 +6,12 @@ import (
"strings"
"github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/util"
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/parse"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/util"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"golang.org/x/term"

View file

@ -5,10 +5,10 @@ import (
"regexp"
"github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/util"
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/util"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)

View file

@ -25,16 +25,14 @@ var (
Example: `podman machine init myvm`,
ValidArgsFunction: completion.AutocompleteNone,
}
)
var (
initOpts = machine.InitOptions{}
defaultMachineName = machine.DefaultMachineName
now bool
)
// maxMachineNameSize is set to thirty to limit huge machine names primarily
// because macos has a much smaller file size limit.
// because macOS has a much smaller file size limit.
const maxMachineNameSize = 30
func init() {
@ -111,8 +109,7 @@ func init() {
flags.BoolVar(&initOpts.Rootful, rootfulFlagName, false, "Whether this machine should prefer rootful container execution")
}
// TODO should we allow for a users to append to the qemu cmdline?
func initMachine(cmd *cobra.Command, args []string) error {
func initMachine(_ *cobra.Command, args []string) error {
var (
err error
vm machine.VM
@ -122,7 +119,7 @@ func initMachine(cmd *cobra.Command, args []string) error {
initOpts.Name = defaultMachineName
if len(args) > 0 {
if len(args[0]) > maxMachineNameSize {
return errors.New("machine name must be 30 characters or less")
return errors.Errorf("machine name %q must be %d characters or less", args[0], maxMachineNameSize)
}
initOpts.Name = args[0]
}

View file

@ -43,11 +43,12 @@ type listFlagType struct {
quiet bool
}
type machineReporter struct {
type ListReporter struct {
Name string
Default bool
Created string
Running bool
Starting bool
LastUp string
Stream string
VMType string
@ -68,7 +69,7 @@ func init() {
flags := lsCmd.Flags()
formatFlagName := "format"
flags.StringVar(&listFlag.format, formatFlagName, "{{.Name}}\t{{.VMType}}\t{{.Created}}\t{{.LastUp}}\t{{.CPUs}}\t{{.Memory}}\t{{.DiskSize}}\n", "Format volume output using JSON or a Go template")
_ = lsCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&machineReporter{}))
_ = lsCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&ListReporter{}))
flags.BoolVar(&listFlag.noHeading, "noheading", false, "Do not print headers")
flags.BoolVarP(&listFlag.quiet, "quiet", "q", false, "Show only machine names")
}
@ -121,8 +122,8 @@ func list(cmd *cobra.Command, args []string) error {
return outputTemplate(cmd, machineReporter)
}
func outputTemplate(cmd *cobra.Command, responses []*machineReporter) error {
headers := report.Headers(machineReporter{}, map[string]string{
func outputTemplate(cmd *cobra.Command, responses []*ListReporter) error {
headers := report.Headers(ListReporter{}, map[string]string{
"LastUp": "LAST UP",
"VmType": "VM TYPE",
"CPUs": "CPUS",
@ -181,15 +182,15 @@ func streamName(imageStream string) string {
return imageStream
}
func toMachineFormat(vms []*machine.ListResponse) ([]*machineReporter, error) {
func toMachineFormat(vms []*machine.ListResponse) ([]*ListReporter, error) {
cfg, err := config.ReadCustomConfig()
if err != nil {
return nil, err
}
machineResponses := make([]*machineReporter, 0, len(vms))
machineResponses := make([]*ListReporter, 0, len(vms))
for _, vm := range vms {
response := new(machineReporter)
response := new(ListReporter)
response.Default = vm.Name == cfg.Engine.ActiveService
response.Name = vm.Name
response.Running = vm.Running
@ -209,25 +210,29 @@ func toMachineFormat(vms []*machine.ListResponse) ([]*machineReporter, error) {
return machineResponses, nil
}
func toHumanFormat(vms []*machine.ListResponse) ([]*machineReporter, error) {
func toHumanFormat(vms []*machine.ListResponse) ([]*ListReporter, error) {
cfg, err := config.ReadCustomConfig()
if err != nil {
return nil, err
}
humanResponses := make([]*machineReporter, 0, len(vms))
humanResponses := make([]*ListReporter, 0, len(vms))
for _, vm := range vms {
response := new(machineReporter)
response := new(ListReporter)
if vm.Name == cfg.Engine.ActiveService {
response.Name = vm.Name + "*"
response.Default = true
} else {
response.Name = vm.Name
}
if vm.Running {
switch {
case vm.Running:
response.LastUp = "Currently running"
response.Running = true
} else {
case vm.Starting:
response.LastUp = "Currently starting"
response.Starting = true
default:
response.LastUp = units.HumanDuration(time.Since(vm.LastUp)) + " ago"
}
response.Created = units.HumanDuration(time.Since(vm.CreatedAt)) + " ago"

View file

@ -56,7 +56,7 @@ func start(_ *cobra.Command, args []string) error {
if vmName == activeName {
return errors.Wrapf(machine.ErrVMAlreadyRunning, "cannot start VM %s", vmName)
}
return errors.Wrapf(machine.ErrMultipleActiveVM, "cannot start VM %s. VM %s is currently running", vmName, activeName)
return errors.Wrapf(machine.ErrMultipleActiveVM, "cannot start VM %s. VM %s is currently running or starting", vmName, activeName)
}
fmt.Printf("Starting machine %q\n", vmName)
if err := vm.Start(vmName, machine.StartOptions{}); err != nil {

View file

@ -21,7 +21,7 @@ var (
Long: networkReloadDescription,
RunE: networkReload,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
ValidArgsFunction: common.AutocompleteContainers,
Example: `podman network reload --latest

View file

@ -18,6 +18,8 @@ import (
const (
Protocol_TCP Protocol = 0
Protocol_UDP Protocol = 1
LabelType string = "label"
ENVType string = "env"
)
type Protocol int32
@ -89,9 +91,7 @@ func GetAllLabels(labelFile, inputLabels []string) (map[string]string, error) {
// There's an argument that we SHOULD be doing that parsing for
// all environment variables, even those sourced from files, but
// that would require a substantial rework.
if err := parseEnvFile(labels, file); err != nil {
// FIXME: parseEnvFile is using parseEnv, so we need to add extra
// logic for labels.
if err := parseEnvOrLabelFile(labels, file, LabelType); err != nil {
return nil, err
}
}
@ -109,7 +109,7 @@ func GetAllLabels(labelFile, inputLabels []string) (map[string]string, error) {
return labels, nil
}
func parseEnv(env map[string]string, line string) error {
func parseEnvOrLabel(env map[string]string, line, configType string) error {
data := strings.SplitN(line, "=", 2)
// catch invalid variables such as "=" or "=A"
@ -137,7 +137,7 @@ func parseEnv(env map[string]string, line string) error {
env[part[0]] = part[1]
}
}
} else {
} else if configType == ENVType {
// if only a pass-through variable is given, clean it up.
if val, ok := os.LookupEnv(name); ok {
env[name] = val
@ -147,8 +147,9 @@ func parseEnv(env map[string]string, line string) error {
return nil
}
// parseEnvFile reads a file with environment variables enumerated by lines
func parseEnvFile(env map[string]string, filename string) error {
// parseEnvOrLabelFile reads a file with environment variables enumerated by lines
// configType should be set to either "label" or "env" based on what type is being parsed
func parseEnvOrLabelFile(envOrLabel map[string]string, filename, configType string) error {
fh, err := os.Open(filename)
if err != nil {
return err
@ -161,7 +162,7 @@ func parseEnvFile(env map[string]string, filename string) error {
line := strings.TrimLeft(scanner.Text(), whiteSpaces)
// line is not empty, and not starting with '#'
if len(line) > 0 && !strings.HasPrefix(line, "#") {
if err := parseEnv(env, line); err != nil {
if err := parseEnvOrLabel(envOrLabel, line, configType); err != nil {
return err
}
}

View file

@ -22,7 +22,7 @@ var (
Long: podKillDescription,
RunE: kill,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
ValidArgsFunction: common.AutocompletePodsRunning,
Example: `podman pod kill podID

View file

@ -22,7 +22,7 @@ var (
Long: podPauseDescription,
RunE: pause,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
ValidArgsFunction: common.AutocompletePodsRunning,
Example: `podman pod pause podID1 podID2

View file

@ -49,7 +49,6 @@ func init() {
flags.BoolVar(&psInput.CtrNames, "ctr-names", false, "Display the container names")
flags.BoolVar(&psInput.CtrIds, "ctr-ids", false, "Display the container UUIDs. If no-trunc is not set they will be truncated")
flags.BoolVar(&psInput.CtrStatus, "ctr-status", false, "Display the container status")
// TODO should we make this a [] ?
filterFlagName := "filter"
flags.StringSliceVarP(&inputFilters, filterFlagName, "f", []string{}, "Filter output based on conditions given")

View file

@ -22,7 +22,7 @@ var (
Long: podRestartDescription,
RunE: restart,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
ValidArgsFunction: common.AutocompletePods,
Example: `podman pod restart podID1 podID2

View file

@ -35,7 +35,7 @@ var (
Long: podRmDescription,
RunE: rm,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndPodIDFile(cmd, args, false, true)
return validate.CheckAllLatestAndIDFile(cmd, args, false, "pod-id-file")
},
ValidArgsFunction: common.AutocompletePods,
Example: `podman pod rm mywebserverpod

View file

@ -31,7 +31,7 @@ var (
Long: podStartDescription,
RunE: start,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndPodIDFile(cmd, args, false, true)
return validate.CheckAllLatestAndIDFile(cmd, args, false, "pod-id-file")
},
ValidArgsFunction: common.AutocompletePods,
Example: `podman pod start podID

View file

@ -36,7 +36,7 @@ var (
Long: podStopDescription,
RunE: stop,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndPodIDFile(cmd, args, false, true)
return validate.CheckAllLatestAndIDFile(cmd, args, false, "pod-id-file")
},
ValidArgsFunction: common.AutocompletePodsRunning,
Example: `podman pod stop mywebserverpod

View file

@ -22,7 +22,7 @@ var (
Long: podUnpauseDescription,
RunE: unpause,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, false, false)
return validate.CheckAllLatestAndIDFile(cmd, args, false, "")
},
// TODO have a function which shows only pods which could be unpaused
// for now show all

View file

@ -423,7 +423,7 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) {
// -s is deprecated due to conflict with -s on subcommands
storageDriverFlagName := "storage-driver"
pFlags.StringVar(&opts.StorageDriver, storageDriverFlagName, "", "Select which storage driver is used to manage storage of images and containers")
_ = cmd.RegisterFlagCompletionFunc(storageDriverFlagName, completion.AutocompleteNone) //TODO: what can we recommend here?
_ = cmd.RegisterFlagCompletionFunc(storageDriverFlagName, completion.AutocompleteNone)
tmpdirFlagName := "tmpdir"
pFlags.StringVar(&opts.Engine.TmpDir, tmpdirFlagName, "", "Path to the tmp directory for libpod state content.\n\nNote: use the environment variable 'TMPDIR' to change the temporary storage location for container images, '/var/tmp'.\n")

View file

@ -1,15 +0,0 @@
//go:build !windows
// +build !windows
package utils
import (
"os"
"golang.org/x/sys/unix"
)
// Platform specific signal synonyms
var (
SIGHUP os.Signal = unix.SIGHUP
)

View file

@ -1,15 +0,0 @@
//go:build windows
// +build windows
package utils
import (
"os"
"golang.org/x/sys/windows"
)
// Platform specific signal synonyms
var (
SIGHUP os.Signal = windows.SIGHUP
)

View file

@ -50,34 +50,44 @@ func IDOrLatestArgs(cmd *cobra.Command, args []string) error {
return nil
}
// TODO: the two functions CheckAllLatestAndCIDFile and CheckAllLatestAndPodIDFile are almost identical.
// It may be worth looking into generalizing the two a bit more and share code but time is scarce and
// we only live once.
// CheckAllLatestAndCIDFile checks that --all and --latest are used correctly.
// If cidfile is set, also check for the --cidfile flag.
// CheckAllLatestAndCIDFile checks that --all and --latest are used correctly for containers and pods
// If idFileFlag is set is set, also checks for the --cidfile or --pod-id-file flag.
// Note: this has been deprecated, use CheckAllLatestAndIDFile instead
func CheckAllLatestAndCIDFile(c *cobra.Command, args []string, ignoreArgLen bool, cidfile bool) error {
return CheckAllLatestAndIDFile(c, args, ignoreArgLen, "cidfile")
}
// CheckAllLatestAndPodIDFile checks that --all and --latest are used correctly.
// If withIDFile is set, also check for the --pod-id-file flag.
// Note: this has been deprecated, use CheckAllLatestAndIDFile instead
func CheckAllLatestAndPodIDFile(c *cobra.Command, args []string, ignoreArgLen bool, withIDFile bool) error {
return CheckAllLatestAndIDFile(c, args, ignoreArgLen, "pod-id-file")
}
// CheckAllLatestAndIDFile checks that --all and --latest are used correctly for containers and pods
// If idFileFlag is set is set, also checks for the --cidfile or --pod-id-file flag.
func CheckAllLatestAndIDFile(c *cobra.Command, args []string, ignoreArgLen bool, idFileFlag string) error {
var specifiedLatest bool
argLen := len(args)
if !registry.IsRemote() {
specifiedLatest, _ = c.Flags().GetBool("latest")
if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil {
if !cidfile {
if idFileFlag == "" {
return errors.New("unable to lookup values for 'latest' or 'all'")
} else if c.Flags().Lookup("cidfile") == nil {
return errors.New("unable to lookup values for 'latest', 'all' or 'cidfile'")
} else if c.Flags().Lookup(idFileFlag) == nil {
return errors.Errorf("unable to lookup values for 'latest', 'all', or '%s'", idFileFlag)
}
}
}
specifiedAll, _ := c.Flags().GetBool("all")
specifiedCIDFile := false
if cid, _ := c.Flags().GetStringArray("cidfile"); len(cid) > 0 {
specifiedCIDFile = true
specifiedIDFile := false
if cid, _ := c.Flags().GetStringArray(idFileFlag); len(cid) > 0 {
specifiedIDFile = true
}
if specifiedCIDFile && (specifiedAll || specifiedLatest) {
return errors.Errorf("--all, --latest and --cidfile cannot be used together")
if specifiedIDFile && (specifiedAll || specifiedLatest) {
return errors.Errorf("--all, --latest, and --%s cannot be used together", idFileFlag)
} else if specifiedAll && specifiedLatest {
return errors.Errorf("--all and --latest cannot be used together")
}
@ -93,71 +103,16 @@ func CheckAllLatestAndCIDFile(c *cobra.Command, args []string, ignoreArgLen bool
if argLen > 0 {
if specifiedLatest {
return errors.Errorf("--latest and containers cannot be used together")
} else if cidfile && (specifiedLatest || specifiedCIDFile) {
return errors.Errorf("no arguments are needed with --latest or --cidfile")
} else if idFileFlag != "" && (specifiedLatest || specifiedIDFile) {
return errors.Errorf("no arguments are needed with --latest or --%s", idFileFlag)
}
}
if specifiedCIDFile {
if specifiedIDFile {
return nil
}
if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedCIDFile {
return errors.Errorf("you must provide at least one name or id")
}
return nil
}
// CheckAllLatestAndPodIDFile checks that --all and --latest are used correctly.
// If withIDFile is set, also check for the --pod-id-file flag.
func CheckAllLatestAndPodIDFile(c *cobra.Command, args []string, ignoreArgLen bool, withIDFile bool) error {
var specifiedLatest bool
argLen := len(args)
if !registry.IsRemote() {
// remote clients have no latest flag
specifiedLatest, _ = c.Flags().GetBool("latest")
if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil {
if !withIDFile {
return errors.New("unable to lookup values for 'latest' or 'all'")
} else if c.Flags().Lookup("pod-id-file") == nil {
return errors.New("unable to lookup values for 'latest', 'all' or 'pod-id-file'")
}
}
}
specifiedAll, _ := c.Flags().GetBool("all")
specifiedPodIDFile := false
if pid, _ := c.Flags().GetStringArray("pod-id-file"); len(pid) > 0 {
specifiedPodIDFile = true
}
if specifiedPodIDFile && (specifiedAll || specifiedLatest) {
return errors.Errorf("--all, --latest and --pod-id-file cannot be used together")
} else if specifiedAll && specifiedLatest {
return errors.Errorf("--all and --latest cannot be used together")
}
if (argLen > 0) && specifiedAll {
return errors.Errorf("no arguments are needed with --all")
}
if ignoreArgLen {
return nil
}
if argLen > 0 {
if specifiedLatest {
return errors.Errorf("--latest and pods cannot be used together")
} else if withIDFile && (specifiedLatest || specifiedPodIDFile) {
return errors.Errorf("no arguments are needed with --latest or --pod-id-file")
}
}
if specifiedPodIDFile {
return nil
}
if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedPodIDFile {
if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedIDFile {
return errors.Errorf("you must provide at least one name or id")
}
return nil

View file

@ -25,6 +25,23 @@ cat ${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/required_host_ports.txt | \
fi
done
# TODO: Pull images required during testing into /dev/null
# Verify we can pull metadata from a few key testing images on quay.io
# in the 'libpod' namespace. This is mostly aimed at validating the
# quay.io service is up and responsive. Images were hand-picked with
# egrep -ro 'quay.io/libpod/.+:latest' test | sort -u
TEST_IMGS=(\
alpine:latest
busybox:latest
alpine_labels:latest
alpine_nginx:latest
alpine_healthcheck:latest
badhealthcheck:latest
cirros:latest
)
# TODO: Refresh DNF package-cache into /dev/null
echo "Checking quay.io test image accessibility"
for testimg in "${TEST_IMGS[@]}"; do
fqin="quay.io/libpod/$testimg"
echo " $fqin"
skopeo inspect --retry-times 5 "docker://$fqin" | jq . > /dev/null
done

View file

@ -96,7 +96,7 @@ EPOCH_TEST_COMMIT="$CIRRUS_BASE_SHA"
# testing operations on all platforms and versions. This is necessary
# to avoid needlessly passing through global/system values across
# contexts, such as host->container or root->rootless user
PASSTHROUGH_ENV_RE='(^CI.*)|(^CIRRUS)|(^DISTRO_NV)|(^GOPATH)|(^GOCACHE)|(^GOSRC)|(^SCRIPT_BASE)|(CGROUP_MANAGER)|(OCI_RUNTIME)|(^TEST.*)|(^PODBIN_NAME)|(^PRIV_NAME)|(^ALT_NAME)|(^ROOTLESS_USER)|(SKIP_USERNS)|(.*_NAME)|(.*_FQIN)|(NETWORK_BACKEND)'
PASSTHROUGH_ENV_RE='(^CI.*)|(^CIRRUS)|(^DISTRO_NV)|(^GOPATH)|(^GOCACHE)|(^GOSRC)|(^SCRIPT_BASE)|(CGROUP_MANAGER)|(OCI_RUNTIME)|(^TEST.*)|(^PODBIN_NAME)|(^PRIV_NAME)|(^ALT_NAME)|(^ROOTLESS_USER)|(SKIP_USERNS)|(.*_NAME)|(.*_FQIN)|(NETWORK_BACKEND)|(DEST_BRANCH)'
# Unsafe env. vars for display
SECRET_ENV_RE='(ACCOUNT)|(GC[EP]..+)|(SSH)|(PASSWORD)|(TOKEN)'
@ -239,11 +239,6 @@ use_netavark() {
export NETWORK_BACKEND=netavark # needed for install_test_configs()
msg "Removing any/all CNI configuration"
rm -rvf /etc/cni/net.d/*
# TODO: Remove this when netavark/aardvark-dns development slows down
warn "Updating netavark/aardvark-dns to avoid frequent VM image rebuilds"
# N/B: This is coming from updates-testing repo in F36
lilto dnf update -y netavark aardvark-dns
}
# Remove all files provided by the distro version of podman.

View file

@ -246,20 +246,28 @@ function _run_altbuild() {
# shellcheck disable=SC2154
msg "Performing alternate build: $ALT_NAME"
msg "************************************************************"
set -x
cd $GOSRC
case "$ALT_NAME" in
*Each*)
git fetch origin
# The check-size script, introduced 2022-03-22 in #13518,
# The make-and-check-size script, introduced 2022-03-22 in #13518,
# runs 'make' (the original purpose of this check) against
# each commit, then checks image sizes to make sure that
# none have grown beyond a given limit. That of course
# requires a baseline, which is why we use '^' to start
# with the *parent* commit of this PR, not the first commit.
# requires a baseline, so our first step is to build the
# branch point of the PR.
local context_dir savedhead pr_base
context_dir=$(mktemp -d --tmpdir make-size-check.XXXXXXX)
make build-all-new-commits \
GIT_BASE_BRANCH=origin/"${DEST_BRANCH}^" \
MAKE="hack/make-and-check-size $context_dir"
savedhead=$(git rev-parse HEAD)
# Push to PR base. First run of the script will write size files
pr_base=$(git merge-base origin/$DEST_BRANCH HEAD)
git checkout $pr_base
hack/make-and-check-size $context_dir
# pop back to PR, and run incremental makes. Subsequent script
# invocations will compare against original size.
git checkout $savedhead
git rebase $pr_base -x "hack/make-and-check-size $context_dir"
rm -rf $context_dir
;;
*Windows*)
@ -295,10 +303,6 @@ function _run_altbuild() {
}
function _run_release() {
# TODO: These tests should come from code external to the podman repo.
# to allow test-changes (and re-runs) in the case of a correctable test
# flaw or flake at release tag-push time. For now, the test is here
# given its simplicity.
msg "podman info:"
bin/podman info

View file

@ -25,7 +25,7 @@ msg "************************************************************"
show_env_vars
req_env_vars USER HOME GOSRC SCRIPT_BASE TEST_FLAVOR TEST_ENVIRON \
PODBIN_NAME PRIV_NAME DISTRO_NV
PODBIN_NAME PRIV_NAME DISTRO_NV DEST_BRANCH
# Verify basic dependencies
for depbin in go rsync unzip sha256sum curl make python3 git

View file

@ -2,12 +2,15 @@
Description=Podman Start All Containers With Restart Policy Set To Always
Documentation=man:podman-start(1)
StartLimitIntervalSec=0
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
RemainAfterExit=true
Environment=LOGGING="--log-level=info"
ExecStart=@@PODMAN@@ $LOGGING start --all --filter restart-policy=always
ExecStop=/bin/sh -c '@@PODMAN@@ $LOGGING stop $(@@PODMAN@@ container ls --filter restart-policy=always -q)'
[Install]
WantedBy=default.target

View file

@ -51,15 +51,6 @@ The `UPDATED` field indicates the availability of a new image with "pending".
Change the default output format. This can be of a supported type like 'json' or a Go template.
Valid placeholders for the Go template are listed below:
#### **--rollback**
If restarting a systemd unit after updating the image has failed, rollback to using the previous image and restart the unit another time. Default is true.
Please note that detecting if a systemd unit has failed is best done by the container sending the READY message via SDNOTIFY. This way, restarting the unit will wait until having received the message or a timeout kicked in. Without that, restarting the systemd unit may succeed even if the container has failed shortly after.
For a container to send the READY message via SDNOTIFY it must be created with the `--sdnotify=container` option (see podman-run(1)). The application running inside the container can then execute `systemd-notify --ready` when ready or use the sdnotify bindings of the specific programming language (e.g., sd_notify(3)).
| **Placeholder** | **Description** |
| --------------- | -------------------------------------- |
| .Unit | Name of the systemd unit |
@ -70,6 +61,14 @@ For a container to send the READY message via SDNOTIFY it must be created with t
| .Policy | Auto-update policy of the container |
| .Updated | Update status: true,false,failed |
#### **--rollback**
If restarting a systemd unit after updating the image has failed, rollback to using the previous image and restart the unit another time. Default is true.
Please note that detecting if a systemd unit has failed is best done by the container sending the READY message via SDNOTIFY. This way, restarting the unit will wait until having received the message or a timeout kicked in. Without that, restarting the systemd unit may succeed even if the container has failed shortly after.
For a container to send the READY message via SDNOTIFY it must be created with the `--sdnotify=container` option (see podman-run(1)). The application running inside the container can then execute `systemd-notify --ready` when ready or use the sdnotify bindings of the specific programming language (e.g., sd_notify(3)).
## EXAMPLES
Autoupdate with registry policy

View file

@ -404,8 +404,8 @@ if it does not exist. This option is useful for building multi architecture imag
#### **--memory**, **-m**=*LIMIT*
Memory limit (format: `<number>[<unit>]`, where unit = b (bytes), k (kilobytes),
m (megabytes), or g (gigabytes))
Memory limit (format: `<number>[<unit>]`, where unit = b (bytes), k (kibibytes),
m (mebibytes), or g (gibibytes))
Allows you to constrain the memory available to a container. If the host
supports swap memory, then the **-m** memory setting can be larger than physical
@ -422,7 +422,7 @@ A limit value equal to memory plus swap. Must be used with the **-m**
the value of --memory.
The format of `LIMIT` is `<number>[<unit>]`. Unit can be `b` (bytes),
`k` (kilobytes), `m` (megabytes), or `g` (gigabytes). If you don't specify a
`k` (kibibytes), `m` (mebibytes), or `g` (gibibytes). If you don't specify a
unit, `b` is used. Set LIMIT to `-1` to enable unlimited swap.
#### **--network**=*mode*, **--net**
@ -600,8 +600,8 @@ as a seccomp filter
Size of `/dev/shm`. The format is `<number><unit>`. `number` must be greater
than `0`.
Unit is optional and can be `b` (bytes), `k` (kilobytes), `m`(megabytes), or
`g` (gigabytes). If you omit the unit, the system uses bytes. If you omit the
Unit is optional and can be `b` (bytes), `k` (kibibytes), `m`(mebibytes), or
`g` (gibibytes). If you omit the unit, the system uses bytes. If you omit the
size entirely, the system uses `64m`.
#### **--sign-by**=*fingerprint*
@ -882,12 +882,10 @@ container. When the mount propagation policy is set to `slave`, one way mount
propagation is enabled and any mounts completed on the host for that volume will
be visible only inside of the container. To control the mount propagation
property of volume use the `:[r]shared`, `:[r]slave` or `:[r]private`
propagation flag. The propagation property can be specified only for bind mounted
volumes and not for internal volumes or named volumes. For mount propagation to
work on the source mount point (mount point where source dir is mounted on) has
to have the right propagation properties. For shared volumes, the source mount
point has to be shared. And for slave volumes, the source mount has to be either
shared or slave. <sup>[[1]](#Footnote1)</sup>
propagation flag. For mount propagation to work on the source mount point (mount
point where source dir is mounted on) has to have the right propagation properties.
For shared volumes, the source mount point has to be shared. And for slave volumes,
the source mount has to be either shared or slave. <sup>[[1]](#Footnote1)</sup>
Use `df <source-dir>` to determine the source mount and then use
`findmnt -o TARGET,PROPAGATION <source-mount-dir>` to determine propagation

View file

@ -131,7 +131,7 @@ Force removal of the original container that we are cloning. Can only be used in
#### **--memory**, **-m**=*limit*
Memory limit (format: `<number>[<unit>]`, where unit = b (bytes), k (kilobytes), m (megabytes), or g (gigabytes))
Memory limit (format: `<number>[<unit>]`, where unit = b (bytes), k (kibibytes), m (mebibytes), or g (gibibytes))
Allows the memory available to a container to be constrained. If the host
supports swap memory, then the **-m** memory setting can be larger than physical
@ -143,7 +143,7 @@ If no memory limits are specified, the original container's will be used.
#### **--memory-reservation**=*limit*
Memory soft limit (format: `<number>[<unit>]`, where unit = b (bytes), k (kilobytes), m (megabytes), or g (gigabytes))
Memory soft limit (format: `<number>[<unit>]`, where unit = b (bytes), k (kibibytes), m (mebibytes), or g (gibibytes))
After setting memory reservation, when the system detects memory contention
or low memory, containers are forced to restrict their consumption to their
@ -159,7 +159,7 @@ A limit value equal to memory plus swap. Must be used with the **-m**
the value of --memory if specified. Otherwise, the container being cloned will be used to derive the swap value.
The format of `LIMIT` is `<number>[<unit>]`. Unit can be `b` (bytes),
`k` (kilobytes), `m` (megabytes), or `g` (gigabytes). If you don't specify a
`k` (kibibytes), `m` (mebibytes), or `g` (gibibytes). If you don't specify a
unit, `b` is used. Set LIMIT to `-1` to enable unlimited swap.
#### **--memory-swappiness**=*number*

View file

@ -571,7 +571,7 @@ To specify multiple static MAC addresses per container, set multiple networks us
#### **--memory**, **-m**=*limit*
Memory limit (format: `<number>[<unit>]`, where unit = b (bytes), k (kilobytes), m (megabytes), or g (gigabytes))
Memory limit (format: `<number>[<unit>]`, where unit = b (bytes), k (kibibytes), m (mebibytes), or g (gibibytes))
Allows you to constrain the memory available to a container. If the host
supports swap memory, then the **-m** memory setting can be larger than physical
@ -581,7 +581,7 @@ system's page size (the value would be very large, that's millions of trillions)
#### **--memory-reservation**=*limit*
Memory soft limit (format: `<number>[<unit>]`, where unit = b (bytes), k (kilobytes), m (megabytes), or g (gigabytes))
Memory soft limit (format: `<number>[<unit>]`, where unit = b (bytes), k (kibibytes), m (mebibytes), or g (gibibytes))
After setting memory reservation, when the system detects memory contention
or low memory, containers are forced to restrict their consumption to their
@ -597,7 +597,7 @@ A limit value equal to memory plus swap. Must be used with the **-m**
the value of --memory.
The format of `LIMIT` is `<number>[<unit>]`. Unit can be `b` (bytes),
`k` (kilobytes), `m` (megabytes), or `g` (gigabytes). If you don't specify a
`k` (kibibytes), `m` (mebibytes), or `g` (gibibytes). If you don't specify a
unit, `b` is used. Set LIMIT to `-1` to enable unlimited swap.
#### **--memory-swappiness**=*number*
@ -824,22 +824,27 @@ container.
Rootless containers cannot have more privileges than the account that launched them.
#### **--publish**, **-p**=*port*
#### **--publish**, **-p**=[[_ip_:][_hostPort_]:]_containerPort_[/_protocol_]
Publish a container's port, or range of ports, to the host
Publish a container's port, or range of ports, to the host.
Format: `ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort`
Both hostPort and containerPort can be specified as a range of ports.
When specifying ranges for both, the number of container ports in the range must match the number of host ports in the range.
(e.g., `podman run -p 1234-1236:1222-1224 --name thisWorks -t busybox`
but not `podman run -p 1230-1236:1230-1240 --name RangeContainerPortsBiggerThanRangeHostPorts -t busybox`)
With host IP: `podman run -p 127.0.0.1:$HOSTPORT:$CONTAINERPORT --name CONTAINER -t someimage`
When specifying ranges for both, the number of container ports in the
range must match the number of host ports in the range.
If host IP is set to 0.0.0.0 or not set at all, the port will be bound on all IPs on the host.
By default, Podman will publish TCP ports. To publish a UDP port instead, give
`udp` as protocol. To publish both TCP and UDP ports, set `--publish` twice,
with `tcp`, and `udp` as protocols respectively. Rootful containers can also
publish ports using the `sctp` protocol.
Host port does not have to be specified (e.g. `podman run -p 127.0.0.1::80`).
If it is not, the container port will be randomly assigned a port on the host.
Use `podman port` to see the actual mapping: `podman port CONTAINER $CONTAINERPORT`
**Note:** if a container will be run within a pod, it is not necessary to publish the port for
Use **podman port** to see the actual mapping: `podman port $CONTAINER $CONTAINERPORT`.
**Note:** If a container will be run within a pod, it is not necessary to publish the port for
the containers in the pod. The port must only be published by the pod itself. Pod network
stacks act like the network stack on the host - you have a variety of containers in the pod,
and programs in the container, all sharing a single interface and IP address, and
@ -1006,7 +1011,7 @@ Note: Labeling can be disabled for all containers by setting label=false in the
#### **--shm-size**=*size*
Size of `/dev/shm` (format: `<number>[<unit>]`, where unit = b (bytes), k (kilobytes), m (megabytes), or g (gigabytes))
Size of `/dev/shm` (format: `<number>[<unit>]`, where unit = b (bytes), k (kibibytes), m (mebibytes), or g (gibibytes))
If you omit the unit, the system uses bytes. If you omit the size entirely, the system uses `64m`.
When size is `0`, there is no limit on the amount of memory used for IPC by the container.
@ -1402,12 +1407,10 @@ will be visible inside container but not the other way around. <sup>[[1]](#Footn
To control mount propagation property of a volume one can use the [**r**]**shared**,
[**r**]**slave**, [**r**]**private** or the [**r**]**unbindable** propagation flag.
Propagation property can be specified only for bind mounted volumes and not for
internal volumes or named volumes. For mount propagation to work the source mount
point (the mount point where source dir is mounted on) has to have the right propagation
properties. For shared volumes, the source mount point has to be shared. And for
slave volumes, the source mount point has to be either shared or slave.
<sup>[[1]](#Footnote1)</sup>
For mount propagation to work the source mount point (the mount point where source dir
is mounted on) has to have the right propagation properties. For shared volumes, the
source mount point has to be shared. And for slave volumes, the source mount point
has to be either shared or slave. <sup>[[1]](#Footnote1)</sup>
If you want to recursively mount a volume and all of its submounts into a
container, then you can use the `rbind` option. By default the bind option is

View file

@ -17,6 +17,12 @@ set, the time of creation and size are printed out in a human readable format.
The **--quiet** flag displays the ID of the image only when set and the **--format**
flag is used to print the information using the Go template provided by the user.
## OPTIONS
#### **--format**=*format*
Alter the output for a format like 'json' or a Go template.
Valid placeholders for the Go template are listed below:
| **Placeholder** | **Description** |
@ -28,12 +34,7 @@ Valid placeholders for the Go template are listed below:
| .CreatedSince | Elapsed time since the image layer was created |
| .Size | Size of layer on disk |
| .Comment | Comment for the layer |
## OPTIONS
Print the numeric IDs only (default *false*).
#### **--format**=*format*
Alter the output for a format like 'json' or a Go template.
| .Tags | Image tags |
#### **--help**, **-h**
@ -49,6 +50,8 @@ Do not truncate the output (default *false*).
#### **--quiet**, **-q**
Print the numeric IDs only (default *false*).
## EXAMPLES
```

View file

@ -33,7 +33,7 @@ Suppress the output
```
$ podman image scp alpine
Loaded image(s): docker.io/library/alpine:latest
Loaded image: docker.io/library/alpine:latest
```
```
@ -43,12 +43,12 @@ Copying blob 72e830a4dff5 done
Copying config 85f9dc67c7 done
Writing manifest to image destination
Storing signatures
Loaded image(s): docker.io/library/alpine:latest
Loaded image: docker.io/library/alpine:latest
```
```
$ podman image scp Fedora::alpine RHEL::
Loaded image(s): docker.io/library/alpine:latest
Loaded image: docker.io/library/alpine:latest
```
```
@ -59,7 +59,7 @@ Copying blob 9450ef9feb15 [--------------------------------------] 0.0b / 0.0b
Copying config 1f97f0559c done
Writing manifest to image destination
Storing signatures
Loaded image(s): docker.io/library/alpine:latest
Loaded image: docker.io/library/alpine:latest
```
```
@ -73,7 +73,7 @@ Copying blob 5eb901baf107 skipped: already exists
Copying config 696d33ca15 done
Writing manifest to image destination
Storing signatures
Loaded image(s): docker.io/library/alpine:latest
Loaded image: docker.io/library/alpine:latest
```
```
@ -87,7 +87,7 @@ Copying blob 5eb901baf107
Copying config 696d33ca15 done
Writing manifest to image destination
Storing signatures
Loaded image(s): docker.io/library/alpine:latest
Loaded image: docker.io/library/alpine:latest
```
## SEE ALSO

View file

@ -208,16 +208,30 @@ Set the PID mode for the pod. The default is to create a private PID namespace f
Write the pod ID to the file.
#### **--publish**=*port*, **-p**
#### **--publish**, **-p**=[[_ip_:][_hostPort_]:]_containerPort_[/_protocol_]
Publish a port or range of ports from the pod to the host.
Publish a container's port, or range of ports, within this pod to the host.
Format: `ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort`
Both hostPort and containerPort can be specified as a range of ports.
When specifying ranges for both, the number of container ports in the range must match the number of host ports in the range.
Use `podman port` to see the actual mapping: `podman port CONTAINER $CONTAINERPORT`.
When specifying ranges for both, the number of container ports in the
range must match the number of host ports in the range.
NOTE: This cannot be modified once the pod is created.
If host IP is set to 0.0.0.0 or not set at all, the port will be bound on all IPs on the host.
By default, Podman will publish TCP ports. To publish a UDP port instead, give
`udp` as protocol. To publish both TCP and UDP ports, set `--publish` twice,
with `tcp`, and `udp` as protocols respectively. Rootful containers can also
publish ports using the `sctp` protocol.
Host port does not have to be specified (e.g. `podman run -p 127.0.0.1::80`).
If it is not, the container port will be randomly assigned a port on the host.
Use **podman port** to see the actual mapping: `podman port $CONTAINER $CONTAINERPORT`.
**Note:** You must not publish ports of containers in the pod individually,
but only by the pod itself.
**Note:** This cannot be modified once the pod is created.
#### **--replace**
@ -457,12 +471,10 @@ will be visible inside container but not the other way around. <sup>[[1]](#Footn
To control mount propagation property of a volume one can use the [**r**]**shared**,
[**r**]**slave**, [**r**]**private** or the [**r**]**unbindable** propagation flag.
Propagation property can be specified only for bind mounted volumes and not for
internal volumes or named volumes. For mount propagation to work the source mount
point (the mount point where source dir is mounted on) has to have the right propagation
properties. For shared volumes, the source mount point has to be shared. And for
slave volumes, the source mount point has to be either shared or slave.
<sup>[[1]](#Footnote1)</sup>
For mount propagation to work the source mount point (the mount point where source dir
is mounted on) has to have the right propagation properties. For shared volumes, the
source mount point has to be shared. And for slave volumes, the source mount point
has to be either shared or slave. <sup>[[1]](#Footnote1)</sup>
If you want to recursively mount a volume and all of its submounts into a
pod, then you can use the `rbind` option. By default the bind option is

View file

@ -595,7 +595,7 @@ To specify multiple static MAC addresses per container, set multiple networks us
#### **--memory**, **-m**=_number_[_unit_]
Memory limit. A _unit_ can be **b** (bytes), **k** (kilobytes), **m** (megabytes), or **g** (gigabytes).
Memory limit. A _unit_ can be **b** (bytes), **k** (kibibytes), **m** (mebibytes), or **g** (gibibytes).
Allows you to constrain the memory available to a container. If the host
supports swap memory, then the **-m** memory setting can be larger than physical
@ -605,7 +605,7 @@ system's page size (the value would be very large, that's millions of trillions)
#### **--memory-reservation**=_number_[_unit_]
Memory soft limit. A _unit_ can be **b** (bytes), **k** (kilobytes), **m** (megabytes), or **g** (gigabytes).
Memory soft limit. A _unit_ can be **b** (bytes), **k** (kibibytes), **m** (mebibytes), or **g** (gibibytes).
After setting memory reservation, when the system detects memory contention
or low memory, containers are forced to restrict their consumption to their
@ -616,7 +616,7 @@ as memory limit.
#### **--memory-swap**=_number_[_unit_]
A limit value equal to memory plus swap.
A _unit_ can be **b** (bytes), **k** (kilobytes), **m** (megabytes), or **g** (gigabytes).
A _unit_ can be **b** (bytes), **k** (kibibytes), **m** (mebibytes), or **g** (gibibytes).
Must be used with the **-m** (**--memory**) flag.
The argument value should always be larger than that of
@ -862,22 +862,27 @@ points, Apparmor/SELinux separation, and Seccomp filters are all disabled.
Rootless containers cannot have more privileges than the account that launched them.
#### **--publish**, **-p**=_ip_:_hostPort_:_containerPort_ | _ip_::_containerPort_ | _hostPort_:_containerPort_ | _containerPort_
#### **--publish**, **-p**=[[_ip_:][_hostPort_]:]_containerPort_[/_protocol_]
Publish a container's port, or range of ports, to the host.
Both hostPort and containerPort can be specified as a range of ports.
When specifying ranges for both, the number of container ports in the range must match the number of host ports in the range.
When specifying ranges for both, the number of container ports in the
range must match the number of host ports in the range.
If host IP is set to 0.0.0.0 or not set at all, the port will be bound on all IPs on the host.
By default, Podman will publish TCP ports. To publish a UDP port instead, give
`udp` as protocol. To publish both TCP and UDP ports, set `--publish` twice,
with `tcp`, and `udp` as protocols respectively. Rootful containers can also
publish ports using the `sctp` protocol.
Host port does not have to be specified (e.g. `podman run -p 127.0.0.1::80`).
If it is not, the container port will be randomly assigned a port on the host.
Use **podman port** to see the actual mapping: **podman port $CONTAINER $CONTAINERPORT**.
Use **podman port** to see the actual mapping: `podman port $CONTAINER $CONTAINERPORT`.
**Note:** if a container will be run within a pod, it is not necessary to publish the port for
**Note:** If a container will be run within a pod, it is not necessary to publish the port for
the containers in the pod. The port must only be published by the pod itself. Pod network
stacks act like the network stack on the host - you have a variety of containers in the pod,
and programs in the container, all sharing a single interface and IP address, and
@ -1051,7 +1056,7 @@ Note: Labeling can be disabled for all containers by setting **label=false** in
#### **--shm-size**=_number_[_unit_]
Size of _/dev/shm_. A _unit_ can be **b** (bytes), **k** (kilobytes), **m** (megabytes), or **g** (gigabytes).
Size of _/dev/shm_. A _unit_ can be **b** (bytes), **k** (kibibytes), **m** (mebibytes), or **g** (gibibytes).
If you omit the unit, the system uses bytes. If you omit the size entirely, the default is **64m**.
When _size_ is **0**, there is no limit on the amount of memory used for IPC by the container.
@ -1471,14 +1476,12 @@ visible on host and vice versa. Making a volume **slave** enables only one
way mount propagation and that is mounts done on host under that volume
will be visible inside container but not the other way around. <sup>[[1]](#Footnote1)</sup>
To control mount propagation property of volume one can use [**r**]**shared**,
[**r**]**slave**, [**r**]**private** or [**r**]**unbindable** propagation flag.
Propagation property can be specified only for bind mounted volumes and not for
internal volumes or named volumes. For mount propagation to work source mount
point (mount point where source dir is mounted on) has to have right propagation
properties. For shared volumes, source mount point has to be shared. And for
slave volumes, source mount has to be either shared or slave.
<sup>[[1]](#Footnote1)</sup>
To control mount propagation property of a volume one can use the [**r**]**shared**,
[**r**]**slave**, [**r**]**private** or the [**r**]**unbindable** propagation flag.
For mount propagation to work the source mount point (the mount point where source dir
is mounted on) has to have the right propagation properties. For shared volumes, the
source mount point has to be shared. And for slave volumes, the source mount point
has to be either shared or slave. <sup>[[1]](#Footnote1)</sup>
If you want to recursively mount a volume and all of its submounts into a
container, then you can use the **rbind** option. By default the bind option is

View file

@ -20,10 +20,10 @@ Valid placeholders for the Go template listed below:
| **Placeholder** | **Description** |
| --------------- | ----------------------------------------------------------------------------- |
| *.Name* | Connection Name/Identifier |
| *.Identity* | Path to file containing SSH identity |
| *.URI* | URI to podman service. Valid schemes are ssh://[user@]*host*[:port]*Unix domain socket*[?secure=True], unix://*Unix domain socket*, and tcp://localhost[:*port*] |
| *.Default* | Indicates whether connection is the default |
| .Name | Connection Name/Identifier |
| .Identity | Path to file containing SSH identity |
| .URI | URI to podman service. Valid schemes are ssh://[user@]*host*[:port]*Unix domain socket*[?secure=True], unix://*Unix domain socket*, and tcp://localhost[:*port*] |
| .Default | Indicates whether connection is the default |
## EXAMPLE
```

View file

@ -162,6 +162,11 @@ func (s *BoltState) Refresh() error {
return err
}
namesBucket, err := getNamesBucket(tx)
if err != nil {
return err
}
ctrsBucket, err := getCtrBucket(tx)
if err != nil {
return err
@ -192,6 +197,7 @@ func (s *BoltState) Refresh() error {
// PID, mountpoint, and state for all of them
// Then save the modified state
// Also clear all network namespaces
toRemoveIDs := []string{}
err = idBucket.ForEach(func(id, name []byte) error {
ctrBkt := ctrsBucket.Bucket(id)
if ctrBkt == nil {
@ -199,8 +205,16 @@ func (s *BoltState) Refresh() error {
podBkt := podsBucket.Bucket(id)
if podBkt == nil {
// This is neither a pod nor a container
// Error out on the dangling ID
return errors.Wrapf(define.ErrInternal, "id %s is not a pod or a container", string(id))
// Something is seriously wrong, but
// continue on and try to clean up the
// state and become consistent.
// Just note what needs to be removed
// for now - ForEach says you shouldn't
// remove things from the table during
// it.
logrus.Errorf("Database issue: dangling ID %s found (not a pod or container) - removing", string(id))
toRemoveIDs = append(toRemoveIDs, string(id))
return nil
}
// Get the state
@ -285,6 +299,24 @@ func (s *BoltState) Refresh() error {
return err
}
// Remove dangling IDs.
for _, id := range toRemoveIDs {
// Look up the ID to see if we also have a dangling name
// in the DB.
name := idBucket.Get([]byte(id))
if name != nil {
if testID := namesBucket.Get(name); testID != nil {
logrus.Infof("Found dangling name %s (ID %s) in database", string(name), id)
if err := namesBucket.Delete(name); err != nil {
return errors.Wrapf(err, "error removing dangling name %s (ID %s) from database", string(name), id)
}
}
}
if err := idBucket.Delete([]byte(id)); err != nil {
return errors.Wrapf(err, "error removing dangling ID %s from database", id)
}
}
// Now refresh volumes
err = allVolsBucket.ForEach(func(id, name []byte) error {
dbVol := volBucket.Bucket(id)

View file

@ -986,6 +986,10 @@ func (c *Container) cGroupPath() (string, error) {
var cgroupPath string
for _, line := range bytes.Split(lines, []byte("\n")) {
// skip last empty line
if len(line) == 0 {
continue
}
// cgroups(7) nails it down to three fields with the 3rd
// pointing to the cgroup's path which works both on v1 and v2.
fields := bytes.Split(line, []byte(":"))

View file

@ -123,7 +123,18 @@ func (c *Container) StartAndAttach(ctx context.Context, streams *define.AttachSt
// Attach to the container before starting it
go func() {
if err := c.attach(streams, keys, resize, true, startedChan, nil); err != nil {
// Start resizing
if c.LogDriver() != define.PassthroughLogging {
registerResizeFunc(resize, c.bundlePath())
}
opts := new(AttachOptions)
opts.Streams = streams
opts.DetachKeys = &keys
opts.Start = true
opts.Started = startedChan
if err := c.ociRuntime.Attach(c, opts); err != nil {
attachChan <- err
}
close(attachChan)
@ -261,8 +272,18 @@ func (c *Container) Attach(streams *define.AttachStreams, keys string, resize <-
}()
}
// Start resizing
if c.LogDriver() != define.PassthroughLogging {
registerResizeFunc(resize, c.bundlePath())
}
opts := new(AttachOptions)
opts.Streams = streams
opts.DetachKeys = &keys
opts.AttachReady = attachRdy
c.newContainerEvent(events.Attach)
return c.attach(streams, keys, resize, false, nil, attachRdy)
return c.ociRuntime.Attach(c, opts)
}
// HTTPAttach forwards an attach session over a hijacked HTTP session.

View file

@ -243,12 +243,12 @@ type ContainerNetworkConfig struct {
// This cannot be set unless CreateNetNS is set.
// If not set, the container will be dynamically assigned an IP by CNI.
// Deprecated: Do no use this anymore, this is only for DB backwards compat.
StaticIP net.IP `json:"staticIP"`
StaticIP net.IP `json:"staticIP,omitempty"`
// StaticMAC is a static MAC to request for the container.
// This cannot be set unless CreateNetNS is set.
// If not set, the container will be dynamically assigned a MAC by CNI.
// Deprecated: Do no use this anymore, this is only for DB backwards compat.
StaticMAC types.HardwareAddr `json:"staticMAC"`
StaticMAC types.HardwareAddr `json:"staticMAC,omitempty"`
// PortMappings are the ports forwarded to the container's network
// namespace
// These are not used unless CreateNetNS is true

View file

@ -1000,6 +1000,9 @@ func (c *Container) completeNetworkSetup() error {
if err := c.runtime.setupNetNS(c); err != nil {
return err
}
if err := c.save(); err != nil {
return err
}
state := c.state
// collect any dns servers that cni tells us to use (dnsname)
for _, status := range c.getNetworkStatus() {

View file

@ -36,6 +36,7 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/subscriptions"
"github.com/containers/common/pkg/umask"
cutil "github.com/containers/common/pkg/util"
is "github.com/containers/image/v5/storage"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/libpod/events"
@ -393,7 +394,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
overrides := c.getUserOverrides()
execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, c.config.User, overrides)
if err != nil {
if util.StringInSlice(c.config.User, c.config.HostUsers) {
if cutil.StringInSlice(c.config.User, c.config.HostUsers) {
execUser, err = lookupHostUser(c.config.User)
}
if err != nil {
@ -2389,7 +2390,7 @@ func (c *Container) generateResolvConf() error {
}
if len(c.config.DNSSearch) > 0 || len(c.runtime.config.Containers.DNSSearches) > 0 {
if !util.StringInSlice(".", c.config.DNSSearch) {
if !cutil.StringInSlice(".", c.config.DNSSearch) {
search = append(search, c.runtime.config.Containers.DNSSearches...)
search = append(search, c.config.DNSSearch...)
}
@ -3108,7 +3109,7 @@ func (c *Container) getOCICgroupPath() (string, error) {
case c.config.NoCgroups:
return "", nil
case c.config.CgroupsMode == cgroupSplit:
selfCgroup, err := utils.GetOwnCgroup()
selfCgroup, err := utils.GetOwnCgroupDisallowRoot()
if err != nil {
return "", err
}
@ -3282,7 +3283,7 @@ func (c *Container) fixVolumePermissions(v *ContainerNamedVolume) error {
return err
}
stat := st.Sys().(*syscall.Stat_t)
atime := time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec))
atime := time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) // nolint: unconvert
if err := os.Chtimes(mountPoint, atime, st.ModTime()); err != nil {
return err
}

View file

@ -75,7 +75,6 @@ func (c *Container) readFromLogFile(ctx context.Context, options *logs.LogOption
go func() {
defer options.WaitGroup.Done()
var partial string
for line := range t.Lines {
select {
case <-ctx.Done():
@ -89,13 +88,6 @@ func (c *Container) readFromLogFile(ctx context.Context, options *logs.LogOption
logrus.Errorf("Getting new log line: %v", err)
continue
}
if nll.Partial() {
partial += nll.Msg
continue
} else if !nll.Partial() && len(partial) > 0 {
nll.Msg = partial + nll.Msg
partial = ""
}
nll.CID = c.ID()
nll.CName = c.Name()
nll.ColorID = colorID

View file

@ -80,6 +80,7 @@ type InspectPodInfraConfig struct {
HostNetwork bool
// StaticIP is a static IPv4 that will be assigned to the infra
// container and then used by the pod.
// swagger:strfmt ipv4
StaticIP net.IP
// StaticMAC is a static MAC address that will be assigned to the infra
// container and then used by the pod.

View file

@ -89,8 +89,8 @@ func (p *Pod) newPodEvent(status events.Status) {
}
}
// newSystemEvent creates a new event for libpod as a whole.
func (r *Runtime) newSystemEvent(status events.Status) {
// NewSystemEvent creates a new event for libpod as a whole.
func (r *Runtime) NewSystemEvent(status events.Status) {
e := events.NewEvent(status)
e.Type = events.System

View file

@ -150,6 +150,8 @@ func StringToStatus(name string) (Status, error) {
switch name {
case Attach.String():
return Attach, nil
case AutoUpdate.String():
return AutoUpdate, nil
case Build.String():
return Build, nil
case Checkpoint.String():

View file

@ -7,6 +7,7 @@ import (
"os/exec"
"strings"
"github.com/containers/podman/v4/pkg/errorhandling"
"github.com/containers/podman/v4/pkg/rootless"
"github.com/containers/podman/v4/pkg/systemd"
"github.com/pkg/errors"
@ -46,6 +47,17 @@ func (c *Container) createTimer() error {
return nil
}
// Wait for a message on the channel. Throw an error if the message is not "done".
func systemdOpSuccessful(c chan string) error {
msg := <-c
switch msg {
case "done":
return nil
default:
return fmt.Errorf("expected %q but received %q", "done", msg)
}
}
// startTimer starts a systemd timer for the healthchecks
func (c *Container) startTimer() error {
if c.disableHealthCheckSystemd() {
@ -56,8 +68,17 @@ func (c *Container) startTimer() error {
return errors.Wrapf(err, "unable to get systemd connection to start healthchecks")
}
defer conn.Close()
_, err = conn.StartUnitContext(context.Background(), fmt.Sprintf("%s.service", c.ID()), "fail", nil)
return err
startFile := fmt.Sprintf("%s.service", c.ID())
startChan := make(chan string)
if _, err := conn.StartUnitContext(context.Background(), startFile, "fail", startChan); err != nil {
return err
}
if err := systemdOpSuccessful(startChan); err != nil {
return fmt.Errorf("starting systemd health-check timer %q: %w", startFile, err)
}
return nil
}
// removeTransientFiles removes the systemd timer and unit files
@ -71,30 +92,37 @@ func (c *Container) removeTransientFiles(ctx context.Context) error {
return errors.Wrapf(err, "unable to get systemd connection to remove healthchecks")
}
defer conn.Close()
// Errors are returned at the very end. Let's make sure to stop and
// clean up as much as possible.
stopErrors := []error{}
// Stop the timer before the service to make sure the timer does not
// fire after the service is stopped.
timerChan := make(chan string)
timerFile := fmt.Sprintf("%s.timer", c.ID())
if _, err := conn.StopUnitContext(ctx, timerFile, "fail", timerChan); err != nil {
if !strings.HasSuffix(err.Error(), ".timer not loaded.") {
stopErrors = append(stopErrors, fmt.Errorf("removing health-check timer %q: %w", timerFile, err))
}
} else if err := systemdOpSuccessful(timerChan); err != nil {
stopErrors = append(stopErrors, fmt.Errorf("stopping systemd health-check timer %q: %w", timerFile, err))
}
// Reset the service before stopping it to make sure it's being removed
// on stop.
serviceChan := make(chan string)
serviceFile := fmt.Sprintf("%s.service", c.ID())
// If the service has failed (the healthcheck has failed), then
// the .service file is not removed on stopping the unit file. If
// we check the properties of the service, it will automatically
// reset the state. But checking the state takes msecs vs usecs to
// blindly call reset.
if err := conn.ResetFailedUnitContext(ctx, serviceFile); err != nil {
logrus.Debugf("failed to reset unit file: %q", err)
logrus.Debugf("Failed to reset unit file: %q", err)
}
if _, err := conn.StopUnitContext(ctx, serviceFile, "fail", serviceChan); err != nil {
if !strings.HasSuffix(err.Error(), ".service not loaded.") {
stopErrors = append(stopErrors, fmt.Errorf("removing health-check service %q: %w", serviceFile, err))
}
} else if err := systemdOpSuccessful(serviceChan); err != nil {
stopErrors = append(stopErrors, fmt.Errorf("stopping systemd health-check service %q: %w", serviceFile, err))
}
// We want to ignore errors where the timer unit and/or service unit has already
// been removed. The error return is generic so we have to check against the
// string in the error
if _, err = conn.StopUnitContext(ctx, serviceFile, "fail", nil); err != nil {
if !strings.HasSuffix(err.Error(), ".service not loaded.") {
return errors.Wrapf(err, "unable to remove service file")
}
}
if _, err = conn.StopUnitContext(ctx, timerFile, "fail", nil); err != nil {
if strings.HasSuffix(err.Error(), ".timer not loaded.") {
return nil
}
}
return err
return errorhandling.JoinErrors(stopErrors)
}

View file

@ -406,26 +406,25 @@ func getCPUUtilization() (*define.CPUUsage, error) {
}
defer f.Close()
scanner := bufio.NewScanner(f)
// Read firt line of /proc/stat
// Read first line of /proc/stat that has entries for system ("cpu" line)
for scanner.Scan() {
break
}
// column 1 is user, column 3 is system, column 4 is idle
stats := strings.Split(scanner.Text(), " ")
stats := strings.Fields(scanner.Text())
return statToPercent(stats)
}
func statToPercent(stats []string) (*define.CPUUsage, error) {
// There is always an extra space between cpu and the first metric
userTotal, err := strconv.ParseFloat(stats[2], 64)
userTotal, err := strconv.ParseFloat(stats[1], 64)
if err != nil {
return nil, errors.Wrapf(err, "unable to parse user value %q", stats[1])
}
systemTotal, err := strconv.ParseFloat(stats[4], 64)
systemTotal, err := strconv.ParseFloat(stats[3], 64)
if err != nil {
return nil, errors.Wrapf(err, "unable to parse system value %q", stats[3])
}
idleTotal, err := strconv.ParseFloat(stats[5], 64)
idleTotal, err := strconv.ParseFloat(stats[4], 64)
if err != nil {
return nil, errors.Wrapf(err, "unable to parse idle value %q", stats[4])
}

View file

@ -20,7 +20,7 @@ func Test_statToPercent(t *testing.T) {
}{
{
name: "GoodParse",
args: args{in0: []string{"cpu", " ", "33628064", "27537", "9696996", "1314806705", "588142", "4775073", "2789228", "0", "598711", "0"}},
args: args{in0: []string{"cpu", "33628064", "27537", "9696996", "1314806705", "588142", "4775073", "2789228", "0", "598711", "0"}},
want: &define.CPUUsage{
UserPercent: 2.48,
SystemPercent: 0.71,
@ -30,19 +30,19 @@ func Test_statToPercent(t *testing.T) {
},
{
name: "BadUserValue",
args: args{in0: []string{"cpu", " ", "k", "27537", "9696996", "1314806705", "588142", "4775073", "2789228", "0", "598711", "0"}},
args: args{in0: []string{"cpu", "k", "27537", "9696996", "1314806705", "588142", "4775073", "2789228", "0", "598711", "0"}},
want: nil,
wantErr: assert.Error,
},
{
name: "BadSystemValue",
args: args{in0: []string{"cpu", " ", "33628064", "27537", "k", "1314806705", "588142", "4775073", "2789228", "0", "598711", "0"}},
args: args{in0: []string{"cpu", "33628064", "27537", "k", "1314806705", "588142", "4775073", "2789228", "0", "598711", "0"}},
want: nil,
wantErr: assert.Error,
},
{
name: "BadIdleValue",
args: args{in0: []string{"cpu", " ", "33628064", "27537", "9696996", "k", "588142", "4775073", "2789228", "0", "598711", "0"}},
args: args{in0: []string{"cpu", "33628064", "27537", "9696996", "k", "588142", "4775073", "2789228", "0", "598711", "0"}},
want: nil,
wantErr: assert.Error,
},

View file

@ -14,6 +14,7 @@ import (
"github.com/containers/common/libnetwork/types"
"github.com/containers/common/pkg/config"
cutil "github.com/containers/common/pkg/util"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/env"
v1 "github.com/containers/podman/v4/pkg/k8s.io/api/core/v1"
@ -515,7 +516,7 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container) (*v1.Pod,
podDNS.Nameservers = make([]string, 0)
}
for _, s := range servers {
if !util.StringInSlice(s, podDNS.Nameservers) { // only append if it does not exist
if !cutil.StringInSlice(s, podDNS.Nameservers) { // only append if it does not exist
podDNS.Nameservers = append(podDNS.Nameservers, s)
}
}
@ -526,7 +527,7 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container) (*v1.Pod,
podDNS.Searches = make([]string, 0)
}
for _, d := range domains {
if !util.StringInSlice(d, podDNS.Searches) { // only append if it does not exist
if !cutil.StringInSlice(d, podDNS.Searches) { // only append if it does not exist
podDNS.Searches = append(podDNS.Searches, d)
}
}
@ -543,7 +544,7 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container) (*v1.Pod,
podName := removeUnderscores(ctrs[0].Name())
// Check if the pod name and container name will end up conflicting
// Append -pod if so
if util.StringInSlice(podName, ctrNames) {
if cutil.StringInSlice(podName, ctrNames) {
podName += "-pod"
}
@ -824,7 +825,7 @@ func libpodMountsToKubeVolumeMounts(c *Container) ([]v1.VolumeMount, []v1.Volume
// generateKubePersistentVolumeClaim converts a ContainerNamedVolume to a Kubernetes PersistentVolumeClaim
func generateKubePersistentVolumeClaim(v *ContainerNamedVolume) (v1.VolumeMount, v1.Volume) {
ro := util.StringInSlice("ro", v.Options)
ro := cutil.StringInSlice("ro", v.Options)
// To avoid naming conflicts with any host path mounts, add a unique suffix to the volume's name.
name := v.Name + "-pvc"
@ -857,7 +858,7 @@ func generateKubeVolumeMount(m specs.Mount) (v1.VolumeMount, v1.Volume, error) {
name += "-host"
vm.Name = name
vm.MountPath = m.Destination
if util.StringInSlice("ro", m.Options) {
if cutil.StringInSlice("ro", m.Options) {
vm.ReadOnly = true
}
@ -915,7 +916,7 @@ func determineCapAddDropFromCapabilities(defaultCaps, containerCaps []string) *v
// Find caps in the defaultCaps but not in the container's
// those indicate a dropped cap
for _, capability := range defaultCaps {
if !util.StringInSlice(capability, containerCaps) {
if !cutil.StringInSlice(capability, containerCaps) {
if _, ok := dedupDrop[capability]; !ok {
drop = append(drop, v1.Capability(capability))
dedupDrop[capability] = true
@ -925,7 +926,7 @@ func determineCapAddDropFromCapabilities(defaultCaps, containerCaps []string) *v
// Find caps in the container but not in the defaults; those indicate
// an added cap
for _, capability := range containerCaps {
if !util.StringInSlice(capability, defaultCaps) {
if !cutil.StringInSlice(capability, defaultCaps) {
if _, ok := dedupAdd[capability]; !ok {
add = append(add, v1.Capability(capability))
dedupAdd[capability] = true

View file

@ -25,13 +25,13 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/machine"
"github.com/containers/common/pkg/netns"
"github.com/containers/common/pkg/util"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/libpod/events"
"github.com/containers/podman/v4/pkg/errorhandling"
"github.com/containers/podman/v4/pkg/namespaces"
"github.com/containers/podman/v4/pkg/resolvconf"
"github.com/containers/podman/v4/pkg/rootless"
"github.com/containers/podman/v4/pkg/util"
"github.com/containers/podman/v4/utils"
"github.com/containers/storage/pkg/lockfile"
spec "github.com/opencontainers/runtime-spec/specs-go"
@ -930,6 +930,8 @@ func (r *Runtime) reloadContainerNetwork(ctr *Container) (map[string]types.Statu
return r.configureNetNS(ctr, ctr.state.NetNS)
}
// TODO (5.0): return the statistics per network interface
// This would allow better compat with docker.
func getContainerNetIO(ctr *Container) (*netlink.LinkStatistics, error) {
var netStats *netlink.LinkStatistics
@ -943,21 +945,39 @@ func getContainerNetIO(ctr *Container) (*netlink.LinkStatistics, error) {
return nil, nil
}
// FIXME get the interface from the container netstatus
dev := "eth0"
netMode := ctr.config.NetMode
netStatus := ctr.getNetworkStatus()
if otherCtr != nil {
netMode = otherCtr.config.NetMode
netStatus = otherCtr.getNetworkStatus()
}
if netMode.IsSlirp4netns() {
dev = "tap0"
// create a fake status with correct interface name for the logic below
netStatus = map[string]types.StatusBlock{
"slirp4netns": {
Interfaces: map[string]types.NetInterface{"tap0": {}},
},
}
}
err := ns.WithNetNSPath(netNSPath, func(_ ns.NetNS) error {
link, err := netlink.LinkByName(dev)
if err != nil {
return err
for _, status := range netStatus {
for dev := range status.Interfaces {
link, err := netlink.LinkByName(dev)
if err != nil {
return err
}
if netStats == nil {
netStats = link.Attrs().Statistics
continue
}
// Currently only Tx/RxBytes are used.
// In the future we should return all stats per interface so that
// api users have a better options.
stats := link.Attrs().Statistics
netStats.TxBytes += stats.TxBytes
netStats.RxBytes += stats.RxBytes
}
}
netStats = link.Attrs().Statistics
return nil
})
return netStats, err

View file

@ -12,9 +12,7 @@ import (
// management logic - e.g., we do not expect it to determine on its own that
// calling 'UnpauseContainer()' on a container that is not paused is an error.
// The code calling the OCIRuntime will manage this.
// TODO: May want to move the Attach() code under this umbrella. It's highly OCI
// runtime dependent.
// TODO: May want to move the conmon cleanup code here too - it depends on
// TODO: May want to move the conmon cleanup code here - it depends on
// Conmon being in use.
type OCIRuntime interface {
// Name returns the name of the runtime.
@ -52,6 +50,8 @@ type OCIRuntime interface {
// UnpauseContainer unpauses the given container.
UnpauseContainer(ctr *Container) error
// Attach to a container.
Attach(ctr *Container, params *AttachOptions) error
// HTTPAttach performs an attach intended to be transported over HTTP.
// For terminal attach, the container's output will be directly streamed
// to output; otherwise, STDOUT and STDERR will be multiplexed, with
@ -149,6 +149,30 @@ type OCIRuntime interface {
RuntimeInfo() (*define.ConmonInfo, *define.OCIRuntimeInfo, error)
}
// AttachOptions are options used when attached to a container or an exec
// session.
type AttachOptions struct {
// Streams are the streams to attach to.
Streams *define.AttachStreams
// DetachKeys containers the key combination that will detach from the
// attach session. Empty string is assumed as no detach keys - user
// detach is impossible. If unset, defaults from containers.conf will be
// used.
DetachKeys *string
// InitialSize is the initial size of the terminal. Set before the
// attach begins.
InitialSize *define.TerminalSize
// AttachReady signals when the attach has successfully completed and
// streaming has begun.
AttachReady chan<- bool
// Start indicates that the container should be started if it is not
// already running.
Start bool
// Started signals when the container has been successfully started.
// Required if Start is true, unused otherwise.
Started chan<- bool
}
// ExecOptions are options passed into ExecContainer. They control the command
// that will be executed and how the exec will proceed.
type ExecOptions struct {

View file

@ -38,19 +38,28 @@ func openUnixSocket(path string) (*net.UnixConn, error) {
return net.DialUnix("unixpacket", nil, &net.UnixAddr{Name: fmt.Sprintf("/proc/self/fd/%d", fd), Net: "unixpacket"})
}
// Attach to the given container
// Does not check if state is appropriate
// started is only required if startContainer is true
func (c *Container) attach(streams *define.AttachStreams, keys string, resize <-chan define.TerminalSize, startContainer bool, started chan bool, attachRdy chan<- bool) error {
// Attach to the given container.
// Does not check if state is appropriate.
// started is only required if startContainer is true.
func (r *ConmonOCIRuntime) Attach(c *Container, params *AttachOptions) error {
passthrough := c.LogDriver() == define.PassthroughLogging
if !streams.AttachOutput && !streams.AttachError && !streams.AttachInput && !passthrough {
if params == nil || params.Streams == nil {
return errors.Wrapf(define.ErrInternal, "must provide parameters to Attach")
}
if !params.Streams.AttachOutput && !params.Streams.AttachError && !params.Streams.AttachInput && !passthrough {
return errors.Wrapf(define.ErrInvalidArg, "must provide at least one stream to attach to")
}
if startContainer && started == nil {
if params.Start && params.Started == nil {
return errors.Wrapf(define.ErrInternal, "started chan not passed when startContainer set")
}
keys := config.DefaultDetachKeys
if params.DetachKeys != nil {
keys = *params.DetachKeys
}
detachKeys, err := processDetachKeys(keys)
if err != nil {
return err
@ -60,7 +69,12 @@ func (c *Container) attach(streams *define.AttachStreams, keys string, resize <-
if !passthrough {
logrus.Debugf("Attaching to container %s", c.ID())
registerResizeFunc(resize, c.bundlePath())
// If we have a resize, do it.
if params.InitialSize != nil {
if err := r.AttachResize(c, *params.InitialSize); err != nil {
return err
}
}
attachSock, err := c.AttachSocketPath()
if err != nil {
@ -80,22 +94,22 @@ func (c *Container) attach(streams *define.AttachStreams, keys string, resize <-
// If starting was requested, start the container and notify when that's
// done.
if startContainer {
if params.Start {
if err := c.start(); err != nil {
return err
}
started <- true
params.Started <- true
}
if passthrough {
return nil
}
receiveStdoutError, stdinDone := setupStdioChannels(streams, conn, detachKeys)
if attachRdy != nil {
attachRdy <- true
receiveStdoutError, stdinDone := setupStdioChannels(params.Streams, conn, detachKeys)
if params.AttachReady != nil {
params.AttachReady <- true
}
return readStdio(conn, streams, receiveStdoutError, stdinDone)
return readStdio(conn, params.Streams, receiveStdoutError, stdinDone)
}
// Attach to the given container's exec session

View file

@ -391,7 +391,7 @@ func (r *ConmonOCIRuntime) startExec(c *Container, sessionID string, options *Ex
}
defer processFile.Close()
args := r.sharedConmonArgs(c, sessionID, c.execBundlePath(sessionID), c.execPidPath(sessionID), c.execLogPath(sessionID), c.execExitFileDir(sessionID), ociLog, define.NoLogging, "")
args := r.sharedConmonArgs(c, sessionID, c.execBundlePath(sessionID), c.execPidPath(sessionID), c.execLogPath(sessionID), c.execExitFileDir(sessionID), ociLog, define.NoLogging, c.config.LogTag)
if options.PreserveFDs > 0 {
args = append(args, formatRuntimeOpts("--preserve-fds", fmt.Sprintf("%d", options.PreserveFDs))...)

View file

@ -36,7 +36,6 @@ import (
"github.com/containers/podman/v4/utils"
"github.com/containers/storage/pkg/homedir"
pmount "github.com/containers/storage/pkg/mount"
"github.com/coreos/go-systemd/v22/daemon"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
@ -412,8 +411,8 @@ func (r *ConmonOCIRuntime) KillContainer(ctr *Container, signal uint, all bool)
if err2 := r.UpdateContainerStatus(ctr); err2 != nil {
logrus.Infof("Error updating status for container %s: %v", ctr.ID(), err2)
}
if ctr.state.State == define.ContainerStateExited {
return nil
if ctr.ensureState(define.ContainerStateStopped, define.ContainerStateExited) {
return define.ErrCtrStateInvalid
}
return errors.Wrapf(err, "error sending signal to container %s", ctr.ID())
}
@ -1279,19 +1278,6 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
// conmon not having a pid file is a valid state, so don't set it if we don't have it
logrus.Infof("Got Conmon PID as %d", conmonPID)
ctr.state.ConmonPID = conmonPID
// Send the MAINPID via sdnotify if needed.
switch ctr.config.SdNotifyMode {
case define.SdNotifyModeContainer, define.SdNotifyModeIgnore:
// Nothing to do or conmon takes care of it already.
default:
if sent, err := daemon.SdNotify(false, fmt.Sprintf("MAINPID=%d", conmonPID)); err != nil {
logrus.Errorf("Notifying systemd of Conmon PID: %v", err)
} else if sent {
logrus.Debugf("Notify MAINPID sent successfully")
}
}
}
runtimeRestoreDuration := func() int64 {

View file

@ -108,6 +108,11 @@ func (r *MissingRuntime) UnpauseContainer(ctr *Container) error {
return r.printError()
}
// Attach is not available as the runtime is missing
func (r *MissingRuntime) Attach(ctr *Container, params *AttachOptions) error {
return r.printError()
}
// HTTPAttach is not available as the runtime is missing
func (r *MissingRuntime) HTTPAttach(ctr *Container, req *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, detachKeys *string, cancel <-chan bool, hijackDone chan<- bool, streamAttach, streamLogs bool) error {
return r.printError()

View file

@ -11,6 +11,7 @@ import (
nettypes "github.com/containers/common/libnetwork/types"
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/secrets"
cutil "github.com/containers/common/pkg/util"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v4/libpod/define"
@ -604,7 +605,7 @@ func WithSdNotifyMode(mode string) CtrCreateOption {
}
// verify values
if len(mode) > 0 && !util.StringInSlice(strings.ToLower(mode), SdNotifyModeValues) {
if len(mode) > 0 && !cutil.StringInSlice(strings.ToLower(mode), SdNotifyModeValues) {
return errors.Wrapf(define.ErrInvalidArg, "--sdnotify values must be one of %q", strings.Join(SdNotifyModeValues, ", "))
}

View file

@ -167,8 +167,8 @@ func (p *Pod) NetworkMode() string {
return infra.NetworkMode()
}
// PidMode returns the PID mode given by the user ex: pod, private...
func (p *Pod) PidMode() string {
// Namespace Mode returns the given NS mode provided by the user ex: host, private...
func (p *Pod) NamespaceMode(kind specs.LinuxNamespaceType) string {
infra, err := p.runtime.GetContainer(p.state.InfraContainerID)
if err != nil {
return ""
@ -176,28 +176,7 @@ func (p *Pod) PidMode() string {
ctrSpec := infra.config.Spec
if ctrSpec != nil && ctrSpec.Linux != nil {
for _, ns := range ctrSpec.Linux.Namespaces {
if ns.Type == specs.PIDNamespace {
if ns.Path != "" {
return fmt.Sprintf("ns:%s", ns.Path)
}
return "private"
}
}
return "host"
}
return ""
}
// PidMode returns the PID mode given by the user ex: pod, private...
func (p *Pod) UserNSMode() string {
infra, err := p.infraContainer()
if err != nil {
return ""
}
ctrSpec := infra.config.Spec
if ctrSpec != nil && ctrSpec.Linux != nil {
for _, ns := range ctrSpec.Linux.Namespaces {
if ns.Type == specs.UserNamespace {
if ns.Type == kind {
if ns.Path != "" {
return fmt.Sprintf("ns:%s", ns.Path)
}

View file

@ -8,6 +8,7 @@ import (
"github.com/containers/podman/v4/libpod/events"
"github.com/containers/podman/v4/pkg/parallel"
"github.com/containers/podman/v4/pkg/rootless"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@ -600,8 +601,8 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
infraConfig.CPUPeriod = p.CPUPeriod()
infraConfig.CPUQuota = p.CPUQuota()
infraConfig.CPUSetCPUs = p.ResourceLim().CPU.Cpus
infraConfig.PidNS = p.PidMode()
infraConfig.UserNS = p.UserNSMode()
infraConfig.PidNS = p.NamespaceMode(specs.PIDNamespace)
infraConfig.UserNS = p.NamespaceMode(specs.UserNamespace)
namedVolumes, mounts := infra.SortUserVolumes(infra.config.Spec)
inspectMounts, err = infra.GetMounts(namedVolumes, infra.config.ImageVolumes, mounts)
infraSecurity = infra.GetSecurityOptions()

View file

@ -345,7 +345,7 @@ func makeRuntime(runtime *Runtime) (retErr error) {
// it will try to use existing XDG_RUNTIME_DIR
// if current user has no write access to XDG_RUNTIME_DIR we will fail later
if err := unix.Access(runtime.storageConfig.RunRoot, unix.W_OK); err != nil {
msg := "XDG_RUNTIME_DIR is pointing to a path which is not writable. Most likely podman will fail."
msg := fmt.Sprintf("RunRoot is pointing to a path (%s) which is not writable. Most likely podman will fail.", runtime.storageConfig.RunRoot)
if errors.Is(err, os.ErrNotExist) {
// if dir does not exists try to create it
if err := os.MkdirAll(runtime.storageConfig.RunRoot, 0700); err != nil {
@ -916,7 +916,7 @@ func (r *Runtime) refresh(alivePath string) error {
}
defer file.Close()
r.newSystemEvent(events.Refresh)
r.NewSystemEvent(events.Refresh)
return nil
}
@ -1058,7 +1058,7 @@ func (r *Runtime) mergeDBConfig(dbConfig *DBConfig) {
if !r.storageSet.GraphDriverNameSet && dbConfig.GraphDriver != "" {
if r.storageConfig.GraphDriverName != dbConfig.GraphDriver &&
r.storageConfig.GraphDriverName != "" {
logrus.Errorf("User-selected graph driver %q overwritten by graph driver %q from database - delete libpod local files to resolve",
logrus.Errorf("User-selected graph driver %q overwritten by graph driver %q from database - delete libpod local files to resolve. May prevent use of images created by other tools",
r.storageConfig.GraphDriverName, dbConfig.GraphDriver)
}
r.storageConfig.GraphDriverName = dbConfig.GraphDriver

View file

@ -13,6 +13,7 @@ import (
"github.com/containers/common/libnetwork/types"
"github.com/containers/common/pkg/cgroups"
"github.com/containers/common/pkg/config"
cutil "github.com/containers/common/pkg/util"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/libpod/events"
"github.com/containers/podman/v4/libpod/shutdown"
@ -246,7 +247,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
for _, opts := range ctr.config.Networks {
if opts.InterfaceName != "" {
// check that no name is assigned to more than network
if util.StringInSlice(opts.InterfaceName, usedIfNames) {
if cutil.StringInSlice(opts.InterfaceName, usedIfNames) {
return nil, errors.Errorf("network interface name %q is already assigned to another network", opts.InterfaceName)
}
usedIfNames = append(usedIfNames, opts.InterfaceName)
@ -262,7 +263,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
if opts.InterfaceName == "" {
for i < 100000 {
ifName := fmt.Sprintf("eth%d", i)
if !util.StringInSlice(ifName, usedIfNames) {
if !cutil.StringInSlice(ifName, usedIfNames) {
opts.InterfaceName = ifName
usedIfNames = append(usedIfNames, ifName)
break
@ -732,7 +733,11 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, remo
// after setting the state to ContainerStateRemoving will prevent that the container is
// restarted
if err := c.removeAllExecSessions(); err != nil {
return err
if cleanupErr == nil {
cleanupErr = err
} else {
logrus.Errorf("Remove exec sessions: %v", err)
}
}
// Stop the container's storage

View file

@ -4,8 +4,8 @@ import (
"context"
"time"
"github.com/containers/common/pkg/util"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/util"
"github.com/pkg/errors"
)

View file

@ -71,7 +71,7 @@ func (r *Runtime) renumberLocks() error {
}
}
r.newSystemEvent(events.Renumber)
r.NewSystemEvent(events.Renumber)
return nil
}

View file

@ -34,8 +34,9 @@ func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*de
}
}
// returns stats with the fields' default values respective of their type
if c.state.State != define.ContainerStateRunning && c.state.State != define.ContainerStatePaused {
return stats, define.ErrCtrStateInvalid
return stats, nil
}
if previousStats == nil {

View file

@ -254,7 +254,7 @@ func KillContainer(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, err)
return
}
if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL {
if sig == 0 || sig == syscall.SIGKILL {
opts := entities.WaitOptions{
Condition: []define.ContainerStatus{define.ContainerStateExited, define.ContainerStateStopped},
Interval: time.Millisecond * 250,
@ -341,8 +341,8 @@ func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error
for idx, portMapping := range portMappings {
ports[idx] = types.Port{
IP: portMapping.HostIP,
PrivatePort: uint16(portMapping.ContainerPort),
PublicPort: uint16(portMapping.HostPort),
PrivatePort: portMapping.ContainerPort,
PublicPort: portMapping.HostPort,
Type: portMapping.Protocol,
}
}
@ -369,26 +369,28 @@ func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error
return nil, err
}
return &handlers.Container{Container: types.Container{
ID: l.ID(),
Names: []string{fmt.Sprintf("/%s", l.Name())},
Image: imageName,
ImageID: "sha256:" + imageID,
Command: strings.Join(l.Command(), " "),
Created: l.CreatedTime().Unix(),
Ports: ports,
SizeRw: sizeRW,
SizeRootFs: sizeRootFs,
Labels: l.Labels(),
State: stateStr,
Status: status,
HostConfig: struct {
NetworkMode string `json:",omitempty"`
}{
"host"},
NetworkSettings: &networkSettings,
Mounts: mounts,
},
return &handlers.Container{
Container: types.Container{
ID: l.ID(),
Names: []string{fmt.Sprintf("/%s", l.Name())},
Image: imageName,
ImageID: "sha256:" + imageID,
Command: strings.Join(l.Command(), " "),
Created: l.CreatedTime().Unix(),
Ports: ports,
SizeRw: sizeRW,
SizeRootFs: sizeRootFs,
Labels: l.Labels(),
State: stateStr,
Status: status,
HostConfig: struct {
NetworkMode string `json:",omitempty"`
}{
"host",
},
NetworkSettings: &networkSettings,
Mounts: mounts,
},
ContainerCreateConfig: types.ContainerCreateConfig{},
}, nil
}

View file

@ -2,18 +2,29 @@ package compat
import (
"encoding/json"
"fmt"
"net"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/common/libnetwork/types"
"github.com/containers/common/pkg/cgroups"
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/libpod"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/api/handlers"
"github.com/containers/podman/v4/pkg/api/handlers/utils"
api "github.com/containers/podman/v4/pkg/api/types"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/domain/infra/abi"
"github.com/containers/podman/v4/pkg/rootless"
"github.com/containers/podman/v4/pkg/specgen"
"github.com/containers/podman/v4/pkg/specgenutil"
"github.com/containers/storage"
"github.com/docker/docker/api/types/mount"
"github.com/gorilla/schema"
"github.com/pkg/errors"
)
@ -70,7 +81,7 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
}
// Take body structure and convert to cliopts
cliOpts, args, err := common.ContainerCreateToContainerCLIOpts(body, rtc)
cliOpts, args, err := cliOpts(body, rtc)
if err != nil {
utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "make cli opts()"))
return
@ -107,3 +118,462 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
}
utils.WriteResponse(w, http.StatusCreated, createResponse)
}
func stringMaptoArray(m map[string]string) []string {
a := make([]string, 0, len(m))
for k, v := range m {
a = append(a, fmt.Sprintf("%s=%s", k, v))
}
return a
}
// cliOpts converts a compat input struct to cliopts
func cliOpts(cc handlers.CreateContainerConfig, rtc *config.Config) (*entities.ContainerCreateOptions, []string, error) {
var (
capAdd []string
cappDrop []string
entrypoint *string
init bool
specPorts []types.PortMapping
)
if cc.HostConfig.Init != nil {
init = *cc.HostConfig.Init
}
// Iterate devices and convert to CLI expected string
devices := make([]string, 0, len(cc.HostConfig.Devices))
for _, dev := range cc.HostConfig.Devices {
devices = append(devices, fmt.Sprintf("%s:%s:%s", dev.PathOnHost, dev.PathInContainer, dev.CgroupPermissions))
}
// iterate blkreaddevicebps
readBps := make([]string, 0, len(cc.HostConfig.BlkioDeviceReadBps))
for _, dev := range cc.HostConfig.BlkioDeviceReadBps {
readBps = append(readBps, dev.String())
}
// iterate blkreaddeviceiops
readIops := make([]string, 0, len(cc.HostConfig.BlkioDeviceReadIOps))
for _, dev := range cc.HostConfig.BlkioDeviceReadIOps {
readIops = append(readIops, dev.String())
}
// iterate blkwritedevicebps
writeBps := make([]string, 0, len(cc.HostConfig.BlkioDeviceWriteBps))
for _, dev := range cc.HostConfig.BlkioDeviceWriteBps {
writeBps = append(writeBps, dev.String())
}
// iterate blkwritedeviceiops
writeIops := make([]string, 0, len(cc.HostConfig.BlkioDeviceWriteIOps))
for _, dev := range cc.HostConfig.BlkioDeviceWriteIOps {
writeIops = append(writeIops, dev.String())
}
// entrypoint
// can be a string or slice. if it is a slice, we need to
// marshall it to json; otherwise it should just be the string
// value
if len(cc.Config.Entrypoint) > 0 {
entrypoint = &cc.Config.Entrypoint[0]
if len(cc.Config.Entrypoint) > 1 {
b, err := json.Marshal(cc.Config.Entrypoint)
if err != nil {
return nil, nil, err
}
jsonString := string(b)
entrypoint = &jsonString
}
}
// expose ports
expose := make([]string, 0, len(cc.Config.ExposedPorts))
for p := range cc.Config.ExposedPorts {
expose = append(expose, fmt.Sprintf("%s/%s", p.Port(), p.Proto()))
}
// mounts type=tmpfs/bind,source=...,target=...=,opt=val
volSources := make(map[string]bool)
volDestinations := make(map[string]bool)
mounts := make([]string, 0, len(cc.HostConfig.Mounts))
var builder strings.Builder
for _, m := range cc.HostConfig.Mounts {
addField(&builder, "type", string(m.Type))
addField(&builder, "source", m.Source)
addField(&builder, "target", m.Target)
// Store source/dest so we don't add duplicates if a volume is
// also mentioned in cc.Volumes.
// Which Docker Compose v2.0 does, for unclear reasons...
volSources[m.Source] = true
volDestinations[m.Target] = true
if m.ReadOnly {
addField(&builder, "ro", "true")
}
addField(&builder, "consistency", string(m.Consistency))
// Map any specialized mount options that intersect between *Options and cli options
switch m.Type {
case mount.TypeBind:
if m.BindOptions != nil {
addField(&builder, "bind-propagation", string(m.BindOptions.Propagation))
addField(&builder, "bind-nonrecursive", strconv.FormatBool(m.BindOptions.NonRecursive))
}
case mount.TypeTmpfs:
if m.TmpfsOptions != nil {
addField(&builder, "tmpfs-size", strconv.FormatInt(m.TmpfsOptions.SizeBytes, 10))
addField(&builder, "tmpfs-mode", strconv.FormatUint(uint64(m.TmpfsOptions.Mode), 8))
}
case mount.TypeVolume:
// All current VolumeOpts are handled above
// See vendor/github.com/containers/common/pkg/parse/parse.go:ValidateVolumeOpts()
}
mounts = append(mounts, builder.String())
builder.Reset()
}
// dns
dns := make([]net.IP, 0, len(cc.HostConfig.DNS))
for _, d := range cc.HostConfig.DNS {
dns = append(dns, net.ParseIP(d))
}
// publish
for port, pbs := range cc.HostConfig.PortBindings {
for _, pb := range pbs {
var hostport int
var err error
if pb.HostPort != "" {
hostport, err = strconv.Atoi(pb.HostPort)
}
if err != nil {
return nil, nil, err
}
tmpPort := types.PortMapping{
HostIP: pb.HostIP,
ContainerPort: uint16(port.Int()),
HostPort: uint16(hostport),
Range: 0,
Protocol: port.Proto(),
}
specPorts = append(specPorts, tmpPort)
}
}
// special case for NetworkMode, the podman default is slirp4netns for
// rootless but for better docker compat we want bridge.
netmode := string(cc.HostConfig.NetworkMode)
if netmode == "" || netmode == "default" {
netmode = "bridge"
}
nsmode, networks, netOpts, err := specgen.ParseNetworkFlag([]string{netmode})
if err != nil {
return nil, nil, err
}
// network
// Note: we cannot emulate compat exactly here. we only allow specifics of networks to be
// defined when there is only one network.
netInfo := entities.NetOptions{
AddHosts: cc.HostConfig.ExtraHosts,
DNSOptions: cc.HostConfig.DNSOptions,
DNSSearch: cc.HostConfig.DNSSearch,
DNSServers: dns,
Network: nsmode,
PublishPorts: specPorts,
NetworkOptions: netOpts,
NoHosts: rtc.Containers.NoHosts,
}
// network names
switch {
case len(cc.NetworkingConfig.EndpointsConfig) > 0:
endpointsConfig := cc.NetworkingConfig.EndpointsConfig
networks := make(map[string]types.PerNetworkOptions, len(endpointsConfig))
for netName, endpoint := range endpointsConfig {
netOpts := types.PerNetworkOptions{}
if endpoint != nil {
netOpts.Aliases = endpoint.Aliases
// if IP address is provided
if len(endpoint.IPAddress) > 0 {
staticIP := net.ParseIP(endpoint.IPAddress)
if staticIP == nil {
return nil, nil, errors.Errorf("failed to parse the ip address %q", endpoint.IPAddress)
}
netOpts.StaticIPs = append(netOpts.StaticIPs, staticIP)
}
if endpoint.IPAMConfig != nil {
// if IPAMConfig.IPv4Address is provided
if len(endpoint.IPAMConfig.IPv4Address) > 0 {
staticIP := net.ParseIP(endpoint.IPAMConfig.IPv4Address)
if staticIP == nil {
return nil, nil, errors.Errorf("failed to parse the ipv4 address %q", endpoint.IPAMConfig.IPv4Address)
}
netOpts.StaticIPs = append(netOpts.StaticIPs, staticIP)
}
// if IPAMConfig.IPv6Address is provided
if len(endpoint.IPAMConfig.IPv6Address) > 0 {
staticIP := net.ParseIP(endpoint.IPAMConfig.IPv6Address)
if staticIP == nil {
return nil, nil, errors.Errorf("failed to parse the ipv6 address %q", endpoint.IPAMConfig.IPv6Address)
}
netOpts.StaticIPs = append(netOpts.StaticIPs, staticIP)
}
}
// If MAC address is provided
if len(endpoint.MacAddress) > 0 {
staticMac, err := net.ParseMAC(endpoint.MacAddress)
if err != nil {
return nil, nil, errors.Errorf("failed to parse the mac address %q", endpoint.MacAddress)
}
netOpts.StaticMAC = types.HardwareAddr(staticMac)
}
}
networks[netName] = netOpts
}
netInfo.Networks = networks
case len(cc.HostConfig.NetworkMode) > 0:
netInfo.Networks = networks
}
parsedTmp := make([]string, 0, len(cc.HostConfig.Tmpfs))
for path, options := range cc.HostConfig.Tmpfs {
finalString := path
if options != "" {
finalString += ":" + options
}
parsedTmp = append(parsedTmp, finalString)
}
// Note: several options here are marked as "don't need". this is based
// on speculation by Matt and I. We think that these come into play later
// like with start. We believe this is just a difference in podman/compat
cliOpts := entities.ContainerCreateOptions{
// Attach: nil, // don't need?
Authfile: "",
CapAdd: append(capAdd, cc.HostConfig.CapAdd...),
CapDrop: append(cappDrop, cc.HostConfig.CapDrop...),
CgroupParent: cc.HostConfig.CgroupParent,
CIDFile: cc.HostConfig.ContainerIDFile,
CPUPeriod: uint64(cc.HostConfig.CPUPeriod),
CPUQuota: cc.HostConfig.CPUQuota,
CPURTPeriod: uint64(cc.HostConfig.CPURealtimePeriod),
CPURTRuntime: cc.HostConfig.CPURealtimeRuntime,
CPUShares: uint64(cc.HostConfig.CPUShares),
// CPUS: 0, // don't need?
CPUSetCPUs: cc.HostConfig.CpusetCpus,
CPUSetMems: cc.HostConfig.CpusetMems,
// Detach: false, // don't need
// DetachKeys: "", // don't need
Devices: devices,
DeviceCgroupRule: nil,
DeviceReadBPs: readBps,
DeviceReadIOPs: readIops,
DeviceWriteBPs: writeBps,
DeviceWriteIOPs: writeIops,
Entrypoint: entrypoint,
Env: cc.Config.Env,
Expose: expose,
GroupAdd: cc.HostConfig.GroupAdd,
Hostname: cc.Config.Hostname,
ImageVolume: "bind",
Init: init,
Interactive: cc.Config.OpenStdin,
IPC: string(cc.HostConfig.IpcMode),
Label: stringMaptoArray(cc.Config.Labels),
LogDriver: cc.HostConfig.LogConfig.Type,
LogOptions: stringMaptoArray(cc.HostConfig.LogConfig.Config),
Name: cc.Name,
OOMScoreAdj: &cc.HostConfig.OomScoreAdj,
Arch: "",
OS: "",
Variant: "",
PID: string(cc.HostConfig.PidMode),
PIDsLimit: cc.HostConfig.PidsLimit,
Privileged: cc.HostConfig.Privileged,
PublishAll: cc.HostConfig.PublishAllPorts,
Quiet: false,
ReadOnly: cc.HostConfig.ReadonlyRootfs,
ReadOnlyTmpFS: true, // podman default
Rm: cc.HostConfig.AutoRemove,
SecurityOpt: cc.HostConfig.SecurityOpt,
StopSignal: cc.Config.StopSignal,
StorageOpts: stringMaptoArray(cc.HostConfig.StorageOpt),
Sysctl: stringMaptoArray(cc.HostConfig.Sysctls),
Systemd: "true", // podman default
TmpFS: parsedTmp,
TTY: cc.Config.Tty,
UnsetEnv: cc.UnsetEnv,
UnsetEnvAll: cc.UnsetEnvAll,
User: cc.Config.User,
UserNS: string(cc.HostConfig.UsernsMode),
UTS: string(cc.HostConfig.UTSMode),
Mount: mounts,
VolumesFrom: cc.HostConfig.VolumesFrom,
Workdir: cc.Config.WorkingDir,
Net: &netInfo,
HealthInterval: define.DefaultHealthCheckInterval,
HealthRetries: define.DefaultHealthCheckRetries,
HealthTimeout: define.DefaultHealthCheckTimeout,
HealthStartPeriod: define.DefaultHealthCheckStartPeriod,
}
if !rootless.IsRootless() {
var ulimits []string
if len(cc.HostConfig.Ulimits) > 0 {
for _, ul := range cc.HostConfig.Ulimits {
ulimits = append(ulimits, ul.String())
}
cliOpts.Ulimit = ulimits
}
}
if cc.HostConfig.Resources.NanoCPUs > 0 {
if cliOpts.CPUPeriod != 0 || cliOpts.CPUQuota != 0 {
return nil, nil, errors.Errorf("NanoCpus conflicts with CpuPeriod and CpuQuota")
}
cliOpts.CPUPeriod = 100000
cliOpts.CPUQuota = cc.HostConfig.Resources.NanoCPUs / 10000
}
// volumes
for _, vol := range cc.HostConfig.Binds {
cliOpts.Volume = append(cliOpts.Volume, vol)
// Extract the destination so we don't add duplicate mounts in
// the volumes phase.
splitVol := specgen.SplitVolumeString(vol)
switch len(splitVol) {
case 1:
volDestinations[vol] = true
default:
volSources[splitVol[0]] = true
volDestinations[splitVol[1]] = true
}
}
// Anonymous volumes are added differently from other volumes, in their
// own special field, for reasons known only to Docker. Still use the
// format of `-v` so we can just append them in there.
// Unfortunately, these may be duplicates of existing mounts in Binds.
// So... We need to catch that.
// This also handles volumes duplicated between cc.HostConfig.Mounts and
// cc.Volumes, as seen in compose v2.0.
for vol := range cc.Volumes {
if _, ok := volDestinations[filepath.Clean(vol)]; ok {
continue
}
cliOpts.Volume = append(cliOpts.Volume, vol)
}
// Make mount points for compat volumes
for vol := range volSources {
// This might be a named volume.
// Assume it is if it's not an absolute path.
if !filepath.IsAbs(vol) {
continue
}
// If volume already exists, there is nothing to do
if _, err := os.Stat(vol); err == nil {
continue
}
if err := os.MkdirAll(vol, 0o755); err != nil {
if !os.IsExist(err) {
return nil, nil, errors.Wrapf(err, "error making volume mountpoint for volume %s", vol)
}
}
}
if len(cc.HostConfig.BlkioWeightDevice) > 0 {
devices := make([]string, 0, len(cc.HostConfig.BlkioWeightDevice))
for _, d := range cc.HostConfig.BlkioWeightDevice {
devices = append(devices, d.String())
}
cliOpts.BlkIOWeightDevice = devices
}
if cc.HostConfig.BlkioWeight > 0 {
cliOpts.BlkIOWeight = strconv.Itoa(int(cc.HostConfig.BlkioWeight))
}
if cc.HostConfig.Memory > 0 {
cliOpts.Memory = strconv.Itoa(int(cc.HostConfig.Memory))
}
if cc.HostConfig.MemoryReservation > 0 {
cliOpts.MemoryReservation = strconv.Itoa(int(cc.HostConfig.MemoryReservation))
}
cgroupsv2, err := cgroups.IsCgroup2UnifiedMode()
if err != nil {
return nil, nil, err
}
if cc.HostConfig.MemorySwap > 0 && (!rootless.IsRootless() || (rootless.IsRootless() && cgroupsv2)) {
cliOpts.MemorySwap = strconv.Itoa(int(cc.HostConfig.MemorySwap))
}
if cc.Config.StopTimeout != nil {
cliOpts.StopTimeout = uint(*cc.Config.StopTimeout)
}
if cc.HostConfig.ShmSize > 0 {
cliOpts.ShmSize = strconv.Itoa(int(cc.HostConfig.ShmSize))
}
if len(cc.HostConfig.RestartPolicy.Name) > 0 {
policy := cc.HostConfig.RestartPolicy.Name
// only add restart count on failure
if cc.HostConfig.RestartPolicy.IsOnFailure() {
policy += fmt.Sprintf(":%d", cc.HostConfig.RestartPolicy.MaximumRetryCount)
}
cliOpts.Restart = policy
}
if cc.HostConfig.MemorySwappiness != nil && (!rootless.IsRootless() || rootless.IsRootless() && cgroupsv2 && rtc.Engine.CgroupManager == "systemd") {
cliOpts.MemorySwappiness = *cc.HostConfig.MemorySwappiness
} else {
cliOpts.MemorySwappiness = -1
}
if cc.HostConfig.OomKillDisable != nil {
cliOpts.OOMKillDisable = *cc.HostConfig.OomKillDisable
}
if cc.Config.Healthcheck != nil {
finCmd := ""
for _, str := range cc.Config.Healthcheck.Test {
finCmd = finCmd + str + " "
}
if len(finCmd) > 1 {
finCmd = finCmd[:len(finCmd)-1]
}
cliOpts.HealthCmd = finCmd
if cc.Config.Healthcheck.Interval > 0 {
cliOpts.HealthInterval = cc.Config.Healthcheck.Interval.String()
}
if cc.Config.Healthcheck.Retries > 0 {
cliOpts.HealthRetries = uint(cc.Config.Healthcheck.Retries)
}
if cc.Config.Healthcheck.StartPeriod > 0 {
cliOpts.HealthStartPeriod = cc.Config.Healthcheck.StartPeriod.String()
}
if cc.Config.Healthcheck.Timeout > 0 {
cliOpts.HealthTimeout = cc.Config.Healthcheck.Timeout.String()
}
}
// specgen assumes the image name is arg[0]
cmd := []string{cc.Config.Image}
cmd = append(cmd, cc.Config.Cmd...)
return &cliOpts, cmd, nil
}
// addField is a helper function to populate mount options
func addField(b *strings.Builder, name, value string) {
if value == "" {
return
}
if b.Len() > 0 {
b.WriteRune(',')
}
b.WriteString(name)
b.WriteRune('=')
b.WriteString(value)
}

View file

@ -44,18 +44,6 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) {
return
}
// If the container isn't running, then let's not bother and return
// immediately.
state, err := ctnr.State()
if err != nil {
utils.InternalServerError(w, err)
return
}
if state != define.ContainerStateRunning {
utils.Error(w, http.StatusConflict, define.ErrCtrStateInvalid)
return
}
stats, err := ctnr.GetContainerStats(nil)
if err != nil {
utils.InternalServerError(w, errors.Wrapf(err, "failed to obtain Container %s stats", name))

View file

@ -63,7 +63,7 @@ func GetEvents(w http.ResponseWriter, r *http.Request) {
errorChannel <- runtime.Events(r.Context(), readOpts)
}()
var flush = func() {}
flush := func() {}
if flusher, ok := w.(http.Flusher); ok {
flush = flusher.Flush
}

View file

@ -11,6 +11,7 @@ import (
"github.com/containers/podman/v4/pkg/api/handlers/utils"
"github.com/containers/podman/v4/pkg/api/server/idle"
api "github.com/containers/podman/v4/pkg/api/types"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/specgenutil"
"github.com/gorilla/mux"
"github.com/pkg/errors"
@ -93,10 +94,7 @@ func ExecCreateHandler(w http.ResponseWriter, r *http.Request) {
return
}
resp := new(handlers.ExecCreateResponse)
resp.ID = sessID
utils.WriteResponse(w, http.StatusCreated, resp)
utils.WriteResponse(w, http.StatusCreated, entities.IDResponse{ID: sessID})
}
// ExecInspectHandler inspects a given exec session.

View file

@ -165,7 +165,7 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) {
utils.Error(w, http.StatusInternalServerError, errors.Wrapf(err, "CommitFailure"))
return
}
utils.WriteResponse(w, http.StatusCreated, handlers.IDResponse{ID: commitImage.ID()}) // nolint
utils.WriteResponse(w, http.StatusCreated, entities.IDResponse{ID: commitImage.ID()}) // nolint
}
func CreateImageFromSrc(w http.ResponseWriter, r *http.Request) {

View file

@ -568,7 +568,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
Output: output,
OutputFormat: format,
PullPolicy: pullPolicy,
PullPushRetryDelay: time.Duration(2 * time.Second),
PullPushRetryDelay: 2 * time.Second,
Quiet: query.Quiet,
Registry: registry,
RemoveIntermediateCtrs: query.Rm,
@ -637,15 +637,17 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
enc := json.NewEncoder(body)
enc.SetEscapeHTML(true)
var stepErrors []string
for {
m := struct {
type BuildResponse struct {
Stream string `json:"stream,omitempty"`
Error *jsonmessage.JSONError `json:"errorDetail,omitempty"`
// NOTE: `error` is being deprecated check https://github.com/moby/moby/blob/master/pkg/jsonmessage/jsonmessage.go#L148
ErrorMessage string `json:"error,omitempty"` // deprecate this slowly
Aux json.RawMessage `json:"aux,omitempty"`
}{}
}
m := BuildResponse{}
select {
case e := <-stdout.Chan():
@ -661,12 +663,27 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
}
flush()
case e := <-auxout.Chan():
m.Stream = string(e)
if err := enc.Encode(m); err != nil {
stderr.Write([]byte(err.Error()))
if !query.Quiet {
m.Stream = string(e)
if err := enc.Encode(m); err != nil {
stderr.Write([]byte(err.Error()))
}
flush()
} else {
stepErrors = append(stepErrors, string(e))
}
flush()
case e := <-stderr.Chan():
// Docker-API Compat parity : Build failed so
// output all step errors irrespective of quiet
// flag.
for _, stepError := range stepErrors {
t := BuildResponse{}
t.Stream = stepError
if err := enc.Encode(t); err != nil {
stderr.Write([]byte(err.Error()))
}
flush()
}
m.ErrorMessage = string(e)
m.Error = &jsonmessage.JSONError{
Message: m.ErrorMessage,
@ -739,7 +756,7 @@ func extractTarFile(r *http.Request) (string, error) {
}
path := filepath.Join(anchorDir, "tarBall")
tarBall, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
tarBall, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600)
if err != nil {
return "", err
}
@ -753,7 +770,7 @@ func extractTarFile(r *http.Request) (string, error) {
}
buildDir := filepath.Join(anchorDir, "build")
err = os.Mkdir(buildDir, 0700)
err = os.Mkdir(buildDir, 0o700)
if err != nil {
return "", err
}

View file

@ -17,9 +17,7 @@ import (
)
func PruneImages(w http.ResponseWriter, r *http.Request) {
var (
filters []string
)
var filters []string
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
filterMap, err := util.PrepareFilters(r)

View file

@ -52,7 +52,10 @@ func RemoveImage(w http.ResponseWriter, r *http.Request) {
utils.ImageNotFound(w, name, errors.Wrapf(err, "failed to find image %s", name))
return
}
if errors.Cause(err) == storage.ErrImageUsedByContainer {
utils.Error(w, http.StatusConflict, errors.Wrapf(err, "image %s is in use", name))
return
}
utils.Error(w, http.StatusInternalServerError, err)
return
}

View file

@ -53,75 +53,76 @@ func GetInfo(w http.ResponseWriter, r *http.Request) {
// FIXME: Need to expose if runtime supports Checkpointing
// liveRestoreEnabled := criu.CheckForCriu() && configInfo.RuntimeSupportsCheckpoint()
info := &handlers.Info{Info: docker.Info{
Architecture: goRuntime.GOARCH,
BridgeNfIP6tables: !sysInfo.BridgeNFCallIP6TablesDisabled,
BridgeNfIptables: !sysInfo.BridgeNFCallIPTablesDisabled,
CPUCfsPeriod: sysInfo.CPUCfsPeriod,
CPUCfsQuota: sysInfo.CPUCfsQuota,
CPUSet: sysInfo.Cpuset,
CPUShares: sysInfo.CPUShares,
CgroupDriver: configInfo.Engine.CgroupManager,
ClusterAdvertise: "",
ClusterStore: "",
ContainerdCommit: docker.Commit{},
Containers: infoData.Store.ContainerStore.Number,
ContainersPaused: stateInfo[define.ContainerStatePaused],
ContainersRunning: stateInfo[define.ContainerStateRunning],
ContainersStopped: stateInfo[define.ContainerStateStopped] + stateInfo[define.ContainerStateExited],
Debug: log.IsLevelEnabled(log.DebugLevel),
DefaultRuntime: configInfo.Engine.OCIRuntime,
DockerRootDir: infoData.Store.GraphRoot,
Driver: infoData.Store.GraphDriverName,
DriverStatus: getGraphStatus(infoData.Store.GraphStatus),
ExperimentalBuild: true,
GenericResources: nil,
HTTPProxy: getEnv("http_proxy"),
HTTPSProxy: getEnv("https_proxy"),
ID: uuid.New().String(),
IPv4Forwarding: !sysInfo.IPv4ForwardingDisabled,
Images: infoData.Store.ImageStore.Number,
IndexServerAddress: "",
InitBinary: "",
InitCommit: docker.Commit{},
Isolation: "",
KernelMemoryTCP: false,
KernelVersion: infoData.Host.Kernel,
Labels: nil,
LiveRestoreEnabled: false,
LoggingDriver: "",
MemTotal: infoData.Host.MemTotal,
MemoryLimit: sysInfo.MemoryLimit,
NCPU: goRuntime.NumCPU(),
NEventsListener: 0,
NFd: getFdCount(),
NGoroutines: goRuntime.NumGoroutine(),
Name: infoData.Host.Hostname,
NoProxy: getEnv("no_proxy"),
OSType: goRuntime.GOOS,
OSVersion: infoData.Host.Distribution.Version,
OomKillDisable: sysInfo.OomKillDisable,
OperatingSystem: infoData.Host.Distribution.Distribution,
PidsLimit: sysInfo.PidsLimit,
Plugins: docker.PluginsInfo{
Volume: infoData.Plugins.Volume,
Network: infoData.Plugins.Network,
Log: infoData.Plugins.Log,
info := &handlers.Info{
Info: docker.Info{
Architecture: goRuntime.GOARCH,
BridgeNfIP6tables: !sysInfo.BridgeNFCallIP6TablesDisabled,
BridgeNfIptables: !sysInfo.BridgeNFCallIPTablesDisabled,
CPUCfsPeriod: sysInfo.CPUCfsPeriod,
CPUCfsQuota: sysInfo.CPUCfsQuota,
CPUSet: sysInfo.Cpuset,
CPUShares: sysInfo.CPUShares,
CgroupDriver: configInfo.Engine.CgroupManager,
ClusterAdvertise: "",
ClusterStore: "",
ContainerdCommit: docker.Commit{},
Containers: infoData.Store.ContainerStore.Number,
ContainersPaused: stateInfo[define.ContainerStatePaused],
ContainersRunning: stateInfo[define.ContainerStateRunning],
ContainersStopped: stateInfo[define.ContainerStateStopped] + stateInfo[define.ContainerStateExited],
Debug: log.IsLevelEnabled(log.DebugLevel),
DefaultRuntime: configInfo.Engine.OCIRuntime,
DockerRootDir: infoData.Store.GraphRoot,
Driver: infoData.Store.GraphDriverName,
DriverStatus: getGraphStatus(infoData.Store.GraphStatus),
ExperimentalBuild: true,
GenericResources: nil,
HTTPProxy: getEnv("http_proxy"),
HTTPSProxy: getEnv("https_proxy"),
ID: uuid.New().String(),
IPv4Forwarding: !sysInfo.IPv4ForwardingDisabled,
Images: infoData.Store.ImageStore.Number,
IndexServerAddress: "",
InitBinary: "",
InitCommit: docker.Commit{},
Isolation: "",
KernelMemoryTCP: false,
KernelVersion: infoData.Host.Kernel,
Labels: nil,
LiveRestoreEnabled: false,
LoggingDriver: "",
MemTotal: infoData.Host.MemTotal,
MemoryLimit: sysInfo.MemoryLimit,
NCPU: goRuntime.NumCPU(),
NEventsListener: 0,
NFd: getFdCount(),
NGoroutines: goRuntime.NumGoroutine(),
Name: infoData.Host.Hostname,
NoProxy: getEnv("no_proxy"),
OSType: goRuntime.GOOS,
OSVersion: infoData.Host.Distribution.Version,
OomKillDisable: sysInfo.OomKillDisable,
OperatingSystem: infoData.Host.Distribution.Distribution,
PidsLimit: sysInfo.PidsLimit,
Plugins: docker.PluginsInfo{
Volume: infoData.Plugins.Volume,
Network: infoData.Plugins.Network,
Log: infoData.Plugins.Log,
},
ProductLicense: "Apache-2.0",
RegistryConfig: getServiceConfig(runtime),
RuncCommit: docker.Commit{},
Runtimes: getRuntimes(configInfo),
SecurityOptions: getSecOpts(sysInfo),
ServerVersion: versionInfo.Version,
SwapLimit: sysInfo.SwapLimit,
Swarm: swarm.Info{
LocalNodeState: swarm.LocalNodeStateInactive,
},
SystemStatus: nil,
SystemTime: time.Now().Format(time.RFC3339Nano),
Warnings: []string{},
},
ProductLicense: "Apache-2.0",
RegistryConfig: getServiceConfig(runtime),
RuncCommit: docker.Commit{},
Runtimes: getRuntimes(configInfo),
SecurityOptions: getSecOpts(sysInfo),
ServerVersion: versionInfo.Version,
SwapLimit: sysInfo.SwapLimit,
Swarm: swarm.Info{
LocalNodeState: swarm.LocalNodeStateInactive,
},
SystemStatus: nil,
SystemTime: time.Now().Format(time.RFC3339Nano),
Warnings: []string{},
},
BuildahVersion: infoData.Host.BuildahVersion,
CPURealtimePeriod: sysInfo.CPURealtimePeriod,
CPURealtimeRuntime: sysInfo.CPURealtimeRuntime,
@ -186,7 +187,7 @@ func getSecOpts(sysInfo *sysinfo.SysInfo) []string {
}
func getRuntimes(configInfo *config.Config) map[string]docker.Runtime {
var runtimes = map[string]docker.Runtime{}
runtimes := map[string]docker.Runtime{}
for name, paths := range configInfo.Engine.OCIRuntimes {
runtimes[name] = docker.Runtime{
Path: paths[0],
@ -206,7 +207,7 @@ func getFdCount() (count int) {
// Just ignoring Container errors here...
func getContainersState(r *libpod.Runtime) map[define.ContainerStatus]int {
var states = map[define.ContainerStatus]int{}
states := map[define.ContainerStatus]int{}
ctnrs, err := r.GetAllContainers()
if err == nil {
for _, ctnr := range ctnrs {

View file

@ -298,9 +298,7 @@ func RemoveNetwork(w http.ResponseWriter, r *http.Request) {
func Connect(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
var (
netConnect types.NetworkConnect
)
var netConnect types.NetworkConnect
if err := json.NewDecoder(r.Body).Decode(&netConnect); err != nil {
utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
return

View file

@ -16,9 +16,7 @@ import (
)
func ListSecrets(w http.ResponseWriter, r *http.Request) {
var (
runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
)
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
filtersMap, err := util.PrepareFilters(r)
if err != nil {
utils.Error(w, http.StatusInternalServerError, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
@ -51,9 +49,7 @@ func ListSecrets(w http.ResponseWriter, r *http.Request) {
}
func InspectSecret(w http.ResponseWriter, r *http.Request) {
var (
runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
)
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
name := utils.GetName(r)
names := []string{name}
ic := abi.ContainerEngine{Libpod: runtime}
@ -84,9 +80,7 @@ func InspectSecret(w http.ResponseWriter, r *http.Request) {
}
func RemoveSecret(w http.ResponseWriter, r *http.Request) {
var (
runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
)
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
opts := entities.SecretRmOptions{}
name := utils.GetName(r)
@ -104,9 +98,7 @@ func RemoveSecret(w http.ResponseWriter, r *http.Request) {
}
func CreateSecret(w http.ResponseWriter, r *http.Request) {
var (
runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
)
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
opts := entities.SecretCreateOptions{}
createParams := struct {
*entities.SecretCreateRequest

View file

@ -1,67 +0,0 @@
package compat
import (
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/docker/docker/api/types"
)
// Create container
// swagger:response ContainerCreateResponse
type swagCtrCreateResponse struct {
// in:body
Body struct {
entities.ContainerCreateResponse
}
}
// Wait container
// swagger:response ContainerWaitResponse
type swagCtrWaitResponse struct {
// in:body
Body struct {
// container exit code
StatusCode int
Error struct {
Message string
}
}
}
// Network inspect
// swagger:response CompatNetworkInspect
type swagCompatNetworkInspect struct {
// in:body
Body types.NetworkResource
}
// Network list
// swagger:response CompatNetworkList
type swagCompatNetworkList struct {
// in:body
Body []types.NetworkResource
}
// Network create
// swagger:model NetworkCreateRequest
type NetworkCreateRequest struct {
types.NetworkCreateRequest
}
// Network create
// swagger:response CompatNetworkCreate
type swagCompatNetworkCreateResponse struct {
// in:body
Body struct{ types.NetworkCreate }
}
// Network disconnect
// swagger:model NetworkCompatConnectRequest
type swagCompatNetworkConnectRequest struct {
types.NetworkConnect
}
// Network disconnect
// swagger:model NetworkCompatDisconnectRequest
type swagCompatNetworkDisconnectRequest struct {
types.NetworkDisconnect
}

View file

@ -57,13 +57,15 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) {
Version: conmon.Version,
Details: map[string]string{
"Package": conmon.Package,
}},
},
},
{
Name: fmt.Sprintf("OCI Runtime (%s)", oci.Name),
Version: oci.Version,
Details: map[string]string{
"Package": oci.Package,
}},
},
},
}
components = append(components, additional...)
}
@ -89,5 +91,6 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) {
MinAPIVersion: fmt.Sprintf("%d.%d", minVersion.Major, minVersion.Minor),
Os: components[0].Details["Os"],
Version: components[0].Version,
}})
},
})
}

View file

@ -180,9 +180,7 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) {
}
func InspectVolume(w http.ResponseWriter, r *http.Request) {
var (
runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
)
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
name := utils.GetName(r)
vol, err := runtime.GetVolume(name)
if err != nil {
@ -263,9 +261,7 @@ func RemoveVolume(w http.ResponseWriter, r *http.Request) {
}
func PruneVolumes(w http.ResponseWriter, r *http.Request) {
var (
runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
)
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
filterMap, err := util.PrepareFilters(r)
if err != nil {
utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "Decode()"))

View file

@ -168,6 +168,7 @@ func UnmountContainer(w http.ResponseWriter, r *http.Request) {
}
utils.WriteResponse(w, http.StatusNoContent, "")
}
func MountContainer(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
name := utils.GetName(r)

View file

@ -41,17 +41,17 @@ func GenerateSystemd(w http.ResponseWriter, r *http.Request) {
return
}
var ContainerPrefix = "container"
ContainerPrefix := "container"
if query.ContainerPrefix != nil {
ContainerPrefix = *query.ContainerPrefix
}
var PodPrefix = "pod"
PodPrefix := "pod"
if query.PodPrefix != nil {
PodPrefix = *query.PodPrefix
}
var Separator = "-"
Separator := "-"
if query.Separator != nil {
Separator = *query.Separator
}
@ -106,5 +106,7 @@ func GenerateKube(w http.ResponseWriter, r *http.Request) {
return
}
// FIXME: Content-Type is being set as application/x-tar NOT text/vnd.yaml
// https://mailarchive.ietf.org/arch/msg/media-types/e9ZNC0hDXKXeFlAVRWxLCCaG9GI/
utils.WriteResponse(w, http.StatusOK, report.Reader)
}

View file

@ -102,9 +102,7 @@ func GetImage(w http.ResponseWriter, r *http.Request) {
}
func PruneImages(w http.ResponseWriter, r *http.Request) {
var (
err error
)
var err error
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
query := struct {
@ -129,7 +127,7 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
return
}
var libpodFilters = []string{}
libpodFilters := []string{}
if _, found := r.URL.Query()["filters"]; found {
dangling := (*filterMap)["all"]
if len(dangling) > 0 {
@ -162,9 +160,7 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
}
func ExportImage(w http.ResponseWriter, r *http.Request) {
var (
output string
)
var output string
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
query := struct {
@ -243,9 +239,7 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
}
func ExportImages(w http.ResponseWriter, r *http.Request) {
var (
output string
)
var output string
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
query := struct {
@ -566,7 +560,7 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) {
utils.Error(w, http.StatusInternalServerError, errors.Wrapf(err, "CommitFailure"))
return
}
utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: commitImage.ID()}) // nolint
utils.WriteResponse(w, http.StatusOK, entities.IDResponse{ID: commitImage.ID()}) // nolint
}
func UntagImage(w http.ResponseWriter, r *http.Request) {

View file

@ -88,7 +88,7 @@ func ManifestCreate(w http.ResponseWriter, r *http.Request) {
// Treat \r\n as empty body
if len(buffer) < 3 {
utils.WriteResponse(w, status, handlers.IDResponse{ID: manID})
utils.WriteResponse(w, status, entities.IDResponse{ID: manID})
return
}
@ -113,7 +113,7 @@ func ManifestCreate(w http.ResponseWriter, r *http.Request) {
return
}
utils.WriteResponse(w, status, handlers.IDResponse{ID: id})
utils.WriteResponse(w, status, entities.IDResponse{ID: id})
}
// ManifestExists return true if manifest list exists.
@ -163,7 +163,6 @@ func ManifestAddV3(w http.ResponseWriter, r *http.Request) {
// Wrapper to support 3.x with 4.x libpod
query := struct {
entities.ManifestAddOptions
Images []string
TLSVerify bool `schema:"tlsVerify"`
}{}
if err := json.NewDecoder(r.Body).Decode(&query); err != nil {
@ -204,7 +203,7 @@ func ManifestAddV3(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, err)
return
}
utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: newID})
utils.WriteResponse(w, http.StatusOK, entities.IDResponse{ID: newID})
}
// ManifestRemoveDigestV3 remove digest from manifest list
@ -238,7 +237,7 @@ func ManifestRemoveDigestV3(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, err)
return
}
utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: manifestList.ID()})
utils.WriteResponse(w, http.StatusOK, entities.IDResponse{ID: manifestList.ID()})
}
// ManifestPushV3 push image to registry
@ -294,7 +293,7 @@ func ManifestPushV3(w http.ResponseWriter, r *http.Request) {
utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "error pushing image %q", query.Destination))
return
}
utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: digest})
utils.WriteResponse(w, http.StatusOK, entities.IDResponse{ID: digest})
}
// ManifestPush push image to registry
@ -353,7 +352,7 @@ func ManifestPush(w http.ResponseWriter, r *http.Request) {
utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "error pushing image %q", destination))
return
}
utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: digest})
utils.WriteResponse(w, http.StatusOK, entities.IDResponse{ID: digest})
}
// ManifestModify efficiently updates the named manifest list

Some files were not shown because too many files have changed in this diff Show more