Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for podman context as alias to podman system connection #15072

Merged
merged 1 commit into from
Sep 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion cmd/podman/registry/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ func IsRemote() bool {
fs.BoolVarP(&remoteFromCLI.Value, "remote", "r", remote, "")
connectionFlagName := "connection"
fs.StringP(connectionFlagName, "c", "", "")
contextFlagName := "context"
fs.String(contextFlagName, "", "")
hostFlagName := "host"
fs.StringP(hostFlagName, "H", "", "")
urlFlagName := "url"
Expand All @@ -46,7 +48,7 @@ func IsRemote() bool {
}
_ = fs.Parse(os.Args[start:])
// --connection or --url implies --remote
remoteFromCLI.Value = remoteFromCLI.Value || fs.Changed(connectionFlagName) || fs.Changed(urlFlagName) || fs.Changed(hostFlagName)
remoteFromCLI.Value = remoteFromCLI.Value || fs.Changed(connectionFlagName) || fs.Changed(urlFlagName) || fs.Changed(hostFlagName) || fs.Changed(contextFlagName)
})
return podmanOptions.EngineMode == entities.TunnelMode || remoteFromCLI.Value
}
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
28 changes: 28 additions & 0 deletions cmd/podman/system/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package system

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

var (
// ContextCmd skips creating engines (PersistentPreRunE/PersistentPostRunE are No-Op's) since
// sub-commands will obtain connection information to said engines
ContextCmd = &cobra.Command{
Use: "context",
Short: "Manage remote API service destinations",
Long: `Manage remote API service destination information in podman configuration`,
PersistentPreRunE: validate.NoOp,
RunE: validate.SubCommandExists,
PersistentPostRunE: validate.NoOp,
Hidden: true,
TraverseChildren: false,
}
)

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