diff --git a/src/cmd/linuxkit/pkglib/build.go b/src/cmd/linuxkit/pkglib/build.go index 18b7ebe00..ff0f52174 100644 --- a/src/cmd/linuxkit/pkglib/build.go +++ b/src/cmd/linuxkit/pkglib/build.go @@ -191,20 +191,6 @@ func (p Pkg) Build(bos ...BuildOpt) error { return errors.New("must provide linuxkit build cache directory") } - // if targeting docker, be sure local arch is a build target - if bo.targetDocker { - var found bool - for _, platform := range bo.platforms { - if platform.Architecture == arch { - found = true - break - } - } - if !found { - return fmt.Errorf("must build for local platform 'linux/%s' when targeting docker", arch) - } - } - if p.git != nil && bo.push && bo.release == "" { r, err := p.git.commitTag("HEAD") if err != nil { @@ -243,6 +229,24 @@ func (p Pkg) Build(bos ...BuildOpt) error { } else { fmt.Fprintf(writer, "%s not found\n", ref) } + if bo.targetDocker { + notFound := false + for _, platform := range bo.platforms { + archRef, err := reference.Parse(fmt.Sprintf("%s-%s", p.FullTag(), platform.Architecture)) + if err != nil { + return err + } + fmt.Fprintf(writer, "checking for %s in local cache, fallback to remote registry...\n", archRef) + if _, err := c.ImagePull(&archRef, "", platform.Architecture, false); err == nil { + fmt.Fprintf(writer, "%s found or pulled\n", archRef) + skipBuild = true + } else { + fmt.Fprintf(writer, "%s not found\n", archRef) + notFound = true + } + } + skipBuild = skipBuild && !notFound + } } if !skipBuild { @@ -327,14 +331,28 @@ func (p Pkg) Build(bos ...BuildOpt) error { } // if requested docker, load the image up + // we will store images with arch suffix, i.e. -amd64 + // if one of the arch equals with system, we will add tag without suffix if bo.targetDocker { - cacheSource := c.NewSource(&ref, arch, desc) - reader, err := cacheSource.V1TarReader() - if err != nil { - return fmt.Errorf("unable to get reader from cache: %v", err) - } - if err := d.load(reader); err != nil { - return err + for _, platform := range bo.platforms { + archRef, err := reference.Parse(fmt.Sprintf("%s-%s", p.FullTag(), platform.Architecture)) + if err != nil { + return err + } + cacheSource := c.NewSource(&archRef, platform.Architecture, desc) + reader, err := cacheSource.V1TarReader() + if err != nil { + return fmt.Errorf("unable to get reader from cache: %v", err) + } + if err := d.load(reader); err != nil { + return err + } + if platform.Architecture == arch { + err = d.tag(fmt.Sprintf("%s-%s", p.FullTag(), platform.Architecture), p.FullTag()) + if err != nil { + return err + } + } } } @@ -375,9 +393,18 @@ func (p Pkg) Build(bos ...BuildOpt) error { } // tag in docker, if requested + // will tag all images with arch suffix + // if one of the arch equals with system will add tag without suffix if bo.targetDocker { - if err := d.tag(p.FullTag(), fullRelTag); err != nil { - return err + for _, platform := range bo.platforms { + if err := d.tag(fmt.Sprintf("%s-%s", p.FullTag(), platform.Architecture), fmt.Sprintf("%s-%s", fullRelTag, platform.Architecture)); err != nil { + return err + } + if platform.Architecture == arch { + if err := d.tag(fmt.Sprintf("%s-%s", p.FullTag(), platform.Architecture), fullRelTag); err != nil { + return err + } + } } } diff --git a/src/cmd/linuxkit/pkglib/build_test.go b/src/cmd/linuxkit/pkglib/build_test.go index eaa5cc714..5aa808718 100644 --- a/src/cmd/linuxkit/pkglib/build_test.go +++ b/src/cmd/linuxkit/pkglib/build_test.go @@ -9,7 +9,6 @@ import ( "io" "io/ioutil" "math/rand" - "runtime" "strings" "testing" @@ -116,7 +115,7 @@ func (c *cacheMocker) ImageLoad(ref *reference.Spec, architecture string, r io.R } func (c *cacheMocker) imageWriteStream(ref *reference.Spec, architecture string, r io.Reader) (lktspec.ImageSource, error) { - image := ref.String() + image := fmt.Sprintf("%s-%s", ref.String(), architecture) // make some random data for a layer b, err := ioutil.ReadAll(r) @@ -154,10 +153,14 @@ func (c *cacheMocker) imageWriteStream(ref *reference.Spec, architecture string, Annotations: map[string]string{ imagespec.AnnotationRefName: image, }, + Platform: ®istry.Platform{ + OS: "linux", + Architecture: architecture, + }, } c.appendImage(image, desc) - return c.NewSource(ref, "", &desc), nil + return c.NewSource(ref, architecture, &desc), nil } func (c *cacheMocker) IndexWrite(ref *reference.Spec, descriptors ...registry.Descriptor) (lktspec.ImageSource, error) { @@ -273,7 +276,13 @@ func (c cacheMockerSource) TarReader() (io.ReadCloser, error) { return nil, errors.New("unsupported") } func (c cacheMockerSource) V1TarReader() (io.ReadCloser, error) { - return nil, errors.New("unsupported") + _, found := c.c.images[c.ref.String()] + if !found { + return nil, fmt.Errorf("no image found with ref: %s", c.ref.String()) + } + b := make([]byte, 256) + rand.Read(b) + return io.NopCloser(bytes.NewReader(b)), nil } func (c cacheMockerSource) Descriptor() *registry.Descriptor { return c.descriptor @@ -281,14 +290,8 @@ func (c cacheMockerSource) Descriptor() *registry.Descriptor { func TestBuild(t *testing.T) { var ( - nonLocal string cacheDir = "somecachedir" ) - if runtime.GOARCH == "amd64" { - nonLocal = "arm64" - } else { - nonLocal = "amd64" - } tests := []struct { msg string p Pkg @@ -302,7 +305,7 @@ func TestBuild(t *testing.T) { {"not at head", Pkg{org: "foo", image: "bar", hash: "abc", arches: []string{"amd64"}, commitHash: "foo"}, nil, []string{"amd64"}, &dockerMocker{supportContexts: false}, &cacheMocker{}, "Cannot build from commit hash != HEAD"}, {"no build cache", Pkg{org: "foo", image: "bar", hash: "abc", arches: []string{"amd64"}, commitHash: "HEAD"}, nil, []string{"amd64"}, &dockerMocker{supportContexts: false}, &cacheMocker{}, "must provide linuxkit build cache"}, {"unsupported contexts", Pkg{org: "foo", image: "bar", hash: "abc", arches: []string{"amd64"}, commitHash: "HEAD"}, []BuildOpt{WithBuildCacheDir(cacheDir)}, []string{"amd64"}, &dockerMocker{supportContexts: false}, &cacheMocker{}, "contexts not supported, check docker version"}, - {"load docker without local platform", Pkg{org: "foo", image: "bar", hash: "abc", arches: []string{"amd64", "arm64"}, commitHash: "HEAD"}, []BuildOpt{WithBuildCacheDir(cacheDir), WithBuildTargetDockerCache()}, []string{nonLocal}, &dockerMocker{supportContexts: false}, &cacheMocker{}, "must build for local platform"}, + {"load docker without local platform", Pkg{org: "foo", image: "bar", hash: "abc", arches: []string{"amd64", "arm64"}, commitHash: "HEAD"}, []BuildOpt{WithBuildCacheDir(cacheDir), WithBuildTargetDockerCache()}, []string{"amd64", "arm64"}, &dockerMocker{supportContexts: true, enableBuild: true, images: map[string][]byte{}, enableTag: true}, &cacheMocker{enableImagePull: false, enableImageLoad: true, enableIndexWrite: true}, ""}, {"amd64", Pkg{org: "foo", image: "bar", hash: "abc", arches: []string{"amd64", "arm64"}, commitHash: "HEAD"}, []BuildOpt{WithBuildCacheDir(cacheDir)}, []string{"amd64"}, &dockerMocker{supportContexts: true, enableBuild: true}, &cacheMocker{enableImagePull: false, enableImageLoad: true, enableIndexWrite: true}, ""}, {"arm64", Pkg{org: "foo", image: "bar", hash: "abc", arches: []string{"amd64", "arm64"}, commitHash: "HEAD"}, []BuildOpt{WithBuildCacheDir(cacheDir)}, []string{"arm64"}, &dockerMocker{supportContexts: true, enableBuild: true}, &cacheMocker{enableImagePull: false, enableImageLoad: true, enableIndexWrite: true}, ""}, {"amd64 and arm64", Pkg{org: "foo", image: "bar", hash: "abc", arches: []string{"amd64", "arm64"}, commitHash: "HEAD"}, []BuildOpt{WithBuildCacheDir(cacheDir)}, []string{"amd64", "arm64"}, &dockerMocker{supportContexts: true, enableBuild: true}, &cacheMocker{enableImagePull: false, enableImageLoad: true, enableIndexWrite: true}, ""}, diff --git a/src/cmd/linuxkit/pkglib/docker.go b/src/cmd/linuxkit/pkglib/docker.go index aa827cb49..f3a4a6f57 100644 --- a/src/cmd/linuxkit/pkglib/docker.go +++ b/src/cmd/linuxkit/pkglib/docker.go @@ -288,7 +288,7 @@ func (dr *dockerRunnerImpl) builderEnsureContainer(ctx context.Context, name, im } // create the builder args := []string{"container", "run", "-d", "--name", name, "--privileged", image, "--allow-insecure-entitlement", "network.host", "--addr", fmt.Sprintf("unix://%s", buildkitSocketPath), "--debug"} - msg := fmt.Sprintf("creating builder container '%s' in context '%s", name, dockerContext) + msg := fmt.Sprintf("creating builder container '%s' in context '%s'", name, dockerContext) fmt.Println(msg) if err := dr.command(nil, ioutil.Discard, ioutil.Discard, args...); err != nil { return nil, err