-
Notifications
You must be signed in to change notification settings - Fork 99
/
unpack.go
110 lines (94 loc) · 3.45 KB
/
unpack.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
/*
* umoci: Umoci Modifies Open Containers' Images
* Copyright (C) 2016-2024 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 (
"context"
"fmt"
"os"
"strings"
"github.com/apex/log"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/umoci/oci/casext"
"github.com/opencontainers/umoci/oci/layer"
"github.com/opencontainers/umoci/pkg/fseval"
)
// Unpack unpacks an image to the specified bundle path.
func Unpack(engineExt casext.Engine, fromName string, bundlePath string, unpackOptions layer.UnpackOptions) error {
var meta Meta
meta.Version = MetaVersion
meta.MapOptions = unpackOptions.MapOptions
meta.WhiteoutMode = unpackOptions.WhiteoutMode
fromDescriptorPaths, err := engineExt.ResolveReference(context.Background(), fromName)
if err != nil {
return fmt.Errorf("get descriptor: %w", err)
}
if len(fromDescriptorPaths) == 0 {
return fmt.Errorf("tag is not found: %s", fromName)
}
if len(fromDescriptorPaths) != 1 {
// TODO: Handle this more nicely.
return fmt.Errorf("tag is ambiguous: %s", fromName)
}
meta.From = fromDescriptorPaths[0]
manifestBlob, err := engineExt.FromDescriptor(context.Background(), meta.From.Descriptor())
if err != nil {
return fmt.Errorf("get manifest: %w", err)
}
defer manifestBlob.Close()
if manifestBlob.Descriptor.MediaType != ispec.MediaTypeImageManifest {
return fmt.Errorf("invalid --image tag: descriptor does not point to ispec.MediaTypeImageManifest: not implemented: %s", manifestBlob.Descriptor.MediaType)
}
mtreeName := strings.Replace(meta.From.Descriptor().Digest.String(), ":", "_", 1)
log.WithFields(log.Fields{
"bundle": bundlePath,
"ref": fromName,
"rootfs": layer.RootfsName,
}).Debugf("umoci: unpacking OCI image")
// Get the manifest.
manifest, ok := manifestBlob.Data.(ispec.Manifest)
if !ok {
// Should _never_ be reached.
return fmt.Errorf("[internal error] unknown manifest blob type: %s", manifestBlob.Descriptor.MediaType)
}
// Unpack the runtime bundle.
if err := os.MkdirAll(bundlePath, 0755); err != nil {
return fmt.Errorf("create bundle path: %w", err)
}
// XXX: We should probably defer os.RemoveAll(bundlePath).
log.Info("unpacking bundle ...")
if err := layer.UnpackManifest(context.Background(), engineExt, bundlePath, manifest, &unpackOptions); err != nil {
return fmt.Errorf("create runtime bundle: %w", err)
}
log.Info("... done")
fsEval := fseval.Default
if meta.MapOptions.Rootless {
fsEval = fseval.Rootless
}
if err := GenerateBundleManifest(mtreeName, bundlePath, fsEval); err != nil {
return fmt.Errorf("write mtree: %w", err)
}
log.WithFields(log.Fields{
"version": meta.Version,
"from": meta.From,
"map_options": meta.MapOptions,
}).Debugf("umoci: saving Meta metadata")
if err := WriteBundleMeta(bundlePath, meta); err != nil {
return fmt.Errorf("write umoci.json metadata: %w", err)
}
log.Infof("unpacked image bundle: %s", bundlePath)
return nil
}