mirror of
https://github.com/containers/podman
synced 2024-10-18 08:13:32 +00:00
commit
932822ab4b
|
@ -44,7 +44,6 @@ linters:
|
|||
- goconst
|
||||
- gocyclo
|
||||
- lll
|
||||
- unconvert
|
||||
- gosec
|
||||
- maligned
|
||||
- gomoddirectives
|
||||
|
|
5
Makefile
5
Makefile
|
@ -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 \
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)))
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
)
|
|
@ -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
|
||||
)
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
```
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
```
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(":"))
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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])
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
|
@ -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))...)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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, ", "))
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ func (r *Runtime) renumberLocks() error {
|
|||
}
|
||||
}
|
||||
|
||||
r.newSystemEvent(events.Renumber)
|
||||
r.NewSystemEvent(events.Renumber)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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,
|
||||
}})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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()"))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue