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 }