diff options
author | OpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com> | 2020-08-21 12:58:55 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-08-21 12:58:55 +0200 |
commit | 11372c4c4d75d731f346c6be06e41bfe9600ce81 (patch) | |
tree | 79a8f85f2f8ac32613ce86db3601b355595cf852 /cmd | |
parent | 84180d99bc808795a1f91747436a42745ddececb (diff) | |
parent | 7fc3c25410bd5ee053473ffd5df2209f41840ec0 (diff) | |
download | podman-11372c4c4d75d731f346c6be06e41bfe9600ce81.tar.gz podman-11372c4c4d75d731f346c6be06e41bfe9600ce81.tar.bz2 podman-11372c4c4d75d731f346c6be06e41bfe9600ce81.zip |
Merge pull request #7363 from mheon/lets_try_this_again
Lets try this again: v2.0.5 backports, round 2
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/podman/common/specgen.go | 23 | ||||
-rw-r--r-- | cmd/podman/containers/create.go | 5 | ||||
-rw-r--r-- | cmd/podman/early_init_linux.go | 2 | ||||
-rw-r--r-- | cmd/podman/images/list.go | 18 | ||||
-rw-r--r-- | cmd/podman/images/save.go | 31 | ||||
-rw-r--r-- | cmd/podman/images/utils_linux.go | 47 | ||||
-rw-r--r-- | cmd/podman/images/utils_unsupported.go | 7 | ||||
-rw-r--r-- | cmd/podman/main.go | 1 | ||||
-rw-r--r-- | cmd/podman/root.go | 61 | ||||
-rw-r--r-- | cmd/podman/system/connection.go | 34 | ||||
-rw-r--r-- | cmd/podman/system/connection/add.go | 223 | ||||
-rw-r--r-- | cmd/podman/system/connection/default.go | 46 | ||||
-rw-r--r-- | cmd/podman/system/connection/list.go | 84 | ||||
-rw-r--r-- | cmd/podman/system/connection/remove.go | 49 | ||||
-rw-r--r-- | cmd/podman/system/connection/rename.go | 54 | ||||
-rw-r--r-- | cmd/podman/validate/args.go | 32 | ||||
-rw-r--r-- | cmd/podman/validate/latest.go | 7 |
17 files changed, 662 insertions, 62 deletions
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go index 2333f2f7e..2074ed4fa 100644 --- a/cmd/podman/common/specgen.go +++ b/cmd/podman/common/specgen.go @@ -308,9 +308,8 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string // // Precedence order (higher index wins): // 1) env-host, 2) image data, 3) env-file, 4) env - env := map[string]string{ - "container": "podman", - } + env := make(map[string]string) + env["container"] = "podman" // First transform the os env into a map. We need it for the labels later in // any case. @@ -387,8 +386,6 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string s.Annotations = annotations s.WorkDir = c.Workdir - userCommand := []string{} - var command []string if c.Entrypoint != nil { entrypoint := []string{} if ep := *c.Entrypoint; len(ep) > 0 { @@ -398,27 +395,13 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string } } s.Entrypoint = entrypoint - // Build the command - // If we have an entry point, it goes first - command = entrypoint } // Include the command used to create the container. s.ContainerCreateCommand = os.Args if len(inputCommand) > 0 { - // User command overrides data CMD - command = append(command, inputCommand...) - userCommand = append(userCommand, inputCommand...) - } - - switch { - case len(inputCommand) > 0: - s.Command = userCommand - case c.Entrypoint != nil: - s.Command = []string{} - default: - s.Command = command + s.Command = inputCommand } // SHM Size diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index 0cd56f540..04a6ff9ba 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -292,7 +292,12 @@ func createPodIfNecessary(s *specgen.SpecGenerator, netOpts *entities.NetOptions Infra: true, Net: netOpts, CreateCommand: os.Args, + Hostname: s.ContainerBasicConfig.Hostname, } + // Unset config values we passed to the pod to prevent them being used twice for the container and pod. + s.ContainerBasicConfig.Hostname = "" + s.ContainerNetworkConfig = specgen.ContainerNetworkConfig{} + s.Pod = podName return registry.ContainerEngine().PodCreate(context.Background(), createOptions) } diff --git a/cmd/podman/early_init_linux.go b/cmd/podman/early_init_linux.go index b43450a7f..8e2f1bd21 100644 --- a/cmd/podman/early_init_linux.go +++ b/cmd/podman/early_init_linux.go @@ -32,7 +32,7 @@ func setUMask() { func earlyInitHook() { if err := setRLimits(); err != nil { - fmt.Fprint(os.Stderr, "Failed to set rlimits: "+err.Error()) + fmt.Fprintf(os.Stderr, "Failed to set rlimits: %s\n", err.Error()) } setUMask() diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go index ea88b519b..60af5c847 100644 --- a/cmd/podman/images/list.go +++ b/cmd/podman/images/list.go @@ -195,6 +195,7 @@ func sortImages(imageS []*entities.ImageSummary) ([]imageReporter, error) { } else { h.ImageSummary = *e h.Repository = "<none>" + h.Tag = "<none>" imgs = append(imgs, h) } listFlag.readOnly = e.IsReadOnly() @@ -205,27 +206,34 @@ func sortImages(imageS []*entities.ImageSummary) ([]imageReporter, error) { } func tokenRepoTag(ref string) (string, string, error) { - if ref == "<none>:<none>" { return "<none>", "<none>", nil } repo, err := reference.Parse(ref) if err != nil { - return "", "", err + return "<none>", "<none>", err } named, ok := repo.(reference.Named) if !ok { - return ref, "", nil + return ref, "<none>", nil + } + name := named.Name() + if name == "" { + name = "<none>" } tagged, ok := repo.(reference.Tagged) if !ok { - return named.Name(), "", nil + return name, "<none>", nil + } + tag := tagged.Tag() + if tag == "" { + tag = "<none>" } - return named.Name(), tagged.Tag(), nil + return name, tag, nil } diff --git a/cmd/podman/images/save.go b/cmd/podman/images/save.go index f84d97b17..00eb9f1e6 100644 --- a/cmd/podman/images/save.go +++ b/cmd/podman/images/save.go @@ -5,10 +5,9 @@ import ( "os" "strings" - "github.com/containers/libpod/v2/libpod/define" - "github.com/containers/libpod/v2/cmd/podman/parse" "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/libpod/define" "github.com/containers/libpod/v2/pkg/domain/entities" "github.com/containers/libpod/v2/pkg/util" "github.com/pkg/errors" @@ -83,9 +82,10 @@ func saveFlags(flags *pflag.FlagSet) { } -func save(cmd *cobra.Command, args []string) error { +func save(cmd *cobra.Command, args []string) (finalErr error) { var ( - tags []string + tags []string + succeeded = false ) if cmd.Flag("compress").Changed && (saveOpts.Format != define.OCIManifestDir && saveOpts.Format != define.V2s2ManifestDir && saveOpts.Format == "") { return errors.Errorf("--compress can only be set when --format is either 'oci-dir' or 'docker-dir'") @@ -95,7 +95,22 @@ func save(cmd *cobra.Command, args []string) error { if terminal.IsTerminal(int(fi.Fd())) { return errors.Errorf("refusing to save to terminal. Use -o flag or redirect") } - saveOpts.Output = "/dev/stdout" + pipePath, cleanup, err := setupPipe() + if err != nil { + return err + } + if cleanup != nil { + defer func() { + errc := cleanup() + if succeeded { + writeErr := <-errc + if writeErr != nil && finalErr == nil { + finalErr = writeErr + } + } + }() + } + saveOpts.Output = pipePath } if err := parse.ValidateFileName(saveOpts.Output); err != nil { return err @@ -103,5 +118,9 @@ func save(cmd *cobra.Command, args []string) error { if len(args) > 1 { tags = args[1:] } - return registry.ImageEngine().Save(context.Background(), args[0], tags, saveOpts) + err := registry.ImageEngine().Save(context.Background(), args[0], tags, saveOpts) + if err == nil { + succeeded = true + } + return err } diff --git a/cmd/podman/images/utils_linux.go b/cmd/podman/images/utils_linux.go new file mode 100644 index 000000000..5521abab4 --- /dev/null +++ b/cmd/podman/images/utils_linux.go @@ -0,0 +1,47 @@ +package images + +import ( + "io" + "io/ioutil" + "os" + "path/filepath" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" +) + +// setupPipe for fixing https://github.com/containers/podman/issues/7017 +// uses named pipe since containers/image EvalSymlinks fails with /dev/stdout +// the caller should use the returned function to clean up the pipeDir +func setupPipe() (string, func() <-chan error, error) { + errc := make(chan error) + pipeDir, err := ioutil.TempDir(os.TempDir(), "pipeDir") + if err != nil { + return "", nil, err + } + pipePath := filepath.Join(pipeDir, "saveio") + err = unix.Mkfifo(pipePath, 0600) + if err != nil { + if e := os.RemoveAll(pipeDir); e != nil { + logrus.Errorf("error removing named pipe: %q", e) + } + return "", nil, errors.Wrapf(err, "error creating named pipe") + } + go func() { + fpipe, err := os.Open(pipePath) + if err != nil { + errc <- err + return + } + _, err = io.Copy(os.Stdout, fpipe) + fpipe.Close() + errc <- err + }() + return pipePath, func() <-chan error { + if e := os.RemoveAll(pipeDir); e != nil { + logrus.Errorf("error removing named pipe: %q", e) + } + return errc + }, nil +} diff --git a/cmd/podman/images/utils_unsupported.go b/cmd/podman/images/utils_unsupported.go new file mode 100644 index 000000000..69d1df786 --- /dev/null +++ b/cmd/podman/images/utils_unsupported.go @@ -0,0 +1,7 @@ +// +build !linux + +package images + +func setupPipe() (string, func() <-chan error, error) { + return "/dev/stdout", nil, nil +} diff --git a/cmd/podman/main.go b/cmd/podman/main.go index 4bdb020af..d0e89c2f5 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -14,6 +14,7 @@ import ( _ "github.com/containers/libpod/v2/cmd/podman/pods" "github.com/containers/libpod/v2/cmd/podman/registry" _ "github.com/containers/libpod/v2/cmd/podman/system" + _ "github.com/containers/libpod/v2/cmd/podman/system/connection" _ "github.com/containers/libpod/v2/cmd/podman/volumes" "github.com/containers/libpod/v2/pkg/rootless" "github.com/containers/libpod/v2/pkg/terminal" diff --git a/cmd/podman/root.go b/cmd/podman/root.go index b2c9f9c2c..44e014906 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -6,7 +6,6 @@ import ( "path" "runtime" "runtime/pprof" - "strconv" "strings" "github.com/containers/common/pkg/config" @@ -112,13 +111,28 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error { cfg := registry.PodmanConfig() - // Validate --remote and --latest not given on same command - latest := cmd.Flags().Lookup("latest") - if latest != nil { - value, _ := strconv.ParseBool(latest.Value.String()) - if cfg.Remote && value { - return errors.Errorf("For %s \"--remote\" and \"--latest\", are mutually exclusive flags", cmd.CommandPath()) + // --connection is not as "special" as --remote so we can wait and process it here + var connErr error + conn := cmd.Root().LocalFlags().Lookup("connection") + if conn != nil && conn.Changed { + cfg.Engine.ActiveService = conn.Value.String() + + var err error + cfg.URI, cfg.Identity, err = cfg.ActiveDestination() + if err != nil { + connErr = errors.Wrap(err, "failed to resolve active destination") } + + if err := cmd.Root().LocalFlags().Set("url", cfg.URI); err != nil { + connErr = errors.Wrap(err, "failed to override --url flag") + } + + if err := cmd.Root().LocalFlags().Set("identity", cfg.Identity); err != nil { + connErr = errors.Wrap(err, "failed to override --identity flag") + } + } + if connErr != nil { + return connErr } // Prep the engines @@ -221,16 +235,13 @@ func loggingHook() { func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) { cfg := opts.Config + srv, uri, ident := resolveDestination() lFlags := cmd.Flags() - custom, _ := config.ReadCustomConfig() - defaultURI := custom.Engine.RemoteURI - if defaultURI == "" { - defaultURI = registry.DefaultAPIAddress() - } lFlags.BoolVarP(&opts.Remote, "remote", "r", false, "Access remote Podman service (default false)") - lFlags.StringVar(&opts.URI, "url", defaultURI, "URL to access Podman service (CONTAINER_HOST)") - lFlags.StringVar(&opts.Identity, "identity", custom.Engine.RemoteIdentity, "path to SSH identity file, (CONTAINER_SSHKEY)") + lFlags.StringVarP(&opts.Engine.ActiveService, "connection", "c", srv, "Connection to use for remote Podman service") + lFlags.StringVar(&opts.URI, "url", uri, "URL to access Podman service (CONTAINER_HOST)") + lFlags.StringVar(&opts.Identity, "identity", ident, "path to SSH identity file, (CONTAINER_SSHKEY)") pFlags := cmd.PersistentFlags() pFlags.StringVar(&cfg.Engine.CgroupManager, "cgroup-manager", cfg.Engine.CgroupManager, "Cgroup manager to use (\"cgroupfs\"|\"systemd\")") @@ -277,3 +288,25 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) { pFlags.BoolVar(&useSyslog, "syslog", false, "Output logging information to syslog as well as the console (default false)") } } + +func resolveDestination() (string, string, string) { + if uri, found := os.LookupEnv("CONTAINER_HOST"); found { + var ident string + if v, found := os.LookupEnv("CONTAINER_SSHKEY"); found { + ident = v + } + return "", uri, ident + } + + cfg, err := config.ReadCustomConfig() + if err != nil { + logrus.Warning(errors.Wrap(err, "unable to read local containers.conf")) + return "", registry.DefaultAPIAddress(), "" + } + + uri, ident, err := cfg.ActiveDestination() + if err != nil { + return "", registry.DefaultAPIAddress(), "" + } + return cfg.Engine.ActiveService, uri, ident +} diff --git a/cmd/podman/system/connection.go b/cmd/podman/system/connection.go new file mode 100644 index 000000000..b1c538803 --- /dev/null +++ b/cmd/podman/system/connection.go @@ -0,0 +1,34 @@ +package system + +import ( + "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/validate" + "github.com/containers/libpod/v2/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + // Skip creating engines since this command will obtain connection information to said engines + noOp = func(cmd *cobra.Command, args []string) error { + return nil + } + + ConnectionCmd = &cobra.Command{ + Use: "connection", + Short: "Manage remote ssh destinations", + Long: `Manage ssh destination information in podman configuration`, + DisableFlagsInUseLine: true, + PersistentPreRunE: noOp, + RunE: validate.SubCommandExists, + PersistentPostRunE: noOp, + TraverseChildren: false, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: ConnectionCmd, + Parent: systemCmd, + }) +} diff --git a/cmd/podman/system/connection/add.go b/cmd/podman/system/connection/add.go new file mode 100644 index 000000000..77a2edf8a --- /dev/null +++ b/cmd/podman/system/connection/add.go @@ -0,0 +1,223 @@ +package connection + +import ( + "bytes" + "encoding/json" + "fmt" + "net" + "net/url" + "os" + "os/user" + "regexp" + + "github.com/containers/common/pkg/config" + "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/system" + "github.com/containers/libpod/v2/libpod/define" + "github.com/containers/libpod/v2/pkg/domain/entities" + "github.com/containers/libpod/v2/pkg/terminal" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/agent" +) + +const schemaPattern = "^[A-Za-z][A-Za-z0-9+.-]*:" + +var ( + addCmd = &cobra.Command{ + Use: "add [flags] NAME DESTINATION", + Args: cobra.ExactArgs(2), + Short: "Record destination for the Podman service", + Long: `Add destination to podman configuration. + "destination" is of the form [user@]hostname or + an URI of the form ssh://[user@]hostname[:port] +`, + RunE: add, + Example: `podman system connection add laptop server.fubar.com + podman system connection add --identity ~/.ssh/dev_rsa testing ssh://root@server.fubar.com:2222 + podman system connection add --identity ~/.ssh/dev_rsa --port 22 production root@server.fubar.com + `, + } + + cOpts = struct { + Identity string + Port int + UDSPath string + Default bool + }{} +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: addCmd, + Parent: system.ConnectionCmd, + }) + + flags := addCmd.Flags() + flags.IntVarP(&cOpts.Port, "port", "p", 22, "SSH port number for destination") + flags.StringVar(&cOpts.Identity, "identity", "", "path to SSH identity file") + flags.StringVar(&cOpts.UDSPath, "socket-path", "", "path to podman socket on remote host. (default '/run/podman/podman.sock' or '/run/user/{uid}/podman/podman.sock)") + flags.BoolVarP(&cOpts.Default, "default", "d", false, "Set connection to be default") +} + +func add(cmd *cobra.Command, args []string) error { + // Default to ssh: schema if none given + dest := args[1] + if match, err := regexp.Match(schemaPattern, []byte(dest)); err != nil { + return errors.Wrapf(err, "internal regex error %q", schemaPattern) + } else if !match { + dest = "ssh://" + dest + } + + uri, err := url.Parse(dest) + if err != nil { + return errors.Wrapf(err, "failed to parse %q", dest) + } + + if uri.User.Username() == "" { + if uri.User, err = getUserInfo(uri); err != nil { + return err + } + } + + if cmd.Flags().Changed("socket-path") { + uri.Path = cmd.Flag("socket-path").Value.String() + } + + if cmd.Flags().Changed("port") { + uri.Host = net.JoinHostPort(uri.Hostname(), cmd.Flag("port").Value.String()) + } + + if uri.Port() == "" { + uri.Host = net.JoinHostPort(uri.Hostname(), cmd.Flag("port").DefValue) + } + + if uri.Path == "" || uri.Path == "/" { + if uri.Path, err = getUDS(cmd, uri); err != nil { + return errors.Wrapf(err, "failed to connect to %q", uri.String()) + } + } + + cfg, err := config.ReadCustomConfig() + if err != nil { + return err + } + + if cmd.Flags().Changed("default") { + if cOpts.Default { + cfg.Engine.ActiveService = args[0] + } + } + + dst := config.Destination{ + URI: uri.String(), + } + + if cmd.Flags().Changed("identity") { + dst.Identity = cOpts.Identity + } + + if cfg.Engine.ServiceDestinations == nil { + cfg.Engine.ServiceDestinations = map[string]config.Destination{ + args[0]: dst, + } + } else { + cfg.Engine.ServiceDestinations[args[0]] = dst + } + return cfg.Write() +} + +func getUserInfo(uri *url.URL) (*url.Userinfo, error) { + var ( + usr *user.User + err error + ) + if u, found := os.LookupEnv("_CONTAINERS_ROOTLESS_UID"); found { + usr, err = user.LookupId(u) + if err != nil { + return nil, errors.Wrapf(err, "failed to find user %q", u) + } + } else { + usr, err = user.Current() + if err != nil { + return nil, errors.Wrapf(err, "failed to obtain current user") + } + } + + pw, set := uri.User.Password() + if set { + return url.UserPassword(usr.Username, pw), nil + } + return url.User(usr.Username), nil +} + +func getUDS(cmd *cobra.Command, uri *url.URL) (string, error) { + var authMethods []ssh.AuthMethod + passwd, set := uri.User.Password() + if set { + authMethods = append(authMethods, ssh.Password(passwd)) + } + + if cmd.Flags().Changed("identity") { + value := cmd.Flag("identity").Value.String() + auth, err := terminal.PublicKey(value, []byte(passwd)) + if err != nil { + return "", errors.Wrapf(err, "Failed to read identity %q", value) + } + authMethods = append(authMethods, auth) + } + + if sock, found := os.LookupEnv("SSH_AUTH_SOCK"); found { + logrus.Debugf("Found SSH_AUTH_SOCK %q, ssh-agent signer enabled", sock) + + c, err := net.Dial("unix", sock) + if err != nil { + return "", err + } + a := agent.NewClient(c) + authMethods = append(authMethods, ssh.PublicKeysCallback(a.Signers)) + } + + config := &ssh.ClientConfig{ + User: uri.User.Username(), + Auth: authMethods, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + } + dial, err := ssh.Dial("tcp", uri.Host, config) + if err != nil { + return "", errors.Wrapf(err, "failed to connect to %q", uri.Host) + } + defer dial.Close() + + session, err := dial.NewSession() + if err != nil { + return "", errors.Wrapf(err, "failed to create new ssh session on %q", uri.Host) + } + defer session.Close() + + // Override podman binary for testing etc + podman := "podman" + if v, found := os.LookupEnv("PODMAN_BINARY"); found { + podman = v + } + run := podman + " info --format=json" + + var buffer bytes.Buffer + session.Stdout = &buffer + if err := session.Run(run); err != nil { + return "", errors.Wrapf(err, "failed to run %q", run) + } + + var info define.Info + if err := json.Unmarshal(buffer.Bytes(), &info); err != nil { + return "", errors.Wrapf(err, "failed to parse 'podman info' results") + } + + if info.Host.RemoteSocket == nil || len(info.Host.RemoteSocket.Path) == 0 { + return "", fmt.Errorf("remote podman %q failed to report its UDS socket", uri.Host) + } + return info.Host.RemoteSocket.Path, nil +} diff --git a/cmd/podman/system/connection/default.go b/cmd/podman/system/connection/default.go new file mode 100644 index 000000000..b85343dc2 --- /dev/null +++ b/cmd/podman/system/connection/default.go @@ -0,0 +1,46 @@ +package connection + +import ( + "fmt" + + "github.com/containers/common/pkg/config" + "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/system" + "github.com/containers/libpod/v2/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + // Skip creating engines since this command will obtain connection information to said engines + dfltCmd = &cobra.Command{ + Use: "default NAME", + Args: cobra.ExactArgs(1), + Short: "Set named destination as default", + Long: `Set named destination as default for the Podman service`, + DisableFlagsInUseLine: true, + RunE: defaultRunE, + Example: `podman system connection default testing`, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: dfltCmd, + Parent: system.ConnectionCmd, + }) +} + +func defaultRunE(cmd *cobra.Command, args []string) error { + cfg, err := config.ReadCustomConfig() + if err != nil { + return err + } + + if _, found := cfg.Engine.ServiceDestinations[args[0]]; !found { + return fmt.Errorf("%q destination is not defined. See \"podman system connection add ...\" to create a connection", args[0]) + } + + cfg.Engine.ActiveService = args[0] + return cfg.Write() +} diff --git a/cmd/podman/system/connection/list.go b/cmd/podman/system/connection/list.go new file mode 100644 index 000000000..c0a9087f5 --- /dev/null +++ b/cmd/podman/system/connection/list.go @@ -0,0 +1,84 @@ +package connection + +import ( + "os" + "text/tabwriter" + "text/template" + + "github.com/containers/common/pkg/config" + "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/system" + "github.com/containers/libpod/v2/cmd/podman/validate" + "github.com/containers/libpod/v2/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + listCmd = &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Args: validate.NoArgs, + Short: "List destination for the Podman service(s)", + Long: `List destination information for the Podman service(s) in podman configuration`, + DisableFlagsInUseLine: true, + Example: `podman system connection list + podman system connection ls`, + RunE: list, + TraverseChildren: false, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: listCmd, + Parent: system.ConnectionCmd, + }) +} + +type namedDestination struct { + Name string + config.Destination +} + +func list(_ *cobra.Command, _ []string) error { + cfg, err := config.ReadCustomConfig() + if err != nil { + return err + } + + if len(cfg.Engine.ServiceDestinations) == 0 { + return nil + } + + hdrs := []map[string]string{{ + "Identity": "Identity", + "Name": "Name", + "URI": "URI", + }} + + rows := make([]namedDestination, 0) + for k, v := range cfg.Engine.ServiceDestinations { + if k == cfg.Engine.ActiveService { + k += "*" + } + + r := namedDestination{ + Name: k, + Destination: config.Destination{ + Identity: v.Identity, + URI: v.URI, + }, + } + rows = append(rows, r) + } + + // TODO: Allow user to override format + format := "{{range . }}{{.Name}}\t{{.Identity}}\t{{.URI}}\n{{end}}" + tmpl := template.Must(template.New("connection").Parse(format)) + w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) + defer w.Flush() + + _ = tmpl.Execute(w, hdrs) + return tmpl.Execute(w, rows) +} diff --git a/cmd/podman/system/connection/remove.go b/cmd/podman/system/connection/remove.go new file mode 100644 index 000000000..a2ca66c8d --- /dev/null +++ b/cmd/podman/system/connection/remove.go @@ -0,0 +1,49 @@ +package connection + +import ( + "github.com/containers/common/pkg/config" + "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/system" + "github.com/containers/libpod/v2/pkg/domain/entities" + "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), + Aliases: []string{"rm"}, + Long: `Delete named destination from podman configuration`, + Short: "Delete named destination", + DisableFlagsInUseLine: true, + RunE: rm, + Example: `podman system connection remove devl + podman system connection rm devl`, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: rmCmd, + Parent: system.ConnectionCmd, + }) +} + +func rm(_ *cobra.Command, args []string) error { + cfg, err := config.ReadCustomConfig() + if err != nil { + return err + } + + if cfg.Engine.ServiceDestinations != nil { + delete(cfg.Engine.ServiceDestinations, args[0]) + } + + if cfg.Engine.ActiveService == args[0] { + cfg.Engine.ActiveService = "" + } + + return cfg.Write() +} diff --git a/cmd/podman/system/connection/rename.go b/cmd/podman/system/connection/rename.go new file mode 100644 index 000000000..d6cd55c31 --- /dev/null +++ b/cmd/podman/system/connection/rename.go @@ -0,0 +1,54 @@ +package connection + +import ( + "fmt" + + "github.com/containers/common/pkg/config" + "github.com/containers/libpod/v2/cmd/podman/registry" + "github.com/containers/libpod/v2/cmd/podman/system" + "github.com/containers/libpod/v2/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + // Skip creating engines since this command will obtain connection information to said engines + renameCmd = &cobra.Command{ + Use: "rename OLD NEW", + Aliases: []string{"mv"}, + Args: cobra.ExactArgs(2), + Short: "Rename \"old\" to \"new\"", + Long: `Rename destination for the Podman service from "old" to "new"`, + DisableFlagsInUseLine: true, + RunE: rename, + Example: `podman system connection rename laptop devl, + podman system connection mv laptop devl`, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: renameCmd, + Parent: system.ConnectionCmd, + }) +} + +func rename(cmd *cobra.Command, args []string) error { + cfg, err := config.ReadCustomConfig() + if err != nil { + return err + } + + if _, found := cfg.Engine.ServiceDestinations[args[0]]; !found { + return fmt.Errorf("%q destination is not defined. See \"podman system connection add ...\" to create a connection", args[0]) + } + + cfg.Engine.ServiceDestinations[args[1]] = cfg.Engine.ServiceDestinations[args[0]] + delete(cfg.Engine.ServiceDestinations, args[0]) + + if cfg.Engine.ActiveService == args[0] { + cfg.Engine.ActiveService = args[1] + } + + return cfg.Write() +} diff --git a/cmd/podman/validate/args.go b/cmd/podman/validate/args.go index a33f47959..cf500c0a3 100644 --- a/cmd/podman/validate/args.go +++ b/cmd/podman/validate/args.go @@ -4,6 +4,7 @@ import ( "fmt" "strconv" + "github.com/containers/libpod/v2/cmd/podman/registry" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -47,17 +48,20 @@ func IDOrLatestArgs(cmd *cobra.Command, args []string) error { // CheckAllLatestAndCIDFile checks that --all and --latest are used correctly. // If cidfile is set, also check for the --cidfile flag. func CheckAllLatestAndCIDFile(c *cobra.Command, args []string, ignoreArgLen bool, cidfile bool) error { + var specifiedLatest bool argLen := len(args) - if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil { - if !cidfile { - return errors.New("unable to lookup values for 'latest' or 'all'") - } else if c.Flags().Lookup("cidfile") == nil { - return errors.New("unable to lookup values for 'latest', 'all' or 'cidfile'") + if !registry.IsRemote() { + specifiedLatest, _ = c.Flags().GetBool("latest") + if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil { + if !cidfile { + return errors.New("unable to lookup values for 'latest' or 'all'") + } else if c.Flags().Lookup("cidfile") == nil { + return errors.New("unable to lookup values for 'latest', 'all' or 'cidfile'") + } } } specifiedAll, _ := c.Flags().GetBool("all") - specifiedLatest, _ := c.Flags().GetBool("latest") specifiedCIDFile := false if cid, _ := c.Flags().GetStringArray("cidfile"); len(cid) > 0 { specifiedCIDFile = true @@ -98,17 +102,21 @@ func CheckAllLatestAndCIDFile(c *cobra.Command, args []string, ignoreArgLen bool // CheckAllLatestAndPodIDFile checks that --all and --latest are used correctly. // If withIDFile is set, also check for the --pod-id-file flag. func CheckAllLatestAndPodIDFile(c *cobra.Command, args []string, ignoreArgLen bool, withIDFile bool) error { + var specifiedLatest bool argLen := len(args) - if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil { - if !withIDFile { - return errors.New("unable to lookup values for 'latest' or 'all'") - } else if c.Flags().Lookup("pod-id-file") == nil { - return errors.New("unable to lookup values for 'latest', 'all' or 'pod-id-file'") + if !registry.IsRemote() { + // remote clients have no latest flag + specifiedLatest, _ = c.Flags().GetBool("latest") + if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil { + if !withIDFile { + return errors.New("unable to lookup values for 'latest' or 'all'") + } else if c.Flags().Lookup("pod-id-file") == nil { + return errors.New("unable to lookup values for 'latest', 'all' or 'pod-id-file'") + } } } specifiedAll, _ := c.Flags().GetBool("all") - specifiedLatest, _ := c.Flags().GetBool("latest") specifiedPodIDFile := false if pid, _ := c.Flags().GetStringArray("pod-id-file"); len(pid) > 0 { specifiedPodIDFile = true diff --git a/cmd/podman/validate/latest.go b/cmd/podman/validate/latest.go index 0ebed7227..a9049ca2c 100644 --- a/cmd/podman/validate/latest.go +++ b/cmd/podman/validate/latest.go @@ -7,9 +7,8 @@ import ( func AddLatestFlag(cmd *cobra.Command, b *bool) { // Initialization flag verification - cmd.Flags().BoolVarP(b, "latest", "l", false, - "Act on the latest container podman is aware of\nNot supported with the \"--remote\" flag") - if registry.IsRemote() { - _ = cmd.Flags().MarkHidden("latest") + if !registry.IsRemote() { + cmd.Flags().BoolVarP(b, "latest", "l", false, + "Act on the latest container podman is aware of\nNot supported with the \"--remote\" flag") } } |