Skip to content

Commit

Permalink
Merge pull request #5449 from baude/manifests
Browse files Browse the repository at this point in the history
apiv2 addition of manifests
  • Loading branch information
openshift-merge-robot committed Mar 17, 2020
2 parents c4a6273 + abbbeac commit 9ef5d28
Show file tree
Hide file tree
Showing 17 changed files with 2,073 additions and 1 deletion.
154 changes: 154 additions & 0 deletions libpod/image/manifests.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package image

import (
"context"

"github.com/containers/buildah/manifests"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/image/v5/types"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
)

// Options for adding a manifest
// swagger:model ManifestAddOpts
type ManifestAddOpts struct {
All bool `json:"all"`
Annotation map[string]string `json:"annotation"`
Arch string `json:"arch"`
Features []string `json:"features"`
Images []string `json:"images"`
OSVersion string `json:"os_version"`
Variant string `json:"variant"`
}

// InspectManifest returns a dockerized version of the manifest list
func (i *Image) InspectManifest() (*manifest.Schema2List, error) {
list, err := i.getManifestList()
if err != nil {
return nil, err
}
return list.Docker(), nil
}

// RemoveManifest removes the given digest from the manifest list.
func (i *Image) RemoveManifest(d digest.Digest) (string, error) {
list, err := i.getManifestList()
if err != nil {
return "", err
}
if err := list.Remove(d); err != nil {
return "", err
}
return list.SaveToImage(i.imageruntime.store, i.ID(), nil, "")
}

// getManifestList is a helper to obtain a manifest list
func (i *Image) getManifestList() (manifests.List, error) {
_, list, err := manifests.LoadFromImage(i.imageruntime.store, i.ID())
return list, err
}

// CreateManifestList creates a new manifest list and can optionally add given images
// to the list
func CreateManifestList(rt *Runtime, systemContext types.SystemContext, names []string, imgs []string, all bool) (string, error) {
list := manifests.Create()
opts := ManifestAddOpts{Images: names, All: all}
for _, img := range imgs {
var ref types.ImageReference
newImage, err := rt.NewFromLocal(img)
if err == nil {
ir, err := newImage.toImageRef(context.Background())
if err != nil {
return "", err
}
if ir == nil {
return "", errors.New("unable to convert image to ImageReference")
}
ref = ir.Reference()
} else {
ref, err = alltransports.ParseImageName(img)
if err != nil {
return "", err
}
}
list, err = addManifestToList(ref, list, systemContext, opts)
if err != nil {
return "", err
}
}
return list.SaveToImage(rt.store, "", names, manifest.DockerV2ListMediaType)
}

func addManifestToList(ref types.ImageReference, list manifests.List, systemContext types.SystemContext, opts ManifestAddOpts) (manifests.List, error) {
d, err := list.Add(context.Background(), &systemContext, ref, opts.All)
if err != nil {
return nil, err
}
if len(opts.OSVersion) > 0 {
if err := list.SetOSVersion(d, opts.OSVersion); err != nil {
return nil, err
}
}
if len(opts.Features) > 0 {
if err := list.SetFeatures(d, opts.Features); err != nil {
return nil, err
}
}
if len(opts.Arch) > 0 {
if err := list.SetArchitecture(d, opts.Arch); err != nil {
return nil, err
}
}
if len(opts.Variant) > 0 {
if err := list.SetVariant(d, opts.Variant); err != nil {
return nil, err
}
}
if len(opts.Annotation) > 0 {
if err := list.SetAnnotations(&d, opts.Annotation); err != nil {
return nil, err
}
}
return list, err
}

// AddManifest adds a manifest to a given manifest list.
func (i *Image) AddManifest(systemContext types.SystemContext, opts ManifestAddOpts) (string, error) {
var (
ref types.ImageReference
)
newImage, err := i.imageruntime.NewFromLocal(opts.Images[0])
if err == nil {
ir, err := newImage.toImageRef(context.Background())
if err != nil {
return "", err
}
ref = ir.Reference()
} else {
ref, err = alltransports.ParseImageName(opts.Images[0])
if err != nil {
return "", err
}
}
list, err := i.getManifestList()
if err != nil {
return "", err
}
list, err = addManifestToList(ref, list, systemContext, opts)
if err != nil {
return "", err
}
return list.SaveToImage(i.imageruntime.store, i.ID(), nil, "")
}

// PushManifest pushes a manifest to a destination
func (i *Image) PushManifest(dest types.ImageReference, opts manifests.PushOptions) (digest.Digest, error) {
list, err := i.getManifestList()
if err != nil {
return "", err
}
_, d, err := list.Push(context.Background(), dest, opts)
return d, err
}
166 changes: 166 additions & 0 deletions pkg/api/handlers/libpod/manifests.go
Original file line number Diff line number Diff line change
@@ -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())
}
9 changes: 9 additions & 0 deletions pkg/api/handlers/libpod/swagger.go
Original file line number Diff line number Diff line change
@@ -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
}
2 changes: 2 additions & 0 deletions pkg/api/handlers/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ type VolumeCreateConfig struct {
Opts map[string]string `schema:"opts"`
}

// swagger:model IDResponse
type IDResponse struct {
// ID
ID string `json:"id"`
}

Expand Down

0 comments on commit 9ef5d28

Please sign in to comment.