Merge pull request #618 from tonistiigi/test-local

integration: use local registry mirrors
docker-18.09
Tõnis Tiigi 2018-09-18 22:05:17 -07:00 committed by GitHub
commit 95e6139521
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 293 additions and 18 deletions

View File

@ -3,10 +3,13 @@ package contentutil
import (
"context"
"io"
"sync"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/remotes"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
func Copy(ctx context.Context, ingester content.Ingester, provider content.Provider, desc ocispec.Descriptor) error {
@ -41,3 +44,38 @@ func (r *rc) Read(b []byte) (int, error) {
}
return n, err
}
func CopyChain(ctx context.Context, ingester content.Ingester, provider content.Provider, desc ocispec.Descriptor) error {
var m sync.Mutex
manifestStack := []ocispec.Descriptor{}
filterHandler := images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
switch desc.MediaType {
case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest,
images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
m.Lock()
manifestStack = append(manifestStack, desc)
m.Unlock()
return nil, images.ErrStopHandler
default:
return nil, nil
}
})
handlers := []images.Handler{
images.ChildrenHandler(provider),
filterHandler,
remotes.FetchHandler(ingester, &localFetcher{provider}),
}
if err := images.Dispatch(ctx, images.Handlers(handlers...), desc); err != nil {
return errors.WithStack(err)
}
for i := len(manifestStack) - 1; i >= 0; i-- {
if err := Copy(ctx, ingester, provider, manifestStack[i]); err != nil {
return errors.WithStack(err)
}
}
return nil
}

View File

