mirror of
https://github.com/openSUSE/umoci.git
synced 2026-02-05 09:44:53 +01:00
147 lines
4.3 KiB
Go
147 lines
4.3 KiB
Go
/*
|
|
* umoci: Umoci Modifies Open Containers' Images
|
|
* Copyright (C) 2016-2019 SUSE LLC.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package umoci
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/apex/log"
|
|
"github.com/openSUSE/umoci/mutate"
|
|
"github.com/openSUSE/umoci/oci/casext"
|
|
"github.com/openSUSE/umoci/oci/layer"
|
|
"github.com/openSUSE/umoci/pkg/fseval"
|
|
"github.com/openSUSE/umoci/pkg/mtreefilter"
|
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"github.com/pkg/errors"
|
|
"github.com/vbatts/go-mtree"
|
|
"golang.org/x/net/context"
|
|
)
|
|
|
|
// Repack repacks a bundle into an image adding a new layer for the changed
|
|
// data in the bundle.
|
|
func Repack(engineExt casext.Engine, tagName string, bundlePath string, meta Meta, history *ispec.History, maskedPaths []string, refreshBundle bool, mutator *mutate.Mutator) error {
|
|
mtreeName := strings.Replace(meta.From.Descriptor().Digest.String(), ":", "_", 1)
|
|
mtreePath := filepath.Join(bundlePath, mtreeName+".mtree")
|
|
fullRootfsPath := filepath.Join(bundlePath, layer.RootfsName)
|
|
|
|
log.WithFields(log.Fields{
|
|
"bundle": bundlePath,
|
|
"rootfs": layer.RootfsName,
|
|
"mtree": mtreePath,
|
|
}).Debugf("umoci: repacking OCI image")
|
|
|
|
mfh, err := os.Open(mtreePath)
|
|
if err != nil {
|
|
return errors.Wrap(err, "open mtree")
|
|
}
|
|
defer mfh.Close()
|
|
|
|
spec, err := mtree.ParseSpec(mfh)
|
|
if err != nil {
|
|
return errors.Wrap(err, "parse mtree")
|
|
}
|
|
|
|
log.WithFields(log.Fields{
|
|
"keywords": MtreeKeywords,
|
|
}).Debugf("umoci: parsed mtree spec")
|
|
|
|
fsEval := fseval.DefaultFsEval
|
|
if meta.MapOptions.Rootless {
|
|
fsEval = fseval.RootlessFsEval
|
|
}
|
|
|
|
log.Info("computing filesystem diff ...")
|
|
diffs, err := mtree.Check(fullRootfsPath, spec, MtreeKeywords, fsEval)
|
|
if err != nil {
|
|
return errors.Wrap(err, "check mtree")
|
|
}
|
|
log.Info("... done")
|
|
|
|
log.WithFields(log.Fields{
|
|
"ndiff": len(diffs),
|
|
}).Debugf("umoci: checked mtree spec")
|
|
|
|
diffs = mtreefilter.FilterDeltas(diffs,
|
|
mtreefilter.MaskFilter(maskedPaths),
|
|
mtreefilter.SimplifyFilter(diffs))
|
|
|
|
if len(diffs) == 0 {
|
|
config, err := mutator.Config(context.Background())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
imageMeta, err := mutator.Meta(context.Background())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
annotations, err := mutator.Annotations(context.Background())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = mutator.Set(context.Background(), config, imageMeta, annotations, history)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
reader, err := layer.GenerateLayer(fullRootfsPath, diffs, &meta.MapOptions)
|
|
if err != nil {
|
|
return errors.Wrap(err, "generate diff layer")
|
|
}
|
|
defer reader.Close()
|
|
|
|
// TODO: We should add a flag to allow for a new layer to be made
|
|
// non-distributable.
|
|
if err := mutator.Add(context.Background(), reader, history); err != nil {
|
|
return errors.Wrap(err, "add diff layer")
|
|
}
|
|
}
|
|
|
|
newDescriptorPath, err := mutator.Commit(context.Background())
|
|
if err != nil {
|
|
return errors.Wrap(err, "commit mutated image")
|
|
}
|
|
|
|
log.Infof("new image manifest created: %s->%s", newDescriptorPath.Root().Digest, newDescriptorPath.Descriptor().Digest)
|
|
|
|
if err := engineExt.UpdateReference(context.Background(), tagName, newDescriptorPath.Root()); err != nil {
|
|
return errors.Wrap(err, "add new tag")
|
|
}
|
|
|
|
log.Infof("created new tag for image manifest: %s", tagName)
|
|
|
|
if refreshBundle {
|
|
newMtreeName := strings.Replace(newDescriptorPath.Descriptor().Digest.String(), ":", "_", 1)
|
|
if err := GenerateBundleManifest(newMtreeName, bundlePath, fsEval); err != nil {
|
|
return errors.Wrap(err, "write mtree metadata")
|
|
}
|
|
if err := os.Remove(mtreePath); err != nil {
|
|
return errors.Wrap(err, "remove old mtree metadata")
|
|
}
|
|
meta.From = newDescriptorPath
|
|
if err := WriteBundleMeta(bundlePath, meta); err != nil {
|
|
return errors.Wrap(err, "write umoci.json metadata")
|
|
}
|
|
}
|
|
return nil
|
|
}
|