Skip to content

Commit

Permalink
Add support for podman context as alias to podman system connection
Browse files Browse the repository at this point in the history
Alias
podman --context -> podman --connection
podman context use -> podman system connection default
podman context rm -> podman system connection rm
podman context create -> podman system connection add
podman context ls ->podman system connection ls
podman context inspect ->podman system connection ls --json (For
specified connections)

Podman context is a hidden command, but can be used for existing scripts
that assume Docker under the covers.

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
  • Loading branch information
rhatdan committed Sep 10, 2022
1 parent 2d8417d commit 38f97dc
Show file tree
Hide file tree
Showing 10 changed files with 270 additions and 23 deletions.
35 changes: 25 additions & 10 deletions cmd/podman/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,7 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error {
}
}

// --connection is not as "special" as --remote so we can wait and process it here
conn := cmd.Root().LocalFlags().Lookup("connection")
if conn != nil && conn.Changed {
cfg.Engine.ActiveService = conn.Value.String()

setupConnection := func() error {
var err error
cfg.URI, cfg.Identity, err = cfg.ActiveDestination()
if err != nil {
Expand All @@ -192,6 +188,29 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error {
if err := cmd.Root().LocalFlags().Set("identity", cfg.Identity); err != nil {
return fmt.Errorf("failed to override --identity flag: %w", err)
}
return nil
}

// --connection is not as "special" as --remote so we can wait and process it here
contextConn := cmd.Root().LocalFlags().Lookup("context")
conn := cmd.Root().LocalFlags().Lookup("connection")
if conn != nil && conn.Changed {
if contextConn != nil && contextConn.Changed {
return fmt.Errorf("use of --connection and --context at the same time is not allowed")
}
cfg.Engine.ActiveService = conn.Value.String()
if err := setupConnection(); err != nil {
return err
}
}
if contextConn != nil && contextConn.Changed {
service := contextConn.Value.String()
if service != "default" {
cfg.Engine.ActiveService = service
if err := setupConnection(); err != nil {
return err
}
}
}

// Special case if command is hidden completion command ("__complete","__completeNoDesc")
Expand Down Expand Up @@ -232,10 +251,6 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error {
}
}

context := cmd.Root().LocalFlags().Lookup("context")
if context.Value.String() != "default" {
return errors.New("podman does not support swarm, the only --context value allowed is \"default\"")
}
if !registry.IsRemote() {
if cmd.Flag("cpu-profile").Changed {
f, err := os.Create(cfg.CPUProfile)
Expand Down Expand Up @@ -362,7 +377,7 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) {
_ = cmd.RegisterFlagCompletionFunc(sshFlagName, common.AutocompleteSSH)

connectionFlagName := "connection"
lFlags.StringVarP(&opts.Engine.ActiveService, connectionFlagName, "c", srv, "Connection to use for remote Podman service")
lFlags.StringP(connectionFlagName, "c", srv, "Connection to use for remote Podman service")
_ = cmd.RegisterFlagCompletionFunc(connectionFlagName, common.AutocompleteSystemConnections)

urlFlagName := "url"
Expand Down
84 changes: 83 additions & 1 deletion cmd/podman/system/connection/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net/url"
"os"
"regexp"
"strings"

"github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/config"
Expand Down Expand Up @@ -37,6 +38,17 @@ var (
`,
}

createCmd = &cobra.Command{
Use: "create [options] NAME DESTINATION",
Args: cobra.ExactArgs(1),
Short: addCmd.Short,
Long: addCmd.Long,
RunE: create,
ValidArgsFunction: completion.AutocompleteNone,
}

dockerPath string

cOpts = struct {
Identity string
Port int
Expand All @@ -50,7 +62,6 @@ func init() {
Command: addCmd,
Parent: system.ConnectionCmd,
})

flags := addCmd.Flags()

portFlagName := "port"
Expand All @@ -66,6 +77,21 @@ func init() {
_ = addCmd.RegisterFlagCompletionFunc(socketPathFlagName, completion.AutocompleteDefault)

flags.BoolVarP(&cOpts.Default, "default", "d", false, "Set connection to be default")

registry.Commands = append(registry.Commands, registry.CliCommand{
Command: createCmd,
Parent: system.ContextCmd,
})

flags = createCmd.Flags()
dockerFlagName := "docker"
flags.StringVar(&dockerPath, dockerFlagName, "", "Description of the context")

_ = createCmd.RegisterFlagCompletionFunc(dockerFlagName, completion.AutocompleteNone)
flags.String("description", "", "Ignored. Just for script compatibility")
flags.String("from", "", "Ignored. Just for script compatibility")
flags.String("kubernetes", "", "Ignored. Just for script compatibility")
flags.String("default-stack-orchestrator", "", "Ignored. Just for script compatibility")
}

func add(cmd *cobra.Command, args []string) error {
Expand Down Expand Up @@ -171,3 +197,59 @@ func add(cmd *cobra.Command, args []string) error {
}
return cfg.Write()
}

func create(cmd *cobra.Command, args []string) error {
dest, err := translateDest(dockerPath)
if err != nil {
return err
}
if match, err := regexp.Match("^[A-Za-z][A-Za-z0-9+.-]*://", []byte(dest)); err != nil {
return fmt.Errorf("invalid destination: %w", err)
} else if !match {
dest = "ssh://" + dest
}

uri, err := url.Parse(dest)
if err != nil {
return err
}

cfg, err := config.ReadCustomConfig()
if err != nil {
return err
}

dst := config.Destination{
URI: uri.String(),
}

if cfg.Engine.ServiceDestinations == nil {
cfg.Engine.ServiceDestinations = map[string]config.Destination{
args[0]: dst,
}
cfg.Engine.ActiveService = args[0]
} else {
cfg.Engine.ServiceDestinations[args[0]] = dst
}
return cfg.Write()
}

func translateDest(path string) (string, error) {
if path == "" {
return "", nil
}
split := strings.SplitN(path, "=", 2)
if len(split) == 1 {
return split[0], nil
}
if split[0] != "host" {
return "", fmt.Errorf("\"host\" is requited for --docker option")
}
// "host=tcp://myserver:2376,ca=~/ca-file,cert=~/cert-file,key=~/key-file"
vals := strings.Split(split[1], ",")
if len(vals) > 1 {
return "", fmt.Errorf("--docker additional options %q not supported", strings.Join(vals[1:], ","))
}
// for now we ignore other fields specified on command line
return vals[0], nil
}
14 changes: 14 additions & 0 deletions cmd/podman/system/connection/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,23 @@ var (
RunE: defaultRunE,
Example: `podman system connection default testing`,
}

useCmd = &cobra.Command{
Use: "use NAME",
Args: cobra.ExactArgs(1),
Short: dfltCmd.Short,
Long: dfltCmd.Long,
ValidArgsFunction: dfltCmd.ValidArgsFunction,
RunE: dfltCmd.RunE,
Example: `podman context use testing`,
}
)

func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: useCmd,
Parent: system.ContextCmd,
})
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: dfltCmd,
Parent: system.ConnectionCmd,
Expand Down
56 changes: 51 additions & 5 deletions cmd/podman/system/connection/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/report"
"github.com/containers/common/pkg/util"
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/system"
Expand All @@ -29,16 +30,36 @@ var (
RunE: list,
TraverseChildren: false,
}
inspectCmd = &cobra.Command{
Use: "inspect [options] [CONTEXT] [CONTEXT...]",
Short: "Inspect destination for a Podman service(s)",
ValidArgsFunction: completion.AutocompleteNone,
RunE: inspect,
}
)

func init() {
initFlags := func(cmd *cobra.Command) {
cmd.Flags().StringP("format", "f", "", "Custom Go template for printing connections")
_ = cmd.RegisterFlagCompletionFunc("format", common.AutocompleteFormat(&namedDestination{}))
cmd.Flags().BoolP("quiet", "q", false, "Custom Go template for printing connections")
}

registry.Commands = append(registry.Commands, registry.CliCommand{
Command: listCmd,
Parent: system.ContextCmd,
})
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: listCmd,
Parent: system.ConnectionCmd,
})
initFlags(listCmd)

listCmd.Flags().String("format", "", "Custom Go template for printing connections")
_ = listCmd.RegisterFlagCompletionFunc("format", common.AutocompleteFormat(&namedDestination{}))
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: inspectCmd,
Parent: system.ContextCmd,
})
initFlags(inspectCmd)
}

type namedDestination struct {
Expand All @@ -48,13 +69,34 @@ type namedDestination struct {
}

func list(cmd *cobra.Command, _ []string) error {
return inspect(cmd, nil)
}

func inspect(cmd *cobra.Command, args []string) error {
cfg, err := config.ReadCustomConfig()
if err != nil {
return err
}

format := cmd.Flag("format").Value.String()
if format == "" && args != nil {
format = "json"
}

quiet, err := cmd.Flags().GetBool("quiet")
if err != nil {
return err
}
rows := make([]namedDestination, 0)
for k, v := range cfg.Engine.ServiceDestinations {
if args != nil && !util.StringInSlice(k, args) {
continue
}

if quiet {
fmt.Println(k)
continue
}
def := false
if k == cfg.Engine.ActiveService {
def = true
Expand All @@ -71,23 +113,27 @@ func list(cmd *cobra.Command, _ []string) error {
rows = append(rows, r)
}

if quiet {
return nil
}

sort.Slice(rows, func(i, j int) bool {
return rows[i].Name < rows[j].Name
})

rpt := report.New(os.Stdout, cmd.Name())
defer rpt.Flush()

if report.IsJSON(cmd.Flag("format").Value.String()) {
if report.IsJSON(format) {
buf, err := registry.JSONLibrary().MarshalIndent(rows, "", " ")
if err == nil {
fmt.Println(string(buf))
}
return err
}

if cmd.Flag("format").Changed {
rpt, err = rpt.Parse(report.OriginUser, cmd.Flag("format").Value.String())
if format != "" {
rpt, err = rpt.Parse(report.OriginUser, format)
} else {
rpt, err = rpt.Parse(report.OriginPodman,
"{{range .}}{{.Name}}\t{{.URI}}\t{{.Identity}}\t{{.Default}}\n{{end -}}")
Expand Down
8 changes: 8 additions & 0 deletions cmd/podman/system/connection/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,21 @@ var (
)

func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: rmCmd,
Parent: system.ContextCmd,
})

registry.Commands = append(registry.Commands, registry.CliCommand{
Command: rmCmd,
Parent: system.ConnectionCmd,
})

flags := rmCmd.Flags()
flags.BoolVarP(&rmOpts.All, "all", "a", false, "Remove all connections")

flags.BoolP("force", "f", false, "Ignored: for Docker compatibility")
_ = flags.MarkHidden("force")
}

func rm(cmd *cobra.Command, args []string) error {
Expand Down
24 changes: 24 additions & 0 deletions cmd/podman/system/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package system

import (
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/spf13/cobra"
)

var (
// Command: podman _system_
ContextCmd = &cobra.Command{
Use: "context",
Short: "Manage podman context",
Long: "Manage podman context",
RunE: validate.SubCommandExists,
Hidden: true,
}
)

func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: ContextCmd,
})
}
6 changes: 5 additions & 1 deletion docs/source/markdown/podman-system-connection-list.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ List ssh destination(s) for podman service(s).

## OPTIONS

#### **--format**=*format*
#### **--format**, **-f**=*format*

Change the default output format. This can be of a supported type like 'json' or a Go template.
Valid placeholders for the Go template listed below:
Expand All @@ -25,6 +25,10 @@ Valid placeholders for the Go template listed below:
| .URI | URI to podman service. Valid schemes are ssh://[user@]*host*[:port]*Unix domain socket*[?secure=True], unix://*Unix domain socket*, and tcp://localhost[:*port*] |
| .Default | Indicates whether connection is the default |

#### **--quiet**, **-q**

Only show connection names

## EXAMPLE
```
$ podman system connection list
Expand Down
1 change: 1 addition & 0 deletions pkg/rootless/rootless_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ can_use_shortcut ()

if (strcmp (argv[argc], "mount") == 0
|| strcmp (argv[argc], "machine") == 0
|| strcmp (argv[argc], "context") == 0
|| strcmp (argv[argc], "search") == 0
|| (strcmp (argv[argc], "system") == 0 && argv[argc+1] && strcmp (argv[argc+1], "service") != 0))
{
Expand Down
Loading

0 comments on commit 38f97dc

Please sign in to comment.