Skip to content

Commit

Permalink
Add support for specifying multiple base images to use in the spec
Browse files Browse the repository at this point in the history
This is pretty much to support building for multiple windows versions
from windowscross but can also be useful for allowing users to specify
something other than a registry reference to provide an image.

The actual support for multiple bases, where it can be supported, will
come in a follow-up commit.
  • Loading branch information
cpuguy83 committed Jan 7, 2025
1 parent 08d2e6c commit 0069430
Show file tree
Hide file tree
Showing 13 changed files with 314 additions and 48 deletions.
25 changes: 24 additions & 1 deletion docs/spec.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,19 @@
"type": "object",
"description": "Artifacts describes all the artifacts to include in the package."
},
"BaseImage": {
"properties": {
"rootfs": {
"$ref": "#/$defs/Source",
"description": "Source represents an image rootfs."
}
},
"additionalProperties": false,
"type": "object",
"required": [
"rootfs"
]
},
"BuildStep": {
"properties": {
"command": {
Expand Down Expand Up @@ -487,7 +500,14 @@
},
"base": {
"type": "string",
"description": "Base is the base image to use for the output image.\nThis only affects the output image, not the intermediate build image."
"description": "Deprecated: Use [Bases] instead."
},
"Bases": {
"items": {
"$ref": "#/$defs/BaseImage"
},
"type": "array",
"description": "Bases is used to specify a list of base images to build images for. The\nintent of allowing multiple bases is for cases, such as Windows, where you\nmay want to publish multiple versions of a base image in one image.\n\nWindows is the example here because of the way Windows works, the image\nthat the base is based off of must match the OS version of the host machine.\nTherefore it is common to have multiple Windows images in one with a\ndifferent value for the os version field of the platform.\n\nFor the most part implementations are not expected to support multiple base\nimages and may error out if multiple are specified.\n\nThis should not be set if [Base] is also set."
},
"post": {
"$ref": "#/$defs/PostInstall",
Expand All @@ -500,6 +520,9 @@
},
"additionalProperties": false,
"type": "object",
"required": [
"Bases"
],
"description": "ImageConfig is the configuration for the output image."
},
"PackageConfig": {
Expand Down
28 changes: 18 additions & 10 deletions frontend/azlinux/handle_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/Azure/dalec"
"github.com/Azure/dalec/frontend"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/client/llb/sourceresolver"
gwclient "github.com/moby/buildkit/frontend/gateway/client"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
Expand All @@ -30,7 +29,7 @@ func handleContainer(w worker) gwclient.BuildFunc {
return nil, nil, fmt.Errorf("error creating rpm: %w", err)
}

img, err := resolveBaseConfig(ctx, w, client, platform, spec, targetKey)
img, err := resolveBaseConfig(ctx, w, sOpt, platform, spec, targetKey)
if err != nil {
return nil, nil, errors.Wrap(err, "could not resolve base image config")
}
Expand All @@ -50,9 +49,13 @@ func specToContainerLLB(w worker, spec *dalec.Spec, targetKey string, rpmDir llb
return llb.Scratch(), err
}

rootfs := llb.Scratch()
if ref := dalec.GetBaseOutputImage(spec, targetKey); ref != "" {
rootfs = llb.Image(ref, llb.WithMetaResolver(sOpt.Resolver), dalec.WithConstraints(opts...))
bi, err := spec.GetSingleBase(targetKey)
if err != nil {
return llb.Scratch(), err
}
rootfs, err := bi.ToState(sOpt, opts...)
if err != nil {
return llb.Scratch(), err
}

installTimeRepos := spec.GetInstallRepos(targetKey)
Expand Down Expand Up @@ -84,13 +87,18 @@ func specToContainerLLB(w worker, spec *dalec.Spec, targetKey string, rpmDir llb
return rootfs, nil
}

func resolveBaseConfig(ctx context.Context, w worker, resolver llb.ImageMetaResolver, platform *ocispecs.Platform, spec *dalec.Spec, targetKey string) (*dalec.DockerImageSpec, error) {
func resolveBaseConfig(ctx context.Context, w worker, sOpt dalec.SourceOpts, platform *ocispecs.Platform, spec *dalec.Spec, targetKey string) (*dalec.DockerImageSpec, error) {
var img *dalec.DockerImageSpec

if ref := dalec.GetBaseOutputImage(spec, targetKey); ref != "" {
_, _, dt, err := resolver.ResolveImageConfig(ctx, ref, sourceresolver.Opt{Platform: platform})
bi, err := spec.GetSingleBase(targetKey)
if err != nil {
return nil, errors.Wrap(err, "error resolving base image config")
}

if bi != nil {
dt, err := bi.ResolveImageConfig(ctx, sOpt, platform)
if err != nil {
return nil, errors.Wrap(err, "error resolving base image config")
return nil, err
}

var i dalec.DockerImageSpec
Expand All @@ -100,7 +108,7 @@ func resolveBaseConfig(ctx context.Context, w worker, resolver llb.ImageMetaReso
img = &i
} else {
var err error
img, err = w.DefaultImageConfig(ctx, resolver, platform)
img, err = w.DefaultImageConfig(ctx, sOpt.Resolver, platform)
if err != nil {
return nil, errors.Wrap(err, "error resolving default image config")
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/azlinux/handle_depsonly.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func handleDepsOnly(w worker) gwclient.BuildFunc {
return nil, nil, err
}

img, err := resolveBaseConfig(ctx, w, client, platform, spec, targetKey)
img, err := resolveBaseConfig(ctx, w, sOpt, platform, spec, targetKey)
if err != nil {
return nil, nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions frontend/azlinux/handle_rpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func handleRPM(w worker) gwclient.BuildFunc {
if imgRef, err := runTests(ctx, client, w, spec, sOpt, st, targetKey, pg); err != nil {
// return the container ref in case of error so it can be used to debug
// the installed package state.
cfg, _ := resolveBaseConfig(ctx, w, client, platform, spec, targetKey)
cfg, _ := resolveBaseConfig(ctx, w, sOpt, platform, spec, targetKey)
return imgRef, cfg, err
}

Expand Down Expand Up @@ -94,7 +94,7 @@ func runTests(ctx context.Context, client gwclient.Client, w worker, spec *dalec
}

err = frontend.RunTests(ctx, client, spec, ref, withDeps, targetKey)
return ref, errors.Wrap(err, "TESTS FAILED")
return ref, err
}

var azlinuxRepoPlatform = dalec.RepoPlatformConfig{
Expand Down
24 changes: 16 additions & 8 deletions frontend/deb/distro/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,23 @@ import (
)

func (c *Config) BuildContainer(worker llb.State, sOpt dalec.SourceOpts, client gwclient.Client, spec *dalec.Spec, targetKey string, debSt llb.State, opts ...llb.ConstraintsOpt) (llb.State, error) {
base := dalec.GetBaseOutputImage(spec, targetKey)
if base == "" {
base = c.DefaultOutputImage
bi, err := spec.GetSingleBase(targetKey)
if err != nil {
return llb.Scratch(), err
}

if base == "" {
return llb.Scratch(), fmt.Errorf("no output image ref specified, cannot build from scratch")
var baseImg llb.State
if bi != nil {
img, err := bi.ToState(sOpt, opts...)
if err != nil {
return llb.Scratch(), err
}
baseImg = img
} else {
if c.DefaultOutputImage == "" {
return llb.Scratch(), fmt.Errorf("no output image ref specified, cannot build from scratch")
}
baseImg = llb.Image(c.DefaultOutputImage, llb.WithMetaResolver(sOpt.Resolver), dalec.WithConstraints(opts...))
}

opts = append(opts, dalec.ProgressGroup("Build Container Image"))
Expand All @@ -31,8 +41,6 @@ func (c *Config) BuildContainer(worker llb.State, sOpt dalec.SourceOpts, client
return llb.Scratch(), err
}

baseImg := llb.Image(base, llb.WithMetaResolver(sOpt.Resolver), dalec.WithConstraints(opts...))

debug := llb.Scratch().File(llb.Mkfile("debug", 0o644, []byte(`debug=2`)), opts...)
opts = append(opts, dalec.ProgressGroup("Install spec package"))

Expand Down Expand Up @@ -93,7 +101,7 @@ func (c *Config) HandleContainer(ctx context.Context, client gwclient.Client) (*
return nil, nil, err
}

img, err := c.BuildImageConfig(ctx, client, spec, platform, targetKey)
img, err := c.BuildImageConfig(ctx, sOpt, spec, platform, targetKey)
if err != nil {
return nil, nil, err
}
Expand Down
21 changes: 11 additions & 10 deletions frontend/deb/distro/distro.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"github.com/Azure/dalec"
"github.com/Azure/dalec/frontend"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/client/llb/sourceresolver"
gwclient "github.com/moby/buildkit/frontend/gateway/client"
"github.com/moby/buildkit/frontend/subrequests/targets"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
Expand Down Expand Up @@ -39,8 +38,8 @@ type Config struct {
ExtraRepos []dalec.PackageRepositoryConfig
}

func (cfg *Config) BuildImageConfig(ctx context.Context, resolver llb.ImageMetaResolver, spec *dalec.Spec, platform *ocispecs.Platform, targetKey string) (*dalec.DockerImageSpec, error) {
img, err := resolveConfig(ctx, resolver, spec, platform, targetKey)
func (cfg *Config) BuildImageConfig(ctx context.Context, sOpt dalec.SourceOpts, spec *dalec.Spec, platform *ocispecs.Platform, targetKey string) (*dalec.DockerImageSpec, error) {
img, err := resolveConfig(ctx, sOpt, spec, platform, targetKey)
if err != nil {
return nil, err
}
Expand All @@ -52,17 +51,19 @@ func (cfg *Config) BuildImageConfig(ctx context.Context, resolver llb.ImageMetaR
return img, nil
}

func resolveConfig(ctx context.Context, resolver llb.ImageMetaResolver, spec *dalec.Spec, platform *ocispecs.Platform, targetKey string) (*dalec.DockerImageSpec, error) {
ref := dalec.GetBaseOutputImage(spec, targetKey)
if ref == "" {
func resolveConfig(ctx context.Context, sOpt dalec.SourceOpts, spec *dalec.Spec, platform *ocispecs.Platform, targetKey string) (*dalec.DockerImageSpec, error) {
bi, err := spec.GetSingleBase(targetKey)
if err != nil {
return nil, err
}

if bi == nil {
return dalec.BaseImageConfig(platform), nil
}

_, _, dt, err := resolver.ResolveImageConfig(ctx, ref, sourceresolver.Opt{
Platform: platform,
})
dt, err := bi.ResolveImageConfig(ctx, sOpt, platform)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "error resolving base image config")
}

var img dalec.DockerImageSpec
Expand Down
2 changes: 1 addition & 1 deletion frontend/deb/distro/pkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ func (cfg *Config) HandleDeb(ctx context.Context, client gwclient.Client) (*gwcl
}

if ref, err := cfg.runTests(ctx, client, spec, sOpt, targetKey, ctr, pg); err != nil {
cfg, _ := cfg.BuildImageConfig(ctx, client, spec, platform, targetKey)
cfg, _ := cfg.BuildImageConfig(ctx, sOpt, spec, platform, targetKey)
return ref, cfg, err
}

Expand Down
17 changes: 10 additions & 7 deletions frontend/windows/handle_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/Azure/dalec"
"github.com/Azure/dalec/frontend"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/client/llb/sourceresolver"
"github.com/moby/buildkit/frontend/dockerui"
gwclient "github.com/moby/buildkit/frontend/gateway/client"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
Expand Down Expand Up @@ -84,14 +83,18 @@ func handleContainer(ctx context.Context, client gwclient.Client) (*gwclient.Res
return nil, nil, err
}

imgRef := dalec.GetBaseOutputImage(spec, targetKey)
if imgRef == "" {
imgRef = defaultBaseImage
bi, err := spec.GetSingleBase(targetKey)
if err != nil {
return nil, nil, err
}

_, _, dt, err := client.ResolveImageConfig(ctx, imgRef, sourceresolver.Opt{
Platform: &targetPlatform,
})
if bi == nil {
bi = &dalec.BaseImage{Rootfs: dalec.Source{
DockerImage: &dalec.SourceDockerImage{Ref: defaultBaseImage},
}}
}

dt, err := bi.ResolveImageConfig(ctx, sOpt, &targetPlatform)
if err != nil {
return nil, nil, errors.Wrap(err, "could not resolve base image config")
}
Expand Down
Loading

0 comments on commit 0069430

Please sign in to comment.