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 }