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  }