github.com/lazyboychen7/engine@v17.12.1-ce-rc2+incompatible/integration-cli/registry/registry.go (about) 1 package registry 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "net/http" 7 "os" 8 "os/exec" 9 "path/filepath" 10 11 "github.com/opencontainers/go-digest" 12 ) 13 14 const ( 15 v2binary = "registry-v2" 16 v2binarySchema1 = "registry-v2-schema1" 17 ) 18 19 type testingT interface { 20 logT 21 Fatal(...interface{}) 22 Fatalf(string, ...interface{}) 23 } 24 25 type logT interface { 26 Logf(string, ...interface{}) 27 } 28 29 // V2 represent a registry version 2 30 type V2 struct { 31 cmd *exec.Cmd 32 registryURL string 33 dir string 34 auth string 35 username string 36 password string 37 email string 38 } 39 40 // NewV2 creates a v2 registry server 41 func NewV2(schema1 bool, auth, tokenURL, registryURL string) (*V2, error) { 42 tmp, err := ioutil.TempDir("", "registry-test-") 43 if err != nil { 44 return nil, err 45 } 46 template := `version: 0.1 47 loglevel: debug 48 storage: 49 filesystem: 50 rootdirectory: %s 51 http: 52 addr: %s 53 %s` 54 var ( 55 authTemplate string 56 username string 57 password string 58 email string 59 ) 60 switch auth { 61 case "htpasswd": 62 htpasswdPath := filepath.Join(tmp, "htpasswd") 63 // generated with: htpasswd -Bbn testuser testpassword 64 userpasswd := "testuser:$2y$05$sBsSqk0OpSD1uTZkHXc4FeJ0Z70wLQdAX/82UiHuQOKbNbBrzs63m" 65 username = "testuser" 66 password = "testpassword" 67 email = "test@test.org" 68 if err := ioutil.WriteFile(htpasswdPath, []byte(userpasswd), os.FileMode(0644)); err != nil { 69 return nil, err 70 } 71 authTemplate = fmt.Sprintf(`auth: 72 htpasswd: 73 realm: basic-realm 74 path: %s 75 `, htpasswdPath) 76 case "token": 77 authTemplate = fmt.Sprintf(`auth: 78 token: 79 realm: %s 80 service: "registry" 81 issuer: "auth-registry" 82 rootcertbundle: "fixtures/registry/cert.pem" 83 `, tokenURL) 84 } 85 86 confPath := filepath.Join(tmp, "config.yaml") 87 config, err := os.Create(confPath) 88 if err != nil { 89 return nil, err 90 } 91 defer config.Close() 92 93 if _, err := fmt.Fprintf(config, template, tmp, registryURL, authTemplate); err != nil { 94 os.RemoveAll(tmp) 95 return nil, err 96 } 97 98 binary := v2binary 99 if schema1 { 100 binary = v2binarySchema1 101 } 102 cmd := exec.Command(binary, confPath) 103 if err := cmd.Start(); err != nil { 104 os.RemoveAll(tmp) 105 return nil, err 106 } 107 return &V2{ 108 cmd: cmd, 109 dir: tmp, 110 auth: auth, 111 username: username, 112 password: password, 113 email: email, 114 registryURL: registryURL, 115 }, nil 116 } 117 118 // Ping sends an http request to the current registry, and fail if it doesn't respond correctly 119 func (r *V2) Ping() error { 120 // We always ping through HTTP for our test registry. 121 resp, err := http.Get(fmt.Sprintf("http://%s/v2/", r.registryURL)) 122 if err != nil { 123 return err 124 } 125 resp.Body.Close() 126 127 fail := resp.StatusCode != http.StatusOK 128 if r.auth != "" { 129 // unauthorized is a _good_ status when pinging v2/ and it needs auth 130 fail = fail && resp.StatusCode != http.StatusUnauthorized 131 } 132 if fail { 133 return fmt.Errorf("registry ping replied with an unexpected status code %d", resp.StatusCode) 134 } 135 return nil 136 } 137 138 // Close kills the registry server 139 func (r *V2) Close() { 140 r.cmd.Process.Kill() 141 r.cmd.Process.Wait() 142 os.RemoveAll(r.dir) 143 } 144 145 func (r *V2) getBlobFilename(blobDigest digest.Digest) string { 146 // Split the digest into its algorithm and hex components. 147 dgstAlg, dgstHex := blobDigest.Algorithm(), blobDigest.Hex() 148 149 // The path to the target blob data looks something like: 150 // baseDir + "docker/registry/v2/blobs/sha256/a3/a3ed...46d4/data" 151 return fmt.Sprintf("%s/docker/registry/v2/blobs/%s/%s/%s/data", r.dir, dgstAlg, dgstHex[:2], dgstHex) 152 } 153 154 // ReadBlobContents read the file corresponding to the specified digest 155 func (r *V2) ReadBlobContents(t testingT, blobDigest digest.Digest) []byte { 156 // Load the target manifest blob. 157 manifestBlob, err := ioutil.ReadFile(r.getBlobFilename(blobDigest)) 158 if err != nil { 159 t.Fatalf("unable to read blob: %s", err) 160 } 161 162 return manifestBlob 163 } 164 165 // WriteBlobContents write the file corresponding to the specified digest with the given content 166 func (r *V2) WriteBlobContents(t testingT, blobDigest digest.Digest, data []byte) { 167 if err := ioutil.WriteFile(r.getBlobFilename(blobDigest), data, os.FileMode(0644)); err != nil { 168 t.Fatalf("unable to write malicious data blob: %s", err) 169 } 170 } 171 172 // TempMoveBlobData moves the existing data file aside, so that we can replace it with a 173 // malicious blob of data for example. 174 func (r *V2) TempMoveBlobData(t testingT, blobDigest digest.Digest) (undo func()) { 175 tempFile, err := ioutil.TempFile("", "registry-temp-blob-") 176 if err != nil { 177 t.Fatalf("unable to get temporary blob file: %s", err) 178 } 179 tempFile.Close() 180 181 blobFilename := r.getBlobFilename(blobDigest) 182 183 // Move the existing data file aside, so that we can replace it with a 184 // another blob of data. 185 if err := os.Rename(blobFilename, tempFile.Name()); err != nil { 186 os.Remove(tempFile.Name()) 187 t.Fatalf("unable to move data blob: %s", err) 188 } 189 190 return func() { 191 os.Rename(tempFile.Name(), blobFilename) 192 os.Remove(tempFile.Name()) 193 } 194 } 195 196 // Username returns the configured user name of the server 197 func (r *V2) Username() string { 198 return r.username 199 } 200 201 // Password returns the configured password of the server 202 func (r *V2) Password() string { 203 return r.password 204 } 205 206 // Email returns the configured email of the server 207 func (r *V2) Email() string { 208 return r.email 209 } 210 211 // Path returns the path where the registry write data 212 func (r *V2) Path() string { 213 return filepath.Join(r.dir, "docker", "registry", "v2") 214 }