github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/docker/registry/internal/base_client_test.go (about) 1 // Copyright 2021 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package internal_test 5 6 import ( 7 "encoding/base64" 8 "io" 9 "net/http" 10 "strings" 11 12 "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 "go.uber.org/mock/gomock" 15 gc "gopkg.in/check.v1" 16 17 "github.com/juju/juju/docker" 18 "github.com/juju/juju/docker/registry" 19 "github.com/juju/juju/docker/registry/internal" 20 "github.com/juju/juju/docker/registry/mocks" 21 ) 22 23 type baseSuite struct { 24 testing.IsolationSuite 25 26 mockRoundTripper *mocks.MockRoundTripper 27 imageRepoDetails docker.ImageRepoDetails 28 isPrivate bool 29 } 30 31 var _ = gc.Suite(&baseSuite{}) 32 33 func (s *baseSuite) getAuthToken(username, password string) string { 34 return base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) 35 } 36 37 func (s *baseSuite) getRegistry(c *gc.C) (*internal.BaseClient, *gomock.Controller) { 38 ctrl := gomock.NewController(c) 39 40 s.imageRepoDetails = docker.ImageRepoDetails{ 41 Repository: "example.com/jujuqa", 42 ServerAddress: "example.com", 43 } 44 authToken := s.getAuthToken("username", "pwd") 45 if s.isPrivate { 46 s.imageRepoDetails.BasicAuthConfig = docker.BasicAuthConfig{ 47 Auth: docker.NewToken(authToken), 48 } 49 } 50 51 s.mockRoundTripper = mocks.NewMockRoundTripper(ctrl) 52 gomock.InOrder( 53 // registry.Ping() 1st try failed - bearer token was missing. 54 s.mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( 55 func(req *http.Request) (*http.Response, error) { 56 c.Assert(req.Header, jc.DeepEquals, http.Header{}) 57 c.Assert(req.Method, gc.Equals, `GET`) 58 c.Assert(req.URL.String(), gc.Equals, `https://example.com/v2`) 59 return &http.Response{ 60 Request: req, 61 StatusCode: http.StatusUnauthorized, 62 Body: io.NopCloser(nil), 63 Header: http.Header{ 64 http.CanonicalHeaderKey("WWW-Authenticate"): []string{ 65 `Bearer realm="https://auth.example.com/token",service="registry.example.com",scope="repository:jujuqa/jujud-operator:pull"`, 66 }, 67 }, 68 }, nil 69 }, 70 ), 71 // Refresh OAuth Token 72 s.mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( 73 func(req *http.Request) (*http.Response, error) { 74 if s.isPrivate { 75 c.Assert(req.Header, jc.DeepEquals, http.Header{"Authorization": []string{"Basic " + authToken}}) 76 } 77 c.Assert(req.Method, gc.Equals, `GET`) 78 c.Assert(req.URL.String(), gc.Equals, `https://auth.example.com/token?scope=repository%3Ajujuqa%2Fjujud-operator%3Apull&service=registry.example.com`) 79 return &http.Response{ 80 Request: req, 81 StatusCode: http.StatusOK, 82 Body: io.NopCloser(strings.NewReader(`{"token": "jwt-token", "access_token": "jwt-token","expires_in": 300}`)), 83 }, nil 84 }, 85 ), 86 s.mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( 87 func(req *http.Request) (*http.Response, error) { 88 c.Assert(req.Header, jc.DeepEquals, http.Header{"Authorization": []string{"Bearer " + `jwt-token`}}) 89 c.Assert(req.Method, gc.Equals, `GET`) 90 c.Assert(req.URL.String(), gc.Equals, `https://example.com/v2`) 91 return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(nil)}, nil 92 }, 93 ), 94 ) 95 s.PatchValue(®istry.DefaultTransport, s.mockRoundTripper) 96 97 reg, err := registry.New(s.imageRepoDetails) 98 c.Assert(err, jc.ErrorIsNil) 99 client, ok := reg.(*internal.BaseClient) 100 c.Assert(ok, jc.IsTrue) 101 err = reg.Ping() 102 c.Assert(err, jc.ErrorIsNil) 103 return client, ctrl 104 } 105 106 func (s *baseSuite) TestPingPublicRepository(c *gc.C) { 107 s.isPrivate = false 108 _, ctrl := s.getRegistry(c) 109 ctrl.Finish() 110 } 111 112 func (s *baseSuite) TestPingPrivateRepository(c *gc.C) { 113 s.isPrivate = true 114 _, ctrl := s.getRegistry(c) 115 ctrl.Finish() 116 } 117 118 func (s *baseSuite) TestInvalidAuth(c *gc.C) { 119 s.imageRepoDetails = docker.ImageRepoDetails{ 120 Repository: "example.com/jujuqa", 121 ServerAddress: "example.com", 122 } 123 s.imageRepoDetails.TokenAuthConfig = docker.TokenAuthConfig{ 124 RegistryToken: &docker.Token{Value: `xxxxx==`}, 125 } 126 127 _, err := registry.New(s.imageRepoDetails) 128 c.Assert(err, gc.ErrorMatches, `only {"username", "password"} or {"auth"} authorization is supported for registry "example.com"`) 129 }