github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/docker/registry/internal/gcr.go (about)

     1  // Copyright 2021 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package internal
     5  
     6  import (
     7  	"fmt"
     8  	"net/http"
     9  	"strings"
    10  
    11  	"github.com/juju/errors"
    12  
    13  	"github.com/juju/juju/docker"
    14  )
    15  
    16  type googleContainerRegistry struct {
    17  	*baseClient
    18  }
    19  
    20  func newGoogleContainerRegistry(repoDetails docker.ImageRepoDetails, transport http.RoundTripper) (RegistryInternal, error) {
    21  	c, err := newBase(repoDetails, transport, normalizeRepoDetailsCommon)
    22  	if err != nil {
    23  		return nil, errors.Trace(err)
    24  	}
    25  	return &googleContainerRegistry{c}, nil
    26  }
    27  
    28  func (c *googleContainerRegistry) String() string {
    29  	return "gcr.io"
    30  }
    31  
    32  // Match checks if the repository details matches current provider format.
    33  func (c *googleContainerRegistry) Match() bool {
    34  	return strings.Contains(c.repoDetails.ServerAddress, "gcr.io")
    35  }
    36  
    37  const (
    38  	googleContainerRegistryUserNameJSONKey = "_json_key"
    39  )
    40  
    41  var invalidGoogleContainerRegistryUserNameError = errors.NewNotValid(nil,
    42  	fmt.Sprintf("google container registry username has to be %q",
    43  		googleContainerRegistryUserNameJSONKey,
    44  	),
    45  )
    46  
    47  func validateGoogleContainerRegistryCredential(auth docker.BasicAuthConfig) (err error) {
    48  	if auth.Username == "" && auth.Auth.Empty() {
    49  		return errors.NewNotValid(nil, "username or auth token is required")
    50  	}
    51  	username := auth.Username
    52  	if !auth.Auth.Empty() {
    53  		username, _, err = unpackAuthToken(auth.Auth.Value)
    54  		if err != nil {
    55  			return errors.Annotate(err, "getting username from the google container registry auth token")
    56  		}
    57  	}
    58  	if username != googleContainerRegistryUserNameJSONKey {
    59  		return invalidGoogleContainerRegistryUserNameError
    60  	}
    61  	return nil
    62  }
    63  
    64  func googleContainerRegistryTransport(transport http.RoundTripper, repoDetails *docker.ImageRepoDetails,
    65  ) (http.RoundTripper, error) {
    66  	if !repoDetails.TokenAuthConfig.Empty() {
    67  		return nil, errors.NewNotValid(nil, "google container registry only supports username and password or auth token")
    68  	}
    69  	if repoDetails.BasicAuthConfig.Empty() {
    70  		// Anonymous login.
    71  		return newTokenTransport(transport, "", "", "", "", false), nil
    72  	}
    73  	if err := validateGoogleContainerRegistryCredential(repoDetails.BasicAuthConfig); err != nil {
    74  		return nil, errors.Annotatef(err, "validating the google container registry credential")
    75  	}
    76  	return newTokenTransport(
    77  		transport,
    78  		repoDetails.Username, repoDetails.Password, repoDetails.Auth.Content(), "", false,
    79  	), nil
    80  }
    81  
    82  func (c *googleContainerRegistry) WrapTransport(...TransportWrapper) (err error) {
    83  	if c.client.Transport, err = mergeTransportWrappers(
    84  		c.client.Transport, c.repoDetails,
    85  		googleContainerRegistryTransport, wrapErrorTransport,
    86  	); err != nil {
    87  		return errors.Trace(err)
    88  	}
    89  	return nil
    90  }
    91  
    92  // Ping pings the github endpoint.
    93  func (c googleContainerRegistry) Ping() error {
    94  	if !c.repoDetails.IsPrivate() {
    95  		// gcr.io root path requires authentication.
    96  		// So skip ping for public repositories.
    97  		return nil
    98  	}
    99  	url := c.url("/")
   100  	if !strings.HasSuffix(url, "/") {
   101  		// gcr v2 root endpoint requires the trailing slash(otherwise 404 returns).
   102  		url += "/"
   103  	}
   104  	logger.Debugf("gcr ping %q", url)
   105  	resp, err := c.client.Get(url)
   106  	if resp != nil {
   107  		defer resp.Body.Close()
   108  	}
   109  	return errors.Trace(unwrapNetError(err))
   110  }