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:
@ -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()
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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"
|
||||
)
|
||||
|
||||
|
||||
@ -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"
|
||||
)
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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")
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user