summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/podman/common/completion.go7
-rw-r--r--cmd/podman/common/create.go13
-rw-r--r--cmd/podman/common/create_opts.go6
-rw-r--r--cmd/podman/containers/checkpoint.go34
-rw-r--r--cmd/podman/containers/create.go20
-rw-r--r--cmd/podman/containers/port.go50
-rw-r--r--cmd/podman/containers/ps.go183
-rw-r--r--cmd/podman/containers/restore.go34
-rw-r--r--cmd/podman/images/load.go15
-rw-r--r--cmd/podman/images/pull.go2
-rw-r--r--cmd/podman/images/scp.go65
-rw-r--r--cmd/podman/images/sign.go5
-rw-r--r--cmd/podman/machine/list.go9
-rw-r--r--cmd/podman/machine/stop.go8
-rw-r--r--cmd/podman/play/kube.go8
-rw-r--r--cmd/podman/pods/create.go31
-rw-r--r--cmd/podman/root.go5
-rw-r--r--cmd/podman/system/connection/add.go7
-rw-r--r--cmd/podman/system/connection/list.go4
-rw-r--r--cmd/podman/system/connection/remove.go27
-rw-r--r--cmd/podman/system/connection/shared.go9
-rw-r--r--cmd/podman/system/unshare.go10
-rw-r--r--cmd/podman/system/version.go4
-rw-r--r--cmd/podman/validate/args.go3
-rw-r--r--cmd/rootlessport/main.go42
25 files changed, 315 insertions, 286 deletions
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go
index ea453a331..4cb29383a 100644
--- a/cmd/podman/common/completion.go
+++ b/cmd/podman/common/completion.go
@@ -1163,6 +1163,13 @@ func AutocompleteEventBackend(cmd *cobra.Command, args []string, toComplete stri
return types, cobra.ShellCompDirectiveNoFileComp
}
+// AutocompleteNetworkBackend - Autocomplete network backend options.
+// -> "cni", "netavark"
+func AutocompleteNetworkBackend(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+ types := []string{"cni", "netavark"}
+ return types, cobra.ShellCompDirectiveNoFileComp
+}
+
// AutocompleteLogLevel - Autocomplete log level options.
// -> "trace", "debug", "info", "warn", "error", "fatal", "panic"
func AutocompleteLogLevel(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index a3ff37c19..4598e535d 100644
--- a/cmd/podman/common/create.go
+++ b/cmd/podman/common/create.go
@@ -5,6 +5,7 @@ import (
"github.com/containers/common/pkg/auth"
"github.com/containers/common/pkg/completion"
+ commonFlag "github.com/containers/common/pkg/flag"
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/pkg/domain/entities"
@@ -319,6 +320,9 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
"Kernel memory limit "+sizeWithUnitFormat,
)
_ = cmd.RegisterFlagCompletionFunc(kernelMemoryFlagName, completion.AutocompleteNone)
+ // kernel-memory is deprecated in the runtime spec.
+ _ = createFlags.MarkHidden("kernel-memory")
+
logDriverFlagName := "log-driver"
createFlags.StringVar(
&cf.LogDriver,
@@ -406,7 +410,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
createFlags.StringVar(
&cf.Variant,
variantFlagName, "",
- "Use _VARIANT_ instead of the running architecture variant for choosing images",
+ "Use `VARIANT` instead of the running architecture variant for choosing images",
)
_ = cmd.RegisterFlagCompletionFunc(variantFlagName, completion.AutocompleteNone)
@@ -586,12 +590,9 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
)
_ = cmd.RegisterFlagCompletionFunc(timeoutFlagName, completion.AutocompleteNone)
- // Flag for TLS verification, so that `run` and `create` commands can make use of it.
- // Make sure to use `=` while using this flag i.e `--tls-verify=false/true`
- tlsVerifyFlagName := "tls-verify"
- createFlags.BoolVar(
+ commonFlag.OptionalBoolFlag(createFlags,
&cf.TLSVerify,
- tlsVerifyFlagName, true,
+ "tls-verify",
"Require HTTPS and verify certificates when contacting registries for pulling images",
)
diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go
index 50d7c446d..6283eb28e 100644
--- a/cmd/podman/common/create_opts.go
+++ b/cmd/podman/common/create_opts.go
@@ -18,6 +18,7 @@ import (
"github.com/containers/podman/v3/pkg/specgen"
"github.com/docker/docker/api/types/mount"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
func stringMaptoArray(m map[string]string) []string {
@@ -291,7 +292,7 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *c
Rm: cc.HostConfig.AutoRemove,
SecurityOpt: cc.HostConfig.SecurityOpt,
StopSignal: cc.Config.StopSignal,
- StorageOpt: stringMaptoArray(cc.HostConfig.StorageOpt),
+ StorageOpts: stringMaptoArray(cc.HostConfig.StorageOpt),
Sysctl: stringMaptoArray(cc.HostConfig.Sysctls),
Systemd: "true", // podman default
TmpFS: parsedTmp,
@@ -383,6 +384,9 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *c
if cc.HostConfig.Memory > 0 {
cliOpts.Memory = strconv.Itoa(int(cc.HostConfig.Memory))
}
+ if cc.HostConfig.KernelMemory > 0 {
+ logrus.Warnf("The --kernel-memory flag has been deprecated. May not work properly on your system.")
+ }
if cc.HostConfig.MemoryReservation > 0 {
cliOpts.MemoryReservation = strconv.Itoa(int(cc.HostConfig.MemoryReservation))
diff --git a/cmd/podman/containers/checkpoint.go b/cmd/podman/containers/checkpoint.go
index 4fa72d520..d92bc3e5e 100644
--- a/cmd/podman/containers/checkpoint.go
+++ b/cmd/podman/containers/checkpoint.go
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"strings"
+ "time"
"github.com/containers/common/pkg/completion"
"github.com/containers/podman/v3/cmd/podman/common"
@@ -40,6 +41,11 @@ var (
var checkpointOptions entities.CheckpointOptions
+type checkpointStatistics struct {
+ PodmanDuration int64 `json:"podman_checkpoint_duration"`
+ ContainerStatistics []*entities.CheckpointReport `json:"container_statistics"`
+}
+
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: checkpointCommand,
@@ -63,11 +69,19 @@ func init() {
flags.StringP("compress", "c", "zstd", "Select compression algorithm (gzip, none, zstd) for checkpoint archive.")
_ = checkpointCommand.RegisterFlagCompletionFunc("compress", common.AutocompleteCheckpointCompressType)
+ flags.BoolVar(
+ &checkpointOptions.PrintStats,
+ "print-stats",
+ false,
+ "Display checkpoint statistics",
+ )
+
validate.AddLatestFlag(checkpointCommand, &checkpointOptions.Latest)
}
func checkpoint(cmd *cobra.Command, args []string) error {
var errs utils.OutputErrors
+ podmanStart := time.Now()
if cmd.Flags().Changed("compress") {
if checkpointOptions.Export == "" {
return errors.Errorf("--compress can only be used with --export")
@@ -102,12 +116,30 @@ func checkpoint(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
+ podmanFinished := time.Now()
+
+ var statistics checkpointStatistics
+
for _, r := range responses {
if r.Err == nil {
- fmt.Println(r.Id)
+ if checkpointOptions.PrintStats {
+ statistics.ContainerStatistics = append(statistics.ContainerStatistics, r)
+ } else {
+ fmt.Println(r.Id)
+ }
} else {
errs = append(errs, r.Err)
}
}
+
+ if checkpointOptions.PrintStats {
+ statistics.PodmanDuration = podmanFinished.Sub(podmanStart).Microseconds()
+ j, err := json.MarshalIndent(statistics, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(j))
+ }
+
return errs.PrintErrors()
}
diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go
index bfeeb7ebe..9b53a730f 100644
--- a/cmd/podman/containers/create.go
+++ b/cmd/podman/containers/create.go
@@ -303,6 +303,11 @@ func PullImage(imageName string, cliVals entities.ContainerCreateOptions) (strin
}
}
+ skipTLSVerify := types.OptionalBoolUndefined
+ if cliVals.TLSVerify.Present() {
+ skipTLSVerify = types.NewOptionalBool(!cliVals.TLSVerify.Value())
+ }
+
pullReport, pullErr := registry.ImageEngine().Pull(registry.GetContext(), imageName, entities.ImagePullOptions{
Authfile: cliVals.Authfile,
Quiet: cliVals.Quiet,
@@ -311,7 +316,7 @@ func PullImage(imageName string, cliVals entities.ContainerCreateOptions) (strin
Variant: cliVals.Variant,
SignaturePolicy: cliVals.SignaturePolicy,
PullPolicy: pullPolicy,
- SkipTLSVerify: types.NewOptionalBool(!cliVals.TLSVerify), // If Flag changed for TLS Verification
+ SkipTLSVerify: skipTLSVerify,
})
if pullErr != nil {
return "", pullErr
@@ -371,16 +376,13 @@ func createPodIfNecessary(s *specgen.SpecGenerator, netOpts *entities.NetOptions
return nil, err
}
- infraOpts := entities.ContainerCreateOptions{ImageVolume: "bind", Net: netOpts, Quiet: true}
- rawImageName := config.DefaultInfraImage
- name, err := PullImage(rawImageName, infraOpts)
- if err != nil {
- fmt.Println(err)
- }
- imageName := name
+ infraOpts := entities.NewInfraContainerCreateOptions()
+ infraOpts.Net = netOpts
+ infraOpts.Quiet = true
+ imageName := config.DefaultInfraImage
podGen.InfraImage = imageName
podGen.InfraContainerSpec = specgen.NewSpecGenerator(imageName, false)
- podGen.InfraContainerSpec.RawImageName = rawImageName
+ podGen.InfraContainerSpec.RawImageName = imageName
podGen.InfraContainerSpec.NetworkOptions = podGen.NetworkOptions
err = specgenutil.FillOutSpecGen(podGen.InfraContainerSpec, &infraOpts, []string{})
if err != nil {
diff --git a/cmd/podman/containers/port.go b/cmd/podman/containers/port.go
index f309390c3..0e582ae52 100644
--- a/cmd/podman/containers/port.go
+++ b/cmd/podman/containers/port.go
@@ -8,7 +8,6 @@ import (
"github.com/containers/podman/v3/cmd/podman/common"
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/cmd/podman/validate"
- "github.com/containers/podman/v3/libpod/network/types"
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -73,7 +72,8 @@ func port(_ *cobra.Command, args []string) error {
var (
container string
err error
- userPort types.OCICNIPortMapping
+ userPort uint16
+ userProto string
)
if len(args) == 0 && !portOpts.Latest && !portOpts.All {
@@ -101,16 +101,12 @@ func port(_ *cobra.Command, args []string) error {
fields = append(fields, "tcp")
}
- portNum, err := strconv.Atoi(fields[0])
+ portNum, err := strconv.ParseUint(fields[0], 10, 16)
if err != nil {
return err
}
- userPort = types.OCICNIPortMapping{
- HostPort: 0,
- ContainerPort: int32(portNum),
- Protocol: fields[1],
- HostIP: "",
- }
+ userPort = uint16(portNum)
+ userProto = fields[1]
}
reports, err := registry.ContainerEngine().ContainerPort(registry.GetContext(), container, portOpts)
@@ -120,24 +116,36 @@ func port(_ *cobra.Command, args []string) error {
var found bool
// Iterate mappings
for _, report := range reports {
+ allPrefix := ""
+ if portOpts.All {
+ allPrefix = report.Id[:12] + "\t"
+ }
for _, v := range report.Ports {
hostIP := v.HostIP
// Set host IP to 0.0.0.0 if blank
if hostIP == "" {
hostIP = "0.0.0.0"
}
- if portOpts.All {
- fmt.Printf("%s\t", report.Id[:12])
- }
- // If not searching by port or port/proto, then dump what we see
- if port == "" {
- fmt.Printf("%d/%s -> %s:%d\n", v.ContainerPort, v.Protocol, hostIP, v.HostPort)
- continue
- }
- if v.ContainerPort == userPort.ContainerPort {
- fmt.Printf("%s:%d\n", hostIP, v.HostPort)
- found = true
- break
+ protocols := strings.Split(v.Protocol, ",")
+ for _, protocol := range protocols {
+ // If not searching by port or port/proto, then dump what we see
+ if port == "" {
+ for i := uint16(0); i < v.Range; i++ {
+ fmt.Printf("%s%d/%s -> %s:%d\n", allPrefix, v.ContainerPort+i, protocol, hostIP, v.HostPort+i)
+ }
+ continue
+ }
+ // check if the proto matches and if the port is in the range
+ // this is faster than looping over the range for no reason
+ if v.Protocol == userProto &&
+ v.ContainerPort <= userPort &&
+ v.ContainerPort+v.Range > userPort {
+ // we have to add the current range to the host port
+ hostPort := v.HostPort + userPort - v.ContainerPort
+ fmt.Printf("%s%s:%d\n", allPrefix, hostIP, hostPort)
+ found = true
+ break
+ }
}
}
if !found && port != "" {
diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go
index 9687cd5bd..712de327c 100644
--- a/cmd/podman/containers/ps.go
+++ b/cmd/podman/containers/ps.go
@@ -3,8 +3,6 @@ package containers
import (
"fmt"
"os"
- "sort"
- "strconv"
"strings"
"time"
@@ -477,174 +475,31 @@ func (l psReporter) UTS() string {
// portsToString converts the ports used to a string of the from "port1, port2"
// and also groups a continuous list of ports into a readable format.
-func portsToString(ports []types.OCICNIPortMapping) string {
+// The format is IP:HostPort(-Range)->ContainerPort(-Range)/Proto
+func portsToString(ports []types.PortMapping) string {
if len(ports) == 0 {
return ""
}
- // Sort the ports, so grouping continuous ports become easy.
- sort.Slice(ports, func(i, j int) bool {
- return comparePorts(ports[i], ports[j])
- })
-
- portGroups := [][]types.OCICNIPortMapping{}
- currentGroup := []types.OCICNIPortMapping{}
- for i, v := range ports {
- var prevPort, nextPort *int32
- if i > 0 {
- prevPort = &ports[i-1].ContainerPort
- }
- if i+1 < len(ports) {
- nextPort = &ports[i+1].ContainerPort
- }
-
- port := v.ContainerPort
-
- // Helper functions
- addToCurrentGroup := func(x types.OCICNIPortMapping) {
- currentGroup = append(currentGroup, x)
- }
-
- addToPortGroup := func(x types.OCICNIPortMapping) {
- portGroups = append(portGroups, []types.OCICNIPortMapping{x})
- }
-
- finishCurrentGroup := func() {
- portGroups = append(portGroups, currentGroup)
- currentGroup = []types.OCICNIPortMapping{}
- }
-
- // Single entry slice
- if prevPort == nil && nextPort == nil {
- addToPortGroup(v)
- }
-
- // Start of the slice with len > 0
- if prevPort == nil && nextPort != nil {
- isGroup := *nextPort-1 == port
-
- if isGroup {
- // Start with a group
- addToCurrentGroup(v)
- } else {
- // Start with single item
- addToPortGroup(v)
- }
-
- continue
- }
-
- // Middle of the slice with len > 0
- if prevPort != nil && nextPort != nil {
- currentIsGroup := *prevPort+1 == port
- nextIsGroup := *nextPort-1 == port
-
- if currentIsGroup {
- // Maybe in the middle of a group
- addToCurrentGroup(v)
-
- if !nextIsGroup {
- // End of a group
- finishCurrentGroup()
- }
- } else if nextIsGroup {
- // Start of a new group
- addToCurrentGroup(v)
- } else {
- // No group at all
- addToPortGroup(v)
- }
-
- continue
- }
-
- // End of the slice with len > 0
- if prevPort != nil && nextPort == nil {
- isGroup := *prevPort+1 == port
-
- if isGroup {
- // End group
- addToCurrentGroup(v)
- finishCurrentGroup()
- } else {
- // End single item
- addToPortGroup(v)
- }
- }
- }
-
- portDisplay := []string{}
- for _, group := range portGroups {
- if len(group) == 0 {
- // Usually should not happen, but better do not crash.
- continue
- }
-
- first := group[0]
-
- hostIP := first.HostIP
+ sb := &strings.Builder{}
+ for _, port := range ports {
+ hostIP := port.HostIP
if hostIP == "" {
hostIP = "0.0.0.0"
}
-
- // Single mappings
- if len(group) == 1 {
- portDisplay = append(portDisplay,
- fmt.Sprintf(
- "%s:%d->%d/%s",
- hostIP, first.HostPort, first.ContainerPort, first.Protocol,
- ),
- )
- continue
- }
-
- // Group mappings
- last := group[len(group)-1]
- portDisplay = append(portDisplay, formatGroup(
- fmt.Sprintf("%s/%s", hostIP, first.Protocol),
- first.HostPort, last.HostPort,
- first.ContainerPort, last.ContainerPort,
- ))
- }
- return strings.Join(portDisplay, ", ")
-}
-
-func comparePorts(i, j types.OCICNIPortMapping) bool {
- if i.ContainerPort != j.ContainerPort {
- return i.ContainerPort < j.ContainerPort
- }
-
- if i.HostIP != j.HostIP {
- return i.HostIP < j.HostIP
- }
-
- if i.HostPort != j.HostPort {
- return i.HostPort < j.HostPort
- }
-
- return i.Protocol < j.Protocol
-}
-
-// formatGroup returns the group in the format:
-// <IP:firstHost:lastHost->firstCtr:lastCtr/Proto>
-// e.g 0.0.0.0:1000-1006->2000-2006/tcp.
-func formatGroup(key string, firstHost, lastHost, firstCtr, lastCtr int32) string {
- parts := strings.Split(key, "/")
- groupType := parts[0]
- var ip string
- if len(parts) > 1 {
- ip = parts[0]
- groupType = parts[1]
- }
-
- group := func(first, last int32) string {
- group := strconv.Itoa(int(first))
- if first != last {
- group = fmt.Sprintf("%s-%d", group, last)
+ protocols := strings.Split(port.Protocol, ",")
+ for _, protocol := range protocols {
+ if port.Range > 1 {
+ fmt.Fprintf(sb, "%s:%d-%d->%d-%d/%s, ",
+ hostIP, port.HostPort, port.HostPort+port.Range-1,
+ port.ContainerPort, port.ContainerPort+port.Range-1, protocol)
+ } else {
+ fmt.Fprintf(sb, "%s:%d->%d/%s, ",
+ hostIP, port.HostPort,
+ port.ContainerPort, protocol)
+ }
}
- return group
}
- hostGroup := group(firstHost, lastHost)
- ctrGroup := group(firstCtr, lastCtr)
-
- return fmt.Sprintf("%s:%s->%s/%s", ip, hostGroup, ctrGroup, groupType)
+ display := sb.String()
+ // make sure to trim the last ", " of the string
+ return display[:len(display)-2]
}
diff --git a/cmd/podman/containers/restore.go b/cmd/podman/containers/restore.go
index 05214f32c..217adc887 100644
--- a/cmd/podman/containers/restore.go
+++ b/cmd/podman/containers/restore.go
@@ -3,6 +3,7 @@ package containers
import (
"context"
"fmt"
+ "time"
"github.com/containers/common/pkg/completion"
"github.com/containers/podman/v3/cmd/podman/common"
@@ -39,6 +40,11 @@ var (
var restoreOptions entities.RestoreOptions
+type restoreStatistics struct {
+ PodmanDuration int64 `json:"podman_restore_duration"`
+ ContainerStatistics []*entities.RestoreReport `json:"container_statistics"`
+}
+
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: restoreCommand,
@@ -75,11 +81,19 @@ func init() {
flags.StringVar(&restoreOptions.Pod, "pod", "", "Restore container into existing Pod (only works with --import)")
_ = restoreCommand.RegisterFlagCompletionFunc("pod", common.AutocompletePodsRunning)
+ flags.BoolVar(
+ &restoreOptions.PrintStats,
+ "print-stats",
+ false,
+ "Display restore statistics",
+ )
+
validate.AddLatestFlag(restoreCommand, &restoreOptions.Latest)
}
func restore(cmd *cobra.Command, args []string) error {
var errs utils.OutputErrors
+ podmanStart := time.Now()
if rootless.IsRootless() {
return errors.New("restoring a container requires root")
}
@@ -132,12 +146,30 @@ func restore(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
+ podmanFinished := time.Now()
+
+ var statistics restoreStatistics
+
for _, r := range responses {
if r.Err == nil {
- fmt.Println(r.Id)
+ if restoreOptions.PrintStats {
+ statistics.ContainerStatistics = append(statistics.ContainerStatistics, r)
+ } else {
+ fmt.Println(r.Id)
+ }
} else {
errs = append(errs, r.Err)
}
}
+
+ if restoreOptions.PrintStats {
+ statistics.PodmanDuration = podmanFinished.Sub(podmanStart).Microseconds()
+ j, err := json.MarshalIndent(statistics, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(j))
+ }
+
return errs.PrintErrors()
}
diff --git a/cmd/podman/images/load.go b/cmd/podman/images/load.go
index c7b7d6b3f..c39ae624e 100644
--- a/cmd/podman/images/load.go
+++ b/cmd/podman/images/load.go
@@ -9,6 +9,7 @@ import (
"strings"
"github.com/containers/common/pkg/completion"
+ "github.com/containers/common/pkg/download"
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/cmd/podman/validate"
"github.com/containers/podman/v3/pkg/domain/entities"
@@ -69,6 +70,20 @@ func loadFlags(cmd *cobra.Command) {
func load(cmd *cobra.Command, args []string) error {
if len(loadOpts.Input) > 0 {
+ // Download the input file if needed.
+ if strings.HasPrefix(loadOpts.Input, "https://") || strings.HasPrefix(loadOpts.Input, "http://") {
+ tmpdir, err := util.DefaultContainerConfig().ImageCopyTmpDir()
+ if err != nil {
+ return err
+ }
+ tmpfile, err := download.FromURL(tmpdir, loadOpts.Input)
+ if err != nil {
+ return err
+ }
+ defer os.Remove(tmpfile)
+ loadOpts.Input = tmpfile
+ }
+
if _, err := os.Stat(loadOpts.Input); err != nil {
return err
}
diff --git a/cmd/podman/images/pull.go b/cmd/podman/images/pull.go
index a4e3515db..a990d1626 100644
--- a/cmd/podman/images/pull.go
+++ b/cmd/podman/images/pull.go
@@ -92,7 +92,7 @@ func pullFlags(cmd *cobra.Command) {
_ = cmd.RegisterFlagCompletionFunc(osFlagName, completion.AutocompleteOS)
variantFlagName := "variant"
- flags.StringVar(&pullOptions.Variant, variantFlagName, "", " use VARIANT instead of the running architecture variant for choosing images")
+ flags.StringVar(&pullOptions.Variant, variantFlagName, "", "Use VARIANT instead of the running architecture variant for choosing images")
_ = cmd.RegisterFlagCompletionFunc(variantFlagName, completion.AutocompleteNone)
platformFlagName := "platform"
diff --git a/cmd/podman/images/scp.go b/cmd/podman/images/scp.go
index 176563440..5c9cadc7a 100644
--- a/cmd/podman/images/scp.go
+++ b/cmd/podman/images/scp.go
@@ -16,6 +16,7 @@ import (
"github.com/containers/podman/v3/cmd/podman/system/connection"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/pkg/domain/entities"
+ "github.com/containers/podman/v3/pkg/rootless"
"github.com/docker/distribution/reference"
scpD "github.com/dtylman/scp"
"github.com/pkg/errors"
@@ -125,20 +126,39 @@ func scp(cmd *cobra.Command, args []string) (finalErr error) {
fmt.Println(rep)
// TODO: Add podman remote support
default: // else native load
+ scpOpts.Save.Format = "oci-archive"
+ _, err := os.Open(scpOpts.Save.Output)
+ if err != nil {
+ return err
+ }
if scpOpts.Tag != "" {
return errors.Wrapf(define.ErrInvalidArg, "Renaming of an image is currently not supported")
}
scpOpts.Save.Format = "oci-archive"
abiErr := abiEng.Save(context.Background(), scpOpts.SourceImageName, []string{}, scpOpts.Save) // save the image locally before loading it on remote, local, or ssh
if abiErr != nil {
- errors.Wrapf(abiErr, "could not save image as specified")
+ return errors.Wrapf(abiErr, "could not save image as specified")
}
- rep, err := abiEng.Load(context.Background(), scpOpts.Load)
- if err != nil {
- return err
+ if !rootless.IsRootless() && scpOpts.Rootless {
+ if scpOpts.User == "" {
+ scpOpts.User = os.Getenv("SUDO_USER")
+ if scpOpts.User == "" {
+ return errors.New("could not obtain root user, make sure the environmental variable SUDO_USER is set, and that this command is being run as root")
+ }
+ }
+ err := abiEng.Transfer(context.Background(), scpOpts)
+ if err != nil {
+ return err
+ }
+ } else {
+ rep, err := abiEng.Load(context.Background(), scpOpts.Load)
+ if err != nil {
+ return err
+ }
+ fmt.Println("Loaded image(s): " + strings.Join(rep.Names, ","))
}
- fmt.Println("Loaded image(s): " + strings.Join(rep.Names, ","))
}
+
return nil
}
@@ -154,7 +174,7 @@ func loadToRemote(localFile string, tag string, url *urlP.URL, iden string) (str
n, err := scpD.CopyTo(dial, localFile, remoteFile)
if err != nil {
- errOut := (strconv.Itoa(int(n)) + " Bytes copied before error")
+ errOut := strconv.Itoa(int(n)) + " Bytes copied before error"
return " ", errors.Wrapf(err, errOut)
}
run := ""
@@ -167,7 +187,7 @@ func loadToRemote(localFile string, tag string, url *urlP.URL, iden string) (str
if err != nil {
return "", err
}
- return strings.TrimSuffix(out, "\n"), nil
+ return strings.TrimSuffix(string(out), "\n"), nil
}
// saveToRemote takes image information and remote connection information. it connects to the specified client
@@ -185,7 +205,7 @@ func saveToRemote(image, localFile string, tag string, uri *urlP.URL, iden strin
return errors.Wrapf(define.ErrInvalidArg, "Renaming of an image is currently not supported")
}
podman := os.Args[0]
- run := podman + " image save " + image + " --format=oci-archive --output=" + remoteFile // run ssh image load of the file copied via scp. Files are reverse in thie case...
+ run := podman + " image save " + image + " --format=oci-archive --output=" + remoteFile // run ssh image load of the file copied via scp. Files are reverse in this case...
_, err = connection.ExecRemoteCommand(dial, run)
if err != nil {
return nil
@@ -193,7 +213,7 @@ func saveToRemote(image, localFile string, tag string, uri *urlP.URL, iden strin
n, err := scpD.CopyFrom(dial, remoteFile, localFile)
connection.ExecRemoteCommand(dial, "rm "+remoteFile)
if err != nil {
- errOut := (strconv.Itoa(int(n)) + " Bytes copied before error")
+ errOut := strconv.Itoa(int(n)) + " Bytes copied before error"
return errors.Wrapf(err, errOut)
}
return nil
@@ -207,11 +227,7 @@ func makeRemoteFile(dial *ssh.Client) (string, error) {
if err != nil {
return "", err
}
- remoteFile = strings.TrimSuffix(remoteFile, "\n")
- if err != nil {
- return "", err
- }
- return remoteFile, nil
+ return strings.TrimSuffix(string(remoteFile), "\n"), nil
}
// createConnections takes a boolean determining which ssh client to dial
@@ -260,7 +276,13 @@ func parseArgs(args []string, cfg *config.Config) (map[string]config.Destination
cliConnections := []string{}
switch len(args) {
case 1:
- if strings.Contains(args[0], "::") {
+ if strings.Contains(args[0], "localhost") {
+ if strings.Split(args[0], "@")[0] != "root" {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "cannot transfer images from any user besides root using sudo")
+ }
+ scpOpts.Rootless = true
+ scpOpts.SourceImageName = strings.Split(args[0], "::")[1]
+ } else if strings.Contains(args[0], "::") {
scpOpts.FromRemote = true
cliConnections = append(cliConnections, args[0])
} else {
@@ -271,7 +293,18 @@ func parseArgs(args []string, cfg *config.Config) (map[string]config.Destination
scpOpts.SourceImageName = args[0]
}
case 2:
- if strings.Contains(args[0], "::") {
+ if strings.Contains(args[0], "localhost") || strings.Contains(args[1], "localhost") { // only supporting root to local using sudo at the moment
+ if strings.Split(args[0], "@")[0] != "root" {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "currently, transferring images to a user account is not supported")
+ }
+ if len(strings.Split(args[0], "::")) > 1 {
+ scpOpts.Rootless = true
+ scpOpts.User = strings.Split(args[1], "@")[0]
+ scpOpts.SourceImageName = strings.Split(args[0], "::")[1]
+ } else {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "currently, you cannot rename images during the transfer or transfer them to a user account")
+ }
+ } else if strings.Contains(args[0], "::") {
if !(strings.Contains(args[1], "::")) && remoteArgLength(args[0], 1) == 0 { // if an image is specified, this mean we are loading to our client
cliConnections = append(cliConnections, args[0])
scpOpts.ToRemote = true
diff --git a/cmd/podman/images/sign.go b/cmd/podman/images/sign.go
index 96f214d0b..4c42a0bd6 100644
--- a/cmd/podman/images/sign.go
+++ b/cmd/podman/images/sign.go
@@ -3,6 +3,7 @@ package images
import (
"os"
+ "github.com/containers/common/pkg/auth"
"github.com/containers/common/pkg/completion"
"github.com/containers/podman/v3/cmd/podman/common"
"github.com/containers/podman/v3/cmd/podman/registry"
@@ -48,6 +49,10 @@ func init() {
flags.StringVar(&signOptions.CertDir, certDirFlagName, "", "`Pathname` of a directory containing TLS certificates and keys")
_ = signCommand.RegisterFlagCompletionFunc(certDirFlagName, completion.AutocompleteDefault)
flags.BoolVarP(&signOptions.All, "all", "a", false, "Sign all the manifests of the multi-architecture image")
+
+ authfileFlagName := "authfile"
+ flags.StringVar(&signOptions.Authfile, authfileFlagName, auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
+ _ = signCommand.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault)
}
func sign(cmd *cobra.Command, args []string) error {
diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go
index 7e5459e08..d569f4db0 100644
--- a/cmd/podman/machine/list.go
+++ b/cmd/podman/machine/list.go
@@ -48,6 +48,7 @@ type machineReporter struct {
Created string
Running bool
LastUp string
+ Stream string
VMType string
CPUs uint64
Memory string
@@ -153,6 +154,13 @@ func strUint(u uint64) string {
return strconv.FormatUint(u, 10)
}
+func streamName(imageStream string) string {
+ if imageStream == "" {
+ return "default"
+ }
+ return imageStream
+}
+
func toMachineFormat(vms []*machine.ListResponse) ([]*machineReporter, error) {
cfg, err := config.ReadCustomConfig()
if err != nil {
@@ -167,6 +175,7 @@ func toMachineFormat(vms []*machine.ListResponse) ([]*machineReporter, error) {
response.Running = vm.Running
response.LastUp = strTime(vm.LastUp)
response.Created = strTime(vm.CreatedAt)
+ response.Stream = streamName(vm.Stream)
response.VMType = vm.VMType
response.CPUs = vm.CPUs
response.Memory = strUint(vm.Memory * units.MiB)
diff --git a/cmd/podman/machine/stop.go b/cmd/podman/machine/stop.go
index 76ba85601..75666f734 100644
--- a/cmd/podman/machine/stop.go
+++ b/cmd/podman/machine/stop.go
@@ -3,6 +3,8 @@
package machine
import (
+ "fmt"
+
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/pkg/machine"
"github.com/containers/podman/v3/pkg/machine/qemu"
@@ -46,5 +48,9 @@ func stop(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
- return vm.Stop(vmName, machine.StopOptions{})
+ if err := vm.Stop(vmName, machine.StopOptions{}); err != nil {
+ return err
+ }
+ fmt.Printf("Machine %q stopped successfully\n", vmName)
+ return nil
}
diff --git a/cmd/podman/play/kube.go b/cmd/podman/play/kube.go
index e6869efd3..581b29113 100644
--- a/cmd/podman/play/kube.go
+++ b/cmd/podman/play/kube.go
@@ -80,6 +80,14 @@ func init() {
flags.StringVar(&kubeOptions.LogDriver, logDriverFlagName, "", "Logging driver for the container")
_ = kubeCmd.RegisterFlagCompletionFunc(logDriverFlagName, common.AutocompleteLogDriver)
+ logOptFlagName := "log-opt"
+ flags.StringSliceVar(
+ &kubeOptions.LogOptions,
+ logOptFlagName, []string{},
+ "Logging driver options",
+ )
+ _ = kubeCmd.RegisterFlagCompletionFunc(logOptFlagName, common.AutocompleteLogOpt)
+
flags.BoolVar(&kubeOptions.NoHosts, "no-hosts", false, "Do not create /etc/hosts within the pod's containers, instead use the version from the image")
flags.BoolVarP(&kubeOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images")
flags.BoolVar(&kubeOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go
index 7c2c72171..7399dd029 100644
--- a/cmd/podman/pods/create.go
+++ b/cmd/podman/pods/create.go
@@ -11,7 +11,6 @@ import (
"strings"
"github.com/containers/common/pkg/completion"
- "github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/sysinfo"
"github.com/containers/podman/v3/cmd/podman/common"
"github.com/containers/podman/v3/cmd/podman/containers"
@@ -47,7 +46,8 @@ var (
var (
createOptions entities.PodCreateOptions
- infraOptions entities.ContainerCreateOptions
+ infraOptions = entities.NewInfraContainerCreateOptions()
+ infraImage string
labels, labelFile []string
podIDFile string
replace bool
@@ -61,7 +61,6 @@ func init() {
})
flags := createCommand.Flags()
flags.SetInterspersed(false)
- infraOptions.IsInfra = true
common.DefineCreateFlags(createCommand, &infraOptions, true)
common.DefineNetFlags(createCommand)
@@ -72,7 +71,7 @@ func init() {
_ = createCommand.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone)
infraImageFlagName := "infra-image"
- flags.String(infraImageFlagName, containerConfig.Engine.InfraImage, "The image of the infra container to associate with the pod")
+ flags.StringVar(&infraImage, infraImageFlagName, containerConfig.Engine.InfraImage, "The image of the infra container to associate with the pod")
_ = createCommand.RegisterFlagCompletionFunc(infraImageFlagName, common.AutocompleteImages)
podIDFileFlagName := "pod-id-file"
@@ -110,14 +109,14 @@ func create(cmd *cobra.Command, args []string) error {
return errors.Wrapf(err, "unable to process labels")
}
- imageName = config.DefaultInfraImage
+ imageName = infraImage
img := imageName
if !createOptions.Infra {
if cmd.Flag("no-hosts").Changed {
return fmt.Errorf("cannot specify no-hosts without an infra container")
}
flags := cmd.Flags()
- createOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags, false)
+ createOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags, createOptions.Infra)
if err != nil {
return err
}
@@ -134,7 +133,7 @@ func create(cmd *cobra.Command, args []string) error {
} else {
// reassign certain options for lbpod api, these need to be populated in spec
flags := cmd.Flags()
- infraOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags, false)
+ infraOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags, createOptions.Infra)
if err != nil {
return err
}
@@ -151,14 +150,6 @@ func create(cmd *cobra.Command, args []string) error {
return err
}
}
- if cmd.Flag("infra-image").Changed {
- // Only send content to server side if user changed defaults
- img, err = cmd.Flags().GetString("infra-image")
- imageName = img
- if err != nil {
- return err
- }
- }
podName = createOptions.Name
err = common.ContainerToPodOptions(&infraOptions, &createOptions)
if err != nil {
@@ -242,16 +233,6 @@ func create(cmd *cobra.Command, args []string) error {
}
if createOptions.Infra {
rawImageName = img
- if !infraOptions.RootFS {
- curr := infraOptions.Quiet
- infraOptions.Quiet = true
- name, err := containers.PullImage(imageName, infraOptions)
- if err != nil {
- fmt.Println(err)
- }
- imageName = name
- infraOptions.Quiet = curr
- }
podSpec.InfraImage = imageName
if infraOptions.Entrypoint != nil {
createOptions.InfraCommand = infraOptions.Entrypoint
diff --git a/cmd/podman/root.go b/cmd/podman/root.go
index 6da34050e..418a70e1e 100644
--- a/cmd/podman/root.go
+++ b/cmd/podman/root.go
@@ -357,6 +357,11 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) {
pFlags.StringVar(&cfg.Engine.Namespace, namespaceFlagName, cfg.Engine.Namespace, "Set the libpod namespace, used to create separate views of the containers and pods on the system")
_ = cmd.RegisterFlagCompletionFunc(namespaceFlagName, completion.AutocompleteNone)
+ networkBackendFlagName := "network-backend"
+ pFlags.StringVar(&cfg.Network.NetworkBackend, networkBackendFlagName, cfg.Network.NetworkBackend, `Network backend to use ("cni"|"netavark")`)
+ _ = cmd.RegisterFlagCompletionFunc(networkBackendFlagName, common.AutocompleteNetworkBackend)
+ pFlags.MarkHidden(networkBackendFlagName)
+
rootFlagName := "root"
pFlags.StringVar(&cfg.Engine.StaticDir, rootFlagName, "", "Path to the root directory in which data, including images, is stored")
_ = cmd.RegisterFlagCompletionFunc(rootFlagName, completion.AutocompleteDefault)
diff --git a/cmd/podman/system/connection/add.go b/cmd/podman/system/connection/add.go
index 290b3c245..ee237d7d0 100644
--- a/cmd/podman/system/connection/add.go
+++ b/cmd/podman/system/connection/add.go
@@ -226,12 +226,7 @@ func getUDS(cmd *cobra.Command, uri *url.URL, iden string) (string, error) {
if v, found := os.LookupEnv("PODMAN_BINARY"); found {
podman = v
}
- run := podman + " info --format=json"
- out, err := ExecRemoteCommand(dial, run)
- if err != nil {
- return "", err
- }
- infoJSON, err := json.Marshal(out)
+ infoJSON, err := ExecRemoteCommand(dial, podman+" info --format=json")
if err != nil {
return "", err
}
diff --git a/cmd/podman/system/connection/list.go b/cmd/podman/system/connection/list.go
index a3290e3d6..f1f7657ad 100644
--- a/cmd/podman/system/connection/list.go
+++ b/cmd/podman/system/connection/list.go
@@ -53,10 +53,6 @@ func list(cmd *cobra.Command, _ []string) error {
return err
}
- if len(cfg.Engine.ServiceDestinations) == 0 {
- return nil
- }
-
hdrs := []map[string]string{{
"Identity": "Identity",
"Name": "Name",
diff --git a/cmd/podman/system/connection/remove.go b/cmd/podman/system/connection/remove.go
index 73bae4994..ffbea76c5 100644
--- a/cmd/podman/system/connection/remove.go
+++ b/cmd/podman/system/connection/remove.go
@@ -5,14 +5,14 @@ import (
"github.com/containers/podman/v3/cmd/podman/common"
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/cmd/podman/system"
+ "github.com/pkg/errors"
"github.com/spf13/cobra"
)
var (
// Skip creating engines since this command will obtain connection information to said engines
rmCmd = &cobra.Command{
- Use: "remove NAME",
- Args: cobra.ExactArgs(1),
+ Use: "remove [options] NAME",
Aliases: []string{"rm"},
Long: `Delete named destination from podman configuration`,
Short: "Delete named destination",
@@ -21,6 +21,10 @@ var (
Example: `podman system connection remove devl
podman system connection rm devl`,
}
+
+ rmOpts = struct {
+ All bool
+ }{}
)
func init() {
@@ -28,14 +32,31 @@ func init() {
Command: rmCmd,
Parent: system.ConnectionCmd,
})
+
+ flags := rmCmd.Flags()
+ flags.BoolVarP(&rmOpts.All, "all", "a", false, "Remove all connections")
}
-func rm(_ *cobra.Command, args []string) error {
+func rm(cmd *cobra.Command, args []string) error {
cfg, err := config.ReadCustomConfig()
if err != nil {
return err
}
+ if rmOpts.All {
+ if cfg.Engine.ServiceDestinations != nil {
+ for k := range cfg.Engine.ServiceDestinations {
+ delete(cfg.Engine.ServiceDestinations, k)
+ }
+ }
+ cfg.Engine.ActiveService = ""
+ return cfg.Write()
+ }
+
+ if len(args) != 1 {
+ return errors.New("accepts 1 arg(s), received 0")
+ }
+
if cfg.Engine.ServiceDestinations != nil {
delete(cfg.Engine.ServiceDestinations, args[0])
}
diff --git a/cmd/podman/system/connection/shared.go b/cmd/podman/system/connection/shared.go
index 3fd7c59fb..714ae827d 100644
--- a/cmd/podman/system/connection/shared.go
+++ b/cmd/podman/system/connection/shared.go
@@ -9,10 +9,10 @@ import (
// ExecRemoteCommand takes a ssh client connection and a command to run and executes the
// command on the specified client. The function returns the Stdout from the client or the Stderr
-func ExecRemoteCommand(dial *ssh.Client, run string) (string, error) {
+func ExecRemoteCommand(dial *ssh.Client, run string) ([]byte, error) {
sess, err := dial.NewSession() // new ssh client session
if err != nil {
- return "", err
+ return nil, err
}
defer sess.Close()
@@ -21,8 +21,7 @@ func ExecRemoteCommand(dial *ssh.Client, run string) (string, error) {
sess.Stdout = &buffer // output from client funneled into buffer
sess.Stderr = &bufferErr // err form client funneled into buffer
if err := sess.Run(run); err != nil { // run the command on the ssh client
- return "", errors.Wrapf(err, bufferErr.String())
+ return nil, errors.Wrapf(err, bufferErr.String())
}
- out := buffer.String() // output from command
- return out, nil
+ return buffer.Bytes(), nil
}
diff --git a/cmd/podman/system/unshare.go b/cmd/podman/system/unshare.go
index 50230609e..9b777dd8f 100644
--- a/cmd/podman/system/unshare.go
+++ b/cmd/podman/system/unshare.go
@@ -10,6 +10,7 @@ import (
"github.com/containers/podman/v3/pkg/rootless"
"github.com/pkg/errors"
"github.com/spf13/cobra"
+ "github.com/spf13/pflag"
)
var (
@@ -34,7 +35,14 @@ func init() {
})
flags := unshareCommand.Flags()
flags.SetInterspersed(false)
- flags.BoolVar(&unshareOptions.RootlessCNI, "rootless-cni", false, "Join the rootless network namespace used for CNI networking")
+ flags.BoolVar(&unshareOptions.RootlessNetNS, "rootless-netns", false, "Join the rootless network namespace used for CNI and netavark networking")
+ // backwards compat still allow --rootless-cni
+ flags.SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName {
+ if name == "rootless-cni" {
+ name = "rootless-netns"
+ }
+ return pflag.NormalizedName(name)
+ })
}
func unshare(cmd *cobra.Command, args []string) error {
diff --git a/cmd/podman/system/version.go b/cmd/podman/system/version.go
index 4502b156c..3443978d6 100644
--- a/cmd/podman/system/version.go
+++ b/cmd/podman/system/version.go
@@ -20,7 +20,7 @@ var (
versionCommand = &cobra.Command{
Use: "version [options]",
Args: validate.NoArgs,
- Short: "Display the Podman Version Information",
+ Short: "Display the Podman version information",
RunE: version,
ValidArgsFunction: completion.AutocompleteNone,
}
@@ -67,7 +67,7 @@ func version(cmd *cobra.Command, args []string) error {
}
if err := tmpl.Execute(w, versions); err != nil {
// On Failure, assume user is using older version of podman version --format and check client
- row = strings.Replace(row, ".Server.", ".", 1)
+ row = strings.ReplaceAll(row, ".Server.", ".")
tmpl, err := report.NewTemplate("version 1.0.0").Parse(row)
if err != nil {
return err
diff --git a/cmd/podman/validate/args.go b/cmd/podman/validate/args.go
index fc07a6acc..6b5425a69 100644
--- a/cmd/podman/validate/args.go
+++ b/cmd/podman/validate/args.go
@@ -27,7 +27,8 @@ func SubCommandExists(cmd *cobra.Command, args []string) error {
}
return errors.Errorf("unrecognized command `%[1]s %[2]s`\n\nDid you mean this?\n\t%[3]s\n\nTry '%[1]s --help' for more information.", cmd.CommandPath(), args[0], strings.Join(suggestions, "\n\t"))
}
- return errors.Errorf("missing command '%[1]s COMMAND'\nTry '%[1]s --help' for more information.", cmd.CommandPath())
+ cmd.Help()
+ return errors.Errorf("missing command '%[1]s COMMAND'", cmd.CommandPath())
}
// IDOrLatestArgs used to validate a nameOrId was provided or the "--latest" flag
diff --git a/cmd/rootlessport/main.go b/cmd/rootlessport/main.go
index feb9f5c06..e691ce2fc 100644
--- a/cmd/rootlessport/main.go
+++ b/cmd/rootlessport/main.go
@@ -10,6 +10,7 @@ import (
"os"
"os/exec"
"path/filepath"
+ "strings"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containers/podman/v3/libpod/network/types"
@@ -289,25 +290,30 @@ func handler(ctx context.Context, conn io.Reader, pm rkport.Manager) error {
return nil
}
-func exposePorts(pm rkport.Manager, portMappings []types.OCICNIPortMapping, childIP string) error {
+func exposePorts(pm rkport.Manager, portMappings []types.PortMapping, childIP string) error {
ctx := context.TODO()
- for _, i := range portMappings {
- hostIP := i.HostIP
- if hostIP == "" {
- hostIP = "0.0.0.0"
- }
- spec := rkport.Spec{
- Proto: i.Protocol,
- ParentIP: hostIP,
- ParentPort: int(i.HostPort),
- ChildPort: int(i.ContainerPort),
- ChildIP: childIP,
- }
- if err := rkportutil.ValidatePortSpec(spec, nil); err != nil {
- return err
- }
- if _, err := pm.AddPort(ctx, spec); err != nil {
- return err
+ for _, port := range portMappings {
+ protocols := strings.Split(port.Protocol, ",")
+ for _, protocol := range protocols {
+ hostIP := port.HostIP
+ if hostIP == "" {
+ hostIP = "0.0.0.0"
+ }
+ for i := uint16(0); i < port.Range; i++ {
+ spec := rkport.Spec{
+ Proto: protocol,
+ ParentIP: hostIP,
+ ParentPort: int(port.HostPort + i),
+ ChildPort: int(port.ContainerPort + i),
+ ChildIP: childIP,
+ }
+ if err := rkportutil.ValidatePortSpec(spec, nil); err != nil {
+ return err
+ }
+ if _, err := pm.AddPort(ctx, spec); err != nil {
+ return err
+ }
+ }
}
}
return nil