@ -55,6 +55,9 @@ func (r *readerAt) ReadAt(b []byte, off int64) (int, error) {
var totalN int
for len(b) > 0 {
n, err := r.Reader.Read(b)
if err == io.EOF && n == len(b) {
err = nil
}
r.offset += int64(n)
totalN += n
b = b[n:]

57
util/contentutil/refs.go Normal file
View File

@ -0,0 +1,57 @@
package contentutil
import (
"context"
"net/http"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/remotes"
"github.com/containerd/containerd/remotes/docker"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
func ProviderFromRef(ref string) (ocispec.Descriptor, content.Provider, error) {
remote := docker.NewResolver(docker.ResolverOptions{
Client: http.DefaultClient,
})
name, desc, err := remote.Resolve(context.TODO(), ref)
if err != nil {
return ocispec.Descriptor{}, nil, err
}
fetcher, err := remote.Fetcher(context.TODO(), name)
if err != nil {
return ocispec.Descriptor{}, nil, err
}
return desc, FromFetcher(fetcher), nil
}
func IngesterFromRef(ref string) (content.Ingester, error) {
remote := docker.NewResolver(docker.ResolverOptions{
Client: http.DefaultClient,
})
pusher, err := remote.Pusher(context.TODO(), ref)
if err != nil {
return nil, err
}
return &ingester{
pusher: pusher,
}, nil
}
type ingester struct {
pusher remotes.Pusher
}
func (w *ingester) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) {
var wo content.WriterOpts
for _, o := range opts {
if err := o(&wo); err != nil {
return nil, err
}
}
return w.pusher.Push(ctx, wo.Desc)
}

View File

@ -48,7 +48,12 @@ func (c *containerd) Name() string {
return c.name
}
func (c *containerd) New() (sb Sandbox, cl func() error, err error) {
func (c *containerd) New(opt ...SandboxOpt) (sb Sandbox, cl func() error, err error) {
var conf SandboxConf
for _, o := range opt {
o(&conf)
}
if err := lookupBinary(c.containerd); err != nil {
return nil, nil, err
}
@ -115,12 +120,25 @@ disabled_plugins = ["cri"]
}
deferF.append(ctdStop)
buildkitdSock, stop, err := runBuildkitd([]string{"buildkitd",
buildkitdArgs := []string{"buildkitd",
"--oci-worker=false",
"--containerd-worker=true",
"--containerd-worker-addr", address,
"--containerd-worker-labels=org.mobyproject.buildkit.worker.sandbox=true", // Include use of --containerd-worker-labels to trigger https://github.com/moby/buildkit/pull/603
}, logs, 0, 0)
}
if conf.mirror != "" {
dir, err := configWithMirror(conf.mirror)
if err != nil {
return nil, nil, err
}
deferF.append(func() error {
return os.RemoveAll(dir)
})
buildkitdArgs = append(buildkitdArgs, "--config="+filepath.Join(dir, "buildkitd.toml"))
}
buildkitdSock, stop, err := runBuildkitd(buildkitdArgs, logs, 0, 0)
if err != nil {
return nil, nil, err
}

View File

@ -30,6 +30,7 @@ func init() {
register(&oci{uid: uid, gid: gid})
}
}
}
type oci struct {
@ -44,7 +45,12 @@ func (s *oci) Name() string {
return "oci"
}
func (s *oci) New() (Sandbox, func() error, error) {
func (s *oci) New(opt ...SandboxOpt) (Sandbox, func() error, error) {
var c SandboxConf
for _, o := range opt {
o(&c)
}
if err := lookupBinary("buildkitd"); err != nil {
return nil, nil, err
}
@ -54,8 +60,23 @@ func (s *oci) New() (Sandbox, func() error, error) {
logs := map[string]*bytes.Buffer{}
// Include use of --oci-worker-labels to trigger https://github.com/moby/buildkit/pull/603
buildkitdArgs := []string{"buildkitd", "--oci-worker=true", "--containerd-worker=false", "--oci-worker-labels=org.mobyproject.buildkit.worker.sandbox=true"}
deferF := &multiCloser{}
if c.mirror != "" {
dir, err := configWithMirror(c.mirror)
if err != nil {
return nil, nil, err
}
deferF.append(func() error {
return os.RemoveAll(dir)
})
buildkitdArgs = append(buildkitdArgs, "--config="+filepath.Join(dir, "buildkitd.toml"))
}
if s.uid != 0 {
if s.gid == 0 {
deferF.F()()
return nil, nil, errors.Errorf("unsupported id pair: uid=%d, gid=%d", s.uid, s.gid)
}
// TODO: make sure the user exists and subuid/subgid are configured.
@ -63,10 +84,10 @@ func (s *oci) New() (Sandbox, func() error, error) {
}
buildkitdSock, stop, err := runBuildkitd(buildkitdArgs, logs, s.uid, s.gid)
if err != nil {
deferF.F()()
return nil, nil, err
}
deferF := &multiCloser{}
deferF.append(stop)
return &sandbox{address: buildkitdSock, logs: logs, cleanup: deferF, rootless: s.uid != 0}, deferF.F(), nil
@ -94,7 +115,7 @@ func (sb *sandbox) PrintLogs(t *testing.T) {
}
func (sb *sandbox) NewRegistry() (string, error) {
url, cl, err := newRegistry()
url, cl, err := newRegistry("")
if err != nil {
return "", err
}

View File

@ -15,7 +15,7 @@ import (
"github.com/pkg/errors"
)
func newRegistry() (url string, cl func() error, err error) {
func newRegistry(dir string) (url string, cl func() error, err error) {
if err := lookupBinary("registry"); err != nil {
return "", nil, err
}
@ -30,26 +30,34 @@ func newRegistry() (url string, cl func() error, err error) {
}
}()
tmpdir, err := ioutil.TempDir("", "test-registry")
if err != nil {
return "", nil, err
if dir == "" {
tmpdir, err := ioutil.TempDir("", "test-registry")
if err != nil {
return "", nil, err
}
deferF.append(func() error { return os.RemoveAll(tmpdir) })
dir = tmpdir
}
deferF.append(func() error { return os.RemoveAll(tmpdir) })
template := fmt.Sprintf(`version: 0.1
if _, err := os.Stat(filepath.Join(dir, "config.yaml")); err != nil {
if !os.IsNotExist(err) {
return "", nil, err
}
template := fmt.Sprintf(`version: 0.1
loglevel: debug
storage:
filesystem:
rootdirectory: %s
http:
addr: 127.0.0.1:0
`, filepath.Join(tmpdir, "data"))
`, filepath.Join(dir, "data"))
if err := ioutil.WriteFile(filepath.Join(tmpdir, "config.yaml"), []byte(template), 0600); err != nil {
return "", nil, err
if err := ioutil.WriteFile(filepath.Join(dir, "config.yaml"), []byte(template), 0600); err != nil {
return "", nil, err
}
}
cmd := exec.Command("registry", "serve", filepath.Join(tmpdir, "config.yaml"))
cmd := exec.Command("registry", "serve", filepath.Join(dir, "config.yaml"))
rc, err := cmd.StdoutPipe()
if err != nil {
return "", nil, err

View File

@ -1,12 +1,21 @@
package integration
import (
"context"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"reflect"
"runtime"
"strings"
"sync"
"syscall"
"testing"
"github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb"
"github.com/moby/buildkit/util/contentutil"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -21,10 +30,22 @@ type Sandbox interface {
}
type Worker interface {
New() (Sandbox, func() error, error)
New(...SandboxOpt) (Sandbox, func() error, error)
Name() string
}
type SandboxConf struct {
mirror string
}
type SandboxOpt func(*SandboxConf)
func WithMirror(h string) SandboxOpt {
return func(c *SandboxConf) {
c.mirror = h
}
}
type Test func(*testing.T, Sandbox)
var defaultWorkers []Worker
@ -41,10 +62,30 @@ func Run(t *testing.T, testCases []Test) {
if testing.Short() {
t.Skip("skipping in short mode")
}
mirror, cleanup, err := runMirror(t)
require.NoError(t, err)
var mu sync.Mutex
var count int
cleanOnComplete := func() func() {
count++
return func() {
mu.Lock()
count--
if count == 0 {
cleanup()
}
mu.Unlock()
}
}
defer cleanOnComplete()()
for _, br := range List() {
for _, tc := range testCases {
ok := t.Run(getFunctionName(tc)+"/worker="+br.Name(), func(t *testing.T) {
sb, close, err := br.New()
defer cleanOnComplete()()
sb, close, err := br.New(WithMirror(mirror))
if err != nil {
if errors.Cause(err) == ErrorRequirements {
t.Skip(err.Error())
@ -69,3 +110,92 @@ func getFunctionName(i interface{}) string {
dot := strings.LastIndex(fullname, ".") + 1
return strings.Title(fullname[dot:])
}
func copyImagesLocal(t *testing.T, host string) error {
for to, from := range offlineImages() {
desc, provider, err := contentutil.ProviderFromRef(from)
if err != nil {
return err
}
ingester, err := contentutil.IngesterFromRef(host + "/" + to)
if err != nil {
return err
}
if err := contentutil.CopyChain(context.TODO(), ingester, provider, desc); err != nil {
return err
}
t.Logf("copied %s to local mirror %s", from, host+"/"+to)
}
return nil
}
func offlineImages() map[string]string {
arch := runtime.GOARCH
if arch == "arm64" {
arch = "arm64v8"
}
return map[string]string{
"library/busybox:latest": "docker.io/" + arch + "/busybox:latest",
"library/alpine:latest": "docker.io/" + arch + "/alpine:latest",
"tonistiigi/copy:v0.1.4": "docker.io/" + dockerfile2llb.DefaultCopyImage,
}
}
func configWithMirror(mirror string) (string, error) {
tmpdir, err := ioutil.TempDir("", "bktest_config")
if err != nil {
return "", err
}
if err := os.Chmod(tmpdir, 0711); err != nil {
return "", err
}
if err := ioutil.WriteFile(filepath.Join(tmpdir, "buildkitd.toml"), []byte(fmt.Sprintf(`
[registry."docker.io"]
mirrors=["%s"]
`, mirror)), 0644); err != nil {
return "", err
}
return tmpdir, nil
}
func runMirror(t *testing.T) (host string, cleanup func() error, err error) {
mirrorDir := os.Getenv("BUILDKIT_REGISTRY_MIRROR_DIR")
var f *os.File
if mirrorDir != "" {
f, err = os.Create(filepath.Join(mirrorDir, "lock"))
if err != nil {
return "", nil, err
}
defer func() {
if err != nil {
f.Close()
}
}()
if err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX); err != nil {
return "", nil, err
}
}
mirror, cleanup, err := newRegistry(mirrorDir)
if err != nil {
return "", nil, err
}
defer func() {
if err != nil {
cleanup()
}
}()
if err := copyImagesLocal(t, mirror); err != nil {
return "", nil, err
}
if mirrorDir != "" {
if err := syscall.Flock(int(f.Fd()), syscall.LOCK_UN); err != nil {
return "", nil, err
}
}
return mirror, cleanup, err
}