diff options
-rw-r--r-- | cmd/podman/containers/start.go | 20 | ||||
-rw-r--r-- | docs/source/markdown/podman-start.1.md | 25 | ||||
-rw-r--r-- | libpod/define/container.go | 9 | ||||
-rw-r--r-- | pkg/domain/entities/containers.go | 1 | ||||
-rw-r--r-- | pkg/domain/filters/containers.go | 27 | ||||
-rw-r--r-- | pkg/domain/infra/abi/containers.go | 28 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/containers.go | 25 | ||||
-rw-r--r-- | test/system/045-start.bats | 17 |
8 files changed, 148 insertions, 4 deletions
diff --git a/cmd/podman/containers/start.go b/cmd/podman/containers/start.go index 8d62dc12f..dcd1eca82 100644 --- a/cmd/podman/containers/start.go +++ b/cmd/podman/containers/start.go @@ -3,6 +3,7 @@ package containers import ( "fmt" "os" + "strings" "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" @@ -42,7 +43,9 @@ var ( ) var ( - startOptions entities.ContainerStartOptions + startOptions = entities.ContainerStartOptions{ + Filters: make(map[string][]string), + } ) func startFlags(cmd *cobra.Command) { @@ -56,6 +59,8 @@ func startFlags(cmd *cobra.Command) { flags.BoolVarP(&startOptions.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached") flags.BoolVar(&startOptions.SigProxy, "sig-proxy", false, "Proxy received signals to the process (default true if attaching, false otherwise)") + flags.StringSliceVarP(&filters, "filter", "f", []string{}, "Filter output based on conditions given") + _ = cmd.RegisterFlagCompletionFunc("filter", common.AutocompletePsFilters) flags.BoolVar(&startOptions.All, "all", false, "Start all containers regardless of their state or configuration") @@ -116,7 +121,18 @@ func start(cmd *cobra.Command, args []string) error { startOptions.Stdout = os.Stdout } - responses, err := registry.ContainerEngine().ContainerStart(registry.GetContext(), args, startOptions) + var containers []string = args + if len(filters) > 0 { + for _, f := range filters { + split := strings.SplitN(f, "=", 2) + if len(split) == 1 { + return errors.Errorf("invalid filter %q", f) + } + startOptions.Filters[split[0]] = append(startOptions.Filters[split[0]], split[1]) + } + } + + responses, err := registry.ContainerEngine().ContainerStart(registry.GetContext(), containers, startOptions) if err != nil { return err } diff --git a/docs/source/markdown/podman-start.1.md b/docs/source/markdown/podman-start.1.md index 626e6e368..7085f3f47 100644 --- a/docs/source/markdown/podman-start.1.md +++ b/docs/source/markdown/podman-start.1.md @@ -42,6 +42,31 @@ Proxy received signals to the process (non-TTY mode only). SIGCHLD, SIGSTOP, and Start all the containers created by Podman, default is only running containers. +#### **\-\-filter**, **-f** + +Filter what containers are going to be started from the given arguments. +Multiple filters can be given with multiple uses of the --filter flag. +Filters with the same key work inclusive with the only exception being +`label` which is exclusive. Filters with different keys always work exclusive. + +Valid filters are listed below: + +| **Filter** | **Description** | +| --------------- | -------------------------------------------------------------------------------- | +| id | [ID] Container's ID (accepts regex) | +| name | [Name] Container's name (accepts regex) | +| label | [Key] or [Key=Value] Label assigned to a container | +| exited | [Int] Container's exit code | +| status | [Status] Container's status: 'created', 'exited', 'paused', 'running', 'unknown' | +| ancestor | [ImageName] Image or descendant used to create container | +| before | [ID] or [Name] Containers created before this container | +| since | [ID] or [Name] Containers created since this container | +| volume | [VolumeName] or [MountpointDestination] Volume mounted in container | +| health | [Status] healthy or unhealthy | +| pod | [Pod] name or full or partial ID of pod | +| network | [Network] name or full ID of network | + + ## EXAMPLE podman start mywebserver diff --git a/libpod/define/container.go b/libpod/define/container.go index 5a2ff026f..f3125afa9 100644 --- a/libpod/define/container.go +++ b/libpod/define/container.go @@ -17,3 +17,12 @@ const ( // handling of system restart, which Podman does not yet support. RestartPolicyUnlessStopped = "unless-stopped" ) + +// RestartPolicyMap maps between restart-policy valid values to restart policy types +var RestartPolicyMap = map[string]string{ + "none": RestartPolicyNone, + RestartPolicyNo: RestartPolicyNo, + RestartPolicyAlways: RestartPolicyAlways, + RestartPolicyOnFailure: RestartPolicyOnFailure, + RestartPolicyUnlessStopped: RestartPolicyUnlessStopped, +} diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 4707ced85..eacc14d50 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -265,6 +265,7 @@ type ContainerExistsOptions struct { // ContainerStartOptions describes the val from the // CLI needed to start a container type ContainerStartOptions struct { + Filters map[string][]string All bool Attach bool DetachKeys string diff --git a/pkg/domain/filters/containers.go b/pkg/domain/filters/containers.go index 9ac72e415..965a12468 100644 --- a/pkg/domain/filters/containers.go +++ b/pkg/domain/filters/containers.go @@ -1,6 +1,7 @@ package filters import ( + "fmt" "strconv" "strings" "time" @@ -226,6 +227,32 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo } return false }, nil + case "restart-policy": + invalidPolicyNames := []string{} + for _, policy := range filterValues { + if _, ok := define.RestartPolicyMap[policy]; !ok { + invalidPolicyNames = append(invalidPolicyNames, policy) + } + } + var filterValueError error = nil + if len(invalidPolicyNames) > 0 { + errPrefix := "invalid restart policy" + if len(invalidPolicyNames) > 1 { + errPrefix = "invalid restart policies" + } + filterValueError = fmt.Errorf("%s %s", strings.Join(invalidPolicyNames, ", "), errPrefix) + } + return func(c *libpod.Container) bool { + for _, policy := range filterValues { + if policy == "none" && c.RestartPolicy() == define.RestartPolicyNone { + return true + } + if c.RestartPolicy() == policy { + return true + } + } + return false + }, filterValueError } return nil, errors.Errorf("%s is an invalid filter", filter) } diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index ef3ccab0c..d0a2b1bae 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -694,7 +694,33 @@ func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID s func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) { reports := []*entities.ContainerStartReport{} var exitCode = define.ExecErrorCodeGeneric - ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, namesOrIds, ic.Libpod) + containersNamesOrIds := namesOrIds + if len(options.Filters) > 0 { + filterFuncs := make([]libpod.ContainerFilter, 0, len(options.Filters)) + if len(options.Filters) > 0 { + for k, v := range options.Filters { + generatedFunc, err := dfilters.GenerateContainerFilterFuncs(k, v, ic.Libpod) + if err != nil { + return nil, err + } + filterFuncs = append(filterFuncs, generatedFunc) + } + } + candidates, err := ic.Libpod.GetContainers(filterFuncs...) + if err != nil { + return nil, err + } + containersNamesOrIds = []string{} + for _, candidate := range candidates { + for _, nameOrID := range namesOrIds { + if nameOrID == candidate.ID() || nameOrID == candidate.Name() { + containersNamesOrIds = append(containersNamesOrIds, nameOrID) + } + } + } + } + + ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, containersNamesOrIds, ic.Libpod) if err != nil { return nil, err } diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index aa26825e0..3830835cc 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -506,7 +506,30 @@ func startAndAttach(ic *ContainerEngine, name string, detachKeys *string, input, func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) { reports := []*entities.ContainerStartReport{} var exitCode = define.ExecErrorCodeGeneric - ctrs, err := getContainersByContext(ic.ClientCtx, options.All, false, namesOrIds) + containersNamesOrIds := namesOrIds + if len(options.Filters) > 0 { + containersNamesOrIds = []string{} + opts := new(containers.ListOptions).WithFilters(options.Filters).WithAll(true) + candidates, listErr := containers.List(ic.ClientCtx, opts) + if listErr != nil { + return nil, listErr + } + for _, candidate := range candidates { + for _, nameOrID := range namesOrIds { + if nameOrID == candidate.ID { + containersNamesOrIds = append(containersNamesOrIds, nameOrID) + continue + } + for _, containerName := range candidate.Names { + if containerName == nameOrID { + containersNamesOrIds = append(containersNamesOrIds, nameOrID) + continue + } + } + } + } + } + ctrs, err := getContainersByContext(ic.ClientCtx, options.All, false, containersNamesOrIds) if err != nil { return nil, err } diff --git a/test/system/045-start.bats b/test/system/045-start.bats index ff818e51d..542f9d1c2 100644 --- a/test/system/045-start.bats +++ b/test/system/045-start.bats @@ -40,4 +40,21 @@ load helpers fi } +@test "podman start --filter - start only containers that match the filter" { + run_podman run -d $IMAGE /bin/true + cid="$output" + run_podman start --filter restart-policy=always $cid "CID of restart-policy=always container" + is "$output" "" + + run_podman start --filter restart-policy=none $cid "CID of restart-policy=none container" + is "$output" "$cid" +} + +@test "podman start --filter invalid-restart-policy - return error" { + run_podman run -d $IMAGE /bin/true + cid="$output" + run_podman 125 start --filter restart-policy=fakepolicy $cid "CID of restart-policy=<not-exists> container" + is "$output" "Error: fakepolicy invalid restart policy" +} + # vim: filetype=sh |