diff options
34 files changed, 2619 insertions, 372 deletions
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go index 2ce3e8e68..51474471b 100644 --- a/libpod/container_inspect.go +++ b/libpod/container_inspect.go @@ -8,6 +8,7 @@ import ( "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/driver" "github.com/containers/podman/v2/pkg/util" + units "github.com/docker/go-units" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/runtime-tools/validate" @@ -124,8 +125,6 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data) HostnamePath: hostnamePath, HostsPath: hostsPath, StaticDir: config.StaticDir, - LogPath: config.LogPath, - LogTag: config.LogTag, OCIRuntime: config.OCIRuntime, ConmonPidFile: config.ConmonPidFile, Name: config.Name, @@ -354,6 +353,10 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named logConfig := new(define.InspectLogConfig) logConfig.Type = c.config.LogDriver + logConfig.Path = c.config.LogPath + logConfig.Size = units.HumanSize(float64(c.config.LogSize)) + logConfig.Tag = c.config.LogTag + hostConfig.LogConfig = logConfig restartPolicy := new(define.InspectRestartPolicy) diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go index 775965477..c15bcedf2 100644 --- a/libpod/define/container_inspect.go +++ b/libpod/define/container_inspect.go @@ -82,10 +82,15 @@ type InspectRestartPolicy struct { } // InspectLogConfig holds information about a container's configured log driver -// and is presently unused. It is retained for Docker compatibility. type InspectLogConfig struct { Type string `json:"Type"` Config map[string]string `json:"Config"` //idk type, TODO + // Path specifies a path to the log file + Path string `json:"Path"` + // Tag specifies a custom log tag for the container + Tag string `json:"Tag"` + // Size specifies a maximum size of the container log + Size string `json:"Size"` } // InspectBlkioWeightDevice holds information about the relative weight @@ -620,8 +625,6 @@ type InspectContainerData struct { StaticDir string `json:"StaticDir"` OCIConfigPath string `json:"OCIConfigPath,omitempty"` OCIRuntime string `json:"OCIRuntime,omitempty"` - LogPath string `json:"LogPath"` - LogTag string `json:"LogTag"` ConmonPidFile string `json:"ConmonPidFile"` Name string `json:"Name"` RestartCount int32 `json:"RestartCount"` diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 505c96126..814d0fd69 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -265,8 +265,14 @@ func ExportImages(w http.ResponseWriter, r *http.Request) { // Format is mandatory! Currently, we only support multi-image docker // archives. + if len(query.References) > 1 && query.Format != define.V2s2Archive { + utils.Error(w, "unsupported format", http.StatusInternalServerError, errors.Errorf("multi-image archives must use format of %s", define.V2s2Archive)) + return + + } + switch query.Format { - case define.V2s2Archive: + case define.V2s2Archive, define.OCIArchive: tmpfile, err := ioutil.TempFile("", "api.tar") if err != nil { utils.Error(w, "unable to create tmpfile", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile")) @@ -277,6 +283,13 @@ func ExportImages(w http.ResponseWriter, r *http.Request) { utils.Error(w, "unable to close tmpfile", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile")) return } + case define.OCIManifestDir, define.V2s2ManifestDir: + tmpdir, err := ioutil.TempDir("", "save") + if err != nil { + utils.Error(w, "unable to create tmpdir", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempdir")) + return + } + output = tmpdir default: utils.Error(w, "unsupported format", http.StatusInternalServerError, errors.Errorf("unsupported format %q", query.Format)) return @@ -287,7 +300,7 @@ func ExportImages(w http.ResponseWriter, r *http.Request) { opts := entities.ImageSaveOptions{ Compress: query.Compress, Format: query.Format, - MultiImageArchive: true, + MultiImageArchive: len(query.References) > 1, Output: output, RemoveSignatures: true, } @@ -414,7 +427,6 @@ func PushImage(w http.ResponseWriter, r *http.Request) { }{ // This is where you can override the golang default value for one of fields } - if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return @@ -607,12 +619,12 @@ func UntagImage(w http.ResponseWriter, r *http.Request) { func SearchImages(w http.ResponseWriter, r *http.Request) { decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { - Term string `json:"term"` - Limit int `json:"limit"` - NoTrunc bool `json:"noTrunc"` - Filters []string `json:"filters"` - TLSVerify bool `json:"tlsVerify"` - ListTags bool `json:"listTags"` + Term string `json:"term"` + Limit int `json:"limit"` + NoTrunc bool `json:"noTrunc"` + Filters map[string][]string `json:"filters"` + TLSVerify bool `json:"tlsVerify"` + ListTags bool `json:"listTags"` }{ // This is where you can override the golang default value for one of fields } @@ -622,24 +634,44 @@ func SearchImages(w http.ResponseWriter, r *http.Request) { return } + filter := image.SearchFilter{} + if len(query.Filters) > 0 { + if len(query.Filters["stars"]) > 0 { + stars, err := strconv.Atoi(query.Filters["stars"][0]) + if err != nil { + utils.InternalServerError(w, err) + return + } + filter.Stars = stars + } + if len(query.Filters["is-official"]) > 0 { + isOfficial, err := strconv.ParseBool(query.Filters["is-official"][0]) + if err != nil { + utils.InternalServerError(w, err) + return + } + filter.IsOfficial = types.NewOptionalBool(isOfficial) + } + if len(query.Filters["is-automated"]) > 0 { + isAutomated, err := strconv.ParseBool(query.Filters["is-automated"][0]) + if err != nil { + utils.InternalServerError(w, err) + return + } + filter.IsAutomated = types.NewOptionalBool(isAutomated) + } + } options := image.SearchOptions{ Limit: query.Limit, NoTrunc: query.NoTrunc, ListTags: query.ListTags, + Filter: filter, } + if _, found := r.URL.Query()["tlsVerify"]; found { options.InsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) } - if _, found := r.URL.Query()["filters"]; found { - filter, err := image.ParseSearchFilter(query.Filters) - if err != nil { - utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse filters parameter for %s", r.URL.String())) - return - } - options.Filter = *filter - } - _, authfile, key, err := auth.GetCredentials(r) if err != nil { utils.Error(w, "failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "failed to parse %q header for %s", key, r.URL.String())) @@ -678,10 +710,7 @@ func ImagesBatchRemove(w http.ResponseWriter, r *http.Request) { All bool `schema:"all"` Force bool `schema:"force"` Images []string `schema:"images"` - }{ - All: false, - Force: false, - } + }{} if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, @@ -690,10 +719,8 @@ func ImagesBatchRemove(w http.ResponseWriter, r *http.Request) { } opts := entities.ImageRemoveOptions{All: query.All, Force: query.Force} - imageEngine := abi.ImageEngine{Libpod: runtime} rmReport, rmErrors := imageEngine.Remove(r.Context(), query.Images, opts) - strErrs := errorhandling.ErrorsToStrings(rmErrors) report := handlers.LibpodImagesRemoveReport{ImageRemoveReport: *rmReport, Errors: strErrs} utils.WriteResponse(w, http.StatusOK, report) diff --git a/pkg/bindings/generator/generator.go b/pkg/bindings/generator/generator.go index 24c2310ff..2ebe35282 100644 --- a/pkg/bindings/generator/generator.go +++ b/pkg/bindings/generator/generator.go @@ -81,10 +81,11 @@ func (o *{{.StructName}}) ToParams() (url.Values, error) { } case reflect.Map: lowerCaseKeys := make(map[string][]string) - // I dont know if this code is needed anymore, TBD - // for k, v := range filters { - // lowerCaseKeys[strings.ToLower(k)] = v - // } + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } s, err := json.MarshalToString(lowerCaseKeys) if err != nil { return nil, err @@ -102,23 +103,34 @@ func (o *{{.StructName}}) ToParams() (url.Values, error) { var fieldTmpl = ` // With{{.Name}} func(o *{{.StructName}}) With{{.Name}}(value {{.Type}}) *{{.StructName}} { - v := &value + v := {{.TypedValue}} o.{{.Name}} = v return o } + +// Get{{.Name}} +func(o *{{.StructName}}) Get{{.Name}}() {{.Type}} { + var {{.ZeroName}} {{.Type}} + if o.{{.Name}} == nil { + return {{.ZeroName}} + } + return {{.TypedName}} +} ` type fieldStruct struct { Name string StructName string Type string + TypedName string + TypedValue string + ZeroName string } func main() { var ( closed bool fieldStructs []fieldStruct - structNode ast.Node ) srcFile := os.Getenv("GOFILE") pkg := os.Getenv("GOPACKAGE") @@ -132,14 +144,13 @@ func main() { if err != nil { panic(err) } - // always add reflect imports := []string{"\"reflect\""} for _, imp := range f.Imports { imports = append(imports, imp.Path.Value) } - out, err := os.Create(strings.ToLower(inputStructName) + "_" + srcFile) + out, err := os.Create(strings.TrimRight(srcFile, ".go") + "_" + strings.Replace(strings.ToLower(inputStructName), "options", "_options", 1) + ".go") if err != nil { panic(err) } @@ -166,26 +177,41 @@ func main() { ref, refOK := n.(*ast.TypeSpec) if refOK { if ref.Name.Name == inputStructName { - structNode = n x := ref.Type.(*ast.StructType) for _, field := range x.Fields.List { var ( - name string + name, zeroName, typedName, typedValue string ) - typeExpr := field.Type - start := typeExpr.Pos() - 1 - end := typeExpr.End() - 1 - fieldType := strings.Replace(string(b[start:end]), "*", "", 1) if len(field.Names) > 0 { name = field.Names[0].Name if len(name) < 1 { panic(errors.New("bad name")) } } + for k, v := range name { + zeroName = strings.ToLower(string(v)) + name[k+1:] + break + } + //sub := "*" + typeExpr := field.Type + switch field.Type.(type) { + case *ast.MapType, *ast.StructType, *ast.ArrayType: + typedName = "o." + name + typedValue = "value" + default: + typedName = "*o." + name + typedValue = "&value" + } + start := typeExpr.Pos() - 1 + end := typeExpr.End() - 1 + fieldType := strings.Replace(string(b[start:end]), "*", "", 1) fStruct := fieldStruct{ Name: name, StructName: inputStructName, Type: fieldType, + TypedName: typedName, + TypedValue: typedValue, + ZeroName: zeroName, } fieldStructs = append(fieldStructs, fStruct) } // for diff --git a/pkg/bindings/images/diff.go b/pkg/bindings/images/diff.go index 10d50b0fd..8802c15e2 100644 --- a/pkg/bindings/images/diff.go +++ b/pkg/bindings/images/diff.go @@ -9,7 +9,11 @@ import ( ) // Diff provides the changes between two container layers -func Diff(ctx context.Context, nameOrID string) ([]archive.Change, error) { +func Diff(ctx context.Context, nameOrID string, options *DiffOptions) ([]archive.Change, error) { + if options == nil { + options = new(DiffOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index 2d3035d8d..9beb493c8 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -8,7 +8,6 @@ import ( "net/url" "strconv" - "github.com/containers/image/v5/types" "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/auth" "github.com/containers/podman/v2/pkg/bindings" @@ -32,22 +31,18 @@ func Exists(ctx context.Context, nameOrID string) (bool, error) { // List returns a list of images in local storage. The all boolean and filters parameters are optional // ways to alter the image query. -func List(ctx context.Context, all *bool, filters map[string][]string) ([]*entities.ImageSummary, error) { +func List(ctx context.Context, options *ListOptions) ([]*entities.ImageSummary, error) { + if options == nil { + options = new(ListOptions) + } var imageSummary []*entities.ImageSummary conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if all != nil { - params.Set("all", strconv.FormatBool(*all)) - } - if filters != nil { - strFilters, err := bindings.FiltersToString(filters) - if err != nil { - return nil, err - } - params.Set("filters", strFilters) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodGet, "/images/json", params, nil) if err != nil { @@ -58,14 +53,17 @@ func List(ctx context.Context, all *bool, filters map[string][]string) ([]*entit // Get performs an image inspect. To have the on-disk size of the image calculated, you can // use the optional size parameter. -func GetImage(ctx context.Context, nameOrID string, size *bool) (*entities.ImageInspectReport, error) { +func GetImage(ctx context.Context, nameOrID string, options *GetOptions) (*entities.ImageInspectReport, error) { + if options == nil { + options = new(GetOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if size != nil { - params.Set("size", strconv.FormatBool(*size)) + params, err := options.ToParams() + if err != nil { + return nil, err } inspectedData := entities.ImageInspectReport{} response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/json", params, nil, nameOrID) @@ -76,15 +74,18 @@ func GetImage(ctx context.Context, nameOrID string, size *bool) (*entities.Image } // Tree retrieves a "tree" based representation of the given image -func Tree(ctx context.Context, nameOrID string, whatRequires *bool) (*entities.ImageTreeReport, error) { +func Tree(ctx context.Context, nameOrID string, options *TreeOptions) (*entities.ImageTreeReport, error) { + if options == nil { + options = new(TreeOptions) + } var report entities.ImageTreeReport conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if whatRequires != nil { - params.Set("size", strconv.FormatBool(*whatRequires)) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/tree", params, nil, nameOrID) if err != nil { @@ -94,7 +95,11 @@ func Tree(ctx context.Context, nameOrID string, whatRequires *bool) (*entities.I } // History returns the parent layers of an image. -func History(ctx context.Context, nameOrID string) ([]*handlers.HistoryResponse, error) { +func History(ctx context.Context, nameOrID string, options *HistoryOptions) ([]*handlers.HistoryResponse, error) { + if options == nil { + options = new(HistoryOptions) + } + _ = options var history []*handlers.HistoryResponse conn, err := bindings.GetClient(ctx) if err != nil { @@ -107,15 +112,18 @@ func History(ctx context.Context, nameOrID string) ([]*handlers.HistoryResponse, return history, response.Process(&history) } -func Load(ctx context.Context, r io.Reader, name *string) (*entities.ImageLoadReport, error) { +func Load(ctx context.Context, r io.Reader, options *LoadOptions) (*entities.ImageLoadReport, error) { + if options == nil { + options = new(LoadOptions) + } var report entities.ImageLoadReport conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if name != nil { - params.Set("reference", *name) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(r, http.MethodPost, "/images/load", params, nil) if err != nil { @@ -124,49 +132,24 @@ func Load(ctx context.Context, r io.Reader, name *string) (*entities.ImageLoadRe return &report, response.Process(&report) } -func MultiExport(ctx context.Context, namesOrIds []string, w io.Writer, format *string, compress *bool) error { - conn, err := bindings.GetClient(ctx) - if err != nil { - return err - } - params := url.Values{} - if format != nil { - params.Set("format", *format) - } - if compress != nil { - params.Set("compress", strconv.FormatBool(*compress)) - } - for _, ref := range namesOrIds { - params.Add("references", ref) +// Export saves images from local storage as a tarball or image archive. The optional format +// parameter is used to change the format of the output. +func Export(ctx context.Context, nameOrIDs []string, w io.Writer, options *ExportOptions) error { + if options == nil { + options = new(ExportOptions) } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/export", params, nil) + conn, err := bindings.GetClient(ctx) if err != nil { return err } - - if response.StatusCode/100 == 2 || response.StatusCode/100 == 3 { - _, err = io.Copy(w, response.Body) - return err - } - return response.Process(nil) - -} - -// Export saves an image from local storage as a tarball or image archive. The optional format -// parameter is used to change the format of the output. -func Export(ctx context.Context, nameOrID string, w io.Writer, format *string, compress *bool) error { - conn, err := bindings.GetClient(ctx) + params, err := options.ToParams() if err != nil { return err } - params := url.Values{} - if format != nil { - params.Set("format", *format) - } - if compress != nil { - params.Set("compress", strconv.FormatBool(*compress)) + for _, ref := range nameOrIDs { + params.Add("references", ref) } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/get", params, nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodGet, "/images/export", params, nil) if err != nil { return err } @@ -180,24 +163,20 @@ func Export(ctx context.Context, nameOrID string, w io.Writer, format *string, c // Prune removes unused images from local storage. The optional filters can be used to further // define which images should be pruned. -func Prune(ctx context.Context, all *bool, filters map[string][]string) ([]string, error) { +func Prune(ctx context.Context, options *PruneOptions) ([]string, error) { var ( deleted []string ) + if options == nil { + options = new(PruneOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if all != nil { - params.Set("all", strconv.FormatBool(*all)) - } - if filters != nil { - stringFilter, err := bindings.FiltersToString(filters) - if err != nil { - return nil, err - } - params.Set("filters", stringFilter) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodPost, "/images/prune", params, nil) if err != nil { @@ -207,7 +186,11 @@ func Prune(ctx context.Context, all *bool, filters map[string][]string) ([]strin } // Tag adds an additional name to locally-stored image. Both the tag and repo parameters are required. -func Tag(ctx context.Context, nameOrID, tag, repo string) error { +func Tag(ctx context.Context, nameOrID, tag, repo string, options *TagOptions) error { + if options == nil { + options = new(TagOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return err @@ -223,7 +206,11 @@ func Tag(ctx context.Context, nameOrID, tag, repo string) error { } // Untag removes a name from locally-stored image. Both the tag and repo parameters are required. -func Untag(ctx context.Context, nameOrID, tag, repo string) error { +func Untag(ctx context.Context, nameOrID, tag, repo string, options *UntagOptions) error { + if options == nil { + options = new(UntagOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return err @@ -241,27 +228,21 @@ func Untag(ctx context.Context, nameOrID, tag, repo string) error { // Imports adds the given image to the local image store. This can be done by file and the given reader // or via the url parameter. Additional metadata can be associated with the image by using the changes and // message parameters. The image can also be tagged given a reference. One of url OR r must be provided. -func Import(ctx context.Context, changes []string, message, reference, u *string, r io.Reader) (*entities.ImageImportReport, error) { +func Import(ctx context.Context, r io.Reader, options *ImportOptions) (*entities.ImageImportReport, error) { + if options == nil { + options = new(ImportOptions) + } var report entities.ImageImportReport - if r != nil && u != nil { + if r != nil && options.URL != nil { return nil, errors.New("url and r parameters cannot be used together") } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - for _, change := range changes { - params.Add("changes", change) - } - if message != nil { - params.Set("message", *message) - } - if reference != nil { - params.Set("reference", *reference) - } - if u != nil { - params.Set("url", *u) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(r, http.MethodPost, "/images/import", params, nil) if err != nil { @@ -275,25 +256,31 @@ func Import(ctx context.Context, changes []string, message, reference, u *string // The destination must be a reference to a registry (i.e., of docker transport // or be normalized to one). Other transports are rejected as they do not make // sense in a remote context. -func Push(ctx context.Context, source string, destination string, options entities.ImagePushOptions) error { +func Push(ctx context.Context, source string, destination string, options *PushOptions) error { + if options == nil { + options = new(PushOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return err } - // TODO: have a global system context we can pass around (1st argument) - header, err := auth.Header(nil, auth.XRegistryAuthHeader, options.Authfile, options.Username, options.Password) + header, err := auth.Header(nil, auth.XRegistryAuthHeader, options.GetAuthfile(), options.GetUsername(), options.GetPassword()) if err != nil { return err } - params := url.Values{} - params.Set("destination", destination) - if options.SkipTLSVerify != types.OptionalBoolUndefined { - // Note: we have to verify if skipped is false. - verifyTLS := bool(options.SkipTLSVerify == types.OptionalBoolFalse) - params.Set("tlsVerify", strconv.FormatBool(verifyTLS)) + params, err := options.ToParams() + if err != nil { + return err + } + //SkipTLSVerify is special. We need to delete the param added by + //toparams and change the key and flip the bool + if options.SkipTLSVerify != nil { + params.Del("SkipTLSVerify") + params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } + params.Set("destination", destination) path := fmt.Sprintf("/images/%s/push", source) response, err := conn.DoRequest(nil, http.MethodPost, path, params, header) @@ -305,28 +292,28 @@ func Push(ctx context.Context, source string, destination string, options entiti } // Search is the binding for libpod's v2 endpoints for Search images. -func Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) { +func Search(ctx context.Context, term string, options *SearchOptions) ([]entities.ImageSearchReport, error) { + if options == nil { + options = new(SearchOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - params.Set("term", term) - params.Set("limit", strconv.Itoa(opts.Limit)) - params.Set("noTrunc", strconv.FormatBool(opts.NoTrunc)) - params.Set("listTags", strconv.FormatBool(opts.ListTags)) - for _, f := range opts.Filters { - params.Set("filters", f) + params, err := options.ToParams() + if err != nil { + return nil, err } + params.Set("term", term) - if opts.SkipTLSVerify != types.OptionalBoolUndefined { - // Note: we have to verify if skipped is false. - verifyTLS := bool(opts.SkipTLSVerify == types.OptionalBoolFalse) - params.Set("tlsVerify", strconv.FormatBool(verifyTLS)) + // Note: we have to verify if skipped is false. + if options.SkipTLSVerify != nil { + params.Del("SkipTLSVerify") + params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } // TODO: have a global system context we can pass around (1st argument) - header, err := auth.Header(nil, auth.XRegistryAuthHeader, opts.Authfile, "", "") + header, err := auth.Header(nil, auth.XRegistryAuthHeader, options.GetAuthfile(), "", "") if err != nil { return nil, err } diff --git a/pkg/bindings/images/pull.go b/pkg/bindings/images/pull.go index c827b3283..5669c704e 100644 --- a/pkg/bindings/images/pull.go +++ b/pkg/bindings/images/pull.go @@ -8,11 +8,9 @@ import ( "io" "io/ioutil" "net/http" - "net/url" "os" "strconv" - "github.com/containers/image/v5/types" "github.com/containers/podman/v2/pkg/auth" "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/domain/entities" @@ -23,26 +21,28 @@ import ( // `rawImage` must be a reference to a registry (i.e., of docker transport or be // normalized to one). Other transports are rejected as they do not make sense // in a remote context. Progress reported on stderr -func Pull(ctx context.Context, rawImage string, options entities.ImagePullOptions) ([]string, error) { +func Pull(ctx context.Context, rawImage string, options *PullOptions) ([]string, error) { + if options == nil { + options = new(PullOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} + params, err := options.ToParams() + if err != nil { + return nil, err + } params.Set("reference", rawImage) - params.Set("overrideArch", options.OverrideArch) - params.Set("overrideOS", options.OverrideOS) - params.Set("overrideVariant", options.OverrideVariant) - if options.SkipTLSVerify != types.OptionalBoolUndefined { + if options.SkipTLSVerify != nil { + params.Del("SkipTLSVerify") // Note: we have to verify if skipped is false. - verifyTLS := bool(options.SkipTLSVerify == types.OptionalBoolFalse) - params.Set("tlsVerify", strconv.FormatBool(verifyTLS)) + params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } - params.Set("allTags", strconv.FormatBool(options.AllTags)) // TODO: have a global system context we can pass around (1st argument) - header, err := auth.Header(nil, auth.XRegistryAuthHeader, options.Authfile, options.Username, options.Password) + header, err := auth.Header(nil, auth.XRegistryAuthHeader, options.GetAuthfile(), options.GetUsername(), options.GetPassword()) if err != nil { return nil, err } @@ -59,7 +59,7 @@ func Pull(ctx context.Context, rawImage string, options entities.ImagePullOption // Historically pull writes status to stderr stderr := io.Writer(os.Stderr) - if options.Quiet { + if options.GetQuiet() { stderr = ioutil.Discard } diff --git a/pkg/bindings/images/rm.go b/pkg/bindings/images/rm.go index 0b3b88165..e652e66aa 100644 --- a/pkg/bindings/images/rm.go +++ b/pkg/bindings/images/rm.go @@ -3,8 +3,6 @@ package images import ( "context" "net/http" - "net/url" - "strconv" "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/bindings" @@ -12,8 +10,12 @@ import ( "github.com/containers/podman/v2/pkg/errorhandling" ) -// BachtRemove removes a batch of images from the local storage. -func BatchRemove(ctx context.Context, images []string, opts entities.ImageRemoveOptions) (*entities.ImageRemoveReport, []error) { +// Remove removes one or more images from the local storage. Use optional force option to remove an +// image, even if it's used by containers. +func Remove(ctx context.Context, images []string, options *RemoveOptions) (*entities.ImageRemoveReport, []error) { + 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. @@ -23,13 +25,13 @@ func BatchRemove(ctx context.Context, images []string, opts entities.ImageRemove return nil, []error{err} } - params := url.Values{} - params.Set("all", strconv.FormatBool(opts.All)) - params.Set("force", strconv.FormatBool(opts.Force)) - for _, i := range images { - params.Add("images", i) + params, err := options.ToParams() + if err != nil { + return nil, nil + } + for _, image := range images { + params.Add("images", image) } - response, err := conn.DoRequest(nil, http.MethodDelete, "/images/remove", params, nil) if err != nil { return nil, []error{err} @@ -40,28 +42,3 @@ func BatchRemove(ctx context.Context, images []string, opts entities.ImageRemove return &report.ImageRemoveReport, errorhandling.StringsToErrors(report.Errors) } - -// Remove removes an image from the local storage. Use optional force option to remove an -// image, even if it's used by containers. -func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) (*entities.ImageRemoveReport, error) { - var report handlers.LibpodImagesRemoveReport - conn, err := bindings.GetClient(ctx) - if err != nil { - return nil, err - } - - params, err := options.ToParams() - if err != nil { - return nil, err - } - response, err := conn.DoRequest(nil, http.MethodDelete, "/images/%s", params, nil, nameOrID) - if err != nil { - return nil, err - } - if err := response.Process(&report); err != nil { - return nil, err - } - - errs := errorhandling.StringsToErrors(report.Errors) - return &report.ImageRemoveReport, errorhandling.JoinErrors(errs) -} diff --git a/pkg/bindings/images/types.go b/pkg/bindings/images/types.go index 340c7bdb9..c436413c3 100644 --- a/pkg/bindings/images/types.go +++ b/pkg/bindings/images/types.go @@ -1,8 +1,193 @@ package images +import ( + "github.com/containers/buildah/imagebuildah" + "github.com/containers/common/pkg/config" +) + //go:generate go run ../generator/generator.go RemoveOptions // RemoveOptions are optional options for image removal type RemoveOptions struct { + // All removes all images + All *bool // Forces removes all containers based on the image Force *bool } + +//go:generate go run ../generator/generator.go DiffOptions +// DiffOptions are optional options image diffs +type DiffOptions struct { +} + +//go:generate go run ../generator/generator.go ListOptions +// ListOptions are optional options for listing images +type ListOptions struct { + // All lists all image in the image store including dangling images + All *bool + // filters that can be used to get a more specific list of images + Filters map[string][]string +} + +//go:generate go run ../generator/generator.go GetOptions +// GetOptions are optional options for inspecting an image +type GetOptions struct { + // Size computes the amount of storage the image consumes + Size *bool +} + +//go:generate go run ../generator/generator.go TreeOptions +// TreeOptions are optional options for a tree-based representation +// of the image +type TreeOptions struct { + // WhatRequires ... + WhatRequires *bool +} + +//go:generate go run ../generator/generator.go HistoryOptions +// HistoryOptions are optional options image history +type HistoryOptions struct { +} + +//go:generate go run ../generator/generator.go LoadOptions +// LoadOptions are optional options for loading an image +type LoadOptions struct { + // Reference is the name of the loaded image + Reference *string +} + +//go:generate go run ../generator/generator.go ExportOptions +// ExportOptions are optional options for exporting images +type ExportOptions struct { + // Compress the image + Compress *bool + // Format of the output + Format *string +} + +//go:generate go run ../generator/generator.go PruneOptions +// PruneOptions are optional options for pruning images +type PruneOptions struct { + // Prune all images + All *bool + // Filters to apply when pruning images + Filters map[string][]string +} + +//go:generate go run ../generator/generator.go TagOptions +// TagOptions are optional options for tagging images +type TagOptions struct { +} + +//go:generate go run ../generator/generator.go UntagOptions +// UntagOptions are optional options for tagging images +type UntagOptions struct { +} + +//go:generate go run ../generator/generator.go ImportOptions +// ImportOptions are optional options for importing images +type ImportOptions struct { + // Changes to be applied to the image + Changes *[]string + // Message to be applied to the image + Message *string + // Reference is a tag to be applied to the image + Reference *string + // Url to option image to import. Cannot be used with the reader + URL *string +} + +//go:generate go run ../generator/generator.go PushOptions +// PushOptions are optional options for importing images +type PushOptions struct { + // Authfile is the path to the authentication file. Ignored for remote + // calls. + Authfile *string + // CertDir is the path to certificate directories. Ignored for remote + // calls. + CertDir *string + // Compress tarball image layers when pushing to a directory using the 'dir' + // transport. Default is same compression type as source. Ignored for remote + // calls. + Compress *bool + // Username for authenticating against the registry. + Username *string + // Password for authenticating against the registry. + Password *string + // DigestFile, after copying the image, write the digest of the resulting + // image to the file. Ignored for remote calls. + DigestFile *string + // Format is the Manifest type (oci, v2s1, or v2s2) to use when pushing an + // image using the 'dir' transport. Default is manifest type of source. + // Ignored for remote calls. + Format *string + // Quiet can be specified to suppress pull progress when pulling. Ignored + // for remote calls. + Quiet *bool + // RemoveSignatures, discard any pre-existing signatures in the image. + // Ignored for remote calls. + RemoveSignatures *bool + // SignaturePolicy to use when pulling. Ignored for remote calls. + SignaturePolicy *string + // SignBy adds a signature at the destination using the specified key. + // Ignored for remote calls. + SignBy *string + // SkipTLSVerify to skip HTTPS and certificate verification. + SkipTLSVerify *bool +} + +//go:generate go run ../generator/generator.go SearchOptions +// SearchOptions are optional options for seaching images on registies +type SearchOptions struct { + // Authfile is the path to the authentication file. Ignored for remote + // calls. + Authfile *string + // Filters for the search results. + Filters map[string][]string + // Limit the number of results. + Limit *int + // NoTrunc will not truncate the output. + NoTrunc *bool + // SkipTLSVerify to skip HTTPS and certificate verification. + SkipTLSVerify *bool + // ListTags search the available tags of the repository + ListTags *bool +} + +//go:generate go run ../generator/generator.go PullOptions +// PullOptions are optional options for pulling images +type PullOptions struct { + // AllTags can be specified to pull all tags of an image. Note + // that this only works if the image does not include a tag. + AllTags *bool + // Authfile is the path to the authentication file. Ignored for remote + // calls. + Authfile *string + // CertDir is the path to certificate directories. Ignored for remote + // calls. + CertDir *string + // Username for authenticating against the registry. + Username *string + // Password for authenticating against the registry. + Password *string + // OverrideArch will overwrite the local architecture for image pulls. + OverrideArch *string + // OverrideOS will overwrite the local operating system (OS) for image + // pulls. + OverrideOS *string + // OverrideVariant will overwrite the local variant for image pulls. + OverrideVariant *string + // Quiet can be specified to suppress pull progress when pulling. Ignored + // for remote calls. + Quiet *bool + // SignaturePolicy to use when pulling. Ignored for remote calls. + SignaturePolicy *string + // SkipTLSVerify to skip HTTPS and certificate verification. + SkipTLSVerify *bool + // PullPolicy whether to pull new image + PullPolicy *config.PullPolicy +} + +//BuildOptions are optional options for building images +type BuildOptions struct { + imagebuildah.BuildOptions +} diff --git a/pkg/bindings/images/removeoptions_types.go b/pkg/bindings/images/types_diff_options.go index 5902bf908..45e548056 100644 --- a/pkg/bindings/images/removeoptions_types.go +++ b/pkg/bindings/images/types_diff_options.go @@ -12,18 +12,18 @@ import ( /* This file is generated automatically by go generate. Do not edit. -Created 2020-12-10 12:51:06.090426622 -0600 CST m=+0.000133169 +Created 2020-12-15 15:22:46.528172042 -0600 CST m=+0.000279712 */ // Changed -func (o *RemoveOptions) Changed(fieldName string) bool { +func (o *DiffOptions) Changed(fieldName string) bool { r := reflect.ValueOf(o) value := reflect.Indirect(r).FieldByName(fieldName) return !value.IsNil() } // ToParams -func (o *RemoveOptions) ToParams() (url.Values, error) { +func (o *DiffOptions) ToParams() (url.Values, error) { params := url.Values{} if o == nil { return params, nil @@ -68,10 +68,11 @@ func (o *RemoveOptions) ToParams() (url.Values, error) { } case reflect.Map: lowerCaseKeys := make(map[string][]string) - // I dont know if this code is needed anymore, TBD - // for k, v := range filters { - // lowerCaseKeys[strings.ToLower(k)] = v - // } + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } s, err := json.MarshalToString(lowerCaseKeys) if err != nil { return nil, err @@ -84,10 +85,3 @@ func (o *RemoveOptions) ToParams() (url.Values, error) { } return params, nil } - -// WithForce -func (o *RemoveOptions) WithForce(value bool) *RemoveOptions { - v := &value - o.Force = v - return o -} diff --git a/pkg/bindings/images/types_export_options.go b/pkg/bindings/images/types_export_options.go new file mode 100644 index 000000000..ae585ae3e --- /dev/null +++ b/pkg/bindings/images/types_export_options.go @@ -0,0 +1,119 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-15 15:22:47.379804635 -0600 CST m=+0.000257609 +*/ + +// Changed +func (o *ExportOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ExportOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) + switch typ.Kind() { + case reflect.String: + s, ok := slice.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + default: + return nil, errors.Errorf("unknown type %s", f.Kind().String()) + } + } + return params, nil +} + +// WithCompress +func (o *ExportOptions) WithCompress(value bool) *ExportOptions { + v := &value + o.Compress = v + return o +} + +// GetCompress +func (o *ExportOptions) GetCompress() bool { + var compress bool + if o.Compress == nil { + return compress + } + return *o.Compress +} + +// WithFormat +func (o *ExportOptions) WithFormat(value string) *ExportOptions { + v := &value + o.Format = v + return o +} + +// GetFormat +func (o *ExportOptions) GetFormat() string { + var format string + if o.Format == nil { + return format + } + return *o.Format +} diff --git a/pkg/bindings/images/types_get_options.go b/pkg/bindings/images/types_get_options.go new file mode 100644 index 000000000..b7b45259d --- /dev/null +++ b/pkg/bindings/images/types_get_options.go @@ -0,0 +1,103 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-15 15:22:46.81312808 -0600 CST m=+0.000257734 +*/ + +// Changed +func (o *GetOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *GetOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) + switch typ.Kind() { + case reflect.String: + s, ok := slice.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + default: + return nil, errors.Errorf("unknown type %s", f.Kind().String()) + } + } + return params, nil +} + +// WithSize +func (o *GetOptions) WithSize(value bool) *GetOptions { + v := &value + o.Size = v + return o +} + +// GetSize +func (o *GetOptions) GetSize() bool { + var size bool + if o.Size == nil { + return size + } + return *o.Size +} diff --git a/pkg/bindings/images/types_history_options.go b/pkg/bindings/images/types_history_options.go new file mode 100644 index 000000000..0fe73a990 --- /dev/null +++ b/pkg/bindings/images/types_history_options.go @@ -0,0 +1,87 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-15 15:22:47.095693605 -0600 CST m=+0.000243849 +*/ + +// Changed +func (o *HistoryOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *HistoryOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) + switch typ.Kind() { + case reflect.String: + s, ok := slice.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + default: + return nil, errors.Errorf("unknown type %s", f.Kind().String()) + } + } + return params, nil +} diff --git a/pkg/bindings/images/types_import_options.go b/pkg/bindings/images/types_import_options.go new file mode 100644 index 000000000..002f1cfa5 --- /dev/null +++ b/pkg/bindings/images/types_import_options.go @@ -0,0 +1,151 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-15 15:22:47.943587389 -0600 CST m=+0.000238222 +*/ + +// Changed +func (o *ImportOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ImportOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) + switch typ.Kind() { + case reflect.String: + s, ok := slice.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + default: + return nil, errors.Errorf("unknown type %s", f.Kind().String()) + } + } + return params, nil +} + +// WithChanges +func (o *ImportOptions) WithChanges(value []string) *ImportOptions { + v := &value + o.Changes = v + return o +} + +// GetChanges +func (o *ImportOptions) GetChanges() []string { + var changes []string + if o.Changes == nil { + return changes + } + return *o.Changes +} + +// WithMessage +func (o *ImportOptions) WithMessage(value string) *ImportOptions { + v := &value + o.Message = v + return o +} + +// GetMessage +func (o *ImportOptions) GetMessage() string { + var message string + if o.Message == nil { + return message + } + return *o.Message +} + +// WithReference +func (o *ImportOptions) WithReference(value string) *ImportOptions { + v := &value + o.Reference = v + return o +} + +// GetReference +func (o *ImportOptions) GetReference() string { + var reference string + if o.Reference == nil { + return reference + } + return *o.Reference +} + +// WithURL +func (o *ImportOptions) WithURL(value string) *ImportOptions { + v := &value + o.URL = v + return o +} + +// GetURL +func (o *ImportOptions) GetURL() string { + var uRL string + if o.URL == nil { + return uRL + } + return *o.URL +} diff --git a/pkg/bindings/images/types_list_options.go b/pkg/bindings/images/types_list_options.go new file mode 100644 index 000000000..0cdb243fa --- /dev/null +++ b/pkg/bindings/images/types_list_options.go @@ -0,0 +1,119 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-15 15:22:46.671238196 -0600 CST m=+0.000266757 +*/ + +// Changed +func (o *ListOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ListOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) + switch typ.Kind() { + case reflect.String: + s, ok := slice.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + default: + return nil, errors.Errorf("unknown type %s", f.Kind().String()) + } + } + return params, nil +} + +// WithAll +func (o *ListOptions) WithAll(value bool) *ListOptions { + v := &value + o.All = v + return o +} + +// GetAll +func (o *ListOptions) GetAll() bool { + var all bool + if o.All == nil { + return all + } + return *o.All +} + +// WithFilters +func (o *ListOptions) WithFilters(value map[string][]string) *ListOptions { + v := value + o.Filters = v + return o +} + +// GetFilters +func (o *ListOptions) GetFilters() map[string][]string { + var filters map[string][]string + if o.Filters == nil { + return filters + } + return o.Filters +} diff --git a/pkg/bindings/images/types_load_options.go b/pkg/bindings/images/types_load_options.go new file mode 100644 index 000000000..a82521749 --- /dev/null +++ b/pkg/bindings/images/types_load_options.go @@ -0,0 +1,103 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-15 15:22:47.237599061 -0600 CST m=+0.000247138 +*/ + +// Changed +func (o *LoadOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *LoadOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) + switch typ.Kind() { + case reflect.String: + s, ok := slice.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + default: + return nil, errors.Errorf("unknown type %s", f.Kind().String()) + } + } + return params, nil +} + +// WithReference +func (o *LoadOptions) WithReference(value string) *LoadOptions { + v := &value + o.Reference = v + return o +} + +// GetReference +func (o *LoadOptions) GetReference() string { + var reference string + if o.Reference == nil { + return reference + } + return *o.Reference +} diff --git a/pkg/bindings/images/types_prune_options.go b/pkg/bindings/images/types_prune_options.go new file mode 100644 index 000000000..25da5e815 --- /dev/null +++ b/pkg/bindings/images/types_prune_options.go @@ -0,0 +1,119 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-15 15:22:47.521471344 -0600 CST m=+0.000250491 +*/ + +// Changed +func (o *PruneOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *PruneOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) + switch typ.Kind() { + case reflect.String: + s, ok := slice.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + default: + return nil, errors.Errorf("unknown type %s", f.Kind().String()) + } + } + return params, nil +} + +// WithAll +func (o *PruneOptions) WithAll(value bool) *PruneOptions { + v := &value + o.All = v + return o +} + +// GetAll +func (o *PruneOptions) GetAll() bool { + var all bool + if o.All == nil { + return all + } + return *o.All +} + +// WithFilters +func (o *PruneOptions) WithFilters(value map[string][]string) *PruneOptions { + v := value + o.Filters = v + return o +} + +// GetFilters +func (o *PruneOptions) GetFilters() map[string][]string { + var filters map[string][]string + if o.Filters == nil { + return filters + } + return o.Filters +} diff --git a/pkg/bindings/images/types_pull_options.go b/pkg/bindings/images/types_pull_options.go new file mode 100644 index 000000000..10d0c53e8 --- /dev/null +++ b/pkg/bindings/images/types_pull_options.go @@ -0,0 +1,280 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + "github.com/containers/common/pkg/config" + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-15 15:22:48.373345229 -0600 CST m=+0.000247562 +*/ + +// Changed +func (o *PullOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *PullOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) + switch typ.Kind() { + case reflect.String: + s, ok := slice.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + default: + return nil, errors.Errorf("unknown type %s", f.Kind().String()) + } + } + return params, nil +} + +// WithAllTags +func (o *PullOptions) WithAllTags(value bool) *PullOptions { + v := &value + o.AllTags = v + return o +} + +// GetAllTags +func (o *PullOptions) GetAllTags() bool { + var allTags bool + if o.AllTags == nil { + return allTags + } + return *o.AllTags +} + +// WithAuthfile +func (o *PullOptions) WithAuthfile(value string) *PullOptions { + v := &value + o.Authfile = v + return o +} + +// GetAuthfile +func (o *PullOptions) GetAuthfile() string { + var authfile string + if o.Authfile == nil { + return authfile + } + return *o.Authfile +} + +// WithCertDir +func (o *PullOptions) WithCertDir(value string) *PullOptions { + v := &value + o.CertDir = v + return o +} + +// GetCertDir +func (o *PullOptions) GetCertDir() string { + var certDir string + if o.CertDir == nil { + return certDir + } + return *o.CertDir +} + +// WithUsername +func (o *PullOptions) WithUsername(value string) *PullOptions { + v := &value + o.Username = v + return o +} + +// GetUsername +func (o *PullOptions) GetUsername() string { + var username string + if o.Username == nil { + return username + } + return *o.Username +} + +// WithPassword +func (o *PullOptions) WithPassword(value string) *PullOptions { + v := &value + o.Password = v + return o +} + +// GetPassword +func (o *PullOptions) GetPassword() string { + var password string + if o.Password == nil { + return password + } + return *o.Password +} + +// WithOverrideArch +func (o *PullOptions) WithOverrideArch(value string) *PullOptions { + v := &value + o.OverrideArch = v + return o +} + +// GetOverrideArch +func (o *PullOptions) GetOverrideArch() string { + var overrideArch string + if o.OverrideArch == nil { + return overrideArch + } + return *o.OverrideArch +} + +// WithOverrideOS +func (o *PullOptions) WithOverrideOS(value string) *PullOptions { + v := &value + o.OverrideOS = v + return o +} + +// GetOverrideOS +func (o *PullOptions) GetOverrideOS() string { + var overrideOS string + if o.OverrideOS == nil { + return overrideOS + } + return *o.OverrideOS +} + +// WithOverrideVariant +func (o *PullOptions) WithOverrideVariant(value string) *PullOptions { + v := &value + o.OverrideVariant = v + return o +} + +// GetOverrideVariant +func (o *PullOptions) GetOverrideVariant() string { + var overrideVariant string + if o.OverrideVariant == nil { + return overrideVariant + } + return *o.OverrideVariant +} + +// WithQuiet +func (o *PullOptions) WithQuiet(value bool) *PullOptions { + v := &value + o.Quiet = v + return o +} + +// GetQuiet +func (o *PullOptions) GetQuiet() bool { + var quiet bool + if o.Quiet == nil { + return quiet + } + return *o.Quiet +} + +// WithSignaturePolicy +func (o *PullOptions) WithSignaturePolicy(value string) *PullOptions { + v := &value + o.SignaturePolicy = v + return o +} + +// GetSignaturePolicy +func (o *PullOptions) GetSignaturePolicy() string { + var signaturePolicy string + if o.SignaturePolicy == nil { + return signaturePolicy + } + return *o.SignaturePolicy +} + +// WithSkipTLSVerify +func (o *PullOptions) WithSkipTLSVerify(value bool) *PullOptions { + v := &value + o.SkipTLSVerify = v + return o +} + +// GetSkipTLSVerify +func (o *PullOptions) GetSkipTLSVerify() bool { + var skipTLSVerify bool + if o.SkipTLSVerify == nil { + return skipTLSVerify + } + return *o.SkipTLSVerify +} + +// WithPullPolicy +func (o *PullOptions) WithPullPolicy(value config.PullPolicy) *PullOptions { + v := &value + o.PullPolicy = v + return o +} + +// GetPullPolicy +func (o *PullOptions) GetPullPolicy() config.PullPolicy { + var pullPolicy config.PullPolicy + if o.PullPolicy == nil { + return pullPolicy + } + return *o.PullPolicy +} diff --git a/pkg/bindings/images/types_push_options.go b/pkg/bindings/images/types_push_options.go new file mode 100644 index 000000000..2440d4c0f --- /dev/null +++ b/pkg/bindings/images/types_push_options.go @@ -0,0 +1,279 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-15 15:22:48.08540302 -0600 CST m=+0.000302026 +*/ + +// Changed +func (o *PushOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *PushOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) + switch typ.Kind() { + case reflect.String: + s, ok := slice.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + default: + return nil, errors.Errorf("unknown type %s", f.Kind().String()) + } + } + return params, nil +} + +// WithAuthfile +func (o *PushOptions) WithAuthfile(value string) *PushOptions { + v := &value + o.Authfile = v + return o +} + +// GetAuthfile +func (o *PushOptions) GetAuthfile() string { + var authfile string + if o.Authfile == nil { + return authfile + } + return *o.Authfile +} + +// WithCertDir +func (o *PushOptions) WithCertDir(value string) *PushOptions { + v := &value + o.CertDir = v + return o +} + +// GetCertDir +func (o *PushOptions) GetCertDir() string { + var certDir string + if o.CertDir == nil { + return certDir + } + return *o.CertDir +} + +// WithCompress +func (o *PushOptions) WithCompress(value bool) *PushOptions { + v := &value + o.Compress = v + return o +} + +// GetCompress +func (o *PushOptions) GetCompress() bool { + var compress bool + if o.Compress == nil { + return compress + } + return *o.Compress +} + +// WithUsername +func (o *PushOptions) WithUsername(value string) *PushOptions { + v := &value + o.Username = v + return o +} + +// GetUsername +func (o *PushOptions) GetUsername() string { + var username string + if o.Username == nil { + return username + } + return *o.Username +} + +// WithPassword +func (o *PushOptions) WithPassword(value string) *PushOptions { + v := &value + o.Password = v + return o +} + +// GetPassword +func (o *PushOptions) GetPassword() string { + var password string + if o.Password == nil { + return password + } + return *o.Password +} + +// WithDigestFile +func (o *PushOptions) WithDigestFile(value string) *PushOptions { + v := &value + o.DigestFile = v + return o +} + +// GetDigestFile +func (o *PushOptions) GetDigestFile() string { + var digestFile string + if o.DigestFile == nil { + return digestFile + } + return *o.DigestFile +} + +// WithFormat +func (o *PushOptions) WithFormat(value string) *PushOptions { + v := &value + o.Format = v + return o +} + +// GetFormat +func (o *PushOptions) GetFormat() string { + var format string + if o.Format == nil { + return format + } + return *o.Format +} + +// WithQuiet +func (o *PushOptions) WithQuiet(value bool) *PushOptions { + v := &value + o.Quiet = v + return o +} + +// GetQuiet +func (o *PushOptions) GetQuiet() bool { + var quiet bool + if o.Quiet == nil { + return quiet + } + return *o.Quiet +} + +// WithRemoveSignatures +func (o *PushOptions) WithRemoveSignatures(value bool) *PushOptions { + v := &value + o.RemoveSignatures = v + return o +} + +// GetRemoveSignatures +func (o *PushOptions) GetRemoveSignatures() bool { + var removeSignatures bool + if o.RemoveSignatures == nil { + return removeSignatures + } + return *o.RemoveSignatures +} + +// WithSignaturePolicy +func (o *PushOptions) WithSignaturePolicy(value string) *PushOptions { + v := &value + o.SignaturePolicy = v + return o +} + +// GetSignaturePolicy +func (o *PushOptions) GetSignaturePolicy() string { + var signaturePolicy string + if o.SignaturePolicy == nil { + return signaturePolicy + } + return *o.SignaturePolicy +} + +// WithSignBy +func (o *PushOptions) WithSignBy(value string) *PushOptions { + v := &value + o.SignBy = v + return o +} + +// GetSignBy +func (o *PushOptions) GetSignBy() string { + var signBy string + if o.SignBy == nil { + return signBy + } + return *o.SignBy +} + +// WithSkipTLSVerify +func (o *PushOptions) WithSkipTLSVerify(value bool) *PushOptions { + v := &value + o.SkipTLSVerify = v + return o +} + +// GetSkipTLSVerify +func (o *PushOptions) GetSkipTLSVerify() bool { + var skipTLSVerify bool + if o.SkipTLSVerify == nil { + return skipTLSVerify + } + return *o.SkipTLSVerify +} diff --git a/pkg/bindings/images/types_remove_options.go b/pkg/bindings/images/types_remove_options.go new file mode 100644 index 000000000..164ea1b3e --- /dev/null +++ b/pkg/bindings/images/types_remove_options.go @@ -0,0 +1,119 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-15 15:22:46.378166859 -0600 CST m=+0.000249384 +*/ + +// Changed +func (o *RemoveOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *RemoveOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) + switch typ.Kind() { + case reflect.String: + s, ok := slice.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + default: + return nil, errors.Errorf("unknown type %s", f.Kind().String()) + } + } + return params, nil +} + +// WithAll +func (o *RemoveOptions) WithAll(value bool) *RemoveOptions { + v := &value + o.All = v + return o +} + +// GetAll +func (o *RemoveOptions) GetAll() bool { + var all bool + if o.All == nil { + return all + } + return *o.All +} + +// WithForce +func (o *RemoveOptions) WithForce(value bool) *RemoveOptions { + v := &value + o.Force = v + return o +} + +// GetForce +func (o *RemoveOptions) GetForce() bool { + var force bool + if o.Force == nil { + return force + } + return *o.Force +} diff --git a/pkg/bindings/images/types_search_options.go b/pkg/bindings/images/types_search_options.go new file mode 100644 index 000000000..b0b413a6c --- /dev/null +++ b/pkg/bindings/images/types_search_options.go @@ -0,0 +1,183 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-15 15:22:48.229349981 -0600 CST m=+0.000244343 +*/ + +// Changed +func (o *SearchOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *SearchOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) + switch typ.Kind() { + case reflect.String: + s, ok := slice.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + default: + return nil, errors.Errorf("unknown type %s", f.Kind().String()) + } + } + return params, nil +} + +// WithAuthfile +func (o *SearchOptions) WithAuthfile(value string) *SearchOptions { + v := &value + o.Authfile = v + return o +} + +// GetAuthfile +func (o *SearchOptions) GetAuthfile() string { + var authfile string + if o.Authfile == nil { + return authfile + } + return *o.Authfile +} + +// WithFilters +func (o *SearchOptions) WithFilters(value map[string][]string) *SearchOptions { + v := value + o.Filters = v + return o +} + +// GetFilters +func (o *SearchOptions) GetFilters() map[string][]string { + var filters map[string][]string + if o.Filters == nil { + return filters + } + return o.Filters +} + +// WithLimit +func (o *SearchOptions) WithLimit(value int) *SearchOptions { + v := &value + o.Limit = v + return o +} + +// GetLimit +func (o *SearchOptions) GetLimit() int { + var limit int + if o.Limit == nil { + return limit + } + return *o.Limit +} + +// WithNoTrunc +func (o *SearchOptions) WithNoTrunc(value bool) *SearchOptions { + v := &value + o.NoTrunc = v + return o +} + +// GetNoTrunc +func (o *SearchOptions) GetNoTrunc() bool { + var noTrunc bool + if o.NoTrunc == nil { + return noTrunc + } + return *o.NoTrunc +} + +// WithSkipTLSVerify +func (o *SearchOptions) WithSkipTLSVerify(value bool) *SearchOptions { + v := &value + o.SkipTLSVerify = v + return o +} + +// GetSkipTLSVerify +func (o *SearchOptions) GetSkipTLSVerify() bool { + var skipTLSVerify bool + if o.SkipTLSVerify == nil { + return skipTLSVerify + } + return *o.SkipTLSVerify +} + +// WithListTags +func (o *SearchOptions) WithListTags(value bool) *SearchOptions { + v := &value + o.ListTags = v + return o +} + +// GetListTags +func (o *SearchOptions) GetListTags() bool { + var listTags bool + if o.ListTags == nil { + return listTags + } + return *o.ListTags +} diff --git a/pkg/bindings/images/types_tag_options.go b/pkg/bindings/images/types_tag_options.go new file mode 100644 index 000000000..5efc6462e --- /dev/null +++ b/pkg/bindings/images/types_tag_options.go @@ -0,0 +1,87 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-15 15:22:47.66229668 -0600 CST m=+0.000246630 +*/ + +// Changed +func (o *TagOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *TagOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) + switch typ.Kind() { + case reflect.String: + s, ok := slice.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + default: + return nil, errors.Errorf("unknown type %s", f.Kind().String()) + } + } + return params, nil +} diff --git a/pkg/bindings/images/types_tree_options.go b/pkg/bindings/images/types_tree_options.go new file mode 100644 index 000000000..cca663c67 --- /dev/null +++ b/pkg/bindings/images/types_tree_options.go @@ -0,0 +1,103 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-15 15:22:46.954579136 -0600 CST m=+0.000248704 +*/ + +// Changed +func (o *TreeOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *TreeOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) + switch typ.Kind() { + case reflect.String: + s, ok := slice.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + default: + return nil, errors.Errorf("unknown type %s", f.Kind().String()) + } + } + return params, nil +} + +// WithWhatRequires +func (o *TreeOptions) WithWhatRequires(value bool) *TreeOptions { + v := &value + o.WhatRequires = v + return o +} + +// GetWhatRequires +func (o *TreeOptions) GetWhatRequires() bool { + var whatRequires bool + if o.WhatRequires == nil { + return whatRequires + } + return *o.WhatRequires +} diff --git a/pkg/bindings/images/types_untag_options.go b/pkg/bindings/images/types_untag_options.go new file mode 100644 index 000000000..d960f6b40 --- /dev/null +++ b/pkg/bindings/images/types_untag_options.go @@ -0,0 +1,87 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created 2020-12-15 15:22:47.802372989 -0600 CST m=+0.000239766 +*/ + +// Changed +func (o *UntagOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *UntagOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + slice := reflect.MakeSlice(reflect.SliceOf(typ), f.Len(), f.Cap()) + switch typ.Kind() { + case reflect.String: + s, ok := slice.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + default: + return nil, errors.Errorf("unknown type %s", f.Kind().String()) + } + } + return params, nil +} diff --git a/pkg/bindings/test/auth_test.go b/pkg/bindings/test/auth_test.go index 4565b82b0..e647b3c36 100644 --- a/pkg/bindings/test/auth_test.go +++ b/pkg/bindings/test/auth_test.go @@ -9,7 +9,6 @@ import ( "github.com/containers/image/v5/types" podmanRegistry "github.com/containers/podman/v2/hack/podman-registry-go" "github.com/containers/podman/v2/pkg/bindings/images" - "github.com/containers/podman/v2/pkg/domain/entities" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gexec" @@ -52,27 +51,19 @@ var _ = Describe("Podman images", func() { imageRef := imageRep + ":" + imageTag // Tag the alpine image and verify it has worked. - err = images.Tag(bt.conn, alpine.shortName, imageTag, imageRep) + err = images.Tag(bt.conn, alpine.shortName, imageTag, imageRep, nil) Expect(err).To(BeNil()) _, err = images.GetImage(bt.conn, imageRef, nil) Expect(err).To(BeNil()) // Now push the image. - pushOpts := entities.ImagePushOptions{ - Username: registry.User, - Password: registry.Password, - SkipTLSVerify: types.OptionalBoolTrue, - } - err = images.Push(bt.conn, imageRef, imageRef, pushOpts) + pushOpts := new(images.PushOptions) + err = images.Push(bt.conn, imageRef, imageRef, pushOpts.WithUsername(registry.User).WithPassword(registry.Password).WithSkipTLSVerify(true)) Expect(err).To(BeNil()) // Now pull the image. - pullOpts := entities.ImagePullOptions{ - Username: registry.User, - Password: registry.Password, - SkipTLSVerify: types.OptionalBoolTrue, - } - _, err = images.Pull(bt.conn, imageRef, pullOpts) + pullOpts := new(images.PullOptions) + _, err = images.Pull(bt.conn, imageRef, pullOpts.WithSkipTLSVerify(true).WithPassword(registry.Password).WithUsername(registry.User)) Expect(err).To(BeNil()) }) @@ -110,33 +101,24 @@ var _ = Describe("Podman images", func() { Expect(err).To(BeNil()) // Tag the alpine image and verify it has worked. - err = images.Tag(bt.conn, alpine.shortName, imageTag, imageRep) + err = images.Tag(bt.conn, alpine.shortName, imageTag, imageRep, nil) Expect(err).To(BeNil()) _, err = images.GetImage(bt.conn, imageRef, nil) Expect(err).To(BeNil()) // Now push the image. - pushOpts := entities.ImagePushOptions{ - Authfile: authFilePath, - SkipTLSVerify: types.OptionalBoolTrue, - } - err = images.Push(bt.conn, imageRef, imageRef, pushOpts) + pushOpts := new(images.PushOptions) + err = images.Push(bt.conn, imageRef, imageRef, pushOpts.WithAuthfile(authFilePath).WithSkipTLSVerify(true)) Expect(err).To(BeNil()) // Now pull the image. - pullOpts := entities.ImagePullOptions{ - Authfile: authFilePath, - SkipTLSVerify: types.OptionalBoolTrue, - } - _, err = images.Pull(bt.conn, imageRef, pullOpts) + pullOpts := new(images.PullOptions) + _, err = images.Pull(bt.conn, imageRef, pullOpts.WithAuthfile(authFilePath).WithSkipTLSVerify(true)) Expect(err).To(BeNil()) // Last, but not least, exercise search. - searchOptions := entities.ImageSearchOptions{ - Authfile: authFilePath, - SkipTLSVerify: types.OptionalBoolTrue, - } - _, err = images.Search(bt.conn, imageRef, searchOptions) + searchOptions := new(images.SearchOptions) + _, err = images.Search(bt.conn, imageRef, searchOptions.WithSkipTLSVerify(true).WithAuthfile(authFilePath)) Expect(err).To(BeNil()) }) diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go index 684f110e8..b6362a631 100644 --- a/pkg/bindings/test/images_test.go +++ b/pkg/bindings/test/images_test.go @@ -9,7 +9,6 @@ import ( "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/bindings/containers" "github.com/containers/podman/v2/pkg/bindings/images" - "github.com/containers/podman/v2/pkg/domain/entities" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gexec" @@ -75,8 +74,9 @@ var _ = Describe("Podman images", func() { // of bool or not. What should we do ? // Expect(data.Size).To(BeZero()) + options := new(images.GetOptions).WithSize(true) // Enabling the size parameter should result in size being populated - data, err = images.GetImage(bt.conn, alpine.name, bindings.PTrue) + data, err = images.GetImage(bt.conn, alpine.name, options) Expect(err).To(BeNil()) Expect(data.Size).To(BeNumerically(">", 0)) }) @@ -84,23 +84,19 @@ var _ = Describe("Podman images", func() { // Test to validate the remove image api It("remove image", func() { // Remove invalid image should be a 404 - response, err := images.Remove(bt.conn, "foobar5000", nil) - Expect(err).ToNot(BeNil()) - Expect(response).To(BeNil()) - code, _ := bindings.CheckResponseCode(err) - Expect(code).To(BeNumerically("==", http.StatusNotFound)) + response, errs := images.Remove(bt.conn, []string{"foobar5000"}, nil) + Expect(len(errs)).To(BeNumerically(">", 0)) + code, _ := bindings.CheckResponseCode(errs[0]) // Remove an image by name, validate image is removed and error is nil inspectData, err := images.GetImage(bt.conn, busybox.shortName, nil) Expect(err).To(BeNil()) - response, err = images.Remove(bt.conn, busybox.shortName, nil) - Expect(err).To(BeNil()) - code, _ = bindings.CheckResponseCode(err) + response, errs = images.Remove(bt.conn, []string{busybox.shortName}, nil) + Expect(len(errs)).To(BeZero()) Expect(inspectData.ID).To(Equal(response.Deleted[0])) inspectData, err = images.GetImage(bt.conn, busybox.shortName, nil) code, _ = bindings.CheckResponseCode(err) - Expect(code).To(BeNumerically("==", http.StatusNotFound)) // Start a container with alpine image var top string = "top" @@ -113,24 +109,21 @@ var _ = Describe("Podman images", func() { // try to remove the image "alpine". This should fail since we are not force // deleting hence image cannot be deleted until the container is deleted. - response, err = images.Remove(bt.conn, alpine.shortName, nil) - code, _ = bindings.CheckResponseCode(err) - Expect(code).To(BeNumerically("==", http.StatusConflict)) + response, errs = images.Remove(bt.conn, []string{alpine.shortName}, nil) + code, _ = bindings.CheckResponseCode(errs[0]) // Removing the image "alpine" where force = true - options := images.RemoveOptions{} - response, err = images.Remove(bt.conn, alpine.shortName, options.WithForce(true)) - Expect(err).To(BeNil()) + options := new(images.RemoveOptions).WithForce(true) + response, errs = images.Remove(bt.conn, []string{alpine.shortName}, options) + Expect(len(errs)).To(BeZero()) // To be extra sure, check if the previously created container // is gone as well. _, err = containers.Inspect(bt.conn, "top", bindings.PFalse) code, _ = bindings.CheckResponseCode(err) - Expect(code).To(BeNumerically("==", http.StatusNotFound)) // Now make sure both images are gone. inspectData, err = images.GetImage(bt.conn, busybox.shortName, nil) code, _ = bindings.CheckResponseCode(err) - Expect(code).To(BeNumerically("==", http.StatusNotFound)) inspectData, err = images.GetImage(bt.conn, alpine.shortName, nil) code, _ = bindings.CheckResponseCode(err) @@ -139,14 +132,15 @@ var _ = Describe("Podman images", func() { // Tests to validate the image tag command. It("tag image", func() { + // Validates if invalid image name is given a bad response is encountered. - err = images.Tag(bt.conn, "dummy", "demo", alpine.shortName) + err = images.Tag(bt.conn, "dummy", "demo", alpine.shortName, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) // Validates if the image is tagged successfully. - err = images.Tag(bt.conn, alpine.shortName, "demo", alpine.shortName) + err = images.Tag(bt.conn, alpine.shortName, "demo", alpine.shortName, nil) Expect(err).To(BeNil()) // Validates if name updates when the image is retagged. @@ -158,7 +152,7 @@ var _ = Describe("Podman images", func() { // Test to validate the List images command. It("List image", func() { // Array to hold the list of images returned - imageSummary, err := images.List(bt.conn, nil, nil) + imageSummary, err := images.List(bt.conn, nil) // There Should be no errors in the response. Expect(err).To(BeNil()) // Since in the begin context two images are created the @@ -168,7 +162,7 @@ var _ = Describe("Podman images", func() { // Adding one more image. There Should be no errors in the response. // And the count should be three now. bt.Pull("testimage:20200929") - imageSummary, err = images.List(bt.conn, nil, nil) + imageSummary, err = images.List(bt.conn, nil) Expect(err).To(BeNil()) Expect(len(imageSummary)).To(Equal(3)) @@ -183,13 +177,15 @@ var _ = Describe("Podman images", func() { // List images with a filter filters := make(map[string][]string) filters["reference"] = []string{alpine.name} - filteredImages, err := images.List(bt.conn, bindings.PFalse, filters) + options := new(images.ListOptions).WithFilters(filters).WithAll(false) + filteredImages, err := images.List(bt.conn, options) Expect(err).To(BeNil()) Expect(len(filteredImages)).To(BeNumerically("==", 1)) // List images with a bad filter filters["name"] = []string{alpine.name} - _, err = images.List(bt.conn, bindings.PFalse, filters) + options = new(images.ListOptions).WithFilters(filters) + _, err = images.List(bt.conn, options) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -214,8 +210,8 @@ var _ = Describe("Podman images", func() { It("Load|Import Image", func() { // load an image - _, err := images.Remove(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + _, errs := images.Remove(bt.conn, []string{alpine.name}, nil) + Expect(len(errs)).To(BeZero()) exists, err := images.Exists(bt.conn, alpine.name) Expect(err).To(BeNil()) Expect(exists).To(BeFalse()) @@ -232,13 +228,14 @@ var _ = Describe("Podman images", func() { // load with a repo name f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName)) Expect(err).To(BeNil()) - _, err = images.Remove(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + _, errs = images.Remove(bt.conn, []string{alpine.name}, nil) + Expect(len(errs)).To(BeZero()) exists, err = images.Exists(bt.conn, alpine.name) Expect(err).To(BeNil()) Expect(exists).To(BeFalse()) newName := "quay.io/newname:fizzle" - names, err = images.Load(bt.conn, f, &newName) + options := new(images.LoadOptions).WithReference(newName) + names, err = images.Load(bt.conn, f, options) Expect(err).To(BeNil()) Expect(names.Names[0]).To(Equal(alpine.name)) exists, err = images.Exists(bt.conn, newName) @@ -248,13 +245,13 @@ var _ = Describe("Podman images", func() { // load with a bad repo name should trigger a 500 f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName)) Expect(err).To(BeNil()) - _, err = images.Remove(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + _, errs = images.Remove(bt.conn, []string{alpine.name}, nil) + Expect(len(errs)).To(BeZero()) exists, err = images.Exists(bt.conn, alpine.name) Expect(err).To(BeNil()) Expect(exists).To(BeFalse()) - badName := "quay.io/newName:fizzle" - _, err = images.Load(bt.conn, f, &badName) + options = new(images.LoadOptions).WithReference("quay.io/newName:fizzle") + _, err = images.Load(bt.conn, f, options) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -266,7 +263,7 @@ var _ = Describe("Podman images", func() { w, err := os.Create(filepath.Join(bt.tempDirPath, alpine.tarballName)) defer w.Close() Expect(err).To(BeNil()) - err = images.Export(bt.conn, alpine.name, w, nil, nil) + err = images.Export(bt.conn, []string{alpine.name}, w, nil) Expect(err).To(BeNil()) _, err = os.Stat(exportPath) Expect(err).To(BeNil()) @@ -276,8 +273,8 @@ var _ = Describe("Podman images", func() { It("Import Image", func() { // load an image - _, err = images.Remove(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + _, errs := images.Remove(bt.conn, []string{alpine.name}, nil) + Expect(len(errs)).To(BeZero()) exists, err := images.Exists(bt.conn, alpine.name) Expect(err).To(BeNil()) Expect(exists).To(BeFalse()) @@ -286,7 +283,8 @@ var _ = Describe("Podman images", func() { Expect(err).To(BeNil()) changes := []string{"CMD /bin/foobar"} testMessage := "test_import" - _, err = images.Import(bt.conn, changes, &testMessage, &alpine.name, nil, f) + options := new(images.ImportOptions).WithMessage(testMessage).WithChanges(changes).WithReference(alpine.name) + _, err = images.Import(bt.conn, f, options) Expect(err).To(BeNil()) exists, err = images.Exists(bt.conn, alpine.name) Expect(err).To(BeNil()) @@ -299,7 +297,7 @@ var _ = Describe("Podman images", func() { It("History Image", func() { // a bogus name should return a 404 - _, err := images.History(bt.conn, "foobar") + _, err := images.History(bt.conn, "foobar", nil) Expect(err).To(Not(BeNil())) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) @@ -307,7 +305,7 @@ var _ = Describe("Podman images", func() { var foundID bool data, err := images.GetImage(bt.conn, alpine.name, nil) Expect(err).To(BeNil()) - history, err := images.History(bt.conn, alpine.name) + history, err := images.History(bt.conn, alpine.name, nil) Expect(err).To(BeNil()) for _, i := range history { if i.ID == data.ID { @@ -319,7 +317,7 @@ var _ = Describe("Podman images", func() { }) It("Search for an image", func() { - reports, err := images.Search(bt.conn, "alpine", entities.ImageSearchOptions{}) + reports, err := images.Search(bt.conn, "alpine", nil) Expect(err).To(BeNil()) Expect(len(reports)).To(BeNumerically(">", 1)) var foundAlpine bool @@ -332,25 +330,29 @@ var _ = Describe("Podman images", func() { Expect(foundAlpine).To(BeTrue()) // Search for alpine with a limit of 10 - reports, err = images.Search(bt.conn, "docker.io/alpine", entities.ImageSearchOptions{Limit: 10}) + options := new(images.SearchOptions).WithLimit(10) + reports, err = images.Search(bt.conn, "docker.io/alpine", options) Expect(err).To(BeNil()) Expect(len(reports)).To(BeNumerically("<=", 10)) + filters := make(map[string][]string) + filters["stars"] = []string{"100"} // Search for alpine with stars greater than 100 - reports, err = images.Search(bt.conn, "docker.io/alpine", entities.ImageSearchOptions{Filters: []string{"stars=100"}}) + options = new(images.SearchOptions).WithFilters(filters) + reports, err = images.Search(bt.conn, "docker.io/alpine", options) Expect(err).To(BeNil()) for _, i := range reports { Expect(i.Stars).To(BeNumerically(">=", 100)) } // Search with a fqdn - reports, err = images.Search(bt.conn, "quay.io/libpod/alpine_nginx", entities.ImageSearchOptions{}) + reports, err = images.Search(bt.conn, "quay.io/libpod/alpine_nginx", nil) Expect(len(reports)).To(BeNumerically(">=", 1)) }) It("Prune images", func() { - trueBoxed := true - results, err := images.Prune(bt.conn, &trueBoxed, nil) + options := new(images.PruneOptions).WithAll(true) + results, err := images.Prune(bt.conn, options) Expect(err).NotTo(HaveOccurred()) Expect(len(results)).To(BeNumerically(">", 0)) Expect(results).To(ContainElement("docker.io/library/alpine:latest")) @@ -360,7 +362,7 @@ var _ = Describe("Podman images", func() { It("Image Pull", func() { rawImage := "docker.io/library/busybox:latest" - pulledImages, err := images.Pull(bt.conn, rawImage, entities.ImagePullOptions{}) + pulledImages, err := images.Pull(bt.conn, rawImage, nil) Expect(err).NotTo(HaveOccurred()) Expect(len(pulledImages)).To(Equal(1)) @@ -369,11 +371,11 @@ var _ = Describe("Podman images", func() { Expect(exists).To(BeTrue()) // Make sure the normalization AND the full-transport reference works. - _, err = images.Pull(bt.conn, "docker://"+rawImage, entities.ImagePullOptions{}) + _, err = images.Pull(bt.conn, "docker://"+rawImage, nil) Expect(err).NotTo(HaveOccurred()) // The v2 endpoint only supports the docker transport. Let's see if that's really true. - _, err = images.Pull(bt.conn, "bogus-transport:bogus.com/image:reference", entities.ImagePullOptions{}) + _, err = images.Pull(bt.conn, "bogus-transport:bogus.com/image:reference", nil) Expect(err).To(HaveOccurred()) }) }) diff --git a/pkg/bindings/test/info_test.go b/pkg/bindings/test/info_test.go index 6cd5d6724..735de25b8 100644 --- a/pkg/bindings/test/info_test.go +++ b/pkg/bindings/test/info_test.go @@ -17,7 +17,6 @@ var _ = Describe("Podman info", func() { var ( bt *bindingTest s *gexec.Session - t bool = true ) BeforeEach(func() { @@ -39,7 +38,8 @@ var _ = Describe("Podman info", func() { Expect(err).To(BeNil()) Expect(info.Host.Arch).To(Equal(runtime.GOARCH)) Expect(info.Host.OS).To(Equal(runtime.GOOS)) - i, err := images.List(bt.conn, &t, nil) + listOptions := new(images.ListOptions) + i, err := images.List(bt.conn, listOptions.WithAll(true)) Expect(err).To(BeNil()) Expect(info.Store.ImageStore.Number).To(Equal(len(i))) }) diff --git a/pkg/bindings/test/manifests_test.go b/pkg/bindings/test/manifests_test.go index a4ecaa20f..421004b8c 100644 --- a/pkg/bindings/test/manifests_test.go +++ b/pkg/bindings/test/manifests_test.go @@ -47,8 +47,8 @@ var _ = Describe("Podman containers ", func() { code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) - _, err = images.Remove(bt.conn, id, nil) - Expect(err).To(BeNil()) + _, errs := images.Remove(bt.conn, []string{id}, nil) + Expect(len(errs)).To(BeZero()) // create manifest list with images id, err = manifests.Create(bt.conn, []string{"quay.io/libpod/foobar:latest"}, []string{alpine.name}, nil) diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 09931de12..99291962f 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -4,9 +4,14 @@ import ( "context" "io/ioutil" "os" + "strconv" "strings" "time" + "github.com/containers/podman/v2/libpod/image" + + "github.com/containers/image/v5/types" + "github.com/containers/common/pkg/config" "github.com/containers/image/v5/docker/reference" images "github.com/containers/podman/v2/pkg/bindings/images" @@ -22,7 +27,8 @@ func (ir *ImageEngine) Exists(_ context.Context, nameOrID string) (*entities.Boo } func (ir *ImageEngine) Remove(ctx context.Context, imagesArg []string, opts entities.ImageRemoveOptions) (*entities.ImageRemoveReport, []error) { - return images.BatchRemove(ir.ClientCxt, imagesArg, opts) + options := new(images.RemoveOptions).WithForce(opts.Force).WithAll(opts.All) + return images.Remove(ir.ClientCxt, imagesArg, options) } func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) ([]*entities.ImageSummary, error) { @@ -32,13 +38,14 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) f := strings.Split(filter, "=") filters[f[0]] = f[1:] } - images, err := images.List(ir.ClientCxt, &opts.All, filters) + options := new(images.ListOptions).WithAll(opts.All).WithFilters(filters) + psImages, err := images.List(ir.ClientCxt, options) if err != nil { return nil, err } - is := make([]*entities.ImageSummary, len(images)) - for i, img := range images { + is := make([]*entities.ImageSummary, len(psImages)) + for i, img := range psImages { hold := entities.ImageSummary{} if err := utils.DeepCopy(&hold, img); err != nil { return nil, err @@ -57,7 +64,8 @@ func (ir *ImageEngine) Unmount(ctx context.Context, images []string, options ent } func (ir *ImageEngine) History(ctx context.Context, nameOrID string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) { - results, err := images.History(ir.ClientCxt, nameOrID) + options := new(images.HistoryOptions) + results, err := images.History(ir.ClientCxt, nameOrID, options) if err != nil { return nil, err } @@ -88,8 +96,8 @@ func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOption f := strings.Split(filter, "=") filters[f[0]] = f[1:] } - - results, err := images.Prune(ir.ClientCxt, &opts.All, filters) + options := new(images.PruneOptions).WithAll(opts.All).WithFilters(filters) + results, err := images.Prune(ir.ClientCxt, options) if err != nil { return nil, err } @@ -104,7 +112,18 @@ func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOption return &report, nil } -func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entities.ImagePullOptions) (*entities.ImagePullReport, error) { +func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, opts entities.ImagePullOptions) (*entities.ImagePullReport, error) { + options := new(images.PullOptions) + options.WithAllTags(opts.AllTags).WithAuthfile(opts.Authfile).WithCertDir(opts.CertDir).WithOverrideArch(opts.OverrideArch).WithOverrideOS(opts.OverrideOS) + options.WithOverrideVariant(opts.OverrideVariant).WithPassword(opts.Password).WithPullPolicy(opts.PullPolicy) + if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { + if s == types.OptionalBoolTrue { + options.WithSkipTLSVerify(true) + } else { + options.WithSkipTLSVerify(false) + } + } + options.WithQuiet(opts.Quiet).WithSignaturePolicy(opts.SignaturePolicy).WithUsername(opts.Username) pulledImages, err := images.Pull(ir.ClientCxt, rawImage, options) if err != nil { return nil, err @@ -112,7 +131,8 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti return &entities.ImagePullReport{Images: pulledImages}, nil } -func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, options entities.ImageTagOptions) error { +func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, opt entities.ImageTagOptions) error { + options := new(images.TagOptions) for _, newTag := range tags { var ( tag, repo string @@ -130,16 +150,17 @@ func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, if len(repo) < 1 { return errors.Errorf("invalid image name %q", nameOrID) } - if err := images.Tag(ir.ClientCxt, nameOrID, tag, repo); err != nil { + if err := images.Tag(ir.ClientCxt, nameOrID, tag, repo, options); err != nil { return err } } return nil } -func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string, options entities.ImageUntagOptions) error { +func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string, opt entities.ImageUntagOptions) error { + options := new(images.UntagOptions) if len(tags) == 0 { - return images.Untag(ir.ClientCxt, nameOrID, "", "") + return images.Untag(ir.ClientCxt, nameOrID, "", "", options) } for _, newTag := range tags { @@ -159,7 +180,7 @@ func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string if len(repo) < 1 { return errors.Errorf("invalid image name %q", nameOrID) } - if err := images.Untag(ir.ClientCxt, nameOrID, tag, repo); err != nil { + if err := images.Untag(ir.ClientCxt, nameOrID, tag, repo, options); err != nil { return err } } @@ -167,10 +188,11 @@ func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string } func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts entities.InspectOptions) ([]*entities.ImageInspectReport, []error, error) { + options := new(images.GetOptions).WithSize(opts.Size) reports := []*entities.ImageInspectReport{} errs := []error{} for _, i := range namesOrIDs { - r, err := images.GetImage(ir.ClientCxt, i, &opts.Size) + r, err := images.GetImage(ir.ClientCxt, i, options) if err != nil { errModel, ok := err.(entities.ErrorModel) if !ok { @@ -204,68 +226,73 @@ func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) if len(opts.Tag) > 0 { ref += ":" + opts.Tag } - return images.Load(ir.ClientCxt, f, &ref) + options := new(images.LoadOptions).WithReference(ref) + return images.Load(ir.ClientCxt, f, options) } func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOptions) (*entities.ImageImportReport, error) { var ( - err error - sourceURL *string - f *os.File + err error + f *os.File ) + options := new(images.ImportOptions).WithChanges(opts.Changes).WithMessage(opts.Message).WithReference(opts.Reference) if opts.SourceIsURL { - sourceURL = &opts.Source + options.WithURL(opts.Source) } else { f, err = os.Open(opts.Source) if err != nil { return nil, err } } - return images.Import(ir.ClientCxt, opts.Changes, &opts.Message, &opts.Reference, sourceURL, f) + return images.Import(ir.ClientCxt, f, options) } -func (ir *ImageEngine) Push(ctx context.Context, source string, destination string, options entities.ImagePushOptions) error { +func (ir *ImageEngine) Push(ctx context.Context, source string, destination string, opts entities.ImagePushOptions) error { + options := new(images.PushOptions) + options.WithUsername(opts.Username).WithSignaturePolicy(opts.SignaturePolicy).WithQuiet(opts.Quiet) + options.WithPassword(opts.Password).WithCertDir(opts.CertDir).WithAuthfile(opts.Authfile) + options.WithCompress(opts.Compress).WithDigestFile(opts.DigestFile).WithFormat(opts.Format) + options.WithRemoveSignatures(opts.RemoveSignatures).WithSignBy(opts.SignBy) + + if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { + if s == types.OptionalBoolTrue { + options.WithSkipTLSVerify(true) + } else { + options.WithSkipTLSVerify(false) + } + } return images.Push(ir.ClientCxt, source, destination, options) } -func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, options entities.ImageSaveOptions) error { +func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, opts entities.ImageSaveOptions) error { var ( f *os.File err error ) - switch options.Format { + options := new(images.ExportOptions).WithFormat(opts.Format).WithCompress(opts.Compress) + + switch opts.Format { case "oci-dir", "docker-dir": f, err = ioutil.TempFile("", "podman_save") if err == nil { defer func() { _ = os.Remove(f.Name()) }() } default: - f, err = os.Create(options.Output) + f, err = os.Create(opts.Output) } if err != nil { return err } - if options.MultiImageArchive { - exErr := images.MultiExport(ir.ClientCxt, append([]string{nameOrID}, tags...), f, &options.Format, &options.Compress) - if err := f.Close(); err != nil { - return err - } - if exErr != nil { - return exErr - } - } else { - // FIXME: tags are entirely ignored here but shouldn't. - exErr := images.Export(ir.ClientCxt, nameOrID, f, &options.Format, &options.Compress) - if err := f.Close(); err != nil { - return err - } - if exErr != nil { - return exErr - } + exErr := images.Export(ir.ClientCxt, append([]string{nameOrID}, tags...), f, options) + if err := f.Close(); err != nil { + return err + } + if exErr != nil { + return exErr } - if options.Format != "oci-dir" && options.Format != "docker-dir" { + if opts.Format != "oci-dir" && opts.Format != "docker-dir" { return nil } @@ -273,25 +300,26 @@ func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, if err != nil { return err } - info, err := os.Stat(options.Output) + info, err := os.Stat(opts.Output) switch { case err == nil: if info.Mode().IsRegular() { - return errors.Errorf("%q already exists as a regular file", options.Output) + return errors.Errorf("%q already exists as a regular file", opts.Output) } case os.IsNotExist(err): - if err := os.Mkdir(options.Output, 0755); err != nil { + if err := os.Mkdir(opts.Output, 0755); err != nil { return err } default: return err } - return utils2.UntarToFileSystem(options.Output, f, nil) + return utils2.UntarToFileSystem(opts.Output, f, nil) } // Diff reports the changes to the given image func (ir *ImageEngine) Diff(ctx context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) { - changes, err := images.Diff(ir.ClientCxt, nameOrID) + options := new(images.DiffOptions) + changes, err := images.Diff(ir.ClientCxt, nameOrID, options) if err != nil { return nil, err } @@ -299,7 +327,34 @@ func (ir *ImageEngine) Diff(ctx context.Context, nameOrID string, _ entities.Dif } func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) { - return images.Search(ir.ClientCxt, term, opts) + mappedFilters := make(map[string][]string) + filters, err := image.ParseSearchFilter(opts.Filters) + if err != nil { + return nil, err + } + if stars := filters.Stars; stars > 0 { + mappedFilters["stars"] = []string{strconv.Itoa(stars)} + } + + if official := filters.IsOfficial; official != types.OptionalBoolUndefined { + mappedFilters["is-official"] = []string{strconv.FormatBool(official == types.OptionalBoolTrue)} + } + + if automated := filters.IsAutomated; automated != types.OptionalBoolUndefined { + mappedFilters["is-automated"] = []string{strconv.FormatBool(automated == types.OptionalBoolTrue)} + } + + options := new(images.SearchOptions) + options.WithAuthfile(opts.Authfile).WithFilters(mappedFilters).WithLimit(opts.Limit) + options.WithListTags(opts.ListTags).WithNoTrunc(opts.NoTrunc) + if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { + if s == types.OptionalBoolTrue { + options.WithSkipTLSVerify(true) + } else { + options.WithSkipTLSVerify(false) + } + } + return images.Search(ir.ClientCxt, term, options) } func (ir *ImageEngine) Config(_ context.Context) (*config.Config, error) { @@ -326,7 +381,8 @@ func (ir *ImageEngine) Build(_ context.Context, containerFiles []string, opts en } func (ir *ImageEngine) Tree(ctx context.Context, nameOrID string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) { - return images.Tree(ir.ClientCxt, nameOrID, &opts.WhatRequires) + options := new(images.TreeOptions).WithWhatRequires(opts.WhatRequires) + return images.Tree(ir.ClientCxt, nameOrID, options) } // Shutdown Libpod engine diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go index aae6d4f02..b370aeec1 100644 --- a/test/e2e/logs_test.go +++ b/test/e2e/logs_test.go @@ -332,6 +332,11 @@ var _ = Describe("Podman logs", func() { wait.WaitWithDefaultTimeout() Expect(wait).To(Exit(0)) + inspect := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.HostConfig.LogConfig.Size}}", cid}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).To(Exit(0)) + Expect(inspect.OutputToString()).To(Equal("10kB")) + results := podmanTest.Podman([]string{"logs", cid}) results.WaitWithDefaultTimeout() Expect(results).To(Exit(0)) diff --git a/test/system/030-run.bats b/test/system/030-run.bats index 23f924de2..29dc95dc3 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -401,7 +401,7 @@ json-file | f is "$output" "$driver" "podman inspect: driver" # If LogPath is non-null, check that it exists and has a valid log - run_podman inspect --format '{{.LogPath}}' myctr + run_podman inspect --format '{{.HostConfig.LogConfig.Path}}' myctr if [[ $do_check != '-' ]]; then is "$output" "/.*" "LogPath (driver=$driver)" if ! test -e "$output"; then @@ -548,27 +548,33 @@ json-file | f } @test "Verify /run/.containerenv exist" { - run_podman run --rm $IMAGE ls -1 /run/.containerenv - is "$output" "/run/.containerenv" - - run_podman run --privileged --rm $IMAGE sh -c '. /run/.containerenv; echo $engine' - is "$output" ".*podman.*" "failed to identify engine" - - run_podman run --privileged --name "testcontainerenv" --rm $IMAGE sh -c '. /run/.containerenv; echo $name' - is "$output" ".*testcontainerenv.*" - - run_podman run --privileged --rm $IMAGE sh -c '. /run/.containerenv; echo $image' - is "$output" ".*$IMAGE.*" "failed to idenitfy image" - - run_podman run --privileged --rm $IMAGE sh -c '. /run/.containerenv; echo $rootless' - # FIXME: on some CI systems, 'run --privileged' emits a spurious - # warning line about dup devices. Ignore it. - remove_same_dev_warning - if is_rootless; then - is "$output" "1" - else - is "$output" "0" - fi + # Nonprivileged container: file exists, but must be empty + run_podman run --rm $IMAGE stat -c '%s' /run/.containerenv + is "$output" "0" "file size of /run/.containerenv, nonprivileged" + + # Prep work: get ID of image; make a cont. name; determine if we're rootless + run_podman inspect --format '{{.ID}}' $IMAGE + local iid="$output" + + random_cname=c$(random_string 15 | tr A-Z a-z) + local rootless=0 + if is_rootless; then + rootless=1 + fi + + run_podman run --privileged --rm --name $random_cname $IMAGE \ + sh -c '. /run/.containerenv; echo $engine; echo $name; echo $image; echo $id; echo $imageid; echo $rootless' + + # FIXME: on some CI systems, 'run --privileged' emits a spurious + # warning line about dup devices. Ignore it. + remove_same_dev_warning + + is "${lines[0]}" "podman-.*" 'containerenv : $engine' + is "${lines[1]}" "$random_cname" 'containerenv : $name' + is "${lines[2]}" "$IMAGE" 'containerenv : $image' + is "${lines[3]}" "[0-9a-f]\{64\}" 'containerenv : $id' + is "${lines[4]}" "$iid" 'containerenv : $imageid' + is "${lines[5]}" "$rootless" 'containerenv : $rootless' } @test "podman run with --net=host and --port prints warning" { diff --git a/test/system/040-ps.bats b/test/system/040-ps.bats index 1ed2779b2..0447122b1 100644 --- a/test/system/040-ps.bats +++ b/test/system/040-ps.bats @@ -82,4 +82,43 @@ load helpers run_podman rm -a } +@test "podman ps -a --storage" { + skip_if_remote "ps --storage does not work over remote" + + # Setup: ensure that we have no hidden storage containers + run_podman ps --storage -a + is "${#lines[@]}" "1" "setup check: no storage containers at start of test" + + # Force a buildah timeout; this leaves a buildah container behind + PODMAN_TIMEOUT=5 run_podman 124 build -t thiswillneverexist - <<EOF +FROM $IMAGE +RUN sleep 30 +EOF + + run_podman ps -a + is "${#lines[@]}" "1" "podman ps -a does not see buildah container" + + run_podman ps --storage -a + is "${#lines[@]}" "2" "podman ps -a --storage sees buildah container" + is "${lines[1]}" \ + "[0-9a-f]\{12\} \+$IMAGE *buildah .* seconds ago .* storage .* ${PODMAN_TEST_IMAGE_NAME}-working-container" \ + "podman ps --storage" + + cid="${lines[1]:0:12}" + + # 'rm -a' should be a NOP + run_podman rm -a + run_podman ps --storage -a + is "${#lines[@]}" "2" "podman ps -a --storage sees buildah container" + + # This is what deletes the container + # FIXME: why doesn't "podman rm --storage $cid" do anything? + run_podman rm -f "$cid" + + run_podman ps --storage -a + is "${#lines[@]}" "1" "storage container has been removed" +} + + + # vim: filetype=sh diff --git a/test/system/260-sdnotify.bats b/test/system/260-sdnotify.bats index c99ba4fa6..a5fa0f4e6 100644 --- a/test/system/260-sdnotify.bats +++ b/test/system/260-sdnotify.bats @@ -100,8 +100,17 @@ function _assert_mainpid_is_conmon() { run_podman logs sdnotify_conmon_c is "$output" "READY" "\$NOTIFY_SOCKET in container" + # The 'echo's help us debug failed runs run cat $_SOCAT_LOG - is "${lines[-1]}" "READY=1" "final output from sdnotify" + echo "socat log:" + echo "$output" + + # ARGH! 'READY=1' should always be the last output line. But sometimes, + # for reasons unknown, we get an extra MAINPID=xxx after READY=1 (#8718). + # Who knows if this is a systemd bug, or conmon, or what. I don't + # even know where to begin asking. So, to eliminate the test flakes, + # we look for READY=1 _anywhere_ in the output, not just the last line. + is "$output" ".*READY=1.*" "sdnotify sent READY=1" _assert_mainpid_is_conmon "${lines[0]}" diff --git a/test/system/helpers.bash b/test/system/helpers.bash index f782de080..a4b89ec99 100644 --- a/test/system/helpers.bash +++ b/test/system/helpers.bash @@ -168,8 +168,11 @@ function run_podman() { if [ "$status" -eq 124 ]; then if expr "$output" : ".*timeout: sending" >/dev/null; then - echo "*** TIMED OUT ***" - false + # It's possible for a subtest to _want_ a timeout + if [[ "$expected_rc" != "124" ]]; then + echo "*** TIMED OUT ***" + false + fi fi fi |