github.com/jfrog/jfrog-cli-core/v2@v2.51.0/artifactory/utils/container/image.go (about) 1 package container 2 3 import ( 4 "net/http" 5 "path" 6 "strings" 7 8 "github.com/jfrog/jfrog-client-go/artifactory" 9 "github.com/jfrog/jfrog-client-go/utils/errorutils" 10 "github.com/jfrog/jfrog-client-go/utils/log" 11 ) 12 13 type Image struct { 14 // Image name includes the registry domain, image base name and image tag e.g.: my-registry:port/docker-local/hello-world:latest. 15 name string 16 } 17 18 func NewImage(imageTag string) *Image { 19 return &Image{name: imageTag} 20 } 21 22 // Get image name 23 func (image *Image) Name() string { 24 return image.name 25 } 26 27 // Get image name from tag by removing the prefixed registry hostname. 28 // e.g.: https://my-registry/docker-local/hello-world:latest. -> docker-local/hello-world:latest 29 func (image *Image) GetImageLongNameWithTag() (string, error) { 30 if err := image.validateTag(); err != nil { 31 return "", err 32 } 33 indexOfLastSlash := strings.Index(image.name, "/") 34 indexOfLastColon := strings.LastIndex(image.name, ":") 35 if indexOfLastColon < 0 || indexOfLastColon < indexOfLastSlash { 36 log.Info("The image '" + image.name + "' does not include tag. Using the 'latest' tag.") 37 image.name += ":latest" 38 } 39 return image.name[indexOfLastSlash+1:], nil 40 } 41 42 // Get image base name by removing the prefixed registry hostname and the tag. 43 // e.g.: https://my-registry/docker-local/hello-world:latest. -> docker-local/hello-world 44 func (image *Image) GetImageLongName() (string, error) { 45 imageName, err := image.GetImageLongNameWithTag() 46 if err != nil { 47 return "", err 48 } 49 tagIndex := strings.Index(imageName, ":") 50 return imageName[:tagIndex], nil 51 } 52 53 func (image *Image) validateTag() error { 54 if !strings.Contains(image.name, "/") { 55 return errorutils.CheckErrorf("The image '%s' is missing '/' which indicates the image name/tag", image.name) 56 } 57 return nil 58 } 59 60 // Get image base name by removing the prefixed registry hostname and the tag. 61 // e.g.: https://my-registry/docker-local/hello-world:latest. -> hello-world 62 func (image *Image) GetImageShortName() (string, error) { 63 imageName, err := image.GetImageShortNameWithTag() 64 if err != nil { 65 return "", err 66 } 67 tagIndex := strings.LastIndex(imageName, ":") 68 if tagIndex != -1 { 69 return imageName[:tagIndex], nil 70 } 71 return imageName, nil 72 } 73 74 // Get image base name by removing the prefixed registry hostname. 75 // e.g.: https://my-registry/docker-local/hello-world:latest. -> hello-world:latest 76 func (image *Image) GetImageShortNameWithTag() (string, error) { 77 imageName, err := image.GetImageLongNameWithTag() 78 if err != nil { 79 return "", err 80 } 81 indexOfSlash := strings.LastIndex(imageName, "/") 82 if indexOfSlash != -1 { 83 return imageName[indexOfSlash+1:], nil 84 85 } 86 return imageName, nil 87 } 88 89 // Get image tag name of an image. 90 // e.g.: https://my-registry/docker-local/hello-world:latest. -> latest 91 func (image *Image) GetImageTag() (string, error) { 92 imageName, err := image.GetImageLongNameWithTag() 93 if err != nil { 94 return "", err 95 } 96 tagIndex := strings.Index(imageName, ":") 97 if tagIndex == -1 { 98 return "", errorutils.CheckErrorf("unexpected image name '%s'. Failed to get image tag.", image.Name()) 99 } 100 return imageName[tagIndex+1:], nil 101 } 102 103 func (image *Image) GetRegistry() (string, error) { 104 if err := image.validateTag(); err != nil { 105 return "", err 106 } 107 indexOfLastSlash := strings.Index(image.name, "/") 108 if indexOfLastSlash == -1 { 109 return "", errorutils.CheckErrorf("unexpected image name '%s'. Failed to get registry.", image.Name()) 110 } 111 return image.name[:indexOfLastSlash], nil 112 } 113 114 // Returns the physical Artifactory repository name of the pulled/pushed image, by reading a response header from Artifactory. 115 func (image *Image) GetRemoteRepo(serviceManager artifactory.ArtifactoryServicesManager) (string, error) { 116 containerRegistryUrl, err := image.GetRegistry() 117 if err != nil { 118 return "", err 119 } 120 longImageName, err := image.GetImageLongName() 121 if err != nil { 122 return "", err 123 } 124 imageTag, err := image.GetImageTag() 125 if err != nil { 126 return "", err 127 } 128 var isSecure bool 129 if rtUrl := serviceManager.GetConfig().GetServiceDetails().GetUrl(); strings.HasPrefix(rtUrl, "https") { 130 isSecure = true 131 } 132 // Build the request URL. 133 endpoint := buildRequestUrl(longImageName, imageTag, containerRegistryUrl, isSecure) 134 artHttpDetails := serviceManager.GetConfig().GetServiceDetails().CreateHttpClientDetails() 135 artHttpDetails.Headers["accept"] = "application/vnd.docker.distribution.manifest.v1+prettyjws, application/json, application/vnd.oci.image.manifest.v1+json, application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.index.v1+json" 136 resp, _, err := serviceManager.Client().SendHead(endpoint, &artHttpDetails) 137 if err != nil { 138 return "", err 139 } 140 if resp.StatusCode != http.StatusOK { 141 return "", errorutils.CheckErrorf("error while getting docker repository name. Artifactory response: " + resp.Status) 142 } 143 if dockerRepo := resp.Header["X-Artifactory-Docker-Registry"]; len(dockerRepo) != 0 { 144 return dockerRepo[0], nil 145 } 146 return "", errorutils.CheckErrorf("couldn't find 'X-Artifactory-Docker-Registry' header docker repository in artifactory") 147 } 148 149 // Returns the name of the repository containing the image in Artifactory. 150 func buildRequestUrl(longImageName, imageTag, containerRegistryUrl string, https bool) string { 151 endpoint := path.Join(containerRegistryUrl, "v2", longImageName, "manifests", imageTag) 152 if https { 153 return "https://" + endpoint 154 } 155 return "http://" + endpoint 156 }