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  }