github.com/opencontainers/umoci@v0.4.8-0.20240508124516-656e4836fb0d/unpack.go (about) 1 /* 2 * umoci: Umoci Modifies Open Containers' Images 3 * Copyright (C) 2016-2020 SUSE LLC 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package umoci 19 20 import ( 21 "context" 22 "fmt" 23 "os" 24 "strings" 25 26 "github.com/apex/log" 27 ispec "github.com/opencontainers/image-spec/specs-go/v1" 28 "github.com/opencontainers/umoci/oci/casext" 29 "github.com/opencontainers/umoci/oci/layer" 30 "github.com/opencontainers/umoci/pkg/fseval" 31 "github.com/pkg/errors" 32 ) 33 34 // Unpack unpacks an image to the specified bundle path. 35 func Unpack(engineExt casext.Engine, fromName string, bundlePath string, unpackOptions layer.UnpackOptions) error { 36 var meta Meta 37 meta.Version = MetaVersion 38 meta.MapOptions = unpackOptions.MapOptions 39 meta.WhiteoutMode = unpackOptions.WhiteoutMode 40 41 fromDescriptorPaths, err := engineExt.ResolveReference(context.Background(), fromName) 42 if err != nil { 43 return errors.Wrap(err, "get descriptor") 44 } 45 if len(fromDescriptorPaths) == 0 { 46 return errors.Errorf("tag is not found: %s", fromName) 47 } 48 if len(fromDescriptorPaths) != 1 { 49 // TODO: Handle this more nicely. 50 return errors.Errorf("tag is ambiguous: %s", fromName) 51 } 52 meta.From = fromDescriptorPaths[0] 53 54 manifestBlob, err := engineExt.FromDescriptor(context.Background(), meta.From.Descriptor()) 55 if err != nil { 56 return errors.Wrap(err, "get manifest") 57 } 58 defer manifestBlob.Close() 59 60 if manifestBlob.Descriptor.MediaType != ispec.MediaTypeImageManifest { 61 return errors.Wrap(fmt.Errorf("descriptor does not point to ispec.MediaTypeImageManifest: not implemented: %s", manifestBlob.Descriptor.MediaType), "invalid --image tag") 62 } 63 64 mtreeName := strings.Replace(meta.From.Descriptor().Digest.String(), ":", "_", 1) 65 log.WithFields(log.Fields{ 66 "bundle": bundlePath, 67 "ref": fromName, 68 "rootfs": layer.RootfsName, 69 }).Debugf("umoci: unpacking OCI image") 70 71 // Get the manifest. 72 manifest, ok := manifestBlob.Data.(ispec.Manifest) 73 if !ok { 74 // Should _never_ be reached. 75 return errors.Errorf("[internal error] unknown manifest blob type: %s", manifestBlob.Descriptor.MediaType) 76 } 77 78 // Unpack the runtime bundle. 79 if err := os.MkdirAll(bundlePath, 0755); err != nil { 80 return errors.Wrap(err, "create bundle path") 81 } 82 // XXX: We should probably defer os.RemoveAll(bundlePath). 83 84 log.Info("unpacking bundle ...") 85 if err := layer.UnpackManifest(context.Background(), engineExt, bundlePath, manifest, &unpackOptions); err != nil { 86 return errors.Wrap(err, "create runtime bundle") 87 } 88 log.Info("... done") 89 90 fsEval := fseval.Default 91 if meta.MapOptions.Rootless { 92 fsEval = fseval.Rootless 93 } 94 95 if err := GenerateBundleManifest(mtreeName, bundlePath, fsEval); err != nil { 96 return errors.Wrap(err, "write mtree") 97 } 98 99 log.WithFields(log.Fields{ 100 "version": meta.Version, 101 "from": meta.From, 102 "map_options": meta.MapOptions, 103 }).Debugf("umoci: saving Meta metadata") 104 105 if err := WriteBundleMeta(bundlePath, meta); err != nil { 106 return errors.Wrap(err, "write umoci.json metadata") 107 } 108 109 log.Infof("unpacked image bundle: %s", bundlePath) 110 return nil 111 }