175 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			175 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Go
		
	
	
	
package imageparser
 | 
						|
 | 
						|
import (
 | 
						|
	_ "crypto/sha256" // initialize crypto/sha256 to enable sha256 algorithm
 | 
						|
	_ "crypto/sha512" // initialize crypto/sha512 to enable sha512 algorithm
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/distribution/distribution/v3/reference"
 | 
						|
)
 | 
						|
 | 
						|
// Components make up a whole image.
 | 
						|
// Basically we presume an image can be made of `[domain][:port][path]<name>[:tag][@sha256:digest]`, ie:
 | 
						|
// fictional.registry.example:10443/karmada/karmada-controller-manager:v1.0.0 or
 | 
						|
// fictional.registry.example:10443/karmada/karmada-controller-manager@sha256:50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c
 | 
						|
type Components struct {
 | 
						|
	// hostname is the prefix of referencing image, like "registry.k8s.io".
 | 
						|
	// It may optionally be followed by a port number, like "fictional.registry.example:10443".
 | 
						|
	hostname string
 | 
						|
	// repository is the short name of referencing image, like "karmada-controller-manager".
 | 
						|
	// It may be made up of slash-separated components, like "karmada/karmada-controller-manager".
 | 
						|
	repository string
 | 
						|
	// tag is the tag of referencing image. ie:
 | 
						|
	// - latest
 | 
						|
	// - v1.19.1
 | 
						|
	tag string
 | 
						|
	// digest is the digest of referencing image. ie:
 | 
						|
	// - sha256:50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c
 | 
						|
	digest string
 | 
						|
}
 | 
						|
 | 
						|
// Hostname returns the hostname, as well known as image registry.
 | 
						|
func (c *Components) Hostname() string {
 | 
						|
	return c.hostname
 | 
						|
}
 | 
						|
 | 
						|
// SetHostname sets the hostname.
 | 
						|
func (c *Components) SetHostname(hostname string) {
 | 
						|
	c.hostname = hostname
 | 
						|
}
 | 
						|
 | 
						|
// RemoveHostname removes the hostname.
 | 
						|
func (c *Components) RemoveHostname() {
 | 
						|
	c.hostname = ""
 | 
						|
}
 | 
						|
 | 
						|
// Repository returns the repository without hostname.
 | 
						|
func (c *Components) Repository() string {
 | 
						|
	return c.repository
 | 
						|
}
 | 
						|
 | 
						|
// SetRepository sets the repository.
 | 
						|
func (c *Components) SetRepository(repository string) {
 | 
						|
	c.repository = repository
 | 
						|
}
 | 
						|
 | 
						|
// RemoveRepository removes the repository.
 | 
						|
func (c *Components) RemoveRepository() {
 | 
						|
	c.repository = ""
 | 
						|
}
 | 
						|
 | 
						|
// FullRepository returns the whole repository, including hostname if not empty.
 | 
						|
func (c *Components) FullRepository() string {
 | 
						|
	if c.hostname == "" {
 | 
						|
		return c.repository
 | 
						|
	}
 | 
						|
 | 
						|
	return c.hostname + "/" + c.repository
 | 
						|
}
 | 
						|
 | 
						|
// String returns the full name of the image, including repository and tag(or digest).
 | 
						|
func (c Components) String() string {
 | 
						|
	if c.tag != "" {
 | 
						|
		return c.FullRepository() + ":" + c.tag
 | 
						|
	} else if c.digest != "" {
 | 
						|
		return c.FullRepository() + "@" + c.digest
 | 
						|
	}
 | 
						|
 | 
						|
	return c.FullRepository()
 | 
						|
}
 | 
						|
 | 
						|
// Tag returns the tag.
 | 
						|
func (c *Components) Tag() string {
 | 
						|
	return c.tag
 | 
						|
}
 | 
						|
 | 
						|
// SetTag sets the tag.
 | 
						|
func (c *Components) SetTag(tag string) {
 | 
						|
	c.tag = tag
 | 
						|
}
 | 
						|
 | 
						|
// RemoveTag removes the tag.
 | 
						|
func (c *Components) RemoveTag() {
 | 
						|
	c.tag = ""
 | 
						|
}
 | 
						|
 | 
						|
// Digest returns the digest.
 | 
						|
func (c *Components) Digest() string {
 | 
						|
	return c.digest
 | 
						|
}
 | 
						|
 | 
						|
// SetDigest sets the digest.
 | 
						|
func (c *Components) SetDigest(digest string) {
 | 
						|
	c.digest = digest
 | 
						|
}
 | 
						|
 | 
						|
// RemoveDigest removes the digest.
 | 
						|
func (c *Components) RemoveDigest() {
 | 
						|
	c.digest = ""
 | 
						|
}
 | 
						|
 | 
						|
// TagOrDigest returns image tag if not empty otherwise returns image digest.
 | 
						|
func (c *Components) TagOrDigest() string {
 | 
						|
	if c.tag != "" {
 | 
						|
		return c.tag
 | 
						|
	}
 | 
						|
 | 
						|
	return c.digest
 | 
						|
}
 | 
						|
 | 
						|
// SetTagOrDigest sets image tag or digest by matching the input.
 | 
						|
func (c *Components) SetTagOrDigest(input string) {
 | 
						|
	if anchoredTagRegexp.MatchString(input) {
 | 
						|
		c.SetTag(input)
 | 
						|
		c.RemoveDigest()
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	if anchoredDigestRegexp.MatchString(input) {
 | 
						|
		c.SetDigest(input)
 | 
						|
		c.RemoveTag()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// RemoveTagOrDigest removes tag or digest.
 | 
						|
// Since tag and digest don't co-exist, so remove tag if tag not empty, otherwise remove digest.
 | 
						|
func (c *Components) RemoveTagOrDigest() {
 | 
						|
	if c.tag != "" {
 | 
						|
		c.tag = ""
 | 
						|
	} else if c.digest != "" {
 | 
						|
		c.digest = ""
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Parse returns a Components of the given image.
 | 
						|
func Parse(image string) (*Components, error) {
 | 
						|
	ref, err := reference.Parse(image)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	comp := &Components{}
 | 
						|
	if named, ok := ref.(reference.Named); ok {
 | 
						|
		comp.hostname, comp.repository = SplitHostname(named.Name())
 | 
						|
	}
 | 
						|
 | 
						|
	if tagged, ok := ref.(reference.Tagged); ok {
 | 
						|
		comp.tag = tagged.Tag()
 | 
						|
	} else if digested, ok := ref.(reference.Digested); ok {
 | 
						|
		comp.digest = digested.Digest().String()
 | 
						|
	}
 | 
						|
 | 
						|
	return comp, nil
 | 
						|
}
 | 
						|
 | 
						|
// SplitHostname splits a repository name(ie: registry.k8s.io/kube-apiserver) to hostname(registry.k8s.io) and remotename(kube-apiserver) string.
 | 
						|
func SplitHostname(name string) (hostname, remoteName string) {
 | 
						|
	i := strings.IndexRune(name, '/')
 | 
						|
	if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
 | 
						|
		hostname, remoteName = "", name
 | 
						|
	} else {
 | 
						|
		hostname, remoteName = name[:i], name[i+1:]
 | 
						|
	}
 | 
						|
	return
 | 
						|
}
 |