diff options
Diffstat (limited to 'pkg')
29 files changed, 439 insertions, 206 deletions
diff --git a/pkg/api/handlers/compat/images_search.go b/pkg/api/handlers/compat/images_search.go index 6808cdad5..885112b31 100644 --- a/pkg/api/handlers/compat/images_search.go +++ b/pkg/api/handlers/compat/images_search.go @@ -1,25 +1,30 @@ package compat import ( + "fmt" "net/http" - "strconv" "github.com/containers/image/v5/types" - "github.com/containers/podman/v2/libpod/image" + "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/api/handlers/utils" "github.com/containers/podman/v2/pkg/auth" - "github.com/docker/docker/api/types/registry" + "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/infra/abi" "github.com/gorilla/schema" "github.com/pkg/errors" ) func SearchImages(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { 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 } @@ -29,67 +34,40 @@ 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{ - Filter: filter, - Limit: query.Limit, - } - - if _, found := r.URL.Query()["tlsVerify"]; found { - options.InsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) - } - _, 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())) return } defer auth.RemoveAuthfile(authfile) - options.Authfile = authfile - results, err := image.SearchImages(query.Term, options) + filters := []string{} + for key, val := range query.Filters { + filters = append(filters, fmt.Sprintf("%s=%s", key, val[0])) + } + + options := entities.ImageSearchOptions{ + Authfile: authfile, + Limit: query.Limit, + NoTrunc: query.NoTrunc, + ListTags: query.ListTags, + Filters: filters, + } + if _, found := r.URL.Query()["tlsVerify"]; found { + options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) + } + ir := abi.ImageEngine{Libpod: runtime} + reports, err := ir.Search(r.Context(), query.Term, options) if err != nil { - utils.BadRequest(w, "term", query.Term, err) + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) return } - - compatResults := make([]registry.SearchResult, 0, len(results)) - for _, result := range results { - compatResult := registry.SearchResult{ - Name: result.Name, - Description: result.Description, - StarCount: result.Stars, - IsAutomated: result.Automated == "[OK]", - IsOfficial: result.Official == "[OK]", + if !utils.IsLibpodRequest(r) { + if len(reports) == 0 { + utils.ImageNotFound(w, query.Term, define.ErrNoSuchImage) + return } - compatResults = append(compatResults, compatResult) } - utils.WriteResponse(w, http.StatusOK, compatResults) + utils.WriteResponse(w, http.StatusOK, reports) } diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 97cd5a65e..3b531652f 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -589,92 +589,6 @@ func UntagImage(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusCreated, "") } -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 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 - } - - 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 - } - - 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) - } - - _, 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())) - return - } - defer auth.RemoveAuthfile(authfile) - options.Authfile = authfile - - searchResults, err := image.SearchImages(query.Term, options) - if err != nil { - utils.BadRequest(w, "term", query.Term, err) - return - } - // Convert from image.SearchResults to entities.ImageSearchReport. We don't - // want to leak any low-level packages into the remote client, which - // requires converting. - reports := make([]entities.ImageSearchReport, len(searchResults)) - for i := range searchResults { - reports[i].Index = searchResults[i].Index - reports[i].Name = searchResults[i].Name - reports[i].Description = searchResults[i].Description - reports[i].Stars = searchResults[i].Stars - reports[i].Official = searchResults[i].Official - reports[i].Automated = searchResults[i].Automated - reports[i].Tag = searchResults[i].Tag - } - - utils.WriteResponse(w, http.StatusOK, reports) -} - // ImagesBatchRemove is the endpoint for batch image removal. func ImagesBatchRemove(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) diff --git a/pkg/api/handlers/libpod/images_pull.go b/pkg/api/handlers/libpod/images_pull.go index bacba006d..efb15b14d 100644 --- a/pkg/api/handlers/libpod/images_pull.go +++ b/pkg/api/handlers/libpod/images_pull.go @@ -30,12 +30,12 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { - Reference string `schema:"reference"` - OverrideOS string `schema:"overrideOS"` - OverrideArch string `schema:"overrideArch"` - OverrideVariant string `schema:"overrideVariant"` - TLSVerify bool `schema:"tlsVerify"` - AllTags bool `schema:"allTags"` + Reference string `schema:"reference"` + OS string `schema:"OS"` + Arch string `schema:"Arch"` + Variant string `schema:"Variant"` + TLSVerify bool `schema:"tlsVerify"` + AllTags bool `schema:"allTags"` }{ TLSVerify: true, } @@ -83,9 +83,9 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { // Setup the registry options dockerRegistryOptions := image.DockerRegistryOptions{ DockerRegistryCreds: authConf, - OSChoice: query.OverrideOS, - ArchitectureChoice: query.OverrideArch, - VariantChoice: query.OverrideVariant, + OSChoice: query.OS, + ArchitectureChoice: query.Arch, + VariantChoice: query.Variant, } if _, found := r.URL.Query()["tlsVerify"]; found { dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go index dce861f6f..35221ecf1 100644 --- a/pkg/api/handlers/libpod/manifests.go +++ b/pkg/api/handlers/libpod/manifests.go @@ -48,6 +48,24 @@ func ManifestCreate(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: manID}) } +// ExistsManifest check if a manifest list exists +func ExistsManifest(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) + name := utils.GetName(r) + + ic := abi.ImageEngine{Libpod: runtime} + report, err := ic.ManifestExists(r.Context(), name) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) + return + } + if !report.Value { + utils.Error(w, "manifest not found", http.StatusNotFound, errors.New("manifest not found")) + return + } + utils.WriteResponse(w, http.StatusNoContent, "") +} + func ManifestInspect(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) name := utils.GetName(r) diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go index 38fdf1b4d..1a8759c6c 100644 --- a/pkg/api/handlers/libpod/volumes.go +++ b/pkg/api/handlers/libpod/volumes.go @@ -11,6 +11,7 @@ import ( "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/domain/filters" + "github.com/containers/podman/v2/pkg/domain/infra/abi" "github.com/containers/podman/v2/pkg/domain/infra/abi/parse" "github.com/gorilla/schema" "github.com/pkg/errors" @@ -203,3 +204,21 @@ func RemoveVolume(w http.ResponseWriter, r *http.Request) { } utils.WriteResponse(w, http.StatusNoContent, "") } + +// ExistsVolume check if a volume exists +func ExistsVolume(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) + name := utils.GetName(r) + + ic := abi.ContainerEngine{Libpod: runtime} + report, err := ic.VolumeExists(r.Context(), name) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) + return + } + if !report.Value { + utils.Error(w, "volume not found", http.StatusNotFound, define.ErrNoSuchVolume) + return + } + utils.WriteResponse(w, http.StatusNoContent, "") +} diff --git a/pkg/api/handlers/swagger/swagger.go b/pkg/api/handlers/swagger/swagger.go index 22670d795..32f041dd3 100644 --- a/pkg/api/handlers/swagger/swagger.go +++ b/pkg/api/handlers/swagger/swagger.go @@ -1,7 +1,6 @@ package swagger import ( - "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/image" "github.com/containers/podman/v2/pkg/api/handlers" @@ -166,7 +165,7 @@ type swagInspectPodResponse struct { type swagInspectVolumeResponse struct { // in:body Body struct { - libpod.InspectVolumeData + define.InspectVolumeData } } diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index 8d0c0800b..a7d350d50 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -930,15 +930,15 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // description: "username:password for the registry" // type: string // - in: query - // name: overrideArch + // name: Arch // description: Pull image for the specified architecture. // type: string // - in: query - // name: overrideOS + // name: OS // description: Pull image for the specified operating system. // type: string // - in: query - // name: overrideVariant + // name: Variant // description: Pull image for the specified variant. // type: string // - in: query @@ -1019,7 +1019,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: "#/responses/DocsSearchResponse" // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/libpod/images/search"), s.APIHandler(libpod.SearchImages)).Methods(http.MethodGet) + r.Handle(VersionedPath("/libpod/images/search"), s.APIHandler(compat.SearchImages)).Methods(http.MethodGet) // swagger:operation GET /libpod/images/{name:.*}/get libpod libpodExportImage // --- // tags: diff --git a/pkg/api/server/register_manifest.go b/pkg/api/server/register_manifest.go index 5e7d94538..eefcc396e 100644 --- a/pkg/api/server/register_manifest.go +++ b/pkg/api/server/register_manifest.go @@ -38,6 +38,26 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error { // 500: // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/libpod/manifests/create"), s.APIHandler(libpod.ManifestCreate)).Methods(http.MethodPost) + // swagger:operation GET /libpod/manifests/{name}/exists manifests Exists + // --- + // summary: Exists + // description: Check if manifest list exists + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: the name of the manifest list + // produces: + // - application/json + // responses: + // 204: + // description: manifest list exists + // 404: + // $ref: '#/responses/NoSuchManifest' + // 500: + // $ref: '#/responses/InternalError' + r.Handle(VersionedPath("/libpod/manifests/{name}/exists"), s.APIHandler(libpod.ExistsManifest)).Methods(http.MethodGet) // swagger:operation GET /libpod/manifests/{name:.*}/json manifests Inspect // --- // summary: Inspect diff --git a/pkg/api/server/register_volumes.go b/pkg/api/server/register_volumes.go index aa0f67604..68727f2e1 100644 --- a/pkg/api/server/register_volumes.go +++ b/pkg/api/server/register_volumes.go @@ -28,6 +28,28 @@ func (s *APIServer) registerVolumeHandlers(r *mux.Router) error { // '500': // "$ref": "#/responses/InternalError" r.Handle(VersionedPath("/libpod/volumes/create"), s.APIHandler(libpod.CreateVolume)).Methods(http.MethodPost) + // swagger:operation GET /libpod/volumes/{name}/exists libpod libpodExistsVolume + // --- + // tags: + // - volumes + // summary: Volume exists + // description: Check if a volume exists + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: the name of the volume + // produces: + // - application/json + // responses: + // 204: + // description: volume exists + // 404: + // $ref: '#/responses/NoSuchVolume' + // 500: + // $ref: '#/responses/InternalError' + r.Handle(VersionedPath("/libpod/volumes/{name}/exists"), s.APIHandler(libpod.ExistsVolume)).Methods(http.MethodGet) // swagger:operation GET /libpod/volumes/json libpod libpodListVolumes // --- // tags: diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index 046f6561c..d612041f6 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -179,15 +179,15 @@ func setupSystemd() { func (s *APIServer) Serve() error { setupSystemd() - // Start the shutdown signal handler. - if err := shutdown.Start(); err != nil { - return err - } if err := shutdown.Register("server", func(sig os.Signal) error { return s.Shutdown() }); err != nil { return err } + // Start the shutdown signal handler. + if err := shutdown.Start(); err != nil { + return err + } errChan := make(chan error, 1) diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index fcbf6fe39..86d92c028 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -297,7 +297,9 @@ func imageAuthToDockerAuth(authConfig types.DockerAuthConfig) dockerAPITypes.Aut func singleAuthHeader(r *http.Request) (map[string]types.DockerAuthConfig, error) { authHeader := r.Header.Get(string(XRegistryAuthHeader)) authConfig := dockerAPITypes.AuthConfig{} - if len(authHeader) > 0 { + // Accept "null" and handle it as empty value for compatibility reason with Docker. + // Some java docker clients pass this value, e.g. this one used in Eclipse. + if len(authHeader) > 0 && authHeader != "null" { authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authHeader)) if err := json.NewDecoder(authJSON).Decode(&authConfig); err != nil { return nil, err @@ -312,7 +314,9 @@ func singleAuthHeader(r *http.Request) (map[string]types.DockerAuthConfig, error // The header content is a map[string]DockerAuthConfigs. func multiAuthHeader(r *http.Request) (map[string]types.DockerAuthConfig, error) { authHeader := r.Header.Get(string(XRegistryAuthHeader)) - if len(authHeader) == 0 { + // Accept "null" and handle it as empty value for compatibility reason with Docker. + // Some java docker clients pass this value, e.g. this one used in Eclipse. + if len(authHeader) == 0 || authHeader == "null" { return nil, nil } diff --git a/pkg/bindings/images/types.go b/pkg/bindings/images/types.go index f216dd073..0248f2fa6 100644 --- a/pkg/bindings/images/types.go +++ b/pkg/bindings/images/types.go @@ -171,13 +171,13 @@ type PullOptions struct { 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 + // Arch will overwrite the local architecture for image pulls. + Arch *string + // OS will overwrite the local operating system (OS) for image // pulls. - OverrideOS *string - // OverrideVariant will overwrite the local variant for image pulls. - OverrideVariant *string + OS *string + // Variant will overwrite the local variant for image pulls. + Variant *string // Quiet can be specified to suppress pull progress when pulling. Ignored // for remote calls. Quiet *bool diff --git a/pkg/bindings/images/types_pull_options.go b/pkg/bindings/images/types_pull_options.go index 5163a6341..2bdf2b66e 100644 --- a/pkg/bindings/images/types_pull_options.go +++ b/pkg/bindings/images/types_pull_options.go @@ -168,52 +168,52 @@ func (o *PullOptions) GetPassword() string { return *o.Password } -// WithOverrideArch -func (o *PullOptions) WithOverrideArch(value string) *PullOptions { +// WithArch +func (o *PullOptions) WithArch(value string) *PullOptions { v := &value - o.OverrideArch = v + o.Arch = v return o } -// GetOverrideArch -func (o *PullOptions) GetOverrideArch() string { - var overrideArch string - if o.OverrideArch == nil { - return overrideArch +// GetArch +func (o *PullOptions) GetArch() string { + var arch string + if o.Arch == nil { + return arch } - return *o.OverrideArch + return *o.Arch } -// WithOverrideOS -func (o *PullOptions) WithOverrideOS(value string) *PullOptions { +// WithOS +func (o *PullOptions) WithOS(value string) *PullOptions { v := &value - o.OverrideOS = v + o.OS = v return o } -// GetOverrideOS -func (o *PullOptions) GetOverrideOS() string { - var overrideOS string - if o.OverrideOS == nil { - return overrideOS +// GetOS +func (o *PullOptions) GetOS() string { + var oS string + if o.OS == nil { + return oS } - return *o.OverrideOS + return *o.OS } -// WithOverrideVariant -func (o *PullOptions) WithOverrideVariant(value string) *PullOptions { +// WithVariant +func (o *PullOptions) WithVariant(value string) *PullOptions { v := &value - o.OverrideVariant = v + o.Variant = v return o } -// GetOverrideVariant -func (o *PullOptions) GetOverrideVariant() string { - var overrideVariant string - if o.OverrideVariant == nil { - return overrideVariant +// GetVariant +func (o *PullOptions) GetVariant() string { + var variant string + if o.Variant == nil { + return variant } - return *o.OverrideVariant + return *o.Variant } // WithQuiet diff --git a/pkg/bindings/manifests/manifests.go b/pkg/bindings/manifests/manifests.go index b6db64b02..fec9832a0 100644 --- a/pkg/bindings/manifests/manifests.go +++ b/pkg/bindings/manifests/manifests.go @@ -49,6 +49,19 @@ func Create(ctx context.Context, names, images []string, options *CreateOptions) return idr.ID, response.Process(&idr) } +// Exists returns true if a given maifest list exists +func Exists(ctx context.Context, name string, options *ExistsOptions) (bool, error) { + conn, err := bindings.GetClient(ctx) + if err != nil { + return false, err + } + response, err := conn.DoRequest(nil, http.MethodGet, "/manifests/%s/exists", nil, nil, name) + if err != nil { + return false, err + } + return response.IsSuccess(), nil +} + // Inspect returns a manifest list for a given name. func Inspect(ctx context.Context, name string, options *InspectOptions) (*manifest.Schema2List, error) { var list manifest.Schema2List diff --git a/pkg/bindings/manifests/types.go b/pkg/bindings/manifests/types.go index 7f84d69fc..fde90a865 100644 --- a/pkg/bindings/manifests/types.go +++ b/pkg/bindings/manifests/types.go @@ -11,6 +11,12 @@ type CreateOptions struct { All *bool } +//go:generate go run ../generator/generator.go ExistsOptions +// ExistsOptions are optional options for checking +// if a manifest list exists +type ExistsOptions struct { +} + //go:generate go run ../generator/generator.go AddOptions // AddOptions are optional options for adding manifests type AddOptions struct { diff --git a/pkg/bindings/manifests/types_exists_options.go b/pkg/bindings/manifests/types_exists_options.go new file mode 100644 index 000000000..fd2cd3ee9 --- /dev/null +++ b/pkg/bindings/manifests/types_exists_options.go @@ -0,0 +1,88 @@ +package manifests + +import ( + "net/url" + "reflect" + "strconv" + "strings" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. +*/ + +// Changed +func (o *ExistsOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ExistsOptions) 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 + } + fieldName = strings.ToLower(fieldName) + 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.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.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) + } + } + return params, nil +} diff --git a/pkg/bindings/volumes/types.go b/pkg/bindings/volumes/types.go index 379174e33..3fda77ddd 100644 --- a/pkg/bindings/volumes/types.go +++ b/pkg/bindings/volumes/types.go @@ -30,3 +30,9 @@ type RemoveOptions struct { // Force removes the volume even if it is being used Force *bool } + +//go:generate go run ../generator/generator.go ExistsOptions +// ExistsOptions are optional options for checking +// if a volume exists +type ExistsOptions struct { +} diff --git a/pkg/bindings/volumes/types_exists_options.go b/pkg/bindings/volumes/types_exists_options.go new file mode 100644 index 000000000..c66586a23 --- /dev/null +++ b/pkg/bindings/volumes/types_exists_options.go @@ -0,0 +1,88 @@ +package volumes + +import ( + "net/url" + "reflect" + "strconv" + "strings" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. +*/ + +// Changed +func (o *ExistsOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ExistsOptions) 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 + } + fieldName = strings.ToLower(fieldName) + 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.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.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) + } + } + return params, nil +} diff --git a/pkg/bindings/volumes/volumes.go b/pkg/bindings/volumes/volumes.go index fe081eb46..60fdd0a23 100644 --- a/pkg/bindings/volumes/volumes.go +++ b/pkg/bindings/volumes/volumes.go @@ -114,3 +114,16 @@ func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) error } return response.Process(nil) } + +// Exists returns true if a given volume exists +func Exists(ctx context.Context, nameOrID string, options *ExistsOptions) (bool, error) { + conn, err := bindings.GetClient(ctx) + if err != nil { + return false, err + } + response, err := conn.DoRequest(nil, http.MethodGet, "/volumes/%s/exists", nil, nil, nameOrID) + if err != nil { + return false, err + } + return response.IsSuccess(), nil +} diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 7b43ac961..39bda1d72 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -86,6 +86,7 @@ type ContainerEngine interface { Unshare(ctx context.Context, args []string) error Version(ctx context.Context) (*SystemVersionReport, error) VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IDOrNameResponse, error) + VolumeExists(ctx context.Context, namesOrId string) (*BoolReport, error) VolumeInspect(ctx context.Context, namesOrIds []string, opts InspectOptions) ([]*VolumeInspectReport, []error, error) VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error) VolumePrune(ctx context.Context, options VolumePruneOptions) ([]*reports.PruneReport, error) diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go index 935ee6f20..ee611502f 100644 --- a/pkg/domain/entities/engine_image.go +++ b/pkg/domain/entities/engine_image.go @@ -32,6 +32,7 @@ type ImageEngine interface { Unmount(ctx context.Context, images []string, options ImageUnmountOptions) ([]*ImageUnmountReport, error) Untag(ctx context.Context, nameOrID string, tags []string, options ImageUntagOptions) error ManifestCreate(ctx context.Context, names, images []string, opts ManifestCreateOptions) (string, error) + ManifestExists(ctx context.Context, name string) (*BoolReport, error) ManifestInspect(ctx context.Context, name string) ([]byte, error) ManifestAdd(ctx context.Context, opts ManifestAddOptions) (string, error) ManifestAnnotate(ctx context.Context, names []string, opts ManifestAnnotateOptions) (string, error) diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index 78a7d8aa7..ef40d5490 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -133,13 +133,13 @@ type ImagePullOptions struct { 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 + // Arch will overwrite the local architecture for image pulls. + Arch string + // OS will overwrite the local operating system (OS) for image // pulls. - OverrideOS string - // OverrideVariant will overwrite the local variant for image pulls. - OverrideVariant string + OS string + // Variant will overwrite the local variant for image pulls. + Variant string // Quiet can be specified to suppress pull progress when pulling. Ignored // for remote calls. Quiet bool diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 1288ab09b..8ca93e770 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -241,9 +241,9 @@ func pull(ctx context.Context, runtime *image.Runtime, rawImage string, options dockerRegistryOptions := image.DockerRegistryOptions{ DockerRegistryCreds: registryCreds, DockerCertPath: options.CertDir, - OSChoice: options.OverrideOS, - ArchitectureChoice: options.OverrideArch, - VariantChoice: options.OverrideVariant, + OSChoice: options.OS, + ArchitectureChoice: options.Arch, + VariantChoice: options.Variant, DockerInsecureSkipTLSVerify: options.SkipTLSVerify, } diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go index 139032ad6..626f1f7bf 100644 --- a/pkg/domain/infra/abi/manifest.go +++ b/pkg/domain/infra/abi/manifest.go @@ -40,6 +40,18 @@ func (ir *ImageEngine) ManifestCreate(ctx context.Context, names, images []strin return imageID, err } +// ManifestExists checks if a manifest list with the given name exists in local storage +func (ir *ImageEngine) ManifestExists(ctx context.Context, name string) (*entities.BoolReport, error) { + if image, err := ir.Libpod.ImageRuntime().NewFromLocal(name); err == nil { + exists, err := image.ExistsManifest() + if err != nil && errors.Cause(err) != buildahManifests.ErrManifestTypeNotSupported { + return nil, err + } + return &entities.BoolReport{Value: exists}, nil + } + return &entities.BoolReport{Value: false}, nil +} + // ManifestInspect returns the content of a manifest list or image func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte, error) { if newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name); err == nil { diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go index 823605052..f15aa2d14 100644 --- a/pkg/domain/infra/abi/volumes.go +++ b/pkg/domain/infra/abi/volumes.go @@ -153,3 +153,12 @@ func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeL } return reports, nil } + +// VolumeExists check if a given volume name exists +func (ic *ContainerEngine) VolumeExists(ctx context.Context, nameOrID string) (*entities.BoolReport, error) { + exists, err := ic.Libpod.HasVolume(nameOrID) + if err != nil { + return nil, err + } + return &entities.BoolReport{Value: exists}, nil +} diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 2d686b2aa..0de756756 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -106,8 +106,8 @@ func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOption 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) + options.WithAllTags(opts.AllTags).WithAuthfile(opts.Authfile).WithCertDir(opts.CertDir).WithArch(opts.Arch).WithOS(opts.OS) + options.WithVariant(opts.Variant).WithPassword(opts.Password).WithPullPolicy(opts.PullPolicy) if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { if s == types.OptionalBoolTrue { options.WithSkipTLSVerify(true) diff --git a/pkg/domain/infra/tunnel/manifest.go b/pkg/domain/infra/tunnel/manifest.go index 22ca44165..c12ba0045 100644 --- a/pkg/domain/infra/tunnel/manifest.go +++ b/pkg/domain/infra/tunnel/manifest.go @@ -23,6 +23,15 @@ func (ir *ImageEngine) ManifestCreate(ctx context.Context, names, images []strin return imageID, err } +// ManifestExists checks if a manifest list with the given name exists +func (ir *ImageEngine) ManifestExists(ctx context.Context, name string) (*entities.BoolReport, error) { + exists, err := manifests.Exists(ir.ClientCtx, name, nil) + if err != nil { + return nil, err + } + return &entities.BoolReport{Value: exists}, nil +} + // ManifestInspect returns contents of manifest list with given name func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte, error) { list, err := manifests.Inspect(ir.ClientCtx, name, nil) diff --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go index f21336828..ffd2c1d35 100644 --- a/pkg/domain/infra/tunnel/volumes.go +++ b/pkg/domain/infra/tunnel/volumes.go @@ -80,3 +80,14 @@ func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeL options := new(volumes.ListOptions).WithFilters(opts.Filter) return volumes.List(ic.ClientCtx, options) } + +// VolumeExists checks if the given volume exists +func (ic *ContainerEngine) VolumeExists(ctx context.Context, nameOrID string) (*entities.BoolReport, error) { + exists, err := volumes.Exists(ic.ClientCtx, nameOrID, nil) + if err != nil { + return nil, err + } + return &entities.BoolReport{ + Value: exists, + }, nil +} diff --git a/pkg/rootlessport/rootlessport_linux.go b/pkg/rootlessport/rootlessport_linux.go index c686d80fc..80e1309a5 100644 --- a/pkg/rootlessport/rootlessport_linux.go +++ b/pkg/rootlessport/rootlessport_linux.go @@ -48,6 +48,7 @@ type Config struct { ExitFD int ReadyFD int TmpDir string + ChildIP string } func init() { @@ -227,7 +228,7 @@ outer: // let parent expose ports logrus.Infof("exposing ports %v", cfg.Mappings) - if err := exposePorts(driver, cfg.Mappings); err != nil { + if err := exposePorts(driver, cfg.Mappings, cfg.ChildIP); err != nil { return err } @@ -248,7 +249,7 @@ outer: return nil } -func exposePorts(pm rkport.Manager, portMappings []ocicni.PortMapping) error { +func exposePorts(pm rkport.Manager, portMappings []ocicni.PortMapping, childIP string) error { ctx := context.TODO() for _, i := range portMappings { hostIP := i.HostIP @@ -260,6 +261,7 @@ func exposePorts(pm rkport.Manager, portMappings []ocicni.PortMapping) error { ParentIP: hostIP, ParentPort: int(i.HostPort), ChildPort: int(i.ContainerPort), + ChildIP: childIP, } if err := rkportutil.ValidatePortSpec(spec, nil); err != nil { return err |