github.com/dinever/docker@v1.11.1/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  	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  	if _, err := fmt.Fprintf(config, template, tmp, privateRegistryURL, authTemplate); err != nil {
    80  		os.RemoveAll(tmp)
    81  		return nil, err
    82  	}
    83  
    84  	binary := v2binary
    85  	if schema1 {
    86  		binary = v2binarySchema1
    87  	}
    88  	cmd := exec.Command(binary, confPath)
    89  	if err := cmd.Start(); err != nil {
    90  		os.RemoveAll(tmp)
    91  		if os.IsNotExist(err) {
    92  			c.Skip(err.Error())
    93  		}
    94  		return nil, err
    95  	}
    96  	return &testRegistryV2{
    97  		cmd:      cmd,
    98  		dir:      tmp,
    99  		auth:     auth,
   100  		username: username,
   101  		password: password,
   102  		email:    email,
   103  	}, nil
   104  }
   105  
   106  func (t *testRegistryV2) Ping() error {
   107  	// We always ping through HTTP for our test registry.
   108  	resp, err := http.Get(fmt.Sprintf("http://%s/v2/", privateRegistryURL))
   109  	if err != nil {
   110  		return err
   111  	}
   112  	resp.Body.Close()
   113  
   114  	fail := resp.StatusCode != http.StatusOK
   115  	if t.auth != "" {
   116  		// unauthorized is a _good_ status when pinging v2/ and it needs auth
   117  		fail = fail && resp.StatusCode != http.StatusUnauthorized
   118  	}
   119  	if fail {
   120  		return fmt.Errorf("registry ping replied with an unexpected status code %d", resp.StatusCode)
   121  	}
   122  	return nil
   123  }
   124  
   125  func (t *testRegistryV2) Close() {
   126  	t.cmd.Process.Kill()
   127  	os.RemoveAll(t.dir)
   128  }
   129  
   130  func (t *testRegistryV2) getBlobFilename(blobDigest digest.Digest) string {
   131  	// Split the digest into it's algorithm and hex components.
   132  	dgstAlg, dgstHex := blobDigest.Algorithm(), blobDigest.Hex()
   133  
   134  	// The path to the target blob data looks something like:
   135  	//   baseDir + "docker/registry/v2/blobs/sha256/a3/a3ed...46d4/data"
   136  	return fmt.Sprintf("%s/docker/registry/v2/blobs/%s/%s/%s/data", t.dir, dgstAlg, dgstHex[:2], dgstHex)
   137  }
   138  
   139  func (t *testRegistryV2) readBlobContents(c *check.C, blobDigest digest.Digest) []byte {
   140  	// Load the target manifest blob.
   141  	manifestBlob, err := ioutil.ReadFile(t.getBlobFilename(blobDigest))
   142  	if err != nil {
   143  		c.Fatalf("unable to read blob: %s", err)
   144  	}
   145  
   146  	return manifestBlob
   147  }
   148  
   149  func (t *testRegistryV2) writeBlobContents(c *check.C, blobDigest digest.Digest, data []byte) {
   150  	if err := ioutil.WriteFile(t.getBlobFilename(blobDigest), data, os.FileMode(0644)); err != nil {
   151  		c.Fatalf("unable to write malicious data blob: %s", err)
   152  	}
   153  }
   154  
   155  func (t *testRegistryV2) tempMoveBlobData(c *check.C, blobDigest digest.Digest) (undo func()) {
   156  	tempFile, err := ioutil.TempFile("", "registry-temp-blob-")
   157  	if err != nil {
   158  		c.Fatalf("unable to get temporary blob file: %s", err)
   159  	}
   160  	tempFile.Close()
   161  
   162  	blobFilename := t.getBlobFilename(blobDigest)
   163  
   164  	// Move the existing data file aside, so that we can replace it with a
   165  	// another blob of data.
   166  	if err := os.Rename(blobFilename, tempFile.Name()); err != nil {
   167  		os.Remove(tempFile.Name())
   168  		c.Fatalf("unable to move data blob: %s", err)
   169  	}
   170  
   171  	return func() {
   172  		os.Rename(tempFile.Name(), blobFilename)
   173  		os.Remove(tempFile.Name())
   174  	}
   175  }