summaryrefslogtreecommitdiff
path: root/registry-scanner/pkg/image/image.go
diff options
context:
space:
mode:
authorIshita Sequeira <46771830+ishitasequeira@users.noreply.github.com>2024-12-04 21:34:58 +0530
committerGitHub <noreply@github.com>2024-12-04 11:04:58 -0500
commit8076d2005ea625c73604073fca43df38eb675751 (patch)
tree1570ba5969882a26e021875da86bee6850a9cfc6 /registry-scanner/pkg/image/image.go
parentc3f0eff54daf871fa1c274462b17f5149c11d368 (diff)
Add image folder to registry scanner (#952)
Signed-off-by: Ishita Sequeira <ishiseq29@gmail.com>
Diffstat (limited to 'registry-scanner/pkg/image/image.go')
-rw-r--r--registry-scanner/pkg/image/image.go275
1 files changed, 275 insertions, 0 deletions
diff --git a/registry-scanner/pkg/image/image.go b/registry-scanner/pkg/image/image.go
new file mode 100644
index 0000000..01261be
--- /dev/null
+++ b/registry-scanner/pkg/image/image.go
@@ -0,0 +1,275 @@
+package image
+
+import (
+ "strings"
+ "time"
+
+ "github.com/distribution/distribution/v3/reference"
+
+ "github.com/argoproj-labs/argocd-image-updater/registry-scanner/pkg/log"
+ "github.com/argoproj-labs/argocd-image-updater/registry-scanner/pkg/tag"
+)
+
+type ContainerImage struct {
+ RegistryURL string
+ ImageName string
+ ImageTag *tag.ImageTag
+ ImageAlias string
+ HelmParamImageName string
+ HelmParamImageVersion string
+ KustomizeImage *ContainerImage
+ original string
+}
+
+type ContainerImageList []*ContainerImage
+
+// NewFromIdentifier parses an image identifier and returns a populated ContainerImage
+func NewFromIdentifier(identifier string) *ContainerImage {
+ imgRef := identifier
+ alias := ""
+ if strings.Contains(identifier, "=") {
+ n := strings.SplitN(identifier, "=", 2)
+ imgRef = n[1]
+ alias = n[0]
+ }
+ if parsed, err := reference.ParseNormalizedNamed(imgRef); err == nil {
+ img := ContainerImage{}
+ img.RegistryURL = reference.Domain(parsed)
+ // remove default registry for backwards-compatibility
+ if img.RegistryURL == "docker.io" && !strings.HasPrefix(imgRef, "docker.io") {
+ img.RegistryURL = ""
+ }
+ img.ImageAlias = alias
+ img.ImageName = reference.Path(parsed)
+ // if library/ was added to the image name, remove it
+ if !strings.HasPrefix(imgRef, "library/") {
+ img.ImageName = strings.TrimPrefix(img.ImageName, "library/")
+ }
+ if digested, ok := parsed.(reference.Digested); ok {
+ img.ImageTag = &tag.ImageTag{
+ TagDigest: string(digested.Digest()),
+ }
+ } else if tagged, ok := parsed.(reference.Tagged); ok {
+ img.ImageTag = &tag.ImageTag{
+ TagName: tagged.Tag(),
+ }
+ }
+ img.original = identifier
+ return &img
+ }
+
+ // if distribution couldn't parse it, fall back to the legacy parsing logic
+ img := ContainerImage{}
+ img.RegistryURL = getRegistryFromIdentifier(identifier)
+ img.ImageAlias, img.ImageName, img.ImageTag = getImageTagFromIdentifier(identifier)
+ img.original = identifier
+ return &img
+}
+
+// String returns the string representation of given ContainerImage
+func (img *ContainerImage) String() string {
+ str := ""
+ if img.ImageAlias != "" {
+ str += img.ImageAlias
+ str += "="
+ }
+ str += img.GetFullNameWithTag()
+ return str
+}
+
+func (img *ContainerImage) GetFullNameWithoutTag() string {
+ str := ""
+ if img.RegistryURL != "" {
+ str += img.RegistryURL + "/"
+ }
+ str += img.ImageName
+ return str
+}
+
+// GetFullNameWithTag returns the complete image slug, including the registry
+// and any tag digest or tag name set for the image.
+func (img *ContainerImage) GetFullNameWithTag() string {
+ str := ""
+ if img.RegistryURL != "" {
+ str += img.RegistryURL + "/"
+ }
+ str += img.ImageName
+ if img.ImageTag != nil {
+ if img.ImageTag.TagName != "" {
+ str += ":"
+ str += img.ImageTag.TagName
+ }
+ if img.ImageTag.TagDigest != "" {
+ str += "@"
+ str += img.ImageTag.TagDigest
+ }
+ }
+ return str
+}
+
+// GetTagWithDigest returns tag name along with any tag digest set for the image
+func (img *ContainerImage) GetTagWithDigest() string {
+ str := ""
+ if img.ImageTag != nil {
+ if img.ImageTag.TagName != "" {
+ str += img.ImageTag.TagName
+ }
+ if img.ImageTag.TagDigest != "" {
+ if str == "" {
+ str += "latest"
+ }
+ str += "@"
+ str += img.ImageTag.TagDigest
+ }
+ }
+ return str
+}
+
+func (img *ContainerImage) Original() string {
+ return img.original
+}
+
+// IsUpdatable checks whether the given image can be updated with newTag while
+// taking tagSpec into account. tagSpec must be given as a semver compatible
+// version spec, i.e. ^1.0 or ~2.1
+func (img *ContainerImage) IsUpdatable(newTag, tagSpec string) bool {
+ return false
+}
+
+// WithTag returns a copy of img with new tag information set
+func (img *ContainerImage) WithTag(newTag *tag.ImageTag) *ContainerImage {
+ nimg := &ContainerImage{}
+ nimg.RegistryURL = img.RegistryURL
+ nimg.ImageName = img.ImageName
+ nimg.ImageTag = newTag
+ nimg.ImageAlias = img.ImageAlias
+ nimg.HelmParamImageName = img.HelmParamImageName
+ nimg.HelmParamImageVersion = img.HelmParamImageVersion
+ return nimg
+}
+
+func (img *ContainerImage) DiffersFrom(other *ContainerImage, checkVersion bool) bool {
+ return img.RegistryURL != other.RegistryURL || img.ImageName != other.ImageName || (checkVersion && img.ImageTag.TagName != other.ImageTag.TagName)
+}
+
+// ContainsImage checks whether img is contained in a list of images
+func (list *ContainerImageList) ContainsImage(img *ContainerImage, checkVersion bool) *ContainerImage {
+ // if there is a KustomizeImage override, check it for a match first
+ if img.KustomizeImage != nil {
+ if kustomizeMatch := list.ContainsImage(img.KustomizeImage, checkVersion); kustomizeMatch != nil {
+ return kustomizeMatch
+ }
+ }
+ for _, image := range *list {
+ if img.ImageName == image.ImageName && image.RegistryURL == img.RegistryURL {
+ if !checkVersion || image.ImageTag.TagName == img.ImageTag.TagName {
+ return image
+ }
+ }
+ }
+ return nil
+}
+
+func (list *ContainerImageList) Originals() []string {
+ results := make([]string, len(*list))
+ for i, img := range *list {
+ results[i] = img.Original()
+ }
+ return results
+}
+
+// String Returns the name of all images as a string, separated using comma
+func (list *ContainerImageList) String() string {
+ imgNameList := make([]string, 0)
+ for _, image := range *list {
+ imgNameList = append(imgNameList, image.String())
+ }
+ return strings.Join(imgNameList, ",")
+}
+
+// Gets the registry URL from an image identifier
+func getRegistryFromIdentifier(identifier string) string {
+ var imageString string
+ comp := strings.Split(identifier, "=")
+ if len(comp) > 1 {
+ imageString = comp[1]
+ } else {
+ imageString = identifier
+ }
+ comp = strings.Split(imageString, "/")
+ if len(comp) > 1 && strings.Contains(comp[0], ".") {
+ return comp[0]
+ } else {
+ return ""
+ }
+}
+
+// Gets the image name and tag from an image identifier
+func getImageTagFromIdentifier(identifier string) (string, string, *tag.ImageTag) {
+ var imageString string
+ var sourceName string
+
+ // The original name is prepended to the image name, separated by =
+ comp := strings.SplitN(identifier, "=", 2)
+ if len(comp) == 2 {
+ sourceName = comp[0]
+ imageString = comp[1]
+ } else {
+ imageString = identifier
+ }
+
+ // Strip any repository identifier from the string
+ comp = strings.Split(imageString, "/")
+ if len(comp) > 1 && strings.Contains(comp[0], ".") {
+ imageString = strings.Join(comp[1:], "/")
+ }
+
+ // We can either have a tag name or a digest reference, or both
+ // jannfis/test-image:0.1
+ // gcr.io/jannfis/test-image:0.1
+ // gcr.io/jannfis/test-image@sha256:abcde
+ // gcr.io/jannfis/test-image:test-tag@sha256:abcde
+ if strings.Contains(imageString, "@") {
+ comp = strings.SplitN(imageString, "@", 2)
+ colonPos := strings.LastIndex(comp[0], ":")
+ slashPos := strings.LastIndex(comp[0], "/")
+ if colonPos > slashPos {
+ // first half (before @) contains image and tag name
+ return sourceName, comp[0][:colonPos], tag.NewImageTag(comp[0][colonPos+1:], time.Unix(0, 0), comp[1])
+ } else {
+ // first half contains image name without tag name
+ return sourceName, comp[0], tag.NewImageTag("", time.Unix(0, 0), comp[1])
+ }
+ } else {
+ comp = strings.SplitN(imageString, ":", 2)
+ if len(comp) != 2 {
+ return sourceName, imageString, nil
+ } else {
+ tagName, tagDigest := getImageDigestFromTag(comp[1])
+ return sourceName, comp[0], tag.NewImageTag(tagName, time.Unix(0, 0), tagDigest)
+ }
+ }
+}
+
+func getImageDigestFromTag(tagStr string) (string, string) {
+ a := strings.Split(tagStr, "@")
+ if len(a) != 2 {
+ return tagStr, ""
+ } else {
+ return a[0], a[1]
+ }
+}
+
+// LogContext returns a log context for the given image, with required fields
+// set to the image's information.
+func (img *ContainerImage) LogContext() *log.LogContext {
+ logCtx := log.WithContext()
+ logCtx.AddField("image_name", img.GetFullNameWithoutTag())
+ logCtx.AddField("image_alias", img.ImageAlias)
+ logCtx.AddField("registry_url", img.RegistryURL)
+ if img.ImageTag != nil {
+ logCtx.AddField("image_tag", img.ImageTag.TagName)
+ logCtx.AddField("image_digest", img.ImageTag.TagDigest)
+ }
+ return logCtx
+}