diff options
77 files changed, 886 insertions, 352 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index ee0131279..fe00974b3 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -697,7 +697,7 @@ image_build_task: &image-build # this task to a specific Cirrus-Cron entry with this name. only_if: $CIRRUS_CRON == 'multiarch' depends_on: - - ext_svc_check + - build timeout_in: 120m # emulation is sssllllooooowwww gce_instance: <<: *standardvm @@ -714,22 +714,24 @@ image_build_task: &image-build - env: CTXDIR: contrib/hello env: + DISTRO_NV: "${FEDORA_NAME}" # Required for repo cache extraction PODMAN_USERNAME: ENCRYPTED[b9f0f2550029dd2196e086d9dd6c2d1fec7e328630b15990d9bb610f9fcccb5baab8b64a8c3e72b0c1d0f5917cf65aa1] PODMAN_PASSWORD: ENCRYPTED[e3444f6072853f0c8db7f964ead5e2204116af485469fa0de367f26b9316b460fd842a9882f552b9e9a83bbaf650d8b4] CONTAINERS_USERNAME: ENCRYPTED[54a372d5f22f424173c114c6fb25c3214956cad323d5b285c7393a71041884ce96471d0ff733774e5dab9fa5a3c8795c] CONTAINERS_PASSWORD: ENCRYPTED[4ecc3fb534935095a99fb1f2e320ac6bc87f3e7e186746e41cbcc4b5f5379a014b9fc8cc90e1f3d5abdbaf31580a4ab9] - clone_script: &noop mkdir -p $CIRRUS_WORKING_DIR - script: + main_script: - set -a; source /etc/automation_environment; set +a - main.sh $CIRRUS_REPO_CLONE_URL $CTXDIR test_image_build_task: <<: *image-build - # Allow this to run inside a PR w/ [CI:BUILD] - only_if: $CIRRUS_PR != '' && $CIRRUS_CHANGE_TITLE !=~ '.*CI:DOCS.*' + alias: test_image_build + # Allow this to run inside a PR w/ [CI:BUILD] only. + only_if: $CIRRUS_PR != '' && $CIRRUS_CHANGE_TITLE =~ '.*CI:BUILD.*' # This takes a LONG time, only run when requested. N/B: Any task # made to depend on this one will block FOREVER unless triggered. + # DO NOT ADD THIS TASK AS DEPENDENCY FOR `success_task`. trigger_type: manual # Overwrite all 'env', don't push anything, just do the build. env: @@ -758,7 +760,7 @@ meta_task: GCPJSON: ENCRYPTED[3a198350077849c8df14b723c0f4c9fece9ebe6408d35982e7adf2105a33f8e0e166ed3ed614875a0887e1af2b8775f4] GCPNAME: ENCRYPTED[2f9738ef295a706f66a13891b40e8eaa92a89e0e87faf8bed66c41eca72bf76cfd190a6f2d0e8444c631fdf15ed32ef6] GCPPROJECT: libpod-218412 - clone_script: *noop + clone_script: &noop mkdir -p $CIRRUS_WORKING_DIR script: /usr/local/bin/entrypoint.sh diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index 58dff3578..c29218d01 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -5,8 +5,10 @@ import ( "fmt" "os" "reflect" + "strconv" "strings" + libimageDefine "github.com/containers/common/libimage/define" "github.com/containers/common/libnetwork/types" "github.com/containers/common/pkg/config" "github.com/containers/image/v5/pkg/sysregistriesv2" @@ -495,6 +497,11 @@ func AutocompleteImages(cmd *cobra.Command, args []string, toComplete string) ([ return getImages(cmd, toComplete) } +// AutocompleteImageSearchFilters - Autocomplate `search --filter`. +func AutocompleteImageSearchFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return libimageDefine.SearchFilters, cobra.ShellCompDirectiveNoFileComp +} + // AutocompletePodExitPolicy - Autocomplete pod exit policy. func AutocompletePodExitPolicy(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return config.PodExitPolicies, cobra.ShellCompDirectiveNoFileComp @@ -963,9 +970,22 @@ func AutocompleteNetworkFlag(cmd *cobra.Command, args []string, toComplete strin return append(networks, suggestions...), dir } +type formatSuggestion struct { + fieldname string + suffix string +} + +func convertFormatSuggestions(suggestions []formatSuggestion) []string { + completions := make([]string, 0, len(suggestions)) + for _, f := range suggestions { + completions = append(completions, f.fieldname+f.suffix) + } + return completions +} + // 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. +// This function will only work for pointer to 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) { @@ -994,6 +1014,12 @@ func AutocompleteFormat(o interface{}) func(cmd *cobra.Command, args []string, t // split this into it struct field names fields := strings.Split(field[len(field)-1], ".") f := reflect.ValueOf(o) + if f.Kind() != reflect.Ptr { + // We panic here to make sure that all callers pass the value by reference. + // If someone passes a by value then all podman commands will panic since + // this function is run at init time. + panic("AutocompleteFormat: passed value must be a pointer to a struct") + } for i := 1; i < len(fields); i++ { // last field get all names to suggest if i == len(fields)-1 { @@ -1002,61 +1028,83 @@ func AutocompleteFormat(o interface{}) func(cmd *cobra.Command, args []string, t toCompArr := strings.Split(toComplete, ".") toCompArr[len(toCompArr)-1] = "" toComplete = strings.Join(toCompArr, ".") - return prefixSlice(toComplete, suggestions), cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp + return prefixSlice(toComplete, convertFormatSuggestions(suggestions)), cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp } - val := getActualStructType(f) - if val == nil { - // no struct return nothing to complete + // first follow pointer and create element when it is nil + f = actualReflectValue(f) + switch f.Kind() { + case reflect.Struct: + for j := 0; j < f.NumField(); j++ { + field := f.Type().Field(j) + // ok this is a bit weird but when we have an embedded nil struct + // calling FieldByName on a name which is present on this struct will panic + // Therefore we have to init them (non nil ptr), https://github.com/containers/podman/issues/14223 + if field.Anonymous && f.Field(j).Type().Kind() == reflect.Ptr { + f.Field(j).Set(reflect.New(f.Field(j).Type().Elem())) + } + } + // set the next struct field + f = f.FieldByName(fields[i]) + case reflect.Map: + rtype := f.Type().Elem() + if rtype.Kind() == reflect.Ptr { + rtype = rtype.Elem() + } + f = reflect.New(rtype) + case reflect.Func: + if f.Type().NumOut() != 1 { + // unsupported type return nothing + return nil, cobra.ShellCompDirectiveNoFileComp + } + f = reflect.New(f.Type().Out(0)) + default: + // unsupported type return nothing return nil, cobra.ShellCompDirectiveNoFileComp } - f = *val - - // set the next struct field - f = f.FieldByName(fields[i]) } return nil, cobra.ShellCompDirectiveNoFileComp } } -// getActualStructType take the value and check if it is a struct, +// actualReflectValue takes the value, // if it is pointer it will dereference it and when it is nil, -// it will create a new value from it to get the actual struct -// returns nil when type is not a struct -func getActualStructType(f reflect.Value) *reflect.Value { +// it will create a new value from it +func actualReflectValue(f reflect.Value) reflect.Value { // follow the pointer first if f.Kind() == reflect.Ptr { // if the pointer is nil we create a new value from the elements type - // this allows us to follow nil pointers and get the actual struct fields + // this allows us to follow nil pointers and get the actual type if f.IsNil() { f = reflect.New(f.Type().Elem()) } f = f.Elem() } - // we only support structs - if f.Kind() != reflect.Struct { - return nil - } - return &f + return f } // getStructFields reads all struct field names and method names and returns them. -func getStructFields(f reflect.Value, prefix string) []string { - var suggestions []string +func getStructFields(f reflect.Value, prefix string) []formatSuggestion { + var suggestions []formatSuggestion if f.IsValid() { suggestions = append(suggestions, getMethodNames(f, prefix)...) } - val := getActualStructType(f) - if val == nil { - // no struct return nothing to complete + f = actualReflectValue(f) + // we only support structs + if f.Kind() != reflect.Struct { return suggestions } - f = *val + var anonymous []formatSuggestion // loop over all field names for j := 0; j < f.NumField(); j++ { field := f.Type().Field(j) + // check if struct field is not exported, templates only use exported fields + // PkgPath is always empty for exported fields + if field.PkgPath != "" { + continue + } fname := field.Name suffix := "}}" kind := field.Type.Kind() @@ -1065,27 +1113,63 @@ func getStructFields(f reflect.Value, prefix string) []string { kind = field.Type.Elem().Kind() } // when we have a nested struct do not append braces instead append a dot - if kind == reflect.Struct { + if kind == reflect.Struct || kind == reflect.Map { suffix = "." } // if field is anonymous add the child fields as well if field.Anonymous { - suggestions = append(suggestions, getStructFields(f.Field(j), prefix)...) - } else if strings.HasPrefix(fname, prefix) { + anonymous = append(anonymous, getStructFields(f.Field(j), prefix)...) + } + if strings.HasPrefix(fname, prefix) { // add field name with suffix - suggestions = append(suggestions, fname+suffix) + suggestions = append(suggestions, formatSuggestion{fieldname: fname, suffix: suffix}) + } + } +outer: + for _, ano := range anonymous { + // we should only add anonymous child fields if they are not already present. + for _, sug := range suggestions { + if ano.fieldname == sug.fieldname { + continue outer + } } + suggestions = append(suggestions, ano) } return suggestions } -func getMethodNames(f reflect.Value, prefix string) []string { - suggestions := make([]string, 0, f.NumMethod()) +func getMethodNames(f reflect.Value, prefix string) []formatSuggestion { + suggestions := make([]formatSuggestion, 0, f.NumMethod()) for j := 0; j < f.NumMethod(); j++ { - fname := f.Type().Method(j).Name + method := f.Type().Method(j) + // in a template we can only run functions with one return value + if method.Func.Type().NumOut() != 1 { + continue + } + // when we have a nested struct do not append braces instead append a dot + kind := method.Func.Type().Out(0).Kind() + suffix := "}}" + if kind == reflect.Struct || kind == reflect.Map { + suffix = "." + } + // From a template users POV it is not importent when the use a struct field or method. + // They only notice the difference when the function requires arguments. + // So lets be nice and let the user know that this method requires arguments via the help text. + // Note since this is actually a method on a type the first argument is always fix so we should skip it. + num := method.Func.Type().NumIn() - 1 + if num > 0 { + // everything after tab will the completion scripts show as help when enabled + // overwrite the suffix because it expects the args + suffix = "\tThis is a function and requires " + strconv.Itoa(num) + " argument" + if num > 1 { + // add plural s + suffix += "s" + } + } + fname := method.Name if strings.HasPrefix(fname, prefix) { // add method name with closing braces - suggestions = append(suggestions, fname+"}}") + suggestions = append(suggestions, formatSuggestion{fieldname: fname, suffix: suffix}) } } return suggestions diff --git a/cmd/podman/common/completion_test.go b/cmd/podman/common/completion_test.go index 13f45a662..ae23b02e2 100644 --- a/cmd/podman/common/completion_test.go +++ b/cmd/podman/common/completion_test.go @@ -14,11 +14,29 @@ type Car struct { HP *int Displacement int } - Extras map[string]string + Extras map[string]Extra + // also ensure it will work with pointers + Extras2 map[string]*Extra +} + +type Extra struct { + Name1 string + Name2 string } type Anonymous struct { Hello string + // The name should match the testStruct Name below. This is used to make + // sure the logic uses the actual struct fields before the embedded ones. + Name struct { + Suffix string + Prefix string + } +} + +// The name should match the testStruct Age name below. +func (a Anonymous) Age() int { + return 0 } func (c Car) Type() string { @@ -31,6 +49,20 @@ func (c *Car) Color() string { return "" } +// This is for reflect testing required. +// nolint:unused +func (c Car) internal() int { + return 0 +} + +func (c Car) TwoOut() (string, string) { + return "", "" +} + +func (c Car) Struct() Car { + return Car{} +} + func TestAutocompleteFormat(t *testing.T) { testStruct := struct { Name string @@ -38,10 +70,10 @@ func TestAutocompleteFormat(t *testing.T) { Car *Car Car2 *Car *Anonymous + private int }{} testStruct.Car = &Car{} - testStruct.Car.Extras = map[string]string{"test": "1"} tests := []struct { name string @@ -76,17 +108,17 @@ func TestAutocompleteFormat(t *testing.T) { { "invalid completion", "{{ ..", - nil, + []string{}, }, { "fist level struct field name", "{{.", - []string{"{{.Name}}", "{{.Age}}", "{{.Car.", "{{.Car2.", "{{.Hello}}"}, + []string{"{{.Name}}", "{{.Age}}", "{{.Car.", "{{.Car2.", "{{.Anonymous.", "{{.Hello}}"}, }, { "fist level struct field name", "{{ .", - []string{"{{ .Name}}", "{{ .Age}}", "{{ .Car.", "{{ .Car2.", "{{ .Hello}}"}, + []string{"{{ .Name}}", "{{ .Age}}", "{{ .Car.", "{{ .Car2.", "{{ .Anonymous.", "{{ .Hello}}"}, }, { "fist level struct field name", @@ -96,7 +128,7 @@ func TestAutocompleteFormat(t *testing.T) { { "second level struct field name", "{{ .Car.", - []string{"{{ .Car.Color}}", "{{ .Car.Type}}", "{{ .Car.Brand}}", "{{ .Car.Stats.", "{{ .Car.Extras}}"}, + []string{"{{ .Car.Color}}", "{{ .Car.Struct.", "{{ .Car.Type}}", "{{ .Car.Brand}}", "{{ .Car.Stats.", "{{ .Car.Extras.", "{{ .Car.Extras2."}, }, { "second level struct field name", @@ -106,7 +138,7 @@ func TestAutocompleteFormat(t *testing.T) { { "second level nil struct field name", "{{ .Car2.", - []string{"{{ .Car2.Color}}", "{{ .Car2.Type}}", "{{ .Car2.Brand}}", "{{ .Car2.Stats.", "{{ .Car2.Extras}}"}, + []string{"{{ .Car2.Color}}", "{{ .Car2.Struct.", "{{ .Car2.Type}}", "{{ .Car2.Brand}}", "{{ .Car2.Stats.", "{{ .Car2.Extras.", "{{ .Car2.Extras2."}, }, { "three level struct field name", @@ -126,28 +158,44 @@ func TestAutocompleteFormat(t *testing.T) { { "invalid field name", "{{ .Ca.B", - nil, + []string{}, }, { "map key names don't work", "{{ .Car.Extras.", - nil, + []string{}, + }, + { + "map values work", + "{{ .Car.Extras.somekey.", + []string{"{{ .Car.Extras.somekey.Name1}}", "{{ .Car.Extras.somekey.Name2}}"}, + }, + { + "map values work with ptr", + "{{ .Car.Extras2.somekey.", + []string{"{{ .Car.Extras2.somekey.Name1}}", "{{ .Car.Extras2.somekey.Name2}}"}, }, { "two variables struct field name", "{{ .Car.Brand }} {{ .Car.", - []string{"{{ .Car.Brand }} {{ .Car.Color}}", "{{ .Car.Brand }} {{ .Car.Type}}", "{{ .Car.Brand }} {{ .Car.Brand}}", - "{{ .Car.Brand }} {{ .Car.Stats.", "{{ .Car.Brand }} {{ .Car.Extras}}"}, + []string{"{{ .Car.Brand }} {{ .Car.Color}}", "{{ .Car.Brand }} {{ .Car.Struct.", "{{ .Car.Brand }} {{ .Car.Type}}", + "{{ .Car.Brand }} {{ .Car.Brand}}", "{{ .Car.Brand }} {{ .Car.Stats.", "{{ .Car.Brand }} {{ .Car.Extras.", + "{{ .Car.Brand }} {{ .Car.Extras2."}, }, { "only dot without variable", ".", nil, }, + { + "access embedded nil struct field", + "{{.Hello.", + []string{}, + }, } for _, test := range tests { - completion, directive := common.AutocompleteFormat(testStruct)(nil, nil, test.toComplete) + 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/images/mount.go b/cmd/podman/images/mount.go index d5ab3d274..532d96196 100644 --- a/cmd/podman/images/mount.go +++ b/cmd/podman/images/mount.go @@ -7,7 +7,6 @@ import ( "github.com/containers/common/pkg/report" "github.com/containers/podman/v4/cmd/podman/common" "github.com/containers/podman/v4/cmd/podman/registry" - "github.com/containers/podman/v4/cmd/podman/utils" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -71,16 +70,12 @@ func mount(cmd *cobra.Command, args []string) error { return err } - if len(args) > 0 || mountOpts.All { - var errs utils.OutputErrors - for _, r := range reports { - if r.Err == nil { - fmt.Println(r.Path) - continue - } - errs = append(errs, r.Err) + if len(args) == 1 && mountOpts.Format == "" && !mountOpts.All { + if len(reports) != 1 { + return fmt.Errorf("internal error: expected 1 report but got %d", len(reports)) } - return errs.PrintErrors() + fmt.Println(reports[0].Path) + return nil } switch { diff --git a/cmd/podman/images/search.go b/cmd/podman/images/search.go index 335ea2b5a..a18f7a11d 100644 --- a/cmd/podman/images/search.go +++ b/cmd/podman/images/search.go @@ -83,8 +83,7 @@ func searchFlags(cmd *cobra.Command) { filterFlagName := "filter" flags.StringSliceVarP(&searchOptions.Filters, filterFlagName, "f", []string{}, "Filter output based on conditions provided (default [])") - // TODO add custom filter function - _ = cmd.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone) + _ = cmd.RegisterFlagCompletionFunc(filterFlagName, common.AutocompleteImageSearchFilters) formatFlagName := "format" flags.StringVar(&searchOptions.Format, formatFlagName, "", "Change the output format to JSON or a Go template") diff --git a/cmd/podman/root.go b/cmd/podman/root.go index 2bd4fa723..1892ff9f7 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -423,7 +423,7 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) { // -s is deprecated due to conflict with -s on subcommands storageDriverFlagName := "storage-driver" pFlags.StringVar(&opts.StorageDriver, storageDriverFlagName, "", "Select which storage driver is used to manage storage of images and containers") - _ = cmd.RegisterFlagCompletionFunc(storageDriverFlagName, completion.AutocompleteNone) //TODO: what can we recommend here? + _ = cmd.RegisterFlagCompletionFunc(storageDriverFlagName, completion.AutocompleteNone) tmpdirFlagName := "tmpdir" pFlags.StringVar(&opts.Engine.TmpDir, tmpdirFlagName, "", "Path to the tmp directory for libpod state content.\n\nNote: use the environment variable 'TMPDIR' to change the temporary storage location for container images, '/var/tmp'.\n") diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md index c63e8814b..009209343 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -460,6 +460,8 @@ content that disappears when the container is stopped. #### **--init** Run an init inside the container that forwards signals and reaps processes. +The container-init binary is mounted at `/run/podman-init`. +Mounting over `/run` will hence break container execution. #### **--init-ctr**=*type* (pods only) diff --git a/docs/source/markdown/podman-play-kube.1.md b/docs/source/markdown/podman-play-kube.1.md index 5c4bdc8c4..08bb2a5bc 100644 --- a/docs/source/markdown/podman-play-kube.1.md +++ b/docs/source/markdown/podman-play-kube.1.md @@ -20,7 +20,7 @@ Currently, the supported Kubernetes kinds are: `Kubernetes Pods or Deployments` -Only two volume types are supported by play kube, the *hostPath* and *persistentVolumeClaim* volume types. For the *hostPath* volume type, only the *default (empty)*, *DirectoryOrCreate*, *Directory*, *FileOrCreate*, *File*, and *Socket* subtypes are supported. The *CharDevice* and *BlockDevice* subtypes are not supported. Podman interprets the value of *hostPath* *path* as a file path when it contains at least one forward slash, otherwise Podman treats the value as the name of a named volume. When using a *persistentVolumeClaim*, the value for *claimName* is the name for the Podman named volume. +Only two volume types are supported by play kube, the *hostPath* and *persistentVolumeClaim* volume types. For the *hostPath* volume type, only the *default (empty)*, *DirectoryOrCreate*, *Directory*, *FileOrCreate*, *File*, *Socket*, *CharDevice* and *BlockDevice* subtypes are supported. Podman interprets the value of *hostPath* *path* as a file path when it contains at least one forward slash, otherwise Podman treats the value as the name of a named volume. When using a *persistentVolumeClaim*, the value for *claimName* is the name for the Podman named volume. Note: When playing a kube YAML with init containers, the init container will be created with init type value `always`. diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index 9d9394020..a16ee9394 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -498,6 +498,8 @@ content that disappears when the container is stopped. #### **--init** Run an init inside the container that forwards signals and reaps processes. +The container-init binary is mounted at `/run/podman-init`. +Mounting over `/run` will hence break container execution. #### **--init-path**=*path* diff --git a/docs/tutorials/mac_experimental.md b/docs/tutorials/mac_experimental.md index b5b815fe5..1e75d01b8 100644 --- a/docs/tutorials/mac_experimental.md +++ b/docs/tutorials/mac_experimental.md @@ -1,4 +1,4 @@ -# Using podman-machine on MacOS (x86_64 and Apple silicon) +# Using podman-machine on MacOS (Apple silicon and x86_64) ## Setup diff --git a/docs/tutorials/podman-for-windows.md b/docs/tutorials/podman-for-windows.md index bb9674774..4e929a14a 100644 --- a/docs/tutorials/podman-for-windows.md +++ b/docs/tutorials/podman-for-windows.md @@ -233,15 +233,15 @@ Linux container. This supports several notation schemes, including: Windows Style Paths: -`podman run -it c:\Users\User\myfolder:/myfolder ubi8-micro ls /myfolder` +`podman run --rm -v c:\Users\User\myfolder:/myfolder ubi8-micro ls /myfolder` Unixy Windows Paths: -`podman run -it /c/Users/User/myfolder:/myfolder ubi8-micro ls /myfolder` +`podman run --rm -v /c/Users/User/myfolder:/myfolder ubi8-micro ls /myfolder` Linux paths local to the WSL filesystem: -`podman run -it /var/myfolder:/myfolder ubi-micro ls /myfolder` +`podman run --rm -v /var/myfolder:/myfolder ubi-micro ls /myfolder` All of the above conventions work, whether running on a Windows prompt or the WSL Linux shell. Although when using Windows paths on Linux, appropriately quote @@ -12,9 +12,9 @@ require ( github.com/containernetworking/cni v1.1.0 github.com/containernetworking/plugins v1.1.1 github.com/containers/buildah v1.26.1 - github.com/containers/common v0.48.1-0.20220512112240-7536bf6ff9b1 + github.com/containers/common v0.48.1-0.20220523155016-2fd37da97824 github.com/containers/conmon v2.0.20+incompatible - github.com/containers/image/v5 v5.21.2-0.20220511203756-fe4fd4ed8be4 + github.com/containers/image/v5 v5.21.2-0.20220519193817-1e26896b8059 github.com/containers/ocicrypt v1.1.4-0.20220428134531-566b808bdf6f github.com/containers/psgo v1.7.2 github.com/containers/storage v1.41.1-0.20220511210719-cacc3325a9c8 @@ -340,13 +340,14 @@ github.com/containernetworking/plugins v1.1.1/go.mod h1:Sr5TH/eBsGLXK/h71HeLfX19 github.com/containers/buildah v1.26.1 h1:D65Vuo+orsI14WWtJhSX6KrpgBBa7+hveVWevzG8p8E= github.com/containers/buildah v1.26.1/go.mod h1:CsWSG8OpJd8v3mlLREJzVAOBgC93DjRNALUVHoi8QsY= github.com/containers/common v0.48.0/go.mod h1:zPLZCfLXfnd1jI0QRsD4By54fP4k1+ifQs+tulIe3o0= -github.com/containers/common v0.48.1-0.20220512112240-7536bf6ff9b1 h1:U+2rYjzRCvI3WRSFf+Rohtu7jRgk/VhJjjFHbU6j0Sk= -github.com/containers/common v0.48.1-0.20220512112240-7536bf6ff9b1/go.mod h1:h8YZVXePE7UViJQ3fPWpYAaeDNYBCzGtL5dA3N8yfT8= +github.com/containers/common v0.48.1-0.20220523155016-2fd37da97824 h1:5gMIUUpIK9DvHrrlj1Tik8GfCh5DEuVqm0JnYHWYUDw= +github.com/containers/common v0.48.1-0.20220523155016-2fd37da97824/go.mod h1:Ru/JjL1CTHzlxghVMhchzcFUwHLvlIeR5/SUMw8VUOI= github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg= github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= github.com/containers/image/v5 v5.21.1/go.mod h1:zl35egpcDQa79IEXIuoUe1bW+D1pdxRxYjNlyb3YiXw= -github.com/containers/image/v5 v5.21.2-0.20220511203756-fe4fd4ed8be4 h1:9yDGjKniCxCIVJwdiUHGTjguGJUcntDtWLUIz+LhyzY= github.com/containers/image/v5 v5.21.2-0.20220511203756-fe4fd4ed8be4/go.mod h1:OsX9sFexyGF0FCNAjfcVFv3IwMqDyLyV/WQY/roLPcE= +github.com/containers/image/v5 v5.21.2-0.20220519193817-1e26896b8059 h1:/FzsjrQ2nJtMom9IXEGieORlwUk/NyDuuz5SWcNo324= +github.com/containers/image/v5 v5.21.2-0.20220519193817-1e26896b8059/go.mod h1:KntCBNQn3qOuZmQuJ38ORyTozmWXiuo05Vef2S0Sm5M= github.com/containers/libtrust v0.0.0-20200511145503-9c3a6c22cd9a h1:spAGlqziZjCJL25C6F1zsQY05tfCKE9F5YwtEWWe6hU= github.com/containers/libtrust v0.0.0-20200511145503-9c3a6c22cd9a/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= @@ -1111,8 +1112,9 @@ github.com/polyfloyd/go-errorlint v0.0.0-20210722154253-910bb7978349/go.mod h1:w github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/proglottis/gpgme v0.1.1 h1:72xI0pt/hy7pqsRxk32KExITkXp+RZErRizsA+up/lQ= github.com/proglottis/gpgme v0.1.1/go.mod h1:fPbW/EZ0LvwQtH8Hy7eixhp1eF3G39dtx7GUN+0Gmy0= +github.com/proglottis/gpgme v0.1.2 h1:dKlhDqJ0kdEt+YHCD8FQEUdF9cJj/+mbJUNyUGNAEzY= +github.com/proglottis/gpgme v0.1.2/go.mod h1:fPbW/EZ0LvwQtH8Hy7eixhp1eF3G39dtx7GUN+0Gmy0= github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go index 9745121c7..c3db6152a 100644 --- a/libpod/boltdb_state.go +++ b/libpod/boltdb_state.go @@ -162,6 +162,11 @@ func (s *BoltState) Refresh() error { return err } + namesBucket, err := getNamesBucket(tx) + if err != nil { + return err + } + ctrsBucket, err := getCtrBucket(tx) if err != nil { return err @@ -192,6 +197,7 @@ func (s *BoltState) Refresh() error { // PID, mountpoint, and state for all of them // Then save the modified state // Also clear all network namespaces + toRemoveIDs := []string{} err = idBucket.ForEach(func(id, name []byte) error { ctrBkt := ctrsBucket.Bucket(id) if ctrBkt == nil { @@ -199,8 +205,16 @@ func (s *BoltState) Refresh() error { podBkt := podsBucket.Bucket(id) if podBkt == nil { // This is neither a pod nor a container - // Error out on the dangling ID - return errors.Wrapf(define.ErrInternal, "id %s is not a pod or a container", string(id)) + // Something is seriously wrong, but + // continue on and try to clean up the + // state and become consistent. + // Just note what needs to be removed + // for now - ForEach says you shouldn't + // remove things from the table during + // it. + logrus.Errorf("Database issue: dangling ID %s found (not a pod or container) - removing", string(id)) + toRemoveIDs = append(toRemoveIDs, string(id)) + return nil } // Get the state @@ -285,6 +299,24 @@ func (s *BoltState) Refresh() error { return err } + // Remove dangling IDs. + for _, id := range toRemoveIDs { + // Look up the ID to see if we also have a dangling name + // in the DB. + name := idBucket.Get([]byte(id)) + if name != nil { + if testID := namesBucket.Get(name); testID != nil { + logrus.Infof("Found dangling name %s (ID %s) in database", string(name), id) + if err := namesBucket.Delete(name); err != nil { + return errors.Wrapf(err, "error removing dangling name %s (ID %s) from database", string(name), id) + } + } + } + if err := idBucket.Delete([]byte(id)); err != nil { + return errors.Wrapf(err, "error removing dangling ID %s from database", id) + } + } + // Now refresh volumes err = allVolsBucket.ForEach(func(id, name []byte) error { dbVol := volBucket.Bucket(id) diff --git a/libpod/container_top_linux.go b/libpod/container_top_linux.go index 9b3dbc873..b30e0c732 100644 --- a/libpod/container_top_linux.go +++ b/libpod/container_top_linux.go @@ -96,7 +96,7 @@ func (c *Container) Top(descriptors []string) ([]string, error) { // For more details, please refer to github.com/containers/psgo. func (c *Container) GetContainerPidInformation(descriptors []string) ([]string, error) { pid := strconv.Itoa(c.state.PID) - // TODO: psgo returns a [][]string to give users the ability to apply + // NOTE: psgo returns a [][]string to give users the ability to apply // filters on the data. We need to change the API here // to return a [][]string if we want to make use of // filtering. diff --git a/libpod/define/container.go b/libpod/define/container.go index bb44a6a4a..ba939578f 100644 --- a/libpod/define/container.go +++ b/libpod/define/container.go @@ -35,4 +35,6 @@ const ( // OneShotInitContainer is a container that only runs as init once // and is then deleted. OneShotInitContainer = "once" + // ContainerInitPath is the default path of the mounted container init. + ContainerInitPath = "/run/podman-init" ) diff --git a/libpod/diff.go b/libpod/diff.go index 794b26b48..86fa063ec 100644 --- a/libpod/diff.go +++ b/libpod/diff.go @@ -8,17 +8,18 @@ import ( ) var initInodes = map[string]bool{ - "/dev": true, - "/etc/hostname": true, - "/etc/hosts": true, - "/etc/resolv.conf": true, - "/proc": true, - "/run": true, - "/run/notify": true, - "/run/.containerenv": true, - "/run/secrets": true, - "/sys": true, - "/etc/mtab": true, + "/dev": true, + "/etc/hostname": true, + "/etc/hosts": true, + "/etc/resolv.conf": true, + "/proc": true, + "/run": true, + "/run/notify": true, + "/run/.containerenv": true, + "/run/secrets": true, + define.ContainerInitPath: true, + "/sys": true, + "/etc/mtab": true, } // GetDiff returns the differences between the two images, layers, or containers diff --git a/libpod/events.go b/libpod/events.go index 39f5786a4..f09d8402a 100644 --- a/libpod/events.go +++ b/libpod/events.go @@ -89,8 +89,8 @@ func (p *Pod) newPodEvent(status events.Status) { } } -// newSystemEvent creates a new event for libpod as a whole. -func (r *Runtime) newSystemEvent(status events.Status) { +// NewSystemEvent creates a new event for libpod as a whole. +func (r *Runtime) NewSystemEvent(status events.Status) { e := events.NewEvent(status) e.Type = events.System diff --git a/libpod/events/events.go b/libpod/events/events.go index 04417fd8d..e83c2efee 100644 --- a/libpod/events/events.go +++ b/libpod/events/events.go @@ -150,6 +150,8 @@ func StringToStatus(name string) (Status, error) { switch name { case Attach.String(): return Attach, nil + case AutoUpdate.String(): + return AutoUpdate, nil case Build.String(): return Build, nil case Checkpoint.String(): diff --git a/libpod/runtime.go b/libpod/runtime.go index 58f20ef5b..4efa7b8e8 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -930,7 +930,7 @@ func (r *Runtime) refresh(alivePath string) error { } defer file.Close() - r.newSystemEvent(events.Refresh) + r.NewSystemEvent(events.Refresh) return nil } diff --git a/libpod/runtime_renumber.go b/libpod/runtime_renumber.go index 17e1d97e5..db055f40b 100644 --- a/libpod/runtime_renumber.go +++ b/libpod/runtime_renumber.go @@ -71,7 +71,7 @@ func (r *Runtime) renumberLocks() error { } } - r.newSystemEvent(events.Renumber) + r.NewSystemEvent(events.Renumber) return nil } diff --git a/pkg/api/handlers/utils/images.go b/pkg/api/handlers/utils/images.go index 7154f5616..433231f59 100644 --- a/pkg/api/handlers/utils/images.go +++ b/pkg/api/handlers/utils/images.go @@ -26,21 +26,26 @@ func NormalizeToDockerHub(r *http.Request, nameOrID string) (string, error) { return nameOrID, nil } - // Try to lookup the input to figure out if it was an ID or not. runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - img, _, err := runtime.LibimageRuntime().LookupImage(nameOrID, nil) + + // The candidate may resolve to a local non-Docker Hub image, such as + // 'busybox' -> 'registry.com/busybox'. + img, candidate, err := runtime.LibimageRuntime().LookupImage(nameOrID, nil) if err != nil { if errors.Cause(err) != storage.ErrImageUnknown { return "", fmt.Errorf("normalizing name for compat API: %v", err) } + // If the image could not be resolved locally, set the + // candidate back to the input. + candidate = nameOrID } else if strings.HasPrefix(img.ID(), strings.TrimPrefix(nameOrID, "sha256:")) { return img.ID(), nil } // No ID, so we can normalize. - named, err := reference.ParseNormalizedNamed(nameOrID) + named, err := reference.ParseNormalizedNamed(candidate) if err != nil { - return "", fmt.Errorf("normalizing name for compat API: %v", err) + return "", fmt.Errorf("normalizing name %q (orig: %q) for compat API: %v", candidate, nameOrID, err) } return named.String(), nil diff --git a/pkg/autoupdate/autoupdate.go b/pkg/autoupdate/autoupdate.go index ee530528e..0c795faed 100644 --- a/pkg/autoupdate/autoupdate.go +++ b/pkg/autoupdate/autoupdate.go @@ -12,6 +12,7 @@ import ( "github.com/containers/image/v5/transports/alltransports" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/libpod/events" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/systemd" systemdDefine "github.com/containers/podman/v4/pkg/systemd/define" @@ -142,6 +143,8 @@ func AutoUpdate(ctx context.Context, runtime *libpod.Runtime, options entities.A } defer conn.Close() + runtime.NewSystemEvent(events.AutoUpdate) + // Update all images/container according to their auto-update policy. var allReports []*entities.AutoUpdateReport updatedRawImages := make(map[string]bool) diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index 8e3b07929..32372019b 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -280,7 +280,6 @@ func Push(ctx context.Context, source string, destination string, options *PushO if err != nil { return err } - // TODO: have a global system context we can pass around (1st argument) header, err := auth.MakeXRegistryAuthHeader(&imageTypes.SystemContext{AuthFilePath: options.GetAuthfile()}, options.GetUsername(), options.GetPassword()) if err != nil { return err @@ -329,7 +328,6 @@ func Search(ctx context.Context, term string, options *SearchOptions) ([]entitie params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } - // TODO: have a global system context we can pass around (1st argument) header, err := auth.MakeXRegistryAuthHeader(&imageTypes.SystemContext{AuthFilePath: options.GetAuthfile()}, "", "") if err != nil { return nil, err diff --git a/pkg/bindings/images/pull.go b/pkg/bindings/images/pull.go index 20e47179c..de02c62fd 100644 --- a/pkg/bindings/images/pull.go +++ b/pkg/bindings/images/pull.go @@ -42,7 +42,6 @@ func Pull(ctx context.Context, rawImage string, options *PullOptions) ([]string, params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } - // TODO: have a global system context we can pass around (1st argument) header, err := auth.MakeXRegistryAuthHeader(&types.SystemContext{AuthFilePath: options.GetAuthfile()}, options.GetUsername(), options.GetPassword()) if err != nil { return nil, err diff --git a/pkg/bindings/images/rm.go b/pkg/bindings/images/rm.go index b80bacf45..eb3eef10c 100644 --- a/pkg/bindings/images/rm.go +++ b/pkg/bindings/images/rm.go @@ -16,9 +16,6 @@ func Remove(ctx context.Context, images []string, options *RemoveOptions) (*enti if options == nil { options = new(RemoveOptions) } - // FIXME - bindings tests are missing for this endpoint. Once the CI is - // re-enabled for bindings, we need to add them. At the time of writing, - // the tests don't compile. var report types.LibpodImagesRemoveReport conn, err := bindings.GetClient(ctx) if err != nil { diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index 7081c5d25..2bb4ceb5b 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -66,10 +66,9 @@ type ImageSummary struct { Dangling bool `json:",omitempty"` // Podman extensions - Names []string `json:",omitempty"` - Digest string `json:",omitempty"` - ConfigDigest string `json:",omitempty"` - History []string `json:",omitempty"` + Names []string `json:",omitempty"` + Digest string `json:",omitempty"` + History []string `json:",omitempty"` } func (i *ImageSummary) Id() string { // nolint @@ -398,7 +397,6 @@ type ImageUnmountOptions struct { // ImageMountReport describes the response from image mount type ImageMountReport struct { - Err error Id string // nolint Name string Repositories []string diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index c3ec7dd8a..d469fa0ca 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -159,10 +159,6 @@ func (ir *ImageEngine) Mount(ctx context.Context, nameOrIDs []string, opts entit mountReports := []*entities.ImageMountReport{} listMountsOnly := !opts.All && len(nameOrIDs) == 0 for _, i := range images { - // TODO: the .Err fields are not used. This pre-dates the - // libimage migration but should be addressed at some point. - // A quick glimpse at cmd/podman/image/mount.go suggests that - // the errors needed to be handled there as well. var mountPoint string var err error if listMountsOnly { diff --git a/pkg/domain/infra/abi/images_list.go b/pkg/domain/infra/abi/images_list.go index 9a0aaaf3a..8f5591e92 100644 --- a/pkg/domain/infra/abi/images_list.go +++ b/pkg/domain/infra/abi/images_list.go @@ -36,9 +36,7 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) } e := entities.ImageSummary{ - ID: img.ID(), - // TODO: libpod/image didn't set it but libimage should - // ConfigDigest: string(img.ConfigDigest), + ID: img.ID(), Created: img.Created().Unix(), Dangling: isDangling, Digest: string(img.Digest()), diff --git a/pkg/domain/infra/abi/images_test.go b/pkg/domain/infra/abi/images_test.go index 311ab3ed7..3999de457 100644 --- a/pkg/domain/infra/abi/images_test.go +++ b/pkg/domain/infra/abi/images_test.go @@ -16,39 +16,3 @@ func TestToDomainHistoryLayer(t *testing.T) { newLayer := toDomainHistoryLayer(&layer) assert.Equal(t, layer.Size, newLayer.Size) } - -// -// import ( -// "context" -// "testing" -// -// "github.com/stretchr/testify/mock" -// ) -// -// type MockImageRuntime struct { -// mock.Mock -// } -// -// func (m *MockImageRuntime) Delete(ctx context.Context, renderer func() interface{}, name string) error { -// _ = m.Called(ctx, renderer, name) -// return nil -// } -// -// func TestImageSuccess(t *testing.T) { -// actual := func() interface{} { return nil } -// -// m := new(MockImageRuntime) -// m.On( -// "Delete", -// mock.AnythingOfType("*context.emptyCtx"), -// mock.AnythingOfType("func() interface {}"), -// "fedora"). -// Return(nil) -// -// r := DirectImageRuntime{m} -// err := r.Delete(context.TODO(), actual, "fedora") -// if err != nil { -// t.Errorf("should be nil, got: %v", err) -// } -// m.AssertExpectations(t) -// } diff --git a/pkg/inspect/inspect.go b/pkg/inspect/inspect.go index 767d86daf..15943858f 100644 --- a/pkg/inspect/inspect.go +++ b/pkg/inspect/inspect.go @@ -41,18 +41,3 @@ type RootFS struct { Type string `json:"Type"` Layers []digest.Digest `json:"Layers"` } - -// ImageResult is used for podman images for collection and output. -type ImageResult struct { - Tag string - Repository string - RepoDigests []string - RepoTags []string - ID string - Digest digest.Digest - ConfigDigest digest.Digest - Created time.Time - Size *uint64 - Labels map[string]string - Dangling bool -} diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index e4c149abf..f37d79798 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -381,6 +381,22 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener Options: options, } s.Volumes = append(s.Volumes, &cmVolume) + case KubeVolumeTypeCharDevice: + // We are setting the path as hostPath:mountPath to comply with pkg/specgen/generate.DeviceFromPath. + // The type is here just to improve readability as it is not taken into account when the actual device is created. + device := spec.LinuxDevice{ + Path: fmt.Sprintf("%s:%s", volumeSource.Source, volume.MountPath), + Type: "c", + } + s.Devices = append(s.Devices, device) + case KubeVolumeTypeBlockDevice: + // We are setting the path as hostPath:mountPath to comply with pkg/specgen/generate.DeviceFromPath. + // The type is here just to improve readability as it is not taken into account when the actual device is created. + device := spec.LinuxDevice{ + Path: fmt.Sprintf("%s:%s", volumeSource.Source, volume.MountPath), + Type: "b", + } + s.Devices = append(s.Devices, device) default: return nil, errors.Errorf("Unsupported volume source type") } diff --git a/pkg/specgen/generate/kube/volume.go b/pkg/specgen/generate/kube/volume.go index 27881e77a..1d6d49b9d 100644 --- a/pkg/specgen/generate/kube/volume.go +++ b/pkg/specgen/generate/kube/volume.go @@ -22,8 +22,10 @@ type KubeVolumeType int const ( KubeVolumeTypeBindMount KubeVolumeType = iota - KubeVolumeTypeNamed KubeVolumeType = iota - KubeVolumeTypeConfigMap KubeVolumeType = iota + KubeVolumeTypeNamed + KubeVolumeTypeConfigMap + KubeVolumeTypeBlockDevice + KubeVolumeTypeCharDevice ) //nolint:revive @@ -78,7 +80,30 @@ func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error) if st.Mode()&os.ModeSocket != os.ModeSocket { return nil, errors.Errorf("checking HostPathSocket: path %s is not a socket", hostPath.Path) } - + case v1.HostPathBlockDev: + dev, err := os.Stat(hostPath.Path) + if err != nil { + return nil, errors.Wrap(err, "error checking HostPathBlockDevice") + } + if dev.Mode()&os.ModeCharDevice == os.ModeCharDevice { + return nil, errors.Errorf("checking HostPathDevice: path %s is not a block device", hostPath.Path) + } + return &KubeVolume{ + Type: KubeVolumeTypeBlockDevice, + Source: hostPath.Path, + }, nil + case v1.HostPathCharDev: + dev, err := os.Stat(hostPath.Path) + if err != nil { + return nil, errors.Wrap(err, "error checking HostPathCharDevice") + } + if dev.Mode()&os.ModeCharDevice != os.ModeCharDevice { + return nil, errors.Errorf("checking HostPathCharDevice: path %s is not a character device", hostPath.Path) + } + return &KubeVolume{ + Type: KubeVolumeTypeCharDevice, + Source: hostPath.Path, + }, nil case v1.HostPathDirectory: case v1.HostPathFile: case v1.HostPathUnset: diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index 081df0441..dda2de6e4 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -128,7 +128,7 @@ func makeCommand(s *specgen.SpecGenerator, imageData *libimage.ImageData, rtc *c if initPath == "" { return nil, errors.Errorf("no path to init binary found but container requested an init") } - finalCommand = append([]string{"/dev/init", "--"}, finalCommand...) + finalCommand = append([]string{define.ContainerInitPath, "--"}, finalCommand...) } return finalCommand, nil diff --git a/pkg/specgen/generate/storage.go b/pkg/specgen/generate/storage.go index f30fc4671..0a4d03780 100644 --- a/pkg/specgen/generate/storage.go +++ b/pkg/specgen/generate/storage.go @@ -20,9 +20,7 @@ import ( "github.com/sirupsen/logrus" ) -var ( - errDuplicateDest = errors.Errorf("duplicate mount destination") -) +var errDuplicateDest = errors.Errorf("duplicate mount destination") // Produce final mounts and named volumes for a container func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, img *libimage.Image) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, error) { @@ -359,7 +357,7 @@ func getVolumesFrom(volumesFrom []string, runtime *libpod.Runtime) (map[string]s // This does *NOT* modify the container command - that must be done elsewhere. func addContainerInitBinary(s *specgen.SpecGenerator, path string) (spec.Mount, error) { mount := spec.Mount{ - Destination: "/dev/init", + Destination: define.ContainerInitPath, Type: define.TypeBind, Source: path, Options: []string{define.TypeBind, "ro"}, diff --git a/test/apiv2/70-short-names.at b/test/apiv2/70-short-names.at index a5087c115..dbf816f55 100644 --- a/test/apiv2/70-short-names.at +++ b/test/apiv2/70-short-names.at @@ -6,11 +6,16 @@ # Pull the libpod/quay image which is used in all tests below. t POST "images/create?fromImage=quay.io/libpod/alpine:latest" 200 .error~null .status~".*Download complete.*" +# 14291 - let a short-name resolve to a *local* non Docker-Hub image. +t POST containers/create Image=alpine 201 .Id~[0-9a-f]\\{64\\} +cid=$(jq -r '.Id' <<<"$output") +t GET containers/$cid/json 200 .Image="quay.io/libpod/alpine:latest" +podman rm -f $cid ########## TAG t POST "images/quay.io/libpod/alpine/tag?repo=foo" 201 -t DELETE "images/foo" 200 +t DELETE "images/docker.io/library/foo" 200 ########## BUILD @@ -52,9 +57,6 @@ t DELETE "images/foo" 200 ########## TAG -# Looking up 'alpine' will fail as it gets normalized to docker.io. -t POST "images/alpine/tag?repo=foo" 404 .cause="image not known" - # The libpod endpoint will resolve to it without issues. t GET "libpod/images/alpine/exists" 204 @@ -67,22 +69,21 @@ t GET "libpod/images/docker.io/library/foo/exists" 204 ########## REMOVE -t DELETE "images/alpine" 404 .cause="image not known" # fails since docker.io/library/alpine does not exist t DELETE "images/foo" 200 # removes the previously tagged image ########## GET # Same procedure as above but with the /get endpoint. -t GET "images/alpine/get" 404 .cause="image not known" t POST "images/quay.io/libpod/alpine/tag?repo=foo" 201 t GET "images/foo/get" 200 '[POSIX tar archive]' t DELETE "images/foo" 200 +t GET "images/alpine/get" 200 ########## HISTORY -t GET "images/alpine/history" 404 .cause="image not known" +t GET "images/alpine/history" 200 t GET "images/quay.io/libpod/alpine/history" 200 t POST "images/quay.io/libpod/alpine/tag?repo=foo" 201 t GET "libpod/images/foo/history" 200 @@ -91,7 +92,7 @@ t DELETE "images/foo" 200 ########## PUSH -t POST "images/alpine/push?destination=localhost:9999/do/not:exist" 404 .cause="image not known" +t POST "images/alpine/push?destination=localhost:9999/do:exist" 200 t POST "images/quay.io/libpod/alpine/push?destination=localhost:9999/do/not:exist" 200 # Error is in the response t POST "images/quay.io/libpod/alpine/tag?repo=foo" 201 t POST "images/foo/push?destination=localhost:9999/do/not:exist" 200 # Error is in the response @@ -100,7 +101,7 @@ t DELETE "images/foo" ########## CREATE A CONTAINER -t POST "containers/create" Image=alpine 404 .cause="image not known" +t POST "containers/create" Image=alpine 201 t POST "containers/create" Image=quay.io/libpod/alpine:latest 201 cid=$(jq -r '.Id' <<<"$output") t POST "images/quay.io/libpod/alpine/tag?repo=foo" 201 @@ -113,7 +114,7 @@ t DELETE "containers/$cid" t POST "containers/create" Image=quay.io/libpod/alpine:latest 201 cid=$(jq -r '.Id' <<<"$output") -t GET "images/alpine/get" 404 .cause="image not known" +t GET "images/alpine/get" 200 t POST "commit?container=$cid&repo=foo&tag=tag" 201 t GET "images/foo/get" 404 .cause="image not known" t GET "images/foo:tag/get" 200 diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 216c3357c..31044f68b 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -21,6 +21,7 @@ import ( "github.com/containers/podman/v4/pkg/util" . "github.com/containers/podman/v4/test/utils" "github.com/containers/storage/pkg/stringid" + "github.com/google/uuid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/format" @@ -3685,4 +3686,150 @@ ENV OPENJ9_JAVA_OPTIONS=%q Expect(usernsInCtr).Should(Exit(0)) Expect(string(usernsInCtr.Out.Contents())).To(Not(Equal(string(initialUsernsConfig)))) }) + + // Check the block devices are exposed inside container + It("ddpodman play kube expose block device inside container", func() { + SkipIfRootless("It needs root access to create devices") + + // randomize the folder name to avoid error when running tests with multiple nodes + uuid, err := uuid.NewUUID() + Expect(err).To(BeNil()) + devFolder := fmt.Sprintf("/dev/foodev%x", uuid[:6]) + Expect(os.MkdirAll(devFolder, os.ModePerm)).To(BeNil()) + defer os.RemoveAll(devFolder) + + devicePath := fmt.Sprintf("%s/blockdevice", devFolder) + mknod := SystemExec("mknod", []string{devicePath, "b", "7", "0"}) + mknod.WaitWithDefaultTimeout() + Expect(mknod).Should(Exit(0)) + + blockVolume := getHostPathVolume("BlockDevice", devicePath) + + pod := getPod(withVolume(blockVolume), withCtr(getCtr(withImage(registry), withCmd(nil), withArg(nil), withVolumeMount(devicePath, false)))) + err = generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + // Container should be in running state + inspect := podmanTest.Podman([]string{"inspect", "--format", "{{.State.Status}}", "testPod-" + defaultCtrName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(ContainSubstring("running")) + + // Container should have a block device /dev/loop1 + inspect = podmanTest.Podman([]string{"inspect", "--format", "{{.HostConfig.Devices}}", "testPod-" + defaultCtrName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(devicePath)) + }) + + // Check the char devices are exposed inside container + It("ddpodman play kube expose character device inside container", func() { + SkipIfRootless("It needs root access to create devices") + + // randomize the folder name to avoid error when running tests with multiple nodes + uuid, err := uuid.NewUUID() + Expect(err).To(BeNil()) + devFolder := fmt.Sprintf("/dev/foodev%x", uuid[:6]) + Expect(os.MkdirAll(devFolder, os.ModePerm)).To(BeNil()) + defer os.RemoveAll(devFolder) + + devicePath := fmt.Sprintf("%s/chardevice", devFolder) + mknod := SystemExec("mknod", []string{devicePath, "c", "3", "1"}) + mknod.WaitWithDefaultTimeout() + Expect(mknod).Should(Exit(0)) + + charVolume := getHostPathVolume("CharDevice", devicePath) + + pod := getPod(withVolume(charVolume), withCtr(getCtr(withImage(registry), withCmd(nil), withArg(nil), withVolumeMount(devicePath, false)))) + err = generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + // Container should be in running state + inspect := podmanTest.Podman([]string{"inspect", "--format", "{{.State.Status}}", "testPod-" + defaultCtrName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(ContainSubstring("running")) + + // Container should have a block device /dev/loop1 + inspect = podmanTest.Podman([]string{"inspect", "--format", "{{.HostConfig.Devices}}", "testPod-" + defaultCtrName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(devicePath)) + }) + + It("podman play kube reports error when the device does not exists", func() { + SkipIfRootless("It needs root access to create devices") + + devicePath := "/dev/foodevdir/baddevice" + + blockVolume := getHostPathVolume("BlockDevice", devicePath) + + pod := getPod(withVolume(blockVolume), withCtr(getCtr(withImage(registry), withCmd(nil), withArg(nil), withVolumeMount(devicePath, false)))) + err = generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(125)) + }) + + It("ddpodman play kube reports error when we try to expose char device as block device", func() { + SkipIfRootless("It needs root access to create devices") + + // randomize the folder name to avoid error when running tests with multiple nodes + uuid, err := uuid.NewUUID() + Expect(err).To(BeNil()) + devFolder := fmt.Sprintf("/dev/foodev%x", uuid[:6]) + Expect(os.MkdirAll(devFolder, os.ModePerm)).To(BeNil()) + defer os.RemoveAll(devFolder) + + devicePath := fmt.Sprintf("%s/chardevice", devFolder) + mknod := SystemExec("mknod", []string{devicePath, "c", "3", "1"}) + mknod.WaitWithDefaultTimeout() + Expect(mknod).Should(Exit(0)) + + charVolume := getHostPathVolume("BlockDevice", devicePath) + + pod := getPod(withVolume(charVolume), withCtr(getCtr(withImage(registry), withCmd(nil), withArg(nil), withVolumeMount(devicePath, false)))) + err = generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(125)) + }) + + It("ddpodman play kube reports error when we try to expose block device as char device", func() { + SkipIfRootless("It needs root access to create devices") + + // randomize the folder name to avoid error when running tests with multiple nodes + uuid, err := uuid.NewUUID() + Expect(err).To(BeNil()) + devFolder := fmt.Sprintf("/dev/foodev%x", uuid[:6]) + Expect(os.MkdirAll(devFolder, os.ModePerm)).To(BeNil()) + + devicePath := fmt.Sprintf("%s/blockdevice", devFolder) + mknod := SystemExec("mknod", []string{devicePath, "b", "7", "0"}) + mknod.WaitWithDefaultTimeout() + Expect(mknod).Should(Exit(0)) + + charVolume := getHostPathVolume("CharDevice", devicePath) + + pod := getPod(withVolume(charVolume), withCtr(getCtr(withImage(registry), withCmd(nil), withArg(nil), withVolumeMount(devicePath, false)))) + err = generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(125)) + }) + }) diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 182ae1888..828e92170 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -13,6 +13,7 @@ import ( "time" "github.com/containers/common/pkg/cgroups" + "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/rootless" . "github.com/containers/podman/v4/test/utils" "github.com/containers/storage/pkg/stringid" @@ -286,19 +287,20 @@ var _ = Describe("Podman run", func() { result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) conData := result.InspectContainerToJSON() - Expect(conData[0]).To(HaveField("Path", "/dev/init")) + Expect(conData[0]).To(HaveField("Path", define.ContainerInitPath)) Expect(conData[0].Config.Annotations).To(HaveKeyWithValue("io.podman.annotations.init", "TRUE")) }) It("podman run a container with --init and --init-path", func() { - session := podmanTest.Podman([]string{"run", "--name", "test", "--init", "--init-path", "/usr/libexec/podman/catatonit", ALPINE, "ls"}) + // Also bind-mount /dev (#14251). + session := podmanTest.Podman([]string{"run", "-v", "/dev:/dev", "--name", "test", "--init", "--init-path", "/usr/libexec/podman/catatonit", ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"inspect", "test"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) conData := result.InspectContainerToJSON() - Expect(conData[0]).To(HaveField("Path", "/dev/init")) + Expect(conData[0]).To(HaveField("Path", define.ContainerInitPath)) Expect(conData[0].Config.Annotations).To(HaveKeyWithValue("io.podman.annotations.init", "TRUE")) }) diff --git a/test/e2e/search_test.go b/test/e2e/search_test.go index 64677ba54..d37d8fd1a 100644 --- a/test/e2e/search_test.go +++ b/test/e2e/search_test.go @@ -455,7 +455,6 @@ registries = ['{{.Host}}:{{.Port}}']` }) It("podman search with wildcards", func() { - Skip("FIXME: search on registry.redhat.io is broken (Dec 16 '21)") search := podmanTest.Podman([]string{"search", "registry.redhat.io/*openshift*"}) search.WaitWithDefaultTimeout() Expect(search).Should(Exit(0)) diff --git a/test/system/060-mount.bats b/test/system/060-mount.bats index 7addbd88e..2735d2afd 100644 --- a/test/system/060-mount.bats +++ b/test/system/060-mount.bats @@ -50,6 +50,10 @@ load helpers run_podman image mount $IMAGE mount_path="$output" + # Make sure that `mount -a` prints a table + run_podman image mount -a + is "$output" "$IMAGE .*$mount_path" + test -d $mount_path # Image is custom-built and has a file containing the YMD tag. Check it. @@ -62,8 +66,8 @@ load helpers run_podman image mount is "$output" "$IMAGE *$mount_path" "podman image mount with no args" - # Clean up - run_podman image umount $IMAGE + # Clean up: -f since we mounted it twice + run_podman image umount -f $IMAGE is "$output" "$iid" "podman image umount: image ID of what was umounted" run_podman image umount $IMAGE diff --git a/test/system/255-auto-update.bats b/test/system/255-auto-update.bats index 6cdae2ada..6cee939fb 100644 --- a/test/system/255-auto-update.bats +++ b/test/system/255-auto-update.bats @@ -135,15 +135,27 @@ function _confirm_update() { # This test can fail in dev. environment because of SELinux. # quick fix: chcon -t container_runtime_exec_t ./bin/podman @test "podman auto-update - label io.containers.autoupdate=image" { + since=$(date --iso-8601=seconds) + run_podman auto-update + is "$output" "" + run_podman events --filter type=system --since $since --stream=false + is "$output" "" + generate_service alpine image _wait_service_ready container-$cname.service + since=$(date --iso-8601=seconds) run_podman auto-update --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}" is "$output" ".*container-$cname.service,quay.io/libpod/alpine:latest,pending,registry.*" "Image update is pending." + run_podman events --filter type=system --since $since --stream=false + is "$output" ".* system auto-update" + since=$(date --iso-8601=seconds) run_podman auto-update --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}" is "$output" "Trying to pull.*" "Image is updated." is "$output" ".*container-$cname.service,quay.io/libpod/alpine:latest,true,registry.*" "Image is updated." + run_podman events --filter type=system --since $since --stream=false + is "$output" ".* system auto-update" _confirm_update $cname $ori_image } diff --git a/test/system/helpers.bash b/test/system/helpers.bash index 6868f2691..fe9e971fb 100644 --- a/test/system/helpers.bash +++ b/test/system/helpers.bash @@ -397,25 +397,25 @@ function _ensure_pod_state() { for i in {0..5}; do run_podman pod inspect $1 --format "{{.State}}" if [[ $output == "$2" ]]; then - break + return fi sleep 0.5 done - is "$output" "$2" "unexpected pod state" + die "Timed out waiting for pod $1 to enter state $2" } # Wait for the container's (1st arg) running state (2nd arg) function _ensure_container_running() { - for i in {0..5}; do + for i in {0..20}; do run_podman container inspect $1 --format "{{.State.Running}}" if [[ $output == "$2" ]]; then - break + return fi sleep 0.5 done - is "$output" "$2" "unexpected pod state" + die "Timed out waiting for container $1 to enter state running=$2" } ########################### diff --git a/vendor/github.com/containers/common/libimage/define/search.go b/vendor/github.com/containers/common/libimage/define/search.go new file mode 100644 index 000000000..0abd2ca1c --- /dev/null +++ b/vendor/github.com/containers/common/libimage/define/search.go @@ -0,0 +1,13 @@ +package define + +const ( + // SearchFilterAutomated is the key for filtering images by their automated attribute. + SearchFilterAutomated = "is-automated" + // SearchFilterOfficial is the key for filtering images by their official attribute. + SearchFilterOfficial = "is-official" + // SearchFilterStars is the key for filtering images by stars. + SearchFilterStars = "stars" +) + +// SearchFilters includes all supported search filters. +var SearchFilters = []string{SearchFilterAutomated, SearchFilterOfficial, SearchFilterStars} diff --git a/vendor/github.com/containers/common/libimage/load.go b/vendor/github.com/containers/common/libimage/load.go index c2d066645..89faa4635 100644 --- a/vendor/github.com/containers/common/libimage/load.go +++ b/vendor/github.com/containers/common/libimage/load.go @@ -114,6 +114,11 @@ func (r *Runtime) loadMultiImageDockerArchive(ctx context.Context, ref types.Ima if err != nil { return nil, err } + defer func() { + if err := reader.Close(); err != nil { + logrus.Errorf("Closing reader of docker archive: %v", err) + } + }() refLists, err := reader.List() if err != nil { diff --git a/vendor/github.com/containers/common/libimage/pull.go b/vendor/github.com/containers/common/libimage/pull.go index 4ce8add2f..771756160 100644 --- a/vendor/github.com/containers/common/libimage/pull.go +++ b/vendor/github.com/containers/common/libimage/pull.go @@ -315,6 +315,11 @@ func (r *Runtime) copyFromDockerArchive(ctx context.Context, ref types.ImageRefe if err != nil { return nil, err } + defer func() { + if err := reader.Close(); err != nil { + logrus.Errorf("Closing reader of docker archive: %v", err) + } + }() return r.copyFromDockerArchiveReaderReference(ctx, reader, readerRef, options) } diff --git a/vendor/github.com/containers/common/libimage/search.go b/vendor/github.com/containers/common/libimage/search.go index 33a4776ce..204bcc8c7 100644 --- a/vendor/github.com/containers/common/libimage/search.go +++ b/vendor/github.com/containers/common/libimage/search.go @@ -7,6 +7,7 @@ import ( "strings" "sync" + "github.com/containers/common/libimage/define" registryTransport "github.com/containers/image/v5/docker" "github.com/containers/image/v5/pkg/sysregistriesv2" "github.com/containers/image/v5/transports/alltransports" @@ -81,22 +82,22 @@ func ParseSearchFilter(filter []string) (*SearchFilter, error) { for _, f := range filter { arr := strings.SplitN(f, "=", 2) switch arr[0] { - case "stars": + case define.SearchFilterStars: if len(arr) < 2 { - return nil, errors.Errorf("invalid `stars` filter %q, should be stars=<value>", filter) + return nil, errors.Errorf("invalid filter %q, should be stars=<value>", filter) } stars, err := strconv.Atoi(arr[1]) if err != nil { return nil, errors.Wrapf(err, "incorrect value type for stars filter") } sFilter.Stars = stars - case "is-automated": + case define.SearchFilterAutomated: if len(arr) == 2 && arr[1] == "false" { sFilter.IsAutomated = types.OptionalBoolFalse } else { sFilter.IsAutomated = types.OptionalBoolTrue } - case "is-official": + case define.SearchFilterOfficial: if len(arr) == 2 && arr[1] == "false" { sFilter.IsOfficial = types.OptionalBoolFalse } else { diff --git a/vendor/github.com/containers/common/libnetwork/cni/cni_conversion.go b/vendor/github.com/containers/common/libnetwork/cni/cni_conversion.go index bda7ed7d0..96a2a9a4a 100644 --- a/vendor/github.com/containers/common/libnetwork/cni/cni_conversion.go +++ b/vendor/github.com/containers/common/libnetwork/cni/cni_conversion.go @@ -1,5 +1,5 @@ -//go:build linux -// +build linux +//go:build linux || freebsd +// +build linux freebsd package cni diff --git a/vendor/github.com/containers/common/libnetwork/cni/cni_exec.go b/vendor/github.com/containers/common/libnetwork/cni/cni_exec.go index 6bfa8d63b..79d7ef120 100644 --- a/vendor/github.com/containers/common/libnetwork/cni/cni_exec.go +++ b/vendor/github.com/containers/common/libnetwork/cni/cni_exec.go @@ -16,8 +16,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build linux -// +build linux +//go:build linux || freebsd +// +build linux freebsd package cni diff --git a/vendor/github.com/containers/common/libnetwork/cni/cni_types.go b/vendor/github.com/containers/common/libnetwork/cni/cni_types.go index 25cc173a6..a407a8dea 100644 --- a/vendor/github.com/containers/common/libnetwork/cni/cni_types.go +++ b/vendor/github.com/containers/common/libnetwork/cni/cni_types.go @@ -1,5 +1,5 @@ -//go:build linux -// +build linux +//go:build linux || freebsd +// +build linux freebsd package cni diff --git a/vendor/github.com/containers/common/libnetwork/cni/config.go b/vendor/github.com/containers/common/libnetwork/cni/config.go index f6954db05..c86196c17 100644 --- a/vendor/github.com/containers/common/libnetwork/cni/config.go +++ b/vendor/github.com/containers/common/libnetwork/cni/config.go @@ -1,5 +1,5 @@ -//go:build linux -// +build linux +//go:build linux || freebsd +// +build linux freebsd package cni @@ -12,7 +12,6 @@ import ( pkgutil "github.com/containers/common/pkg/util" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/vishvananda/netlink" ) // NetworkCreate will take a partial filled Network and fill the @@ -133,14 +132,7 @@ func (n *cniNetwork) NetworkRemove(nameOrID string) error { // Remove the bridge network interface on the host. if network.libpodNet.Driver == types.BridgeNetworkDriver { - link, err := netlink.LinkByName(network.libpodNet.NetworkInterface) - if err == nil { - err = netlink.LinkDel(link) - // only log the error, it is not fatal - if err != nil { - logrus.Infof("Failed to remove network interface %s: %v", network.libpodNet.NetworkInterface, err) - } - } + deleteLink(network.libpodNet.NetworkInterface) } file := network.filename diff --git a/vendor/github.com/containers/common/libnetwork/cni/config_freebsd.go b/vendor/github.com/containers/common/libnetwork/cni/config_freebsd.go new file mode 100644 index 000000000..ff95c0e17 --- /dev/null +++ b/vendor/github.com/containers/common/libnetwork/cni/config_freebsd.go @@ -0,0 +1,17 @@ +//go:build freebsd +// +build freebsd + +package cni + +import ( + "os/exec" + + "github.com/sirupsen/logrus" +) + +func deleteLink(name string) { + if output, err := exec.Command("ifconfig", name, "destroy").CombinedOutput(); err != nil { + // only log the error, it is not fatal + logrus.Infof("Failed to remove network interface %s: %v: %s", name, err, output) + } +} diff --git a/vendor/github.com/containers/common/libnetwork/cni/config_linux.go b/vendor/github.com/containers/common/libnetwork/cni/config_linux.go new file mode 100644 index 000000000..836fd73bf --- /dev/null +++ b/vendor/github.com/containers/common/libnetwork/cni/config_linux.go @@ -0,0 +1,20 @@ +//go:build linux +// +build linux + +package cni + +import ( + "github.com/sirupsen/logrus" + "github.com/vishvananda/netlink" +) + +func deleteLink(name string) { + link, err := netlink.LinkByName(name) + if err == nil { + err = netlink.LinkDel(link) + // only log the error, it is not fatal + if err != nil { + logrus.Infof("Failed to remove network interface %s: %v", name, err) + } + } +} diff --git a/vendor/github.com/containers/common/libnetwork/cni/network.go b/vendor/github.com/containers/common/libnetwork/cni/network.go index 82b9cbd2e..561f309d0 100644 --- a/vendor/github.com/containers/common/libnetwork/cni/network.go +++ b/vendor/github.com/containers/common/libnetwork/cni/network.go @@ -1,5 +1,5 @@ -//go:build linux -// +build linux +//go:build linux || freebsd +// +build linux freebsd package cni diff --git a/vendor/github.com/containers/common/libnetwork/cni/run.go b/vendor/github.com/containers/common/libnetwork/cni/run.go index c5461d74c..35236cf25 100644 --- a/vendor/github.com/containers/common/libnetwork/cni/run.go +++ b/vendor/github.com/containers/common/libnetwork/cni/run.go @@ -1,5 +1,5 @@ -//go:build linux -// +build linux +//go:build linux || freebsd +// +build linux freebsd package cni @@ -12,13 +12,11 @@ import ( "github.com/containernetworking/cni/libcni" cnitypes "github.com/containernetworking/cni/pkg/types" types040 "github.com/containernetworking/cni/pkg/types/040" - "github.com/containernetworking/plugins/pkg/ns" "github.com/containers/common/libnetwork/internal/util" "github.com/containers/common/libnetwork/types" "github.com/hashicorp/go-multierror" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/vishvananda/netlink" ) // Setup will setup the container network namespace. It returns @@ -36,14 +34,7 @@ func (n *cniNetwork) Setup(namespacePath string, options types.SetupOptions) (ma return nil, err } - // set the loopback adapter up in the container netns - err = ns.WithNetNSPath(namespacePath, func(_ ns.NetNS) error { - link, err := netlink.LinkByName("lo") - if err == nil { - err = netlink.LinkSetUp(link) - } - return err - }) + err = setupLoopback(namespacePath) if err != nil { return nil, errors.Wrapf(err, "failed to set the loopback adapter up") } diff --git a/vendor/github.com/containers/common/libnetwork/cni/run_freebsd.go b/vendor/github.com/containers/common/libnetwork/cni/run_freebsd.go new file mode 100644 index 000000000..c356a864a --- /dev/null +++ b/vendor/github.com/containers/common/libnetwork/cni/run_freebsd.go @@ -0,0 +1,13 @@ +package cni + +import ( + "os/exec" +) + +// FreeBSD vnet adds the lo0 interface automatically - we just need to +// add the default address. Note: this will also add ::1 as a side +// effect. +func setupLoopback(namespacePath string) error { + // The jexec wrapper runs the ifconfig command inside the jail. + return exec.Command("jexec", namespacePath, "ifconfig", "lo0", "inet", "127.0.0.1").Run() +} diff --git a/vendor/github.com/containers/common/libnetwork/cni/run_linux.go b/vendor/github.com/containers/common/libnetwork/cni/run_linux.go new file mode 100644 index 000000000..735e4960e --- /dev/null +++ b/vendor/github.com/containers/common/libnetwork/cni/run_linux.go @@ -0,0 +1,17 @@ +package cni + +import ( + "github.com/containernetworking/plugins/pkg/ns" + "github.com/vishvananda/netlink" +) + +func setupLoopback(namespacePath string) error { + // set the loopback adapter up in the container netns + return ns.WithNetNSPath(namespacePath, func(_ ns.NetNS) error { + link, err := netlink.LinkByName("lo") + if err == nil { + err = netlink.LinkSetUp(link) + } + return err + }) +} diff --git a/vendor/github.com/containers/common/libnetwork/netavark/config.go b/vendor/github.com/containers/common/libnetwork/netavark/config.go index f2c72ab9e..d8843eb2c 100644 --- a/vendor/github.com/containers/common/libnetwork/netavark/config.go +++ b/vendor/github.com/containers/common/libnetwork/netavark/config.go @@ -1,5 +1,5 @@ -//go:build linux -// +build linux +//go:build linux || freebsd +// +build linux freebsd package netavark diff --git a/vendor/github.com/containers/common/libnetwork/netavark/const.go b/vendor/github.com/containers/common/libnetwork/netavark/const.go index 29a7b4f2a..b375acd1b 100644 --- a/vendor/github.com/containers/common/libnetwork/netavark/const.go +++ b/vendor/github.com/containers/common/libnetwork/netavark/const.go @@ -1,5 +1,5 @@ -//go:build linux -// +build linux +//go:build linux || freebsd +// +build linux freebsd package netavark diff --git a/vendor/github.com/containers/common/libnetwork/netavark/exec.go b/vendor/github.com/containers/common/libnetwork/netavark/exec.go index ac87c5438..65dcd5497 100644 --- a/vendor/github.com/containers/common/libnetwork/netavark/exec.go +++ b/vendor/github.com/containers/common/libnetwork/netavark/exec.go @@ -1,5 +1,5 @@ -//go:build linux -// +build linux +//go:build linux || freebsd +// +build linux freebsd package netavark diff --git a/vendor/github.com/containers/common/libnetwork/netavark/ipam.go b/vendor/github.com/containers/common/libnetwork/netavark/ipam.go index 861854351..89820f1d6 100644 --- a/vendor/github.com/containers/common/libnetwork/netavark/ipam.go +++ b/vendor/github.com/containers/common/libnetwork/netavark/ipam.go @@ -1,5 +1,5 @@ -//go:build linux -// +build linux +//go:build linux || freebsd +// +build linux freebsd package netavark diff --git a/vendor/github.com/containers/common/libnetwork/netavark/network.go b/vendor/github.com/containers/common/libnetwork/netavark/network.go index 8e7576a56..0d03cd5e6 100644 --- a/vendor/github.com/containers/common/libnetwork/netavark/network.go +++ b/vendor/github.com/containers/common/libnetwork/netavark/network.go @@ -1,5 +1,5 @@ -//go:build linux -// +build linux +//go:build linux || freebsd +// +build linux freebsd package netavark diff --git a/vendor/github.com/containers/common/libnetwork/netavark/run.go b/vendor/github.com/containers/common/libnetwork/netavark/run.go index c5aa181fd..7f0a84140 100644 --- a/vendor/github.com/containers/common/libnetwork/netavark/run.go +++ b/vendor/github.com/containers/common/libnetwork/netavark/run.go @@ -1,5 +1,5 @@ -//go:build linux -// +build linux +//go:build linux || freebsd +// +build linux freebsd package netavark diff --git a/vendor/github.com/containers/common/libnetwork/network/interface.go b/vendor/github.com/containers/common/libnetwork/network/interface.go index e70f096a4..f41598f77 100644 --- a/vendor/github.com/containers/common/libnetwork/network/interface.go +++ b/vendor/github.com/containers/common/libnetwork/network/interface.go @@ -1,5 +1,5 @@ -//go:build linux -// +build linux +//go:build linux || freebsd +// +build linux freebsd package network @@ -25,14 +25,8 @@ import ( const ( // defaultNetworkBackendFileName is the file name for sentinel file to store the backend defaultNetworkBackendFileName = "defaultNetworkBackend" - // cniConfigDir is the directory where cni configuration is found - cniConfigDir = "/etc/cni/net.d/" // cniConfigDirRootless is the directory in XDG_CONFIG_HOME for cni plugins cniConfigDirRootless = "cni/net.d/" - // netavarkConfigDir is the config directory for the rootful network files - netavarkConfigDir = "/etc/containers/networks" - // netavarkRunDir is the run directory for the rootful temporary network files such as the ipam db - netavarkRunDir = "/run/containers/networks" // netavarkBinary is the name of the netavark binary netavarkBinary = "netavark" diff --git a/vendor/github.com/containers/common/libnetwork/network/interface_freebsd.go b/vendor/github.com/containers/common/libnetwork/network/interface_freebsd.go new file mode 100644 index 000000000..4d60b25c7 --- /dev/null +++ b/vendor/github.com/containers/common/libnetwork/network/interface_freebsd.go @@ -0,0 +1,10 @@ +package network + +const ( + // cniConfigDir is the directory where cni configuration is found + cniConfigDir = "/usr/local/etc/cni/net.d/" + // netavarkConfigDir is the config directory for the rootful network files + netavarkConfigDir = "/usr/local/etc/containers/networks" + // netavarkRunDir is the run directory for the rootful temporary network files such as the ipam db + netavarkRunDir = "/var/run/containers/networks" +) diff --git a/vendor/github.com/containers/common/libnetwork/network/interface_linux.go b/vendor/github.com/containers/common/libnetwork/network/interface_linux.go new file mode 100644 index 000000000..a16194400 --- /dev/null +++ b/vendor/github.com/containers/common/libnetwork/network/interface_linux.go @@ -0,0 +1,10 @@ +package network + +const ( + // cniConfigDir is the directory where cni configuration is found + cniConfigDir = "/etc/cni/net.d/" + // netavarkConfigDir is the config directory for the rootful network files + netavarkConfigDir = "/etc/containers/networks" + // netavarkRunDir is the run directory for the rootful temporary network files such as the ipam db + netavarkRunDir = "/run/containers/networks" +) diff --git a/vendor/github.com/containers/common/pkg/cgroups/cgroups_supported.go b/vendor/github.com/containers/common/pkg/cgroups/cgroups_supported.go index 5c6c199e0..3e7653672 100644 --- a/vendor/github.com/containers/common/pkg/cgroups/cgroups_supported.go +++ b/vendor/github.com/containers/common/pkg/cgroups/cgroups_supported.go @@ -99,7 +99,7 @@ func UserOwnsCurrentSystemdCgroup() (bool, error) { func rmDirRecursively(path string) error { killProcesses := func(signal syscall.Signal) { if signal == unix.SIGKILL { - if err := ioutil.WriteFile(filepath.Join(path, "cgroup.kill"), []byte("1"), 0600); err == nil { + if err := ioutil.WriteFile(filepath.Join(path, "cgroup.kill"), []byte("1"), 0o600); err == nil { return } } diff --git a/vendor/github.com/containers/common/pkg/completion/command.go b/vendor/github.com/containers/common/pkg/completion/command.go new file mode 100644 index 000000000..2deb58757 --- /dev/null +++ b/vendor/github.com/containers/common/pkg/completion/command.go @@ -0,0 +1,96 @@ +package completion + +import ( + "fmt" + "io" + "os" + "strings" + + "github.com/spf13/cobra" +) + +const ( + completionDescription = `Generate shell autocompletions. +Valid arguments are bash, zsh, fish and powershell.` + + bash = "bash" + zsh = "zsh" + fish = "fish" + powershell = "powershell" +) + +var ( + file string + noDesc bool + shells = []string{bash, zsh, fish, powershell} +) + +// AddCompletionCommand adds the completion command to the given command which should be the root command. +// This command can be used the generate the cobra shell completion scripts for bash, zsh, fish and powershell. +func AddCompletionCommand(rootCmd *cobra.Command) { + completionCmd := &cobra.Command{ + Use: fmt.Sprintf("completion [options] {%s}", strings.Join(shells, "|")), + Short: "Generate shell autocompletions", + Long: completionDescription, + ValidArgs: shells, + Args: cobra.ExactValidArgs(1), + RunE: completion, + Example: fmt.Sprintf(`%[1]s completion bash + %[1]s completion zsh -f _%[1]s + %[1]s completion fish --no-desc`, rootCmd.Name()), + // don't show this command to users + Hidden: true, + } + + flags := completionCmd.Flags() + fileFlagName := "file" + flags.StringVarP(&file, fileFlagName, "f", "", "Output the completion to file rather than stdout.") + _ = completionCmd.RegisterFlagCompletionFunc(fileFlagName, AutocompleteDefault) + + flags.BoolVar(&noDesc, "no-desc", false, "Don't include descriptions in the completion output.") + + rootCmd.AddCommand(completionCmd) +} + +func completion(cmd *cobra.Command, args []string) error { + var w io.Writer + + if file != "" { + file, err := os.Create(file) + if err != nil { + return err + } + defer file.Close() + w = file + } else { + w = os.Stdout + } + + var err error + switch args[0] { + case bash: + err = cmd.Root().GenBashCompletionV2(w, !noDesc) + case zsh: + if noDesc { + err = cmd.Root().GenZshCompletionNoDesc(w) + } else { + err = cmd.Root().GenZshCompletion(w) + } + case fish: + err = cmd.Root().GenFishCompletion(w, !noDesc) + case powershell: + if noDesc { + err = cmd.Root().GenPowerShellCompletion(w) + } else { + err = cmd.Root().GenPowerShellCompletionWithDesc(w) + } + } + if err != nil { + return err + } + + _, err = io.WriteString(w, fmt.Sprintf( + "# This file is generated with %q; DO NOT EDIT!\n", cmd.CommandPath(), + )) + return err +} diff --git a/vendor/github.com/containers/common/pkg/config/config.go b/vendor/github.com/containers/common/pkg/config/config.go index 25572968f..e3d19ee88 100644 --- a/vendor/github.com/containers/common/pkg/config/config.go +++ b/vendor/github.com/containers/common/pkg/config/config.go @@ -1,6 +1,7 @@ package config import ( + "errors" "fmt" "io/fs" "os" @@ -13,10 +14,10 @@ import ( "github.com/BurntSushi/toml" "github.com/containers/common/libnetwork/types" "github.com/containers/common/pkg/capabilities" + "github.com/containers/common/pkg/util" "github.com/containers/storage/pkg/unshare" units "github.com/docker/go-units" selinux "github.com/opencontainers/selinux/go-selinux" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -46,6 +47,8 @@ const ( BoltDBStateStore RuntimeStateStore = iota ) +var validImageVolumeModes = []string{"bind", "tmpfs", "ignore"} + // ProxyEnv is a list of Proxy Environment variables var ProxyEnv = []string{ "http_proxy", @@ -77,7 +80,6 @@ type Config struct { // ContainersConfig represents the "containers" TOML config table // containers global options for containers tools type ContainersConfig struct { - // Devices to add to all containers Devices []string `toml:"devices,omitempty"` @@ -294,6 +296,10 @@ type EngineConfig struct { // Building/committing defaults to OCI. ImageDefaultFormat string `toml:"image_default_format,omitempty"` + // ImageVolumeMode Tells container engines how to handle the builtin + // image volumes. Acceptable values are "bind", "tmpfs", and "ignore". + ImageVolumeMode string `toml:"image_volume_mode,omitempty"` + // InfraCommand is the command run to start up a pod infra container. InfraCommand string `toml:"infra_command,omitempty"` @@ -604,14 +610,14 @@ func NewConfig(userConfigPath string) (*Config, error) { // Now, gather the system configs and merge them as needed. configs, err := systemConfigs() if err != nil { - return nil, errors.Wrap(err, "finding config on system") + return nil, fmt.Errorf("finding config on system: %w", err) } for _, path := range configs { // Merge changes in later configs with the previous configs. // Each config file that specified fields, will override the // previous fields. if err = readConfigFromFile(path, config); err != nil { - return nil, errors.Wrapf(err, "reading system config %q", path) + return nil, fmt.Errorf("reading system config %q: %w", path, err) } logrus.Debugf("Merged system config %q", path) logrus.Tracef("%+v", config) @@ -624,7 +630,7 @@ func NewConfig(userConfigPath string) (*Config, error) { // readConfigFromFile reads in container config in the specified // file and then merge changes with the current default. if err = readConfigFromFile(userConfigPath, config); err != nil { - return nil, errors.Wrapf(err, "reading user config %q", userConfigPath) + return nil, fmt.Errorf("reading user config %q: %w", userConfigPath, err) } logrus.Debugf("Merged user config %q", userConfigPath) logrus.Tracef("%+v", config) @@ -650,7 +656,7 @@ func readConfigFromFile(path string, config *Config) error { logrus.Tracef("Reading configuration file %q", path) meta, err := toml.DecodeFile(path, config) if err != nil { - return errors.Wrapf(err, "decode configuration %v", path) + return fmt.Errorf("decode configuration %v: %w", path, err) } keys := meta.Undecoded() if len(keys) > 0 { @@ -704,7 +710,7 @@ func systemConfigs() ([]string, error) { path := os.Getenv("CONTAINERS_CONF") if path != "" { if _, err := os.Stat(path); err != nil { - return nil, errors.Wrap(err, "CONTAINERS_CONF file") + return nil, fmt.Errorf("CONTAINERS_CONF file: %w", err) } return append(configs, path), nil } @@ -779,7 +785,7 @@ func (c *Config) addCAPPrefix() { // Validate is the main entry point for library configuration validation. func (c *Config) Validate() error { if err := c.Containers.Validate(); err != nil { - return errors.Wrap(err, "validating containers config") + return fmt.Errorf("validating containers config: %w", err) } if !c.Containers.EnableLabeling { @@ -787,11 +793,11 @@ func (c *Config) Validate() error { } if err := c.Engine.Validate(); err != nil { - return errors.Wrap(err, "validating engine configs") + return fmt.Errorf("validating engine configs: %w", err) } if err := c.Network.Validate(); err != nil { - return errors.Wrap(err, "validating network configs") + return fmt.Errorf("validating network configs %w", err) } return nil @@ -821,11 +827,14 @@ func (c *EngineConfig) Validate() error { return err } + if err := ValidateImageVolumeMode(c.ImageVolumeMode); err != nil { + return err + } // Check if the pullPolicy from containers.conf is valid // if it is invalid returns the error pullPolicy := strings.ToLower(c.PullPolicy) if _, err := ValidatePullPolicy(pullPolicy); err != nil { - return errors.Wrapf(err, "invalid pull type from containers.conf %q", c.PullPolicy) + return fmt.Errorf("invalid pull type from containers.conf %q: %w", c.PullPolicy, err) } return nil } @@ -851,11 +860,11 @@ func (c *ContainersConfig) Validate() error { } if c.LogSizeMax >= 0 && c.LogSizeMax < OCIBufSize { - return errors.Errorf("log size max should be negative or >= %d", OCIBufSize) + return fmt.Errorf("log size max should be negative or >= %d", OCIBufSize) } if _, err := units.FromHumanSize(c.ShmSize); err != nil { - return errors.Errorf("invalid --shm-size %s, %q", c.ShmSize, err) + return fmt.Errorf("invalid --shm-size %s, %q", c.ShmSize, err) } return nil @@ -869,11 +878,11 @@ func (c *NetworkConfig) Validate() error { if &c.DefaultSubnetPools != &DefaultSubnetPools { for _, pool := range c.DefaultSubnetPools { if pool.Base.IP.To4() == nil { - return errors.Errorf("invalid subnet pool ip %q", pool.Base.IP) + return fmt.Errorf("invalid subnet pool ip %q", pool.Base.IP) } ones, _ := pool.Base.IPNet.Mask.Size() if ones > pool.Size { - return errors.Errorf("invalid subnet pool, size is bigger than subnet %q", &pool.Base.IPNet) + return fmt.Errorf("invalid subnet pool, size is bigger than subnet %q", &pool.Base.IPNet) } if pool.Size > 32 { return errors.New("invalid subnet pool size, must be between 0-32") @@ -891,7 +900,7 @@ func (c *NetworkConfig) Validate() error { } } - return errors.Errorf("invalid cni_plugin_dirs: %s", strings.Join(c.CNIPluginDirs, ",")) + return fmt.Errorf("invalid cni_plugin_dirs: %s", strings.Join(c.CNIPluginDirs, ",")) } // FindConmon iterates over (*Config).ConmonPath and returns the path @@ -928,14 +937,12 @@ func (c *Config) FindConmon() (string, error) { } if foundOutdatedConmon { - return "", errors.Wrapf(ErrConmonOutdated, - "please update to v%d.%d.%d or later", - _conmonMinMajorVersion, _conmonMinMinorVersion, _conmonMinPatchVersion) + return "", fmt.Errorf("please update to v%d.%d.%d or later: %w", + _conmonMinMajorVersion, _conmonMinMinorVersion, _conmonMinPatchVersion, ErrConmonOutdated) } - return "", errors.Wrapf(ErrInvalidArg, - "could not find a working conmon binary (configured options: %v)", - c.Engine.ConmonPath) + return "", fmt.Errorf("could not find a working conmon binary (configured options: %v: %w)", + c.Engine.ConmonPath, ErrInvalidArg) } // GetDefaultEnv returns the environment variables for the container. @@ -992,7 +999,7 @@ func Device(device string) (src, dst, permissions string, err error) { switch len(split) { case 3: if !IsValidDeviceMode(split[2]) { - return "", "", "", errors.Errorf("invalid device mode: %s", split[2]) + return "", "", "", fmt.Errorf("invalid device mode: %s", split[2]) } permissions = split[2] fallthrough @@ -1001,18 +1008,18 @@ func Device(device string) (src, dst, permissions string, err error) { permissions = split[1] } else { if split[1] == "" || split[1][0] != '/' { - return "", "", "", errors.Errorf("invalid device mode: %s", split[1]) + return "", "", "", fmt.Errorf("invalid device mode: %s", split[1]) } dst = split[1] } fallthrough case 1: if !strings.HasPrefix(split[0], "/dev/") { - return "", "", "", errors.Errorf("invalid device mode: %s", split[0]) + return "", "", "", fmt.Errorf("invalid device mode: %s", split[0]) } src = split[0] default: - return "", "", "", errors.Errorf("invalid device specification: %s", device) + return "", "", "", fmt.Errorf("invalid device specification: %s", device) } if dst == "" { @@ -1195,14 +1202,14 @@ func (c *Config) ActiveDestination() (uri, identity string, err error) { case connEnv != "": d, found := c.Engine.ServiceDestinations[connEnv] if !found { - return "", "", errors.Errorf("environment variable CONTAINER_CONNECTION=%q service destination not found", connEnv) + return "", "", fmt.Errorf("environment variable CONTAINER_CONNECTION=%q service destination not found", connEnv) } return d.URI, d.Identity, nil case c.Engine.ActiveService != "": d, found := c.Engine.ServiceDestinations[c.Engine.ActiveService] if !found { - return "", "", errors.Errorf("%q service destination not found", c.Engine.ActiveService) + return "", "", fmt.Errorf("%q service destination not found", c.Engine.ActiveService) } return d.URI, d.Identity, nil case c.Engine.RemoteURI != "": @@ -1232,9 +1239,9 @@ func (c *Config) FindHelperBinary(name string, searchPATH bool) (string, error) } configHint := "To resolve this error, set the helper_binaries_dir key in the `[engine]` section of containers.conf to the directory containing your helper binaries." if len(c.Engine.HelperBinariesDir) == 0 { - return "", errors.Errorf("could not find %q because there are no helper binary directories configured. %s", name, configHint) + return "", fmt.Errorf("could not find %q because there are no helper binary directories configured. %s", name, configHint) } - return "", errors.Errorf("could not find %q in one of %v. %s", name, c.Engine.HelperBinariesDir, configHint) + return "", fmt.Errorf("could not find %q in one of %v. %s", name, c.Engine.HelperBinariesDir, configHint) } // ImageCopyTmpDir default directory to store temporary image files during copy @@ -1253,7 +1260,7 @@ func (c *Config) ImageCopyTmpDir() (string, error) { } } - return "", errors.Errorf("invalid image_copy_tmp_dir value %q (relative paths are not accepted)", c.Engine.ImageCopyTmpDir) + return "", fmt.Errorf("invalid image_copy_tmp_dir value %q (relative paths are not accepted)", c.Engine.ImageCopyTmpDir) } // setupEnv sets the environment variables for the engine @@ -1305,3 +1312,14 @@ func (e eventsLogMaxSize) MarshalText() ([]byte, error) { } return []byte(fmt.Sprintf("%d", e)), nil } + +func ValidateImageVolumeMode(mode string) error { + if mode == "" { + return nil + } + if util.StringInSlice(mode, validImageVolumeModes) { + return nil + } + + return fmt.Errorf("invalid image volume mode %q required value: %s", mode, strings.Join(validImageVolumeModes, ", ")) +} diff --git a/vendor/github.com/containers/common/pkg/config/config_local.go b/vendor/github.com/containers/common/pkg/config/config_local.go index bfb967582..bc8ddc655 100644 --- a/vendor/github.com/containers/common/pkg/config/config_local.go +++ b/vendor/github.com/containers/common/pkg/config/config_local.go @@ -4,6 +4,7 @@ package config import ( + "fmt" "os" "path/filepath" "regexp" @@ -11,7 +12,6 @@ import ( "syscall" units "github.com/docker/go-units" - "github.com/pkg/errors" ) // isDirectory tests whether the given path exists and is a directory. It @@ -44,13 +44,13 @@ func (c *EngineConfig) validatePaths() error { // shift between runs or even parts of the program. - The OCI runtime // uses a different working directory than we do, for example. if c.StaticDir != "" && !filepath.IsAbs(c.StaticDir) { - return errors.Errorf("static directory must be an absolute path - instead got %q", c.StaticDir) + return fmt.Errorf("static directory must be an absolute path - instead got %q", c.StaticDir) } if c.TmpDir != "" && !filepath.IsAbs(c.TmpDir) { - return errors.Errorf("temporary directory must be an absolute path - instead got %q", c.TmpDir) + return fmt.Errorf("temporary directory must be an absolute path - instead got %q", c.TmpDir) } if c.VolumePath != "" && !filepath.IsAbs(c.VolumePath) { - return errors.Errorf("volume path must be an absolute path - instead got %q", c.VolumePath) + return fmt.Errorf("volume path must be an absolute path - instead got %q", c.VolumePath) } return nil } @@ -69,7 +69,7 @@ func (c *ContainersConfig) validateUlimits() error { for _, u := range c.DefaultUlimits { ul, err := units.ParseUlimit(u) if err != nil { - return errors.Wrapf(err, "unrecognized ulimit %s", u) + return fmt.Errorf("unrecognized ulimit %s: %w", u, err) } _, err = ul.GetRlimit() if err != nil { @@ -97,7 +97,7 @@ func (c *ContainersConfig) validateTZ() error { } } - return errors.Errorf( + return fmt.Errorf( "find timezone %s in paths: %s", c.TZ, strings.Join(lookupPaths, ", "), ) @@ -106,7 +106,7 @@ func (c *ContainersConfig) validateTZ() error { func (c *ContainersConfig) validateUmask() error { validUmask := regexp.MustCompile(`^[0-7]{1,4}$`) if !validUmask.MatchString(c.Umask) { - return errors.Errorf("not a valid umask %s", c.Umask) + return fmt.Errorf("not a valid umask %s", c.Umask) } return nil } diff --git a/vendor/github.com/containers/common/pkg/config/containers.conf b/vendor/github.com/containers/common/pkg/config/containers.conf index a4e755a66..8fd951c4a 100644 --- a/vendor/github.com/containers/common/pkg/config/containers.conf +++ b/vendor/github.com/containers/common/pkg/config/containers.conf @@ -434,6 +434,16 @@ default_sysctls = [ # #image_parallel_copies = 0 +# Tells container engines how to handle the builtin image volumes. +# * bind: An anonymous named volume will be created and mounted +# into the container. +# * tmpfs: The volume is mounted onto the container as a tmpfs, +# which allows users to create content that disappears when +# the container is stopped. +# * ignore: All volumes are just ignored and no action is taken. +# +#image_volume_mode = "" + # Default command to run the infra container # #infra_command = "/pause" diff --git a/vendor/github.com/containers/common/pkg/config/default.go b/vendor/github.com/containers/common/pkg/config/default.go index d988d3b1c..f381818f1 100644 --- a/vendor/github.com/containers/common/pkg/config/default.go +++ b/vendor/github.com/containers/common/pkg/config/default.go @@ -2,6 +2,7 @@ package config import ( "bytes" + "errors" "fmt" "net" "os" @@ -19,7 +20,6 @@ import ( "github.com/containers/storage/pkg/unshare" "github.com/containers/storage/types" "github.com/opencontainers/selinux/go-selinux" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -35,7 +35,7 @@ const ( // _conmonVersionFormatErr is used when the expected versio-format of conmon // has changed. - _conmonVersionFormatErr = "conmon version changed format" + _conmonVersionFormatErr = "conmon version changed format: %w" // _defaultGraphRoot points to the default path of the graph root. _defaultGraphRoot = "/var/lib/containers/storage" @@ -43,26 +43,29 @@ const ( // _defaultTransport is a prefix that we apply to an image name to check // docker hub first for the image. _defaultTransport = "docker://" + + // _defaultImageVolumeMode is a mode to handle built-in image volumes. + _defaultImageVolumeMode = "bind" ) var ( - // DefaultInitPath is the default path to the container-init binary + // DefaultInitPath is the default path to the container-init binary. DefaultInitPath = "/usr/libexec/podman/catatonit" - // DefaultInfraImage to use for infra container + // DefaultInfraImage is the default image to run as infrastructure containers in pods. DefaultInfraImage = "" - // DefaultRootlessSHMLockPath is the default path for rootless SHM locks + // DefaultRootlessSHMLockPath is the default path for rootless SHM locks. DefaultRootlessSHMLockPath = "/libpod_rootless_lock" // DefaultDetachKeys is the default keys sequence for detaching a - // container + // container. DefaultDetachKeys = "ctrl-p,ctrl-q" // ErrConmonOutdated indicates the version of conmon found (whether via the configuration or $PATH) - // is out of date for the current podman version + // is out of date for the current podman version. ErrConmonOutdated = errors.New("outdated conmon version") - // ErrInvalidArg indicates that an invalid argument was passed + // ErrInvalidArg indicates that an invalid argument was passed. ErrInvalidArg = errors.New("invalid argument") - // DefaultHooksDirs defines the default hooks directory + // DefaultHooksDirs defines the default hooks directory. DefaultHooksDirs = []string{"/usr/share/containers/oci/hooks.d"} - // DefaultCapabilities for the default_capabilities option in the containers.conf file + // DefaultCapabilities is the default for the default_capabilities option in the containers.conf file. DefaultCapabilities = []string{ "CAP_AUDIT_WRITE", "CAP_CHOWN", @@ -80,7 +83,7 @@ var ( "CAP_SYS_CHROOT", } - // It may seem a bit unconventional, but it is necessary to do so + // Search these locations in which CNIPlugins can be installed. DefaultCNIPluginDirs = []string{ "/usr/local/libexec/cni", "/usr/libexec/cni", @@ -98,7 +101,7 @@ var ( } // additionalHelperBinariesDir is an extra helper binaries directory that // should be set during link-time, if different packagers put their - // helper binary in a different location + // helper binary in a different location. additionalHelperBinariesDir string ) @@ -118,13 +121,13 @@ const ( // InstallPrefix is the prefix where podman will be installed. // It can be overridden at build time. _installPrefix = "/usr" - // CgroupfsCgroupsManager represents cgroupfs native cgroup manager + // CgroupfsCgroupsManager represents cgroupfs native cgroup manager. CgroupfsCgroupsManager = "cgroupfs" // DefaultApparmorProfile specifies the default apparmor profile for the container. DefaultApparmorProfile = apparmor.Profile - // DefaultHostsFile is the default path to the hosts file + // DefaultHostsFile is the default path to the hosts file. DefaultHostsFile = "/etc/hosts" - // SystemdCgroupsManager represents systemd native cgroup manager + // SystemdCgroupsManager represents systemd native cgroup manager. SystemdCgroupsManager = "systemd" // DefaultLogSizeMax is the default value for the maximum log size // allowed for a container. Negative values mean that no limit is imposed. @@ -133,9 +136,9 @@ const ( // before rotation. DefaultEventsLogSizeMax = uint64(1000000) // DefaultPidsLimit is the default value for maximum number of processes - // allowed inside a container + // allowed inside a container. DefaultPidsLimit = 2048 - // DefaultPullPolicy pulls the image if it does not exist locally + // DefaultPullPolicy pulls the image if it does not exist locally. DefaultPullPolicy = "missing" // DefaultSignaturePolicyPath is the default value for the // policy.json file. @@ -146,11 +149,11 @@ const ( // DefaultRootlessSignaturePolicyPath is the location within // XDG_CONFIG_HOME of the rootless policy.json file. DefaultRootlessSignaturePolicyPath = "containers/policy.json" - // DefaultShmSize default value + // DefaultShmSize is the default upper limit on the size of tmpfs mounts. DefaultShmSize = "65536k" - // DefaultUserNSSize default value + // DefaultUserNSSize indicates the default number of UIDs allocated for user namespace within a container. DefaultUserNSSize = 65536 - // OCIBufSize limits maximum LogSizeMax + // OCIBufSize limits maximum LogSizeMax. OCIBufSize = 8192 // SeccompOverridePath if this exists it overrides the default seccomp path. SeccompOverridePath = _etcDir + "/containers/seccomp.json" @@ -158,7 +161,7 @@ const ( SeccompDefaultPath = _installPrefix + "/share/containers/seccomp.json" ) -// DefaultConfig defines the default values from containers.conf +// DefaultConfig defines the default values from containers.conf. func DefaultConfig() (*Config, error) { defaultEngineConfig, err := defaultConfigFromMemory() if err != nil { @@ -294,6 +297,7 @@ func defaultConfigFromMemory() (*EngineConfig, error) { } c.HooksDir = DefaultHooksDirs c.ImageDefaultTransport = _defaultTransport + c.ImageVolumeMode = _defaultImageVolumeMode c.StateType = BoltDBStateStore c.ImageBuildFormat = "oci" @@ -350,7 +354,7 @@ func defaultConfigFromMemory() (*EngineConfig, error) { "/usr/local/bin/krun", }, } - // Needs to be called after populating c.OCIRuntimes + // Needs to be called after populating c.OCIRuntimes. c.OCIRuntime = c.findRuntime() c.ConmonEnvVars = []string{ @@ -411,8 +415,8 @@ func defaultTmpDir() (string, error) { if !os.IsExist(err) { return "", err } else if err := os.Chmod(libpodRuntimeDir, 0o700|os.ModeSticky); err != nil { - // The directory already exist, just set the sticky bit - return "", errors.Wrap(err, "set sticky bit on") + // The directory already exists, so we try to make sure it's private and has the sticky bit set on it. + return "", fmt.Errorf("set sticky bit on: %w", err) } } return filepath.Join(libpodRuntimeDir, "tmp"), nil @@ -435,7 +439,7 @@ func probeConmon(conmonBinary string) error { } major, err := strconv.Atoi(matches[1]) if err != nil { - return errors.Wrap(err, _conmonVersionFormatErr) + return fmt.Errorf(_conmonVersionFormatErr, err) } if major < _conmonMinMajorVersion { return ErrConmonOutdated @@ -446,7 +450,7 @@ func probeConmon(conmonBinary string) error { minor, err := strconv.Atoi(matches[2]) if err != nil { - return errors.Wrap(err, _conmonVersionFormatErr) + return fmt.Errorf(_conmonVersionFormatErr, err) } if minor < _conmonMinMinorVersion { return ErrConmonOutdated @@ -457,7 +461,7 @@ func probeConmon(conmonBinary string) error { patch, err := strconv.Atoi(matches[3]) if err != nil { - return errors.Wrap(err, _conmonVersionFormatErr) + return fmt.Errorf(_conmonVersionFormatErr, err) } if patch < _conmonMinPatchVersion { return ErrConmonOutdated @@ -469,7 +473,7 @@ func probeConmon(conmonBinary string) error { return nil } -// NetNS returns the default network namespace +// NetNS returns the default network namespace. func (c *Config) NetNS() string { return c.Containers.NetNS } @@ -478,7 +482,7 @@ func (c EngineConfig) EventsLogMaxSize() uint64 { return uint64(c.EventsLogFileMaxSize) } -// SecurityOptions returns the default security options +// SecurityOptions returns the default security options. func (c *Config) SecurityOptions() []string { securityOpts := []string{} if c.Containers.SeccompProfile != "" && c.Containers.SeccompProfile != SeccompDefaultPath { @@ -493,82 +497,82 @@ func (c *Config) SecurityOptions() []string { return securityOpts } -// Sysctls returns the default sysctls +// Sysctls returns the default sysctls to set in containers. func (c *Config) Sysctls() []string { return c.Containers.DefaultSysctls } -// Volumes returns the default additional volumes for containersvolumes +// Volumes returns the default set of volumes that should be mounted in containers. func (c *Config) Volumes() []string { return c.Containers.Volumes } -// Devices returns the default additional devices for containers +// Devices returns the default additional devices for containers. func (c *Config) Devices() []string { return c.Containers.Devices } -// DNSServers returns the default DNS servers to add to resolv.conf in containers +// DNSServers returns the default DNS servers to add to resolv.conf in containers. func (c *Config) DNSServers() []string { return c.Containers.DNSServers } -// DNSSerches returns the default DNS searches to add to resolv.conf in containers +// DNSSerches returns the default DNS searches to add to resolv.conf in containers. func (c *Config) DNSSearches() []string { return c.Containers.DNSSearches } -// DNSOptions returns the default DNS options to add to resolv.conf in containers +// DNSOptions returns the default DNS options to add to resolv.conf in containers. func (c *Config) DNSOptions() []string { return c.Containers.DNSOptions } -// Env returns the default additional environment variables to add to containers +// Env returns the default additional environment variables to add to containers. func (c *Config) Env() []string { return c.Containers.Env } -// InitPath returns the default init path to add to containers +// InitPath returns location where init program added to containers when users specify the --init flag. func (c *Config) InitPath() string { return c.Containers.InitPath } -// IPCNS returns the default IPC Namespace configuration to run containers with +// IPCNS returns the default IPC Namespace configuration to run containers with. func (c *Config) IPCNS() string { return c.Containers.IPCNS } -// PIDNS returns the default PID Namespace configuration to run containers with +// PIDNS returns the default PID Namespace configuration to run containers with. func (c *Config) PidNS() string { return c.Containers.PidNS } -// CgroupNS returns the default Cgroup Namespace configuration to run containers with +// CgroupNS returns the default Cgroup Namespace configuration to run containers with. func (c *Config) CgroupNS() string { return c.Containers.CgroupNS } -// Cgroups returns whether to containers with cgroup confinement +// Cgroups returns whether to run containers in their own control groups, as configured by the "cgroups" setting in containers.conf. func (c *Config) Cgroups() string { return c.Containers.Cgroups } -// UTSNS returns the default UTS Namespace configuration to run containers with +// UTSNS returns the default UTS Namespace configuration to run containers with. func (c *Config) UTSNS() string { return c.Containers.UTSNS } -// ShmSize returns the default size for temporary file systems to use in containers +// ShmSize returns the default size for temporary file systems to use in containers. func (c *Config) ShmSize() string { return c.Containers.ShmSize } -// Ulimits returns the default ulimits to use in containers +// Ulimits returns the default ulimits to use in containers. func (c *Config) Ulimits() []string { return c.Containers.DefaultUlimits } -// PidsLimit returns the default maximum number of pids to use in containers +// PidsLimit returns the default maximum number of pids to use in containers. func (c *Config) PidsLimit() int64 { if unshare.IsRootless() { if c.Engine.CgroupManager != SystemdCgroupsManager { @@ -583,12 +587,12 @@ func (c *Config) PidsLimit() int64 { return c.Containers.PidsLimit } -// DetachKeys returns the default detach keys to detach from a container +// DetachKeys returns the default detach keys to detach from a container. func (c *Config) DetachKeys() string { return c.Engine.DetachKeys } -// Tz returns the timezone in the container +// TZ returns the timezone to set in containers. func (c *Config) TZ() string { return c.Containers.TZ } @@ -598,17 +602,17 @@ func (c *Config) Umask() string { } // LogDriver returns the logging driver to be used -// currently k8s-file or journald +// currently k8s-file or journald. func (c *Config) LogDriver() string { return c.Containers.LogDriver } -// MachineEnabled returns if podman is running inside a VM or not +// MachineEnabled returns if podman is running inside a VM or not. func (c *Config) MachineEnabled() bool { return c.Engine.MachineEnabled } -// MachineVolumes returns volumes to mount into the VM +// MachineVolumes returns volumes to mount into the VM. func (c *Config) MachineVolumes() ([]string, error) { return machineVolumes(c.Machine.Volumes) } @@ -619,10 +623,10 @@ func machineVolumes(volumes []string) ([]string, error) { vol := os.ExpandEnv(v) split := strings.Split(vol, ":") if len(split) < 2 || len(split) > 3 { - return nil, errors.Errorf("invalid machine volume %s, 2 or 3 fields required", v) + return nil, fmt.Errorf("invalid machine volume %s, 2 or 3 fields required", v) } if split[0] == "" || split[1] == "" { - return nil, errors.Errorf("invalid machine volume %s, fields must container data", v) + return nil, fmt.Errorf("invalid machine volume %s, fields must container data", v) } translatedVolumes = append(translatedVolumes, vol) } diff --git a/vendor/github.com/containers/common/pkg/config/pull_policy.go b/vendor/github.com/containers/common/pkg/config/pull_policy.go index 8c1f0ec29..c85227fe4 100644 --- a/vendor/github.com/containers/common/pkg/config/pull_policy.go +++ b/vendor/github.com/containers/common/pkg/config/pull_policy.go @@ -2,8 +2,6 @@ package config import ( "fmt" - - "github.com/pkg/errors" ) // PullPolicy determines how and which images are being pulled from a container @@ -63,7 +61,7 @@ func (p PullPolicy) Validate() error { case PullPolicyAlways, PullPolicyMissing, PullPolicyNewer, PullPolicyNever: return nil default: - return errors.Errorf("unsupported pull policy %d", p) + return fmt.Errorf("unsupported pull policy %d", p) } } @@ -85,7 +83,7 @@ func ParsePullPolicy(s string) (PullPolicy, error) { case "never", "Never": return PullPolicyNever, nil default: - return PullPolicyUnsupported, errors.Errorf("unsupported pull policy %q", s) + return PullPolicyUnsupported, fmt.Errorf("unsupported pull policy %q", s) } } diff --git a/vendor/github.com/containers/image/v5/copy/copy.go b/vendor/github.com/containers/image/v5/copy/copy.go index d28cc4a3f..123c23e02 100644 --- a/vendor/github.com/containers/image/v5/copy/copy.go +++ b/vendor/github.com/containers/image/v5/copy/copy.go @@ -305,7 +305,7 @@ func Image(ctx context.Context, policyContext *signature.PolicyContext, destRef, unparsedInstance := image.UnparsedInstance(rawSource, &instanceDigest) if copiedManifest, _, _, err = c.copyOneImage(ctx, policyContext, options, unparsedToplevel, unparsedInstance, nil); err != nil { - return nil, err + return nil, errors.Wrap(err, "copying system image from manifest list") } } else { /* options.ImageListSelection == CopyAllImages or options.ImageListSelection == CopySpecificImages, */ // If we were asked to copy multiple images and can't, that's an error. @@ -501,7 +501,7 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur unparsedInstance := image.UnparsedInstance(c.rawSource, &instanceDigest) updatedManifest, updatedManifestType, updatedManifestDigest, err := c.copyOneImage(ctx, policyContext, options, unparsedToplevel, unparsedInstance, &instanceDigest) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "copying image %d/%d from manifest list", instancesCopied+1, imagesToCopy) } instancesCopied++ // Record the result of a possible conversion here. diff --git a/vendor/github.com/containers/image/v5/image/docker_list.go b/vendor/github.com/containers/image/v5/image/docker_list.go index 4fe84413c..af78ac1df 100644 --- a/vendor/github.com/containers/image/v5/image/docker_list.go +++ b/vendor/github.com/containers/image/v5/image/docker_list.go @@ -19,7 +19,7 @@ func manifestSchema2FromManifestList(ctx context.Context, sys *types.SystemConte } manblob, mt, err := src.GetManifest(ctx, &targetManifestDigest) if err != nil { - return nil, errors.Wrapf(err, "loading manifest for target platform") + return nil, errors.Wrapf(err, "fetching target platform image selected from manifest list") } matches, err := manifest.MatchesDigest(manblob, targetManifestDigest) diff --git a/vendor/github.com/containers/image/v5/image/oci_index.go b/vendor/github.com/containers/image/v5/image/oci_index.go index 4e6ca879a..d6e6685b1 100644 --- a/vendor/github.com/containers/image/v5/image/oci_index.go +++ b/vendor/github.com/containers/image/v5/image/oci_index.go @@ -19,7 +19,7 @@ func manifestOCI1FromImageIndex(ctx context.Context, sys *types.SystemContext, s } manblob, mt, err := src.GetManifest(ctx, &targetManifestDigest) if err != nil { - return nil, errors.Wrapf(err, "loading manifest for target platform") + return nil, errors.Wrapf(err, "fetching target platform image selected from image index") } matches, err := manifest.MatchesDigest(manblob, targetManifestDigest) diff --git a/vendor/github.com/proglottis/gpgme/gpgme.go b/vendor/github.com/proglottis/gpgme/gpgme.go index 9833057a6..82effbd9e 100644 --- a/vendor/github.com/proglottis/gpgme/gpgme.go +++ b/vendor/github.com/proglottis/gpgme/gpgme.go @@ -1,6 +1,7 @@ // Package gpgme provides a Go wrapper for the GPGME library package gpgme +// #cgo pkg-config: gpgme // #cgo LDFLAGS: -lgpgme -lassuan -lgpg-error // #cgo CPPFLAGS: -D_FILE_OFFSET_BITS=64 // #include <stdlib.h> diff --git a/vendor/modules.txt b/vendor/modules.txt index c73f11330..cababae5f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -109,9 +109,10 @@ github.com/containers/buildah/pkg/rusage github.com/containers/buildah/pkg/sshagent github.com/containers/buildah/pkg/util github.com/containers/buildah/util -# github.com/containers/common v0.48.1-0.20220512112240-7536bf6ff9b1 +# github.com/containers/common v0.48.1-0.20220523155016-2fd37da97824 ## explicit github.com/containers/common/libimage +github.com/containers/common/libimage/define github.com/containers/common/libimage/manifests github.com/containers/common/libnetwork/cni github.com/containers/common/libnetwork/etchosts @@ -155,7 +156,7 @@ github.com/containers/common/version # github.com/containers/conmon v2.0.20+incompatible ## explicit github.com/containers/conmon/runner/config -# github.com/containers/image/v5 v5.21.2-0.20220511203756-fe4fd4ed8be4 +# github.com/containers/image/v5 v5.21.2-0.20220519193817-1e26896b8059 ## explicit github.com/containers/image/v5/copy github.com/containers/image/v5/directory @@ -594,7 +595,7 @@ github.com/pkg/errors # github.com/pmezard/go-difflib v1.0.0 ## explicit github.com/pmezard/go-difflib/difflib -# github.com/proglottis/gpgme v0.1.1 +# github.com/proglottis/gpgme v0.1.2 github.com/proglottis/gpgme # github.com/prometheus/client_golang v1.11.1 github.com/prometheus/client_golang/prometheus |