integration-cli/registry.go
2fc2862a
 package main
 
 import (
 	"fmt"
 	"io/ioutil"
de8ea06d
 	"net/http"
2fc2862a
 	"os"
 	"os/exec"
 	"path/filepath"
dc944ea7
 
de52a3bc
 	"github.com/docker/distribution/digest"
dc944ea7
 	"github.com/go-check/check"
2fc2862a
 )
 
1fa2e311
 const (
 	v2binary        = "registry-v2"
 	v2binarySchema1 = "registry-v2-schema1"
 )
2fc2862a
 
 type testRegistryV2 struct {
011b4f01
 	cmd      *exec.Cmd
 	dir      string
1b5c2e1d
 	auth     string
011b4f01
 	username string
 	password string
 	email    string
2fc2862a
 }
 
1b5c2e1d
 func newTestRegistryV2(c *check.C, schema1 bool, auth, tokenURL string) (*testRegistryV2, error) {
011b4f01
 	tmp, err := ioutil.TempDir("", "registry-test-")
 	if err != nil {
 		return nil, err
 	}
2fc2862a
 	template := `version: 0.1
 loglevel: debug
 storage:
     filesystem:
         rootdirectory: %s
 http:
011b4f01
     addr: %s
 %s`
 	var (
1b5c2e1d
 		authTemplate string
 		username     string
 		password     string
 		email        string
011b4f01
 	)
1b5c2e1d
 	switch auth {
 	case "htpasswd":
011b4f01
 		htpasswdPath := filepath.Join(tmp, "htpasswd")
 		// generated with: htpasswd -Bbn testuser testpassword
 		userpasswd := "testuser:$2y$05$sBsSqk0OpSD1uTZkHXc4FeJ0Z70wLQdAX/82UiHuQOKbNbBrzs63m"
 		username = "testuser"
 		password = "testpassword"
 		email = "test@test.org"
 		if err := ioutil.WriteFile(htpasswdPath, []byte(userpasswd), os.FileMode(0644)); err != nil {
 			return nil, err
 		}
1b5c2e1d
 		authTemplate = fmt.Sprintf(`auth:
011b4f01
     htpasswd:
         realm: basic-realm
         path: %s
 `, htpasswdPath)
1b5c2e1d
 	case "token":
 		authTemplate = fmt.Sprintf(`auth:
     token:
         realm: %s
         service: "registry"
         issuer: "auth-registry"
         rootcertbundle: "fixtures/registry/cert.pem"
 `, tokenURL)
2fc2862a
 	}
011b4f01
 
2fc2862a
 	confPath := filepath.Join(tmp, "config.yaml")
 	config, err := os.Create(confPath)
 	if err != nil {
 		return nil, err
 	}
1b5c2e1d
 	if _, err := fmt.Fprintf(config, template, tmp, privateRegistryURL, authTemplate); err != nil {
2fc2862a
 		os.RemoveAll(tmp)
 		return nil, err
 	}
 
1fa2e311
 	binary := v2binary
 	if schema1 {
 		binary = v2binarySchema1
 	}
 	cmd := exec.Command(binary, confPath)
2fc2862a
 	if err := cmd.Start(); err != nil {
 		os.RemoveAll(tmp)
 		if os.IsNotExist(err) {
dc944ea7
 			c.Skip(err.Error())
2fc2862a
 		}
 		return nil, err
 	}
 	return &testRegistryV2{
011b4f01
 		cmd:      cmd,
 		dir:      tmp,
1b5c2e1d
 		auth:     auth,
011b4f01
 		username: username,
 		password: password,
 		email:    email,
2fc2862a
 	}, nil
 }
 
de8ea06d
 func (t *testRegistryV2) Ping() error {
 	// We always ping through HTTP for our test registry.
 	resp, err := http.Get(fmt.Sprintf("http://%s/v2/", privateRegistryURL))
 	if err != nil {
 		return err
 	}
011b4f01
 	resp.Body.Close()
 
 	fail := resp.StatusCode != http.StatusOK
1b5c2e1d
 	if t.auth != "" {
011b4f01
 		// unauthorized is a _good_ status when pinging v2/ and it needs auth
 		fail = fail && resp.StatusCode != http.StatusUnauthorized
 	}
 	if fail {
4ee05a4d
 		return fmt.Errorf("registry ping replied with an unexpected status code %d", resp.StatusCode)
de8ea06d
 	}
 	return nil
 }
 
6b3c9281
 func (t *testRegistryV2) Close() {
 	t.cmd.Process.Kill()
 	os.RemoveAll(t.dir)
2fc2862a
 }
de52a3bc
 
 func (t *testRegistryV2) getBlobFilename(blobDigest digest.Digest) string {
 	// Split the digest into it's algorithm and hex components.
 	dgstAlg, dgstHex := blobDigest.Algorithm(), blobDigest.Hex()
 
 	// The path to the target blob data looks something like:
 	//   baseDir + "docker/registry/v2/blobs/sha256/a3/a3ed...46d4/data"
 	return fmt.Sprintf("%s/docker/registry/v2/blobs/%s/%s/%s/data", t.dir, dgstAlg, dgstHex[:2], dgstHex)
 }
 
 func (t *testRegistryV2) readBlobContents(c *check.C, blobDigest digest.Digest) []byte {
 	// Load the target manifest blob.
 	manifestBlob, err := ioutil.ReadFile(t.getBlobFilename(blobDigest))
 	if err != nil {
 		c.Fatalf("unable to read blob: %s", err)
 	}
 
 	return manifestBlob
 }
 
 func (t *testRegistryV2) writeBlobContents(c *check.C, blobDigest digest.Digest, data []byte) {
 	if err := ioutil.WriteFile(t.getBlobFilename(blobDigest), data, os.FileMode(0644)); err != nil {
 		c.Fatalf("unable to write malicious data blob: %s", err)
 	}
 }
 
 func (t *testRegistryV2) tempMoveBlobData(c *check.C, blobDigest digest.Digest) (undo func()) {
 	tempFile, err := ioutil.TempFile("", "registry-temp-blob-")
 	if err != nil {
 		c.Fatalf("unable to get temporary blob file: %s", err)
 	}
 	tempFile.Close()
 
 	blobFilename := t.getBlobFilename(blobDigest)
 
 	// Move the existing data file aside, so that we can replace it with a
 	// another blob of data.
 	if err := os.Rename(blobFilename, tempFile.Name()); err != nil {
 		os.Remove(tempFile.Name())
 		c.Fatalf("unable to move data blob: %s", err)
 	}
 
 	return func() {
 		os.Rename(tempFile.Name(), blobFilename)
 		os.Remove(tempFile.Name())
 	}
 }