github.com/jfrog/jfrog-cli-core/v2@v2.52.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  }