mirror of
https://github.com/containers/podman.git
synced 2025-10-11 16:26:00 +08:00
apiv2 addition of manifests
add endpoints for create, add, remove, inspect, and push. this allows manifests to be managed through the restful interfaces. also added go-bindings and tests Signed-off-by: Brent Baude <bbaude@redhat.com>
This commit is contained in:
166
pkg/api/handlers/libpod/manifests.go
Normal file
166
pkg/api/handlers/libpod/manifests.go
Normal file
@ -0,0 +1,166 @@
|
||||
package libpod
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/containers/buildah/manifests"
|
||||
copy2 "github.com/containers/image/v5/copy"
|
||||
"github.com/containers/image/v5/transports/alltransports"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/image"
|
||||
"github.com/containers/libpod/pkg/api/handlers"
|
||||
"github.com/containers/libpod/pkg/api/handlers/utils"
|
||||
"github.com/gorilla/schema"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func ManifestCreate(w http.ResponseWriter, r *http.Request) {
|
||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||
query := struct {
|
||||
Name []string `schema:"name"`
|
||||
Image []string `schema:"image"`
|
||||
All bool `schema:"all"`
|
||||
}{
|
||||
// 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
|
||||
}
|
||||
rtc, err := runtime.GetConfig()
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
sc := image.GetSystemContext(rtc.SignaturePolicyPath, "", false)
|
||||
manID, err := image.CreateManifestList(runtime.ImageRuntime(), *sc, query.Name, query.Image, query.All)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: manID})
|
||||
}
|
||||
|
||||
func ManifestInspect(w http.ResponseWriter, r *http.Request) {
|
||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||
name := utils.GetName(r)
|
||||
newImage, err := runtime.ImageRuntime().NewFromLocal(name)
|
||||
if err != nil {
|
||||
utils.ImageNotFound(w, name, err)
|
||||
return
|
||||
}
|
||||
data, err := newImage.InspectManifest()
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
utils.WriteResponse(w, http.StatusOK, data)
|
||||
}
|
||||
|
||||
func ManifestAdd(w http.ResponseWriter, r *http.Request) {
|
||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||
var manifestInput image.ManifestAddOpts
|
||||
if err := json.NewDecoder(r.Body).Decode(&manifestInput); err != nil {
|
||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
|
||||
return
|
||||
}
|
||||
name := utils.GetName(r)
|
||||
newImage, err := runtime.ImageRuntime().NewFromLocal(name)
|
||||
if err != nil {
|
||||
utils.ImageNotFound(w, name, err)
|
||||
return
|
||||
}
|
||||
rtc, err := runtime.GetConfig()
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
sc := image.GetSystemContext(rtc.SignaturePolicyPath, "", false)
|
||||
newID, err := newImage.AddManifest(*sc, manifestInput)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: newID})
|
||||
}
|
||||
|
||||
func ManifestRemove(w http.ResponseWriter, r *http.Request) {
|
||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||
query := struct {
|
||||
Digest string `schema:"digest"`
|
||||
}{
|
||||
// Add defaults here once needed.
|
||||
}
|
||||
name := utils.GetName(r)
|
||||
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
|
||||
}
|
||||
newImage, err := runtime.ImageRuntime().NewFromLocal(name)
|
||||
if err != nil {
|
||||
utils.ImageNotFound(w, name, err)
|
||||
return
|
||||
}
|
||||
d, err := digest.Parse(query.Digest)
|
||||
if err != nil {
|
||||
utils.Error(w, "invalid digest", http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
newID, err := newImage.RemoveManifest(d)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: newID})
|
||||
}
|
||||
func ManifestPush(w http.ResponseWriter, r *http.Request) {
|
||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||
query := struct {
|
||||
All bool `schema:"all"`
|
||||
Destination string `schema:"destination"`
|
||||
}{
|
||||
// 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
|
||||
}
|
||||
name := utils.GetName(r)
|
||||
newImage, err := runtime.ImageRuntime().NewFromLocal(name)
|
||||
if err != nil {
|
||||
utils.ImageNotFound(w, name, err)
|
||||
return
|
||||
}
|
||||
dest, err := alltransports.ParseImageName(query.Destination)
|
||||
if err != nil {
|
||||
utils.Error(w, "invalid destination parameter", http.StatusBadRequest, errors.Errorf("invalid destination parameter %q", query.Destination))
|
||||
return
|
||||
}
|
||||
rtc, err := runtime.GetConfig()
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
sc := image.GetSystemContext(rtc.SignaturePolicyPath, "", false)
|
||||
opts := manifests.PushOptions{
|
||||
ImageListSelection: copy2.CopySpecificImages,
|
||||
SystemContext: sc,
|
||||
}
|
||||
if query.All {
|
||||
opts.ImageListSelection = copy2.CopyAllImages
|
||||
}
|
||||
newD, err := newImage.PushManifest(dest, opts)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
utils.WriteResponse(w, http.StatusOK, newD.String())
|
||||
}
|
@ -1,8 +1,17 @@
|
||||
package libpod
|
||||
|
||||
import "github.com/containers/image/v5/manifest"
|
||||
|
||||
// List Containers
|
||||
// swagger:response ListContainers
|
||||
type swagInspectPodResponse struct {
|
||||
// in:body
|
||||
Body []ListContainer
|
||||
}
|
||||
|
||||
// Inspect Manifest
|
||||
// swagger:response InspectManifest
|
||||
type swagInspectManifestResponse struct {
|
||||
// in:body
|
||||
Body manifest.List
|
||||
}
|
||||
|
@ -140,7 +140,9 @@ type VolumeCreateConfig struct {
|
||||
Opts map[string]string `schema:"opts"`
|
||||
}
|
||||
|
||||
// swagger:model IDResponse
|
||||
type IDResponse struct {
|
||||
// ID
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
|
145
pkg/api/server/register_manifest.go
Normal file
145
pkg/api/server/register_manifest.go
Normal file
@ -0,0 +1,145 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/containers/libpod/pkg/api/handlers/libpod"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
|
||||
// swagger:operation POST /libpod/manifests/create manifests Create
|
||||
// ---
|
||||
// summary: Create
|
||||
// description: Create a manifest list
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - in: query
|
||||
// name: name
|
||||
// type: string
|
||||
// description: manifest list name
|
||||
// required: true
|
||||
// - in: query
|
||||
// name: image
|
||||
// type: string
|
||||
// description: name of the image
|
||||
// - in: query
|
||||
// name: all
|
||||
// type: boolean
|
||||
// description: add all contents if given list
|
||||
// responses:
|
||||
// 200:
|
||||
// $ref: "#/definitions/IDResponse"
|
||||
// 400:
|
||||
// $ref: "#/responses/BadParamError"
|
||||
// 404:
|
||||
// $ref: "#/responses/NoSuchImage"
|
||||
// 500:
|
||||
// $ref: "#/responses/InternalError"
|
||||
r.Handle(VersionedPath("/libpod/manifests/create"), s.APIHandler(libpod.ManifestCreate)).Methods(http.MethodPost)
|
||||
// swagger:operation GET /libpod/manifests/{name}/json manifests Inspect
|
||||
// ---
|
||||
// summary: Inspect
|
||||
// description: Display a manifest list
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - in: path
|
||||
// name: name
|
||||
// type: string
|
||||
// required: true
|
||||
// description: the name or ID of the manifest
|
||||
// responses:
|
||||
// 200:
|
||||
// $ref: "#/responses/InspectManifest"
|
||||
// 404:
|
||||
// $ref: "#/responses/NoSuchManifest"
|
||||
// 500:
|
||||
// $ref: "#/responses/InternalError"
|
||||
r.Handle(VersionedPath("/libpod/manifests/{name:.*}/json"), s.APIHandler(libpod.ManifestInspect)).Methods(http.MethodGet)
|
||||
// swagger:operation POST /libpod/manifests/{name}/add manifests AddManifest
|
||||
// ---
|
||||
// description: Add an image to a manifest list
|
||||
// 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 creating a manifest
|
||||
// schema:
|
||||
// $ref: "#/definitions/ManifestAddOpts"
|
||||
// responses:
|
||||
// 200:
|
||||
// $ref: "#/definitions/IDResponse"
|
||||
// 404:
|
||||
// $ref: "#/responses/NoSuchManifest"
|
||||
// 409:
|
||||
// $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 RemoveManifest
|
||||
// ---
|
||||
// summary: Remove
|
||||
// description: Remove an image from a manifest list
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - in: path
|
||||
// name: name
|
||||
// type: string
|
||||
// required: true
|
||||
// description: the image associated with the manifest
|
||||
// - in: query
|
||||
// name: digest
|
||||
// type: string
|
||||
// description: image digest to be removed
|
||||
// responses:
|
||||
// 200:
|
||||
// $ref: "#/definitions/IDResponse"
|
||||
// 400:
|
||||
// $ref: "#/responses/BadParamError"
|
||||
// 404:
|
||||
// $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 PushManifest
|
||||
// ---
|
||||
// summary: Push
|
||||
// description: Push a manifest list or image index to a registry
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - in: path
|
||||
// name: name
|
||||
// type: string
|
||||
// required: true
|
||||
// description: the name or ID of the manifest
|
||||
// - in: query
|
||||
// name: destination
|
||||
// type: string
|
||||
// required: true
|
||||
// description: the destination for the manifest
|
||||
// - in: query
|
||||
// name: all
|
||||
// description: push all images
|
||||
// type: boolean
|
||||
// responses:
|
||||
// 200:
|
||||
// $ref: "#/definitions/IDResponse"
|
||||
// 400:
|
||||
// $ref: "#/responses/BadParamError"
|
||||
// 404:
|
||||
// $ref: "#/responses/NoSuchManifest"
|
||||
// 500:
|
||||
// $ref: "#/responses/InternalError"
|
||||
r.Handle(VersionedPath("/libpod/manifests/{name}/push"), s.APIHandler(libpod.ManifestPush)).Methods(http.MethodPost)
|
||||
return nil
|
||||
}
|
@ -99,11 +99,12 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
|
||||
server.registerAuthHandlers,
|
||||
server.registerContainersHandlers,
|
||||
server.registerDistributionHandlers,
|
||||
server.registerExecHandlers,
|
||||
server.registerEventsHandlers,
|
||||
server.registerExecHandlers,
|
||||
server.registerHealthCheckHandlers,
|
||||
server.registerImagesHandlers,
|
||||
server.registerInfoHandlers,
|
||||
server.registerManifestHandlers,
|
||||
server.registerMonitorHandlers,
|
||||
server.registerPingHandlers,
|
||||
server.registerPluginsHandlers,
|
||||
|
@ -51,6 +51,15 @@ type swagErrNoSuchPod struct {
|
||||
}
|
||||
}
|
||||
|
||||
// No such manifest
|
||||
// swagger:response NoSuchManifest
|
||||
type swagErrNoSuchManifest struct {
|
||||
// in:body
|
||||
Body struct {
|
||||
utils.ErrorModel
|
||||
}
|
||||
}
|
||||
|
||||
// Internal server error
|
||||
// swagger:response InternalError
|
||||
type swagInternalError struct {
|
||||
|
@ -6,6 +6,8 @@ tags:
|
||||
- name: images
|
||||
description: Actions related to images
|
||||
- name: pods
|
||||
description: Actions related to manifests
|
||||
- name: manifests
|
||||
description: Actions related to pods
|
||||
- name: volumes
|
||||
description: Actions related to volumes
|
||||
|
126
pkg/bindings/manifests/manifests.go
Normal file
126
pkg/bindings/manifests/manifests.go
Normal file
@ -0,0 +1,126 @@
|
||||
package manifests
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/image/v5/manifest"
|
||||
"github.com/containers/libpod/libpod/image"
|
||||
"github.com/containers/libpod/pkg/api/handlers"
|
||||
"github.com/containers/libpod/pkg/bindings"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
// Create creates a manifest for the given name. Optional images to be associated with
|
||||
// the new manifest can also be specified. The all boolean specifies to add all entries
|
||||
// of a list if the name provided is a manifest list. The ID of the new manifest list
|
||||
// is returned as a string.
|
||||
func Create(ctx context.Context, names, images []string, all *bool) (string, error) {
|
||||
var idr handlers.IDResponse
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(names) < 1 {
|
||||
return "", errors.New("creating a manifest requires at least one name argument")
|
||||
}
|
||||
params := url.Values{}
|
||||
if all != nil {
|
||||
params.Set("all", strconv.FormatBool(*all))
|
||||
}
|
||||
for _, name := range names {
|
||||
params.Add("name", name)
|
||||
}
|
||||
for _, i := range images {
|
||||
params.Add("image", i)
|
||||
}
|
||||
|
||||
response, err := conn.DoRequest(nil, http.MethodPost, "/manifests/create", params)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return idr.ID, response.Process(&idr)
|
||||
}
|
||||
|
||||
// Inspect returns a manifest list for a given name.
|
||||
func Inspect(ctx context.Context, name string) (*manifest.Schema2List, error) {
|
||||
var list manifest.Schema2List
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := conn.DoRequest(nil, http.MethodGet, "/manifests/%s/json", nil, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &list, response.Process(&list)
|
||||
}
|
||||
|
||||
// Add adds a manifest to a given manifest list. Additional options for the manifest
|
||||
// can also be specified. The ID of the new manifest list is returned as a string
|
||||
func Add(ctx context.Context, name string, options image.ManifestAddOpts) (string, error) {
|
||||
var idr handlers.IDResponse
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
optionsString, err := jsoniter.MarshalToString(options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
stringReader := strings.NewReader(optionsString)
|
||||
response, err := conn.DoRequest(stringReader, http.MethodPost, "/manifests/%s/add", nil, name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return idr.ID, response.Process(&idr)
|
||||
}
|
||||
|
||||
// Remove deletes a manifest entry from a manifest list. Both name and the digest to be
|
||||
// removed are mandatory inputs. The ID of the new manifest list is returned as a string.
|
||||
func Remove(ctx context.Context, name, digest string) (string, error) {
|
||||
var idr handlers.IDResponse
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("digest", digest)
|
||||
response, err := conn.DoRequest(nil, http.MethodDelete, "/manifests/%s", params, name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return idr.ID, response.Process(&idr)
|
||||
}
|
||||
|
||||
// Push takes a manifest list and pushes to a destination. If the destination is not specified,
|
||||
// the name will be used instead. If the optional all boolean is specified, all images specified
|
||||
// in the list will be pushed as well.
|
||||
func Push(ctx context.Context, name string, destination *string, all *bool) (string, error) {
|
||||
var (
|
||||
idr handlers.IDResponse
|
||||
)
|
||||
dest := name
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("image", name)
|
||||
if destination != nil {
|
||||
dest = name
|
||||
}
|
||||
params.Set("destination", dest)
|
||||
if all != nil {
|
||||
params.Set("all", strconv.FormatBool(*all))
|
||||
}
|
||||
response, err := conn.DoRequest(nil, http.MethodPost, "/manifests/%s/push", params, name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return idr.ID, response.Process(&idr)
|
||||
}
|
124
pkg/bindings/test/manifests_test.go
Normal file
124
pkg/bindings/test/manifests_test.go
Normal file
@ -0,0 +1,124 @@
|
||||
package test_bindings
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/containers/libpod/libpod/image"
|
||||
"github.com/containers/libpod/pkg/bindings"
|
||||
"github.com/containers/libpod/pkg/bindings/images"
|
||||
"github.com/containers/libpod/pkg/bindings/manifests"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/onsi/gomega/gexec"
|
||||
)
|
||||
|
||||
var _ = Describe("Podman containers ", func() {
|
||||
var (
|
||||
bt *bindingTest
|
||||
s *gexec.Session
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
bt = newBindingTest()
|
||||
bt.RestoreImagesFromCache()
|
||||
s = bt.startAPIService()
|
||||
time.Sleep(1 * time.Second)
|
||||
err := bt.NewConnection()
|
||||
Expect(err).To(BeNil())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
s.Kill()
|
||||
bt.cleanup()
|
||||
})
|
||||
|
||||
It("create manifest", func() {
|
||||
// create manifest list without images
|
||||
id, err := manifests.Create(bt.conn, []string{"quay.io/libpod/foobar:latest"}, []string{}, nil)
|
||||
Expect(err).To(BeNil())
|
||||
list, err := manifests.Inspect(bt.conn, id)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(list.Manifests)).To(BeZero())
|
||||
|
||||
// creating a duplicate should fail as a 500
|
||||
_, err = manifests.Create(bt.conn, []string{"quay.io/libpod/foobar:latest"}, []string{}, nil)
|
||||
Expect(err).ToNot(BeNil())
|
||||
code, _ := bindings.CheckResponseCode(err)
|
||||
Expect(code).To(BeNumerically("==", http.StatusInternalServerError))
|
||||
|
||||
_, err = images.Remove(bt.conn, id, nil)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
// create manifest list with images
|
||||
id, err = manifests.Create(bt.conn, []string{"quay.io/libpod/foobar:latest"}, []string{alpine.name}, nil)
|
||||
Expect(err).To(BeNil())
|
||||
list, err = manifests.Inspect(bt.conn, id)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(list.Manifests)).To(BeNumerically("==", 1))
|
||||
})
|
||||
|
||||
It("inspect bogus manifest", func() {
|
||||
_, err := manifests.Inspect(bt.conn, "larry")
|
||||
Expect(err).ToNot(BeNil())
|
||||
code, _ := bindings.CheckResponseCode(err)
|
||||
Expect(code).To(BeNumerically("==", http.StatusNotFound))
|
||||
})
|
||||
|
||||
It("add manifest", func() {
|
||||
// add to bogus should 404
|
||||
_, err := manifests.Add(bt.conn, "foobar", image.ManifestAddOpts{})
|
||||
Expect(err).ToNot(BeNil())
|
||||
code, _ := bindings.CheckResponseCode(err)
|
||||
Expect(code).To(BeNumerically("==", http.StatusNotFound))
|
||||
|
||||
id, err := manifests.Create(bt.conn, []string{"quay.io/libpod/foobar:latest"}, []string{}, nil)
|
||||
Expect(err).To(BeNil())
|
||||
opts := image.ManifestAddOpts{Images: []string{alpine.name}}
|
||||
_, err = manifests.Add(bt.conn, id, opts)
|
||||
Expect(err).To(BeNil())
|
||||
list, err := manifests.Inspect(bt.conn, id)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(list.Manifests)).To(BeNumerically("==", 1))
|
||||
|
||||
// add bogus name to existing list should fail
|
||||
opts.Images = []string{"larry"}
|
||||
_, err = manifests.Add(bt.conn, id, opts)
|
||||
Expect(err).ToNot(BeNil())
|
||||
code, _ = bindings.CheckResponseCode(err)
|
||||
Expect(code).To(BeNumerically("==", http.StatusInternalServerError))
|
||||
})
|
||||
|
||||
It("remove manifest", func() {
|
||||
// removal on bogus manifest list should be 404
|
||||
_, err := manifests.Remove(bt.conn, "larry", "1234")
|
||||
Expect(err).ToNot(BeNil())
|
||||
code, _ := bindings.CheckResponseCode(err)
|
||||
Expect(code).To(BeNumerically("==", http.StatusNotFound))
|
||||
|
||||
id, err := manifests.Create(bt.conn, []string{"quay.io/libpod/foobar:latest"}, []string{alpine.name}, nil)
|
||||
Expect(err).To(BeNil())
|
||||
data, err := manifests.Inspect(bt.conn, id)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(data.Manifests)).To(BeNumerically("==", 1))
|
||||
|
||||
// removal on a good manifest list with a bad digest should be 400
|
||||
_, err = manifests.Remove(bt.conn, id, "!234")
|
||||
Expect(err).ToNot(BeNil())
|
||||
code, _ = bindings.CheckResponseCode(err)
|
||||
Expect(code).To(BeNumerically("==", http.StatusBadRequest))
|
||||
|
||||
digest := data.Manifests[0].Digest.String()
|
||||
_, err = manifests.Remove(bt.conn, id, digest)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
// removal on good manifest with good digest should work
|
||||
data, err = manifests.Inspect(bt.conn, id)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(data.Manifests)).To(BeZero())
|
||||
})
|
||||
|
||||
It("push manifest", func() {
|
||||
Skip("TODO")
|
||||
})
|
||||
})
|
Reference in New Issue
Block a user