github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/cmd/podman/common.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/containers/buildah"
     9  	buildahcli "github.com/containers/buildah/pkg/cli"
    10  	"github.com/containers/libpod/cmd/podman/cliconfig"
    11  	"github.com/containers/libpod/pkg/util/camelcase"
    12  	jsoniter "github.com/json-iterator/go"
    13  	"github.com/pkg/errors"
    14  	"github.com/spf13/cobra"
    15  	"github.com/spf13/pflag"
    16  )
    17  
    18  var (
    19  	json = jsoniter.ConfigCompatibleWithStandardLibrary
    20  )
    21  
    22  const (
    23  	idTruncLength      = 12
    24  	sizeWithUnitFormat = "(format: `<number>[<unit>]`, where unit = b (bytes), k (kilobytes), m (megabytes), or g (gigabytes))"
    25  )
    26  
    27  func splitCamelCase(src string) string {
    28  	entries := camelcase.Split(src)
    29  	return strings.Join(entries, " ")
    30  }
    31  
    32  func shortID(id string) string {
    33  	if len(id) > idTruncLength {
    34  		return id[:idTruncLength]
    35  	}
    36  	return id
    37  }
    38  
    39  // checkAllLatestAndCIDFile checks that --all and --latest are used correctly.
    40  // If cidfile is set, also check for the --cidfile flag.
    41  func checkAllLatestAndCIDFile(c *cobra.Command, args []string, ignoreArgLen bool, cidfile bool) error {
    42  	argLen := len(args)
    43  	if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil {
    44  		if !cidfile {
    45  			return errors.New("unable to lookup values for 'latest' or 'all'")
    46  		} else if c.Flags().Lookup("cidfile") == nil {
    47  			return errors.New("unable to lookup values for 'latest', 'all' or 'cidfile'")
    48  		}
    49  	}
    50  
    51  	specifiedAll, _ := c.Flags().GetBool("all")
    52  	specifiedLatest, _ := c.Flags().GetBool("latest")
    53  	specifiedCIDFile := false
    54  	if cid, _ := c.Flags().GetStringArray("cidfile"); len(cid) > 0 {
    55  		specifiedCIDFile = true
    56  	}
    57  
    58  	if specifiedCIDFile && (specifiedAll || specifiedLatest) {
    59  		return errors.Errorf("--all, --latest and --cidfile cannot be used together")
    60  	} else if specifiedAll && specifiedLatest {
    61  		return errors.Errorf("--all and --latest cannot be used together")
    62  	}
    63  
    64  	if ignoreArgLen {
    65  		return nil
    66  	}
    67  	if (argLen > 0) && (specifiedAll || specifiedLatest) {
    68  		return errors.Errorf("no arguments are needed with --all or --latest")
    69  	} else if cidfile && (argLen > 0) && (specifiedAll || specifiedLatest || specifiedCIDFile) {
    70  		return errors.Errorf("no arguments are needed with --all, --latest or --cidfile")
    71  	}
    72  
    73  	if specifiedCIDFile {
    74  		return nil
    75  	}
    76  
    77  	if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedCIDFile {
    78  		return errors.Errorf("you must provide at least one name or id")
    79  	}
    80  	return nil
    81  }
    82  
    83  // noSubArgs checks that there are no further positional parameters
    84  func noSubArgs(c *cobra.Command, args []string) error {
    85  	if len(args) > 0 {
    86  		return errors.Errorf("`%s` takes no arguments", c.CommandPath())
    87  	}
    88  	return nil
    89  }
    90  
    91  func commandRunE() func(*cobra.Command, []string) error {
    92  	return func(cmd *cobra.Command, args []string) error {
    93  		if len(args) > 0 {
    94  			return errors.Errorf("unrecognized command `%s %s`\nTry '%s --help' for more information.", cmd.CommandPath(), args[0], cmd.CommandPath())
    95  		} else {
    96  			return errors.Errorf("missing command '%s COMMAND'\nTry '%s --help' for more information.", cmd.CommandPath(), cmd.CommandPath())
    97  		}
    98  	}
    99  }
   100  
   101  // getContext returns a non-nil, empty context
   102  func getContext() context.Context {
   103  	if Ctx != nil {
   104  		return Ctx
   105  	}
   106  	return context.TODO()
   107  }
   108  
   109  func getNetFlags() *pflag.FlagSet {
   110  	netFlags := pflag.FlagSet{}
   111  	netFlags.StringSlice(
   112  		"add-host", []string{},
   113  		"Add a custom host-to-IP mapping (host:ip)",
   114  	)
   115  	netFlags.StringSlice(
   116  		"dns", getDefaultDNSServers(),
   117  		"Set custom DNS servers",
   118  	)
   119  	netFlags.StringSlice(
   120  		"dns-opt", getDefaultDNSOptions(),
   121  		"Set custom DNS options",
   122  	)
   123  	netFlags.StringSlice(
   124  		"dns-search", getDefaultDNSSearches(),
   125  		"Set custom DNS search domains",
   126  	)
   127  	netFlags.String(
   128  		"ip", "",
   129  		"Specify a static IPv4 address for the container",
   130  	)
   131  	netFlags.String(
   132  		"mac-address", "",
   133  		"Container MAC address (e.g. 92:d0:c6:0a:29:33)",
   134  	)
   135  	netFlags.String(
   136  		"network", getDefaultNetNS(),
   137  		"Connect a container to a network",
   138  	)
   139  	netFlags.StringSliceP(
   140  		"publish", "p", []string{},
   141  		"Publish a container's port, or a range of ports, to the host (default [])",
   142  	)
   143  	netFlags.Bool(
   144  		"no-hosts", false,
   145  		"Do not create /etc/hosts within the container, instead use the version from the image",
   146  	)
   147  	return &netFlags
   148  }
   149  
   150  func getCreateFlags(c *cliconfig.PodmanCommand) {
   151  	createFlags := c.Flags()
   152  	createFlags.StringSlice(
   153  		"annotation", []string{},
   154  		"Add annotations to container (key:value)",
   155  	)
   156  	createFlags.StringSliceP(
   157  		"attach", "a", []string{},
   158  		"Attach to STDIN, STDOUT or STDERR",
   159  	)
   160  	createFlags.String(
   161  		"authfile", buildahcli.GetDefaultAuthFile(),
   162  		"Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override",
   163  	)
   164  	createFlags.String(
   165  		"blkio-weight", "",
   166  		"Block IO weight (relative weight) accepts a weight value between 10 and 1000.",
   167  	)
   168  	createFlags.StringSlice(
   169  		"blkio-weight-device", []string{},
   170  		"Block IO weight (relative device weight, format: `DEVICE_NAME:WEIGHT`)",
   171  	)
   172  	createFlags.StringSlice(
   173  		"cap-add", []string{},
   174  		"Add capabilities to the container",
   175  	)
   176  	createFlags.StringSlice(
   177  		"cap-drop", []string{},
   178  		"Drop capabilities from the container",
   179  	)
   180  	createFlags.String(
   181  		"cgroupns", getDefaultCgroupNS(),
   182  		"cgroup namespace to use",
   183  	)
   184  	createFlags.String(
   185  		"cgroups", "enabled",
   186  		`control container cgroup configuration ("enabled"|"disabled"|"no-conmon")`,
   187  	)
   188  	createFlags.String(
   189  		"cgroup-parent", "",
   190  		"Optional parent cgroup for the container",
   191  	)
   192  	createFlags.String(
   193  		"cidfile", "",
   194  		"Write the container ID to the file",
   195  	)
   196  	createFlags.String(
   197  		"conmon-pidfile", "",
   198  		"Path to the file that will receive the PID of conmon",
   199  	)
   200  	createFlags.Uint64(
   201  		"cpu-period", 0,
   202  		"Limit the CPU CFS (Completely Fair Scheduler) period",
   203  	)
   204  	createFlags.Int64(
   205  		"cpu-quota", 0,
   206  		"Limit the CPU CFS (Completely Fair Scheduler) quota",
   207  	)
   208  	createFlags.Uint64(
   209  		"cpu-rt-period", 0,
   210  		"Limit the CPU real-time period in microseconds",
   211  	)
   212  	createFlags.Int64(
   213  		"cpu-rt-runtime", 0,
   214  		"Limit the CPU real-time runtime in microseconds",
   215  	)
   216  	createFlags.Uint64(
   217  		"cpu-shares", 0,
   218  		"CPU shares (relative weight)",
   219  	)
   220  	createFlags.Float64(
   221  		"cpus", 0,
   222  		"Number of CPUs. The default is 0.000 which means no limit",
   223  	)
   224  	createFlags.String(
   225  		"cpuset-cpus", "",
   226  		"CPUs in which to allow execution (0-3, 0,1)",
   227  	)
   228  	createFlags.String(
   229  		"cpuset-mems", "",
   230  		"Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.",
   231  	)
   232  	createFlags.BoolP(
   233  		"detach", "d", false,
   234  		"Run container in background and print container ID",
   235  	)
   236  	createFlags.String(
   237  		"detach-keys", getDefaultDetachKeys(),
   238  		"Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`",
   239  	)
   240  	createFlags.StringSlice(
   241  		"device", getDefaultDevices(),
   242  		fmt.Sprintf("Add a host device to the container"),
   243  	)
   244  	createFlags.StringSlice(
   245  		"device-cgroup-rule", []string{},
   246  		"Add a rule to the cgroup allowed devices list",
   247  	)
   248  	createFlags.StringSlice(
   249  		"device-read-bps", []string{},
   250  		"Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)",
   251  	)
   252  	createFlags.StringSlice(
   253  		"device-read-iops", []string{},
   254  		"Limit read rate (IO per second) from a device (e.g. --device-read-iops=/dev/sda:1000)",
   255  	)
   256  	createFlags.StringSlice(
   257  		"device-write-bps", []string{},
   258  		"Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)",
   259  	)
   260  	createFlags.StringSlice(
   261  		"device-write-iops", []string{},
   262  		"Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)",
   263  	)
   264  	createFlags.String(
   265  		"entrypoint", "",
   266  		"Overwrite the default ENTRYPOINT of the image",
   267  	)
   268  	createFlags.StringArrayP(
   269  		"env", "e", getDefaultEnv(),
   270  		"Set environment variables in container",
   271  	)
   272  	createFlags.Bool(
   273  		"env-host", false, "Use all current host environment variables in container",
   274  	)
   275  	createFlags.StringSlice(
   276  		"env-file", []string{},
   277  		"Read in a file of environment variables",
   278  	)
   279  	createFlags.StringSlice(
   280  		"expose", []string{},
   281  		"Expose a port or a range of ports",
   282  	)
   283  	createFlags.StringSlice(
   284  		"gidmap", []string{},
   285  		"GID map to use for the user namespace",
   286  	)
   287  	createFlags.StringSlice(
   288  		"group-add", []string{},
   289  		"Add additional groups to join",
   290  	)
   291  	createFlags.Bool(
   292  		"help", false, "",
   293  	)
   294  	createFlags.String(
   295  		"health-cmd", "",
   296  		"set a healthcheck command for the container ('none' disables the existing healthcheck)",
   297  	)
   298  	createFlags.String(
   299  		"health-interval", cliconfig.DefaultHealthCheckInterval,
   300  		"set an interval for the healthchecks (a value of disable results in no automatic timer setup)",
   301  	)
   302  	createFlags.Uint(
   303  		"health-retries", cliconfig.DefaultHealthCheckRetries,
   304  		"the number of retries allowed before a healthcheck is considered to be unhealthy",
   305  	)
   306  	createFlags.String(
   307  		"health-start-period", cliconfig.DefaultHealthCheckStartPeriod,
   308  		"the initialization time needed for a container to bootstrap",
   309  	)
   310  	createFlags.String(
   311  		"health-timeout", cliconfig.DefaultHealthCheckTimeout,
   312  		"the maximum time allowed to complete the healthcheck before an interval is considered failed",
   313  	)
   314  	createFlags.StringP(
   315  		"hostname", "h", "",
   316  		"Set container hostname",
   317  	)
   318  	createFlags.Bool(
   319  		"http-proxy", true,
   320  		"Set proxy environment variables in the container based on the host proxy vars",
   321  	)
   322  	createFlags.String(
   323  		"image-volume", cliconfig.DefaultImageVolume,
   324  		`Tells podman how to handle the builtin image volumes ("bind"|"tmpfs"|"ignore")`,
   325  	)
   326  	createFlags.Bool(
   327  		"init", false,
   328  		"Run an init binary inside the container that forwards signals and reaps processes",
   329  	)
   330  	createFlags.String(
   331  		"init-path", getDefaultInitPath(),
   332  		// Do not use  the Value field for setting the default value to determine user input (i.e., non-empty string)
   333  		fmt.Sprintf("Path to the container-init binary"),
   334  	)
   335  	createFlags.BoolP(
   336  		"interactive", "i", false,
   337  		"Keep STDIN open even if not attached",
   338  	)
   339  	createFlags.String(
   340  		"ipc", getDefaultIPCNS(),
   341  		"IPC namespace to use",
   342  	)
   343  	createFlags.String(
   344  		"kernel-memory", "",
   345  		"Kernel memory limit "+sizeWithUnitFormat,
   346  	)
   347  	createFlags.StringArrayP(
   348  		"label", "l", []string{},
   349  		"Set metadata on container",
   350  	)
   351  	createFlags.StringSlice(
   352  		"label-file", []string{},
   353  		"Read in a line delimited file of labels",
   354  	)
   355  	createFlags.String(
   356  		"log-driver", "",
   357  		"Logging driver for the container",
   358  	)
   359  	createFlags.StringSlice(
   360  		"log-opt", []string{},
   361  		"Logging driver options",
   362  	)
   363  	createFlags.StringP(
   364  		"memory", "m", "",
   365  		"Memory limit "+sizeWithUnitFormat,
   366  	)
   367  	createFlags.String(
   368  		"memory-reservation", "",
   369  		"Memory soft limit "+sizeWithUnitFormat,
   370  	)
   371  	createFlags.String(
   372  		"memory-swap", "",
   373  		"Swap limit equal to memory plus swap: '-1' to enable unlimited swap",
   374  	)
   375  	createFlags.Int64(
   376  		"memory-swappiness", -1,
   377  		"Tune container memory swappiness (0 to 100, or -1 for system default)",
   378  	)
   379  	createFlags.String(
   380  		"name", "",
   381  		"Assign a name to the container",
   382  	)
   383  	createFlags.Bool(
   384  		"no-healthcheck", false,
   385  		"Disable healthchecks on container",
   386  	)
   387  	createFlags.Bool(
   388  		"oom-kill-disable", false,
   389  		"Disable OOM Killer",
   390  	)
   391  	createFlags.Int(
   392  		"oom-score-adj", 0,
   393  		"Tune the host's OOM preferences (-1000 to 1000)",
   394  	)
   395  	createFlags.String(
   396  		"override-arch", "",
   397  		"use `ARCH` instead of the architecture of the machine for choosing images",
   398  	)
   399  	markFlagHidden(createFlags, "override-arch")
   400  	createFlags.String(
   401  		"override-os", "",
   402  		"use `OS` instead of the running OS for choosing images",
   403  	)
   404  	markFlagHidden(createFlags, "override-os")
   405  	createFlags.String(
   406  		"pid", getDefaultPidNS(),
   407  		"PID namespace to use",
   408  	)
   409  	createFlags.Int64(
   410  		"pids-limit", getDefaultPidsLimit(),
   411  		getDefaultPidsDescription(),
   412  	)
   413  	createFlags.String(
   414  		"pod", "",
   415  		"Run container in an existing pod",
   416  	)
   417  	createFlags.Bool(
   418  		"privileged", false,
   419  		"Give extended privileges to container",
   420  	)
   421  	createFlags.BoolP(
   422  		"publish-all", "P", false,
   423  		"Publish all exposed ports to random ports on the host interface",
   424  	)
   425  	createFlags.String(
   426  		"pull", "missing",
   427  		`Pull image before creating ("always"|"missing"|"never")`,
   428  	)
   429  	createFlags.BoolP(
   430  		"quiet", "q", false,
   431  		"Suppress output information when pulling images",
   432  	)
   433  	createFlags.Bool(
   434  		"read-only", false,
   435  		"Make containers root filesystem read-only",
   436  	)
   437  	createFlags.Bool(
   438  		"read-only-tmpfs", true,
   439  		"When running containers in read-only mode mount a read-write tmpfs on /run, /tmp and /var/tmp",
   440  	)
   441  	createFlags.String(
   442  		"restart", "",
   443  		`Restart policy to apply when a container exits ("always"|"no"|"on-failure")`,
   444  	)
   445  	createFlags.Bool(
   446  		"rm", false,
   447  		"Remove container (and pod if created) after exit",
   448  	)
   449  	createFlags.Bool(
   450  		"rootfs", false,
   451  		"The first argument is not an image but the rootfs to the exploded container",
   452  	)
   453  	createFlags.StringArray(
   454  		"security-opt", getDefaultSecurityOptions(),
   455  		fmt.Sprintf("Security Options"),
   456  	)
   457  	createFlags.String(
   458  		"shm-size", getDefaultShmSize(),
   459  		"Size of /dev/shm "+sizeWithUnitFormat,
   460  	)
   461  	createFlags.String(
   462  		"stop-signal", "",
   463  		"Signal to stop a container. Default is SIGTERM",
   464  	)
   465  	createFlags.Uint(
   466  		"stop-timeout", defaultContainerConfig.Engine.StopTimeout,
   467  		"Timeout (in seconds) to stop a container. Default is 10",
   468  	)
   469  	createFlags.StringSlice(
   470  		"storage-opt", []string{},
   471  		"Storage driver options per container",
   472  	)
   473  	createFlags.String(
   474  		"subgidname", "",
   475  		"Name of range listed in /etc/subgid for use in user namespace",
   476  	)
   477  	createFlags.String(
   478  		"subuidname", "",
   479  		"Name of range listed in /etc/subuid for use in user namespace",
   480  	)
   481  
   482  	createFlags.StringSlice(
   483  		"sysctl", getDefaultSysctls(),
   484  		"Sysctl options",
   485  	)
   486  	createFlags.String(
   487  		"systemd", "true",
   488  		`Run container in systemd mode ("true"|"false"|"always")`,
   489  	)
   490  	createFlags.StringArray(
   491  		"tmpfs", []string{},
   492  		"Mount a temporary filesystem (`tmpfs`) into a container",
   493  	)
   494  	createFlags.BoolP(
   495  		"tty", "t", false,
   496  		"Allocate a pseudo-TTY for container",
   497  	)
   498  	createFlags.StringSlice(
   499  		"uidmap", []string{},
   500  		"UID map to use for the user namespace",
   501  	)
   502  	createFlags.StringSlice(
   503  		"ulimit", getDefaultUlimits(),
   504  		"Ulimit options",
   505  	)
   506  	createFlags.StringP(
   507  		"user", "u", "",
   508  		"Username or UID (format: <name|uid>[:<group|gid>])",
   509  	)
   510  	createFlags.String(
   511  		"userns", getDefaultUserNS(),
   512  		"User namespace to use",
   513  	)
   514  	createFlags.String(
   515  		"uts", getDefaultUTSNS(),
   516  		"UTS namespace to use",
   517  	)
   518  	createFlags.StringArray(
   519  		"mount", []string{},
   520  		"Attach a filesystem mount to the container",
   521  	)
   522  	createFlags.StringArrayP(
   523  		"volume", "v", getDefaultVolumes(),
   524  		"Bind mount a volume into the container",
   525  	)
   526  	createFlags.StringSlice(
   527  		"volumes-from", []string{},
   528  		"Mount volumes from the specified container(s)",
   529  	)
   530  	createFlags.StringP(
   531  		"workdir", "w", "",
   532  		"Working directory inside the container",
   533  	)
   534  	createFlags.String(
   535  		"seccomp-policy", "default",
   536  		"Policy for selecting a seccomp profile (experimental)",
   537  	)
   538  }
   539  
   540  func getFormat(c *cliconfig.PodmanCommand) (string, error) {
   541  	format := strings.ToLower(c.String("format"))
   542  	if strings.HasPrefix(format, buildah.OCI) {
   543  		return buildah.OCIv1ImageManifest, nil
   544  	}
   545  
   546  	if strings.HasPrefix(format, buildah.DOCKER) {
   547  		return buildah.Dockerv2ImageManifest, nil
   548  	}
   549  	return "", errors.Errorf("unrecognized image type %q", format)
   550  }
   551  
   552  // scrubServer removes 'http://' or 'https://' from the front of the
   553  // server/registry string if either is there.  This will be mostly used
   554  // for user input from 'podman login' and 'podman logout'.
   555  func scrubServer(server string) string {
   556  	server = strings.TrimPrefix(server, "https://")
   557  	return strings.TrimPrefix(server, "http://")
   558  }
   559  
   560  // HelpTemplate returns the help template for podman commands
   561  // This uses the short and long options.
   562  // command should not use this.
   563  func HelpTemplate() string {
   564  	return `{{.Short}}
   565  
   566  Description:
   567    {{.Long}}
   568  
   569  {{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
   570  }
   571  
   572  // UsageTemplate returns the usage template for podman commands
   573  // This blocks the desplaying of the global options. The main podman
   574  // command should not use this.
   575  func UsageTemplate() string {
   576  	return `Usage:{{if (and .Runnable (not .HasAvailableSubCommands))}}
   577    {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
   578    {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
   579  
   580  Aliases:
   581    {{.NameAndAliases}}{{end}}{{if .HasExample}}
   582  
   583  Examples:
   584    {{.Example}}{{end}}{{if .HasAvailableSubCommands}}
   585  
   586  Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
   587    {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
   588  
   589  Flags:
   590  {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
   591  {{end}}
   592  `
   593  }