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  }