github.com/Racer159/jackal@v0.32.7-0.20240401174413-0bd2339e4f2e/src/pkg/transform/image.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: 2021-Present The Jackal Authors 3 4 // Package transform provides helper functions to transform URLs to airgap equivalents 5 package transform 6 7 import ( 8 "fmt" 9 "strings" 10 11 "github.com/defenseunicorns/pkg/helpers" 12 "github.com/distribution/reference" 13 ) 14 15 // Image represents a config for an OCI image. 16 type Image struct { 17 Host string 18 Name string 19 Path string 20 Tag string 21 Digest string 22 Reference string 23 TagOrDigest string 24 } 25 26 // ImageTransformHost replaces the base url for an image and adds a crc32 of the original url to the end of the src (note image refs are not full URLs). 27 func ImageTransformHost(targetHost, srcReference string) (string, error) { 28 image, err := ParseImageRef(srcReference) 29 if err != nil { 30 return "", err 31 } 32 33 // check if image has already been transformed 34 if strings.HasPrefix(targetHost, image.Host) { 35 return srcReference, nil 36 } 37 38 // Generate a crc32 hash of the image host + name 39 checksum := helpers.GetCRCHash(image.Name) 40 41 // If this image is specified by digest then don't add a checksum it as it will already be a specific SHA 42 if image.Digest != "" { 43 return fmt.Sprintf("%s/%s@%s", targetHost, image.Path, image.Digest), nil 44 } 45 46 return fmt.Sprintf("%s/%s:%s-jackal-%d", targetHost, image.Path, image.Tag, checksum), nil 47 } 48 49 // ImageTransformHostWithoutChecksum replaces the base url for an image but avoids adding a checksum of the original url (note image refs are not full URLs). 50 func ImageTransformHostWithoutChecksum(targetHost, srcReference string) (string, error) { 51 image, err := ParseImageRef(srcReference) 52 if err != nil { 53 return "", err 54 } 55 56 // check if image has already been transformed 57 if strings.HasPrefix(targetHost, image.Host) { 58 return srcReference, nil 59 } 60 61 return fmt.Sprintf("%s/%s%s", targetHost, image.Path, image.TagOrDigest), nil 62 } 63 64 // ParseImageRef parses a source reference into an Image struct 65 func ParseImageRef(srcReference string) (out Image, err error) { 66 ref, err := reference.ParseAnyReference(srcReference) 67 if err != nil { 68 return out, err 69 } 70 71 // Parse the reference into its components 72 if named, ok := ref.(reference.Named); ok { 73 out.Name = named.Name() 74 out.Path = reference.Path(named) 75 out.Host = reference.Domain(named) 76 out.Reference = ref.String() 77 } else { 78 return out, fmt.Errorf("unable to parse image name from %s", srcReference) 79 } 80 81 // Parse the tag and add it to digestOrReference 82 if tagged, ok := ref.(reference.Tagged); ok { 83 out.Tag = tagged.Tag() 84 out.TagOrDigest = fmt.Sprintf(":%s", tagged.Tag()) 85 } 86 87 // Parse the digest and override digestOrReference 88 if digested, ok := ref.(reference.Digested); ok { 89 out.Digest = digested.Digest().String() 90 out.TagOrDigest = fmt.Sprintf("@%s", digested.Digest().String()) 91 } 92 93 // If no tag or digest was provided use the default tag (latest) 94 if out.TagOrDigest == "" { 95 out.Tag = "latest" 96 out.TagOrDigest = ":latest" 97 out.Reference += ":latest" 98 } 99 100 return out, nil 101 }