1
0
mirror of https://github.com/regclient/regclient.git synced 2025-04-18 22:44:00 +03:00
regclient/mod/manifest.go
Brandon Mitchell eea06e2a5c
Refactoring the type package
I feel like I need to explain, this is all to move the descriptor package.
The platform package could not use the predefined errors in types because of a circular dependency from descriptor.
The most appropriate way to reorg this is to move descriptor out of the type package since it was more complex than a self contained type.
When doing that, type aliases were needed to avoid breaking changes to existing users.
Those aliases themselves caused circular dependency loops because of the media types and errors, so those were also pulled out to separate packages.
All of the old values were aliased and deprecated, and to fix the linter, those deprecations were fixed by updating the imports... everywhere.

Signed-off-by: Brandon Mitchell <git@bmitch.net>
2024-03-04 15:43:18 -05:00

813 lines
23 KiB
Go

package mod
import (
"context"
"fmt"
"strings"
"github.com/opencontainers/go-digest"
"github.com/regclient/regclient"
"github.com/regclient/regclient/types"
"github.com/regclient/regclient/types/docker/schema2"
"github.com/regclient/regclient/types/errs"
"github.com/regclient/regclient/types/manifest"
"github.com/regclient/regclient/types/mediatype"
"github.com/regclient/regclient/types/platform"
"github.com/regclient/regclient/types/ref"
)
// WithAnnotation adds an annotation, or deletes it if the value is set to an empty string.
// If name is not prefixed with a platform selector, this only applies to the top level manifest.
// Name may be prefixed with a list of platforms "[p1,p2,...]name", e.g. "[linux/amd64]com.example.field".
// The platform selector may also be "[*]" to apply to all manifests, including the top level manifest list.
func WithAnnotation(name, value string) Opts {
return func(dc *dagConfig, dm *dagManifest) error {
// extract the list for platforms to update from the name
name = strings.TrimSpace(name)
platforms := []platform.Platform{}
allPlatforms := false
if name[0] == '[' && strings.Index(name, "]") > 0 {
end := strings.Index(name, "]")
list := strings.Split(name[1:end], ",")
for _, entry := range list {
entry = strings.TrimSpace(entry)
if entry == "*" {
allPlatforms = true
continue
}
p, err := platform.Parse(entry)
if err != nil {
return fmt.Errorf("failed to parse annotation platform %s: %w", entry, err)
}
platforms = append(platforms, p)
}
name = name[end+1:]
}
dc.stepsManifest = append(dc.stepsManifest, func(ctx context.Context, rc *regclient.RegClient, rSrc, rTgt ref.Ref, dm *dagManifest) error {
// skip deleted manifest, those not in the platform list, or the non-top manifest if no platform list provided
if dm.mod == deleted {
return nil
}
if len(platforms) > 0 && !allPlatforms {
if dm.m.IsList() || dm.config == nil || dm.config.oc == nil {
return nil
}
p := dm.config.oc.GetConfig().Platform
found := false
for _, pe := range platforms {
if platform.Match(p, pe) {
found = true
break
}
}
if !found {
return nil
}
}
if len(platforms) == 0 && !allPlatforms && !dm.top {
return nil
}
// check if annotation is already set to the correct value
ma := dm.m.(manifest.Annotator)
annotations, err := ma.GetAnnotations()
if err != nil {
return err
}
if annotations == nil {
annotations = map[string]string{}
}
cur, ok := annotations[name]
if (value == "" && !ok) || (value != "" && value == cur) {
return nil
}
// update annotation
err = ma.SetAnnotation(name, value)
if err != nil {
return err
}
dm.mod = replaced
dm.newDesc = dm.m.GetDescriptor()
return nil
})
return nil
}
}
// WithAnnotationOCIBase adds annotations for the base image.
func WithAnnotationOCIBase(rBase ref.Ref, dBase digest.Digest) Opts {
return func(dc *dagConfig, dm *dagManifest) error {
dc.stepsManifest = append(dc.stepsManifest, func(ctx context.Context, rc *regclient.RegClient, rSrc, rTgt ref.Ref, dm *dagManifest) error {
if dm.mod == deleted {
return nil
}
annoBaseDig := "org.opencontainers.image.base.digest"
annoBaseName := "org.opencontainers.image.base.name"
changed := false
om := dm.m.GetOrig()
if dm.m.IsList() {
ociI, err := manifest.OCIIndexFromAny(om)
if err != nil {
return err
}
if ociI.Annotations == nil {
ociI.Annotations = map[string]string{}
}
if ociI.Annotations[annoBaseName] != rBase.CommonName() {
ociI.Annotations[annoBaseName] = rBase.CommonName()
changed = true
}
if ociI.Annotations[annoBaseDig] != dBase.String() {
ociI.Annotations[annoBaseDig] = dBase.String()
changed = true
}
err = manifest.OCIIndexToAny(ociI, &om)
if err != nil {
return err
}
} else {
ociM, err := manifest.OCIManifestFromAny(om)
if err != nil {
return err
}
if ociM.Annotations == nil {
ociM.Annotations = map[string]string{}
}
if ociM.Annotations[annoBaseName] != rBase.CommonName() {
ociM.Annotations[annoBaseName] = rBase.CommonName()
changed = true
}
if ociM.Annotations[annoBaseDig] != dBase.String() {
ociM.Annotations[annoBaseDig] = dBase.String()
changed = true
}
err = manifest.OCIManifestToAny(ociM, &om)
if err != nil {
return err
}
}
if changed {
dm.mod = replaced
err := dm.m.SetOrig(om)
if err != nil {
return err
}
dm.newDesc = dm.m.GetDescriptor()
}
return nil
})
return nil
}
}
// WithAnnotationPromoteCommon pulls up common annotations from child images to the index.
func WithAnnotationPromoteCommon() Opts {
return func(dc *dagConfig, dm *dagManifest) error {
dc.stepsManifest = append(dc.stepsManifest, func(ctx context.Context, rc *regclient.RegClient, rSrc, rTgt ref.Ref, dm *dagManifest) error {
if dm.mod == deleted {
return nil
}
changed := false
if !dm.m.IsList() {
return nil
}
ociI, err := manifest.OCIIndexFromAny(dm.m.GetOrig())
if err != nil {
return err
}
var common map[string]string
for _, child := range dm.manifests {
if child.mod == deleted {
continue
}
mAnnot, ok := child.m.(manifest.Annotator)
if !ok {
return fmt.Errorf("manifest does not support annotations: %s%.0w", child.m.GetDescriptor().Digest.String(), errs.ErrUnsupportedMediaType)
}
cur, err := mAnnot.GetAnnotations()
if err != nil {
return err
}
if common == nil {
common = cur
} else {
for k, v := range common {
if curV, ok := cur[k]; !ok || v != curV {
delete(common, k)
}
}
}
if len(common) == 0 {
return nil
}
}
if len(common) == 0 {
return nil
}
if ociI.Annotations == nil {
ociI.Annotations = common
changed = true
} else {
for k, v := range common {
if curV, ok := ociI.Annotations[k]; !ok || v != curV {
ociI.Annotations[k] = v
changed = true
}
}
}
if changed {
dm.mod = replaced
err = dm.m.SetOrig(ociI)
if err != nil {
return err
}
dm.newDesc = dm.m.GetDescriptor()
}
return nil
})
return nil
}
}
// WithLabelToAnnotation copies image config labels to manifest annotations.
func WithLabelToAnnotation() Opts {
return func(dc *dagConfig, dm *dagManifest) error {
dc.stepsManifest = append(dc.stepsManifest, func(c context.Context, rc *regclient.RegClient, rSrc, rTgt ref.Ref, dm *dagManifest) error {
if dm.mod == deleted {
return nil
}
changed := false
if dm.m.IsList() {
return nil
}
om := dm.m.GetOrig()
ociOM, err := manifest.OCIManifestFromAny(om)
if err != nil {
return err
}
if ociOM.Annotations == nil {
ociOM.Annotations = map[string]string{}
}
if dm.config == nil || dm.config.oc == nil {
return nil
}
oc := dm.config.oc.GetConfig()
if oc.Config.Labels == nil {
return nil
}
for name, value := range oc.Config.Labels {
cur, ok := ociOM.Annotations[name]
if !ok || cur != value {
ociOM.Annotations[name] = value
changed = true
}
}
if !changed {
return nil
}
err = manifest.OCIManifestToAny(ociOM, &om)
if err != nil {
return err
}
err = dm.m.SetOrig(om)
if err != nil {
return err
}
dm.newDesc = dm.m.GetDescriptor()
dm.mod = replaced
return nil
})
return nil
}
}
// WithManifestToDocker converts the manifest to Docker schema2 media types.
func WithManifestToDocker() Opts {
return func(dc *dagConfig, dm *dagManifest) error {
dc.stepsManifest = append(dc.stepsManifest, func(c context.Context, rc *regclient.RegClient, rSrc, rTgt ref.Ref, dm *dagManifest) error {
if dm.mod == deleted {
return nil
}
changed := false
om := dm.m.GetOrig()
if dm.m.IsList() {
if dm.m.GetDescriptor().MediaType != mediatype.Docker2ManifestList {
ociM, err := manifest.OCIIndexFromAny(om)
if err != nil {
return err
}
dml := schema2.ManifestList{}
err = manifest.OCIIndexToAny(ociM, &dml)
if err != nil {
return err
}
changed = true
om = dml
}
} else {
ociM, err := manifest.OCIManifestFromAny(om)
if err != nil {
return err
}
if dm.m.GetDescriptor().MediaType != mediatype.Docker2Manifest {
changed = true
}
if ociM.ArtifactType != "" {
return fmt.Errorf("unable to convert artifactType to docker manifest, ref %s%.0w", rSrc.CommonName(), errs.ErrUnsupportedMediaType)
}
if ociM.Config.MediaType == mediatype.OCI1ImageConfig {
ociM.Config.MediaType = mediatype.Docker2ImageConfig
changed = true
}
for i, l := range ociM.Layers {
if l.MediaType == mediatype.OCI1LayerGzip {
ociM.Layers[i].MediaType = mediatype.Docker2LayerGzip
changed = true
} else if l.MediaType == mediatype.OCI1ForeignLayerGzip {
ociM.Layers[i].MediaType = mediatype.Docker2ForeignLayer
changed = true
}
}
if changed {
dm := schema2.Manifest{}
err = manifest.OCIManifestToAny(ociM, &dm)
if err != nil {
return err
}
om = dm
}
}
if !changed {
return nil
}
newM, err := manifest.New(manifest.WithOrig(om))
if err != nil {
return err
}
dm.m = newM
dm.newDesc = dm.m.GetDescriptor()
dm.mod = replaced
return nil
})
return nil
}
}
// WithManifestToOCI converts the manifest to OCI media types.
func WithManifestToOCI() Opts {
return func(dc *dagConfig, dm *dagManifest) error {
dc.stepsManifest = append(dc.stepsManifest, func(c context.Context, rc *regclient.RegClient, rSrc, rTgt ref.Ref, dm *dagManifest) error {
if dm.mod == deleted {
return nil
}
changed := false
om := dm.m.GetOrig()
if dm.m.IsList() {
ociM, err := manifest.OCIIndexFromAny(om)
if err != nil {
return err
}
if dm.m.GetDescriptor().MediaType != mediatype.OCI1ManifestList {
changed = true
om = ociM
}
} else {
ociM, err := manifest.OCIManifestFromAny(om)
if err != nil {
return err
}
if dm.m.GetDescriptor().MediaType != mediatype.OCI1Manifest {
changed = true
}
if ociM.Config.MediaType == mediatype.Docker2ImageConfig {
ociM.Config.MediaType = mediatype.OCI1ImageConfig
changed = true
}
for i, l := range ociM.Layers {
if l.MediaType == mediatype.Docker2LayerGzip {
ociM.Layers[i].MediaType = mediatype.OCI1LayerGzip
changed = true
} else if l.MediaType == mediatype.Docker2ForeignLayer {
ociM.Layers[i].MediaType = mediatype.OCI1ForeignLayerGzip
changed = true
}
}
if changed {
om = ociM
}
}
if !changed {
return nil
}
newM, err := manifest.New(manifest.WithOrig(om))
if err != nil {
return err
}
dm.m = newM
dm.newDesc = dm.m.GetDescriptor()
dm.mod = replaced
return nil
})
return nil
}
}
const (
dockerReferenceType = "vnd.docker.reference.type"
dockerReferenceDigest = "vnd.docker.reference.digest"
)
// WithManifestToOCIReferrers converts other referrer types to OCI subject/referrers.
func WithManifestToOCIReferrers() Opts {
return func(dc *dagConfig, dm *dagManifest) error {
dc.stepsManifest = append(dc.stepsManifest, func(c context.Context, rc *regclient.RegClient, rSrc, rTgt ref.Ref, dm *dagManifest) error {
if dm.mod == deleted {
return nil
}
mi, ok := dm.m.(manifest.Indexer)
if ok {
changed := false
// create a map of digests for each child DM we may need to reference later
dmLU := map[string]*dagManifest{
dm.origDesc.Digest.String(): dm,
}
for _, childDM := range dm.manifests {
dmLU[childDM.origDesc.Digest.String()] = childDM
}
ml, err := mi.GetManifestList()
if err != nil {
return fmt.Errorf("failed to get manifest list: %w", err)
}
mlI := 0
for _, childDM := range dm.manifests {
if childDM.mod == added {
continue
}
if childDM.mod == deleted {
mlI++
continue
}
// get descriptor, skip descriptors that are not docker referrers
if mlI >= len(ml) {
return fmt.Errorf("could not find descriptor, index=%d, digest=%s", mlI, dm.origDesc.Digest.String())
}
desc := ml[mlI]
mlI++
if len(desc.Annotations) == 0 || desc.Annotations[dockerReferenceType] == "" || desc.Annotations[dockerReferenceDigest] == "" {
continue
}
// find the subjectDM
subjectDM, ok := dmLU[desc.Annotations[dockerReferenceDigest]]
if !ok || subjectDM == nil {
return fmt.Errorf("could not find digest, convert referrers before other mod actions, digest=%s", desc.Annotations[dockerReferenceDigest])
}
// validate the manifest being converted
_, ok = childDM.m.(manifest.Subjecter)
if !ok {
return fmt.Errorf("docker reference type does not support subject, mt=%s", childDM.m.GetDescriptor().MediaType)
}
am, ok := childDM.m.(manifest.Annotator)
if !ok {
return fmt.Errorf("docker reference type does not support annotations, mt=%s", childDM.m.GetDescriptor().MediaType)
}
err := am.SetAnnotation(dockerReferenceType, desc.Annotations[dockerReferenceType])
if err != nil {
return fmt.Errorf("failed to set annotations: %w", err)
}
// copy childDM to add a referrer entry to the targetDM
referrerDM := *childDM
referrerDM.mod = added
subjectDM.referrers = append(subjectDM.referrers, &referrerDM)
// mark this childDM for deletion
changed = true
childDM.mod = deleted
}
if changed {
if dm.mod == unchanged {
dm.mod = replaced
}
}
}
return nil
})
return nil
}
}
// WithExternalURLsRm strips external URLs from descriptors and adjusts media type to match.
func WithExternalURLsRm() Opts {
return func(dc *dagConfig, dm *dagManifest) error {
dc.stepsManifest = append(dc.stepsManifest, func(ctx context.Context, rc *regclient.RegClient, rSrc, rTgt ref.Ref, dm *dagManifest) error {
if dm.mod == deleted {
return nil
}
changed := false
if dm.m.IsList() {
return nil
}
om := dm.m.GetOrig()
ociOM, err := manifest.OCIManifestFromAny(om)
if err != nil {
return err
}
// strip layers from image
for i := range ociOM.Layers {
if len(ociOM.Layers[i].URLs) > 0 {
ociOM.Layers[i].URLs = []string{}
mt := ociOM.Layers[i].MediaType
switch mt {
case mediatype.Docker2ForeignLayer:
mt = mediatype.Docker2LayerGzip
case mediatype.OCI1ForeignLayer:
mt = mediatype.OCI1Layer
case mediatype.OCI1ForeignLayerGzip:
mt = mediatype.OCI1LayerGzip
case mediatype.OCI1ForeignLayerZstd:
mt = mediatype.OCI1LayerZstd
}
ociOM.Layers[i].MediaType = mt
changed = true
}
}
// also strip from dag so other steps don't skip the external layer
for i, dl := range dm.layers {
if dl.mod == deleted {
continue
}
if dl.newDesc.Digest == "" && len(dl.desc.URLs) > 0 {
dl.newDesc = dl.desc
}
if len(dl.newDesc.URLs) > 0 {
dl.newDesc.URLs = []string{}
dm.layers[i] = dl
}
}
if !changed {
return nil
}
err = manifest.OCIManifestToAny(ociOM, &om)
if err != nil {
return err
}
err = dm.m.SetOrig(om)
if err != nil {
return err
}
dm.newDesc = dm.m.GetDescriptor()
dm.mod = replaced
return nil
})
return nil
}
}
// WithRebase attempts to rebase the image using OCI annotations identifying the base image.
func WithRebase() Opts {
return func(dc *dagConfig, dm *dagManifest) error {
ma, ok := dm.m.(manifest.Annotator)
if !ok {
return fmt.Errorf("rebased failed, manifest does not support annotations")
}
annot, err := ma.GetAnnotations()
if err != nil {
return fmt.Errorf("failed getting annotations: %w", err)
}
baseName, ok := annot[types.AnnotationBaseImageName]
if !ok {
return fmt.Errorf("annotation for base image is missing (%s or %s)%.0w", types.AnnotationBaseImageName, types.AnnotationBaseImageDigest, errs.ErrMissingAnnotation)
}
baseDigest, ok := annot[types.AnnotationBaseImageDigest]
if !ok {
return fmt.Errorf("annotation for base image is missing (%s or %s)%.0w", types.AnnotationBaseImageName, types.AnnotationBaseImageDigest, errs.ErrMissingAnnotation)
}
rNew, err := ref.New(baseName)
if err != nil {
return fmt.Errorf("failed to parse base name: %w", err)
}
dig, err := digest.Parse(baseDigest)
if err != nil {
return fmt.Errorf("failed to parse base digest: %w", err)
}
rOld := rNew
rOld.Digest = dig.String()
return rebaseAddStep(dc, rOld, rNew)
}
}
// WithRebaseRefs swaps the base image layers from the old to the new reference.
func WithRebaseRefs(rOld, rNew ref.Ref) Opts {
// cache old and new manifests, variable is nil until first pulled
return func(dc *dagConfig, dm *dagManifest) error {
return rebaseAddStep(dc, rOld, rNew)
}
}
func rebaseAddStep(dc *dagConfig, rBaseOld, rBaseNew ref.Ref) error {
var mbOldCache, mbNewCache manifest.Manifest
dc.stepsManifest = append(dc.stepsManifest, func(ctx context.Context, rc *regclient.RegClient, rSrc, rTgt ref.Ref, dm *dagManifest) error {
// skip if manifest list or deleted
if dm.m.IsList() || dm.mod == deleted || dm.config == nil {
return nil
}
// get and cache base manifests
var err error
if mbOldCache == nil {
mbOldCache, err = rc.ManifestGet(ctx, rBaseOld)
if err != nil {
return err
}
}
if mbNewCache == nil {
mbNewCache, err = rc.ManifestGet(ctx, rBaseNew)
if err != nil {
return err
}
}
// if old and new are the same, skip rebase
if mbOldCache.GetDescriptor().Equal(mbNewCache.GetDescriptor()) {
return nil
}
// get the platform from the config, pull the relevant manifest
oc := dm.config.oc.GetConfig()
p := platform.Platform{
OS: oc.OS,
Architecture: oc.Architecture,
Variant: oc.Variant,
OSVersion: oc.OSVersion,
OSFeatures: oc.OSFeatures,
Features: oc.OSFeatures,
}
mbOld := mbOldCache
if mbOld.IsList() {
d, err := manifest.GetPlatformDesc(mbOld, &p)
if err != nil {
return err
}
rp := rBaseOld
rp.Digest = d.Digest.String()
mbOld, err = rc.ManifestGet(ctx, rp)
if err != nil {
return err
}
}
mbNew := mbNewCache
if mbNew.IsList() {
d, err := manifest.GetPlatformDesc(mbNew, &p)
if err != nil {
return err
}
rp := rBaseNew
rp.Digest = d.Digest.String()
mbNew, err = rc.ManifestGet(ctx, rp)
if err != nil {
return err
}
}
// load layers and config from image and old/new base images
imageOld, ok := mbOld.(manifest.Imager)
if !ok {
return fmt.Errorf("base original image is not an image")
}
layersOld, err := imageOld.GetLayers()
if err != nil {
return err
}
cdOld, err := imageOld.GetConfig()
if err != nil {
return err
}
confOld, err := rc.BlobGetOCIConfig(ctx, rBaseOld, cdOld)
if err != nil {
return err
}
confOCIOld := confOld.GetConfig()
imageNew, ok := mbNew.(manifest.Imager)
if !ok {
return fmt.Errorf("base new image is not an image")
}
layersNew, err := imageNew.GetLayers()
if err != nil {
return err
}
cdNew, err := imageNew.GetConfig()
if err != nil {
return err
}
confNew, err := rc.BlobGetOCIConfig(ctx, rBaseNew, cdNew)
if err != nil {
return err
}
confOCINew := confNew.GetConfig()
mi, ok := dm.m.(manifest.Imager)
if !ok {
return fmt.Errorf("manifest is not an image")
}
layers, err := mi.GetLayers()
if err != nil {
return err
}
conf := dm.config.oc
confOCI := conf.GetConfig()
// validate current base
if len(layersOld) > len(layers) {
return fmt.Errorf("base image has more layers than modified image%.0w", errs.ErrMismatch)
}
for i := range layersOld {
if !layers[i].Same(layersOld[i]) {
return fmt.Errorf("old base image does not match image layers, layer %d, base %v, image %v%.0w", i, layersOld[i], layers[i], errs.ErrMismatch)
}
}
if len(confOCIOld.History) > len(confOCI.History) {
return fmt.Errorf("base image has more history entries than modified image%.0w", errs.ErrMismatch)
}
historyLayers := 0
for i := range confOCIOld.History {
if confOCI.History[i].Author != confOCIOld.History[i].Author ||
confOCI.History[i].Comment != confOCIOld.History[i].Comment ||
!confOCI.History[i].Created.Equal(*confOCIOld.History[i].Created) ||
confOCI.History[i].CreatedBy != confOCIOld.History[i].CreatedBy ||
confOCI.History[i].EmptyLayer != confOCIOld.History[i].EmptyLayer {
return fmt.Errorf("old base image does not match image history, entry %d, base %v, image %v%.0w", i, confOCIOld.History[i], confOCI.History[i], errs.ErrMismatch)
}
if !confOCIOld.History[i].EmptyLayer {
historyLayers++
}
}
if len(layersOld) != historyLayers || len(layersOld) != len(confOCIOld.RootFS.DiffIDs) {
return fmt.Errorf("old base image layer count doesn't match history%.0w", errs.ErrMismatch)
}
if len(confOCIOld.RootFS.DiffIDs) > len(confOCI.RootFS.DiffIDs) {
return fmt.Errorf("base image has more rootfs entries than modified image%.0w", errs.ErrMismatch)
}
for i := range confOCIOld.RootFS.DiffIDs {
if confOCI.RootFS.DiffIDs[i] != confOCIOld.RootFS.DiffIDs[i] {
return fmt.Errorf("old base image does not match image rootfs, entry %d, base %s, image %s%.0w", i, confOCIOld.RootFS.DiffIDs[i].String(), confOCI.RootFS.DiffIDs[i].String(), errs.ErrMismatch)
}
}
// validate new base
historyLayers = 0
for i := range confOCINew.History {
if !confOCINew.History[i].EmptyLayer {
historyLayers++
}
}
if len(layersNew) != historyLayers || len(confOCINew.RootFS.DiffIDs) != historyLayers {
return fmt.Errorf("new base image config history doesn't match layer count")
}
// delete the old layers and config entries from vars and dag
pruneNum := len(layersOld)
i := 0
for pruneNum > 0 {
if dm.layers[i].mod == added {
i++
continue
}
if i == 0 {
dm.layers = dm.layers[1:]
} else if i >= len(dm.layers)-1 {
dm.layers = dm.layers[:i]
} else {
dm.layers = append(dm.layers[:i], dm.layers[i+1:]...)
}
pruneNum--
}
layers = layers[len(layersOld):]
confOCI.History = confOCI.History[len(confOCIOld.History):]
confOCI.RootFS.DiffIDs = confOCI.RootFS.DiffIDs[len(confOCIOld.RootFS.DiffIDs):]
// insert new layers and config entries in vars and dag, mark as modified
dagAdd := []*dagLayer{}
for i, l := range layersNew {
dagAdd = append(dagAdd, &dagLayer{
mod: unchanged,
ucDigest: confOCINew.RootFS.DiffIDs[i],
desc: l,
rSrc: rBaseNew,
})
}
dm.layers = append(dagAdd, dm.layers...)
layers = append(layersNew, layers...)
err = mi.SetLayers(layers)
if err != nil {
return err
}
confOCI.History = append(confOCINew.History, confOCI.History...)
confOCI.RootFS.DiffIDs = append(confOCINew.RootFS.DiffIDs, confOCI.RootFS.DiffIDs...)
dm.config.oc.SetConfig(confOCI)
dm.config.newDesc = dm.config.oc.GetDescriptor()
// set modified flags on config and manifest
dm.config.modified = true
dm.mod = replaced
dc.forceLayerWalk = true
return nil
})
return nil
}