diff options
35 files changed, 484 insertions, 40 deletions
diff --git a/.github/workflows/multi-arch-build.yaml b/.github/workflows/multi-arch-build.yaml new file mode 100644 index 000000000..1781604fe --- /dev/null +++ b/.github/workflows/multi-arch-build.yaml @@ -0,0 +1,181 @@ +name: build multi-arch images + +on: + # Upstream podman tends to be very active, with many merges per day. + # Only run this daily via cron schedule, or manually, not by branch push. + schedule: + - cron: '0 8 * * *' + # allows to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + multi: + name: multi-arch Podman build + env: + PODMAN_QUAY_REGISTRY: quay.io/podman + CONTAINERS_QUAY_REGISTRY: quay.io/containers + # list of architectures for build + PLATFORMS: linux/amd64,linux/s390x,linux/ppc64le,linux/arm64 + + # build several images (upstream, testing, stable) in parallel + strategy: + matrix: + # Builds are located under contrib/podmanimage/<source> directory + source: + - upstream + - testing + - stable + runs-on: ubuntu-latest + # internal registry caches build for inspection before push + services: + registry: + image: quay.io/libpod/registry:2 + ports: + - 5000:5000 + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + with: + driver-opts: network=host + install: true + + - name: Build and locally push Podman + uses: docker/build-push-action@v2 + with: + context: contrib/podmanimage/${{ matrix.source }} + file: ./contrib/podmanimage/${{ matrix.source }}/Dockerfile + platforms: ${{ env.PLATFORMS }} + push: true + tags: localhost:5000/podman/${{ matrix.source }} + + # Simple verification that container works + grab version number + - name: amd64 container sniff test + id: sniff_test + run: | + VERSION_OUTPUT="$(docker run localhost:5000/podman/${{ matrix.source }} \ + podman --storage-driver=vfs version)" + echo "$VERSION_OUTPUT" + VERSION=$(grep -Em1 '^Version: ' <<<"$VERSION_OUTPUT" | awk '{print $2}') + test -n "$VERSION" + echo "::set-output name=version::${VERSION}" + + # Generate image FQINs, labels, check whether to push + - name: Generate image information + id: image_info + run: | + if [[ "${{ matrix.source }}" == 'stable' ]]; then + # quay.io/podman/stable:vX.X.X + ALLTAGS=$(skopeo list-tags \ + docker://${{ env.PODMAN_QUAY_REGISTRY }}/stable | \ + jq -r '.Tags[]') + PUSH="false" + if fgrep -qx "$VERSION" <<<"$ALLTAGS"; then + PUSH="true" + fi + + FQIN='${{ env.PODMAN_QUAY_REGISTRY }}/stable:v${{ steps.sniff_test.outputs.version }}' # workaround vim syntax-hilighting bug: ' + # Only push if version tag does not exist + if [[ "$PUSH" == "true" ]]; then + echo "Will push $FQIN" + echo "::set-output name=podman_push::${PUSH}" + echo "::set-output name=podman_fqin::${FQIN}" + fi + + # quay.io/containers/podman:vX.X.X + unset ALLTAGS + ALLTAGS=$(skopeo list-tags \ + docker://${{ env.CONTAINERS_QUAY_REGISTRY }}/podman | \ + jq -r '.Tags[]') + PUSH="false" + if fgrep -qx "$VERSION" <<<"$ALLTAGS"; then + PUSH="true" + fi + + FQIN='${{ env.CONTAINERS_QUAY_REGISTRY}}/podman:v${{ steps.sniff_test.outputs.version }}' # workaround vim syntax-hilighting bug: ' + # Only push if version tag does not exist + if [[ "$PUSH" == "true" ]]; then + echo "Will push $FQIN" + echo "::set-output name=containers_push::${PUSH}" + echo "::set-output name=containers_fqin::$FQIN" + fi + else # upstream and testing podman image + P_FQIN='${{ env.PODMAN_QUAY_REGISTRY }}/${{ matrix.source }}:master' # workaround vim syntax-hilighting bug: ' + C_FQIN='${{ env.CONTAINERS_QUAY_REGISTRY}}/podman:master' # workaround vim syntax-hilighting bug: ' + echo "Will push $P_FQIN and $C_FQIN" + echo "::set-output name=podman_fqin::${P_FQIN}" + echo "::set-output name=containers_fqin::${C_FQIN}" + # Always push 'master' tag + echo '::set-output name=podman_push::true' + echo '::set-output name=containers_push::true' + fi + + # Hack to set $LABELS env. var. in _future_ steps. + # https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#multiline-strings + cat << EOF | tee $GITHUB_ENV + LABELS<<DELIMITER + org.opencontainers.image.source=https://github.com/${{ github.repository }}.git + org.opencontainers.image.revision=${{ github.sha }} + org.opencontainers.image.created=$(date -u --iso-8601=seconds) + DELIMITER + EOF + + # Separate steps to login and push for podman and containers quay + # repositories are required, because 2 sets of credentials are used and `docker + # login` as well as `podman login` do not support having 2 different + # credential sets for 1 registry. + # At the same time reuse of non-shell steps is not supported by Github Actions + # via anchors or composite actions + + # Push to 'podman' Quay repo for stable, testing. and upstream + - name: Login to 'podman' Quay registry + uses: docker/login-action@v1 + if: ${{ steps.image_info.outputs.podman_push == 'true' }} + with: + registry: ${{ env.PODMAN_QUAY_REGISTRY }} + # N/B: Secrets are not passed to workflows that are triggered + # by a pull request from a fork + username: ${{ secrets.PODMAN_QUAY_USERNAME }} + password: ${{ secrets.PODMAN_QUAY_PASSWORD }} + + - name: Push images to 'podman' Quay + uses: docker/build-push-action@v2 + if: ${{ steps.image_info.outputs.podman_push == 'true' }} + with: + cache-from: type=registry,ref=localhost:5000/podman/${{ matrix.source }} + cache-to: type=inline + context: contrib/podmanimage/${{ matrix.source }} + file: ./contrib/podmanimage/${{ matrix.source }}/Dockerfile + platforms: ${{ env.PLATFORMS }} + push: true + tags: ${{ steps.image_info.outputs.podman_fqin }} + labels: | + ${{ env.LABELS }} + + # Push to 'containers' Quay repo only stable podman + - name: Login to 'containers' Quay registry + if: ${{ steps.image_info.outputs.containers_push == 'true' }} + uses: docker/login-action@v1 + with: + registry: ${{ env.CONTAINERS_QUAY_REGISTRY}} + username: ${{ secrets.CONTAINERS_QUAY_USERNAME }} + password: ${{ secrets.CONTAINERS_QUAY_PASSWORD }} + + - name: Push images to 'containers' Quay + if: ${{ steps.image_info.outputs.containers_push == 'true' }} + uses: docker/build-push-action@v2 + with: + cache-from: type=registry,ref=localhost:5000/podman/${{ matrix.source }} + cache-to: type=inline + context: contrib/podmanimage/${{ matrix.source }} + file: ./contrib/podmanimage/${{ matrix.source }}/Dockerfile + platforms: ${{ env.PLATFORMS }} + push: true + tags: ${{ steps.image_info.outputs.containers_fqin }} + labels: | + ${{ env.LABELS }} diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index 6086df297..4aca79770 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -4,6 +4,7 @@ import ( "bufio" "fmt" "os" + "reflect" "strings" "github.com/containers/common/pkg/config" @@ -891,10 +892,85 @@ func AutocompleteNetworkFlag(cmd *cobra.Command, args []string, toComplete strin return append(networks, suggestions...), dir } -// AutocompleteJSONFormat - Autocomplete format flag option. -// -> "json" -func AutocompleteJSONFormat(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return []string{"json"}, cobra.ShellCompDirectiveNoFileComp +// AutocompleteFormat - Autocomplete json or a given struct to use for a go template. +// The input can be nil, In this case only json will be autocompleted. +// This function will only work for structs other types are not supported. +// When "{{." is typed the field and method names of the given struct will be completed. +// This also works recursive for nested structs. +func AutocompleteFormat(o interface{}) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // this function provides shell completion for go templates + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // autocomplete json when nothing or json is typed + if strings.HasPrefix("json", toComplete) { + return []string{"json"}, cobra.ShellCompDirectiveNoFileComp + } + // no input struct we cannot provide completion return nothing + if o == nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + // toComplete could look like this: {{ .Config }} {{ .Field.F + // 1. split the template variable delimiter + vars := strings.Split(toComplete, "{{") + if len(vars) == 1 { + // no variables return no completion + return nil, cobra.ShellCompDirectiveNoFileComp + } + // clean the spaces from the last var + field := strings.Split(vars[len(vars)-1], " ") + // split this into it struct field names + fields := strings.Split(field[len(field)-1], ".") + f := reflect.ValueOf(o) + for i := 1; i < len(fields); i++ { + if f.Kind() == reflect.Ptr { + f = f.Elem() + } + + // // the only supported type is struct + if f.Kind() != reflect.Struct { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + // last field get all names to suggest + if i == len(fields)-1 { + suggestions := []string{} + for j := 0; j < f.NumField(); j++ { + fname := f.Type().Field(j).Name + suffix := "}}" + kind := f.Type().Field(j).Type.Kind() + if kind == reflect.Ptr { + // make sure to read the actual type when it is a pointer + kind = f.Type().Field(j).Type.Elem().Kind() + } + // when we have a nested struct do not append braces instead append a dot + if kind == reflect.Struct { + suffix = "." + } + if strings.HasPrefix(fname, fields[i]) { + // add field name with closing braces + suggestions = append(suggestions, fname+suffix) + } + } + + for j := 0; j < f.NumMethod(); j++ { + fname := f.Type().Method(j).Name + if strings.HasPrefix(fname, fields[i]) { + // add method name with closing braces + suggestions = append(suggestions, fname+"}}") + } + } + + // add the current toComplete value in front so that the shell can complete this correctly + toCompArr := strings.Split(toComplete, ".") + toCompArr[len(toCompArr)-1] = "" + toComplete = strings.Join(toCompArr, ".") + return prefixSlice(toComplete, suggestions), cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp + } + // set the next struct field + f = f.FieldByName(fields[i]) + } + return nil, cobra.ShellCompDirectiveNoFileComp + } } // AutocompleteEventFilter - Autocomplete event filter flag options. diff --git a/cmd/podman/common/completion_test.go b/cmd/podman/common/completion_test.go new file mode 100644 index 000000000..5bd627b85 --- /dev/null +++ b/cmd/podman/common/completion_test.go @@ -0,0 +1,142 @@ +package common_test + +import ( + "testing" + + "github.com/containers/podman/v3/cmd/podman/common" + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" +) + +type Car struct { + Brand string + Stats struct { + HP *int + Displacement int + } + Extras map[string]string +} + +func (c Car) Type() string { + return "" +} + +func (c Car) Color() string { + return "" +} + +func TestAutocompleteFormat(t *testing.T) { + testStruct := struct { + Name string + Age int + Car *Car + }{} + + testStruct.Car = &Car{} + testStruct.Car.Extras = map[string]string{"test": "1"} + + tests := []struct { + name string + toComplete string + expected []string + }{ + { + "empty completion", + "", + []string{"json"}, + }, + { + "json completion", + "json", + []string{"json"}, + }, + { + "invalid completion", + "blahblah", + nil, + }, + { + "invalid completion", + "{{", + nil, + }, + { + "invalid completion", + "{{ ", + nil, + }, + { + "invalid completion", + "{{ ..", + nil, + }, + { + "fist level struct field name", + "{{.", + []string{"{{.Name}}", "{{.Age}}", "{{.Car."}, + }, + { + "fist level struct field name", + "{{ .", + []string{"{{ .Name}}", "{{ .Age}}", "{{ .Car."}, + }, + { + "fist level struct field name", + "{{ .N", + []string{"{{ .Name}}"}, + }, + { + "second level struct field name", + "{{ .Car.", + []string{"{{ .Car.Brand}}", "{{ .Car.Stats.", "{{ .Car.Extras}}", "{{ .Car.Color}}", "{{ .Car.Type}}"}, + }, + { + "second level struct field name", + "{{ .Car.B", + []string{"{{ .Car.Brand}}"}, + }, + { + "three level struct field name", + "{{ .Car.Stats.", + []string{"{{ .Car.Stats.HP}}", "{{ .Car.Stats.Displacement}}"}, + }, + { + "three level struct field name", + "{{ .Car.Stats.D", + []string{"{{ .Car.Stats.Displacement}}"}, + }, + { + "second level struct field name", + "{{ .Car.B", + []string{"{{ .Car.Brand}}"}, + }, + { + "invalid field name", + "{{ .Ca.B", + nil, + }, + { + "map key names don't work", + "{{ .Car.Extras.", + nil, + }, + { + "two variables struct field name", + "{{ .Car.Brand }} {{ .Car.", + []string{"{{ .Car.Brand }} {{ .Car.Brand}}", "{{ .Car.Brand }} {{ .Car.Stats.", "{{ .Car.Brand }} {{ .Car.Extras}}", + "{{ .Car.Brand }} {{ .Car.Color}}", "{{ .Car.Brand }} {{ .Car.Type}}"}, + }, + { + "only dot without variable", + ".", + nil, + }, + } + + for _, test := range tests { + completion, directive := common.AutocompleteFormat(testStruct)(nil, nil, test.toComplete) + // directive should always be greater than ShellCompDirectiveNoFileComp + assert.GreaterOrEqual(t, directive, cobra.ShellCompDirectiveNoFileComp, "unexpected ShellCompDirective") + assert.Equal(t, test.expected, completion, test.name) + } +} diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index 040dc6570..983b9e5ca 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -252,21 +252,24 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup return nil, nil, err } - netNS := specgen.Namespace{ - NSMode: nsmode.NSMode, - Value: nsmode.Value, + var netOpts map[string][]string + parts := strings.SplitN(string(cc.HostConfig.NetworkMode), ":", 2) + if len(parts) > 1 { + netOpts = make(map[string][]string) + netOpts[parts[0]] = strings.Split(parts[1], ",") } // network // Note: we cannot emulate compat exactly here. we only allow specifics of networks to be // defined when there is only one network. netInfo := entities.NetOptions{ - AddHosts: cc.HostConfig.ExtraHosts, - DNSOptions: cc.HostConfig.DNSOptions, - DNSSearch: cc.HostConfig.DNSSearch, - DNSServers: dns, - Network: netNS, - PublishPorts: specPorts, + AddHosts: cc.HostConfig.ExtraHosts, + DNSOptions: cc.HostConfig.DNSOptions, + DNSSearch: cc.HostConfig.DNSSearch, + DNSServers: dns, + Network: nsmode, + PublishPorts: specPorts, + NetworkOptions: netOpts, } // network names diff --git a/cmd/podman/containers/diff.go b/cmd/podman/containers/diff.go index f6f262066..799d01127 100644 --- a/cmd/podman/containers/diff.go +++ b/cmd/podman/containers/diff.go @@ -39,7 +39,7 @@ func init() { formatFlagName := "format" flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format") - _ = diffCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = diffCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(nil)) validate.AddLatestFlag(diffCmd, &diffOpts.Latest) } diff --git a/cmd/podman/containers/inspect.go b/cmd/podman/containers/inspect.go index e7921fc39..eb29b7285 100644 --- a/cmd/podman/containers/inspect.go +++ b/cmd/podman/containers/inspect.go @@ -5,6 +5,7 @@ import ( "github.com/containers/podman/v3/cmd/podman/inspect" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -35,7 +36,12 @@ func init() { formatFlagName := "format" flags.StringVarP(&inspectOpts.Format, formatFlagName, "f", "json", "Format the output to a Go template or json") - _ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(define.InspectContainerData{ + State: &define.InspectContainerState{}, + NetworkSettings: &define.InspectNetworkSettings{}, + Config: &define.InspectContainerConfig{}, + HostConfig: &define.InspectContainerHostConfig{}, + })) validate.AddLatestFlag(inspectCmd, &inspectOpts.Latest) } diff --git a/cmd/podman/containers/mount.go b/cmd/podman/containers/mount.go index 7853bfae6..fd5a279d2 100644 --- a/cmd/podman/containers/mount.go +++ b/cmd/podman/containers/mount.go @@ -61,7 +61,7 @@ func mountFlags(cmd *cobra.Command) { formatFlagName := "format" flags.StringVar(&mountOpts.Format, formatFlagName, "", "Print the mounted containers in specified format (json)") - _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(nil)) flags.BoolVar(&mountOpts.NoTruncate, "notruncate", false, "Do not truncate output") } diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index 97451eb46..3c0162676 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -87,7 +87,7 @@ func listFlagSet(cmd *cobra.Command) { formatFlagName := "format" flags.StringVar(&listOpts.Format, formatFlagName, "", "Pretty-print containers to JSON or using a Go template") - _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(entities.ListContainer{})) lastFlagName := "last" flags.IntVarP(&listOpts.Last, lastFlagName, "n", -1, "Print the n last created containers (all states)") diff --git a/cmd/podman/containers/stats.go b/cmd/podman/containers/stats.go index 4c31896be..7160f1ba8 100644 --- a/cmd/podman/containers/stats.go +++ b/cmd/podman/containers/stats.go @@ -71,7 +71,7 @@ func statFlags(cmd *cobra.Command) { formatFlagName := "format" flags.StringVar(&statsOptions.Format, formatFlagName, "", "Pretty-print container statistics to JSON or using a Go template") - _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(define.ContainerStats{})) flags.BoolVar(&statsOptions.NoReset, "no-reset", false, "Disable resetting the screen between intervals") flags.BoolVar(&statsOptions.NoStream, "no-stream", false, "Disable streaming stats and only pull the first result, default setting is false") diff --git a/cmd/podman/diff.go b/cmd/podman/diff.go index 4862d31b5..ae7d6c4bc 100644 --- a/cmd/podman/diff.go +++ b/cmd/podman/diff.go @@ -43,7 +43,7 @@ func init() { formatFlagName := "format" flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format") - _ = diffCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = diffCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(nil)) validate.AddLatestFlag(diffCmd, &diffOpts.Latest) } diff --git a/cmd/podman/generate/systemd.go b/cmd/podman/generate/systemd.go index 693506725..72b2e6335 100644 --- a/cmd/podman/generate/systemd.go +++ b/cmd/podman/generate/systemd.go @@ -72,7 +72,7 @@ func init() { formatFlagName := "format" flags.StringVar(&format, formatFlagName, "", "Print the created units in specified format (json)") - _ = systemdCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = systemdCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(nil)) flags.SetNormalizeFunc(utils.AliasFlags) } diff --git a/cmd/podman/images/diff.go b/cmd/podman/images/diff.go index 36b0d4e7a..7f4c3e83d 100644 --- a/cmd/podman/images/diff.go +++ b/cmd/podman/images/diff.go @@ -41,7 +41,7 @@ func diffFlags(flags *pflag.FlagSet) { formatFlagName := "format" flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format") - _ = diffCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = diffCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(nil)) } func diff(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/images/history.go b/cmd/podman/images/history.go index eaf56651f..16be0bb19 100644 --- a/cmd/podman/images/history.go +++ b/cmd/podman/images/history.go @@ -74,7 +74,7 @@ func historyFlags(cmd *cobra.Command) { formatFlagName := "format" flags.StringVar(&opts.format, formatFlagName, "", "Change the output to JSON or a Go template") - _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(entities.ImageHistoryLayer{})) flags.BoolVarP(&opts.human, "human", "H", true, "Display sizes and dates in human readable format") flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Do not truncate the output") diff --git a/cmd/podman/images/inspect.go b/cmd/podman/images/inspect.go index fb96286fa..ac3becaa6 100644 --- a/cmd/podman/images/inspect.go +++ b/cmd/podman/images/inspect.go @@ -5,6 +5,7 @@ import ( "github.com/containers/podman/v3/cmd/podman/inspect" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/pkg/domain/entities" + inspectTypes "github.com/containers/podman/v3/pkg/inspect" "github.com/spf13/cobra" ) @@ -34,7 +35,7 @@ func init() { formatFlagName := "format" flags.StringVarP(&inspectOpts.Format, formatFlagName, "f", "json", "Format the output to a Go template or json") - _ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(inspectTypes.ImageData{})) } func inspectExec(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go index 7a9a8a804..132af858b 100644 --- a/cmd/podman/images/list.go +++ b/cmd/podman/images/list.go @@ -83,7 +83,7 @@ func imageListFlagSet(cmd *cobra.Command) { formatFlagName := "format" flags.StringVar(&listFlag.format, formatFlagName, "", "Change the output format to JSON or a Go template") - _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(entities.ImageSummary{})) flags.BoolVar(&listFlag.digests, "digests", false, "Show digests") flags.BoolVarP(&listFlag.noHeading, "noheading", "n", false, "Do not print column headings") diff --git a/cmd/podman/images/mount.go b/cmd/podman/images/mount.go index 79c97006d..a098aac63 100644 --- a/cmd/podman/images/mount.go +++ b/cmd/podman/images/mount.go @@ -51,7 +51,7 @@ func mountFlags(cmd *cobra.Command) { formatFlagName := "format" flags.StringVar(&mountOpts.Format, formatFlagName, "", "Print the mounted images in specified format (json)") - _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(nil)) } func init() { diff --git a/cmd/podman/networks/inspect.go b/cmd/podman/networks/inspect.go index 953f5e6e8..a05b9026d 100644 --- a/cmd/podman/networks/inspect.go +++ b/cmd/podman/networks/inspect.go @@ -33,7 +33,7 @@ func init() { formatFlagName := "format" flags.StringVarP(&inspectOpts.Format, formatFlagName, "f", "", "Pretty-print network to JSON or using a Go template") - _ = networkinspectCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = networkinspectCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(nil)) } func networkInspect(_ *cobra.Command, args []string) error { diff --git a/cmd/podman/networks/list.go b/cmd/podman/networks/list.go index bc100da8c..e1b182cbf 100644 --- a/cmd/podman/networks/list.go +++ b/cmd/podman/networks/list.go @@ -41,7 +41,7 @@ var ( func networkListFlags(flags *pflag.FlagSet) { formatFlagName := "format" flags.StringVar(&networkListOptions.Format, formatFlagName, "", "Pretty-print networks to JSON or using a Go template") - _ = networklistCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = networklistCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(ListPrintReports{})) flags.BoolVarP(&networkListOptions.Quiet, "quiet", "q", false, "display only names") flags.BoolVar(&noTrunc, "no-trunc", false, "Do not truncate the network ID") diff --git a/cmd/podman/pods/inspect.go b/cmd/podman/pods/inspect.go index 96e007fa3..c66b81adb 100644 --- a/cmd/podman/pods/inspect.go +++ b/cmd/podman/pods/inspect.go @@ -10,6 +10,7 @@ import ( "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -44,7 +45,7 @@ func init() { formatFlagName := "format" flags.StringVarP(&inspectOptions.Format, formatFlagName, "f", "json", "Format the output to a Go template or json") - _ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(define.InspectPodData{})) validate.AddLatestFlag(inspectCmd, &inspectOptions.Latest) } diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go index a4b6d1afa..beaeda871 100644 --- a/cmd/podman/pods/ps.go +++ b/cmd/podman/pods/ps.go @@ -61,7 +61,7 @@ func init() { formatFlagName := "format" flags.StringVar(&psInput.Format, formatFlagName, "", "Pretty-print pods to JSON or using a Go template") - _ = psCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = psCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(ListPodReporter{})) flags.Bool("noheading", false, "Do not print headers") flags.BoolVar(&psInput.Namespace, "namespace", false, "Display namespace information of the pod") diff --git a/cmd/podman/pods/stats.go b/cmd/podman/pods/stats.go index e336b864e..97147275e 100644 --- a/cmd/podman/pods/stats.go +++ b/cmd/podman/pods/stats.go @@ -58,7 +58,7 @@ func init() { formatFlagName := "format" flags.StringVar(&statsOptions.Format, formatFlagName, "", "Pretty-print container statistics to JSON or using a Go template") - _ = statsCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = statsCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(entities.PodStatsReport{})) flags.BoolVar(&statsOptions.NoReset, "no-reset", false, "Disable resetting the screen when streaming") flags.BoolVar(&statsOptions.NoStream, "no-stream", false, "Disable streaming stats and only pull the first result") diff --git a/cmd/podman/secrets/inspect.go b/cmd/podman/secrets/inspect.go index 4036291ec..bcb1adb5e 100644 --- a/cmd/podman/secrets/inspect.go +++ b/cmd/podman/secrets/inspect.go @@ -40,7 +40,7 @@ func init() { flags := inspectCmd.Flags() formatFlagName := "format" flags.StringVar(&format, formatFlagName, "", "Format volume output using Go template") - _ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(entities.SecretInfoReport{})) } func inspect(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/secrets/list.go b/cmd/podman/secrets/list.go index 2006fb0ee..ba7065d61 100644 --- a/cmd/podman/secrets/list.go +++ b/cmd/podman/secrets/list.go @@ -47,7 +47,7 @@ func init() { flags := lsCmd.Flags() formatFlagName := "format" flags.StringVar(&listFlag.format, formatFlagName, "{{.ID}}\t{{.Name}}\t{{.Driver}}\t{{.CreatedAt}}\t{{.UpdatedAt}}\t\n", "Format volume output using Go template") - _ = lsCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = lsCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(entities.SecretInfoReport{})) flags.BoolVar(&listFlag.noHeading, "noheading", false, "Do not print headers") } diff --git a/cmd/podman/system/events.go b/cmd/podman/system/events.go index 0f52282a9..568610bdc 100644 --- a/cmd/podman/system/events.go +++ b/cmd/podman/system/events.go @@ -52,7 +52,7 @@ func init() { formatFlagName := "format" flags.StringVar(&eventFormat, formatFlagName, "", "format the output using a Go template") - _ = eventsCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = eventsCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(events.Event{})) flags.BoolVar(&eventOptions.Stream, "stream", true, "stream new events; for testing only") diff --git a/cmd/podman/system/info.go b/cmd/podman/system/info.go index 2babd49c8..afd5b3a34 100644 --- a/cmd/podman/system/info.go +++ b/cmd/podman/system/info.go @@ -10,6 +10,7 @@ import ( "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/ghodss/yaml" "github.com/spf13/cobra" @@ -68,7 +69,7 @@ func infoFlags(cmd *cobra.Command) { formatFlagName := "format" flags.StringVarP(&inFormat, formatFlagName, "f", "", "Change the output format to JSON or a Go template") - _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(define.Info{Host: &define.HostInfo{}, Store: &define.StoreInfo{}})) } func info(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/system/version.go b/cmd/podman/system/version.go index dfb10c080..ad9fd2a85 100644 --- a/cmd/podman/system/version.go +++ b/cmd/podman/system/version.go @@ -38,7 +38,7 @@ func init() { formatFlagName := "format" flags.StringVarP(&versionFormat, formatFlagName, "f", "", "Change the output format to JSON or a Go template") - _ = versionCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = versionCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(entities.SystemVersionReport{})) } func version(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/volumes/inspect.go b/cmd/podman/volumes/inspect.go index 91269e3d1..d52eee9f3 100644 --- a/cmd/podman/volumes/inspect.go +++ b/cmd/podman/volumes/inspect.go @@ -4,6 +4,7 @@ import ( "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/inspect" "github.com/containers/podman/v3/cmd/podman/registry" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -41,7 +42,7 @@ func init() { formatFlagName := "format" flags.StringVarP(&inspectOpts.Format, formatFlagName, "f", "json", "Format volume output using Go template") - _ = inspectCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = inspectCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(define.InspectVolumeData{})) } func volumeInspect(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/volumes/list.go b/cmd/podman/volumes/list.go index 0d764e988..f402afa94 100644 --- a/cmd/podman/volumes/list.go +++ b/cmd/podman/volumes/list.go @@ -14,6 +14,7 @@ import ( "github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -60,7 +61,7 @@ func init() { formatFlagName := "format" flags.StringVar(&cliOpts.Format, formatFlagName, "{{.Driver}}\t{{.Name}}\n", "Format volume output using Go template") - _ = lsCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteJSONFormat) + _ = lsCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(define.InspectVolumeData{})) flags.Bool("noheading", false, "Do not print headers") flags.BoolVarP(&cliOpts.Quiet, "quiet", "q", false, "Print volume output in quiet mode") diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md index 1ea9d1ea6..229bb82f5 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -1365,6 +1365,7 @@ $ podman create --name container1 -t -i fedora bash $ podman create --name container2 -t -i fedora bash $ podman create --name container3 --requires container1,container2 -t -i fedora bash $ podman start --attach container3 +``` ### Configure keep supplemental groups for access to volume diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index 3a2651f98..2e6d97a05 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -1719,6 +1719,7 @@ Multiple containers can be required. $ podman create --name container1 -t -i fedora bash $ podman create --name container2 -t -i fedora bash $ podman run --name container3 --requires container1,container2 -t -i fedora bash +``` ### Configure keep supplemental groups for access to volume diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index e7146a5d8..d97a4d3bd 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -26,6 +26,7 @@ import ( "github.com/docker/go-units" "github.com/gorilla/schema" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) func RemoveContainer(w http.ResponseWriter, r *http.Request) { @@ -148,14 +149,19 @@ func ListContainers(w http.ResponseWriter, r *http.Request) { containers = containers[:query.Limit] } } - var list = make([]*handlers.Container, len(containers)) - for i, ctnr := range containers { + list := make([]*handlers.Container, 0, len(containers)) + for _, ctnr := range containers { api, err := LibpodToContainer(ctnr, query.Size) if err != nil { + if errors.Cause(err) == define.ErrNoSuchCtr { + // container was removed between the initial fetch of the list and conversion + logrus.Debugf("Container %s removed between initial fetch and conversion, ignoring in output", ctnr.ID()) + continue + } utils.InternalServerError(w, err) return } - list[i] = api + list = append(list, api) } utils.WriteResponse(w, http.StatusOK, list) } diff --git a/test/compose/slirp4netns_opts/docker-compose.yml b/test/compose/slirp4netns_opts/docker-compose.yml new file mode 100644 index 000000000..dcdcae04c --- /dev/null +++ b/test/compose/slirp4netns_opts/docker-compose.yml @@ -0,0 +1,5 @@ +services: + alpine: + image: alpine + network_mode: "slirp4netns:allow_host_loopback=true" + command: sh -c "echo teststring | nc 10.0.2.2 5001" diff --git a/test/compose/slirp4netns_opts/setup.sh b/test/compose/slirp4netns_opts/setup.sh new file mode 100644 index 000000000..35bbf7c70 --- /dev/null +++ b/test/compose/slirp4netns_opts/setup.sh @@ -0,0 +1,8 @@ +# -*- bash -*- + +# create tempfile to store nc output +OUTFILE=$(mktemp) +# listen on a port, the container will try to connect to it +nc -l 5001 > $OUTFILE & + +nc_pid=$! diff --git a/test/compose/slirp4netns_opts/teardown.sh b/test/compose/slirp4netns_opts/teardown.sh new file mode 100644 index 000000000..656724363 --- /dev/null +++ b/test/compose/slirp4netns_opts/teardown.sh @@ -0,0 +1,4 @@ +# -*- bash -*- + +kill $nc_pid &> /dev/null +rm -f $OUTFILE diff --git a/test/compose/slirp4netns_opts/tests.sh b/test/compose/slirp4netns_opts/tests.sh new file mode 100644 index 000000000..1efce45c4 --- /dev/null +++ b/test/compose/slirp4netns_opts/tests.sh @@ -0,0 +1,6 @@ +# -*- bash -*- + +output="$(cat $OUTFILE)" +expected="teststring" + +is "$output" "$expected" "$testname : nc received teststring" |