diff options
| -rw-r--r-- | libpod/image/manifests.go | 154 | ||||
| -rw-r--r-- | pkg/api/handlers/libpod/manifests.go | 166 | ||||
| -rw-r--r-- | pkg/api/handlers/libpod/swagger.go | 9 | ||||
| -rw-r--r-- | pkg/api/handlers/types.go | 2 | ||||
| -rw-r--r-- | pkg/api/server/register_manifest.go | 145 | ||||
| -rw-r--r-- | pkg/api/server/server.go | 3 | ||||
| -rw-r--r-- | pkg/api/server/swagger.go | 9 | ||||
| -rw-r--r-- | pkg/api/tags.yaml | 2 | ||||
| -rw-r--r-- | pkg/bindings/manifests/manifests.go | 126 | ||||
| -rw-r--r-- | pkg/bindings/test/manifests_test.go | 124 | ||||
| -rw-r--r-- | vendor/github.com/containers/buildah/manifests/copy.go | 15 | ||||
| -rw-r--r-- | vendor/github.com/containers/buildah/manifests/manifests.go | 397 | ||||
| -rw-r--r-- | vendor/github.com/containers/buildah/pkg/manifests/errors.go | 16 | ||||
| -rw-r--r-- | vendor/github.com/containers/buildah/pkg/manifests/manifests.go | 493 | ||||
| -rw-r--r-- | vendor/github.com/containers/buildah/pkg/supplemented/errors.go | 17 | ||||
| -rw-r--r-- | vendor/github.com/containers/buildah/pkg/supplemented/supplemented.go | 393 | ||||
| -rw-r--r-- | vendor/modules.txt | 3 | 
17 files changed, 2073 insertions, 1 deletions
diff --git a/libpod/image/manifests.go b/libpod/image/manifests.go new file mode 100644 index 000000000..9dbeb4cc5 --- /dev/null +++ b/libpod/image/manifests.go @@ -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 +} diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go new file mode 100644 index 000000000..a3d2caba6 --- /dev/null +++ b/pkg/api/handlers/libpod/manifests.go @@ -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()) +} diff --git a/pkg/api/handlers/libpod/swagger.go b/pkg/api/handlers/libpod/swagger.go index aec30ef56..f6a26134b 100644 --- a/pkg/api/handlers/libpod/swagger.go +++ b/pkg/api/handlers/libpod/swagger.go @@ -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 +} diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go index 2e429dc58..ce4a9957b 100644 --- a/pkg/api/handlers/types.go +++ b/pkg/api/handlers/types.go @@ -140,7 +140,9 @@ type VolumeCreateConfig struct {  	Opts map[string]string `schema:"opts"`  } +// swagger:model IDResponse  type IDResponse struct { +	// ID  	ID string `json:"id"`  } diff --git a/pkg/api/server/register_manifest.go b/pkg/api/server/register_manifest.go new file mode 100644 index 000000000..ccfc2192d --- /dev/null +++ b/pkg/api/server/register_manifest.go @@ -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 +} diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index a0addb303..8496cd11c 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -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, diff --git a/pkg/api/server/swagger.go b/pkg/api/server/swagger.go index 011196e5a..bd5c1df10 100644 --- a/pkg/api/server/swagger.go +++ b/pkg/api/server/swagger.go @@ -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 { diff --git a/pkg/api/tags.yaml b/pkg/api/tags.yaml index 571f49e44..5b5d9f5bb 100644 --- a/pkg/api/tags.yaml +++ b/pkg/api/tags.yaml @@ -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 diff --git a/pkg/bindings/manifests/manifests.go b/pkg/bindings/manifests/manifests.go new file mode 100644 index 000000000..a8d1e6ca3 --- /dev/null +++ b/pkg/bindings/manifests/manifests.go @@ -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) +} diff --git a/pkg/bindings/test/manifests_test.go b/pkg/bindings/test/manifests_test.go new file mode 100644 index 000000000..23c3d8194 --- /dev/null +++ b/pkg/bindings/test/manifests_test.go @@ -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") +	}) +}) diff --git a/vendor/github.com/containers/buildah/manifests/copy.go b/vendor/github.com/containers/buildah/manifests/copy.go new file mode 100644 index 000000000..7e651a46c --- /dev/null +++ b/vendor/github.com/containers/buildah/manifests/copy.go @@ -0,0 +1,15 @@ +package manifests + +import ( +	"github.com/containers/image/v5/signature" +) + +var ( +	// storageAllowedPolicyScopes overrides the policy for local storage +	// to ensure that we can read images from it. +	storageAllowedPolicyScopes = signature.PolicyTransportScopes{ +		"": []signature.PolicyRequirement{ +			signature.NewPRInsecureAcceptAnything(), +		}, +	} +) diff --git a/vendor/github.com/containers/buildah/manifests/manifests.go b/vendor/github.com/containers/buildah/manifests/manifests.go new file mode 100644 index 000000000..0fe7e477b --- /dev/null +++ b/vendor/github.com/containers/buildah/manifests/manifests.go @@ -0,0 +1,397 @@ +package manifests + +import ( +	"context" +	"encoding/json" +	stderrors "errors" +	"io" + +	"github.com/containers/buildah/pkg/manifests" +	"github.com/containers/buildah/pkg/supplemented" +	cp "github.com/containers/image/v5/copy" +	"github.com/containers/image/v5/docker/reference" +	"github.com/containers/image/v5/image" +	"github.com/containers/image/v5/manifest" +	"github.com/containers/image/v5/signature" +	is "github.com/containers/image/v5/storage" +	"github.com/containers/image/v5/transports" +	"github.com/containers/image/v5/transports/alltransports" +	"github.com/containers/image/v5/types" +	"github.com/containers/storage" +	digest "github.com/opencontainers/go-digest" +	v1 "github.com/opencontainers/image-spec/specs-go/v1" +	"github.com/pkg/errors" +	"github.com/sirupsen/logrus" +) + +const instancesData = "instances.json" + +// ErrListImageUnknown is returned when we attempt to create an image reference +// for a List that has not yet been saved to an image. +var ErrListImageUnknown = stderrors.New("unable to determine which image holds the manifest list") + +type list struct { +	manifests.List +	instances map[digest.Digest]string +} + +// List is a manifest list or image index, either created using Create(), or +// loaded from local storage using LoadFromImage(). +type List interface { +	manifests.List +	SaveToImage(store storage.Store, imageID string, names []string, mimeType string) (string, error) +	Reference(store storage.Store, multiple cp.ImageListSelection, instances []digest.Digest) (types.ImageReference, error) +	Push(ctx context.Context, dest types.ImageReference, options PushOptions) (reference.Canonical, digest.Digest, error) +	Add(ctx context.Context, sys *types.SystemContext, ref types.ImageReference, all bool) (digest.Digest, error) +} + +// PushOptions includes various settings which are needed for pushing the +// manifest list and its instances. +type PushOptions struct { +	Store              storage.Store +	SystemContext      *types.SystemContext  // github.com/containers/image/types.SystemContext +	ImageListSelection cp.ImageListSelection // set to either CopySystemImage, CopyAllImages, or CopySpecificImages +	Instances          []digest.Digest       // instances to copy if ImageListSelection == CopySpecificImages +	ReportWriter       io.Writer             // will be used to log the writing of the list and any blobs +	SignBy             string                // fingerprint of GPG key to use to sign images +	RemoveSignatures   bool                  // true to discard signatures in images +	ManifestType       string                // the format to use when saving the list - possible options are oci, v2s1, and v2s2 +} + +// Create creates a new list containing information about the specified image, +// computing its manifest's digest, and retrieving OS and architecture +// information from its configuration blob.  Returns the new list, and the +// instanceDigest for the initial image. +func Create() List { +	return &list{ +		List:      manifests.Create(), +		instances: make(map[digest.Digest]string), +	} +} + +// LoadFromImage reads the manifest list or image index, and additional +// information about where the various instances that it contains live, from an +// image record with the specified ID in local storage. +func LoadFromImage(store storage.Store, image string) (string, List, error) { +	img, err := store.Image(image) +	if err != nil { +		return "", nil, errors.Wrapf(err, "error locating image %q for loading manifest list", image) +	} +	manifestBytes, err := store.ImageBigData(img.ID, storage.ImageDigestManifestBigDataNamePrefix) +	if err != nil { +		return "", nil, errors.Wrapf(err, "error locating image %q for loading manifest list", image) +	} +	manifestList, err := manifests.FromBlob(manifestBytes) +	if err != nil { +		return "", nil, err +	} +	list := &list{ +		List:      manifestList, +		instances: make(map[digest.Digest]string), +	} +	instancesBytes, err := store.ImageBigData(img.ID, instancesData) +	if err != nil { +		return "", nil, errors.Wrapf(err, "error locating image %q for loading instance list", image) +	} +	if err := json.Unmarshal(instancesBytes, &list.instances); err != nil { +		return "", nil, errors.Wrapf(err, "error decoding instance list for image %q", image) +	} +	list.instances[""] = img.ID +	return img.ID, list, err +} + +// SaveToImage saves the manifest list or image index as the manifest of an +// Image record with the specified names in local storage, generating a random +// image ID if none is specified.  It also stores information about where the +// images whose manifests are included in the list can be found. +func (l *list) SaveToImage(store storage.Store, imageID string, names []string, mimeType string) (string, error) { +	manifestBytes, err := l.List.Serialize(mimeType) +	if err != nil { +		return "", err +	} +	instancesBytes, err := json.Marshal(&l.instances) +	if err != nil { +		return "", err +	} +	img, err := store.CreateImage(imageID, names, "", "", &storage.ImageOptions{}) +	if err == nil || errors.Cause(err) == storage.ErrDuplicateID { +		created := (err == nil) +		if created { +			imageID = img.ID +			l.instances[""] = img.ID +		} +		err := store.SetImageBigData(imageID, storage.ImageDigestManifestBigDataNamePrefix, manifestBytes, manifest.Digest) +		if err != nil { +			if created { +				if _, err2 := store.DeleteImage(img.ID, true); err2 != nil { +					logrus.Errorf("error deleting image %q after failing to save manifest for it", img.ID) +				} +			} +			return "", errors.Wrapf(err, "error saving manifest list to image %q", imageID) +		} +		err = store.SetImageBigData(imageID, instancesData, instancesBytes, nil) +		if err != nil { +			if created { +				if _, err2 := store.DeleteImage(img.ID, true); err2 != nil { +					logrus.Errorf("error deleting image %q after failing to save instance locations for it", img.ID) +				} +			} +			return "", errors.Wrapf(err, "error saving instance list to image %q", imageID) +		} +		return imageID, nil +	} +	return "", errors.Wrapf(err, "error creating image to hold manifest list") +} + +// Reference returns an image reference for the composite image being built +// in the list, or an error if the list has never been saved to a local image. +func (l *list) Reference(store storage.Store, multiple cp.ImageListSelection, instances []digest.Digest) (types.ImageReference, error) { +	if l.instances[""] == "" { +		return nil, errors.Wrap(ErrListImageUnknown, "error building reference to list") +	} +	s, err := is.Transport.ParseStoreReference(store, l.instances[""]) +	if err != nil { +		return nil, errors.Wrapf(err, "error creating ImageReference from image %q", l.instances[""]) +	} +	references := make([]types.ImageReference, 0, len(l.instances)) +	whichInstances := make([]digest.Digest, 0, len(l.instances)) +	switch multiple { +	case cp.CopyAllImages, cp.CopySystemImage: +		for instance := range l.instances { +			if instance != "" { +				whichInstances = append(whichInstances, instance) +			} +		} +	case cp.CopySpecificImages: +		for instance := range l.instances { +			for _, allowed := range instances { +				if instance == allowed { +					whichInstances = append(whichInstances, instance) +				} +			} +		} +	} +	for _, instance := range whichInstances { +		imageName := l.instances[instance] +		ref, err := alltransports.ParseImageName(imageName) +		if err != nil { +			return nil, errors.Wrapf(err, "error creating ImageReference from image %q", imageName) +		} +		references = append(references, ref) +	} +	return supplemented.Reference(s, references, multiple, instances), nil +} + +// Push saves the manifest list and whichever blobs are needed to a destination location. +func (l *list) Push(ctx context.Context, dest types.ImageReference, options PushOptions) (reference.Canonical, digest.Digest, error) { +	// Load the system signing policy. +	pushPolicy, err := signature.DefaultPolicy(options.SystemContext) +	if err != nil { +		return nil, "", errors.Wrapf(err, "error obtaining default signature policy") +	} + +	// Override the settings for local storage to make sure that we can always read the source "image". +	pushPolicy.Transports[is.Transport.Name()] = storageAllowedPolicyScopes + +	policyContext, err := signature.NewPolicyContext(pushPolicy) +	if err != nil { +		return nil, "", errors.Wrapf(err, "error creating new signature policy context") +	} +	defer func() { +		if err2 := policyContext.Destroy(); err2 != nil { +			logrus.Errorf("error destroying signature policy context: %v", err2) +		} +	}() + +	// If we were given a media type that corresponds to a multiple-images +	// type, reset it to a valid corresponding single-image type, since we +	// already expect the image library to infer the list type from the +	// image type that we're telling it to force. +	singleImageManifestType := options.ManifestType +	switch singleImageManifestType { +	case v1.MediaTypeImageIndex: +		singleImageManifestType = v1.MediaTypeImageManifest +	case manifest.DockerV2ListMediaType: +		singleImageManifestType = manifest.DockerV2Schema2MediaType +	} + +	// Build a source reference for our list and grab bag full of blobs. +	src, err := l.Reference(options.Store, options.ImageListSelection, options.Instances) +	if err != nil { +		return nil, "", err +	} +	copyOptions := &cp.Options{ +		ImageListSelection:    options.ImageListSelection, +		Instances:             options.Instances, +		SourceCtx:             options.SystemContext, +		DestinationCtx:        options.SystemContext, +		ReportWriter:          options.ReportWriter, +		RemoveSignatures:      options.RemoveSignatures, +		SignBy:                options.SignBy, +		ForceManifestMIMEType: singleImageManifestType, +	} + +	// Copy whatever we were asked to copy. +	manifestBytes, err := cp.Image(ctx, policyContext, dest, src, copyOptions) +	if err != nil { +		return nil, "", err +	} +	manifestDigest, err := manifest.Digest(manifestBytes) +	if err != nil { +		return nil, "", err +	} +	return nil, manifestDigest, nil +} + +// Add adds information about the specified image to the list, computing the +// image's manifest's digest, retrieving OS and architecture information from +// the image's configuration, and recording the image's reference so that it +// can be found at push-time.  Returns the instanceDigest for the image.  If +// the reference points to an image list, either all instances are added (if +// "all" is true), or the instance which matches "sys" (if "all" is false) will +// be added. +func (l *list) Add(ctx context.Context, sys *types.SystemContext, ref types.ImageReference, all bool) (digest.Digest, error) { +	src, err := ref.NewImageSource(ctx, sys) +	if err != nil { +		return "", errors.Wrapf(err, "error setting up to read manifest and configuration from %q", transports.ImageName(ref)) +	} +	defer src.Close() + +	type instanceInfo struct { +		instanceDigest                       *digest.Digest +		OS, Architecture, OSVersion, Variant string +		Features, OSFeatures, Annotations    []string +		Size                                 int64 +	} +	var instanceInfos []instanceInfo +	var manifestDigest digest.Digest + +	primaryManifestBytes, primaryManifestType, err := src.GetManifest(ctx, nil) +	if err != nil { +		return "", errors.Wrapf(err, "error reading manifest from %q", transports.ImageName(ref)) +	} + +	if manifest.MIMETypeIsMultiImage(primaryManifestType) { +		lists, err := manifests.FromBlob(primaryManifestBytes) +		if err != nil { +			return "", errors.Wrapf(err, "error parsing manifest list in %q", transports.ImageName(ref)) +		} +		if all { +			for i, instance := range lists.OCIv1().Manifests { +				platform := instance.Platform +				if platform == nil { +					platform = &v1.Platform{} +				} +				instanceDigest := instance.Digest +				instanceInfo := instanceInfo{ +					instanceDigest: &instanceDigest, +					OS:             platform.OS, +					Architecture:   platform.Architecture, +					OSVersion:      platform.OSVersion, +					Variant:        platform.Variant, +					Features:       append([]string{}, lists.Docker().Manifests[i].Platform.Features...), +					OSFeatures:     append([]string{}, platform.OSFeatures...), +					Size:           instance.Size, +				} +				instanceInfos = append(instanceInfos, instanceInfo) +			} +		} else { +			list, err := manifest.ListFromBlob(primaryManifestBytes, primaryManifestType) +			if err != nil { +				return "", errors.Wrapf(err, "error parsing manifest list in %q", transports.ImageName(ref)) +			} +			instanceDigest, err := list.ChooseInstance(sys) +			if err != nil { +				return "", errors.Wrapf(err, "error selecting image from manifest list in %q", transports.ImageName(ref)) +			} +			added := false +			for i, instance := range lists.OCIv1().Manifests { +				if instance.Digest != instanceDigest { +					continue +				} +				platform := instance.Platform +				if platform == nil { +					platform = &v1.Platform{} +				} +				instanceInfo := instanceInfo{ +					instanceDigest: &instanceDigest, +					OS:             platform.OS, +					Architecture:   platform.Architecture, +					OSVersion:      platform.OSVersion, +					Variant:        platform.Variant, +					Features:       append([]string{}, lists.Docker().Manifests[i].Platform.Features...), +					OSFeatures:     append([]string{}, platform.OSFeatures...), +					Size:           instance.Size, +				} +				instanceInfos = append(instanceInfos, instanceInfo) +				added = true +			} +			if !added { +				instanceInfo := instanceInfo{ +					instanceDigest: &instanceDigest, +				} +				instanceInfos = append(instanceInfos, instanceInfo) +			} +		} +	} else { +		instanceInfo := instanceInfo{ +			instanceDigest: nil, +		} +		instanceInfos = append(instanceInfos, instanceInfo) +	} + +	for _, instanceInfo := range instanceInfos { +		if instanceInfo.OS == "" || instanceInfo.Architecture == "" { +			img, err := image.FromUnparsedImage(ctx, sys, image.UnparsedInstance(src, instanceInfo.instanceDigest)) +			if err != nil { +				return "", errors.Wrapf(err, "error reading configuration blob from %q", transports.ImageName(ref)) +			} +			config, err := img.OCIConfig(ctx) +			if err != nil { +				return "", errors.Wrapf(err, "error reading info about config blob from %q", transports.ImageName(ref)) +			} +			if instanceInfo.OS == "" { +				instanceInfo.OS = config.OS +			} +			if instanceInfo.Architecture == "" { +				instanceInfo.Architecture = config.Architecture +			} +		} +		manifestBytes, manifestType, err := src.GetManifest(ctx, instanceInfo.instanceDigest) +		if err != nil { +			return "", errors.Wrapf(err, "error reading manifest from %q, instance %q", transports.ImageName(ref), instanceInfo.instanceDigest) +		} +		if instanceInfo.instanceDigest == nil { +			manifestDigest, err = manifest.Digest(manifestBytes) +			if err != nil { +				return "", errors.Wrapf(err, "error computing digest of manifest from %q", transports.ImageName(ref)) +			} +			instanceInfo.instanceDigest = &manifestDigest +			instanceInfo.Size = int64(len(manifestBytes)) +		} else { +			if manifestDigest == "" { +				manifestDigest = *instanceInfo.instanceDigest +			} +		} +		err = l.List.AddInstance(*instanceInfo.instanceDigest, instanceInfo.Size, manifestType, instanceInfo.OS, instanceInfo.Architecture, instanceInfo.OSVersion, instanceInfo.OSFeatures, instanceInfo.Variant, instanceInfo.Features, instanceInfo.Annotations) +		if err != nil { +			return "", errors.Wrapf(err, "error adding instance with digest %q", *instanceInfo.instanceDigest) +		} +		if _, ok := l.instances[*instanceInfo.instanceDigest]; !ok { +			l.instances[*instanceInfo.instanceDigest] = transports.ImageName(ref) +		} +	} + +	return manifestDigest, nil +} + +// Remove filters out any instances in the list which match the specified digest. +func (l *list) Remove(instanceDigest digest.Digest) error { +	err := l.List.Remove(instanceDigest) +	if err == nil { +		if _, needToDelete := l.instances[instanceDigest]; needToDelete { +			delete(l.instances, instanceDigest) +		} +	} +	return err +} diff --git a/vendor/github.com/containers/buildah/pkg/manifests/errors.go b/vendor/github.com/containers/buildah/pkg/manifests/errors.go new file mode 100644 index 000000000..8398d7efc --- /dev/null +++ b/vendor/github.com/containers/buildah/pkg/manifests/errors.go @@ -0,0 +1,16 @@ +package manifests + +import ( +	"errors" +) + +var ( +	// ErrDigestNotFound is returned when we look for an image instance +	// with a particular digest in a list or index, and fail to find it. +	ErrDigestNotFound = errors.New("no image instance matching the specified digest was found in the list or index") +	// ErrManifestTypeNotSupported is returned when we attempt to parse a +	// manifest with a known MIME type as a list or index, or when we attempt +	// to serialize a list or index to a manifest with a MIME type that we +	// don't know how to encode. +	ErrManifestTypeNotSupported = errors.New("manifest type not supported") +) diff --git a/vendor/github.com/containers/buildah/pkg/manifests/manifests.go b/vendor/github.com/containers/buildah/pkg/manifests/manifests.go new file mode 100644 index 000000000..ea9495ee7 --- /dev/null +++ b/vendor/github.com/containers/buildah/pkg/manifests/manifests.go @@ -0,0 +1,493 @@ +package manifests + +import ( +	"encoding/json" +	"os" + +	"github.com/containers/image/v5/manifest" +	digest "github.com/opencontainers/go-digest" +	imgspec "github.com/opencontainers/image-spec/specs-go" +	v1 "github.com/opencontainers/image-spec/specs-go/v1" +	"github.com/pkg/errors" +) + +// List is a generic interface for manipulating a manifest list or an image +// index. +type List interface { +	AddInstance(manifestDigest digest.Digest, manifestSize int64, manifestType, os, architecture, osVersion string, osFeatures []string, variant string, features []string, annotations []string) error +	Remove(instanceDigest digest.Digest) error + +	SetURLs(instanceDigest digest.Digest, urls []string) error +	URLs(instanceDigest digest.Digest) ([]string, error) + +	SetAnnotations(instanceDigest *digest.Digest, annotations map[string]string) error +	Annotations(instanceDigest *digest.Digest) (map[string]string, error) + +	SetOS(instanceDigest digest.Digest, os string) error +	OS(instanceDigest digest.Digest) (string, error) + +	SetArchitecture(instanceDigest digest.Digest, arch string) error +	Architecture(instanceDigest digest.Digest) (string, error) + +	SetOSVersion(instanceDigest digest.Digest, osVersion string) error +	OSVersion(instanceDigest digest.Digest) (string, error) + +	SetVariant(instanceDigest digest.Digest, variant string) error +	Variant(instanceDigest digest.Digest) (string, error) + +	SetFeatures(instanceDigest digest.Digest, features []string) error +	Features(instanceDigest digest.Digest) ([]string, error) + +	SetOSFeatures(instanceDigest digest.Digest, osFeatures []string) error +	OSFeatures(instanceDigest digest.Digest) ([]string, error) + +	Serialize(mimeType string) ([]byte, error) +	Instances() []digest.Digest +	OCIv1() *v1.Index +	Docker() *manifest.Schema2List + +	findDocker(instanceDigest digest.Digest) (*manifest.Schema2ManifestDescriptor, error) +	findOCIv1(instanceDigest digest.Digest) (*v1.Descriptor, error) +} + +type list struct { +	docker manifest.Schema2List +	oci    v1.Index +} + +// OCIv1 returns the list as a Docker schema 2 list.  The returned structure should NOT be modified. +func (l *list) Docker() *manifest.Schema2List { +	return &l.docker +} + +// OCIv1 returns the list as an OCI image index.  The returned structure should NOT be modified. +func (l *list) OCIv1() *v1.Index { +	return &l.oci +} + +// Create creates a new list. +func Create() List { +	return &list{ +		docker: manifest.Schema2List{ +			SchemaVersion: 2, +			MediaType:     manifest.DockerV2ListMediaType, +		}, +		oci: v1.Index{ +			Versioned: imgspec.Versioned{SchemaVersion: 2}, +		}, +	} +} + +// AddInstance adds an entry for the specified manifest digest, with assorted +// additional information specified in parameters, to the list or index. +func (l *list) AddInstance(manifestDigest digest.Digest, manifestSize int64, manifestType, osName, architecture, osVersion string, osFeatures []string, variant string, features []string, annotations []string) error { +	if err := l.Remove(manifestDigest); err != nil && !os.IsNotExist(errors.Cause(err)) { +		return err +	} + +	schema2platform := manifest.Schema2PlatformSpec{ +		Architecture: architecture, +		OS:           osName, +		OSVersion:    osVersion, +		OSFeatures:   osFeatures, +		Variant:      variant, +		Features:     features, +	} +	l.docker.Manifests = append(l.docker.Manifests, manifest.Schema2ManifestDescriptor{ +		Schema2Descriptor: manifest.Schema2Descriptor{ +			MediaType: manifestType, +			Size:      manifestSize, +			Digest:    manifestDigest, +		}, +		Platform: schema2platform, +	}) + +	ociv1platform := v1.Platform{ +		Architecture: architecture, +		OS:           osName, +		OSVersion:    osVersion, +		OSFeatures:   osFeatures, +		Variant:      variant, +	} +	l.oci.Manifests = append(l.oci.Manifests, v1.Descriptor{ +		MediaType: manifestType, +		Size:      manifestSize, +		Digest:    manifestDigest, +		Platform:  &ociv1platform, +	}) + +	return nil +} + +// Remove filters out any instances in the list which match the specified digest. +func (l *list) Remove(instanceDigest digest.Digest) error { +	err := errors.Wrapf(os.ErrNotExist, "no instance matching digest %q found in manifest list", instanceDigest) +	newDockerManifests := make([]manifest.Schema2ManifestDescriptor, 0, len(l.docker.Manifests)) +	for i := range l.docker.Manifests { +		if l.docker.Manifests[i].Digest != instanceDigest { +			newDockerManifests = append(newDockerManifests, l.docker.Manifests[i]) +		} else { +			err = nil +		} +	} +	l.docker.Manifests = newDockerManifests +	newOCIv1Manifests := make([]v1.Descriptor, 0, len(l.oci.Manifests)) +	for i := range l.oci.Manifests { +		if l.oci.Manifests[i].Digest != instanceDigest { +			newOCIv1Manifests = append(newOCIv1Manifests, l.oci.Manifests[i]) +		} else { +			err = nil +		} +	} +	l.oci.Manifests = newOCIv1Manifests +	return err +} + +func (l *list) findDocker(instanceDigest digest.Digest) (*manifest.Schema2ManifestDescriptor, error) { +	for i := range l.docker.Manifests { +		if l.docker.Manifests[i].Digest == instanceDigest { +			return &l.docker.Manifests[i], nil +		} +	} +	return nil, errors.Wrapf(ErrDigestNotFound, "no Docker manifest matching digest %q was found in list", instanceDigest.String()) +} + +func (l *list) findOCIv1(instanceDigest digest.Digest) (*v1.Descriptor, error) { +	for i := range l.oci.Manifests { +		if l.oci.Manifests[i].Digest == instanceDigest { +			return &l.oci.Manifests[i], nil +		} +	} +	return nil, errors.Wrapf(ErrDigestNotFound, "no OCI manifest matching digest %q was found in list", instanceDigest.String()) +} + +// SetURLs sets the URLs where the manifest might also be found. +func (l *list) SetURLs(instanceDigest digest.Digest, urls []string) error { +	oci, err := l.findOCIv1(instanceDigest) +	if err != nil { +		return err +	} +	docker, err := l.findDocker(instanceDigest) +	if err != nil { +		return err +	} +	oci.URLs = append([]string{}, urls...) +	docker.URLs = append([]string{}, urls...) +	return nil +} + +// URLs retrieves the locations from which this object might possibly be downloaded. +func (l *list) URLs(instanceDigest digest.Digest) ([]string, error) { +	oci, err := l.findOCIv1(instanceDigest) +	if err != nil { +		return nil, err +	} +	return append([]string{}, oci.URLs...), nil +} + +// SetAnnotations sets annotations on the image index, or on a specific manifest. +// The field is specific to the OCI image index format, and is not present in Docker manifest lists. +func (l *list) SetAnnotations(instanceDigest *digest.Digest, annotations map[string]string) error { +	a := &l.oci.Annotations +	if instanceDigest != nil { +		oci, err := l.findOCIv1(*instanceDigest) +		if err != nil { +			return err +		} +		a = &oci.Annotations +	} +	(*a) = make(map[string]string) +	for k, v := range annotations { +		(*a)[k] = v +	} +	return nil +} + +// Annotations retrieves the annotations which have been set on the image index, or on one instance. +// The field is specific to the OCI image index format, and is not present in Docker manifest lists. +func (l *list) Annotations(instanceDigest *digest.Digest) (map[string]string, error) { +	a := l.oci.Annotations +	if instanceDigest != nil { +		oci, err := l.findOCIv1(*instanceDigest) +		if err != nil { +			return nil, err +		} +		a = oci.Annotations +	} +	annotations := make(map[string]string) +	for k, v := range a { +		annotations[k] = v +	} +	return annotations, nil +} + +// SetOS sets the OS field in the platform information associated with the instance with the specified digest. +func (l *list) SetOS(instanceDigest digest.Digest, os string) error { +	docker, err := l.findDocker(instanceDigest) +	if err != nil { +		return err +	} +	oci, err := l.findOCIv1(instanceDigest) +	if err != nil { +		return err +	} +	docker.Platform.OS = os +	oci.Platform.OS = os +	return nil +} + +// OS retrieves the OS field in the platform information associated with the instance with the specified digest. +func (l *list) OS(instanceDigest digest.Digest) (string, error) { +	oci, err := l.findOCIv1(instanceDigest) +	if err != nil { +		return "", err +	} +	return oci.Platform.OS, nil +} + +// SetArchitecture sets the Architecture field in the platform information associated with the instance with the specified digest. +func (l *list) SetArchitecture(instanceDigest digest.Digest, arch string) error { +	docker, err := l.findDocker(instanceDigest) +	if err != nil { +		return err +	} +	oci, err := l.findOCIv1(instanceDigest) +	if err != nil { +		return err +	} +	docker.Platform.Architecture = arch +	oci.Platform.Architecture = arch +	return nil +} + +// Architecture retrieves the Architecture field in the platform information associated with the instance with the specified digest. +func (l *list) Architecture(instanceDigest digest.Digest) (string, error) { +	oci, err := l.findOCIv1(instanceDigest) +	if err != nil { +		return "", err +	} +	return oci.Platform.Architecture, nil +} + +// SetOSVersion sets the OSVersion field in the platform information associated with the instance with the specified digest. +func (l *list) SetOSVersion(instanceDigest digest.Digest, osVersion string) error { +	docker, err := l.findDocker(instanceDigest) +	if err != nil { +		return err +	} +	oci, err := l.findOCIv1(instanceDigest) +	if err != nil { +		return err +	} +	docker.Platform.OSVersion = osVersion +	oci.Platform.OSVersion = osVersion +	return nil +} + +// OSVersion retrieves the OSVersion field in the platform information associated with the instance with the specified digest. +func (l *list) OSVersion(instanceDigest digest.Digest) (string, error) { +	oci, err := l.findOCIv1(instanceDigest) +	if err != nil { +		return "", err +	} +	return oci.Platform.OSVersion, nil +} + +// SetVariant sets the Variant field in the platform information associated with the instance with the specified digest. +func (l *list) SetVariant(instanceDigest digest.Digest, variant string) error { +	docker, err := l.findDocker(instanceDigest) +	if err != nil { +		return err +	} +	oci, err := l.findOCIv1(instanceDigest) +	if err != nil { +		return err +	} +	docker.Platform.Variant = variant +	oci.Platform.Variant = variant +	return nil +} + +// Variant retrieves the Variant field in the platform information associated with the instance with the specified digest. +func (l *list) Variant(instanceDigest digest.Digest) (string, error) { +	oci, err := l.findOCIv1(instanceDigest) +	if err != nil { +		return "", err +	} +	return oci.Platform.Variant, nil +} + +// SetFeatures sets the features list in the platform information associated with the instance with the specified digest. +// The field is specific to the Docker manifest list format, and is not present in OCI's image indexes. +func (l *list) SetFeatures(instanceDigest digest.Digest, features []string) error { +	docker, err := l.findDocker(instanceDigest) +	if err != nil { +		return err +	} +	docker.Platform.Features = append([]string{}, features...) +	// no OCI equivalent +	return nil +} + +// Features retrieves the features list from the platform information associated with the instance with the specified digest. +// The field is specific to the Docker manifest list format, and is not present in OCI's image indexes. +func (l *list) Features(instanceDigest digest.Digest) ([]string, error) { +	docker, err := l.findDocker(instanceDigest) +	if err != nil { +		return nil, err +	} +	return append([]string{}, docker.Platform.Features...), nil +} + +// SetOSFeatures sets the OS features list in the platform information associated with the instance with the specified digest. +func (l *list) SetOSFeatures(instanceDigest digest.Digest, osFeatures []string) error { +	docker, err := l.findDocker(instanceDigest) +	if err != nil { +		return err +	} +	oci, err := l.findOCIv1(instanceDigest) +	if err != nil { +		return err +	} +	docker.Platform.OSFeatures = append([]string{}, osFeatures...) +	oci.Platform.OSFeatures = append([]string{}, osFeatures...) +	return nil +} + +// OSFeatures retrieves the OS features list from the platform information associated with the instance with the specified digest. +func (l *list) OSFeatures(instanceDigest digest.Digest) ([]string, error) { +	oci, err := l.findOCIv1(instanceDigest) +	if err != nil { +		return nil, err +	} +	return append([]string{}, oci.Platform.OSFeatures...), nil +} + +// FromBlob builds a list from an encoded manifest list or image index. +func FromBlob(manifestBytes []byte) (List, error) { +	manifestType := manifest.GuessMIMEType(manifestBytes) +	list := &list{ +		docker: manifest.Schema2List{ +			SchemaVersion: 2, +			MediaType:     manifest.DockerV2ListMediaType, +		}, +		oci: v1.Index{ +			Versioned: imgspec.Versioned{SchemaVersion: 2}, +		}, +	} +	switch manifestType { +	default: +		return nil, errors.Wrapf(ErrManifestTypeNotSupported, "unable to load manifest list: unsupported format %q", manifestType) +	case manifest.DockerV2ListMediaType: +		if err := json.Unmarshal(manifestBytes, &list.docker); err != nil { +			return nil, errors.Wrapf(err, "unable to parse Docker manifest list from image") +		} +		for _, m := range list.docker.Manifests { +			list.oci.Manifests = append(list.oci.Manifests, v1.Descriptor{ +				MediaType: m.Schema2Descriptor.MediaType, +				Size:      m.Schema2Descriptor.Size, +				Digest:    m.Schema2Descriptor.Digest, +				Platform: &v1.Platform{ +					Architecture: m.Platform.Architecture, +					OS:           m.Platform.OS, +					OSVersion:    m.Platform.OSVersion, +					OSFeatures:   m.Platform.OSFeatures, +					Variant:      m.Platform.Variant, +				}, +			}) +		} +	case v1.MediaTypeImageIndex: +		if err := json.Unmarshal(manifestBytes, &list.oci); err != nil { +			return nil, errors.Wrapf(err, "unable to parse OCIv1 manifest list") +		} +		for _, m := range list.oci.Manifests { +			platform := m.Platform +			if platform == nil { +				platform = &v1.Platform{} +			} +			list.docker.Manifests = append(list.docker.Manifests, manifest.Schema2ManifestDescriptor{ +				Schema2Descriptor: manifest.Schema2Descriptor{ +					MediaType: m.MediaType, +					Size:      m.Size, +					Digest:    m.Digest, +				}, +				Platform: manifest.Schema2PlatformSpec{ +					Architecture: platform.Architecture, +					OS:           platform.OS, +					OSVersion:    platform.OSVersion, +					OSFeatures:   platform.OSFeatures, +					Variant:      platform.Variant, +				}, +			}) +		} +	} +	return list, nil +} + +func (l *list) preferOCI() bool { +	// If we have any data that's only in the OCI format, use that. +	for _, m := range l.oci.Manifests { +		if len(m.URLs) > 0 { +			return true +		} +		if len(m.Annotations) > 0 { +			return true +		} +	} +	// If we have any data that's only in the Docker format, use that. +	for _, m := range l.docker.Manifests { +		if len(m.Platform.Features) > 0 { +			return false +		} +	} +	// If we have no manifests, remember that the Docker format is +	// explicitly typed, so use that.  Otherwise, default to using the OCI +	// format. +	return len(l.docker.Manifests) != 0 +} + +// Serialize encodes the list using the specified format, or by selecting one +// which it thinks is appropriate. +func (l *list) Serialize(mimeType string) ([]byte, error) { +	var manifestBytes []byte +	switch mimeType { +	case "": +		if l.preferOCI() { +			manifest, err := json.Marshal(&l.oci) +			if err != nil { +				return nil, errors.Wrapf(err, "error marshalling OCI image index") +			} +			manifestBytes = manifest +		} else { +			manifest, err := json.Marshal(&l.docker) +			if err != nil { +				return nil, errors.Wrapf(err, "error marshalling Docker manifest list") +			} +			manifestBytes = manifest +		} +	case v1.MediaTypeImageIndex: +		manifest, err := json.Marshal(&l.oci) +		if err != nil { +			return nil, errors.Wrapf(err, "error marshalling OCI image index") +		} +		manifestBytes = manifest +	case manifest.DockerV2ListMediaType: +		manifest, err := json.Marshal(&l.docker) +		if err != nil { +			return nil, errors.Wrapf(err, "error marshalling Docker manifest list") +		} +		manifestBytes = manifest +	default: +		return nil, errors.Wrapf(ErrManifestTypeNotSupported, "serializing list to type %q not implemented", mimeType) +	} +	return manifestBytes, nil +} + +// Instances returns the list of image instances mentioned in this list. +func (l *list) Instances() []digest.Digest { +	instances := make([]digest.Digest, 0, len(l.oci.Manifests)) +	for _, instance := range l.oci.Manifests { +		instances = append(instances, instance.Digest) +	} +	return instances +} diff --git a/vendor/github.com/containers/buildah/pkg/supplemented/errors.go b/vendor/github.com/containers/buildah/pkg/supplemented/errors.go new file mode 100644 index 000000000..6de679b50 --- /dev/null +++ b/vendor/github.com/containers/buildah/pkg/supplemented/errors.go @@ -0,0 +1,17 @@ +package supplemented + +import ( +	"errors" + +	"github.com/containers/buildah/pkg/manifests" +) + +var ( +	// ErrDigestNotFound is returned when we look for an image instance +	// with a particular digest in a list or index, and fail to find it. +	ErrDigestNotFound = manifests.ErrDigestNotFound +	// ErrBlobNotFound is returned when try to figure out which supplemental +	// image we should ask for a blob with the specified characteristics, +	// based on the information in each of the supplemental images' manifests. +	ErrBlobNotFound = errors.New("location of blob could not be determined") +) diff --git a/vendor/github.com/containers/buildah/pkg/supplemented/supplemented.go b/vendor/github.com/containers/buildah/pkg/supplemented/supplemented.go new file mode 100644 index 000000000..9dd47a0e2 --- /dev/null +++ b/vendor/github.com/containers/buildah/pkg/supplemented/supplemented.go @@ -0,0 +1,393 @@ +package supplemented + +import ( +	"container/list" +	"context" +	"io" + +	cp "github.com/containers/image/v5/copy" +	"github.com/containers/image/v5/image" +	"github.com/containers/image/v5/manifest" +	"github.com/containers/image/v5/transports" +	"github.com/containers/image/v5/types" +	multierror "github.com/hashicorp/go-multierror" +	digest "github.com/opencontainers/go-digest" +	"github.com/pkg/errors" +	"github.com/sirupsen/logrus" +) + +// supplementedImageReference groups multiple references together. +type supplementedImageReference struct { +	types.ImageReference +	references []types.ImageReference +	multiple   cp.ImageListSelection +	instances  []digest.Digest +} + +// supplementedImageSource represents an image, plus all of the blobs of other images. +type supplementedImageSource struct { +	types.ImageSource +	reference                 types.ImageReference +	manifest                  []byte                              // The manifest list or image index. +	manifestType              string                              // The MIME type of the manifest list or image index. +	sourceDefaultInstances    map[types.ImageSource]digest.Digest // The default manifest instances of open ImageSource objects. +	sourceInstancesByInstance map[digest.Digest]types.ImageSource // A map from manifest instance digests to open ImageSource objects. +	instancesByBlobDigest     map[digest.Digest]digest.Digest     // A map from blob digests to manifest instance digests. +} + +// Reference groups one reference and some number of additional references +// together as a group.  The first reference's default instance will be treated +// as the default instance of the resulting reference, with the other +// references' instances made available as instances for their respective +// digests. +func Reference(ref types.ImageReference, supplemental []types.ImageReference, multiple cp.ImageListSelection, instances []digest.Digest) types.ImageReference { +	if len(instances) > 0 { +		i := make([]digest.Digest, len(instances)) +		copy(i, instances) +		instances = i +	} +	return &supplementedImageReference{ +		ImageReference: ref, +		references:     append([]types.ImageReference{}, supplemental...), +		multiple:       multiple, +		instances:      instances, +	} +} + +// NewImage returns a new higher-level view of the image. +func (s *supplementedImageReference) NewImage(ctx context.Context, sys *types.SystemContext) (types.ImageCloser, error) { +	src, err := s.NewImageSource(ctx, sys) +	if err != nil { +		return nil, errors.Wrapf(err, "error building a new Image using an ImageSource") +	} +	return image.FromSource(ctx, sys, src) +} + +// NewImageSource opens the referenced images, scans their manifests for +// instances, and builds mappings from each blob mentioned in them to their +// instances. +func (s *supplementedImageReference) NewImageSource(ctx context.Context, sys *types.SystemContext) (iss types.ImageSource, err error) { +	sources := make(map[digest.Digest]types.ImageSource) +	defaultInstances := make(map[types.ImageSource]digest.Digest) +	instances := make(map[digest.Digest]digest.Digest) +	var sis *supplementedImageSource + +	// Open the default instance for reading. +	top, err := s.ImageReference.NewImageSource(ctx, sys) +	if err != nil { +		return nil, errors.Wrapf(err, "error opening %q as image source", transports.ImageName(s.ImageReference)) +	} + +	defer func() { +		if err != nil { +			if iss != nil { +				// The composite source has been created.  Use its Close method. +				if err2 := iss.Close(); err2 != nil { +					logrus.Errorf("error opening image: %v", err2) +				} +			} else if top != nil { +				// The composite source has not been created, but the top was already opened.  Close it. +				if err2 := top.Close(); err2 != nil { +					logrus.Errorf("error opening image: %v", err2) +				} +			} +		} +	}() + +	var addSingle, addMulti func(manifestBytes []byte, manifestType string, src types.ImageSource) error +	type manifestToRead struct { +		src      types.ImageSource +		instance *digest.Digest +	} +	manifestsToRead := list.New() + +	addSingle = func(manifestBytes []byte, manifestType string, src types.ImageSource) error { +		// Mark this instance as being associated with this ImageSource. +		manifestDigest, err := manifest.Digest(manifestBytes) +		if err != nil { +			return errors.Wrapf(err, "error computing digest over manifest %q", string(manifestBytes)) +		} +		sources[manifestDigest] = src + +		// Parse the manifest as a single image. +		man, err := manifest.FromBlob(manifestBytes, manifestType) +		if err != nil { +			return errors.Wrapf(err, "error parsing manifest %q", string(manifestBytes)) +		} + +		// Log the config blob's digest and the blobs of its layers as associated with this manifest. +		config := man.ConfigInfo() +		if config.Digest != "" { +			instances[config.Digest] = manifestDigest +			logrus.Debugf("blob %q belongs to %q", config.Digest, manifestDigest) +		} + +		layers := man.LayerInfos() +		for _, layer := range layers { +			instances[layer.Digest] = manifestDigest +			logrus.Debugf("layer %q belongs to %q", layer.Digest, manifestDigest) +		} + +		return nil +	} + +	addMulti = func(manifestBytes []byte, manifestType string, src types.ImageSource) error { +		// Mark this instance as being associated with this ImageSource. +		manifestDigest, err := manifest.Digest(manifestBytes) +		if err != nil { +			return errors.Wrapf(err, "error computing manifest digest") +		} +		sources[manifestDigest] = src + +		// Parse the manifest as a list of images. +		list, err := manifest.ListFromBlob(manifestBytes, manifestType) +		if err != nil { +			return errors.Wrapf(err, "error parsing manifest blob %q as a %q", string(manifestBytes), manifestType) +		} + +		// Figure out which of its instances we want to look at. +		var chaseInstances []digest.Digest +		switch s.multiple { +		case cp.CopySystemImage: +			instance, err := list.ChooseInstance(sys) +			if err != nil { +				return errors.Wrapf(err, "error selecting appropriate instance from list") +			} +			chaseInstances = []digest.Digest{instance} +		case cp.CopySpecificImages: +			chaseInstances = s.instances +		case cp.CopyAllImages: +			chaseInstances = list.Instances() +		} + +		// Queue these manifest instances for reading from this +		// ImageSource later, if we don't stumble across them somewhere +		// else first. +		for _, instanceIterator := range chaseInstances { +			instance := instanceIterator +			next := &manifestToRead{ +				src:      src, +				instance: &instance, +			} +			if src == top { +				// Prefer any other source. +				manifestsToRead.PushBack(next) +			} else { +				// Prefer this source over the first ("main") one. +				manifestsToRead.PushFront(next) +			} +		} +		return nil +	} + +	visitedReferences := make(map[types.ImageReference]struct{}) +	for i, ref := range append([]types.ImageReference{s.ImageReference}, s.references...) { +		if _, visited := visitedReferences[ref]; visited { +			continue +		} +		visitedReferences[ref] = struct{}{} + +		// Open this image for reading. +		var src types.ImageSource +		if ref == s.ImageReference { +			src = top +		} else { +			src, err = ref.NewImageSource(ctx, sys) +			if err != nil { +				return nil, errors.Wrapf(err, "error opening %q as image source", transports.ImageName(ref)) +			} +		} + +		// Read the default manifest for the image. +		manifestBytes, manifestType, err := src.GetManifest(ctx, nil) +		if err != nil { +			return nil, errors.Wrapf(err, "error reading default manifest from image %q", transports.ImageName(ref)) +		} + +		// If this is the first image, mark it as our starting point. +		if i == 0 { +			sources[""] = src + +			sis = &supplementedImageSource{ +				ImageSource:               top, +				reference:                 s, +				manifest:                  manifestBytes, +				manifestType:              manifestType, +				sourceDefaultInstances:    defaultInstances, +				sourceInstancesByInstance: sources, +				instancesByBlobDigest:     instances, +			} +			iss = sis +		} + +		// Record the digest of the ImageSource's default instance's manifest. +		manifestDigest, err := manifest.Digest(manifestBytes) +		if err != nil { +			return nil, errors.Wrapf(err, "error computing digest of manifest from image %q", transports.ImageName(ref)) +		} +		sis.sourceDefaultInstances[src] = manifestDigest + +		// If the ImageSource's default manifest is a list, parse each of its instances. +		if manifest.MIMETypeIsMultiImage(manifestType) { +			if err = addMulti(manifestBytes, manifestType, src); err != nil { +				return nil, errors.Wrapf(err, "error adding multi-image %q", transports.ImageName(ref)) +			} +		} else { +			if err = addSingle(manifestBytes, manifestType, src); err != nil { +				return nil, errors.Wrapf(err, "error adding single image %q", transports.ImageName(ref)) +			} +		} +	} + +	// Parse the rest of the instances. +	for manifestsToRead.Front() != nil { +		front := manifestsToRead.Front() +		value := front.Value +		manifestToRead, ok := value.(*manifestToRead) +		if !ok { +			panic("bug: wrong type looking for *manifestToRead in list?") +		} +		manifestsToRead.Remove(front) + +		// If we already read this manifest, no need to read it again. +		if _, alreadyRead := sources[*manifestToRead.instance]; alreadyRead { +			continue +		} + +		// Read the instance's manifest. +		manifestBytes, manifestType, err := manifestToRead.src.GetManifest(ctx, manifestToRead.instance) +		if err != nil { +			// if errors.Cause(err) == storage.ErrImageUnknown || os.IsNotExist(errors.Cause(err)) { +			// Trust that we either don't need it, or that it's in another reference. +			// continue +			// } +			return nil, errors.Wrapf(err, "error reading manifest for instance %q", manifestToRead.instance) +		} + +		if manifest.MIMETypeIsMultiImage(manifestType) { +			// Add the list's contents. +			if err = addMulti(manifestBytes, manifestType, manifestToRead.src); err != nil { +				return nil, errors.Wrapf(err, "error adding single image instance %q", manifestToRead.instance) +			} +		} else { +			// Add the single image's contents. +			if err = addSingle(manifestBytes, manifestType, manifestToRead.src); err != nil { +				return nil, errors.Wrapf(err, "error adding single image instance %q", manifestToRead.instance) +			} +		} +	} + +	return iss, nil +} + +func (s *supplementedImageReference) DeleteImage(ctx context.Context, sys *types.SystemContext) error { +	return errors.Errorf("deletion of images not implemented") +} + +func (s *supplementedImageSource) Close() error { +	var returnErr *multierror.Error +	closed := make(map[types.ImageSource]struct{}) +	for _, sourceInstance := range s.sourceInstancesByInstance { +		if _, closed := closed[sourceInstance]; closed { +			continue +		} +		if err := sourceInstance.Close(); err != nil { +			returnErr = multierror.Append(returnErr, err) +		} +		closed[sourceInstance] = struct{}{} +	} +	return returnErr.ErrorOrNil() +} + +func (s *supplementedImageSource) GetManifest(ctx context.Context, instanceDigest *digest.Digest) ([]byte, string, error) { +	requestInstanceDigest := instanceDigest +	if instanceDigest == nil { +		return s.manifest, s.manifestType, nil +	} +	if sourceInstance, ok := s.sourceInstancesByInstance[*instanceDigest]; ok { +		if *instanceDigest == s.sourceDefaultInstances[sourceInstance] { +			requestInstanceDigest = nil +		} +		return sourceInstance.GetManifest(ctx, requestInstanceDigest) +	} +	return nil, "", errors.Wrapf(ErrDigestNotFound, "error getting manifest for digest %q", *instanceDigest) +} + +func (s *supplementedImageSource) GetBlob(ctx context.Context, blob types.BlobInfo, bic types.BlobInfoCache) (io.ReadCloser, int64, error) { +	sourceInstance, ok := s.instancesByBlobDigest[blob.Digest] +	if !ok { +		return nil, -1, errors.Wrapf(ErrBlobNotFound, "error blob %q in known instances", blob.Digest) +	} +	src, ok := s.sourceInstancesByInstance[sourceInstance] +	if !ok { +		return nil, -1, errors.Wrapf(ErrDigestNotFound, "error getting image source for instance %q", sourceInstance) +	} +	return src.GetBlob(ctx, blob, bic) +} + +func (s *supplementedImageSource) HasThreadSafeGetBlob() bool { +	checked := make(map[types.ImageSource]struct{}) +	for _, sourceInstance := range s.sourceInstancesByInstance { +		if _, checked := checked[sourceInstance]; checked { +			continue +		} +		if !sourceInstance.HasThreadSafeGetBlob() { +			return false +		} +		checked[sourceInstance] = struct{}{} +	} +	return true +} + +func (s *supplementedImageSource) GetSignatures(ctx context.Context, instanceDigest *digest.Digest) ([][]byte, error) { +	var src types.ImageSource +	requestInstanceDigest := instanceDigest +	if instanceDigest == nil { +		if sourceInstance, ok := s.sourceInstancesByInstance[""]; ok { +			src = sourceInstance +		} +	} else { +		if sourceInstance, ok := s.sourceInstancesByInstance[*instanceDigest]; ok { +			src = sourceInstance +		} +		if *instanceDigest == s.sourceDefaultInstances[src] { +			requestInstanceDigest = nil +		} +	} +	if src != nil { +		return src.GetSignatures(ctx, requestInstanceDigest) +	} +	return nil, errors.Wrapf(ErrDigestNotFound, "error finding instance for instance digest %q to read signatures", *instanceDigest) +} + +func (s *supplementedImageSource) LayerInfosForCopy(ctx context.Context, instanceDigest *digest.Digest) ([]types.BlobInfo, error) { +	var src types.ImageSource +	requestInstanceDigest := instanceDigest +	if instanceDigest == nil { +		if sourceInstance, ok := s.sourceInstancesByInstance[""]; ok { +			src = sourceInstance +		} +	} else { +		if sourceInstance, ok := s.sourceInstancesByInstance[*instanceDigest]; ok { +			src = sourceInstance +		} +		if *instanceDigest == s.sourceDefaultInstances[src] { +			requestInstanceDigest = nil +		} +	} +	if src != nil { +		blobInfos, err := src.LayerInfosForCopy(ctx, requestInstanceDigest) +		if err != nil { +			return nil, errors.Wrapf(err, "error reading layer infos for copy from instance %q", instanceDigest) +		} +		var manifestDigest digest.Digest +		if instanceDigest != nil { +			manifestDigest = *instanceDigest +		} +		for _, blobInfo := range blobInfos { +			s.instancesByBlobDigest[blobInfo.Digest] = manifestDigest +		} +		return blobInfos, nil +	} +	return nil, errors.Wrapf(ErrDigestNotFound, "error finding instance for instance digest %q to copy layers", *instanceDigest) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index f6b0e9b40..b6f075400 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -68,13 +68,16 @@ github.com/containers/buildah/bind  github.com/containers/buildah/chroot  github.com/containers/buildah/docker  github.com/containers/buildah/imagebuildah +github.com/containers/buildah/manifests  github.com/containers/buildah/pkg/blobcache  github.com/containers/buildah/pkg/chrootuser  github.com/containers/buildah/pkg/cli  github.com/containers/buildah/pkg/formats +github.com/containers/buildah/pkg/manifests  github.com/containers/buildah/pkg/overlay  github.com/containers/buildah/pkg/parse  github.com/containers/buildah/pkg/secrets +github.com/containers/buildah/pkg/supplemented  github.com/containers/buildah/pkg/umask  github.com/containers/buildah/util  # github.com/containers/common v0.4.2  | 
