Move and refactor integration-cli/registry to internal/test

- Move the code from `integration-cli` to `internal/test`.
- Use `testingT` and `assert` when creating the registry.

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
Upstream-commit: 66de2e6e3b6d927a3396743cd7c363aa9f7b776e
Component: engine
This commit is contained in:
Vincent Demeester
2018-04-13 10:45:34 +02:00
parent 1fe7cf068a
commit af04bf6300
10 changed files with 119 additions and 81 deletions

View File

@ -20,9 +20,9 @@ import (
"github.com/docker/docker/integration-cli/daemon"
"github.com/docker/docker/integration-cli/environment"
"github.com/docker/docker/integration-cli/fixtures/plugin"
"github.com/docker/docker/integration-cli/registry"
testdaemon "github.com/docker/docker/internal/test/daemon"
ienv "github.com/docker/docker/internal/test/environment"
"github.com/docker/docker/internal/test/registry"
"github.com/docker/docker/pkg/reexec"
"github.com/go-check/check"
"golang.org/x/net/context"
@ -30,7 +30,7 @@ import (
const (
// the private registry to use for tests
privateRegistryURL = "127.0.0.1:5000"
privateRegistryURL = registry.DefaultURL
// path to containerd's ctr binary
ctrBinary = "docker-containerd-ctr"
@ -126,8 +126,9 @@ func (s *DockerRegistrySuite) OnTimeout(c *check.C) {
}
func (s *DockerRegistrySuite) SetUpTest(c *check.C) {
testRequires(c, DaemonIsLinux, registry.Hosting, SameHostDaemon)
s.reg = setupRegistry(c, false, "", "")
testRequires(c, DaemonIsLinux, RegistryHosting, SameHostDaemon)
s.reg = registry.NewV2(c)
s.reg.WaitReady(c)
s.d = daemon.New(c, dockerBinary, dockerdBinary, daemon.Config{
Experimental: testEnv.DaemonInfo.ExperimentalBuild,
})
@ -160,8 +161,9 @@ func (s *DockerSchema1RegistrySuite) OnTimeout(c *check.C) {
}
func (s *DockerSchema1RegistrySuite) SetUpTest(c *check.C) {
testRequires(c, DaemonIsLinux, registry.Hosting, NotArm64, SameHostDaemon)
s.reg = setupRegistry(c, true, "", "")
testRequires(c, DaemonIsLinux, RegistryHosting, NotArm64, SameHostDaemon)
s.reg = registry.NewV2(c, registry.Schema1)
s.reg.WaitReady(c)
s.d = daemon.New(c, dockerBinary, dockerdBinary, daemon.Config{
Experimental: testEnv.DaemonInfo.ExperimentalBuild,
})
@ -194,8 +196,9 @@ func (s *DockerRegistryAuthHtpasswdSuite) OnTimeout(c *check.C) {
}
func (s *DockerRegistryAuthHtpasswdSuite) SetUpTest(c *check.C) {
testRequires(c, DaemonIsLinux, registry.Hosting, SameHostDaemon)
s.reg = setupRegistry(c, false, "htpasswd", "")
testRequires(c, DaemonIsLinux, RegistryHosting, SameHostDaemon)
s.reg = registry.NewV2(c, registry.Htpasswd)
s.reg.WaitReady(c)
s.d = daemon.New(c, dockerBinary, dockerdBinary, daemon.Config{
Experimental: testEnv.DaemonInfo.ExperimentalBuild,
})
@ -230,7 +233,7 @@ func (s *DockerRegistryAuthTokenSuite) OnTimeout(c *check.C) {
}
func (s *DockerRegistryAuthTokenSuite) SetUpTest(c *check.C) {
testRequires(c, DaemonIsLinux, registry.Hosting, SameHostDaemon)
testRequires(c, DaemonIsLinux, RegistryHosting, SameHostDaemon)
s.d = daemon.New(c, dockerBinary, dockerdBinary, daemon.Config{
Experimental: testEnv.DaemonInfo.ExperimentalBuild,
})
@ -252,7 +255,8 @@ func (s *DockerRegistryAuthTokenSuite) setupRegistryWithTokenService(c *check.C,
if s == nil {
c.Fatal("registry suite isn't initialized")
}
s.reg = setupRegistry(c, false, "token", tokenURL)
s.reg = registry.NewV2(c, registry.Token(tokenURL))
s.reg.WaitReady(c)
}
func init() {
@ -405,8 +409,9 @@ func (ps *DockerPluginSuite) getPluginRepoWithTag() string {
}
func (ps *DockerPluginSuite) SetUpSuite(c *check.C) {
testRequires(c, DaemonIsLinux, registry.Hosting)
ps.registry = setupRegistry(c, false, "", "")
testRequires(c, DaemonIsLinux, RegistryHosting)
ps.registry = registry.NewV2(c)
ps.registry.WaitReady(c)
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()

View File

@ -16,6 +16,7 @@ import (
"github.com/docker/docker/integration-cli/daemon"
"github.com/docker/docker/integration-cli/fixtures/plugin"
testdaemon "github.com/docker/docker/internal/test/daemon"
"github.com/docker/docker/internal/test/registry"
"github.com/go-check/check"
"golang.org/x/net/context"
"golang.org/x/sys/unix"
@ -615,7 +616,7 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesStateReporting(c *check.C) {
func (s *DockerSwarmSuite) TestAPISwarmServicesPlugin(c *check.C) {
testRequires(c, ExperimentalDaemon, DaemonIsLinux, IsAmd64)
reg := setupRegistry(c, false, "", "")
reg := registry.NewV2(c)
defer reg.Close()
repo := path.Join(privateRegistryURL, "swarm", "test:v1")

View File

@ -7,7 +7,7 @@ import (
"os"
"regexp"
"github.com/docker/docker/integration-cli/registry"
"github.com/docker/docker/internal/test/registry"
"github.com/go-check/check"
)

View File

@ -6,7 +6,7 @@ import (
"net/http"
"os"
"github.com/docker/docker/integration-cli/registry"
"github.com/docker/docker/internal/test/registry"
"github.com/go-check/check"
)

View File

@ -18,7 +18,6 @@ import (
"github.com/docker/docker/integration-cli/checker"
"github.com/docker/docker/integration-cli/cli"
"github.com/docker/docker/integration-cli/daemon"
"github.com/docker/docker/integration-cli/registry"
"github.com/docker/docker/integration-cli/request"
"github.com/go-check/check"
"github.com/gotestyourself/gotestyourself/icmd"
@ -284,22 +283,6 @@ func parseEventTime(t time.Time) string {
return fmt.Sprintf("%d.%09d", t.Unix(), int64(t.Nanosecond()))
}
func setupRegistry(c *check.C, schema1 bool, auth, tokenURL string) *registry.V2 {
reg, err := registry.NewV2(schema1, auth, tokenURL, privateRegistryURL)
c.Assert(err, check.IsNil)
// Wait for registry to be ready to serve requests.
for i := 0; i != 50; i++ {
if err = reg.Ping(); err == nil {
break
}
time.Sleep(100 * time.Millisecond)
}
c.Assert(err, check.IsNil, check.Commentf("Timeout waiting for test registry to become available: %v", err))
return reg
}
// appendBaseEnv appends the minimum set of environment variables to exec the
// docker cli binary for testing with correct configuration to the given env
// list.

View File

@ -1,214 +0,0 @@
package registry // import "github.com/docker/docker/integration-cli/registry"
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"os/exec"
"path/filepath"
"github.com/opencontainers/go-digest"
)
const (
v2binary = "registry-v2"
v2binarySchema1 = "registry-v2-schema1"
)
type testingT interface {
logT
Fatal(...interface{})
Fatalf(string, ...interface{})
}
type logT interface {
Logf(string, ...interface{})
}
// V2 represent a registry version 2
type V2 struct {
cmd *exec.Cmd
registryURL string
dir string
auth string
username string
password string
email string
}
// NewV2 creates a v2 registry server
func NewV2(schema1 bool, auth, tokenURL, registryURL string) (*V2, error) {
tmp, err := ioutil.TempDir("", "registry-test-")
if err != nil {
return nil, 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 auth {
case "htpasswd":
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
}
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"
`, tokenURL)
}
confPath := filepath.Join(tmp, "config.yaml")
config, err := os.Create(confPath)
if err != nil {
return nil, err
}
defer config.Close()
if _, err := fmt.Fprintf(config, template, tmp, registryURL, authTemplate); err != nil {
os.RemoveAll(tmp)
return nil, err
}
binary := v2binary
if schema1 {
binary = v2binarySchema1
}
cmd := exec.Command(binary, confPath)
if err := cmd.Start(); err != nil {
os.RemoveAll(tmp)
return nil, err
}
return &V2{
cmd: cmd,
dir: tmp,
auth: auth,
username: username,
password: password,
email: email,
registryURL: registryURL,
}, nil
}
// 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 testingT, blobDigest digest.Digest) []byte {
// Load the target manifest blob.
manifestBlob, err := ioutil.ReadFile(r.getBlobFilename(blobDigest))
if err != nil {
t.Fatalf("unable to read blob: %s", err)
}
return manifestBlob
}
// WriteBlobContents write the file corresponding to the specified digest with the given content
func (r *V2) WriteBlobContents(t testingT, blobDigest digest.Digest, data []byte) {
if err := ioutil.WriteFile(r.getBlobFilename(blobDigest), data, os.FileMode(0644)); err != nil {
t.Fatalf("unable to write malicious data blob: %s", err)
}
}
// 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 testingT, blobDigest digest.Digest) (undo func()) {
tempFile, err := ioutil.TempFile("", "registry-temp-blob-")
if err != nil {
t.Fatalf("unable to get temporary blob file: %s", err)
}
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 {
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")
}

View File

@ -1,66 +0,0 @@
package registry // import "github.com/docker/docker/integration-cli/registry"
import (
"net/http"
"net/http/httptest"
"regexp"
"strings"
"sync"
)
type handlerFunc func(w http.ResponseWriter, r *http.Request)
// Mock represent a registry mock
type Mock struct {
server *httptest.Server
hostport string
handlers map[string]handlerFunc
mu sync.Mutex
}
// RegisterHandler register the specified handler for the registry mock
func (tr *Mock) RegisterHandler(path string, h handlerFunc) {
tr.mu.Lock()
defer tr.mu.Unlock()
tr.handlers[path] = h
}
// NewMock creates a registry mock
func NewMock(t testingT) (*Mock, error) {
testReg := &Mock{handlers: make(map[string]handlerFunc)}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
url := r.URL.String()
var matched bool
var err error
for re, function := range testReg.handlers {
matched, err = regexp.MatchString(re, url)
if err != nil {
t.Fatal("Error with handler regexp")
}
if matched {
function(w, r)
break
}
}
if !matched {
t.Fatalf("Unable to match %s with regexp", url)
}
}))
testReg.server = ts
testReg.hostport = strings.Replace(ts.URL, "http://", "", 1)
return testReg, nil
}
// URL returns the url of the registry
func (tr *Mock) URL() string {
return tr.hostport
}
// Close closes mock and releases resources
func (tr *Mock) Close() {
tr.server.Close()
}

View File

@ -1,12 +0,0 @@
package registry // import "github.com/docker/docker/integration-cli/registry"
import "os/exec"
// Hosting returns wether the host can host a registry (v2) or not
func Hosting() bool {
// for now registry binary is built only if we're running inside
// container through `make test`. Figure that out by testing if
// registry binary is in PATH.
_, err := exec.LookPath(v2binary)
return err == nil
}

View File

@ -14,6 +14,7 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/docker/docker/integration-cli/requirement"
"github.com/docker/docker/internal/test/registry"
)
func ArchitectureIsNot(arch string) bool {
@ -183,6 +184,15 @@ func IsolationIsProcess() bool {
return IsolationIs("process")
}
// RegistryHosting returns wether the host can host a registry (v2) or not
func RegistryHosting() bool {
// for now registry binary is built only if we're running inside
// container through `make test`. Figure that out by testing if
// registry binary is in PATH.
_, err := exec.LookPath(registry.V2binary)
return err == nil
}
// testRequires checks if the environment satisfies the requirements
// for the test to run or skips the tests.
func testRequires(c requirement.SkipT, requirements ...requirement.Test) {