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  }