registry.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "net/http"
  6. "os"
  7. "os/exec"
  8. "path/filepath"
  9. "github.com/docker/distribution/digest"
  10. "github.com/go-check/check"
  11. )
  12. const (
  13. v2binary = "registry-v2"
  14. v2binarySchema1 = "registry-v2-schema1"
  15. )
  16. type testRegistryV2 struct {
  17. cmd *exec.Cmd
  18. dir string
  19. auth string
  20. username string
  21. password string
  22. email string
  23. }
  24. func newTestRegistryV2(c *check.C, schema1 bool, auth, tokenURL string) (*testRegistryV2, error) {
  25. tmp, err := ioutil.TempDir("", "registry-test-")
  26. if err != nil {
  27. return nil, err
  28. }
  29. template := `version: 0.1
  30. loglevel: debug
  31. storage:
  32. filesystem:
  33. rootdirectory: %s
  34. http:
  35. addr: %s
  36. %s`
  37. var (
  38. authTemplate string
  39. username string
  40. password string
  41. email string
  42. )
  43. switch auth {
  44. case "htpasswd":
  45. htpasswdPath := filepath.Join(tmp, "htpasswd")
  46. // generated with: htpasswd -Bbn testuser testpassword
  47. userpasswd := "testuser:$2y$05$sBsSqk0OpSD1uTZkHXc4FeJ0Z70wLQdAX/82UiHuQOKbNbBrzs63m"
  48. username = "testuser"
  49. password = "testpassword"
  50. email = "test@test.org"
  51. if err := ioutil.WriteFile(htpasswdPath, []byte(userpasswd), os.FileMode(0644)); err != nil {
  52. return nil, err
  53. }
  54. authTemplate = fmt.Sprintf(`auth:
  55. htpasswd:
  56. realm: basic-realm
  57. path: %s
  58. `, htpasswdPath)
  59. case "token":
  60. authTemplate = fmt.Sprintf(`auth:
  61. token:
  62. realm: %s
  63. service: "registry"
  64. issuer: "auth-registry"
  65. rootcertbundle: "fixtures/registry/cert.pem"
  66. `, tokenURL)
  67. }
  68. confPath := filepath.Join(tmp, "config.yaml")
  69. config, err := os.Create(confPath)
  70. if err != nil {
  71. return nil, err
  72. }
  73. defer config.Close()
  74. if _, err := fmt.Fprintf(config, template, tmp, privateRegistryURL, authTemplate); err != nil {
  75. os.RemoveAll(tmp)
  76. return nil, err
  77. }
  78. binary := v2binary
  79. if schema1 {
  80. binary = v2binarySchema1
  81. }
  82. cmd := exec.Command(binary, confPath)
  83. if err := cmd.Start(); err != nil {
  84. os.RemoveAll(tmp)
  85. if os.IsNotExist(err) {
  86. c.Skip(err.Error())
  87. }
  88. return nil, err
  89. }
  90. return &testRegistryV2{
  91. cmd: cmd,
  92. dir: tmp,
  93. auth: auth,
  94. username: username,
  95. password: password,
  96. email: email,
  97. }, nil
  98. }
  99. func (t *testRegistryV2) Ping() error {
  100. // We always ping through HTTP for our test registry.
  101. resp, err := http.Get(fmt.Sprintf("http://%s/v2/", privateRegistryURL))
  102. if err != nil {
  103. return err
  104. }
  105. resp.Body.Close()
  106. fail := resp.StatusCode != http.StatusOK
  107. if t.auth != "" {
  108. // unauthorized is a _good_ status when pinging v2/ and it needs auth
  109. fail = fail && resp.StatusCode != http.StatusUnauthorized
  110. }
  111. if fail {
  112. return fmt.Errorf("registry ping replied with an unexpected status code %d", resp.StatusCode)
  113. }
  114. return nil
  115. }
  116. func (t *testRegistryV2) Close() {
  117. t.cmd.Process.Kill()
  118. os.RemoveAll(t.dir)
  119. }
  120. func (t *testRegistryV2) getBlobFilename(blobDigest digest.Digest) string {
  121. // Split the digest into its algorithm and hex components.
  122. dgstAlg, dgstHex := blobDigest.Algorithm(), blobDigest.Hex()
  123. // The path to the target blob data looks something like:
  124. // baseDir + "docker/registry/v2/blobs/sha256/a3/a3ed...46d4/data"
  125. return fmt.Sprintf("%s/docker/registry/v2/blobs/%s/%s/%s/data", t.dir, dgstAlg, dgstHex[:2], dgstHex)
  126. }
  127. func (t *testRegistryV2) readBlobContents(c *check.C, blobDigest digest.Digest) []byte {
  128. // Load the target manifest blob.
  129. manifestBlob, err := ioutil.ReadFile(t.getBlobFilename(blobDigest))
  130. if err != nil {
  131. c.Fatalf("unable to read blob: %s", err)
  132. }
  133. return manifestBlob
  134. }
  135. func (t *testRegistryV2) writeBlobContents(c *check.C, blobDigest digest.Digest, data []byte) {
  136. if err := ioutil.WriteFile(t.getBlobFilename(blobDigest), data, os.FileMode(0644)); err != nil {
  137. c.Fatalf("unable to write malicious data blob: %s", err)
  138. }
  139. }
  140. func (t *testRegistryV2) tempMoveBlobData(c *check.C, blobDigest digest.Digest) (undo func()) {
  141. tempFile, err := ioutil.TempFile("", "registry-temp-blob-")
  142. if err != nil {
  143. c.Fatalf("unable to get temporary blob file: %s", err)
  144. }
  145. tempFile.Close()
  146. blobFilename := t.getBlobFilename(blobDigest)
  147. // Move the existing data file aside, so that we can replace it with a
  148. // another blob of data.
  149. if err := os.Rename(blobFilename, tempFile.Name()); err != nil {
  150. os.Remove(tempFile.Name())
  151. c.Fatalf("unable to move data blob: %s", err)
  152. }
  153. return func() {
  154. os.Rename(tempFile.Name(), blobFilename)
  155. os.Remove(tempFile.Name())
  156. }
  157. }