mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
d7ba1f85ef
This removes the use of the old distribution code in the plugin packages and replaces it with containerd libraries for plugin pushes and pulls. Additionally it uses a content store from containerd which seems like it's compatible with the old "basicBlobStore" in the plugin package. This is being used locally isntead of through the containerd client for now. Signed-off-by: Brian Goff <cpuguy83@gmail.com>
240 lines
6.3 KiB
Go
240 lines
6.3 KiB
Go
package registry // import "github.com/docker/docker/testutil/registry"
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/opencontainers/go-digest"
|
|
"gotest.tools/v3/assert"
|
|
)
|
|
|
|
const (
|
|
// V2binary is the name of the registry v2 binary
|
|
V2binary = "registry-v2"
|
|
// V2binarySchema1 is the name of the registry that serve schema1
|
|
V2binarySchema1 = "registry-v2-schema1"
|
|
// DefaultURL is the default url that will be used by the registry (if not specified otherwise)
|
|
DefaultURL = "127.0.0.1:5000"
|
|
)
|
|
|
|
// V2 represent a registry version 2
|
|
type V2 struct {
|
|
cmd *exec.Cmd
|
|
registryURL string
|
|
dir string
|
|
auth string
|
|
username string
|
|
password string
|
|
email string
|
|
}
|
|
|
|
// Config contains the test registry configuration
|
|
type Config struct {
|
|
schema1 bool
|
|
auth string
|
|
tokenURL string
|
|
registryURL string
|
|
stdout io.Writer
|
|
stderr io.Writer
|
|
}
|
|
|
|
// NewV2 creates a v2 registry server
|
|
func NewV2(t testing.TB, ops ...func(*Config)) *V2 {
|
|
t.Helper()
|
|
c := &Config{
|
|
registryURL: DefaultURL,
|
|
}
|
|
for _, op := range ops {
|
|
op(c)
|
|
}
|
|
tmp, err := ioutil.TempDir("", "registry-test-")
|
|
assert.NilError(t, err)
|
|
template := `version: 0.1
|
|
loglevel: debug
|
|
storage:
|
|
filesystem:
|
|
rootdirectory: %s
|
|
http:
|
|
addr: %s
|
|
%s`
|
|
var (
|
|
authTemplate string
|
|
username string
|
|
password string
|
|
email string
|
|
)
|
|
switch c.auth {
|
|
case "htpasswd":
|
|
htpasswdPath := filepath.Join(tmp, "htpasswd")
|
|
// generated with: htpasswd -Bbn testuser testpassword
|
|
// #nosec G101
|
|
userpasswd := "testuser:$2y$05$sBsSqk0OpSD1uTZkHXc4FeJ0Z70wLQdAX/82UiHuQOKbNbBrzs63m"
|
|
username = "testuser"
|
|
password = "testpassword"
|
|
email = "test@test.org"
|
|
err := ioutil.WriteFile(htpasswdPath, []byte(userpasswd), os.FileMode(0644))
|
|
assert.NilError(t, err)
|
|
authTemplate = fmt.Sprintf(`auth:
|
|
htpasswd:
|
|
realm: basic-realm
|
|
path: %s
|
|
`, htpasswdPath)
|
|
case "token":
|
|
authTemplate = fmt.Sprintf(`auth:
|
|
token:
|
|
realm: %s
|
|
service: "registry"
|
|
issuer: "auth-registry"
|
|
rootcertbundle: "fixtures/registry/cert.pem"
|
|
`, c.tokenURL)
|
|
}
|
|
|
|
confPath := filepath.Join(tmp, "config.yaml")
|
|
config, err := os.Create(confPath)
|
|
assert.NilError(t, err)
|
|
defer config.Close()
|
|
|
|
if _, err := fmt.Fprintf(config, template, tmp, c.registryURL, authTemplate); err != nil {
|
|
// FIXME(vdemeester) use a defer/clean func
|
|
os.RemoveAll(tmp)
|
|
t.Fatal(err)
|
|
}
|
|
|
|
binary := V2binary
|
|
if c.schema1 {
|
|
binary = V2binarySchema1
|
|
}
|
|
cmd := exec.Command(binary, confPath)
|
|
cmd.Stdout = c.stdout
|
|
cmd.Stderr = c.stderr
|
|
if err := cmd.Start(); err != nil {
|
|
// FIXME(vdemeester) use a defer/clean func
|
|
os.RemoveAll(tmp)
|
|
t.Fatal(err)
|
|
}
|
|
return &V2{
|
|
cmd: cmd,
|
|
dir: tmp,
|
|
auth: c.auth,
|
|
username: username,
|
|
password: password,
|
|
email: email,
|
|
registryURL: c.registryURL,
|
|
}
|
|
}
|
|
|
|
// WaitReady waits for the registry to be ready to serve requests (or fail after a while)
|
|
func (r *V2) WaitReady(t testing.TB) {
|
|
t.Helper()
|
|
var err error
|
|
for i := 0; i != 50; i++ {
|
|
if err = r.Ping(); err == nil {
|
|
return
|
|
}
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|
|
t.Fatalf("timeout waiting for test registry to become available: %v", err)
|
|
}
|
|
|
|
// Ping sends an http request to the current registry, and fail if it doesn't respond correctly
|
|
func (r *V2) Ping() error {
|
|
// We always ping through HTTP for our test registry.
|
|
resp, err := http.Get(fmt.Sprintf("http://%s/v2/", r.registryURL))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
resp.Body.Close()
|
|
|
|
fail := resp.StatusCode != http.StatusOK
|
|
if r.auth != "" {
|
|
// unauthorized is a _good_ status when pinging v2/ and it needs auth
|
|
fail = fail && resp.StatusCode != http.StatusUnauthorized
|
|
}
|
|
if fail {
|
|
return fmt.Errorf("registry ping replied with an unexpected status code %d", resp.StatusCode)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Close kills the registry server
|
|
func (r *V2) Close() {
|
|
r.cmd.Process.Kill()
|
|
r.cmd.Process.Wait()
|
|
os.RemoveAll(r.dir)
|
|
}
|
|
|
|
func (r *V2) getBlobFilename(blobDigest digest.Digest) string {
|
|
// Split the digest into its 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", r.dir, dgstAlg, dgstHex[:2], dgstHex)
|
|
}
|
|
|
|
// ReadBlobContents read the file corresponding to the specified digest
|
|
func (r *V2) ReadBlobContents(t testing.TB, blobDigest digest.Digest) []byte {
|
|
t.Helper()
|
|
// Load the target manifest blob.
|
|
manifestBlob, err := ioutil.ReadFile(r.getBlobFilename(blobDigest))
|
|
assert.NilError(t, err, "unable to read blob")
|
|
return manifestBlob
|
|
}
|
|
|
|
// WriteBlobContents write the file corresponding to the specified digest with the given content
|
|
func (r *V2) WriteBlobContents(t testing.TB, blobDigest digest.Digest, data []byte) {
|
|
t.Helper()
|
|
err := ioutil.WriteFile(r.getBlobFilename(blobDigest), data, os.FileMode(0644))
|
|
assert.NilError(t, err, "unable to write malicious data blob")
|
|
}
|
|
|
|
// TempMoveBlobData moves the existing data file aside, so that we can replace it with a
|
|
// malicious blob of data for example.
|
|
func (r *V2) TempMoveBlobData(t testing.TB, blobDigest digest.Digest) (undo func()) {
|
|
t.Helper()
|
|
tempFile, err := ioutil.TempFile("", "registry-temp-blob-")
|
|
assert.NilError(t, err, "unable to get temporary blob file")
|
|
tempFile.Close()
|
|
|
|
blobFilename := r.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 {
|
|
// FIXME(vdemeester) use a defer/clean func
|
|
os.Remove(tempFile.Name())
|
|
t.Fatalf("unable to move data blob: %s", err)
|
|
}
|
|
|
|
return func() {
|
|
os.Rename(tempFile.Name(), blobFilename)
|
|
os.Remove(tempFile.Name())
|
|
}
|
|
}
|
|
|
|
// Username returns the configured user name of the server
|
|
func (r *V2) Username() string {
|
|
return r.username
|
|
}
|
|
|
|
// Password returns the configured password of the server
|
|
func (r *V2) Password() string {
|
|
return r.password
|
|
}
|
|
|
|
// Email returns the configured email of the server
|
|
func (r *V2) Email() string {
|
|
return r.email
|
|
}
|
|
|
|
// Path returns the path where the registry write data
|
|
func (r *V2) Path() string {
|
|
return filepath.Join(r.dir, "docker", "registry", "v2")
|
|
}
|