forked from opencontainers/umoci
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrepack.go
129 lines (108 loc) · 3.63 KB
/
repack.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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
}