github.com/apptainer/singularity@v3.1.1+incompatible/internal/pkg/client/oci/oci.go (about) 1 // Copyright (c) 2018, Sylabs Inc. All rights reserved. 2 // This software is licensed under a 3-clause BSD license. Please consult the 3 // LICENSE.md file distributed with the sources of this project regarding your 4 // rights to use or distribute this software. 5 6 // Package oci provides transparent caching of oci-like refs 7 package oci 8 9 import ( 10 "context" 11 "crypto/sha256" 12 "fmt" 13 "io" 14 "strings" 15 16 "github.com/containers/image/copy" 17 "github.com/containers/image/oci/layout" 18 "github.com/containers/image/signature" 19 "github.com/containers/image/transports" 20 "github.com/containers/image/types" 21 "github.com/sylabs/singularity/internal/pkg/client/cache" 22 "github.com/sylabs/singularity/internal/pkg/sylog" 23 ) 24 25 // ImageReference wraps containers/image ImageReference type 26 type ImageReference struct { 27 source types.ImageReference 28 types.ImageReference 29 } 30 31 // ConvertReference converts a source reference into a cache.ImageReference to cache its blobs 32 func ConvertReference(src types.ImageReference, sys *types.SystemContext) (types.ImageReference, error) { 33 // Our cache dir is an OCI directory. We are using this as a 'blob pool' 34 // storing all incoming containers under unique tags, which are a hash of 35 // their source URI. 36 cacheTag, err := calculateRefHash(src, sys) 37 if err != nil { 38 return nil, err 39 } 40 41 c, err := layout.ParseReference(cache.OciBlob() + ":" + cacheTag) 42 if err != nil { 43 return nil, err 44 } 45 46 return &ImageReference{ 47 source: src, 48 ImageReference: c, 49 }, nil 50 51 } 52 53 // NewImageSource wraps the cache's oci-layout ref to first download the real source image to the cache 54 func (t *ImageReference) NewImageSource(ctx context.Context, sys *types.SystemContext) (types.ImageSource, error) { 55 return t.newImageSource(ctx, sys, sylog.Writer()) 56 } 57 58 func (t *ImageReference) newImageSource(ctx context.Context, sys *types.SystemContext, w io.Writer) (types.ImageSource, error) { 59 policy := &signature.Policy{Default: []signature.PolicyRequirement{signature.NewPRInsecureAcceptAnything()}} 60 policyCtx, err := signature.NewPolicyContext(policy) 61 62 // First we are fetching into the cache 63 err = copy.Image(context.Background(), policyCtx, t.ImageReference, t.source, ©.Options{ 64 ReportWriter: w, 65 SourceCtx: sys, 66 }) 67 if err != nil { 68 return nil, err 69 } 70 return t.ImageReference.NewImageSource(ctx, sys) 71 } 72 73 // ParseImageName parses a uri (e.g. docker://ubuntu) into it's transport:reference 74 // combination and then returns the proper reference 75 func ParseImageName(uri string, sys *types.SystemContext) (types.ImageReference, error) { 76 ref, err := parseURI(uri) 77 if err != nil { 78 return nil, fmt.Errorf("Unable to parse image name %v: %v", uri, err) 79 } 80 81 return ConvertReference(ref, sys) 82 } 83 84 func parseURI(uri string) (types.ImageReference, error) { 85 sylog.Debugf("Parsing %s into reference", uri) 86 87 split := strings.SplitN(uri, ":", 2) 88 if len(split) != 2 { 89 return nil, fmt.Errorf("%s not in transport:reference pair", uri) 90 } 91 92 transport := transports.Get(split[0]) 93 if transport == nil { 94 return nil, fmt.Errorf("%s not a registered transport", split[0]) 95 } 96 97 return transport.ParseReference(split[1]) 98 } 99 100 // TempImageExists returns whether or not the uri exists splatted out in the cache.OciTemp() directory 101 func TempImageExists(uri string) (bool, string, error) { 102 sum, err := ImageSHA(uri, nil) 103 if err != nil { 104 return false, "", err 105 } 106 107 split := strings.Split(uri, ":") 108 if len(split) < 2 { 109 return false, "", fmt.Errorf("poorly formatted URI %v", uri) 110 } 111 112 exists, err := cache.OciTempExists(sum, split[1]) 113 return exists, cache.OciTempImage(sum, split[1]), err 114 } 115 116 // ImageSHA calculates the SHA of a uri's manifest 117 func ImageSHA(uri string, sys *types.SystemContext) (string, error) { 118 ref, err := parseURI(uri) 119 if err != nil { 120 return "", fmt.Errorf("Unable to parse image name %v: %v", uri, err) 121 } 122 123 return calculateRefHash(ref, sys) 124 } 125 126 func calculateRefHash(ref types.ImageReference, sys *types.SystemContext) (string, error) { 127 source, err := ref.NewImageSource(context.TODO(), sys) 128 if err != nil { 129 return "", err 130 } 131 132 man, _, err := source.GetManifest(context.TODO(), nil) 133 if err != nil { 134 return "", err 135 } 136 137 hash := fmt.Sprintf("%x", sha256.Sum256(man)) 138 return hash, nil 139 }