github.com/ncdc/docker@v0.10.1-0.20160129113957-6c6729ef5b74/integration-cli/registry.go (about) 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 username string 24 password string 25 email string 26 } 27 28 func newTestRegistryV2(c *check.C, schema1, auth bool) (*testRegistryV2, error) { 29 tmp, err := ioutil.TempDir("", "registry-test-") 30 if err != nil { 31 return nil, err 32 } 33 template := `version: 0.1 34 loglevel: debug 35 storage: 36 filesystem: 37 rootdirectory: %s 38 http: 39 addr: %s 40 %s` 41 var ( 42 htpasswd string 43 username string 44 password string 45 email string 46 ) 47 if auth { 48 htpasswdPath := filepath.Join(tmp, "htpasswd") 49 // generated with: htpasswd -Bbn testuser testpassword 50 userpasswd := "testuser:$2y$05$sBsSqk0OpSD1uTZkHXc4FeJ0Z70wLQdAX/82UiHuQOKbNbBrzs63m" 51 username = "testuser" 52 password = "testpassword" 53 email = "test@test.org" 54 if err := ioutil.WriteFile(htpasswdPath, []byte(userpasswd), os.FileMode(0644)); err != nil { 55 return nil, err 56 } 57 htpasswd = fmt.Sprintf(`auth: 58 htpasswd: 59 realm: basic-realm 60 path: %s 61 `, htpasswdPath) 62 } 63 64 confPath := filepath.Join(tmp, "config.yaml") 65 config, err := os.Create(confPath) 66 if err != nil { 67 return nil, err 68 } 69 if _, err := fmt.Fprintf(config, template, tmp, privateRegistryURL, htpasswd); err != nil { 70 os.RemoveAll(tmp) 71 return nil, err 72 } 73 74 binary := v2binary 75 if schema1 { 76 binary = v2binarySchema1 77 } 78 cmd := exec.Command(binary, confPath) 79 if err := cmd.Start(); err != nil { 80 os.RemoveAll(tmp) 81 if os.IsNotExist(err) { 82 c.Skip(err.Error()) 83 } 84 return nil, err 85 } 86 return &testRegistryV2{ 87 cmd: cmd, 88 dir: tmp, 89 username: username, 90 password: password, 91 email: email, 92 }, nil 93 } 94 95 func (t *testRegistryV2) Ping() error { 96 // We always ping through HTTP for our test registry. 97 resp, err := http.Get(fmt.Sprintf("http://%s/v2/", privateRegistryURL)) 98 if err != nil { 99 return err 100 } 101 resp.Body.Close() 102 103 fail := resp.StatusCode != http.StatusOK 104 if t.username != "" { 105 // unauthorized is a _good_ status when pinging v2/ and it needs auth 106 fail = fail && resp.StatusCode != http.StatusUnauthorized 107 } 108 if fail { 109 return fmt.Errorf("registry ping replied with an unexpected status code %d", resp.StatusCode) 110 } 111 return nil 112 } 113 114 func (t *testRegistryV2) Close() { 115 t.cmd.Process.Kill() 116 os.RemoveAll(t.dir) 117 } 118 119 func (t *testRegistryV2) getBlobFilename(blobDigest digest.Digest) string { 120 // Split the digest into it's algorithm and hex components. 121 dgstAlg, dgstHex := blobDigest.Algorithm(), blobDigest.Hex() 122 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 128 func (t *testRegistryV2) readBlobContents(c *check.C, blobDigest digest.Digest) []byte { 129 // Load the target manifest blob. 130 manifestBlob, err := ioutil.ReadFile(t.getBlobFilename(blobDigest)) 131 if err != nil { 132 c.Fatalf("unable to read blob: %s", err) 133 } 134 135 return manifestBlob 136 } 137 138 func (t *testRegistryV2) writeBlobContents(c *check.C, blobDigest digest.Digest, data []byte) { 139 if err := ioutil.WriteFile(t.getBlobFilename(blobDigest), data, os.FileMode(0644)); err != nil { 140 c.Fatalf("unable to write malicious data blob: %s", err) 141 } 142 } 143 144 func (t *testRegistryV2) tempMoveBlobData(c *check.C, blobDigest digest.Digest) (undo func()) { 145 tempFile, err := ioutil.TempFile("", "registry-temp-blob-") 146 if err != nil { 147 c.Fatalf("unable to get temporary blob file: %s", err) 148 } 149 tempFile.Close() 150 151 blobFilename := t.getBlobFilename(blobDigest) 152 153 // Move the existing data file aside, so that we can replace it with a 154 // another blob of data. 155 if err := os.Rename(blobFilename, tempFile.Name()); err != nil { 156 os.Remove(tempFile.Name()) 157 c.Fatalf("unable to move data blob: %s", err) 158 } 159 160 return func() { 161 os.Rename(tempFile.Name(), blobFilename) 162 os.Remove(tempFile.Name()) 163 } 164 }