mirror of
https://github.com/mudler/luet.git
synced 2025-09-02 07:45:02 +00:00
Update gomod and vendor
This commit is contained in:
319
vendor/github.com/moby/buildkit/exporter/containerimage/export.go
generated
vendored
Normal file
319
vendor/github.com/moby/buildkit/exporter/containerimage/export.go
generated
vendored
Normal file
@@ -0,0 +1,319 @@
|
||||
package containerimage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/content"
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/images"
|
||||
"github.com/containerd/containerd/leases"
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/containerd/containerd/remotes/docker"
|
||||
"github.com/containerd/containerd/rootfs"
|
||||
"github.com/moby/buildkit/cache/blobs"
|
||||
"github.com/moby/buildkit/exporter"
|
||||
"github.com/moby/buildkit/session"
|
||||
"github.com/moby/buildkit/snapshot"
|
||||
"github.com/moby/buildkit/util/leaseutil"
|
||||
"github.com/moby/buildkit/util/push"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/opencontainers/image-spec/identity"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
keyImageName = "name"
|
||||
keyPush = "push"
|
||||
keyPushByDigest = "push-by-digest"
|
||||
keyInsecure = "registry.insecure"
|
||||
keyUnpack = "unpack"
|
||||
keyDanglingPrefix = "dangling-name-prefix"
|
||||
keyNameCanonical = "name-canonical"
|
||||
keyLayerCompression = "compression"
|
||||
ociTypes = "oci-mediatypes"
|
||||
)
|
||||
|
||||
type Opt struct {
|
||||
SessionManager *session.Manager
|
||||
ImageWriter *ImageWriter
|
||||
Images images.Store
|
||||
RegistryHosts docker.RegistryHosts
|
||||
LeaseManager leases.Manager
|
||||
}
|
||||
|
||||
type imageExporter struct {
|
||||
opt Opt
|
||||
}
|
||||
|
||||
// New returns a new containerimage exporter instance that supports exporting
|
||||
// to an image store and pushing the image to registry.
|
||||
// This exporter supports following values in returned kv map:
|
||||
// - containerimage.digest - The digest of the root manifest for the image.
|
||||
func New(opt Opt) (exporter.Exporter, error) {
|
||||
im := &imageExporter{opt: opt}
|
||||
return im, nil
|
||||
}
|
||||
|
||||
func (e *imageExporter) Resolve(ctx context.Context, opt map[string]string) (exporter.ExporterInstance, error) {
|
||||
i := &imageExporterInstance{
|
||||
imageExporter: e,
|
||||
layerCompression: blobs.DefaultCompression,
|
||||
}
|
||||
|
||||
for k, v := range opt {
|
||||
switch k {
|
||||
case keyImageName:
|
||||
i.targetName = v
|
||||
case keyPush:
|
||||
if v == "" {
|
||||
i.push = true
|
||||
continue
|
||||
}
|
||||
b, err := strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "non-bool value specified for %s", k)
|
||||
}
|
||||
i.push = b
|
||||
case keyPushByDigest:
|
||||
if v == "" {
|
||||
i.pushByDigest = true
|
||||
continue
|
||||
}
|
||||
b, err := strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "non-bool value specified for %s", k)
|
||||
}
|
||||
i.pushByDigest = b
|
||||
case keyInsecure:
|
||||
if v == "" {
|
||||
i.insecure = true
|
||||
continue
|
||||
}
|
||||
b, err := strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "non-bool value specified for %s", k)
|
||||
}
|
||||
i.insecure = b
|
||||
case keyUnpack:
|
||||
if v == "" {
|
||||
i.unpack = true
|
||||
continue
|
||||
}
|
||||
b, err := strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "non-bool value specified for %s", k)
|
||||
}
|
||||
i.unpack = b
|
||||
case ociTypes:
|
||||
if v == "" {
|
||||
i.ociTypes = true
|
||||
continue
|
||||
}
|
||||
b, err := strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "non-bool value specified for %s", k)
|
||||
}
|
||||
i.ociTypes = b
|
||||
case keyDanglingPrefix:
|
||||
i.danglingPrefix = v
|
||||
case keyNameCanonical:
|
||||
if v == "" {
|
||||
i.nameCanonical = true
|
||||
continue
|
||||
}
|
||||
b, err := strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "non-bool value specified for %s", k)
|
||||
}
|
||||
i.nameCanonical = b
|
||||
case keyLayerCompression:
|
||||
switch v {
|
||||
case "gzip":
|
||||
i.layerCompression = blobs.Gzip
|
||||
case "uncompressed":
|
||||
i.layerCompression = blobs.Uncompressed
|
||||
default:
|
||||
return nil, errors.Errorf("unsupported layer compression type: %v", v)
|
||||
}
|
||||
default:
|
||||
if i.meta == nil {
|
||||
i.meta = make(map[string][]byte)
|
||||
}
|
||||
i.meta[k] = []byte(v)
|
||||
}
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
type imageExporterInstance struct {
|
||||
*imageExporter
|
||||
targetName string
|
||||
push bool
|
||||
pushByDigest bool
|
||||
unpack bool
|
||||
insecure bool
|
||||
ociTypes bool
|
||||
nameCanonical bool
|
||||
danglingPrefix string
|
||||
layerCompression blobs.CompressionType
|
||||
meta map[string][]byte
|
||||
}
|
||||
|
||||
func (e *imageExporterInstance) Name() string {
|
||||
return "exporting to image"
|
||||
}
|
||||
|
||||
func (e *imageExporterInstance) Export(ctx context.Context, src exporter.Source) (map[string]string, error) {
|
||||
if src.Metadata == nil {
|
||||
src.Metadata = make(map[string][]byte)
|
||||
}
|
||||
for k, v := range e.meta {
|
||||
src.Metadata[k] = v
|
||||
}
|
||||
|
||||
ctx, done, err := leaseutil.WithLease(ctx, e.opt.LeaseManager, leaseutil.MakeTemporary)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer done(context.TODO())
|
||||
|
||||
desc, err := e.opt.ImageWriter.Commit(ctx, src, e.ociTypes, e.layerCompression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
e.opt.ImageWriter.ContentStore().Delete(context.TODO(), desc.Digest)
|
||||
}()
|
||||
|
||||
resp := make(map[string]string)
|
||||
|
||||
if n, ok := src.Metadata["image.name"]; e.targetName == "*" && ok {
|
||||
e.targetName = string(n)
|
||||
}
|
||||
|
||||
nameCanonical := e.nameCanonical
|
||||
if e.targetName == "" && e.danglingPrefix != "" {
|
||||
e.targetName = e.danglingPrefix + "@" + desc.Digest.String()
|
||||
nameCanonical = false
|
||||
}
|
||||
|
||||
if e.targetName != "" {
|
||||
targetNames := strings.Split(e.targetName, ",")
|
||||
for _, targetName := range targetNames {
|
||||
if e.opt.Images != nil {
|
||||
tagDone := oneOffProgress(ctx, "naming to "+targetName)
|
||||
img := images.Image{
|
||||
Target: *desc,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
sfx := []string{""}
|
||||
if nameCanonical {
|
||||
sfx = append(sfx, "@"+desc.Digest.String())
|
||||
}
|
||||
for _, sfx := range sfx {
|
||||
img.Name = targetName + sfx
|
||||
if _, err := e.opt.Images.Update(ctx, img); err != nil {
|
||||
if !errdefs.IsNotFound(err) {
|
||||
return nil, tagDone(err)
|
||||
}
|
||||
|
||||
if _, err := e.opt.Images.Create(ctx, img); err != nil {
|
||||
return nil, tagDone(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
tagDone(nil)
|
||||
|
||||
if e.unpack {
|
||||
if err := e.unpackImage(ctx, img); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if e.push {
|
||||
if err := push.Push(ctx, e.opt.SessionManager, e.opt.ImageWriter.ContentStore(), desc.Digest, targetName, e.insecure, e.opt.RegistryHosts, e.pushByDigest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
resp["image.name"] = e.targetName
|
||||
}
|
||||
|
||||
resp["containerimage.digest"] = desc.Digest.String()
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (e *imageExporterInstance) unpackImage(ctx context.Context, img images.Image) (err0 error) {
|
||||
unpackDone := oneOffProgress(ctx, "unpacking to "+img.Name)
|
||||
defer func() {
|
||||
unpackDone(err0)
|
||||
}()
|
||||
|
||||
var (
|
||||
contentStore = e.opt.ImageWriter.ContentStore()
|
||||
applier = e.opt.ImageWriter.Applier()
|
||||
snapshotter = e.opt.ImageWriter.Snapshotter()
|
||||
)
|
||||
|
||||
// fetch manifest by default platform
|
||||
manifest, err := images.Manifest(ctx, contentStore, img.Target, platforms.Default())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
layers, err := getLayers(ctx, contentStore, manifest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// get containerd snapshotter
|
||||
ctrdSnapshotter, release := snapshot.NewContainerdSnapshotter(snapshotter)
|
||||
defer release()
|
||||
|
||||
var chain []digest.Digest
|
||||
for _, layer := range layers {
|
||||
if _, err := rootfs.ApplyLayer(ctx, layer, chain, ctrdSnapshotter, applier); err != nil {
|
||||
return err
|
||||
}
|
||||
chain = append(chain, layer.Diff.Digest)
|
||||
}
|
||||
|
||||
var (
|
||||
keyGCLabel = fmt.Sprintf("containerd.io/gc.ref.snapshot.%s", snapshotter.Name())
|
||||
valueGCLabel = identity.ChainID(chain).String()
|
||||
)
|
||||
|
||||
cinfo := content.Info{
|
||||
Digest: manifest.Config.Digest,
|
||||
Labels: map[string]string{keyGCLabel: valueGCLabel},
|
||||
}
|
||||
_, err = contentStore.Update(ctx, cinfo, fmt.Sprintf("labels.%s", keyGCLabel))
|
||||
return err
|
||||
}
|
||||
|
||||
func getLayers(ctx context.Context, contentStore content.Store, manifest ocispec.Manifest) ([]rootfs.Layer, error) {
|
||||
diffIDs, err := images.RootFS(ctx, contentStore, manifest.Config)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to resolve rootfs")
|
||||
}
|
||||
|
||||
if len(diffIDs) != len(manifest.Layers) {
|
||||
return nil, errors.Errorf("mismatched image rootfs and manifest layers")
|
||||
}
|
||||
|
||||
layers := make([]rootfs.Layer, len(diffIDs))
|
||||
for i := range diffIDs {
|
||||
layers[i].Diff = ocispec.Descriptor{
|
||||
MediaType: ocispec.MediaTypeImageLayer,
|
||||
Digest: diffIDs[i],
|
||||
}
|
||||
layers[i].Blob = manifest.Layers[i]
|
||||
}
|
||||
return layers, nil
|
||||
}
|
16
vendor/github.com/moby/buildkit/exporter/containerimage/exptypes/types.go
generated
vendored
Normal file
16
vendor/github.com/moby/buildkit/exporter/containerimage/exptypes/types.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package exptypes
|
||||
|
||||
import specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
|
||||
const ExporterImageConfigKey = "containerimage.config"
|
||||
const ExporterInlineCache = "containerimage.inlinecache"
|
||||
const ExporterPlatformsKey = "refs.platforms"
|
||||
|
||||
type Platforms struct {
|
||||
Platforms []Platform
|
||||
}
|
||||
|
||||
type Platform struct {
|
||||
ID string
|
||||
Platform specs.Platform
|
||||
}
|
523
vendor/github.com/moby/buildkit/exporter/containerimage/writer.go
generated
vendored
Normal file
523
vendor/github.com/moby/buildkit/exporter/containerimage/writer.go
generated
vendored
Normal file
@@ -0,0 +1,523 @@
|
||||
package containerimage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/content"
|
||||
"github.com/containerd/containerd/diff"
|
||||
"github.com/containerd/containerd/images"
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/moby/buildkit/cache"
|
||||
"github.com/moby/buildkit/cache/blobs"
|
||||
"github.com/moby/buildkit/exporter"
|
||||
"github.com/moby/buildkit/exporter/containerimage/exptypes"
|
||||
"github.com/moby/buildkit/snapshot"
|
||||
"github.com/moby/buildkit/util/progress"
|
||||
"github.com/moby/buildkit/util/system"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
specs "github.com/opencontainers/image-spec/specs-go"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
const (
|
||||
emptyGZLayer = digest.Digest("sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1")
|
||||
)
|
||||
|
||||
type WriterOpt struct {
|
||||
Snapshotter snapshot.Snapshotter
|
||||
ContentStore content.Store
|
||||
Applier diff.Applier
|
||||
Differ diff.Comparer
|
||||
}
|
||||
|
||||
func NewImageWriter(opt WriterOpt) (*ImageWriter, error) {
|
||||
return &ImageWriter{opt: opt}, nil
|
||||
}
|
||||
|
||||
type ImageWriter struct {
|
||||
opt WriterOpt
|
||||
}
|
||||
|
||||
func (ic *ImageWriter) Commit(ctx context.Context, inp exporter.Source, oci bool, compression blobs.CompressionType) (*ocispec.Descriptor, error) {
|
||||
platformsBytes, ok := inp.Metadata[exptypes.ExporterPlatformsKey]
|
||||
|
||||
if len(inp.Refs) > 0 && !ok {
|
||||
return nil, errors.Errorf("unable to export multiple refs, missing platforms mapping")
|
||||
}
|
||||
|
||||
if len(inp.Refs) == 0 {
|
||||
layers, err := ic.exportLayers(ctx, compression, inp.Ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ic.commitDistributionManifest(ctx, inp.Ref, inp.Metadata[exptypes.ExporterImageConfigKey], layers[0], oci, inp.Metadata[exptypes.ExporterInlineCache])
|
||||
}
|
||||
|
||||
var p exptypes.Platforms
|
||||
if err := json.Unmarshal(platformsBytes, &p); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse platforms passed to exporter")
|
||||
}
|
||||
|
||||
if len(p.Platforms) != len(inp.Refs) {
|
||||
return nil, errors.Errorf("number of platforms does not match references %d %d", len(p.Platforms), len(inp.Refs))
|
||||
}
|
||||
|
||||
refs := make([]cache.ImmutableRef, 0, len(inp.Refs))
|
||||
layersMap := make(map[string]int, len(inp.Refs))
|
||||
for id, r := range inp.Refs {
|
||||
layersMap[id] = len(refs)
|
||||
refs = append(refs, r)
|
||||
}
|
||||
|
||||
layers, err := ic.exportLayers(ctx, compression, refs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
idx := struct {
|
||||
// MediaType is reserved in the OCI spec but
|
||||
// excluded from go types.
|
||||
MediaType string `json:"mediaType,omitempty"`
|
||||
|
||||
ocispec.Index
|
||||
}{
|
||||
MediaType: ocispec.MediaTypeImageIndex,
|
||||
Index: ocispec.Index{
|
||||
Versioned: specs.Versioned{
|
||||
SchemaVersion: 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if !oci {
|
||||
idx.MediaType = images.MediaTypeDockerSchema2ManifestList
|
||||
}
|
||||
|
||||
labels := map[string]string{}
|
||||
|
||||
for i, p := range p.Platforms {
|
||||
r, ok := inp.Refs[p.ID]
|
||||
if !ok {
|
||||
return nil, errors.Errorf("failed to find ref for ID %s", p.ID)
|
||||
}
|
||||
config := inp.Metadata[fmt.Sprintf("%s/%s", exptypes.ExporterImageConfigKey, p.ID)]
|
||||
|
||||
desc, err := ic.commitDistributionManifest(ctx, r, config, layers[layersMap[p.ID]], oci, inp.Metadata[fmt.Sprintf("%s/%s", exptypes.ExporterInlineCache, p.ID)])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dp := p.Platform
|
||||
desc.Platform = &dp
|
||||
idx.Manifests = append(idx.Manifests, *desc)
|
||||
|
||||
labels[fmt.Sprintf("containerd.io/gc.ref.content.%d", i)] = desc.Digest.String()
|
||||
}
|
||||
|
||||
idxBytes, err := json.MarshalIndent(idx, "", " ")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshal index")
|
||||
}
|
||||
|
||||
idxDigest := digest.FromBytes(idxBytes)
|
||||
idxDesc := ocispec.Descriptor{
|
||||
Digest: idxDigest,
|
||||
Size: int64(len(idxBytes)),
|
||||
MediaType: idx.MediaType,
|
||||
}
|
||||
idxDone := oneOffProgress(ctx, "exporting manifest list "+idxDigest.String())
|
||||
|
||||
if err := content.WriteBlob(ctx, ic.opt.ContentStore, idxDigest.String(), bytes.NewReader(idxBytes), idxDesc, content.WithLabels(labels)); err != nil {
|
||||
return nil, idxDone(errors.Wrapf(err, "error writing manifest list blob %s", idxDigest))
|
||||
}
|
||||
idxDone(nil)
|
||||
|
||||
return &idxDesc, nil
|
||||
}
|
||||
|
||||
func (ic *ImageWriter) exportLayers(ctx context.Context, compression blobs.CompressionType, refs ...cache.ImmutableRef) ([][]blobs.DiffPair, error) {
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
layersDone := oneOffProgress(ctx, "exporting layers")
|
||||
|
||||
out := make([][]blobs.DiffPair, len(refs))
|
||||
|
||||
for i, ref := range refs {
|
||||
func(i int, ref cache.ImmutableRef) {
|
||||
eg.Go(func() error {
|
||||
diffPairs, err := blobs.GetDiffPairs(ctx, ic.opt.ContentStore, ic.opt.Differ, ref, true, compression)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed calculating diff pairs for exported snapshot")
|
||||
}
|
||||
out[i] = diffPairs
|
||||
return nil
|
||||
})
|
||||
}(i, ref)
|
||||
}
|
||||
|
||||
if err := layersDone(eg.Wait()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (ic *ImageWriter) commitDistributionManifest(ctx context.Context, ref cache.ImmutableRef, config []byte, layers []blobs.DiffPair, oci bool, cache []byte) (*ocispec.Descriptor, error) {
|
||||
if len(config) == 0 {
|
||||
var err error
|
||||
config, err = emptyImageConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
history, err := parseHistoryFromConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
diffPairs, history := normalizeLayersAndHistory(layers, history, ref)
|
||||
|
||||
config, err = patchImageConfig(config, diffPairs, history, cache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
configDigest = digest.FromBytes(config)
|
||||
manifestType = ocispec.MediaTypeImageManifest
|
||||
configType = ocispec.MediaTypeImageConfig
|
||||
)
|
||||
|
||||
// Use docker media types for older Docker versions and registries
|
||||
if !oci {
|
||||
manifestType = images.MediaTypeDockerSchema2Manifest
|
||||
configType = images.MediaTypeDockerSchema2Config
|
||||
}
|
||||
|
||||
mfst := struct {
|
||||
// MediaType is reserved in the OCI spec but
|
||||
// excluded from go types.
|
||||
MediaType string `json:"mediaType,omitempty"`
|
||||
|
||||
ocispec.Manifest
|
||||
}{
|
||||
MediaType: manifestType,
|
||||
Manifest: ocispec.Manifest{
|
||||
Versioned: specs.Versioned{
|
||||
SchemaVersion: 2,
|
||||
},
|
||||
Config: ocispec.Descriptor{
|
||||
Digest: configDigest,
|
||||
Size: int64(len(config)),
|
||||
MediaType: configType,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
labels := map[string]string{
|
||||
"containerd.io/gc.ref.content.0": configDigest.String(),
|
||||
}
|
||||
|
||||
layerMediaTypes := blobs.GetMediaTypeForLayers(diffPairs, ref)
|
||||
cs := ic.opt.ContentStore
|
||||
for i, dp := range diffPairs {
|
||||
info, err := cs.Info(ctx, dp.Blobsum)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not find blob %s from contentstore", dp.Blobsum)
|
||||
}
|
||||
|
||||
var layerType string
|
||||
if len(layerMediaTypes) > i {
|
||||
layerType = layerMediaTypes[i]
|
||||
}
|
||||
|
||||
// NOTE: The media type might be missing for some migrated ones
|
||||
// from before lease based storage. If so, we should detect
|
||||
// the media type from blob data.
|
||||
//
|
||||
// Discussion: https://github.com/moby/buildkit/pull/1277#discussion_r352795429
|
||||
if layerType == "" {
|
||||
layerType, err = blobs.DetectLayerMediaType(ctx, cs, dp.Blobsum, oci)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
mfst.Layers = append(mfst.Layers, ocispec.Descriptor{
|
||||
Digest: dp.Blobsum,
|
||||
Size: info.Size,
|
||||
MediaType: layerType,
|
||||
})
|
||||
labels[fmt.Sprintf("containerd.io/gc.ref.content.%d", i+1)] = dp.Blobsum.String()
|
||||
}
|
||||
|
||||
mfstJSON, err := json.MarshalIndent(mfst, "", " ")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshal manifest")
|
||||
}
|
||||
|
||||
mfstDigest := digest.FromBytes(mfstJSON)
|
||||
mfstDesc := ocispec.Descriptor{
|
||||
Digest: mfstDigest,
|
||||
Size: int64(len(mfstJSON)),
|
||||
}
|
||||
mfstDone := oneOffProgress(ctx, "exporting manifest "+mfstDigest.String())
|
||||
|
||||
if err := content.WriteBlob(ctx, ic.opt.ContentStore, mfstDigest.String(), bytes.NewReader(mfstJSON), mfstDesc, content.WithLabels((labels))); err != nil {
|
||||
return nil, mfstDone(errors.Wrapf(err, "error writing manifest blob %s", mfstDigest))
|
||||
}
|
||||
mfstDone(nil)
|
||||
|
||||
configDesc := ocispec.Descriptor{
|
||||
Digest: configDigest,
|
||||
Size: int64(len(config)),
|
||||
MediaType: configType,
|
||||
}
|
||||
configDone := oneOffProgress(ctx, "exporting config "+configDigest.String())
|
||||
|
||||
if err := content.WriteBlob(ctx, ic.opt.ContentStore, configDigest.String(), bytes.NewReader(config), configDesc); err != nil {
|
||||
return nil, configDone(errors.Wrap(err, "error writing config blob"))
|
||||
}
|
||||
configDone(nil)
|
||||
|
||||
return &ocispec.Descriptor{
|
||||
Digest: mfstDigest,
|
||||
Size: int64(len(mfstJSON)),
|
||||
MediaType: manifestType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ic *ImageWriter) ContentStore() content.Store {
|
||||
return ic.opt.ContentStore
|
||||
}
|
||||
|
||||
func (ic *ImageWriter) Snapshotter() snapshot.Snapshotter {
|
||||
return ic.opt.Snapshotter
|
||||
}
|
||||
|
||||
func (ic *ImageWriter) Applier() diff.Applier {
|
||||
return ic.opt.Applier
|
||||
}
|
||||
|
||||
func emptyImageConfig() ([]byte, error) {
|
||||
pl := platforms.Normalize(platforms.DefaultSpec())
|
||||
|
||||
type image struct {
|
||||
ocispec.Image
|
||||
|
||||
// Variant defines platform variant. To be added to OCI.
|
||||
Variant string `json:"variant,omitempty"`
|
||||
}
|
||||
|
||||
img := image{
|
||||
Image: ocispec.Image{
|
||||
Architecture: pl.Architecture,
|
||||
OS: pl.OS,
|
||||
},
|
||||
Variant: pl.Variant,
|
||||
}
|
||||
img.RootFS.Type = "layers"
|
||||
img.Config.WorkingDir = "/"
|
||||
img.Config.Env = []string{"PATH=" + system.DefaultPathEnv}
|
||||
dt, err := json.Marshal(img)
|
||||
return dt, errors.Wrap(err, "failed to create empty image config")
|
||||
}
|
||||
|
||||
func parseHistoryFromConfig(dt []byte) ([]ocispec.History, error) {
|
||||
var config struct {
|
||||
History []ocispec.History
|
||||
}
|
||||
if err := json.Unmarshal(dt, &config); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal history from config")
|
||||
}
|
||||
return config.History, nil
|
||||
}
|
||||
|
||||
func patchImageConfig(dt []byte, dps []blobs.DiffPair, history []ocispec.History, cache []byte) ([]byte, error) {
|
||||
m := map[string]json.RawMessage{}
|
||||
if err := json.Unmarshal(dt, &m); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse image config for patch")
|
||||
}
|
||||
|
||||
var rootFS ocispec.RootFS
|
||||
rootFS.Type = "layers"
|
||||
for _, dp := range dps {
|
||||
rootFS.DiffIDs = append(rootFS.DiffIDs, dp.DiffID)
|
||||
}
|
||||
dt, err := json.Marshal(rootFS)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshal rootfs")
|
||||
}
|
||||
m["rootfs"] = dt
|
||||
|
||||
dt, err = json.Marshal(history)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshal history")
|
||||
}
|
||||
m["history"] = dt
|
||||
|
||||
if _, ok := m["created"]; !ok {
|
||||
var tm *time.Time
|
||||
for _, h := range history {
|
||||
if h.Created != nil {
|
||||
tm = h.Created
|
||||
}
|
||||
}
|
||||
dt, err = json.Marshal(&tm)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshal creation time")
|
||||
}
|
||||
m["created"] = dt
|
||||
}
|
||||
|
||||
if cache != nil {
|
||||
dt, err := json.Marshal(cache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m["moby.buildkit.cache.v0"] = dt
|
||||
}
|
||||
|
||||
dt, err = json.Marshal(m)
|
||||
return dt, errors.Wrap(err, "failed to marshal config after patch")
|
||||
}
|
||||
|
||||
func normalizeLayersAndHistory(diffs []blobs.DiffPair, history []ocispec.History, ref cache.ImmutableRef) ([]blobs.DiffPair, []ocispec.History) {
|
||||
|
||||
refMeta := getRefMetadata(ref, len(diffs))
|
||||
|
||||
var historyLayers int
|
||||
for _, h := range history {
|
||||
if !h.EmptyLayer {
|
||||
historyLayers += 1
|
||||
}
|
||||
}
|
||||
|
||||
if historyLayers > len(diffs) {
|
||||
// this case shouldn't happen but if it does force set history layers empty
|
||||
// from the bottom
|
||||
logrus.Warn("invalid image config with unaccounted layers")
|
||||
historyCopy := make([]ocispec.History, 0, len(history))
|
||||
var l int
|
||||
for _, h := range history {
|
||||
if l >= len(diffs) {
|
||||
h.EmptyLayer = true
|
||||
}
|
||||
if !h.EmptyLayer {
|
||||
l++
|
||||
}
|
||||
historyCopy = append(historyCopy, h)
|
||||
}
|
||||
history = historyCopy
|
||||
}
|
||||
|
||||
if len(diffs) > historyLayers {
|
||||
// some history items are missing. add them based on the ref metadata
|
||||
for _, md := range refMeta[historyLayers:] {
|
||||
history = append(history, ocispec.History{
|
||||
Created: &md.createdAt,
|
||||
CreatedBy: md.description,
|
||||
Comment: "buildkit.exporter.image.v0",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var layerIndex int
|
||||
for i, h := range history {
|
||||
if !h.EmptyLayer {
|
||||
if h.Created == nil {
|
||||
h.Created = &refMeta[layerIndex].createdAt
|
||||
}
|
||||
if diffs[layerIndex].Blobsum == emptyGZLayer {
|
||||
h.EmptyLayer = true
|
||||
diffs = append(diffs[:layerIndex], diffs[layerIndex+1:]...)
|
||||
} else {
|
||||
layerIndex++
|
||||
}
|
||||
}
|
||||
history[i] = h
|
||||
}
|
||||
|
||||
// Find the first new layer time. Otherwise, the history item for a first
|
||||
// metadata command would be the creation time of a base image layer.
|
||||
// If there is no such then the last layer with timestamp.
|
||||
var created *time.Time
|
||||
var noCreatedTime bool
|
||||
for _, h := range history {
|
||||
if h.Created != nil {
|
||||
created = h.Created
|
||||
if noCreatedTime {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
noCreatedTime = true
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in created times for all history items to be either the first new
|
||||
// layer time or the previous layer.
|
||||
noCreatedTime = false
|
||||
for i, h := range history {
|
||||
if h.Created != nil {
|
||||
if noCreatedTime {
|
||||
created = h.Created
|
||||
}
|
||||
} else {
|
||||
noCreatedTime = true
|
||||
h.Created = created
|
||||
}
|
||||
history[i] = h
|
||||
}
|
||||
|
||||
return diffs, history
|
||||
}
|
||||
|
||||
type refMetadata struct {
|
||||
description string
|
||||
createdAt time.Time
|
||||
}
|
||||
|
||||
func getRefMetadata(ref cache.ImmutableRef, limit int) []refMetadata {
|
||||
if limit <= 0 {
|
||||
return nil
|
||||
}
|
||||
meta := refMetadata{
|
||||
description: "created by buildkit", // shouldn't be shown but don't fail build
|
||||
createdAt: time.Now(),
|
||||
}
|
||||
if ref == nil {
|
||||
return append(getRefMetadata(nil, limit-1), meta)
|
||||
}
|
||||
if descr := cache.GetDescription(ref.Metadata()); descr != "" {
|
||||
meta.description = descr
|
||||
}
|
||||
meta.createdAt = cache.GetCreatedAt(ref.Metadata())
|
||||
p := ref.Parent()
|
||||
if p != nil {
|
||||
defer p.Release(context.TODO())
|
||||
}
|
||||
return append(getRefMetadata(p, limit-1), meta)
|
||||
}
|
||||
|
||||
func oneOffProgress(ctx context.Context, id string) func(err error) error {
|
||||
pw, _, _ := progress.FromContext(ctx)
|
||||
now := time.Now()
|
||||
st := progress.Status{
|
||||
Started: &now,
|
||||
}
|
||||
pw.Write(id, st)
|
||||
return func(err error) error {
|
||||
// TODO: set error on status
|
||||
now := time.Now()
|
||||
st.Completed = &now
|
||||
pw.Write(id, st)
|
||||
pw.Close()
|
||||
return err
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user