diff options
Diffstat (limited to 'cmd')
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 |