github.com/dctrud/umoci@v0.4.3-0.20191016193643-05a1d37de015/oci/casext/blob.go (about)

     1  /*
     2   * umoci: Umoci Modifies Open Containers' Images
     3   * Copyright (C) 2016, 2017, 2018 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 casext
    19  
    20  import (
    21  	"encoding/json"
    22  	"fmt"
    23  	"io"
    24  
    25  	"github.com/openSUSE/umoci/oci/cas"
    26  	"github.com/opencontainers/go-digest"
    27  	ispec "github.com/opencontainers/image-spec/specs-go/v1"
    28  	"github.com/pkg/errors"
    29  	"golang.org/x/net/context"
    30  )
    31  
    32  // Blob represents a "parsed" blob in an OCI image's blob store. MediaType
    33  // offers a type-safe way of checking what the type of Data is.
    34  type Blob struct {
    35  	// MediaType is the OCI media type of Data.
    36  	MediaType string
    37  
    38  	// Digest is the digest of the parsed image. Note that this does not update
    39  	// if Data is changed (it is the digest that this blob was parsed *from*).
    40  	Digest digest.Digest
    41  
    42  	// Data is the "parsed" blob taken from the OCI image's blob store, and is
    43  	// typed according to the media type. The mapping from MIME => type is as
    44  	// follows.
    45  	//
    46  	// ispec.MediaTypeDescriptor => ispec.Descriptor
    47  	// ispec.MediaTypeImageManifest => ispec.Manifest
    48  	// ispec.MediaTypeImageManifestList => ispec.ManifestList
    49  	// ispec.MediaTypeImageLayer => io.ReadCloser
    50  	// ispec.MediaTypeImageLayerGzip => io.ReadCloser
    51  	// ispec.MediaTypeImageLayerNonDistributable => io.ReadCloser
    52  	// ispec.MediaTypeImageLayerNonDistributableGzip => io.ReadCloser
    53  	// ispec.MediaTypeImageConfig => ispec.Image
    54  	// unknown => io.ReadCloser
    55  	Data interface{}
    56  }
    57  
    58  func (b *Blob) load(ctx context.Context, engine cas.Engine) error {
    59  	reader, err := engine.GetBlob(ctx, b.Digest)
    60  	if err != nil {
    61  		return errors.Wrap(err, "get blob")
    62  	}
    63  
    64  	switch b.MediaType {
    65  	// ispec.MediaTypeDescriptor => ispec.Descriptor
    66  	case ispec.MediaTypeDescriptor:
    67  		defer reader.Close()
    68  		parsed := ispec.Descriptor{}
    69  		if err := json.NewDecoder(reader).Decode(&parsed); err != nil {
    70  			return errors.Wrap(err, "parse MediaTypeDescriptor")
    71  		}
    72  		b.Data = parsed
    73  
    74  	// ispec.MediaTypeImageManifest => ispec.Manifest
    75  	case ispec.MediaTypeImageManifest:
    76  		defer reader.Close()
    77  		parsed := ispec.Manifest{}
    78  		if err := json.NewDecoder(reader).Decode(&parsed); err != nil {
    79  			return errors.Wrap(err, "parse MediaTypeImageManifest")
    80  		}
    81  		b.Data = parsed
    82  
    83  	// ispec.MediaTypeImageIndex => ispec.Index
    84  	case ispec.MediaTypeImageIndex:
    85  		defer reader.Close()
    86  		parsed := ispec.Index{}
    87  		if err := json.NewDecoder(reader).Decode(&parsed); err != nil {
    88  			return errors.Wrap(err, "parse MediaTypeImageIndex")
    89  		}
    90  		b.Data = parsed
    91  
    92  	// ispec.MediaTypeImageConfig => ispec.Image
    93  	case ispec.MediaTypeImageConfig:
    94  		defer reader.Close()
    95  		parsed := ispec.Image{}
    96  		if err := json.NewDecoder(reader).Decode(&parsed); err != nil {
    97  			return errors.Wrap(err, "parse MediaTypeImageConfig")
    98  		}
    99  		b.Data = parsed
   100  
   101  	// ispec.MediaTypeImageLayer => io.ReadCloser
   102  	// ispec.MediaTypeImageLayerGzip => io.ReadCloser
   103  	// ispec.MediaTypeImageLayerNonDistributable => io.ReadCloser
   104  	// ispec.MediaTypeImageLayerNonDistributableGzip => io.ReadCloser
   105  	case ispec.MediaTypeImageLayer, ispec.MediaTypeImageLayerNonDistributable,
   106  		ispec.MediaTypeImageLayerGzip, ispec.MediaTypeImageLayerNonDistributableGzip:
   107  		// There isn't anything else we can practically do here.
   108  		b.Data = reader
   109  		return nil
   110  
   111  	// unknown => io.ReadCloser()
   112  	default:
   113  		b.Data = reader
   114  		return nil
   115  	}
   116  
   117  	if b.Data == nil {
   118  		return fmt.Errorf("[internal error] b.Data was nil after parsing")
   119  	}
   120  
   121  	return nil
   122  }
   123  
   124  // Close cleans up all of the resources for the opened blob.
   125  func (b *Blob) Close() {
   126  	switch b.MediaType {
   127  	case ispec.MediaTypeImageLayer, ispec.MediaTypeImageLayerNonDistributable,
   128  		ispec.MediaTypeImageLayerGzip, ispec.MediaTypeImageLayerNonDistributableGzip:
   129  		if b.Data != nil {
   130  			b.Data.(io.Closer).Close()
   131  		}
   132  	}
   133  }
   134  
   135  // FromDescriptor parses the blob referenced by the given descriptor.
   136  func (e Engine) FromDescriptor(ctx context.Context, descriptor ispec.Descriptor) (*Blob, error) {
   137  	blob := &Blob{
   138  		MediaType: descriptor.MediaType,
   139  		Digest:    descriptor.Digest,
   140  		Data:      nil,
   141  	}
   142  
   143  	if err := blob.load(ctx, e); err != nil {
   144  		return nil, errors.Wrap(err, "load")
   145  	}
   146  
   147  	return blob, nil
   148  }