Skip to content

Commit

Permalink
[RFC] Support Bazel platform mappings
Browse files Browse the repository at this point in the history
This is a proof of concept/RFC to support Bazel platforms in skaffold.

Skaffold uses github.com/opencontainers/image-spec/specs-go/v1 and
containerd strings like "linux/amd64" or "linux/arm64/v7" to represent
platforms.

Bazel's platform system uses bazel targets to define platforms
(https://bazel.build/extending/platforms). While there are common
cpu and os definitions available, each Bazel project defines its own
target platforms (!)

Currently, when skaffold invokes bazel, skaffold's platform information
is not passed to Bazel. Bazel defaults to the host platform, unless the
target skaffold is building explicitly specifies a platform.

This change allows a build-level setting for configuring Bazel and
defines a platform mapping from Skaffold platforms to Bazel platforms.
The Bazel artifact builder then passes the --platforms flag to Bazel if applicable.

This allows a Bazel + Skaffold project to flexibly target multiple platforms.

This PR is intended to start a discussion w/ the Skaffold team on whether
this should be supported and if so, how it should be implemented. If there's
interest we can discuss pushing forward from there. This doesn't include
any tests or documentation, and is not intended to be merged as-is.
  • Loading branch information
aran committed Feb 23, 2024
1 parent eeaf001 commit 450c948
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 7 deletions.
16 changes: 14 additions & 2 deletions pkg/skaffold/build/bazel/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (b *Builder) Build(ctx context.Context, out io.Writer, artifact *latest.Art

a := artifact.ArtifactType.BazelArtifact

tarPath, err := b.buildTar(ctx, out, artifact.Workspace, a)
tarPath, err := b.buildTar(ctx, out, artifact.Workspace, a, matcher)
if err != nil {
return "", err
}
Expand All @@ -58,7 +58,7 @@ func (b *Builder) Build(ctx context.Context, out io.Writer, artifact *latest.Art

func (b *Builder) SupportedPlatforms() platform.Matcher { return platform.All }

func (b *Builder) buildTar(ctx context.Context, out io.Writer, workspace string, a *latest.BazelArtifact) (string, error) {
func (b *Builder) buildTar(ctx context.Context, out io.Writer, workspace string, a *latest.BazelArtifact, matcher platform.Matcher) (string, error) {
if !strings.HasSuffix(a.BuildTarget, ".tar") {
return "", errors.New("the bazel build target should end with .tar, see https://github.com/bazelbuild/rules_docker#using-with-docker-locally")
}
Expand All @@ -67,6 +67,18 @@ func (b *Builder) buildTar(ctx context.Context, out io.Writer, workspace string,
args = append(args, a.BuildArgs...)
args = append(args, a.BuildTarget)

if b.bazelCfg != nil {
platformMappings := b.bazelCfg.PlatformMappings
for _, mapping := range platformMappings {
m, err := platform.Parse([]string{mapping.Platform})
if err == nil {
if matcher.Intersect(m).IsNotEmpty() {
args = append(args, fmt.Sprintf("--platforms=%s", mapping.BazelPlatformTarget))
}
}
}
}

if output.IsColorable(out) {
args = append(args, "--color=yes")
} else {
Expand Down
9 changes: 7 additions & 2 deletions pkg/skaffold/build/bazel/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,25 @@ limitations under the License.

package bazel

import "github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/docker"
import (
"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/docker"
"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/schema/latest"
)

// Builder is an artifact builder that uses Bazel
type Builder struct {
localDocker docker.LocalDaemon
cfg docker.Config
pushImages bool
bazelCfg *latest.BazelBuildConfig
}

// NewArtifactBuilder returns a new bazel artifact builder
func NewArtifactBuilder(localDocker docker.LocalDaemon, cfg docker.Config, pushImages bool) *Builder {
func NewArtifactBuilder(localDocker docker.LocalDaemon, cfg docker.Config, pushImages bool, bazelCfg *latest.BazelBuildConfig) *Builder {
return &Builder{
localDocker: localDocker,
cfg: cfg,
pushImages: pushImages,
bazelCfg: bazelCfg,
}
}
6 changes: 4 additions & 2 deletions pkg/skaffold/build/local/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type Builder struct {
localPruner *pruner
artifactStore build.ArtifactStore
sourceDependencies graph.SourceDependenciesCache
bazelCfg *latest.BazelBuildConfig
}

type Config interface {
Expand All @@ -80,7 +81,7 @@ type BuilderContext interface {
}

// NewBuilder returns an new instance of a local Builder.
func NewBuilder(ctx context.Context, bCtx BuilderContext, buildCfg *latest.LocalBuild) (*Builder, error) {
func NewBuilder(ctx context.Context, bCtx BuilderContext, buildCfg *latest.LocalBuild, bazelCfg *latest.BazelBuildConfig) (*Builder, error) {
localDocker, err := docker.NewAPIClient(ctx, bCtx)
if err != nil {
return nil, fmt.Errorf("getting docker client: %w", err)
Expand Down Expand Up @@ -120,6 +121,7 @@ func NewBuilder(ctx context.Context, bCtx BuilderContext, buildCfg *latest.Local
muted: bCtx.Muted(),
artifactStore: bCtx.ArtifactStore(),
sourceDependencies: bCtx.SourceDependenciesResolver(),
bazelCfg: bazelCfg,
}, nil
}

Expand All @@ -136,7 +138,7 @@ func newPerArtifactBuilder(b *Builder, a *latest.Artifact) (artifactBuilder, err
return dockerbuilder.NewArtifactBuilder(b.localDocker, b.cfg, b.local.UseDockerCLI, b.local.UseBuildkit, b.pushImages, b.artifactStore, b.sourceDependencies), nil

case a.BazelArtifact != nil:
return bazel.NewArtifactBuilder(b.localDocker, b.cfg, b.pushImages), nil
return bazel.NewArtifactBuilder(b.localDocker, b.cfg, b.pushImages, b.bazelCfg), nil

case a.JibArtifact != nil:
return jib.NewArtifactBuilder(b.localDocker, b.cfg, b.pushImages, b.skipTests, b.artifactStore), nil
Expand Down
2 changes: 1 addition & 1 deletion pkg/skaffold/runner/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func GetBuilder(ctx context.Context, r *runcontext.RunContext, s build.ArtifactS
switch {
case p.Build.LocalBuild != nil:
log.Entry(context.TODO()).Debug("Using builder: local")
builder, err := local.NewBuilder(ctx, bCtx, p.Build.LocalBuild)
builder, err := local.NewBuilder(ctx, bCtx, p.Build.LocalBuild, p.Build.Bazel)
if err != nil {
return nil, err
}
Expand Down
11 changes: 11 additions & 0 deletions pkg/skaffold/schema/latest/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ type BuildConfig struct {
// Example: `["linux/amd64", "linux/arm64"]`.
Platforms []string `yaml:"platforms,omitempty"`

Bazel *BazelBuildConfig `yaml:"bazel,omitempty"`

BuildType `yaml:",inline"`
}

Expand Down Expand Up @@ -331,6 +333,15 @@ type TaggerComponent struct {
Component TagPolicy `yaml:",inline" yamltags:"skipTrim"`
}

type BazelBuildConfig struct {
PlatformMappings []BazelPlatformMapping `yaml:"platforms,omitempty"`
}

type BazelPlatformMapping struct {
Platform string `yaml:"platform,omitempty"`
BazelPlatformTarget string `yaml:"target,omitempty"`
}

// BuildType contains the specific implementation and parameters needed
// for the build step. Only one field should be populated.
type BuildType struct {
Expand Down

0 comments on commit 450c948

Please sign in to comment.