diff options
Diffstat (limited to 'pkg/api')
-rw-r--r-- | pkg/api/handlers/compat/images.go | 54 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/images.go | 43 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/manifests.go | 265 | ||||
-rw-r--r-- | pkg/api/handlers/types.go | 40 | ||||
-rw-r--r-- | pkg/api/handlers/utils/handler.go | 4 | ||||
-rw-r--r-- | pkg/api/handlers/utils/images.go | 40 | ||||
-rw-r--r-- | pkg/api/server/register_images.go | 2 | ||||
-rw-r--r-- | pkg/api/server/register_manifest.go | 177 | ||||
-rw-r--r-- | pkg/api/server/register_swagger.go | 4 | ||||
-rw-r--r-- | pkg/api/server/server.go | 2 |
10 files changed, 443 insertions, 188 deletions
diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index c1cc99da4..f08a7ee41 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -11,6 +11,7 @@ import ( "github.com/containers/buildah" "github.com/containers/common/libimage" "github.com/containers/common/pkg/config" + "github.com/containers/common/pkg/filters" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/types" "github.com/containers/podman/v3/libpod" @@ -404,25 +405,52 @@ func GetImage(w http.ResponseWriter, r *http.Request) { } func GetImages(w http.ResponseWriter, r *http.Request) { - images, err := utils.GetImages(w, r) - if err != nil { - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Failed get images")) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + query := struct { + All bool + Digests bool + Filter string // Docker 1.24 compatibility + }{ + // 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, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, + errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + return + } + if _, found := r.URL.Query()["digests"]; found && query.Digests { + utils.UnSupportedParameter("digests") return } - summaries := make([]*entities.ImageSummary, 0, len(images)) - for _, img := range images { - // If the image is a manifest list, extract as much as we can. - if isML, _ := img.IsManifestList(r.Context()); isML { - continue + filterList, err := filters.FiltersFromRequest(r) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) + return + } + if !utils.IsLibpodRequest(r) { + if len(query.Filter) > 0 { // Docker 1.24 compatibility + filterList = append(filterList, "reference="+query.Filter) } + filterList = append(filterList, "manifest=false") + } - is, err := handlers.ImageToImageSummary(img) - if err != nil { - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Failed transform image summaries")) - return + imageEngine := abi.ImageEngine{Libpod: runtime} + + listOptions := entities.ImageListOptions{All: query.All, Filter: filterList} + summaries, err := imageEngine.List(r.Context(), listOptions) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) + return + } + + if !utils.IsLibpodRequest(r) { + // docker adds sha256: in front of the ID + for _, s := range summaries { + s.ID = "sha256:" + s.ID } - summaries = append(summaries, is) } utils.WriteResponse(w, http.StatusOK, summaries) } diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 6e23845f0..d3dbf3023 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -12,7 +12,6 @@ import ( "github.com/containers/buildah" "github.com/containers/common/libimage" - "github.com/containers/common/pkg/filters" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/types" "github.com/containers/podman/v3/libpod" @@ -103,48 +102,6 @@ func GetImage(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, inspect) } -func GetImages(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) - runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - query := struct { - All bool - Digests bool - Filter string // Docker 1.24 compatibility - }{ - // 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, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, - errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) - return - } - if _, found := r.URL.Query()["digests"]; found && query.Digests { - utils.UnSupportedParameter("digests") - return - } - - filterList, err := filters.FiltersFromRequest(r) - if err != nil { - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) - return - } - if !utils.IsLibpodRequest(r) && len(query.Filter) > 0 { // Docker 1.24 compatibility - filterList = append(filterList, "reference="+query.Filter) - } - - imageEngine := abi.ImageEngine{Libpod: runtime} - - listOptions := entities.ImageListOptions{All: query.All, Filter: filterList} - summaries, err := imageEngine.List(r.Context(), listOptions) - if err != nil { - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) - return - } - - utils.WriteResponse(w, http.StatusOK, summaries) -} - func PruneImages(w http.ResponseWriter, r *http.Request) { var ( err error diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go index eb0b6827f..ef0839d1f 100644 --- a/pkg/api/handlers/libpod/manifests.go +++ b/pkg/api/handlers/libpod/manifests.go @@ -3,7 +3,11 @@ package libpod import ( "context" "encoding/json" + "fmt" + "io/ioutil" "net/http" + "net/url" + "strings" "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/manifest" @@ -15,6 +19,8 @@ import ( "github.com/containers/podman/v3/pkg/auth" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" + "github.com/containers/podman/v3/pkg/errorhandling" + "github.com/gorilla/mux" "github.com/gorilla/schema" "github.com/opencontainers/go-digest" "github.com/pkg/errors" @@ -24,40 +30,93 @@ func ManifestCreate(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { - Name []string `schema:"name"` - Image []string `schema:"image"` - All bool `schema:"all"` + Name string `schema:"name"` + Images []string `schema:"images"` + All bool `schema:"all"` }{ // Add defaults here once needed. } + + // Support 3.x API calls, alias image to images + if image, ok := r.URL.Query()["image"]; ok { + query.Images = image + } + if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } - // TODO: (jhonce) When c/image is refactored the roadmap calls for this check to be pushed into that library. - for _, n := range query.Name { - if _, err := reference.ParseNormalizedNamed(n); err != nil { + // Support 4.x API calls, map query parameter to path + if name, ok := mux.Vars(r)["name"]; ok { + n, err := url.QueryUnescape(name) + if err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, - errors.Wrapf(err, "invalid image name %s", n)) + errors.Wrapf(err, "failed to parse name parameter %q", name)) return } + query.Name = n + } + + if _, err := reference.ParseNormalizedNamed(query.Name); err != nil { + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, + errors.Wrapf(err, "invalid image name %s", query.Name)) + return } imageEngine := abi.ImageEngine{Libpod: runtime} createOptions := entities.ManifestCreateOptions{All: query.All} - manID, err := imageEngine.ManifestCreate(r.Context(), query.Name, query.Image, createOptions) + manID, err := imageEngine.ManifestCreate(r.Context(), query.Name, query.Images, createOptions) + if err != nil { + utils.InternalServerError(w, err) + return + } + + status := http.StatusOK + if _, err := utils.SupportedVersion(r, "< 4.0.0"); err == utils.ErrVersionNotSupported { + status = http.StatusCreated + } + + buffer, err := ioutil.ReadAll(r.Body) + if err != nil { + utils.InternalServerError(w, err) + return + } + + // Treat \r\n as empty body + if len(buffer) < 3 { + utils.WriteResponse(w, status, handlers.IDResponse{ID: manID}) + return + } + + body := new(entities.ManifestModifyOptions) + if err := json.Unmarshal(buffer, body); err != nil { + utils.InternalServerError(w, errors.Wrap(err, "Decode()")) + return + } + + // gather all images for manifest list + var images []string + if len(query.Images) > 0 { + images = append(query.Images) + } + if len(body.Images) > 0 { + images = append(body.Images) + } + + id, err := imageEngine.ManifestAdd(r.Context(), query.Name, images, body.ManifestAddOptions) if err != nil { utils.InternalServerError(w, err) return } - utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: manID}) + + utils.WriteResponse(w, status, handlers.IDResponse{ID: id}) } -// ExistsManifest check if a manifest list exists -func ExistsManifest(w http.ResponseWriter, r *http.Request) { +// ManifestExists return true if manifest list exists. +func ManifestExists(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) @@ -94,10 +153,18 @@ func ManifestInspect(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, schema2List) } +// ManifestAdd remove digest from manifest list +// +// Deprecated: As of 4.0.0 use ManifestModify instead func ManifestAdd(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - var addOptions entities.ManifestAddOptions - if err := json.NewDecoder(r.Body).Decode(&addOptions); err != nil { + + // Wrapper to support 3.x with 4.x libpod + query := struct { + entities.ManifestAddOptions + Images []string + }{} + if err := json.NewDecoder(r.Body).Decode(&query); err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) return } @@ -108,15 +175,8 @@ func ManifestAdd(w http.ResponseWriter, r *http.Request) { return } - // FIXME: we really need to clean up the manifest API. Swagger states - // the arguments were strings not string slices. The use of string - // slices, mixing lists and images is incredibly confusing. - if len(addOptions.Images) == 1 { - addOptions.Images = append(addOptions.Images, name) - } - imageEngine := abi.ImageEngine{Libpod: runtime} - newID, err := imageEngine.ManifestAdd(r.Context(), addOptions) + newID, err := imageEngine.ManifestAdd(r.Context(), name, query.Images, query.ManifestAddOptions) if err != nil { utils.InternalServerError(w, err) return @@ -124,7 +184,10 @@ func ManifestAdd(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: newID}) } -func ManifestRemove(w http.ResponseWriter, r *http.Request) { +// ManifestRemoveDigest remove digest from manifest list +// +// Deprecated: As of 4.0.0 use ManifestModify instead +func ManifestRemoveDigest(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { @@ -155,7 +218,10 @@ func ManifestRemove(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: manifestList.ID()}) } -func ManifestPush(w http.ResponseWriter, r *http.Request) { +// ManifestPushV3 push image to registry +// +// Deprecated: As of 4.0.0 use ManifestPush instead +func ManifestPushV3(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { @@ -207,3 +273,156 @@ func ManifestPush(w http.ResponseWriter, r *http.Request) { } utils.WriteResponse(w, http.StatusOK, digest) } + +// ManifestPush push image to registry +// +// As of 4.0.0 +func ManifestPush(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + + query := struct { + All bool `schema:"all"` + TLSVerify bool `schema:"tlsVerify"` + }{ + // Add defaults here once needed. + } + if err := decoder.Decode(&query, r.URL.Query()); err != nil { + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, + errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + return + } + + destination := utils.GetVar(r, "destination") + if err := utils.IsRegistryReference(destination); err != nil { + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) + return + } + + authconf, authfile, err := auth.GetCredentials(r) + if err != nil { + utils.Error(w, "failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "failed to parse registry header for %s", r.URL.String())) + return + } + defer auth.RemoveAuthfile(authfile) + var username, password string + if authconf != nil { + username = authconf.Username + password = authconf.Password + } + options := entities.ImagePushOptions{ + Authfile: authfile, + Username: username, + Password: password, + All: query.All, + } + if sys := runtime.SystemContext(); sys != nil { + options.CertDir = sys.DockerCertPath + } + if _, found := r.URL.Query()["tlsVerify"]; found { + options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) + } + + imageEngine := abi.ImageEngine{Libpod: runtime} + source := utils.GetName(r) + digest, err := imageEngine.ManifestPush(context.Background(), source, destination, options) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "error pushing image %q", destination)) + return + } + utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: digest}) +} + +// ManifestModify efficiently updates the named manifest list +func ManifestModify(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + imageEngine := abi.ImageEngine{Libpod: runtime} + + body := new(entities.ManifestModifyOptions) + if err := json.NewDecoder(r.Body).Decode(body); err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) + return + } + + name := utils.GetName(r) + if _, err := runtime.LibimageRuntime().LookupManifestList(name); err != nil { + utils.Error(w, "Something went wrong.", http.StatusNotFound, err) + return + } + + var report entities.ManifestModifyReport + switch { + case strings.EqualFold("update", body.Operation): + id, err := imageEngine.ManifestAdd(r.Context(), name, body.Images, body.ManifestAddOptions) + if err != nil { + report.Errors = append(report.Errors, err) + break + } + report = entities.ManifestModifyReport{ + ID: id, + Images: body.Images, + } + case strings.EqualFold("remove", body.Operation): + for _, image := range body.Images { + id, err := imageEngine.ManifestRemoveDigest(r.Context(), name, image) + if err != nil { + report.Errors = append(report.Errors, err) + continue + } + report.ID = id + report.Images = append(report.Images, image) + } + case strings.EqualFold("annotate", body.Operation): + options := entities.ManifestAnnotateOptions{ + Annotation: body.Annotation, + Arch: body.Arch, + Features: body.Features, + OS: body.OS, + OSFeatures: body.OSFeatures, + OSVersion: body.OSVersion, + Variant: body.Variant, + } + for _, image := range body.Images { + id, err := imageEngine.ManifestAnnotate(r.Context(), name, image, options) + if err != nil { + report.Errors = append(report.Errors, err) + continue + } + report.ID = id + report.Images = append(report.Images, image) + } + default: + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, + fmt.Errorf("illegal operation %q for %q", body.Operation, r.URL.String())) + return + } + + statusCode := http.StatusOK + switch { + case len(report.Errors) > 0 && len(report.Images) > 0: + statusCode = http.StatusConflict + case len(report.Errors) > 0: + statusCode = http.StatusInternalServerError + } + utils.WriteResponse(w, statusCode, report) +} + +// ManifestDelete removes a manifest list from storage +func ManifestDelete(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + imageEngine := abi.ImageEngine{Libpod: runtime} + + name := utils.GetName(r) + if _, err := runtime.LibimageRuntime().LookupManifestList(name); err != nil { + utils.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound, err) + return + } + + results, errs := imageEngine.ManifestRm(r.Context(), []string{name}) + errsString := errorhandling.ErrorsToStrings(errs) + report := handlers.LibpodImagesRemoveReport{ + ImageRemoveReport: *results, + Errors: errsString, + } + utils.WriteResponse(w, http.StatusOK, report) +} diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go index 1733476cc..d3a592bdf 100644 --- a/pkg/api/handlers/types.go +++ b/pkg/api/handlers/types.go @@ -184,46 +184,6 @@ type ExecStartConfig struct { Width uint16 `json:"w"` } -func ImageToImageSummary(l *libimage.Image) (*entities.ImageSummary, error) { - options := &libimage.InspectOptions{WithParent: true, WithSize: true} - imageData, err := l.Inspect(context.TODO(), options) - if err != nil { - return nil, errors.Wrapf(err, "failed to obtain summary for image %s", l.ID()) - } - - containers, err := l.Containers() - if err != nil { - return nil, errors.Wrapf(err, "failed to obtain Containers for image %s", l.ID()) - } - containerCount := len(containers) - - isDangling, err := l.IsDangling(context.TODO()) - if err != nil { - return nil, errors.Wrapf(err, "failed to check if image %s is dangling", l.ID()) - } - - is := entities.ImageSummary{ - // docker adds sha256: in front of the ID - ID: "sha256:" + l.ID(), - ParentId: imageData.Parent, - RepoTags: imageData.RepoTags, - RepoDigests: imageData.RepoDigests, - Created: l.Created().Unix(), - Size: imageData.Size, - SharedSize: 0, - VirtualSize: imageData.VirtualSize, - Labels: imageData.Labels, - Containers: containerCount, - ReadOnly: l.IsReadOnly(), - Dangling: isDangling, - Names: l.Names(), - Digest: string(imageData.Digest), - ConfigDigest: "", // TODO: libpod/image didn't set it but libimage should - History: imageData.NamesHistory, - } - return &is, nil -} - func ImageDataToImageInspect(ctx context.Context, l *libimage.Image) (*ImageInspect, error) { options := &libimage.InspectOptions{WithParent: true, WithSize: true} info, err := l.Inspect(context.Background(), options) diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go index 96b7a957c..ee83755a1 100644 --- a/pkg/api/handlers/utils/handler.go +++ b/pkg/api/handlers/utils/handler.go @@ -174,7 +174,7 @@ func FilterMapToString(filters map[string][]string) (string, error) { return string(f), nil } -func getVar(r *http.Request, k string) string { +func GetVar(r *http.Request, k string) string { val := mux.Vars(r)[k] safeVal, err := url.PathUnescape(val) if err != nil { @@ -186,5 +186,5 @@ func getVar(r *http.Request, k string) string { // GetName extracts the name from the mux func GetName(r *http.Request) string { - return getVar(r, "name") + return GetVar(r, "name") } diff --git a/pkg/api/handlers/utils/images.go b/pkg/api/handlers/utils/images.go index 3f3f48193..639de9915 100644 --- a/pkg/api/handlers/utils/images.go +++ b/pkg/api/handlers/utils/images.go @@ -6,7 +6,6 @@ import ( "strings" "github.com/containers/common/libimage" - "github.com/containers/common/pkg/filters" "github.com/containers/image/v5/docker" storageTransport "github.com/containers/image/v5/storage" "github.com/containers/image/v5/transports/alltransports" @@ -16,7 +15,6 @@ import ( "github.com/containers/podman/v3/pkg/util" "github.com/containers/storage" "github.com/docker/distribution/reference" - "github.com/gorilla/schema" "github.com/pkg/errors" ) @@ -91,44 +89,6 @@ func ParseStorageReference(name string) (types.ImageReference, error) { return imageRef, nil } -// GetImages is a common function used to get images for libpod and other compatibility -// mechanisms -func GetImages(w http.ResponseWriter, r *http.Request) ([]*libimage.Image, error) { - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) - runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - query := struct { - All bool - Digests bool - Filter string // Docker 1.24 compatibility - }{ - // This is where you can override the golang default value for one of fields - } - - if err := decoder.Decode(&query, r.URL.Query()); err != nil { - return nil, err - } - if _, found := r.URL.Query()["digests"]; found && query.Digests { - UnSupportedParameter("digests") - } - - filterList, err := filters.FiltersFromRequest(r) - if err != nil { - return nil, err - } - if !IsLibpodRequest(r) && len(query.Filter) > 0 { // Docker 1.24 compatibility - filterList = append(filterList, "reference="+query.Filter) - } - - if !query.All { - // Filter intermediate images unless we want to list *all*. - // NOTE: it's a positive filter, so `intermediate=false` means - // to display non-intermediate images. - filterList = append(filterList, "intermediate=false") - } - listOptions := &libimage.ListImagesOptions{Filters: filterList} - return runtime.LibimageRuntime().ListImages(r.Context(), nil, listOptions) -} - func GetImage(r *http.Request, name string) (*libimage.Image, error) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) image, _, err := runtime.LibimageRuntime().LookupImage(name, nil) diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index d9cda8579..df48253e2 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -840,7 +840,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: "#/responses/LibpodImageSummaryResponse" // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/libpod/images/json"), s.APIHandler(libpod.GetImages)).Methods(http.MethodGet) + r.Handle(VersionedPath("/libpod/images/json"), s.APIHandler(compat.GetImages)).Methods(http.MethodGet) // swagger:operation POST /libpod/images/load libpod ImageLoadLibpod // --- // tags: diff --git a/pkg/api/server/register_manifest.go b/pkg/api/server/register_manifest.go index 010d8a79e..8cd3d8b22 100644 --- a/pkg/api/server/register_manifest.go +++ b/pkg/api/server/register_manifest.go @@ -8,7 +8,9 @@ import ( ) func (s *APIServer) registerManifestHandlers(r *mux.Router) error { - // swagger:operation POST /libpod/manifests/create manifests ManifestCreateLibpod + v3 := r.PathPrefix("/v{version:[0-3][0-9A-Za-z.-]*}/libpod/manifests").Subrouter() + v4 := r.PathPrefix("/v{version:[4-9][0-9A-Za-z.-]*}/libpod/manifests").Subrouter() + // swagger:operation POST /libpod/manifests manifests ManifestCreateLibpod // --- // summary: Create // description: Create a manifest list @@ -18,18 +20,30 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error { // - in: query // name: name // type: string - // description: manifest list name + // description: manifest list or index name to create // required: true // - in: query - // name: image + // name: images // type: string - // description: name of the image + // required: true + // description: | + // One or more names of an image or a manifest list. Repeat parameter as needed. + // + // Support for multiple images, as of version 4.0.0 + // Alias of `image` is support for compatibility with < 4.0.0 + // Response status code is 200 with < 4.0.0 for compatibility // - in: query // name: all // type: boolean // description: add all contents if given list + // - in: body + // name: options + // description: options for new manifest + // required: false + // schema: + // $ref: "#/definitions/ManifestModifyOptions" // responses: - // 200: + // 201: // schema: // $ref: "#/definitions/IDResponse" // 400: @@ -38,17 +52,21 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchImage" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/manifests/create"), s.APIHandler(libpod.ManifestCreate)).Methods(http.MethodPost) + v3.Handle("/create", s.APIHandler(libpod.ManifestCreate)).Methods(http.MethodPost) + v4.Handle("/{name:.*}", s.APIHandler(libpod.ManifestCreate)).Methods(http.MethodPost) // swagger:operation GET /libpod/manifests/{name}/exists manifests ManifestExistsLibpod // --- // summary: Exists - // description: Check if manifest list exists + // description: | + // Check if manifest list exists + // + // Note: There is no contract that the manifest list will exist for a follow-on operation // parameters: // - in: path // name: name // type: string // required: true - // description: the name of the manifest list + // description: the name or ID of the manifest list // produces: // - application/json // responses: @@ -58,11 +76,12 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error { // $ref: '#/responses/NoSuchManifest' // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/libpod/manifests/{name}/exists"), s.APIHandler(libpod.ExistsManifest)).Methods(http.MethodGet) + v3.Handle("/{name:.*}/exists", s.APIHandler(libpod.ManifestExists)).Methods(http.MethodGet) + v4.Handle("/{name:.*}/exists", s.APIHandler(libpod.ManifestExists)).Methods(http.MethodGet) // swagger:operation GET /libpod/manifests/{name}/json manifests ManifestInspectLibpod // --- // summary: Inspect - // description: Display a manifest list + // description: Display attributes of given manifest list // produces: // - application/json // parameters: @@ -70,7 +89,7 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error { // name: name // type: string // required: true - // description: the name or ID of the manifest + // description: the name or ID of the manifest list // responses: // 200: // $ref: "#/responses/InspectManifest" @@ -78,11 +97,53 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchManifest" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/manifests/{name:.*}/json"), s.APIHandler(libpod.ManifestInspect)).Methods(http.MethodGet) + v3.Handle("/{name:.*}/json", s.APIHandler(libpod.ManifestInspect)).Methods(http.MethodGet) + v4.Handle("/{name:.*}/json", s.APIHandler(libpod.ManifestInspect)).Methods(http.MethodGet) + // swagger:operation PUT /libpod/manifests/{name} manifests ManifestModifyLibpod + // --- + // summary: Modify manifest list + // description: | + // Add/Remove an image(s) to a manifest list + // + // Note: operations are not atomic when multiple Images are provided. + // + // As of v4.0.0 + // produces: + // - application/json + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: the name or ID of the manifest + // - in: body + // name: options + // description: options for mutating a manifest + // required: true + // schema: + // $ref: "#/definitions/ManifestModifyOptions" + // responses: + // 200: + // schema: + // $ref: "#/definitions/ManifestModifyReport" + // 404: + // $ref: "#/responses/NoSuchManifest" + // 400: + // $ref: "#/responses/BadParamError" + // 409: + // description: Operation had partial success, both Images and Errors may have members + // schema: + // $ref: "#/definitions/ManifestModifyReport" + // 500: + // $ref: "#/responses/InternalError" + v4.Handle("/{name:.*}", s.APIHandler(libpod.ManifestModify)).Methods(http.MethodPut) // swagger:operation POST /libpod/manifests/{name}/add manifests ManifestAddLibpod // --- // summary: Add image - // description: Add an image to a manifest list + // description: | + // Add an image to a manifest list + // + // Deprecated: As of 4.0.0 use ManifestModifyLibpod instead // produces: // - application/json // parameters: @@ -95,7 +156,7 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error { // name: options // description: options for creating a manifest // schema: - // $ref: "#/definitions/ManifestAddOpts" + // $ref: "#/definitions/ManifestAddOptions" // responses: // 200: // schema: @@ -106,11 +167,14 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error { // $ref: "#/responses/BadParamError" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/manifests/{name:.*}/add"), s.APIHandler(libpod.ManifestAdd)).Methods(http.MethodPost) - // swagger:operation DELETE /libpod/manifests/{name} manifests ManifestDeleteLibpod + v3.Handle("/{name:.*}/add", s.APIHandler(libpod.ManifestAdd)).Methods(http.MethodPost) + // swagger:operation DELETE /libpod/manifests/{name} manifests ManifestDeleteV3Libpod // --- - // summary: Remove - // description: Remove an image from a manifest list + // summary: Remove image from a manifest list + // description: | + // Remove an image from a manifest list + // + // Deprecated: As of 4.0.0 use ManifestModifyLibpod instead // produces: // - application/json // parameters: @@ -133,11 +197,37 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchManifest" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/manifests/{name:.*}"), s.APIHandler(libpod.ManifestRemove)).Methods(http.MethodDelete) - // swagger:operation POST /libpod/manifests/{name}/push manifests ManifestPushLibpod + v3.Handle("/{name:.*}", s.APIHandler(libpod.ManifestRemoveDigest)).Methods(http.MethodDelete) + // swagger:operation DELETE /libpod/manifests/{name} manifests ManifestDeleteLibpod // --- - // summary: Push - // description: Push a manifest list or image index to a registry + // summary: Delete manifest list + // description: | + // Delete named manifest list + // + // As of v4.0.0 + // produces: + // - application/json + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: The name or ID of the list to be deleted + // responses: + // 200: + // $ref: "#/responses/DocsLibpodImagesRemoveResponse" + // 404: + // $ref: "#/responses/NoSuchManifest" + // 500: + // $ref: "#/responses/InternalError" + v4.Handle("/{name:.*}", s.APIHandler(libpod.ManifestDelete)).Methods(http.MethodDelete) + // swagger:operation POST /libpod/manifests/{name}/push manifests ManifestPushV3Libpod + // --- + // summary: Push manifest to registry + // description: | + // Push a manifest list or image index to a registry + // + // Deprecated: As of 4.0.0 use ManifestPushLibpod instead // produces: // - application/json // parameters: @@ -165,6 +255,47 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchManifest" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/manifests/{name}/push"), s.APIHandler(libpod.ManifestPush)).Methods(http.MethodPost) + v3.Handle("/{name}/push", s.APIHandler(libpod.ManifestPushV3)).Methods(http.MethodPost) + // swagger:operation POST /libpod/manifests/{name}/registry/{destination} manifests ManifestPushLibpod + // --- + // summary: Push manifest list to registry + // description: | + // Push a manifest list or image index to the named registry + // + // As of v4.0.0 + // produces: + // - application/json + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: the name or ID of the manifest list + // - in: path + // name: destination + // type: string + // required: true + // description: the registry for the manifest list + // - in: query + // name: all + // description: push all images + // type: boolean + // default: false + // - in: query + // name: tlsVerify + // type: boolean + // default: false + // description: skip TLS verification for registries + // responses: + // 200: + // schema: + // $ref: "#/definitions/IDResponse" + // 400: + // $ref: "#/responses/BadParamError" + // 404: + // $ref: "#/responses/NoSuchManifest" + // 500: + // $ref: "#/responses/InternalError" + v4.Handle("/{name:.*}/registry/{destination:.*}", s.APIHandler(libpod.ManifestPush)).Methods(http.MethodPost) return nil } diff --git a/pkg/api/server/register_swagger.go b/pkg/api/server/register_swagger.go index dca1df14b..48af7713f 100644 --- a/pkg/api/server/register_swagger.go +++ b/pkg/api/server/register_swagger.go @@ -7,8 +7,8 @@ import ( "github.com/gorilla/mux" ) -// RegisterSwaggerHandlers maps the swagger endpoint for the server -func (s *APIServer) RegisterSwaggerHandlers(r *mux.Router) error { +// registerSwaggerHandlers maps the swagger endpoint for the server +func (s *APIServer) registerSwaggerHandlers(r *mux.Router) error { // This handler does _*NOT*_ provide an UI rather just a swagger spec that an UI could render r.HandleFunc(VersionedPath("/libpod/swagger"), s.APIHandler(libpod.ServeSwagger)).Methods(http.MethodGet) return nil diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index 8c5c7aeeb..65b7e2474 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -151,7 +151,7 @@ func newServer(runtime *libpod.Runtime, listener *net.Listener, opts entities.Se server.registerPluginsHandlers, server.registerPodsHandlers, server.registerSecretHandlers, - server.RegisterSwaggerHandlers, + server.registerSwaggerHandlers, server.registerSwarmHandlers, server.registerSystemHandlers, server.registerVersionHandlers, |