[test-integration] Add a registry package with registry v1/v2 code
| ... | ... |
@@ -16,6 +16,7 @@ import ( |
| 16 | 16 |
cliconfig "github.com/docker/docker/cli/config" |
| 17 | 17 |
"github.com/docker/docker/integration-cli/daemon" |
| 18 | 18 |
"github.com/docker/docker/integration-cli/environment" |
| 19 |
+ "github.com/docker/docker/integration-cli/registry" |
|
| 19 | 20 |
"github.com/docker/docker/pkg/reexec" |
| 20 | 21 |
"github.com/go-check/check" |
| 21 | 22 |
) |
| ... | ... |
@@ -172,7 +173,7 @@ func init() {
|
| 172 | 172 |
|
| 173 | 173 |
type DockerRegistrySuite struct {
|
| 174 | 174 |
ds *DockerSuite |
| 175 |
- reg *testRegistryV2 |
|
| 175 |
+ reg *registry.V2 |
|
| 176 | 176 |
d *daemon.Daemon |
| 177 | 177 |
} |
| 178 | 178 |
|
| ... | ... |
@@ -181,7 +182,7 @@ func (s *DockerRegistrySuite) OnTimeout(c *check.C) {
|
| 181 | 181 |
} |
| 182 | 182 |
|
| 183 | 183 |
func (s *DockerRegistrySuite) SetUpTest(c *check.C) {
|
| 184 |
- testRequires(c, DaemonIsLinux, RegistryHosting) |
|
| 184 |
+ testRequires(c, DaemonIsLinux, registry.Hosting) |
|
| 185 | 185 |
s.reg = setupRegistry(c, false, "", "") |
| 186 | 186 |
s.d = daemon.New(c, dockerBinary, dockerdBinary, daemon.Config{
|
| 187 | 187 |
Experimental: experimentalDaemon, |
| ... | ... |
@@ -206,7 +207,7 @@ func init() {
|
| 206 | 206 |
|
| 207 | 207 |
type DockerSchema1RegistrySuite struct {
|
| 208 | 208 |
ds *DockerSuite |
| 209 |
- reg *testRegistryV2 |
|
| 209 |
+ reg *registry.V2 |
|
| 210 | 210 |
d *daemon.Daemon |
| 211 | 211 |
} |
| 212 | 212 |
|
| ... | ... |
@@ -215,7 +216,7 @@ func (s *DockerSchema1RegistrySuite) OnTimeout(c *check.C) {
|
| 215 | 215 |
} |
| 216 | 216 |
|
| 217 | 217 |
func (s *DockerSchema1RegistrySuite) SetUpTest(c *check.C) {
|
| 218 |
- testRequires(c, DaemonIsLinux, RegistryHosting, NotArm64) |
|
| 218 |
+ testRequires(c, DaemonIsLinux, registry.Hosting, NotArm64) |
|
| 219 | 219 |
s.reg = setupRegistry(c, true, "", "") |
| 220 | 220 |
s.d = daemon.New(c, dockerBinary, dockerdBinary, daemon.Config{
|
| 221 | 221 |
Experimental: experimentalDaemon, |
| ... | ... |
@@ -240,7 +241,7 @@ func init() {
|
| 240 | 240 |
|
| 241 | 241 |
type DockerRegistryAuthHtpasswdSuite struct {
|
| 242 | 242 |
ds *DockerSuite |
| 243 |
- reg *testRegistryV2 |
|
| 243 |
+ reg *registry.V2 |
|
| 244 | 244 |
d *daemon.Daemon |
| 245 | 245 |
} |
| 246 | 246 |
|
| ... | ... |
@@ -249,7 +250,7 @@ func (s *DockerRegistryAuthHtpasswdSuite) OnTimeout(c *check.C) {
|
| 249 | 249 |
} |
| 250 | 250 |
|
| 251 | 251 |
func (s *DockerRegistryAuthHtpasswdSuite) SetUpTest(c *check.C) {
|
| 252 |
- testRequires(c, DaemonIsLinux, RegistryHosting) |
|
| 252 |
+ testRequires(c, DaemonIsLinux, registry.Hosting) |
|
| 253 | 253 |
s.reg = setupRegistry(c, false, "htpasswd", "") |
| 254 | 254 |
s.d = daemon.New(c, dockerBinary, dockerdBinary, daemon.Config{
|
| 255 | 255 |
Experimental: experimentalDaemon, |
| ... | ... |
@@ -276,7 +277,7 @@ func init() {
|
| 276 | 276 |
|
| 277 | 277 |
type DockerRegistryAuthTokenSuite struct {
|
| 278 | 278 |
ds *DockerSuite |
| 279 |
- reg *testRegistryV2 |
|
| 279 |
+ reg *registry.V2 |
|
| 280 | 280 |
d *daemon.Daemon |
| 281 | 281 |
} |
| 282 | 282 |
|
| ... | ... |
@@ -285,7 +286,7 @@ func (s *DockerRegistryAuthTokenSuite) OnTimeout(c *check.C) {
|
| 285 | 285 |
} |
| 286 | 286 |
|
| 287 | 287 |
func (s *DockerRegistryAuthTokenSuite) SetUpTest(c *check.C) {
|
| 288 |
- testRequires(c, DaemonIsLinux, RegistryHosting) |
|
| 288 |
+ testRequires(c, DaemonIsLinux, registry.Hosting) |
|
| 289 | 289 |
s.d = daemon.New(c, dockerBinary, dockerdBinary, daemon.Config{
|
| 290 | 290 |
Experimental: experimentalDaemon, |
| 291 | 291 |
}) |
| ... | ... |
@@ -449,12 +450,12 @@ func init() {
|
| 449 | 449 |
|
| 450 | 450 |
type DockerTrustSuite struct {
|
| 451 | 451 |
ds *DockerSuite |
| 452 |
- reg *testRegistryV2 |
|
| 452 |
+ reg *registry.V2 |
|
| 453 | 453 |
not *testNotary |
| 454 | 454 |
} |
| 455 | 455 |
|
| 456 | 456 |
func (s *DockerTrustSuite) SetUpTest(c *check.C) {
|
| 457 |
- testRequires(c, RegistryHosting, NotaryServerHosting) |
|
| 457 |
+ testRequires(c, registry.Hosting, NotaryServerHosting) |
|
| 458 | 458 |
s.reg = setupRegistry(c, false, "", "") |
| 459 | 459 |
s.not = setupNotary(c) |
| 460 | 460 |
} |
| ... | ... |
@@ -487,7 +488,7 @@ func init() {
|
| 487 | 487 |
type DockerTrustedSwarmSuite struct {
|
| 488 | 488 |
swarmSuite DockerSwarmSuite |
| 489 | 489 |
trustSuite DockerTrustSuite |
| 490 |
- reg *testRegistryV2 |
|
| 490 |
+ reg *registry.V2 |
|
| 491 | 491 |
not *testNotary |
| 492 | 492 |
} |
| 493 | 493 |
|
| ... | ... |
@@ -6580,7 +6580,7 @@ func (s *DockerSuite) TestBuildLabelOverwrite(c *check.C) {
|
| 6580 | 6580 |
} |
| 6581 | 6581 |
|
| 6582 | 6582 |
func (s *DockerRegistryAuthHtpasswdSuite) TestBuildFromAuthenticatedRegistry(c *check.C) {
|
| 6583 |
- dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, privateRegistryURL) |
|
| 6583 |
+ dockerCmd(c, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL) |
|
| 6584 | 6584 |
|
| 6585 | 6585 |
baseImage := privateRegistryURL + "/baseimage" |
| 6586 | 6586 |
|
| ... | ... |
@@ -6625,7 +6625,7 @@ func (s *DockerRegistryAuthHtpasswdSuite) TestBuildWithExternalAuth(c *check.C) |
| 6625 | 6625 |
err = ioutil.WriteFile(configPath, []byte(externalAuthConfig), 0644) |
| 6626 | 6626 |
c.Assert(err, checker.IsNil) |
| 6627 | 6627 |
|
| 6628 |
- dockerCmd(c, "--config", tmp, "login", "-u", s.reg.username, "-p", s.reg.password, privateRegistryURL) |
|
| 6628 |
+ dockerCmd(c, "--config", tmp, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL) |
|
| 6629 | 6629 |
|
| 6630 | 6630 |
b, err := ioutil.ReadFile(configPath) |
| 6631 | 6631 |
c.Assert(err, checker.IsNil) |
| ... | ... |
@@ -533,7 +533,7 @@ func (s *DockerRegistrySuite) TestPullFailsWithAlteredManifest(c *check.C) {
|
| 533 | 533 |
c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
|
| 534 | 534 |
|
| 535 | 535 |
// Load the target manifest blob. |
| 536 |
- manifestBlob := s.reg.readBlobContents(c, manifestDigest) |
|
| 536 |
+ manifestBlob := s.reg.ReadBlobContents(c, manifestDigest) |
|
| 537 | 537 |
|
| 538 | 538 |
var imgManifest schema2.Manifest |
| 539 | 539 |
err = json.Unmarshal(manifestBlob, &imgManifest) |
| ... | ... |
@@ -544,13 +544,13 @@ func (s *DockerRegistrySuite) TestPullFailsWithAlteredManifest(c *check.C) {
|
| 544 | 544 |
|
| 545 | 545 |
// Move the existing data file aside, so that we can replace it with a |
| 546 | 546 |
// malicious blob of data. NOTE: we defer the returned undo func. |
| 547 |
- undo := s.reg.tempMoveBlobData(c, manifestDigest) |
|
| 547 |
+ undo := s.reg.TempMoveBlobData(c, manifestDigest) |
|
| 548 | 548 |
defer undo() |
| 549 | 549 |
|
| 550 | 550 |
alteredManifestBlob, err := json.MarshalIndent(imgManifest, "", " ") |
| 551 | 551 |
c.Assert(err, checker.IsNil, check.Commentf("unable to encode altered image manifest to JSON"))
|
| 552 | 552 |
|
| 553 |
- s.reg.writeBlobContents(c, manifestDigest, alteredManifestBlob) |
|
| 553 |
+ s.reg.WriteBlobContents(c, manifestDigest, alteredManifestBlob) |
|
| 554 | 554 |
|
| 555 | 555 |
// Now try pulling that image by digest. We should get an error about |
| 556 | 556 |
// digest verification for the manifest digest. |
| ... | ... |
@@ -573,7 +573,7 @@ func (s *DockerSchema1RegistrySuite) TestPullFailsWithAlteredManifest(c *check.C |
| 573 | 573 |
c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
|
| 574 | 574 |
|
| 575 | 575 |
// Load the target manifest blob. |
| 576 |
- manifestBlob := s.reg.readBlobContents(c, manifestDigest) |
|
| 576 |
+ manifestBlob := s.reg.ReadBlobContents(c, manifestDigest) |
|
| 577 | 577 |
|
| 578 | 578 |
var imgManifest schema1.Manifest |
| 579 | 579 |
err = json.Unmarshal(manifestBlob, &imgManifest) |
| ... | ... |
@@ -586,13 +586,13 @@ func (s *DockerSchema1RegistrySuite) TestPullFailsWithAlteredManifest(c *check.C |
| 586 | 586 |
|
| 587 | 587 |
// Move the existing data file aside, so that we can replace it with a |
| 588 | 588 |
// malicious blob of data. NOTE: we defer the returned undo func. |
| 589 |
- undo := s.reg.tempMoveBlobData(c, manifestDigest) |
|
| 589 |
+ undo := s.reg.TempMoveBlobData(c, manifestDigest) |
|
| 590 | 590 |
defer undo() |
| 591 | 591 |
|
| 592 | 592 |
alteredManifestBlob, err := json.MarshalIndent(imgManifest, "", " ") |
| 593 | 593 |
c.Assert(err, checker.IsNil, check.Commentf("unable to encode altered image manifest to JSON"))
|
| 594 | 594 |
|
| 595 |
- s.reg.writeBlobContents(c, manifestDigest, alteredManifestBlob) |
|
| 595 |
+ s.reg.WriteBlobContents(c, manifestDigest, alteredManifestBlob) |
|
| 596 | 596 |
|
| 597 | 597 |
// Now try pulling that image by digest. We should get an error about |
| 598 | 598 |
// digest verification for the manifest digest. |
| ... | ... |
@@ -615,7 +615,7 @@ func (s *DockerRegistrySuite) TestPullFailsWithAlteredLayer(c *check.C) {
|
| 615 | 615 |
c.Assert(err, checker.IsNil) |
| 616 | 616 |
|
| 617 | 617 |
// Load the target manifest blob. |
| 618 |
- manifestBlob := s.reg.readBlobContents(c, manifestDigest) |
|
| 618 |
+ manifestBlob := s.reg.ReadBlobContents(c, manifestDigest) |
|
| 619 | 619 |
|
| 620 | 620 |
var imgManifest schema2.Manifest |
| 621 | 621 |
err = json.Unmarshal(manifestBlob, &imgManifest) |
| ... | ... |
@@ -626,11 +626,11 @@ func (s *DockerRegistrySuite) TestPullFailsWithAlteredLayer(c *check.C) {
|
| 626 | 626 |
|
| 627 | 627 |
// Move the existing data file aside, so that we can replace it with a |
| 628 | 628 |
// malicious blob of data. NOTE: we defer the returned undo func. |
| 629 |
- undo := s.reg.tempMoveBlobData(c, targetLayerDigest) |
|
| 629 |
+ undo := s.reg.TempMoveBlobData(c, targetLayerDigest) |
|
| 630 | 630 |
defer undo() |
| 631 | 631 |
|
| 632 | 632 |
// Now make a fake data blob in this directory. |
| 633 |
- s.reg.writeBlobContents(c, targetLayerDigest, []byte("This is not the data you are looking for."))
|
|
| 633 |
+ s.reg.WriteBlobContents(c, targetLayerDigest, []byte("This is not the data you are looking for."))
|
|
| 634 | 634 |
|
| 635 | 635 |
// Now try pulling that image by digest. We should get an error about |
| 636 | 636 |
// digest verification for the target layer digest. |
| ... | ... |
@@ -658,7 +658,7 @@ func (s *DockerSchema1RegistrySuite) TestPullFailsWithAlteredLayer(c *check.C) {
|
| 658 | 658 |
c.Assert(err, checker.IsNil) |
| 659 | 659 |
|
| 660 | 660 |
// Load the target manifest blob. |
| 661 |
- manifestBlob := s.reg.readBlobContents(c, manifestDigest) |
|
| 661 |
+ manifestBlob := s.reg.ReadBlobContents(c, manifestDigest) |
|
| 662 | 662 |
|
| 663 | 663 |
var imgManifest schema1.Manifest |
| 664 | 664 |
err = json.Unmarshal(manifestBlob, &imgManifest) |
| ... | ... |
@@ -669,11 +669,11 @@ func (s *DockerSchema1RegistrySuite) TestPullFailsWithAlteredLayer(c *check.C) {
|
| 669 | 669 |
|
| 670 | 670 |
// Move the existing data file aside, so that we can replace it with a |
| 671 | 671 |
// malicious blob of data. NOTE: we defer the returned undo func. |
| 672 |
- undo := s.reg.tempMoveBlobData(c, targetLayerDigest) |
|
| 672 |
+ undo := s.reg.TempMoveBlobData(c, targetLayerDigest) |
|
| 673 | 673 |
defer undo() |
| 674 | 674 |
|
| 675 | 675 |
// Now make a fake data blob in this directory. |
| 676 |
- s.reg.writeBlobContents(c, targetLayerDigest, []byte("This is not the data you are looking for."))
|
|
| 676 |
+ s.reg.WriteBlobContents(c, targetLayerDigest, []byte("This is not the data you are looking for."))
|
|
| 677 | 677 |
|
| 678 | 678 |
// Now try pulling that image by digest. We should get an error about |
| 679 | 679 |
// digest verification for the target layer digest. |
| ... | ... |
@@ -21,10 +21,10 @@ func (s *DockerSuite) TestLoginWithoutTTY(c *check.C) {
|
| 21 | 21 |
|
| 22 | 22 |
func (s *DockerRegistryAuthHtpasswdSuite) TestLoginToPrivateRegistry(c *check.C) {
|
| 23 | 23 |
// wrong credentials |
| 24 |
- out, _, err := dockerCmdWithError("login", "-u", s.reg.username, "-p", "WRONGPASSWORD", privateRegistryURL)
|
|
| 24 |
+ out, _, err := dockerCmdWithError("login", "-u", s.reg.Username(), "-p", "WRONGPASSWORD", privateRegistryURL)
|
|
| 25 | 25 |
c.Assert(err, checker.NotNil, check.Commentf(out)) |
| 26 | 26 |
c.Assert(out, checker.Contains, "401 Unauthorized") |
| 27 | 27 |
|
| 28 | 28 |
// now it's fine |
| 29 |
- dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, privateRegistryURL) |
|
| 29 |
+ dockerCmd(c, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL) |
|
| 30 | 30 |
} |
| ... | ... |
@@ -35,7 +35,7 @@ func (s *DockerRegistryAuthHtpasswdSuite) TestLogoutWithExternalAuth(c *check.C) |
| 35 | 35 |
err = ioutil.WriteFile(configPath, []byte(externalAuthConfig), 0644) |
| 36 | 36 |
c.Assert(err, checker.IsNil) |
| 37 | 37 |
|
| 38 |
- dockerCmd(c, "--config", tmp, "login", "-u", s.reg.username, "-p", s.reg.password, privateRegistryURL) |
|
| 38 |
+ dockerCmd(c, "--config", tmp, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL) |
|
| 39 | 39 |
|
| 40 | 40 |
b, err := ioutil.ReadFile(configPath) |
| 41 | 41 |
c.Assert(err, checker.IsNil) |
| ... | ... |
@@ -71,7 +71,7 @@ func (s *DockerRegistryAuthHtpasswdSuite) TestLogoutWithWrongHostnamesStored(c * |
| 71 | 71 |
os.Setenv("PATH", testPath)
|
| 72 | 72 |
|
| 73 | 73 |
cmd := exec.Command("docker-credential-shell-test", "store")
|
| 74 |
- stdin := bytes.NewReader([]byte(fmt.Sprintf(`{"ServerURL": "https://%s", "Username": "%s", "Secret": "%s"}`, privateRegistryURL, s.reg.username, s.reg.password)))
|
|
| 74 |
+ stdin := bytes.NewReader([]byte(fmt.Sprintf(`{"ServerURL": "https://%s", "Username": "%s", "Secret": "%s"}`, privateRegistryURL, s.reg.Username(), s.reg.Password())))
|
|
| 75 | 75 |
cmd.Stdin = stdin |
| 76 | 76 |
c.Assert(cmd.Run(), checker.IsNil) |
| 77 | 77 |
|
| ... | ... |
@@ -84,7 +84,7 @@ func (s *DockerRegistryAuthHtpasswdSuite) TestLogoutWithWrongHostnamesStored(c * |
| 84 | 84 |
err = ioutil.WriteFile(configPath, []byte(externalAuthConfig), 0644) |
| 85 | 85 |
c.Assert(err, checker.IsNil) |
| 86 | 86 |
|
| 87 |
- dockerCmd(c, "--config", tmp, "login", "-u", s.reg.username, "-p", s.reg.password, privateRegistryURL) |
|
| 87 |
+ dockerCmd(c, "--config", tmp, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL) |
|
| 88 | 88 |
|
| 89 | 89 |
b, err := ioutil.ReadFile(configPath) |
| 90 | 90 |
c.Assert(err, checker.IsNil) |
| ... | ... |
@@ -347,7 +347,7 @@ func (s *DockerRegistrySuite) TestPullManifestList(c *check.C) {
|
| 347 | 347 |
manifestListDigest := digest.FromBytes(manifestListJSON) |
| 348 | 348 |
hexDigest := manifestListDigest.Hex() |
| 349 | 349 |
|
| 350 |
- registryV2Path := filepath.Join(s.reg.dir, "docker", "registry", "v2") |
|
| 350 |
+ registryV2Path := s.reg.Path() |
|
| 351 | 351 |
|
| 352 | 352 |
// Write manifest list to blob store |
| 353 | 353 |
blobDir := filepath.Join(registryV2Path, "blobs", "sha256", hexDigest[:2], hexDigest) |
| ... | ... |
@@ -411,7 +411,7 @@ func (s *DockerRegistryAuthHtpasswdSuite) TestPullWithExternalAuthLoginWithSchem |
| 411 | 411 |
err = ioutil.WriteFile(configPath, []byte(externalAuthConfig), 0644) |
| 412 | 412 |
c.Assert(err, checker.IsNil) |
| 413 | 413 |
|
| 414 |
- dockerCmd(c, "--config", tmp, "login", "-u", s.reg.username, "-p", s.reg.password, privateRegistryURL) |
|
| 414 |
+ dockerCmd(c, "--config", tmp, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL) |
|
| 415 | 415 |
|
| 416 | 416 |
b, err := ioutil.ReadFile(configPath) |
| 417 | 417 |
c.Assert(err, checker.IsNil) |
| ... | ... |
@@ -421,7 +421,7 @@ func (s *DockerRegistryAuthHtpasswdSuite) TestPullWithExternalAuthLoginWithSchem |
| 421 | 421 |
dockerCmd(c, "--config", tmp, "push", repoName) |
| 422 | 422 |
|
| 423 | 423 |
dockerCmd(c, "--config", tmp, "logout", privateRegistryURL) |
| 424 |
- dockerCmd(c, "--config", tmp, "login", "-u", s.reg.username, "-p", s.reg.password, "https://"+privateRegistryURL) |
|
| 424 |
+ dockerCmd(c, "--config", tmp, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), "https://"+privateRegistryURL) |
|
| 425 | 425 |
dockerCmd(c, "--config", tmp, "pull", repoName) |
| 426 | 426 |
|
| 427 | 427 |
// likewise push should work |
| ... | ... |
@@ -456,7 +456,7 @@ func (s *DockerRegistryAuthHtpasswdSuite) TestPullWithExternalAuth(c *check.C) {
|
| 456 | 456 |
err = ioutil.WriteFile(configPath, []byte(externalAuthConfig), 0644) |
| 457 | 457 |
c.Assert(err, checker.IsNil) |
| 458 | 458 |
|
| 459 |
- dockerCmd(c, "--config", tmp, "login", "-u", s.reg.username, "-p", s.reg.password, privateRegistryURL) |
|
| 459 |
+ dockerCmd(c, "--config", tmp, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL) |
|
| 460 | 460 |
|
| 461 | 461 |
b, err := ioutil.ReadFile(configPath) |
| 462 | 462 |
c.Assert(err, checker.IsNil) |
| ... | ... |
@@ -5,6 +5,7 @@ import ( |
| 5 | 5 |
"net/http" |
| 6 | 6 |
"regexp" |
| 7 | 7 |
|
| 8 |
+ "github.com/docker/docker/integration-cli/registry" |
|
| 8 | 9 |
"github.com/go-check/check" |
| 9 | 10 |
) |
| 10 | 11 |
|
| ... | ... |
@@ -46,8 +47,8 @@ func regexpCheckUA(c *check.C, ua string) {
|
| 46 | 46 |
c.Assert(bMatchUpstreamUA, check.Equals, true, check.Commentf("(Upstream) Docker Client User-Agent malformed"))
|
| 47 | 47 |
} |
| 48 | 48 |
|
| 49 |
-func registerUserAgentHandler(reg *testRegistry, result *string) {
|
|
| 50 |
- reg.registerHandler("/v2/", func(w http.ResponseWriter, r *http.Request) {
|
|
| 49 |
+func registerUserAgentHandler(reg *registry.Mock, result *string) {
|
|
| 50 |
+ reg.RegisterHandler("/v2/", func(w http.ResponseWriter, r *http.Request) {
|
|
| 51 | 51 |
w.WriteHeader(404) |
| 52 | 52 |
var ua string |
| 53 | 53 |
for k, v := range r.Header {
|
| ... | ... |
@@ -70,30 +71,30 @@ func (s *DockerRegistrySuite) TestUserAgentPassThrough(c *check.C) {
|
| 70 | 70 |
loginUA string |
| 71 | 71 |
) |
| 72 | 72 |
|
| 73 |
- buildReg, err := newTestRegistry(c) |
|
| 73 |
+ buildReg, err := registry.NewMock(c) |
|
| 74 | 74 |
c.Assert(err, check.IsNil) |
| 75 | 75 |
registerUserAgentHandler(buildReg, &buildUA) |
| 76 |
- buildRepoName := fmt.Sprintf("%s/busybox", buildReg.hostport)
|
|
| 76 |
+ buildRepoName := fmt.Sprintf("%s/busybox", buildReg.URL())
|
|
| 77 | 77 |
|
| 78 |
- pullReg, err := newTestRegistry(c) |
|
| 78 |
+ pullReg, err := registry.NewMock(c) |
|
| 79 | 79 |
c.Assert(err, check.IsNil) |
| 80 | 80 |
registerUserAgentHandler(pullReg, &pullUA) |
| 81 |
- pullRepoName := fmt.Sprintf("%s/busybox", pullReg.hostport)
|
|
| 81 |
+ pullRepoName := fmt.Sprintf("%s/busybox", pullReg.URL())
|
|
| 82 | 82 |
|
| 83 |
- pushReg, err := newTestRegistry(c) |
|
| 83 |
+ pushReg, err := registry.NewMock(c) |
|
| 84 | 84 |
c.Assert(err, check.IsNil) |
| 85 | 85 |
registerUserAgentHandler(pushReg, &pushUA) |
| 86 |
- pushRepoName := fmt.Sprintf("%s/busybox", pushReg.hostport)
|
|
| 86 |
+ pushRepoName := fmt.Sprintf("%s/busybox", pushReg.URL())
|
|
| 87 | 87 |
|
| 88 |
- loginReg, err := newTestRegistry(c) |
|
| 88 |
+ loginReg, err := registry.NewMock(c) |
|
| 89 | 89 |
c.Assert(err, check.IsNil) |
| 90 | 90 |
registerUserAgentHandler(loginReg, &loginUA) |
| 91 | 91 |
|
| 92 | 92 |
s.d.Start(c, |
| 93 |
- "--insecure-registry", buildReg.hostport, |
|
| 94 |
- "--insecure-registry", pullReg.hostport, |
|
| 95 |
- "--insecure-registry", pushReg.hostport, |
|
| 96 |
- "--insecure-registry", loginReg.hostport, |
|
| 93 |
+ "--insecure-registry", buildReg.URL(), |
|
| 94 |
+ "--insecure-registry", pullReg.URL(), |
|
| 95 |
+ "--insecure-registry", pushReg.URL(), |
|
| 96 |
+ "--insecure-registry", loginReg.URL(), |
|
| 97 | 97 |
"--disable-legacy-registry=true") |
| 98 | 98 |
|
| 99 | 99 |
dockerfileName, cleanup1, err := makefile(fmt.Sprintf("FROM %s", buildRepoName))
|
| ... | ... |
@@ -102,7 +103,7 @@ func (s *DockerRegistrySuite) TestUserAgentPassThrough(c *check.C) {
|
| 102 | 102 |
s.d.Cmd("build", "--file", dockerfileName, ".")
|
| 103 | 103 |
regexpCheckUA(c, buildUA) |
| 104 | 104 |
|
| 105 |
- s.d.Cmd("login", "-u", "richard", "-p", "testtest", loginReg.hostport)
|
|
| 105 |
+ s.d.Cmd("login", "-u", "richard", "-p", "testtest", loginReg.URL())
|
|
| 106 | 106 |
regexpCheckUA(c, loginUA) |
| 107 | 107 |
|
| 108 | 108 |
s.d.Cmd("pull", pullRepoName)
|
| ... | ... |
@@ -6,6 +6,7 @@ import ( |
| 6 | 6 |
"net/http" |
| 7 | 7 |
"os" |
| 8 | 8 |
|
| 9 |
+ "github.com/docker/docker/integration-cli/registry" |
|
| 9 | 10 |
"github.com/go-check/check" |
| 10 | 11 |
) |
| 11 | 12 |
|
| ... | ... |
@@ -36,29 +37,29 @@ func makefile(contents string) (string, func(), error) {
|
| 36 | 36 |
// TestV2Only ensures that a daemon in v2-only mode does not |
| 37 | 37 |
// attempt to contact any v1 registry endpoints. |
| 38 | 38 |
func (s *DockerRegistrySuite) TestV2Only(c *check.C) {
|
| 39 |
- reg, err := newTestRegistry(c) |
|
| 39 |
+ reg, err := registry.NewMock(c) |
|
| 40 | 40 |
c.Assert(err, check.IsNil) |
| 41 | 41 |
|
| 42 |
- reg.registerHandler("/v2/", func(w http.ResponseWriter, r *http.Request) {
|
|
| 42 |
+ reg.RegisterHandler("/v2/", func(w http.ResponseWriter, r *http.Request) {
|
|
| 43 | 43 |
w.WriteHeader(404) |
| 44 | 44 |
}) |
| 45 | 45 |
|
| 46 |
- reg.registerHandler("/v1/.*", func(w http.ResponseWriter, r *http.Request) {
|
|
| 46 |
+ reg.RegisterHandler("/v1/.*", func(w http.ResponseWriter, r *http.Request) {
|
|
| 47 | 47 |
c.Fatal("V1 registry contacted")
|
| 48 | 48 |
}) |
| 49 | 49 |
|
| 50 |
- repoName := fmt.Sprintf("%s/busybox", reg.hostport)
|
|
| 50 |
+ repoName := fmt.Sprintf("%s/busybox", reg.URL())
|
|
| 51 | 51 |
|
| 52 |
- s.d.Start(c, "--insecure-registry", reg.hostport, "--disable-legacy-registry=true") |
|
| 52 |
+ s.d.Start(c, "--insecure-registry", reg.URL(), "--disable-legacy-registry=true") |
|
| 53 | 53 |
|
| 54 |
- dockerfileName, cleanup, err := makefile(fmt.Sprintf("FROM %s/busybox", reg.hostport))
|
|
| 54 |
+ dockerfileName, cleanup, err := makefile(fmt.Sprintf("FROM %s/busybox", reg.URL()))
|
|
| 55 | 55 |
c.Assert(err, check.IsNil, check.Commentf("Unable to create test dockerfile"))
|
| 56 | 56 |
defer cleanup() |
| 57 | 57 |
|
| 58 | 58 |
s.d.Cmd("build", "--file", dockerfileName, ".")
|
| 59 | 59 |
|
| 60 | 60 |
s.d.Cmd("run", repoName)
|
| 61 |
- s.d.Cmd("login", "-u", "richard", "-p", "testtest", "-e", "testuser@testdomain.com", reg.hostport)
|
|
| 61 |
+ s.d.Cmd("login", "-u", "richard", "-p", "testtest", "-e", "testuser@testdomain.com", reg.URL())
|
|
| 62 | 62 |
s.d.Cmd("tag", "busybox", repoName)
|
| 63 | 63 |
s.d.Cmd("push", repoName)
|
| 64 | 64 |
s.d.Cmd("pull", repoName)
|
| ... | ... |
@@ -68,49 +69,49 @@ func (s *DockerRegistrySuite) TestV2Only(c *check.C) {
|
| 68 | 68 |
// and ensure v1 endpoints are hit for the following operations: |
| 69 | 69 |
// login, push, pull, build & run |
| 70 | 70 |
func (s *DockerRegistrySuite) TestV1(c *check.C) {
|
| 71 |
- reg, err := newTestRegistry(c) |
|
| 71 |
+ reg, err := registry.NewMock(c) |
|
| 72 | 72 |
c.Assert(err, check.IsNil) |
| 73 | 73 |
|
| 74 | 74 |
v2Pings := 0 |
| 75 |
- reg.registerHandler("/v2/", func(w http.ResponseWriter, r *http.Request) {
|
|
| 75 |
+ reg.RegisterHandler("/v2/", func(w http.ResponseWriter, r *http.Request) {
|
|
| 76 | 76 |
v2Pings++ |
| 77 | 77 |
// V2 ping 404 causes fallback to v1 |
| 78 | 78 |
w.WriteHeader(404) |
| 79 | 79 |
}) |
| 80 | 80 |
|
| 81 | 81 |
v1Pings := 0 |
| 82 |
- reg.registerHandler("/v1/_ping", func(w http.ResponseWriter, r *http.Request) {
|
|
| 82 |
+ reg.RegisterHandler("/v1/_ping", func(w http.ResponseWriter, r *http.Request) {
|
|
| 83 | 83 |
v1Pings++ |
| 84 | 84 |
}) |
| 85 | 85 |
|
| 86 | 86 |
v1Logins := 0 |
| 87 |
- reg.registerHandler("/v1/users/", func(w http.ResponseWriter, r *http.Request) {
|
|
| 87 |
+ reg.RegisterHandler("/v1/users/", func(w http.ResponseWriter, r *http.Request) {
|
|
| 88 | 88 |
v1Logins++ |
| 89 | 89 |
}) |
| 90 | 90 |
|
| 91 | 91 |
v1Repo := 0 |
| 92 |
- reg.registerHandler("/v1/repositories/busybox/", func(w http.ResponseWriter, r *http.Request) {
|
|
| 92 |
+ reg.RegisterHandler("/v1/repositories/busybox/", func(w http.ResponseWriter, r *http.Request) {
|
|
| 93 | 93 |
v1Repo++ |
| 94 | 94 |
}) |
| 95 | 95 |
|
| 96 |
- reg.registerHandler("/v1/repositories/busybox/images", func(w http.ResponseWriter, r *http.Request) {
|
|
| 96 |
+ reg.RegisterHandler("/v1/repositories/busybox/images", func(w http.ResponseWriter, r *http.Request) {
|
|
| 97 | 97 |
v1Repo++ |
| 98 | 98 |
}) |
| 99 | 99 |
|
| 100 |
- s.d.Start(c, "--insecure-registry", reg.hostport, "--disable-legacy-registry=false") |
|
| 100 |
+ s.d.Start(c, "--insecure-registry", reg.URL(), "--disable-legacy-registry=false") |
|
| 101 | 101 |
|
| 102 |
- dockerfileName, cleanup, err := makefile(fmt.Sprintf("FROM %s/busybox", reg.hostport))
|
|
| 102 |
+ dockerfileName, cleanup, err := makefile(fmt.Sprintf("FROM %s/busybox", reg.URL()))
|
|
| 103 | 103 |
c.Assert(err, check.IsNil, check.Commentf("Unable to create test dockerfile"))
|
| 104 | 104 |
defer cleanup() |
| 105 | 105 |
|
| 106 | 106 |
s.d.Cmd("build", "--file", dockerfileName, ".")
|
| 107 | 107 |
c.Assert(v1Repo, check.Equals, 1, check.Commentf("Expected v1 repository access after build"))
|
| 108 | 108 |
|
| 109 |
- repoName := fmt.Sprintf("%s/busybox", reg.hostport)
|
|
| 109 |
+ repoName := fmt.Sprintf("%s/busybox", reg.URL())
|
|
| 110 | 110 |
s.d.Cmd("run", repoName)
|
| 111 | 111 |
c.Assert(v1Repo, check.Equals, 2, check.Commentf("Expected v1 repository access after run"))
|
| 112 | 112 |
|
| 113 |
- s.d.Cmd("login", "-u", "richard", "-p", "testtest", reg.hostport)
|
|
| 113 |
+ s.d.Cmd("login", "-u", "richard", "-p", "testtest", reg.URL())
|
|
| 114 | 114 |
c.Assert(v1Logins, check.Equals, 1, check.Commentf("Expected v1 login attempt"))
|
| 115 | 115 |
|
| 116 | 116 |
s.d.Cmd("tag", "busybox", repoName)
|
| ... | ... |
@@ -25,6 +25,7 @@ import ( |
| 25 | 25 |
volumetypes "github.com/docker/docker/api/types/volume" |
| 26 | 26 |
"github.com/docker/docker/integration-cli/checker" |
| 27 | 27 |
"github.com/docker/docker/integration-cli/daemon" |
| 28 |
+ "github.com/docker/docker/integration-cli/registry" |
|
| 28 | 29 |
"github.com/docker/docker/opts" |
| 29 | 30 |
"github.com/docker/docker/pkg/ioutils" |
| 30 | 31 |
"github.com/docker/docker/pkg/stringutils" |
| ... | ... |
@@ -1083,8 +1084,8 @@ func parseEventTime(t time.Time) string {
|
| 1083 | 1083 |
return fmt.Sprintf("%d.%09d", t.Unix(), int64(t.Nanosecond()))
|
| 1084 | 1084 |
} |
| 1085 | 1085 |
|
| 1086 |
-func setupRegistry(c *check.C, schema1 bool, auth, tokenURL string) *testRegistryV2 {
|
|
| 1087 |
- reg, err := newTestRegistryV2(c, schema1, auth, tokenURL) |
|
| 1086 |
+func setupRegistry(c *check.C, schema1 bool, auth, tokenURL string) *registry.V2 {
|
|
| 1087 |
+ reg, err := registry.NewV2(schema1, auth, tokenURL, privateRegistryURL) |
|
| 1088 | 1088 |
c.Assert(err, check.IsNil) |
| 1089 | 1089 |
|
| 1090 | 1090 |
// Wait for registry to be ready to serve requests. |
| 1091 | 1091 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,208 @@ |
| 0 |
+package registry |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "io/ioutil" |
|
| 5 |
+ "net/http" |
|
| 6 |
+ "os" |
|
| 7 |
+ "os/exec" |
|
| 8 |
+ "path/filepath" |
|
| 9 |
+ |
|
| 10 |
+ "github.com/docker/distribution/digest" |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+const ( |
|
| 14 |
+ v2binary = "registry-v2" |
|
| 15 |
+ v2binarySchema1 = "registry-v2-schema1" |
|
| 16 |
+) |
|
| 17 |
+ |
|
| 18 |
+type testingT interface {
|
|
| 19 |
+ logT |
|
| 20 |
+ Fatal(...interface{})
|
|
| 21 |
+ Fatalf(string, ...interface{})
|
|
| 22 |
+} |
|
| 23 |
+ |
|
| 24 |
+type logT interface {
|
|
| 25 |
+ Logf(string, ...interface{})
|
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+// V2 represent a registry version 2 |
|
| 29 |
+type V2 struct {
|
|
| 30 |
+ cmd *exec.Cmd |
|
| 31 |
+ registryURL string |
|
| 32 |
+ dir string |
|
| 33 |
+ auth string |
|
| 34 |
+ username string |
|
| 35 |
+ password string |
|
| 36 |
+ email string |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+// NewV2 creates a v2 registry server |
|
| 40 |
+func NewV2(schema1 bool, auth, tokenURL, registryURL string) (*V2, error) {
|
|
| 41 |
+ tmp, err := ioutil.TempDir("", "registry-test-")
|
|
| 42 |
+ if err != nil {
|
|
| 43 |
+ return nil, err |
|
| 44 |
+ } |
|
| 45 |
+ template := `version: 0.1 |
|
| 46 |
+loglevel: debug |
|
| 47 |
+storage: |
|
| 48 |
+ filesystem: |
|
| 49 |
+ rootdirectory: %s |
|
| 50 |
+http: |
|
| 51 |
+ addr: %s |
|
| 52 |
+%s` |
|
| 53 |
+ var ( |
|
| 54 |
+ authTemplate string |
|
| 55 |
+ username string |
|
| 56 |
+ password string |
|
| 57 |
+ email string |
|
| 58 |
+ ) |
|
| 59 |
+ switch auth {
|
|
| 60 |
+ case "htpasswd": |
|
| 61 |
+ htpasswdPath := filepath.Join(tmp, "htpasswd") |
|
| 62 |
+ // generated with: htpasswd -Bbn testuser testpassword |
|
| 63 |
+ userpasswd := "testuser:$2y$05$sBsSqk0OpSD1uTZkHXc4FeJ0Z70wLQdAX/82UiHuQOKbNbBrzs63m" |
|
| 64 |
+ username = "testuser" |
|
| 65 |
+ password = "testpassword" |
|
| 66 |
+ email = "test@test.org" |
|
| 67 |
+ if err := ioutil.WriteFile(htpasswdPath, []byte(userpasswd), os.FileMode(0644)); err != nil {
|
|
| 68 |
+ return nil, err |
|
| 69 |
+ } |
|
| 70 |
+ authTemplate = fmt.Sprintf(`auth: |
|
| 71 |
+ htpasswd: |
|
| 72 |
+ realm: basic-realm |
|
| 73 |
+ path: %s |
|
| 74 |
+`, htpasswdPath) |
|
| 75 |
+ case "token": |
|
| 76 |
+ authTemplate = fmt.Sprintf(`auth: |
|
| 77 |
+ token: |
|
| 78 |
+ realm: %s |
|
| 79 |
+ service: "registry" |
|
| 80 |
+ issuer: "auth-registry" |
|
| 81 |
+ rootcertbundle: "fixtures/registry/cert.pem" |
|
| 82 |
+`, tokenURL) |
|
| 83 |
+ } |
|
| 84 |
+ |
|
| 85 |
+ confPath := filepath.Join(tmp, "config.yaml") |
|
| 86 |
+ config, err := os.Create(confPath) |
|
| 87 |
+ if err != nil {
|
|
| 88 |
+ return nil, err |
|
| 89 |
+ } |
|
| 90 |
+ defer config.Close() |
|
| 91 |
+ |
|
| 92 |
+ if _, err := fmt.Fprintf(config, template, tmp, registryURL, authTemplate); err != nil {
|
|
| 93 |
+ os.RemoveAll(tmp) |
|
| 94 |
+ return nil, err |
|
| 95 |
+ } |
|
| 96 |
+ |
|
| 97 |
+ binary := v2binary |
|
| 98 |
+ if schema1 {
|
|
| 99 |
+ binary = v2binarySchema1 |
|
| 100 |
+ } |
|
| 101 |
+ cmd := exec.Command(binary, confPath) |
|
| 102 |
+ if err := cmd.Start(); err != nil {
|
|
| 103 |
+ os.RemoveAll(tmp) |
|
| 104 |
+ return nil, err |
|
| 105 |
+ } |
|
| 106 |
+ return &V2{
|
|
| 107 |
+ cmd: cmd, |
|
| 108 |
+ dir: tmp, |
|
| 109 |
+ auth: auth, |
|
| 110 |
+ username: username, |
|
| 111 |
+ password: password, |
|
| 112 |
+ email: email, |
|
| 113 |
+ registryURL: registryURL, |
|
| 114 |
+ }, nil |
|
| 115 |
+} |
|
| 116 |
+ |
|
| 117 |
+// Ping sends an http request to the current registry, and fail if it doesn't respond correctly |
|
| 118 |
+func (r *V2) Ping() error {
|
|
| 119 |
+ // We always ping through HTTP for our test registry. |
|
| 120 |
+ resp, err := http.Get(fmt.Sprintf("http://%s/v2/", r.registryURL))
|
|
| 121 |
+ if err != nil {
|
|
| 122 |
+ return err |
|
| 123 |
+ } |
|
| 124 |
+ resp.Body.Close() |
|
| 125 |
+ |
|
| 126 |
+ fail := resp.StatusCode != http.StatusOK |
|
| 127 |
+ if r.auth != "" {
|
|
| 128 |
+ // unauthorized is a _good_ status when pinging v2/ and it needs auth |
|
| 129 |
+ fail = fail && resp.StatusCode != http.StatusUnauthorized |
|
| 130 |
+ } |
|
| 131 |
+ if fail {
|
|
| 132 |
+ return fmt.Errorf("registry ping replied with an unexpected status code %d", resp.StatusCode)
|
|
| 133 |
+ } |
|
| 134 |
+ return nil |
|
| 135 |
+} |
|
| 136 |
+ |
|
| 137 |
+// Close kills the registry server |
|
| 138 |
+func (r *V2) Close() {
|
|
| 139 |
+ r.cmd.Process.Kill() |
|
| 140 |
+ os.RemoveAll(r.dir) |
|
| 141 |
+} |
|
| 142 |
+ |
|
| 143 |
+func (r *V2) getBlobFilename(blobDigest digest.Digest) string {
|
|
| 144 |
+ // Split the digest into its algorithm and hex components. |
|
| 145 |
+ dgstAlg, dgstHex := blobDigest.Algorithm(), blobDigest.Hex() |
|
| 146 |
+ |
|
| 147 |
+ // The path to the target blob data looks something like: |
|
| 148 |
+ // baseDir + "docker/registry/v2/blobs/sha256/a3/a3ed...46d4/data" |
|
| 149 |
+ return fmt.Sprintf("%s/docker/registry/v2/blobs/%s/%s/%s/data", r.dir, dgstAlg, dgstHex[:2], dgstHex)
|
|
| 150 |
+} |
|
| 151 |
+ |
|
| 152 |
+// ReadBlobContents read the file corresponding to the specified digest |
|
| 153 |
+func (r *V2) ReadBlobContents(t testingT, blobDigest digest.Digest) []byte {
|
|
| 154 |
+ // Load the target manifest blob. |
|
| 155 |
+ manifestBlob, err := ioutil.ReadFile(r.getBlobFilename(blobDigest)) |
|
| 156 |
+ if err != nil {
|
|
| 157 |
+ t.Fatalf("unable to read blob: %s", err)
|
|
| 158 |
+ } |
|
| 159 |
+ |
|
| 160 |
+ return manifestBlob |
|
| 161 |
+} |
|
| 162 |
+ |
|
| 163 |
+// WriteBlobContents write the file corresponding to the specified digest with the given content |
|
| 164 |
+func (r *V2) WriteBlobContents(t testingT, blobDigest digest.Digest, data []byte) {
|
|
| 165 |
+ if err := ioutil.WriteFile(r.getBlobFilename(blobDigest), data, os.FileMode(0644)); err != nil {
|
|
| 166 |
+ t.Fatalf("unable to write malicious data blob: %s", err)
|
|
| 167 |
+ } |
|
| 168 |
+} |
|
| 169 |
+ |
|
| 170 |
+// TempMoveBlobData moves the existing data file aside, so that we can replace it with a |
|
| 171 |
+// malicious blob of data for example. |
|
| 172 |
+func (r *V2) TempMoveBlobData(t testingT, blobDigest digest.Digest) (undo func()) {
|
|
| 173 |
+ tempFile, err := ioutil.TempFile("", "registry-temp-blob-")
|
|
| 174 |
+ if err != nil {
|
|
| 175 |
+ t.Fatalf("unable to get temporary blob file: %s", err)
|
|
| 176 |
+ } |
|
| 177 |
+ tempFile.Close() |
|
| 178 |
+ |
|
| 179 |
+ blobFilename := r.getBlobFilename(blobDigest) |
|
| 180 |
+ |
|
| 181 |
+ // Move the existing data file aside, so that we can replace it with a |
|
| 182 |
+ // another blob of data. |
|
| 183 |
+ if err := os.Rename(blobFilename, tempFile.Name()); err != nil {
|
|
| 184 |
+ os.Remove(tempFile.Name()) |
|
| 185 |
+ t.Fatalf("unable to move data blob: %s", err)
|
|
| 186 |
+ } |
|
| 187 |
+ |
|
| 188 |
+ return func() {
|
|
| 189 |
+ os.Rename(tempFile.Name(), blobFilename) |
|
| 190 |
+ os.Remove(tempFile.Name()) |
|
| 191 |
+ } |
|
| 192 |
+} |
|
| 193 |
+ |
|
| 194 |
+// Username returns the configured user name of the server |
|
| 195 |
+func (r *V2) Username() string {
|
|
| 196 |
+ return r.username |
|
| 197 |
+} |
|
| 198 |
+ |
|
| 199 |
+// Password returns the configured password of the server |
|
| 200 |
+func (r *V2) Password() string {
|
|
| 201 |
+ return r.password |
|
| 202 |
+} |
|
| 203 |
+ |
|
| 204 |
+// Path returns the path where the registry write data |
|
| 205 |
+func (r *V2) Path() string {
|
|
| 206 |
+ return filepath.Join(r.dir, "docker", "registry", "v2") |
|
| 207 |
+} |
| 0 | 208 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,61 @@ |
| 0 |
+package registry |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "net/http" |
|
| 4 |
+ "net/http/httptest" |
|
| 5 |
+ "regexp" |
|
| 6 |
+ "strings" |
|
| 7 |
+ "sync" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+type handlerFunc func(w http.ResponseWriter, r *http.Request) |
|
| 11 |
+ |
|
| 12 |
+// Mock represent a registry mock |
|
| 13 |
+type Mock struct {
|
|
| 14 |
+ server *httptest.Server |
|
| 15 |
+ hostport string |
|
| 16 |
+ handlers map[string]handlerFunc |
|
| 17 |
+ mu sync.Mutex |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+// RegisterHandler register the specified handler for the registry mock |
|
| 21 |
+func (tr *Mock) RegisterHandler(path string, h handlerFunc) {
|
|
| 22 |
+ tr.mu.Lock() |
|
| 23 |
+ defer tr.mu.Unlock() |
|
| 24 |
+ tr.handlers[path] = h |
|
| 25 |
+} |
|
| 26 |
+ |
|
| 27 |
+// NewMock creates a registry mock |
|
| 28 |
+func NewMock(t testingT) (*Mock, error) {
|
|
| 29 |
+ testReg := &Mock{handlers: make(map[string]handlerFunc)}
|
|
| 30 |
+ |
|
| 31 |
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
| 32 |
+ url := r.URL.String() |
|
| 33 |
+ |
|
| 34 |
+ var matched bool |
|
| 35 |
+ var err error |
|
| 36 |
+ for re, function := range testReg.handlers {
|
|
| 37 |
+ matched, err = regexp.MatchString(re, url) |
|
| 38 |
+ if err != nil {
|
|
| 39 |
+ t.Fatal("Error with handler regexp")
|
|
| 40 |
+ } |
|
| 41 |
+ if matched {
|
|
| 42 |
+ function(w, r) |
|
| 43 |
+ break |
|
| 44 |
+ } |
|
| 45 |
+ } |
|
| 46 |
+ |
|
| 47 |
+ if !matched {
|
|
| 48 |
+ t.Fatalf("Unable to match %s with regexp", url)
|
|
| 49 |
+ } |
|
| 50 |
+ })) |
|
| 51 |
+ |
|
| 52 |
+ testReg.server = ts |
|
| 53 |
+ testReg.hostport = strings.Replace(ts.URL, "http://", "", 1) |
|
| 54 |
+ return testReg, nil |
|
| 55 |
+} |
|
| 56 |
+ |
|
| 57 |
+// URL returns the url of the registry |
|
| 58 |
+func (tr *Mock) URL() string {
|
|
| 59 |
+ return tr.hostport |
|
| 60 |
+} |
| 0 | 61 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,12 @@ |
| 0 |
+package registry |
|
| 1 |
+ |
|
| 2 |
+import "os/exec" |
|
| 3 |
+ |
|
| 4 |
+// Hosting returns wether the host can host a registry (v2) or not |
|
| 5 |
+func Hosting() bool {
|
|
| 6 |
+ // for now registry binary is built only if we're running inside |
|
| 7 |
+ // container through `make test`. Figure that out by testing if |
|
| 8 |
+ // registry binary is in PATH. |
|
| 9 |
+ _, err := exec.LookPath(v2binary) |
|
| 10 |
+ return err == nil |
|
| 11 |
+} |
| 0 | 12 |
deleted file mode 100644 |
| ... | ... |
@@ -1,55 +0,0 @@ |
| 1 |
-package main |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "net/http" |
|
| 5 |
- "net/http/httptest" |
|
| 6 |
- "regexp" |
|
| 7 |
- "strings" |
|
| 8 |
- "sync" |
|
| 9 |
- |
|
| 10 |
- "github.com/go-check/check" |
|
| 11 |
-) |
|
| 12 |
- |
|
| 13 |
-type handlerFunc func(w http.ResponseWriter, r *http.Request) |
|
| 14 |
- |
|
| 15 |
-type testRegistry struct {
|
|
| 16 |
- server *httptest.Server |
|
| 17 |
- hostport string |
|
| 18 |
- handlers map[string]handlerFunc |
|
| 19 |
- mu sync.Mutex |
|
| 20 |
-} |
|
| 21 |
- |
|
| 22 |
-func (tr *testRegistry) registerHandler(path string, h handlerFunc) {
|
|
| 23 |
- tr.mu.Lock() |
|
| 24 |
- defer tr.mu.Unlock() |
|
| 25 |
- tr.handlers[path] = h |
|
| 26 |
-} |
|
| 27 |
- |
|
| 28 |
-func newTestRegistry(c *check.C) (*testRegistry, error) {
|
|
| 29 |
- testReg := &testRegistry{handlers: make(map[string]handlerFunc)}
|
|
| 30 |
- |
|
| 31 |
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
| 32 |
- url := r.URL.String() |
|
| 33 |
- |
|
| 34 |
- var matched bool |
|
| 35 |
- var err error |
|
| 36 |
- for re, function := range testReg.handlers {
|
|
| 37 |
- matched, err = regexp.MatchString(re, url) |
|
| 38 |
- if err != nil {
|
|
| 39 |
- c.Fatal("Error with handler regexp")
|
|
| 40 |
- } |
|
| 41 |
- if matched {
|
|
| 42 |
- function(w, r) |
|
| 43 |
- break |
|
| 44 |
- } |
|
| 45 |
- } |
|
| 46 |
- |
|
| 47 |
- if !matched {
|
|
| 48 |
- c.Fatalf("Unable to match %s with regexp", url)
|
|
| 49 |
- } |
|
| 50 |
- })) |
|
| 51 |
- |
|
| 52 |
- testReg.server = ts |
|
| 53 |
- testReg.hostport = strings.Replace(ts.URL, "http://", "", 1) |
|
| 54 |
- return testReg, nil |
|
| 55 |
-} |
| 56 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,177 +0,0 @@ |
| 1 |
-package main |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "io/ioutil" |
|
| 6 |
- "net/http" |
|
| 7 |
- "os" |
|
| 8 |
- "os/exec" |
|
| 9 |
- "path/filepath" |
|
| 10 |
- |
|
| 11 |
- "github.com/docker/distribution/digest" |
|
| 12 |
- "github.com/go-check/check" |
|
| 13 |
-) |
|
| 14 |
- |
|
| 15 |
-const ( |
|
| 16 |
- v2binary = "registry-v2" |
|
| 17 |
- v2binarySchema1 = "registry-v2-schema1" |
|
| 18 |
-) |
|
| 19 |
- |
|
| 20 |
-type testRegistryV2 struct {
|
|
| 21 |
- cmd *exec.Cmd |
|
| 22 |
- dir string |
|
| 23 |
- auth string |
|
| 24 |
- username string |
|
| 25 |
- password string |
|
| 26 |
- email string |
|
| 27 |
-} |
|
| 28 |
- |
|
| 29 |
-func newTestRegistryV2(c *check.C, schema1 bool, auth, tokenURL string) (*testRegistryV2, error) {
|
|
| 30 |
- tmp, err := ioutil.TempDir("", "registry-test-")
|
|
| 31 |
- if err != nil {
|
|
| 32 |
- return nil, err |
|
| 33 |
- } |
|
| 34 |
- template := `version: 0.1 |
|
| 35 |
-loglevel: debug |
|
| 36 |
-storage: |
|
| 37 |
- filesystem: |
|
| 38 |
- rootdirectory: %s |
|
| 39 |
-http: |
|
| 40 |
- addr: %s |
|
| 41 |
-%s` |
|
| 42 |
- var ( |
|
| 43 |
- authTemplate string |
|
| 44 |
- username string |
|
| 45 |
- password string |
|
| 46 |
- email string |
|
| 47 |
- ) |
|
| 48 |
- switch auth {
|
|
| 49 |
- case "htpasswd": |
|
| 50 |
- htpasswdPath := filepath.Join(tmp, "htpasswd") |
|
| 51 |
- // generated with: htpasswd -Bbn testuser testpassword |
|
| 52 |
- userpasswd := "testuser:$2y$05$sBsSqk0OpSD1uTZkHXc4FeJ0Z70wLQdAX/82UiHuQOKbNbBrzs63m" |
|
| 53 |
- username = "testuser" |
|
| 54 |
- password = "testpassword" |
|
| 55 |
- email = "test@test.org" |
|
| 56 |
- if err := ioutil.WriteFile(htpasswdPath, []byte(userpasswd), os.FileMode(0644)); err != nil {
|
|
| 57 |
- return nil, err |
|
| 58 |
- } |
|
| 59 |
- authTemplate = fmt.Sprintf(`auth: |
|
| 60 |
- htpasswd: |
|
| 61 |
- realm: basic-realm |
|
| 62 |
- path: %s |
|
| 63 |
-`, htpasswdPath) |
|
| 64 |
- case "token": |
|
| 65 |
- authTemplate = fmt.Sprintf(`auth: |
|
| 66 |
- token: |
|
| 67 |
- realm: %s |
|
| 68 |
- service: "registry" |
|
| 69 |
- issuer: "auth-registry" |
|
| 70 |
- rootcertbundle: "fixtures/registry/cert.pem" |
|
| 71 |
-`, tokenURL) |
|
| 72 |
- } |
|
| 73 |
- |
|
| 74 |
- confPath := filepath.Join(tmp, "config.yaml") |
|
| 75 |
- config, err := os.Create(confPath) |
|
| 76 |
- if err != nil {
|
|
| 77 |
- return nil, err |
|
| 78 |
- } |
|
| 79 |
- defer config.Close() |
|
| 80 |
- |
|
| 81 |
- if _, err := fmt.Fprintf(config, template, tmp, privateRegistryURL, authTemplate); err != nil {
|
|
| 82 |
- os.RemoveAll(tmp) |
|
| 83 |
- return nil, err |
|
| 84 |
- } |
|
| 85 |
- |
|
| 86 |
- binary := v2binary |
|
| 87 |
- if schema1 {
|
|
| 88 |
- binary = v2binarySchema1 |
|
| 89 |
- } |
|
| 90 |
- cmd := exec.Command(binary, confPath) |
|
| 91 |
- if err := cmd.Start(); err != nil {
|
|
| 92 |
- os.RemoveAll(tmp) |
|
| 93 |
- if os.IsNotExist(err) {
|
|
| 94 |
- c.Skip(err.Error()) |
|
| 95 |
- } |
|
| 96 |
- return nil, err |
|
| 97 |
- } |
|
| 98 |
- return &testRegistryV2{
|
|
| 99 |
- cmd: cmd, |
|
| 100 |
- dir: tmp, |
|
| 101 |
- auth: auth, |
|
| 102 |
- username: username, |
|
| 103 |
- password: password, |
|
| 104 |
- email: email, |
|
| 105 |
- }, nil |
|
| 106 |
-} |
|
| 107 |
- |
|
| 108 |
-func (t *testRegistryV2) Ping() error {
|
|
| 109 |
- // We always ping through HTTP for our test registry. |
|
| 110 |
- resp, err := http.Get(fmt.Sprintf("http://%s/v2/", privateRegistryURL))
|
|
| 111 |
- if err != nil {
|
|
| 112 |
- return err |
|
| 113 |
- } |
|
| 114 |
- resp.Body.Close() |
|
| 115 |
- |
|
| 116 |
- fail := resp.StatusCode != http.StatusOK |
|
| 117 |
- if t.auth != "" {
|
|
| 118 |
- // unauthorized is a _good_ status when pinging v2/ and it needs auth |
|
| 119 |
- fail = fail && resp.StatusCode != http.StatusUnauthorized |
|
| 120 |
- } |
|
| 121 |
- if fail {
|
|
| 122 |
- return fmt.Errorf("registry ping replied with an unexpected status code %d", resp.StatusCode)
|
|
| 123 |
- } |
|
| 124 |
- return nil |
|
| 125 |
-} |
|
| 126 |
- |
|
| 127 |
-func (t *testRegistryV2) Close() {
|
|
| 128 |
- t.cmd.Process.Kill() |
|
| 129 |
- os.RemoveAll(t.dir) |
|
| 130 |
-} |
|
| 131 |
- |
|
| 132 |
-func (t *testRegistryV2) getBlobFilename(blobDigest digest.Digest) string {
|
|
| 133 |
- // Split the digest into its algorithm and hex components. |
|
| 134 |
- dgstAlg, dgstHex := blobDigest.Algorithm(), blobDigest.Hex() |
|
| 135 |
- |
|
| 136 |
- // The path to the target blob data looks something like: |
|
| 137 |
- // baseDir + "docker/registry/v2/blobs/sha256/a3/a3ed...46d4/data" |
|
| 138 |
- return fmt.Sprintf("%s/docker/registry/v2/blobs/%s/%s/%s/data", t.dir, dgstAlg, dgstHex[:2], dgstHex)
|
|
| 139 |
-} |
|
| 140 |
- |
|
| 141 |
-func (t *testRegistryV2) readBlobContents(c *check.C, blobDigest digest.Digest) []byte {
|
|
| 142 |
- // Load the target manifest blob. |
|
| 143 |
- manifestBlob, err := ioutil.ReadFile(t.getBlobFilename(blobDigest)) |
|
| 144 |
- if err != nil {
|
|
| 145 |
- c.Fatalf("unable to read blob: %s", err)
|
|
| 146 |
- } |
|
| 147 |
- |
|
| 148 |
- return manifestBlob |
|
| 149 |
-} |
|
| 150 |
- |
|
| 151 |
-func (t *testRegistryV2) writeBlobContents(c *check.C, blobDigest digest.Digest, data []byte) {
|
|
| 152 |
- if err := ioutil.WriteFile(t.getBlobFilename(blobDigest), data, os.FileMode(0644)); err != nil {
|
|
| 153 |
- c.Fatalf("unable to write malicious data blob: %s", err)
|
|
| 154 |
- } |
|
| 155 |
-} |
|
| 156 |
- |
|
| 157 |
-func (t *testRegistryV2) tempMoveBlobData(c *check.C, blobDigest digest.Digest) (undo func()) {
|
|
| 158 |
- tempFile, err := ioutil.TempFile("", "registry-temp-blob-")
|
|
| 159 |
- if err != nil {
|
|
| 160 |
- c.Fatalf("unable to get temporary blob file: %s", err)
|
|
| 161 |
- } |
|
| 162 |
- tempFile.Close() |
|
| 163 |
- |
|
| 164 |
- blobFilename := t.getBlobFilename(blobDigest) |
|
| 165 |
- |
|
| 166 |
- // Move the existing data file aside, so that we can replace it with a |
|
| 167 |
- // another blob of data. |
|
| 168 |
- if err := os.Rename(blobFilename, tempFile.Name()); err != nil {
|
|
| 169 |
- os.Remove(tempFile.Name()) |
|
| 170 |
- c.Fatalf("unable to move data blob: %s", err)
|
|
| 171 |
- } |
|
| 172 |
- |
|
| 173 |
- return func() {
|
|
| 174 |
- os.Rename(tempFile.Name(), blobFilename) |
|
| 175 |
- os.Remove(tempFile.Name()) |
|
| 176 |
- } |
|
| 177 |
-} |
| ... | ... |
@@ -105,14 +105,6 @@ func Apparmor() bool {
|
| 105 | 105 |
return err == nil && len(buf) > 1 && buf[0] == 'Y' |
| 106 | 106 |
} |
| 107 | 107 |
|
| 108 |
-func RegistryHosting() bool {
|
|
| 109 |
- // for now registry binary is built only if we're running inside |
|
| 110 |
- // container through `make test`. Figure that out by testing if |
|
| 111 |
- // registry binary is in PATH. |
|
| 112 |
- _, err := exec.LookPath(v2binary) |
|
| 113 |
- return err == nil |
|
| 114 |
-} |
|
| 115 |
- |
|
| 116 | 108 |
func NotaryHosting() bool {
|
| 117 | 109 |
// for now notary binary is built only if we're running inside |
| 118 | 110 |
// container through `make test`. Figure that out by testing if |