github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/docker/registry/internal/base_manifests_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 "io" 8 "net/http" 9 "strings" 10 11 "github.com/juju/errors" 12 jc "github.com/juju/testing/checkers" 13 "go.uber.org/mock/gomock" 14 gc "gopkg.in/check.v1" 15 16 "github.com/juju/juju/docker/registry/internal" 17 "github.com/juju/juju/docker/registry/internal/mocks" 18 ) 19 20 func (s *baseSuite) assertGetManifestsSchemaVersion1(c *gc.C, responseData, contentType string, statusCode int, f func(*internal.ManifestsResult, error)) { 21 // Use v2 for private repository. 22 s.isPrivate = true 23 reg, ctrl := s.getRegistry(c) 24 defer ctrl.Finish() 25 26 gomock.InOrder( 27 s.mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) { 28 c.Assert(req.Header, jc.DeepEquals, http.Header{}) 29 c.Assert(req.Method, gc.Equals, `GET`) 30 c.Assert(req.URL.String(), gc.Equals, `https://example.com/v2/jujuqa/jujud-operator/manifests/2.9.10`) 31 return &http.Response{ 32 Request: req, 33 StatusCode: http.StatusUnauthorized, 34 Body: io.NopCloser(nil), 35 Header: http.Header{ 36 http.CanonicalHeaderKey("WWW-Authenticate"): []string{ 37 `Bearer realm="https://auth.example.com/token",service="registry.example.com",scope="repository:jujuqa/jujud-operator:pull"`, 38 }, 39 }, 40 }, nil 41 }), 42 // Refresh OAuth Token. 43 s.mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( 44 func(req *http.Request) (*http.Response, error) { 45 c.Assert(req.Header, jc.DeepEquals, http.Header{"Authorization": []string{"Basic " + s.getAuthToken("username", "pwd")}}) 46 c.Assert(req.Method, gc.Equals, `GET`) 47 c.Assert(req.URL.String(), gc.Equals, `https://auth.example.com/token?scope=repository%3Ajujuqa%2Fjujud-operator%3Apull&service=registry.example.com`) 48 return &http.Response{ 49 Request: req, 50 StatusCode: http.StatusOK, 51 Body: io.NopCloser(strings.NewReader(`{"token": "jwt-token", "access_token": "jwt-token","expires_in": 300}`)), 52 }, nil 53 }, 54 ), 55 s.mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) { 56 c.Assert(req.Header, jc.DeepEquals, http.Header{"Authorization": []string{"Bearer jwt-token"}}) 57 c.Assert(req.Method, gc.Equals, `GET`) 58 c.Assert(req.URL.String(), gc.Equals, `https://example.com/v2/jujuqa/jujud-operator/manifests/2.9.10`) 59 resps := &http.Response{ 60 Header: http.Header{ 61 http.CanonicalHeaderKey("Content-Type"): []string{contentType}, 62 }, 63 Request: req, 64 StatusCode: statusCode, 65 Body: io.NopCloser(strings.NewReader(responseData)), 66 } 67 return resps, nil 68 }), 69 ) 70 manifests, err := reg.GetManifests("jujud-operator", "2.9.10") 71 f(manifests, err) 72 } 73 74 func (s *baseSuite) TestGetManifestsSchemaVersion1(c *gc.C) { 75 s.assertGetManifestsSchemaVersion1(c, 76 ` 77 { "schemaVersion": 1, "name": "jujuqa/jujud-operator", "tag": "2.9.13", "architecture": "amd64"} 78 `[1:], 79 `application/vnd.docker.distribution.manifest.v1+prettyjws`, 80 http.StatusOK, 81 func(result *internal.ManifestsResult, err error) { 82 c.Assert(err, jc.ErrorIsNil) 83 c.Assert(result, jc.DeepEquals, &internal.ManifestsResult{Architecture: "amd64"}) 84 }, 85 ) 86 } 87 88 func (s *baseSuite) TestGetManifestsSchemaVersion2(c *gc.C) { 89 s.assertGetManifestsSchemaVersion1(c, 90 ` 91 { 92 "schemaVersion": 2, 93 "mediaType": "application/vnd.docker.distribution.manifest.v2+json", 94 "config": { 95 "mediaType": "application/vnd.docker.container.image.v1+json", 96 "size": 4596, 97 "digest": "sha256:f0609d8a844f7271411c1a9c5d7a898fd9f9c5a4844e3bc7db6d725b54671ac1" 98 } 99 } 100 `[1:], 101 `application/vnd.docker.distribution.manifest.v2+prettyjws`, 102 http.StatusOK, 103 func(result *internal.ManifestsResult, err error) { 104 c.Assert(err, jc.ErrorIsNil) 105 c.Assert(result, jc.DeepEquals, &internal.ManifestsResult{Digest: "sha256:f0609d8a844f7271411c1a9c5d7a898fd9f9c5a4844e3bc7db6d725b54671ac1"}) 106 }, 107 ) 108 } 109 110 func (s *baseSuite) TestGetManifestsSchemaVersion2NotFound(c *gc.C) { 111 s.assertGetManifestsSchemaVersion1(c, 112 ` 113 { 114 "schemaVersion": 2, 115 "mediaType": "application/vnd.docker.distribution.manifest.v2+json", 116 "config": { 117 "mediaType": "application/vnd.docker.container.image.v1+json", 118 "size": 4596, 119 "digest": "sha256:f0609d8a844f7271411c1a9c5d7a898fd9f9c5a4844e3bc7db6d725b54671ac1" 120 } 121 } 122 `[1:], 123 `application/vnd.docker.distribution.manifest.v2+prettyjws`, 124 http.StatusNotFound, 125 func(_ *internal.ManifestsResult, err error) { 126 c.Assert(err, jc.Satisfies, errors.IsNotFound) 127 c.Assert(err, gc.ErrorMatches, `Get "https://example.com/v2/jujuqa/jujud-operator/manifests/2.9.10": non-successful response status=404 not found`) 128 }, 129 ) 130 } 131 132 func (s *baseSuite) TestGetBlobs(c *gc.C) { 133 // Use v2 for private repository. 134 s.isPrivate = true 135 reg, ctrl := s.getRegistry(c) 136 defer ctrl.Finish() 137 138 gomock.InOrder( 139 s.mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) { 140 c.Assert(req.Header, jc.DeepEquals, http.Header{}) 141 c.Assert(req.Method, gc.Equals, `GET`) 142 c.Assert(req.URL.String(), gc.Equals, 143 `https://example.com/v2/jujuqa/jujud-operator/blobs/sha256:f0609d8a844f7271411c1a9c5d7a898fd9f9c5a4844e3bc7db6d725b54671ac1`, 144 ) 145 return &http.Response{ 146 Request: req, 147 StatusCode: http.StatusUnauthorized, 148 Body: io.NopCloser(nil), 149 Header: http.Header{ 150 http.CanonicalHeaderKey("WWW-Authenticate"): []string{ 151 `Bearer realm="https://auth.example.com/token",service="registry.example.com",scope="repository:jujuqa/jujud-operator:pull"`, 152 }, 153 }, 154 }, nil 155 }), 156 // Refresh OAuth Token. 157 s.mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn( 158 func(req *http.Request) (*http.Response, error) { 159 c.Assert(req.Header, jc.DeepEquals, http.Header{"Authorization": []string{"Basic " + s.getAuthToken("username", "pwd")}}) 160 c.Assert(req.Method, gc.Equals, `GET`) 161 c.Assert(req.URL.String(), gc.Equals, `https://auth.example.com/token?scope=repository%3Ajujuqa%2Fjujud-operator%3Apull&service=registry.example.com`) 162 return &http.Response{ 163 Request: req, 164 StatusCode: http.StatusOK, 165 Body: io.NopCloser(strings.NewReader(`{"token": "jwt-token", "access_token": "jwt-token","expires_in": 300}`)), 166 }, nil 167 }, 168 ), 169 s.mockRoundTripper.EXPECT().RoundTrip(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) { 170 c.Assert(req.Header, jc.DeepEquals, http.Header{"Authorization": []string{"Bearer jwt-token"}}) 171 c.Assert(req.Method, gc.Equals, `GET`) 172 c.Assert(req.URL.String(), gc.Equals, 173 `https://example.com/v2/jujuqa/jujud-operator/blobs/sha256:f0609d8a844f7271411c1a9c5d7a898fd9f9c5a4844e3bc7db6d725b54671ac1`, 174 ) 175 resps := &http.Response{ 176 Request: req, 177 StatusCode: http.StatusOK, 178 Body: io.NopCloser(strings.NewReader(` 179 {"architecture":"amd64"} 180 `[1:])), 181 } 182 return resps, nil 183 }), 184 ) 185 manifests, err := reg.GetBlobs("jujud-operator", "sha256:f0609d8a844f7271411c1a9c5d7a898fd9f9c5a4844e3bc7db6d725b54671ac1") 186 c.Assert(err, jc.ErrorIsNil) 187 c.Assert(manifests, jc.DeepEquals, &internal.BlobsResponse{Architecture: "amd64"}) 188 } 189 190 func (s *baseSuite) TestGetArchitectureV1(c *gc.C) { 191 ctrl := gomock.NewController(c) 192 client := mocks.NewMockArchitectureGetter(ctrl) 193 194 gomock.InOrder( 195 client.EXPECT().GetManifests("jujud-operator", "2.9.12").Return( 196 &internal.ManifestsResult{Architecture: "amd64"}, nil, 197 ), 198 ) 199 arch, err := internal.GetArchitecture("jujud-operator", "2.9.12", client) 200 c.Assert(err, jc.ErrorIsNil) 201 c.Assert(arch, jc.DeepEquals, "amd64") 202 } 203 204 func (s *baseSuite) TestGetArchitectureV2(c *gc.C) { 205 ctrl := gomock.NewController(c) 206 client := mocks.NewMockArchitectureGetter(ctrl) 207 208 gomock.InOrder( 209 client.EXPECT().GetManifests("jujud-operator", "2.9.12").Return( 210 &internal.ManifestsResult{Digest: "sha256:f0609d8a844f7271411c1a9c5d7a898fd9f9c5a4844e3bc7db6d725b54671ac1"}, nil, 211 ), 212 client.EXPECT().GetBlobs("jujud-operator", "sha256:f0609d8a844f7271411c1a9c5d7a898fd9f9c5a4844e3bc7db6d725b54671ac1").Return( 213 &internal.BlobsResponse{Architecture: "amd64"}, nil, 214 ), 215 ) 216 arch, err := internal.GetArchitecture("jujud-operator", "2.9.12", client) 217 c.Assert(err, jc.ErrorIsNil) 218 c.Assert(arch, jc.DeepEquals, "amd64") 219 } 220 221 func (s *baseSuite) TestGetArchitectureInvalidResponse(c *gc.C) { 222 ctrl := gomock.NewController(c) 223 client := mocks.NewMockArchitectureGetter(ctrl) 224 225 gomock.InOrder( 226 client.EXPECT().GetManifests("jujud-operator", "2.9.12").Return( 227 &internal.ManifestsResult{}, nil, 228 ), 229 ) 230 _, err := internal.GetArchitecture("jujud-operator", "2.9.12", client) 231 c.Assert(err, gc.ErrorMatches, `faild to get manifests for "jujud-operator" "2.9.12"`) 232 }