github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/pkg/ociinstaller/imageref.go (about)

     1  package ociinstaller
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/turbot/steampipe/pkg/constants"
     8  )
     9  
    10  const (
    11  	DefaultImageTag            = "latest"
    12  	DefaultImageRepoActualURL  = "ghcr.io/turbot/steampipe"
    13  	DefaultImageRepoDisplayURL = "hub.steampipe.io"
    14  
    15  	DefaultImageOrg  = "turbot"
    16  	DefaultImageType = "plugins"
    17  )
    18  
    19  // SteampipeImageRef a struct encapsulating a ref to an OCI image
    20  type SteampipeImageRef struct {
    21  	requestedRef string
    22  }
    23  
    24  // NewSteampipeImageRef creates and returns a New SteampipeImageRef
    25  func NewSteampipeImageRef(ref string) *SteampipeImageRef {
    26  	ref = sanitizeRefStream(ref)
    27  	return &SteampipeImageRef{
    28  		requestedRef: ref,
    29  	}
    30  }
    31  
    32  // ActualImageRef returns the actual, physical full image ref
    33  // (ghcr.io/turbot/steampipe/plugins/turbot/aws:1.0.0)
    34  func (r *SteampipeImageRef) ActualImageRef() string {
    35  	ref := r.requestedRef
    36  
    37  	if !isDigestRef(ref) {
    38  		ref = strings.ReplaceAll(ref, "@", ":")
    39  	}
    40  
    41  	fullRef := getFullImageRef(ref)
    42  
    43  	if strings.HasPrefix(fullRef, DefaultImageRepoDisplayURL) {
    44  		fullRef = strings.ReplaceAll(fullRef, DefaultImageRepoDisplayURL, DefaultImageRepoActualURL)
    45  	}
    46  
    47  	return fullRef
    48  }
    49  
    50  // DisplayImageRef returns the "friendly" user-facing full image ref
    51  // (hub.steampipe.io/plugins/turbot/aws@1.0.0)
    52  func (r *SteampipeImageRef) DisplayImageRef() string {
    53  	fullRef := r.ActualImageRef()
    54  	if isDigestRef(fullRef) {
    55  		fullRef = strings.ReplaceAll(fullRef, ":", "-")
    56  	}
    57  	fullRef = strings.ReplaceAll(fullRef, ":", "@")
    58  
    59  	if strings.HasPrefix(fullRef, DefaultImageRepoActualURL) {
    60  		fullRef = strings.ReplaceAll(fullRef, DefaultImageRepoActualURL, DefaultImageRepoDisplayURL)
    61  	}
    62  
    63  	return fullRef
    64  }
    65  
    66  // DisplayImageRefConstraintOverride returns a "friendly" user-facing version of the image ref
    67  // but with the version replaced by provided constraint
    68  // (hub.steampipe.io/plugins/turbot/aws@^1.0)
    69  func (r *SteampipeImageRef) DisplayImageRefConstraintOverride(constraint string) string {
    70  	dir := r.DisplayImageRef()
    71  	s := strings.Split(dir, "@")
    72  	return fmt.Sprintf("%s@%s", s[0], constraint)
    73  }
    74  
    75  func isDigestRef(ref string) bool {
    76  	return strings.Contains(ref, "@sha256:")
    77  }
    78  
    79  // sanitizes the ref to exclude any 'v' prefix
    80  // in the stream (if any)
    81  func sanitizeRefStream(ref string) string {
    82  	if !isDigestRef(ref) {
    83  		splitByAt := strings.Split(ref, "@")
    84  		if len(splitByAt) == 1 {
    85  			// no stream mentioned
    86  			return ref
    87  		}
    88  		// trim out the 'v' prefix
    89  		splitByAt[1] = strings.TrimPrefix(splitByAt[1], "v")
    90  
    91  		ref = strings.Join(splitByAt, "@")
    92  	}
    93  	return ref
    94  }
    95  
    96  func (r *SteampipeImageRef) IsFromSteampipeHub() bool {
    97  	return strings.HasPrefix(r.DisplayImageRef(), constants.SteampipeHubOCIBase)
    98  }
    99  
   100  // GetOrgNameAndConstraint splits the full image reference into (org, name, constraint)
   101  // Constraint will be either a SemVer version (1.2.3) or a SemVer constraint (^0.4)
   102  func (r *SteampipeImageRef) GetOrgNameAndConstraint() (string, string, string) {
   103  	// plugin.Name looks like `hub.steampipe.io/plugins/turbot/aws@latest`
   104  	split := strings.Split(r.DisplayImageRef(), "/")
   105  	pluginNameAndSuffix := strings.Split(split[len(split)-1], "@")
   106  	if r.IsFromSteampipeHub() {
   107  		return split[len(split)-2], pluginNameAndSuffix[0], pluginNameAndSuffix[1]
   108  	}
   109  	return strings.Join(split[0:len(split)-1], "/"), pluginNameAndSuffix[0], pluginNameAndSuffix[1]
   110  }
   111  
   112  // GetFriendlyName returns the minimum friendly name so that the original name can be rebuilt using preset defaults:
   113  // hub.steampipe.io/plugins/turbot/aws@1.0.0    => aws@1.0.0
   114  // hub.steampipe.io/plugins/turbot/aws@latest   => aws
   115  // hub.steampipe.io/plugins/otherOrg/aws@latest => otherOrg/aws
   116  // hub.steampipe.io/plugins/otherOrg/aws@1.0.0  => otherOrg/aws@1.0.0
   117  // differentRegistry.com/otherOrg/aws@latest    => differentRegistry.com/otherOrg/aws@latest
   118  // differentRegistry.com/otherOrg/aws@1.0.0     => differentRegistry.com/otherOrg/aws@1.0.0
   119  func (r *SteampipeImageRef) GetFriendlyName() string {
   120  	return getCondensedImageRef(r.DisplayImageRef())
   121  }
   122  
   123  func getCondensedImageRef(imageRef string) string {
   124  	// if this is not from the default steampipe registry - DO NOT CONDENSE - return as is
   125  	// (we are not aware of any conventions in the registry)
   126  	if !strings.HasPrefix(imageRef, DefaultImageRepoDisplayURL) {
   127  		return imageRef
   128  	}
   129  
   130  	// So this is an image reference from the Steampipe HUB registry
   131  	// remove the registry URL
   132  	ref := strings.TrimPrefix(imageRef, DefaultImageRepoDisplayURL)
   133  	// remove the 'plugins' namespace where steampipe hub keeps the images
   134  	ref = strings.TrimPrefix(ref, "/plugins/")
   135  	// remove the default organization - "turbot"
   136  	ref = strings.TrimPrefix(ref, DefaultImageOrg)
   137  	// remove any leading '/'
   138  	ref = strings.TrimPrefix(ref, "/")
   139  	// remove the '@latest' tag (not others)
   140  	ref = strings.TrimSuffix(ref, fmt.Sprintf("@%s", DefaultImageTag))
   141  
   142  	return ref
   143  }
   144  
   145  // possible formats include
   146  //		ghcr.io/turbot/steampipe/plugins/turbot/aws:1.0.0
   147  //		ghcr.io/turbot/steampipe/plugins/turbot/aws@sha256:766389c9dd892132c7e7b9124f446b9599a80863d466cd1d333a167dedf2c2b1
   148  //		turbot/aws:1.0.0
   149  //		turbot/aws
   150  //      dockerhub.org/myimage
   151  //      dockerhub.org/myimage:mytag
   152  //		aws:1.0.0
   153  //		aws
   154  //		ghcr.io/turbot/steampipe/plugins/turbot/aws@1.0.0
   155  //		ghcr.io/turbot/steampipe/plugins/turbot/aws@sha256:766389c9dd892132c7e7b9124f446b9599a80863d466cd1d333a167dedf2c2b1
   156  //		turbot/aws@1.0.0
   157  //      dockerhub.org/myimage@mytag
   158  //		aws@1.0.0
   159  //		hub.steampipe.io/plugin/turbot/aws@1.0.0
   160  
   161  func getFullImageRef(imagePath string) string {
   162  
   163  	tag := DefaultImageTag
   164  
   165  	// Get the tag, default to `latest`
   166  	items := strings.Split(imagePath, ":")
   167  	if len(items) > 1 {
   168  		tag = items[1]
   169  	}
   170  
   171  	// Image path
   172  	parts := strings.Split(items[0], "/")
   173  	switch len(parts) {
   174  	case 1: //ex:  aws
   175  		return fmt.Sprintf("%s/%s/%s/%s:%s", DefaultImageRepoActualURL, DefaultImageType, DefaultImageOrg, parts[len(parts)-1], tag)
   176  	case 2: //ex:   turbot/aws OR dockerhub.com/my-image
   177  		org := parts[len(parts)-2]
   178  		if strings.Contains(org, ".") {
   179  			return fmt.Sprintf("%s:%s", items[0], tag)
   180  		}
   181  		return fmt.Sprintf("%s/%s/%s/%s:%s", DefaultImageRepoActualURL, DefaultImageType, org, parts[len(parts)-1], tag)
   182  	default: //ex: ghcr.io/turbot/steampipe/plugins/turbot/aws
   183  		return fmt.Sprintf("%s:%s", items[0], tag)
   184  	}
   185  }