Bladeren bron

Merge pull request #1682 from porter-dev/nafees/fix-cr-manifest-fetch

[hotfix] GCR and DOCR do not follow manifest fetching APIs like ECR does
sunguroku 4 jaren geleden
bovenliggende
commit
c099494efd
1 gewijzigde bestanden met toevoegingen van 131 en 0 verwijderingen
  1. 131 0
      cli/cmd/docker/agent.go

+ 131 - 0
cli/cmd/docker/agent.go

@@ -6,6 +6,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	"net/http"
 	"os"
 	"strings"
 	"time"
@@ -158,8 +159,122 @@ var PullImageErrNotFound = fmt.Errorf("Requested image not found")
 
 var PullImageErrUnauthorized = fmt.Errorf("Could not pull image: unauthorized")
 
+func getRegistryRepositoryPair(image, prefix string) []string {
+	if !strings.HasSuffix(prefix, "/") {
+		prefix = prefix + "/"
+	}
+
+	return strings.Split(strings.TrimPrefix(strings.Split(image, ":")[0], prefix), "/")
+}
+
 // CheckIfImageExists checks if the image exists in the registry
 func (a *Agent) CheckIfImageExists(image string) (bool, error) {
+	registryToken, err := a.getContainerRegistryToken(image)
+
+	if err != nil {
+		return false, err
+	}
+
+	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
+	defer cancel()
+
+	if strings.Contains(image, "gcr.io") {
+		gcrRegRepo := getRegistryRepositoryPair(image, "gcr.io")
+
+		if len(gcrRegRepo) != 2 {
+			return false, errors.New("invalid GCR image URL")
+		}
+
+		req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf(
+			"https://gcr.io/v2/%s/%s/tags/list", gcrRegRepo[0], gcrRegRepo[1],
+		), nil)
+
+		if err != nil {
+			return false, err
+		}
+
+		req.Header.Add("Content-Type", "application/json")
+		req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", registryToken))
+
+		resp, err := http.DefaultClient.Do(req)
+
+		if err != nil {
+			return false, err
+		}
+
+		defer resp.Body.Close()
+
+		var tags struct {
+			Tags []string `json:"tags,omitempty"`
+		}
+
+		err = json.NewDecoder(resp.Body).Decode(&tags)
+
+		if err != nil {
+			return false, err
+		}
+
+		reqTag := strings.Split(image, ":")[1]
+
+		for _, tag := range tags.Tags {
+			if tag == reqTag {
+				return true, nil
+			}
+		}
+
+		return false, nil
+	} else if strings.Contains(image, "registry.digitalocean.com") {
+		doRegRepo := getRegistryRepositoryPair(image, "registry.digitalocean.com")
+
+		if len(doRegRepo) != 2 {
+			return false, errors.New("invalid DOCR image URL")
+		}
+
+		req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf(
+			"https://api.digitalocean.com/v2/registry/%s/repositories/%s/digests", doRegRepo[0], doRegRepo[1],
+		), nil)
+
+		if err != nil {
+			return false, err
+		}
+
+		req.Header.Add("Content-Type", "application/json")
+		req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", registryToken))
+
+		resp, err := http.DefaultClient.Do(req)
+
+		if err != nil {
+			return false, err
+		}
+
+		defer resp.Body.Close()
+
+		// refer https://github.com/digitalocean/godo/blob/main/registry.go#L106
+		var digest struct {
+			Manifests []struct {
+				Tags []string `json:"tags,omitempty"`
+			} `json:"manifests,omitempty"`
+		}
+
+		err = json.NewDecoder(resp.Body).Decode(&digest)
+
+		if err != nil {
+			return false, err
+		}
+
+		reqTag := strings.Split(image, ":")[1]
+
+		for _, manifest := range digest.Manifests {
+			for _, tag := range manifest.Tags {
+				if tag == reqTag {
+					return true, nil
+				}
+			}
+		}
+
+		return false, nil
+	}
+
 	encodedRegistryAuth, err := a.getEncodedRegistryAuth(image)
 
 	if err != nil {
@@ -251,6 +366,22 @@ func (a *Agent) getPullOptions(image string) (types.ImagePullOptions, error) {
 	}, nil
 }
 
+func (a *Agent) getContainerRegistryToken(image string) (string, error) {
+	serverURL, err := GetServerURLFromTag(image)
+
+	if err != nil {
+		return "", err
+	}
+
+	_, secret, err := a.authGetter.GetCredentials(serverURL)
+
+	if err != nil {
+		return "", err
+	}
+
+	return secret, nil
+}
+
 func (a *Agent) getEncodedRegistryAuth(image string) (string, error) {
 	// get using server url
 	serverURL, err := GetServerURLFromTag(image)