diff options
70 files changed, 404 insertions, 96 deletions
diff --git a/cmd/podman/attach.go b/cmd/podman/attach.go index 5b58c022a..dc0563a94 100644 --- a/cmd/podman/attach.go +++ b/cmd/podman/attach.go @@ -30,7 +30,7 @@ var ( Name: "attach", Usage: "Attach to a running container", Description: attachDescription, - Flags: attachFlags, + Flags: sortFlags(attachFlags), Action: attachCmd, ArgsUsage: "", OnUsageError: usageErrorHandler, diff --git a/cmd/podman/build.go b/cmd/podman/build.go index 1b8a5faec..424f9d471 100644 --- a/cmd/podman/build.go +++ b/cmd/podman/build.go @@ -29,7 +29,7 @@ var ( Name: "build", Usage: "Build an image using instructions from Dockerfiles", Description: buildDescription, - Flags: append(append(buildahcli.BudFlags, layerFlags...), buildahcli.FromAndBudFlags...), + Flags: sortFlags(append(append(buildahcli.BudFlags, layerFlags...), buildahcli.FromAndBudFlags...)), Action: buildCmd, ArgsUsage: "CONTEXT-DIRECTORY | URL", SkipArgReorder: true, diff --git a/cmd/podman/checkpoint.go b/cmd/podman/checkpoint.go index cbbbcd740..8582ce138 100644 --- a/cmd/podman/checkpoint.go +++ b/cmd/podman/checkpoint.go @@ -27,7 +27,7 @@ var ( Name: "checkpoint", Usage: "Checkpoints one or more containers", Description: checkpointDescription, - Flags: checkpointFlags, + Flags: sortFlags(checkpointFlags), Action: checkpointCmd, ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", } diff --git a/cmd/podman/cleanup.go b/cmd/podman/cleanup.go index 316704f91..3fd150783 100644 --- a/cmd/podman/cleanup.go +++ b/cmd/podman/cleanup.go @@ -27,7 +27,7 @@ var ( Name: "cleanup", Usage: "Cleanup network and mountpoints of one or more containers", Description: cleanupDescription, - Flags: cleanupFlags, + Flags: sortFlags(cleanupFlags), Action: cleanupCmd, ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", OnUsageError: usageErrorHandler, diff --git a/cmd/podman/commit.go b/cmd/podman/commit.go index 234926de0..b09c6b0d9 100644 --- a/cmd/podman/commit.go +++ b/cmd/podman/commit.go @@ -52,7 +52,7 @@ var ( Name: "commit", Usage: "Create new image based on the changed container", Description: commitDescription, - Flags: commitFlags, + Flags: sortFlags(commitFlags), Action: commitCmd, ArgsUsage: "CONTAINER [REPOSITORY[:TAG]]", OnUsageError: usageErrorHandler, diff --git a/cmd/podman/common.go b/cmd/podman/common.go index 9ab0e57e5..1e7f8d4d8 100644 --- a/cmd/podman/common.go +++ b/cmd/podman/common.go @@ -6,6 +6,7 @@ import ( "os" "reflect" "regexp" + "sort" "strings" "github.com/containers/buildah" @@ -249,6 +250,10 @@ var createFlags = []cli.Flag{ Usage: "Keep STDIN open even if not attached", }, cli.StringFlag{ + Name: "ip", + Usage: "Specify a static IPv4 address for the container", + }, + cli.StringFlag{ Name: "ipc", Usage: "IPC namespace to use", }, @@ -446,3 +451,10 @@ func getFormat(c *cli.Context) (string, error) { } return "", errors.Errorf("unrecognized image type %q", format) } + +func sortFlags(flags []cli.Flag) []cli.Flag { + sort.Slice(flags, func(i, j int) bool { + return strings.Compare(flags[i].GetName(), flags[j].GetName()) < 0 + }) + return flags +} diff --git a/cmd/podman/create.go b/cmd/podman/create.go index e442e5c03..248ff1b7d 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -48,7 +48,7 @@ var createCommand = cli.Command{ Name: "create", Usage: "Create but do not start a container", Description: createDescription, - Flags: createFlags, + Flags: sortFlags(createFlags), Action: createCmd, ArgsUsage: "IMAGE [COMMAND [ARG...]]", HideHelp: true, diff --git a/cmd/podman/diff.go b/cmd/podman/diff.go index dc35ea5a3..5f813699f 100644 --- a/cmd/podman/diff.go +++ b/cmd/podman/diff.go @@ -51,7 +51,7 @@ var ( Name: "diff", Usage: "Inspect changes on container's file systems", Description: diffDescription, - Flags: diffFlags, + Flags: sortFlags(diffFlags), Action: diffCmd, ArgsUsage: "ID-NAME", OnUsageError: usageErrorHandler, diff --git a/cmd/podman/exec.go b/cmd/podman/exec.go index 38cee67d6..1dcb88dbd 100644 --- a/cmd/podman/exec.go +++ b/cmd/podman/exec.go @@ -46,7 +46,7 @@ var ( Name: "exec", Usage: "Run a process in a running container", Description: execDescription, - Flags: execFlags, + Flags: sortFlags(execFlags), Action: execCmd, ArgsUsage: "CONTAINER-NAME", SkipArgReorder: true, diff --git a/cmd/podman/export.go b/cmd/podman/export.go index dd73c8663..667b8d012 100644 --- a/cmd/podman/export.go +++ b/cmd/podman/export.go @@ -23,7 +23,7 @@ var ( Name: "export", Usage: "Export container's filesystem contents as a tar archive", Description: exportDescription, - Flags: exportFlags, + Flags: sortFlags(exportFlags), Action: exportCmd, ArgsUsage: "CONTAINER", OnUsageError: usageErrorHandler, diff --git a/cmd/podman/history.go b/cmd/podman/history.go index 35f71dc60..7c8c619c8 100644 --- a/cmd/podman/history.go +++ b/cmd/podman/history.go @@ -59,7 +59,7 @@ var ( Name: "history", Usage: "Show history of a specified image", Description: historyDescription, - Flags: historyFlags, + Flags: sortFlags(historyFlags), Action: historyCmd, ArgsUsage: "", UseShortOptionHandling: true, diff --git a/cmd/podman/images.go b/cmd/podman/images.go index f88ca32fd..a8955e49e 100644 --- a/cmd/podman/images.go +++ b/cmd/podman/images.go @@ -124,7 +124,7 @@ var ( Name: "images", Usage: "List images in local storage", Description: imagesDescription, - Flags: imagesFlags, + Flags: sortFlags(imagesFlags), Action: imagesCmd, ArgsUsage: "", UseShortOptionHandling: true, diff --git a/cmd/podman/import.go b/cmd/podman/import.go index c663e7128..be516e4fa 100644 --- a/cmd/podman/import.go +++ b/cmd/podman/import.go @@ -39,7 +39,7 @@ var ( Name: "import", Usage: "Import a tarball to create a filesystem image", Description: importDescription, - Flags: importFlags, + Flags: sortFlags(importFlags), Action: importCmd, ArgsUsage: "TARBALL [REFERENCE]", OnUsageError: usageErrorHandler, diff --git a/cmd/podman/info.go b/cmd/podman/info.go index 927bf57be..563e63ba3 100644 --- a/cmd/podman/info.go +++ b/cmd/podman/info.go @@ -16,7 +16,7 @@ var ( Name: "info", Usage: infoDescription, Description: `Information display here pertain to the host, current storage stats, and build of podman. Useful for the user and when reporting issues.`, - Flags: infoFlags, + Flags: sortFlags(infoFlags), Action: infoCmd, ArgsUsage: "", OnUsageError: usageErrorHandler, diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go index f4c460777..bd9e8c13c 100644 --- a/cmd/podman/inspect.go +++ b/cmd/podman/inspect.go @@ -41,7 +41,7 @@ var ( Name: "inspect", Usage: "Displays the configuration of a container or image", Description: inspectDescription, - Flags: inspectFlags, + Flags: sortFlags(inspectFlags), Action: inspectCmd, ArgsUsage: "CONTAINER-OR-IMAGE [CONTAINER-OR-IMAGE]...", OnUsageError: usageErrorHandler, diff --git a/cmd/podman/kill.go b/cmd/podman/kill.go index db3300984..56dd170b5 100644 --- a/cmd/podman/kill.go +++ b/cmd/podman/kill.go @@ -31,7 +31,7 @@ var ( Name: "kill", Usage: "Kill one or more running containers with a specific signal", Description: killDescription, - Flags: killFlags, + Flags: sortFlags(killFlags), Action: killCmd, ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", UseShortOptionHandling: true, diff --git a/cmd/podman/load.go b/cmd/podman/load.go index 4dc5c1e3a..f39ee4487 100644 --- a/cmd/podman/load.go +++ b/cmd/podman/load.go @@ -36,7 +36,7 @@ var ( Name: "load", Usage: "Load an image from docker archive", Description: loadDescription, - Flags: loadFlags, + Flags: sortFlags(loadFlags), Action: loadCmd, ArgsUsage: "", OnUsageError: usageErrorHandler, diff --git a/cmd/podman/login.go b/cmd/podman/login.go index afbd180f8..8625828de 100644 --- a/cmd/podman/login.go +++ b/cmd/podman/login.go @@ -43,7 +43,7 @@ var ( Name: "login", Usage: "Login to a container registry", Description: loginDescription, - Flags: loginFlags, + Flags: sortFlags(loginFlags), Action: loginCmd, ArgsUsage: "REGISTRY", OnUsageError: usageErrorHandler, diff --git a/cmd/podman/logout.go b/cmd/podman/logout.go index fa77df2ab..d2a2da790 100644 --- a/cmd/podman/logout.go +++ b/cmd/podman/logout.go @@ -25,7 +25,7 @@ var ( Name: "logout", Usage: "Logout of a container registry", Description: logoutDescription, - Flags: logoutFlags, + Flags: sortFlags(logoutFlags), Action: logoutCmd, ArgsUsage: "REGISTRY", OnUsageError: usageErrorHandler, diff --git a/cmd/podman/logs.go b/cmd/podman/logs.go index 34d062c56..84aca5e61 100644 --- a/cmd/podman/logs.go +++ b/cmd/podman/logs.go @@ -43,7 +43,7 @@ var ( Name: "logs", Usage: "Fetch the logs of a container", Description: logsDescription, - Flags: logsFlags, + Flags: sortFlags(logsFlags), Action: logsCmd, ArgsUsage: "CONTAINER", SkipArgReorder: true, diff --git a/cmd/podman/mount.go b/cmd/podman/mount.go index fbaf2baf1..585f506cd 100644 --- a/cmd/podman/mount.go +++ b/cmd/podman/mount.go @@ -36,7 +36,7 @@ var ( Description: mountDescription, Action: mountCmd, ArgsUsage: "[CONTAINER-NAME-OR-ID [...]]", - Flags: mountFlags, + Flags: sortFlags(mountFlags), OnUsageError: usageErrorHandler, } ) diff --git a/cmd/podman/pod_create.go b/cmd/podman/pod_create.go index 61086f890..c3a45a093 100644 --- a/cmd/podman/pod_create.go +++ b/cmd/podman/pod_create.go @@ -69,7 +69,7 @@ var podCreateCommand = cli.Command{ Name: "create", Usage: "Create a new empty pod", Description: podCreateDescription, - Flags: podCreateFlags, + Flags: sortFlags(podCreateFlags), Action: podCreateCmd, SkipArgReorder: true, UseShortOptionHandling: true, diff --git a/cmd/podman/pod_inspect.go b/cmd/podman/pod_inspect.go index 34208336b..77178b14d 100644 --- a/cmd/podman/pod_inspect.go +++ b/cmd/podman/pod_inspect.go @@ -19,7 +19,7 @@ var ( Name: "inspect", Usage: "displays a pod configuration", Description: podInspectDescription, - Flags: podInspectFlags, + Flags: sortFlags(podInspectFlags), Action: podInspectCmd, UseShortOptionHandling: true, ArgsUsage: "[POD_NAME_OR_ID]", diff --git a/cmd/podman/pod_kill.go b/cmd/podman/pod_kill.go index 11a3a9207..c8029eb46 100644 --- a/cmd/podman/pod_kill.go +++ b/cmd/podman/pod_kill.go @@ -29,7 +29,7 @@ var ( Name: "kill", Usage: "Send the specified signal or SIGKILL to containers in pod", Description: podKillDescription, - Flags: podKillFlags, + Flags: sortFlags(podKillFlags), Action: podKillCmd, ArgsUsage: "[POD_NAME_OR_ID]", UseShortOptionHandling: true, diff --git a/cmd/podman/pod_pause.go b/cmd/podman/pod_pause.go index 9eb80cddf..e8de0debc 100644 --- a/cmd/podman/pod_pause.go +++ b/cmd/podman/pod_pause.go @@ -25,7 +25,7 @@ var ( Name: "pause", Usage: "Pause one or more pods", Description: podPauseDescription, - Flags: podPauseFlags, + Flags: sortFlags(podPauseFlags), Action: podPauseCmd, ArgsUsage: "POD-NAME|POD-ID [POD-NAME|POD-ID ...]", UseShortOptionHandling: true, diff --git a/cmd/podman/pod_ps.go b/cmd/podman/pod_ps.go index 85d4e51e2..2030b9b04 100644 --- a/cmd/podman/pod_ps.go +++ b/cmd/podman/pod_ps.go @@ -161,7 +161,7 @@ var ( Aliases: []string{"ls", "list"}, Usage: "List pods", Description: podPsDescription, - Flags: podPsFlags, + Flags: sortFlags(podPsFlags), Action: podPsCmd, UseShortOptionHandling: true, OnUsageError: usageErrorHandler, diff --git a/cmd/podman/pod_restart.go b/cmd/podman/pod_restart.go index b4a7f9e10..e956b2f70 100644 --- a/cmd/podman/pod_restart.go +++ b/cmd/podman/pod_restart.go @@ -23,7 +23,7 @@ var ( Name: "restart", Usage: "Restart one or more pods", Description: podRestartDescription, - Flags: podRestartFlags, + Flags: sortFlags(podRestartFlags), Action: podRestartCmd, ArgsUsage: "POD-NAME|POD-ID [POD-NAME|POD-ID ...]", UseShortOptionHandling: true, diff --git a/cmd/podman/pod_rm.go b/cmd/podman/pod_rm.go index 09eb9b394..49f2104cf 100644 --- a/cmd/podman/pod_rm.go +++ b/cmd/podman/pod_rm.go @@ -30,7 +30,7 @@ If --force is specified, all containers will be stopped, then removed. Name: "rm", Usage: "Remove one or more pods", Description: podRmDescription, - Flags: podRmFlags, + Flags: sortFlags(podRmFlags), Action: podRmCmd, ArgsUsage: "[POD ...]", UseShortOptionHandling: true, diff --git a/cmd/podman/pod_start.go b/cmd/podman/pod_start.go index 4735028e0..f0a7926c9 100644 --- a/cmd/podman/pod_start.go +++ b/cmd/podman/pod_start.go @@ -27,7 +27,7 @@ var ( Name: "start", Usage: "Start one or more pods", Description: podStartDescription, - Flags: podStartFlags, + Flags: sortFlags(podStartFlags), Action: podStartCmd, ArgsUsage: "POD-NAME [POD-NAME ...]", UseShortOptionHandling: true, diff --git a/cmd/podman/pod_stats.go b/cmd/podman/pod_stats.go index 0a3c6942b..2e29445b4 100644 --- a/cmd/podman/pod_stats.go +++ b/cmd/podman/pod_stats.go @@ -39,7 +39,7 @@ var ( Name: "stats", Usage: "Display percentage of CPU, memory, network I/O, block I/O and PIDs for containers in one or more pods", Description: podStatsDescription, - Flags: podStatsFlags, + Flags: sortFlags(podStatsFlags), Action: podStatsCmd, ArgsUsage: "[POD_NAME_OR_ID]", UseShortOptionHandling: true, diff --git a/cmd/podman/pod_stop.go b/cmd/podman/pod_stop.go index 6dc6a2b2d..14114aa11 100644 --- a/cmd/podman/pod_stop.go +++ b/cmd/podman/pod_stop.go @@ -27,7 +27,7 @@ var ( Name: "stop", Usage: "Stop one or more pods", Description: podStopDescription, - Flags: podStopFlags, + Flags: sortFlags(podStopFlags), Action: podStopCmd, ArgsUsage: "POD-NAME [POD-NAME ...]", OnUsageError: usageErrorHandler, diff --git a/cmd/podman/pod_top.go b/cmd/podman/pod_top.go index fe351c707..1bd1287db 100644 --- a/cmd/podman/pod_top.go +++ b/cmd/podman/pod_top.go @@ -32,7 +32,7 @@ the latest pod. Name: "top", Usage: "Display the running processes of containers in a pod", Description: podTopDescription, - Flags: podTopFlags, + Flags: sortFlags(podTopFlags), Action: podTopCmd, ArgsUsage: "POD-NAME [format descriptors]", SkipArgReorder: true, diff --git a/cmd/podman/pod_unpause.go b/cmd/podman/pod_unpause.go index cdee3cbe7..5256f680c 100644 --- a/cmd/podman/pod_unpause.go +++ b/cmd/podman/pod_unpause.go @@ -25,7 +25,7 @@ var ( Name: "unpause", Usage: "Unpause one or more pods", Description: podUnpauseDescription, - Flags: podUnpauseFlags, + Flags: sortFlags(podUnpauseFlags), Action: podUnpauseCmd, ArgsUsage: "POD-NAME|POD-ID [POD-NAME|POD-ID ...]", UseShortOptionHandling: true, diff --git a/cmd/podman/port.go b/cmd/podman/port.go index b7c88887a..d6497d450 100644 --- a/cmd/podman/port.go +++ b/cmd/podman/port.go @@ -29,7 +29,7 @@ var ( Name: "port", Usage: "List port mappings or a specific mapping for the container", Description: portDescription, - Flags: portFlags, + Flags: sortFlags(portFlags), Action: portCmd, ArgsUsage: "CONTAINER-NAME [mapping]", OnUsageError: usageErrorHandler, diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index e53afe1bf..32b3a0574 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -182,7 +182,7 @@ var ( Name: "ps", Usage: "List containers", Description: psDescription, - Flags: psFlags, + Flags: sortFlags(psFlags), Action: psCmd, ArgsUsage: "", UseShortOptionHandling: true, diff --git a/cmd/podman/pull.go b/cmd/podman/pull.go index 902bd867c..097c88536 100644 --- a/cmd/podman/pull.go +++ b/cmd/podman/pull.go @@ -54,7 +54,7 @@ specified, the image with the 'latest' tag (if it exists) is pulled Name: "pull", Usage: "Pull an image from a registry", Description: pullDescription, - Flags: pullFlags, + Flags: sortFlags(pullFlags), Action: pullCmd, ArgsUsage: "", OnUsageError: usageErrorHandler, diff --git a/cmd/podman/push.go b/cmd/podman/push.go index d9aa2246b..9f2f41835 100644 --- a/cmd/podman/push.go +++ b/cmd/podman/push.go @@ -70,7 +70,7 @@ var ( Name: "push", Usage: "Push an image to a specified destination", Description: pushDescription, - Flags: pushFlags, + Flags: sortFlags(pushFlags), Action: pushCmd, ArgsUsage: "IMAGE DESTINATION", OnUsageError: usageErrorHandler, diff --git a/cmd/podman/refresh.go b/cmd/podman/refresh.go index df16ad5f0..b07376170 100644 --- a/cmd/podman/refresh.go +++ b/cmd/podman/refresh.go @@ -18,7 +18,7 @@ var ( Name: "refresh", Usage: "Refresh container state", Description: refreshDescription, - Flags: refreshFlags, + Flags: sortFlags(refreshFlags), Action: refreshCmd, UseShortOptionHandling: true, OnUsageError: usageErrorHandler, diff --git a/cmd/podman/restart.go b/cmd/podman/restart.go index d2d0c0fd7..7b48ef24e 100644 --- a/cmd/podman/restart.go +++ b/cmd/podman/restart.go @@ -26,7 +26,7 @@ var ( Name: "restart", Usage: "Restart one or more containers", Description: restartDescription, - Flags: restartFlags, + Flags: sortFlags(restartFlags), Action: restartCmd, ArgsUsage: "CONTAINER [CONTAINER ...]", UseShortOptionHandling: true, diff --git a/cmd/podman/restore.go b/cmd/podman/restore.go index 43ef87ca2..623c4936e 100644 --- a/cmd/podman/restore.go +++ b/cmd/podman/restore.go @@ -27,7 +27,7 @@ var ( Name: "restore", Usage: "Restores one or more containers from a checkpoint", Description: restoreDescription, - Flags: restoreFlags, + Flags: sortFlags(restoreFlags), Action: restoreCmd, ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", } diff --git a/cmd/podman/rm.go b/cmd/podman/rm.go index caf7a5f36..f64eca6f4 100644 --- a/cmd/podman/rm.go +++ b/cmd/podman/rm.go @@ -35,7 +35,7 @@ Running containers will not be removed without the -f option. Name: "rm", Usage: "Remove one or more containers", Description: rmDescription, - Flags: rmFlags, + Flags: sortFlags(rmFlags), Action: rmCmd, ArgsUsage: "", UseShortOptionHandling: true, diff --git a/cmd/podman/rmi.go b/cmd/podman/rmi.go index fdeab6b80..c0a0d69df 100644 --- a/cmd/podman/rmi.go +++ b/cmd/podman/rmi.go @@ -29,7 +29,7 @@ var ( Description: rmiDescription, Action: rmiCmd, ArgsUsage: "IMAGE-NAME-OR-ID [...]", - Flags: rmiFlags, + Flags: sortFlags(rmiFlags), UseShortOptionHandling: true, OnUsageError: usageErrorHandler, } diff --git a/cmd/podman/run.go b/cmd/podman/run.go index fbad4237d..e4b25eaf4 100644 --- a/cmd/podman/run.go +++ b/cmd/podman/run.go @@ -27,7 +27,7 @@ var runCommand = cli.Command{ Name: "run", Usage: "Run a command in a new container", Description: runDescription, - Flags: runFlags, + Flags: sortFlags(runFlags), Action: runCmd, ArgsUsage: "IMAGE [COMMAND [ARG...]]", HideHelp: true, diff --git a/cmd/podman/run_test.go b/cmd/podman/run_test.go index 0a79f6ec3..079e570aa 100644 --- a/cmd/podman/run_test.go +++ b/cmd/podman/run_test.go @@ -18,7 +18,7 @@ var ( CLI *cli.Context testCommand = cli.Command{ Name: "test", - Flags: createFlags, + Flags: sortFlags(createFlags), Action: testCmd, HideHelp: true, } diff --git a/cmd/podman/runlabel.go b/cmd/podman/runlabel.go index d514a79fc..2d464b949 100644 --- a/cmd/podman/runlabel.go +++ b/cmd/podman/runlabel.go @@ -79,7 +79,7 @@ Executes a command as described by a container image label. Name: "runlabel", Usage: "Execute the command described by an image label", Description: runlabelDescription, - Flags: runlabelFlags, + Flags: sortFlags(runlabelFlags), Action: runlabelCmd, ArgsUsage: "", SkipArgReorder: true, diff --git a/cmd/podman/save.go b/cmd/podman/save.go index a1e980f34..7edc42e0d 100644 --- a/cmd/podman/save.go +++ b/cmd/podman/save.go @@ -53,7 +53,7 @@ var ( Name: "save", Usage: "Save image to an archive", Description: saveDescription, - Flags: saveFlags, + Flags: sortFlags(saveFlags), Action: saveCmd, ArgsUsage: "", SkipArgReorder: true, diff --git a/cmd/podman/search.go b/cmd/podman/search.go index f64b822fc..49b1b7f7b 100644 --- a/cmd/podman/search.go +++ b/cmd/podman/search.go @@ -55,7 +55,7 @@ var ( Name: "search", Usage: "Search registry for image", Description: searchDescription, - Flags: searchFlags, + Flags: sortFlags(searchFlags), Action: searchCmd, ArgsUsage: "TERM", OnUsageError: usageErrorHandler, diff --git a/cmd/podman/start.go b/cmd/podman/start.go index a34f6df5d..8cf85405e 100644 --- a/cmd/podman/start.go +++ b/cmd/podman/start.go @@ -41,7 +41,7 @@ var ( Name: "start", Usage: "Start one or more containers", Description: startDescription, - Flags: startFlags, + Flags: sortFlags(startFlags), Action: startCmd, ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", UseShortOptionHandling: true, diff --git a/cmd/podman/stats.go b/cmd/podman/stats.go index 0e1d2a9c6..dea351e88 100644 --- a/cmd/podman/stats.go +++ b/cmd/podman/stats.go @@ -52,7 +52,7 @@ var ( Name: "stats", Usage: "Display percentage of CPU, memory, network I/O, block I/O and PIDs for one or more containers", Description: statsDescription, - Flags: statsFlags, + Flags: sortFlags(statsFlags), Action: statsCmd, ArgsUsage: "", OnUsageError: usageErrorHandler, diff --git a/cmd/podman/stop.go b/cmd/podman/stop.go index d2fa87730..ff0b36bf1 100644 --- a/cmd/podman/stop.go +++ b/cmd/podman/stop.go @@ -3,6 +3,7 @@ package main import ( "fmt" "os" + rt "runtime" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" @@ -35,7 +36,7 @@ var ( Name: "stop", Usage: "Stop one or more containers", Description: stopDescription, - Flags: stopFlags, + Flags: sortFlags(stopFlags), Action: stopCmd, ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", OnUsageError: usageErrorHandler, @@ -98,21 +99,33 @@ func stopCmd(c *cli.Context) error { } } + var stopFuncs []workerInput for _, ctr := range containers { + con := ctr var stopTimeout uint if c.IsSet("timeout") { stopTimeout = c.Uint("timeout") } else { stopTimeout = ctr.StopTimeout() } - if err := ctr.StopWithTimeout(stopTimeout); err != nil && err != libpod.ErrCtrStopped { - if lastError != nil { - fmt.Fprintln(os.Stderr, lastError) - } - lastError = errors.Wrapf(err, "failed to stop container %v", ctr.ID()) - } else { - fmt.Println(ctr.ID()) + f := func() error { + return con.StopWithTimeout(stopTimeout) + } + stopFuncs = append(stopFuncs, workerInput{ + containerID: con.ID(), + parallelFunc: f, + }) + } + + stopErrors := parallelExecuteWorkerPool(rt.NumCPU()*3, stopFuncs) + + for cid, result := range stopErrors { + if result != nil && result != libpod.ErrCtrStopped { + fmt.Println(result.Error()) + lastError = result + continue } + fmt.Println(cid) } return lastError } diff --git a/cmd/podman/top.go b/cmd/podman/top.go index 9b5c3afae..3012265ea 100644 --- a/cmd/podman/top.go +++ b/cmd/podman/top.go @@ -42,7 +42,7 @@ the latest container. Name: "top", Usage: "Display the running processes of a container", Description: topDescription, - Flags: topFlags, + Flags: sortFlags(topFlags), Action: topCmd, ArgsUsage: "CONTAINER-NAME [format descriptors]", SkipArgReorder: true, diff --git a/cmd/podman/umount.go b/cmd/podman/umount.go index b6837fb5b..24f0f178b 100644 --- a/cmd/podman/umount.go +++ b/cmd/podman/umount.go @@ -35,7 +35,7 @@ An unmount can be forced with the --force flag. Aliases: []string{"unmount"}, Usage: "Unmounts working container's root filesystem", Description: description, - Flags: umountFlags, + Flags: sortFlags(umountFlags), Action: umountCmd, ArgsUsage: "CONTAINER-NAME-OR-ID", OnUsageError: usageErrorHandler, diff --git a/cmd/podman/utils.go b/cmd/podman/utils.go index 89ec48dbe..1b767532e 100644 --- a/cmd/podman/utils.go +++ b/cmd/podman/utils.go @@ -5,6 +5,7 @@ import ( "fmt" "os" gosignal "os/signal" + "sync" "github.com/containers/libpod/libpod" "github.com/docker/docker/pkg/signal" @@ -215,3 +216,50 @@ func getPodsFromContext(c *cli.Context, r *libpod.Runtime) ([]*libpod.Pod, error } return pods, lastError } + +type pFunc func() error + +type workerInput struct { + containerID string + parallelFunc pFunc +} + +// worker is a "threaded" worker that takes jobs from the channel "queue" +func worker(wg *sync.WaitGroup, jobs <-chan workerInput, results map[string]error) { + for j := range jobs { + err := j.parallelFunc() + results[j.containerID] = err + wg.Done() + } +} + +// parallelExecuteWorkerPool takes container jobs and performs them in parallel. The worker +// int is determines how many workers/threads should be premade. +func parallelExecuteWorkerPool(workers int, functions []workerInput) map[string]error { + var ( + wg sync.WaitGroup + ) + results := make(map[string]error) + paraJobs := make(chan workerInput, len(functions)) + + // If we have more workers than functions, match up the number of workers and functions + if workers > len(functions) { + workers = len(functions) + } + + // Create the workers + for w := 1; w <= workers; w++ { + go worker(&wg, paraJobs, results) + } + + // Add jobs to the workers + for _, j := range functions { + j := j + wg.Add(1) + paraJobs <- j + } + + close(paraJobs) + wg.Wait() + return results +} diff --git a/cmd/podman/varlink.go b/cmd/podman/varlink.go index 2f92d9adb..a7c195041 100644 --- a/cmd/podman/varlink.go +++ b/cmd/podman/varlink.go @@ -32,7 +32,7 @@ var ( Name: "varlink", Usage: "Run varlink interface", Description: varlinkDescription, - Flags: varlinkFlags, + Flags: sortFlags(varlinkFlags), Action: varlinkCmd, ArgsUsage: "VARLINK_URI", OnUsageError: usageErrorHandler, diff --git a/cmd/podman/wait.go b/cmd/podman/wait.go index 07db20eee..35ad7a662 100644 --- a/cmd/podman/wait.go +++ b/cmd/podman/wait.go @@ -28,7 +28,7 @@ var ( Name: "wait", Usage: "Block on one or more containers", Description: waitDescription, - Flags: waitFlags, + Flags: sortFlags(waitFlags), Action: waitCmd, ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", OnUsageError: usageErrorHandler, diff --git a/completions/bash/podman b/completions/bash/podman index 604a25f5d..5cfed348f 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -1512,6 +1512,7 @@ _podman_container_run() { --hostname -h --image-volume --init-path + --ip --ipc --kernel-memory --label-file diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md index c42671b76..509d8820f 100644 --- a/docs/podman-create.1.md +++ b/docs/podman-create.1.md @@ -286,7 +286,9 @@ Not implemented **--ip**="" -Not implemented +Specify a static IP address for the container, for example '10.88.64.128'. +Can only be used if no additional CNI networks to join were specified via '--network=<network-name>', and if the container is not joining another container's network namespace via '--network=container:<name|id>'. +The address must be within the default CNI network's pool (default 10.88.0.0/16). **--ipc**="" @@ -416,7 +418,7 @@ to the container with **--name** then the daemon will also generate a random string name. The name is useful any place you need to identify a container. This works for both background and foreground containers. -**--network**="*bridge*" +**--net**, **--network**="*bridge*" Set the Network mode for the container 'bridge': create a network stack on the default bridge diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md index fccebb7f7..c303492e7 100644 --- a/docs/podman-run.1.md +++ b/docs/podman-run.1.md @@ -297,7 +297,9 @@ Not implemented **--ip**="" -Not implemented +Specify a static IP address for the container, for example '10.88.64.128'. +Can only be used if no additional CNI networks to join were specified via '--network=<network-name>', and if the container is not joining another container's network namespace via '--network=container:<name|id>'. +The address must be within the default CNI network's pool (default 10.88.0.0/16). **--ipc**="" @@ -401,7 +403,7 @@ to the container with **--name** then the daemon will also generate a random string name. The name is useful any place you need to identify a container. This works for both background and foreground containers. -**--network**="*bridge*" +**--net**, **--network**="*bridge*" Set the Network mode for the container: - `bridge`: create a network stack on the default bridge diff --git a/libpod/boltdb_state_linux.go b/libpod/boltdb_state_linux.go index fce3a1b1e..d91f311e5 100644 --- a/libpod/boltdb_state_linux.go +++ b/libpod/boltdb_state_linux.go @@ -25,7 +25,7 @@ func replaceNetNS(netNSPath string, ctr *Container, newState *containerState) er if err == nil { newState.NetNS = ns } else { - logrus.Errorf("error joining network namespace for container %s", ctr.ID()) + logrus.Errorf("error joining network namespace for container %s: %v", ctr.ID(), err) ctr.valid = false } } diff --git a/libpod/container.go b/libpod/container.go index 55a0f3a2c..5997c0b66 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -269,9 +269,13 @@ type ContainerConfig struct { // Network Config // CreateNetNS indicates that libpod should create and configure a new - // network namespace for the container - // This cannot be set if NetNsCtr is also set + // network namespace for the container. + // This cannot be set if NetNsCtr is also set. CreateNetNS bool `json:"createNetNS"` + // StaticIP is a static IP to request for the container. + // This cannot be set unless CreateNetNS is set. + // If not set, the container will be dynamically assigned an IP by CNI. + StaticIP net.IP `json:"staticIP"` // PortMappings are the ports forwarded to the container's network // namespace // These are not used unless CreateNetNS is true diff --git a/libpod/container_easyjson.go b/libpod/container_easyjson.go index 916118aec..f78366065 100644 --- a/libpod/container_easyjson.go +++ b/libpod/container_easyjson.go @@ -1383,6 +1383,10 @@ func easyjson1dbef17bDecodeGithubComContainersLibpodLibpod2(in *jlexer.Lexer, ou } case "createNetNS": out.CreateNetNS = bool(in.Bool()) + case "staticIP": + if data := in.UnsafeBytes(); in.Ok() { + in.AddError((out.StaticIP).UnmarshalText(data)) + } case "portMappings": if in.IsNull() { in.Skip() @@ -2005,6 +2009,16 @@ func easyjson1dbef17bEncodeGithubComContainersLibpodLibpod2(out *jwriter.Writer, } out.Bool(bool(in.CreateNetNS)) } + { + const prefix string = ",\"staticIP\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.RawText((in.StaticIP).MarshalText()) + } if len(in.PortMappings) != 0 { const prefix string = ",\"portMappings\":" if first { diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 17e79aa62..acb4e2a90 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -5,6 +5,7 @@ package libpod import ( "crypto/rand" "fmt" + "net" "os" "os/exec" "path/filepath" @@ -25,8 +26,8 @@ import ( ) // Get an OCICNI network config -func getPodNetwork(id, name, nsPath string, networks []string, ports []ocicni.PortMapping) ocicni.PodNetwork { - return ocicni.PodNetwork{ +func (r *Runtime) getPodNetwork(id, name, nsPath string, networks []string, ports []ocicni.PortMapping, staticIP net.IP) ocicni.PodNetwork { + network := ocicni.PodNetwork{ Name: name, Namespace: name, // TODO is there something else we should put here? We don't know about Kube namespaces ID: id, @@ -34,11 +35,21 @@ func getPodNetwork(id, name, nsPath string, networks []string, ports []ocicni.Po PortMappings: ports, Networks: networks, } + + if staticIP != nil { + defaultNetwork := r.netPlugin.GetDefaultNetworkName() + + network.Networks = []string{defaultNetwork} + network.NetworkConfig = make(map[string]ocicni.NetworkConfig) + network.NetworkConfig[defaultNetwork] = ocicni.NetworkConfig{IP: staticIP.String()} + } + + return network } // Create and configure a new network namespace for a container func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (err error) { - podNetwork := getPodNetwork(ctr.ID(), ctr.Name(), ctrNS.Path(), ctr.config.Networks, ctr.config.PortMappings) + podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctrNS.Path(), ctr.config.Networks, ctr.config.PortMappings, ctr.config.StaticIP) results, err := r.netPlugin.SetUpPod(podNetwork) if err != nil { @@ -216,7 +227,7 @@ func (r *Runtime) teardownNetNS(ctr *Container) error { logrus.Debugf("Tearing down network namespace at %s for container %s", ctr.state.NetNS.Path(), ctr.ID()) - podNetwork := getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.Networks, ctr.config.PortMappings) + podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.Networks, ctr.config.PortMappings, ctr.config.StaticIP) // The network may have already been torn down, so don't fail here, just log if err := r.netPlugin.TearDownPod(podNetwork); err != nil { diff --git a/libpod/options.go b/libpod/options.go index 977f3f4c2..9f966cead 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -828,6 +828,31 @@ func WithNetNS(portMappings []ocicni.PortMapping, postConfigureNetNS bool, netwo } } +// WithStaticIP indicates that the container should request a static IP from +// the CNI plugins. +// It cannot be set unless WithNetNS has already been passed. +// Further, it cannot be set if additional CNI networks to join have been +// specified. +func WithStaticIP(ip net.IP) CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return ErrCtrFinalized + } + + if !ctr.config.CreateNetNS { + return errors.Wrapf(ErrInvalidArg, "cannot set a static IP if the container is not creating a network namespace") + } + + if len(ctr.config.Networks) != 0 { + return errors.Wrapf(ErrInvalidArg, "cannot set a static IP if joining additional CNI networks") + } + + ctr.config.StaticIP = ip + + return nil + } +} + // WithLogPath sets the path to the log file. func WithLogPath(path string) CtrCreateOption { return func(ctr *Container) error { diff --git a/libpod/pod_easyjson.go b/libpod/pod_easyjson.go index 2891e51f2..6c1c939f3 100644 --- a/libpod/pod_easyjson.go +++ b/libpod/pod_easyjson.go @@ -1,3 +1,5 @@ +// +build seccomp ostree selinux varlink exclude_graphdriver_devicemapper + // Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. package libpod diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c index d78d95453..9eb16c1a5 100644 --- a/pkg/rootless/rootless_linux.c +++ b/pkg/rootless/rootless_linux.c @@ -11,6 +11,22 @@ #include <signal.h> #include <fcntl.h> #include <sys/wait.h> +#include <string.h> + +static const char *_max_user_namespaces = "/proc/sys/user/max_user_namespaces"; +static const char *_unprivileged_user_namespaces = "/proc/sys/kernel/unprivileged_userns_clone"; + +static int +syscall_setresuid (uid_t ruid, uid_t euid, uid_t suid) +{ + return (int) syscall (__NR_setresuid, ruid, euid, suid); +} + +static int +syscall_setresgid (gid_t rgid, gid_t egid, gid_t sgid) +{ + return (int) syscall (__NR_setresgid, rgid, egid, sgid); +} static int syscall_clone (unsigned long flags, void *child_stack) @@ -94,9 +110,14 @@ reexec_userns_join (int userns) argv = get_cmd_line_args (ppid); if (argv == NULL) - _exit (EXIT_FAILURE); + { + fprintf (stderr, "cannot read argv: %s\n", strerror (errno)); + _exit (EXIT_FAILURE); + } pid = fork (); + if (pid < 0) + fprintf (stderr, "cannot fork: %s\n", strerror (errno)); if (pid) return pid; @@ -104,18 +125,48 @@ reexec_userns_join (int userns) setenv ("_LIBPOD_ROOTLESS_UID", uid, 1); if (setns (userns, 0) < 0) - _exit (EXIT_FAILURE); + { + fprintf (stderr, "cannot setns: %s\n", strerror (errno)); + _exit (EXIT_FAILURE); + } close (userns); - if (setresgid (0, 0, 0) < 0 || - setresuid (0, 0, 0) < 0) - _exit (EXIT_FAILURE); + if (syscall_setresgid (0, 0, 0) < 0) + { + fprintf (stderr, "cannot setresgid: %s\n", strerror (errno)); + _exit (EXIT_FAILURE); + } + + if (syscall_setresuid (0, 0, 0) < 0) + { + fprintf (stderr, "cannot setresuid: %s\n", strerror (errno)); + _exit (EXIT_FAILURE); + } execvp (argv[0], argv); _exit (EXIT_FAILURE); } +static void +check_proc_sys_userns_file (const char *path) +{ + FILE *fp; + fp = fopen (path, "r"); + if (fp) + { + char buf[32]; + size_t n_read = fread (buf, 1, sizeof(buf) - 1, fp); + if (n_read > 0) + { + buf[n_read] = '\0'; + if (strtol (buf, NULL, 10) == 0) + fprintf (stderr, "user namespaces are not enabled in %s\n", path); + } + fclose (fp); + } +} + int reexec_in_user_namespace (int ready) { @@ -129,12 +180,22 @@ reexec_in_user_namespace (int ready) sprintf (uid, "%d", geteuid ()); pid = syscall_clone (CLONE_NEWUSER|CLONE_NEWNS|SIGCHLD, NULL); + if (pid < 0) + { + FILE *fp; + fprintf (stderr, "cannot clone: %s\n", strerror (errno)); + check_proc_sys_userns_file (_max_user_namespaces); + check_proc_sys_userns_file (_unprivileged_user_namespaces); + } if (pid) return pid; argv = get_cmd_line_args (ppid); if (argv == NULL) - _exit (EXIT_FAILURE); + { + fprintf (stderr, "cannot read argv: %s\n", strerror (errno)); + _exit (EXIT_FAILURE); + } setenv ("_LIBPOD_USERNS_CONFIGURED", "init", 1); setenv ("_LIBPOD_ROOTLESS_UID", uid, 1); @@ -143,12 +204,23 @@ reexec_in_user_namespace (int ready) ret = read (ready, &b, 1) < 0; while (ret < 0 && errno == EINTR); if (ret < 0) - _exit (EXIT_FAILURE); + { + fprintf (stderr, "cannot read from sync pipe: %s\n", strerror (errno)); + _exit (EXIT_FAILURE); + } close (ready); - if (setresgid (0, 0, 0) < 0 || - setresuid (0, 0, 0) < 0) - _exit (EXIT_FAILURE); + if (syscall_setresgid (0, 0, 0) < 0) + { + fprintf (stderr, "cannot setresgid: %s\n", strerror (errno)); + _exit (EXIT_FAILURE); + } + + if (syscall_setresuid (0, 0, 0) < 0) + { + fprintf (stderr, "cannot setresuid: %s\n", strerror (errno)); + _exit (EXIT_FAILURE); + } execvp (argv[0], argv); diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go index 9e8f6253b..d34b21189 100644 --- a/pkg/spec/createconfig.go +++ b/pkg/spec/createconfig.go @@ -2,6 +2,7 @@ package createconfig import ( "encoding/json" + "net" "os" "strconv" "strings" @@ -315,9 +316,6 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib var pod *libpod.Pod var err error - // Uncomment after talking to mheon about unimplemented funcs - // options = append(options, libpod.WithLabels(c.labels)) - if c.Interactive { options = append(options, libpod.WithStdin()) } @@ -446,6 +444,15 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib if logPath != "" { options = append(options, libpod.WithLogPath(logPath)) } + if c.IPAddress != "" { + ip := net.ParseIP(c.IPAddress) + if ip == nil { + return nil, errors.Wrapf(libpod.ErrInvalidArg, "cannot parse %s as IP address", c.IPAddress) + } else if ip.To4() == nil { + return nil, errors.Wrapf(libpod.ErrInvalidArg, "%s is not an IPv4 address", c.IPAddress) + } + options = append(options, libpod.WithStaticIP(ip)) + } options = append(options, libpod.WithPrivileged(c.Privileged)) diff --git a/test/e2e/run_staticip_test.go b/test/e2e/run_staticip_test.go new file mode 100644 index 000000000..b69d15cee --- /dev/null +++ b/test/e2e/run_staticip_test.go @@ -0,0 +1,58 @@ +package integration + +import ( + "fmt" + "os" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Podman run with --ip flag", func() { + var ( + tempdir string + err error + podmanTest PodmanTest + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanCreate(tempdir) + podmanTest.RestoreAllArtifacts() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds()) + GinkgoWriter.Write([]byte(timedResult)) + }) + + It("Podman run --ip with garbage address", func() { + result := podmanTest.Podman([]string{"run", "-ti", "--ip", "114232346", ALPINE, "ls"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).ToNot(Equal(0)) + }) + + It("Podman run --ip with v6 address", func() { + result := podmanTest.Podman([]string{"run", "-ti", "--ip", "2001:db8:bad:beef::1", ALPINE, "ls"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).ToNot(Equal(0)) + }) + + It("Podman run --ip with non-allocatable IP", func() { + result := podmanTest.Podman([]string{"run", "-ti", "--ip", "203.0.113.124", ALPINE, "ls"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).ToNot(Equal(0)) + }) + + It("Podman run with specified static IP has correct IP", func() { + result := podmanTest.Podman([]string{"run", "-ti", "--ip", "10.88.64.128", ALPINE, "ip", "addr"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(ContainSubstring("10.88.64.128/16")) + }) +}) diff --git a/vendor.conf b/vendor.conf index d65014992..be89c418e 100644 --- a/vendor.conf +++ b/vendor.conf @@ -14,7 +14,7 @@ github.com/containers/image 918dbb93e6e099b196b498c38d079f6bb924d0c8 github.com/containers/storage 41294c85d97bef688e18f710402895dbecde3308 github.com/containers/psgo 5dde6da0bc8831b35243a847625bcf18183bd1ee github.com/coreos/go-systemd v14 -github.com/cri-o/ocicni master +github.com/cri-o/ocicni 2d2983e40c242322a56c22a903785e7f83eb378c github.com/cyphar/filepath-securejoin v0.2.1 github.com/davecgh/go-spew v1.1.0 github.com/docker/distribution 7a8efe719e55bbfaff7bc5718cdf0ed51ca821df @@ -75,6 +75,7 @@ golang.org/x/net c427ad74c6d7a814201695e9ffde0c5d400a7674 golang.org/x/sys master golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756 golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631 +golang.org/x/sync master google.golang.org/grpc v1.0.4 https://github.com/grpc/grpc-go gopkg.in/cheggaaa/pb.v1 v1.0.7 gopkg.in/inf.v0 v0.9.0 diff --git a/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go b/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go index 33a3ae063..dfc216389 100644 --- a/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go +++ b/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go @@ -3,6 +3,7 @@ package ocicni import ( "errors" "fmt" + "net" "os" "path" "sort" @@ -351,14 +352,14 @@ func (plugin *cniNetworkPlugin) getNetwork(name string) (*cniNetwork, error) { return net, nil } -func (plugin *cniNetworkPlugin) getDefaultNetworkName() string { +func (plugin *cniNetworkPlugin) GetDefaultNetworkName() string { plugin.RLock() defer plugin.RUnlock() return plugin.defaultNetName } func (plugin *cniNetworkPlugin) getDefaultNetwork() *cniNetwork { - defaultNetName := plugin.getDefaultNetworkName() + defaultNetName := plugin.GetDefaultNetworkName() if defaultNetName == "" { return nil } @@ -383,7 +384,7 @@ func (plugin *cniNetworkPlugin) Name() string { func (plugin *cniNetworkPlugin) forEachNetwork(podNetwork *PodNetwork, forEachFunc func(*cniNetwork, string, *PodNetwork) error) error { networks := podNetwork.Networks if len(networks) == 0 { - networks = append(networks, plugin.getDefaultNetworkName()) + networks = append(networks, plugin.GetDefaultNetworkName()) } for i, netName := range networks { // Interface names start at "eth0" and count up for each network @@ -408,7 +409,7 @@ func (plugin *cniNetworkPlugin) SetUpPod(podNetwork PodNetwork) ([]cnitypes.Resu plugin.podLock(podNetwork).Lock() defer plugin.podUnlock(podNetwork) - _, err := plugin.loNetwork.addToNetwork(plugin.cacheDir, &podNetwork, "lo") + _, err := plugin.loNetwork.addToNetwork(plugin.cacheDir, &podNetwork, "lo", "") if err != nil { logrus.Errorf("Error while adding to cni lo network: %s", err) return nil, err @@ -416,7 +417,12 @@ func (plugin *cniNetworkPlugin) SetUpPod(podNetwork PodNetwork) ([]cnitypes.Resu results := make([]cnitypes.Result, 0) if err := plugin.forEachNetwork(&podNetwork, func(network *cniNetwork, ifName string, podNetwork *PodNetwork) error { - result, err := network.addToNetwork(plugin.cacheDir, podNetwork, ifName) + ip := "" + if conf, ok := podNetwork.NetworkConfig[network.name]; ok { + ip = conf.IP + } + + result, err := network.addToNetwork(plugin.cacheDir, podNetwork, ifName, ip) if err != nil { logrus.Errorf("Error while adding pod to CNI network %q: %s", network.name, err) return err @@ -439,7 +445,12 @@ func (plugin *cniNetworkPlugin) TearDownPod(podNetwork PodNetwork) error { defer plugin.podUnlock(podNetwork) return plugin.forEachNetwork(&podNetwork, func(network *cniNetwork, ifName string, podNetwork *PodNetwork) error { - if err := network.deleteFromNetwork(plugin.cacheDir, podNetwork, ifName); err != nil { + ip := "" + if conf, ok := podNetwork.NetworkConfig[network.name]; ok { + ip = conf.IP + } + + if err := network.deleteFromNetwork(plugin.cacheDir, podNetwork, ifName, ip); err != nil { logrus.Errorf("Error while removing pod from CNI network %q: %s", network.name, err) return err } @@ -491,8 +502,8 @@ func (plugin *cniNetworkPlugin) GetPodNetworkStatus(podNetwork PodNetwork) ([]cn return results, nil } -func (network *cniNetwork) addToNetwork(cacheDir string, podNetwork *PodNetwork, ifName string) (cnitypes.Result, error) { - rt, err := buildCNIRuntimeConf(cacheDir, podNetwork, ifName) +func (network *cniNetwork) addToNetwork(cacheDir string, podNetwork *PodNetwork, ifName, ip string) (cnitypes.Result, error) { + rt, err := buildCNIRuntimeConf(cacheDir, podNetwork, ifName, ip) if err != nil { logrus.Errorf("Error adding network: %v", err) return nil, err @@ -509,8 +520,8 @@ func (network *cniNetwork) addToNetwork(cacheDir string, podNetwork *PodNetwork, return res, nil } -func (network *cniNetwork) deleteFromNetwork(cacheDir string, podNetwork *PodNetwork, ifName string) error { - rt, err := buildCNIRuntimeConf(cacheDir, podNetwork, ifName) +func (network *cniNetwork) deleteFromNetwork(cacheDir string, podNetwork *PodNetwork, ifName, ip string) error { + rt, err := buildCNIRuntimeConf(cacheDir, podNetwork, ifName, ip) if err != nil { logrus.Errorf("Error deleting network: %v", err) return err @@ -526,7 +537,7 @@ func (network *cniNetwork) deleteFromNetwork(cacheDir string, podNetwork *PodNet return nil } -func buildCNIRuntimeConf(cacheDir string, podNetwork *PodNetwork, ifName string) (*libcni.RuntimeConf, error) { +func buildCNIRuntimeConf(cacheDir string, podNetwork *PodNetwork, ifName, ip string) (*libcni.RuntimeConf, error) { logrus.Infof("Got pod network %+v", podNetwork) rt := &libcni.RuntimeConf{ @@ -542,6 +553,14 @@ func buildCNIRuntimeConf(cacheDir string, podNetwork *PodNetwork, ifName string) }, } + // Add requested static IP to CNI_ARGS + if ip != "" { + if tstIP := net.ParseIP(ip); tstIP == nil { + return nil, fmt.Errorf("unable to parse IP address %q", ip) + } + rt.Args = append(rt.Args, [2]string{"IP", ip}) + } + if len(podNetwork.PortMappings) == 0 { return rt, nil } diff --git a/vendor/github.com/cri-o/ocicni/pkg/ocicni/types.go b/vendor/github.com/cri-o/ocicni/pkg/ocicni/types.go index 8ca61657a..d76094292 100644 --- a/vendor/github.com/cri-o/ocicni/pkg/ocicni/types.go +++ b/vendor/github.com/cri-o/ocicni/pkg/ocicni/types.go @@ -24,6 +24,14 @@ type PortMapping struct { HostIP string `json:"hostIP"` } +// NetworkConfig is additional configuration for a single CNI network. +type NetworkConfig struct { + // IP is a static IP to be specified in the network. Can only be used + // with the hostlocal IP allocator. If left unset, an IP will be + // dynamically allocated. + IP string +} + // PodNetwork configures the network of a pod sandbox. type PodNetwork struct { // Name is the name of the sandbox. @@ -40,6 +48,11 @@ type PodNetwork struct { // Networks is a list of CNI network names to attach to the sandbox // Leave this list empty to attach the default network to the sandbox Networks []string + + // NetworkConfig is configuration specific to a single CNI network. + // It is optional, and can be omitted for some or all specified networks + // without issue. + NetworkConfig map[string]NetworkConfig } // CNIPlugin is the interface that needs to be implemented by a plugin @@ -48,6 +61,10 @@ type CNIPlugin interface { // for a plugin by name, e.g. Name() string + // GetDefaultNetworkName returns the name of the plugin's default + // network. + GetDefaultNetworkName() string + // SetUpPod is the method called after the sandbox container of // the pod has been created but before the other containers of the // pod are launched. |