diff --git a/integration/run_test.go b/integration/run_test.go index 33e4c35c7ed..5409b8da40e 100644 --- a/integration/run_test.go +++ b/integration/run_test.go @@ -104,6 +104,17 @@ var tests = []struct { dir: "examples/bazel", pods: []string{"bazel"}, }, + { + description: "bazel oci", + dir: "testdata/bazel-rules-oci", + deployments: []string{"helloweb"}, + }, + { + description: "bazel oci sub-directory", + dir: "testdata/bazel-rules-oci", + args: []string{"-p", "target-with-package"}, + deployments: []string{"helloweb"}, + }, { description: "jib", dir: "testdata/jib", diff --git a/integration/testdata/bazel-rules-oci/BUILD b/integration/testdata/bazel-rules-oci/BUILD new file mode 100644 index 00000000000..821f8c599d9 --- /dev/null +++ b/integration/testdata/bazel-rules-oci/BUILD @@ -0,0 +1,7 @@ +load("@rules_oci//oci:defs.bzl", "oci_tarball") + +oci_tarball( + name = "hello.tar", + image = "@hello//:hello", + repo_tags = ["hello:latest"], +) diff --git a/integration/testdata/bazel-rules-oci/WORKSPACE b/integration/testdata/bazel-rules-oci/WORKSPACE new file mode 100644 index 00000000000..07e22a13b7b --- /dev/null +++ b/integration/testdata/bazel-rules-oci/WORKSPACE @@ -0,0 +1,30 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "rules_oci", + sha256 = "db57efd706f01eb3ce771468366baa1614b5b25f4cce99757e2b8d942155b8ec", + strip_prefix = "rules_oci-1.0.0", + url = "https://github.com/bazel-contrib/rules_oci/releases/download/v1.0.0/rules_oci-v1.0.0.tar.gz", +) + +load("@rules_oci//oci:dependencies.bzl", "rules_oci_dependencies") + +rules_oci_dependencies() + +load("@rules_oci//oci:repositories.bzl", "LATEST_CRANE_VERSION", "LATEST_ZOT_VERSION", "oci_register_toolchains") + +oci_register_toolchains( + name = "oci", + crane_version = LATEST_CRANE_VERSION, + # Uncommenting the zot toolchain will cause it to be used instead of crane for some tasks. + # Note that it does not support docker-format images. + # zot_version = LATEST_ZOT_VERSION, +) + +load("@rules_oci//oci:pull.bzl", "oci_pull") + +oci_pull( + name = "hello", + digest = "sha256:845f77fab71033404f4cfceaa1ddb27b70c3551ceb22a5e7f4498cdda6c9daea", + image = "us-docker.pkg.dev/google-samples/containers/gke/hello-app", +) diff --git a/integration/testdata/bazel-rules-oci/deploy.yaml b/integration/testdata/bazel-rules-oci/deploy.yaml new file mode 100644 index 00000000000..528f39217a9 --- /dev/null +++ b/integration/testdata/bazel-rules-oci/deploy.yaml @@ -0,0 +1,25 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: helloweb + labels: + app: hello +spec: + selector: + matchLabels: + app: hello + tier: web + template: + metadata: + labels: + app: hello + tier: web + spec: + containers: + - name: hello-app + image: hello-image + ports: + - containerPort: 8080 + resources: + requests: + cpu: 200m \ No newline at end of file diff --git a/integration/testdata/bazel-rules-oci/skaffold.yaml b/integration/testdata/bazel-rules-oci/skaffold.yaml new file mode 100644 index 00000000000..6ebf5795977 --- /dev/null +++ b/integration/testdata/bazel-rules-oci/skaffold.yaml @@ -0,0 +1,23 @@ +apiVersion: skaffold/v4beta5 +kind: Config +metadata: + name: hello +build: + tagPolicy: + sha256: {} + artifacts: + - image: hello-image + bazel: + target: //:hello.tar +deploy: + kubectl: {} +manifests: + rawYaml: + - "deploy.yaml" +profiles: + - name: target-with-package + build: + artifacts: + - image: hello-image + bazel: + target: //sub-dir:hello.tar diff --git a/integration/testdata/bazel-rules-oci/sub-dir/BUILD b/integration/testdata/bazel-rules-oci/sub-dir/BUILD new file mode 100644 index 00000000000..14dd5caba2d --- /dev/null +++ b/integration/testdata/bazel-rules-oci/sub-dir/BUILD @@ -0,0 +1,7 @@ +load("@rules_oci//oci:defs.bzl", "oci_tarball") + +oci_tarball( + name = "hello.tar", + image = "@hello//:hello", + repo_tags = ["hello-whatever:latest"], +) diff --git a/pkg/skaffold/build/bazel/build.go b/pkg/skaffold/build/bazel/build.go index ae10565fc6b..ec6db37142a 100644 --- a/pkg/skaffold/build/bazel/build.go +++ b/pkg/skaffold/build/bazel/build.go @@ -26,6 +26,8 @@ import ( "path/filepath" "strings" + "github.com/google/go-containerregistry/pkg/v1/tarball" + "github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/docker" "github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/output" "github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/output/log" @@ -51,7 +53,7 @@ func (b *Builder) Build(ctx context.Context, out io.Writer, artifact *latest.Art if b.pushImages { return docker.Push(tarPath, tag, b.cfg, nil) } - return b.loadImage(ctx, out, tarPath, a, tag) + return b.loadImage(ctx, out, tarPath, tag) } func (b *Builder) SupportedPlatforms() platform.Matcher { return platform.All } @@ -88,14 +90,22 @@ func (b *Builder) buildTar(ctx context.Context, out io.Writer, workspace string, return tarPath, nil } -func (b *Builder) loadImage(ctx context.Context, out io.Writer, tarPath string, a *latest.BazelArtifact, tag string) (string, error) { +func (b *Builder) loadImage(ctx context.Context, out io.Writer, tarPath string, tag string) (string, error) { + manifest, err := tarball.LoadManifest(func() (io.ReadCloser, error) { + return os.Open(tarPath) + }) + + if err != nil { + return "", fmt.Errorf("loading manifest from tarball failed: %w", err) + } + imageTar, err := os.Open(tarPath) if err != nil { return "", fmt.Errorf("opening image tarball: %w", err) } defer imageTar.Close() - bazelTag := buildImageTag(a.BuildTarget) + bazelTag := manifest[0].RepoTags[0] imageID, err := b.localDocker.Load(ctx, out, imageTar, bazelTag) if err != nil { return "", fmt.Errorf("loading image into docker daemon: %w", err) @@ -143,26 +153,3 @@ func bazelTarPath(ctx context.Context, workspace string, a *latest.BazelArtifact return filepath.Join(execRoot, targetPath), nil } - -func trimTarget(buildTarget string) string { - // TODO(r2d4): strip off leading //:, bad - trimmedTarget := strings.TrimPrefix(buildTarget, "//") - // Useful if root target "//:target" - trimmedTarget = strings.TrimPrefix(trimmedTarget, ":") - - return trimmedTarget -} - -func buildImageTag(buildTarget string) string { - imageTag := trimTarget(buildTarget) - imageTag = strings.TrimPrefix(imageTag, ":") - - // TODO(r2d4): strip off trailing .tar, even worse - imageTag = strings.TrimSuffix(imageTag, ".tar") - - if strings.Contains(imageTag, ":") { - return fmt.Sprintf("bazel/%s", imageTag) - } - - return fmt.Sprintf("bazel:%s", imageTag) -} diff --git a/pkg/skaffold/build/bazel/build_test.go b/pkg/skaffold/build/bazel/build_test.go index 6c746290137..ea2e2cca22e 100644 --- a/pkg/skaffold/build/bazel/build_test.go +++ b/pkg/skaffold/build/bazel/build_test.go @@ -128,14 +128,6 @@ func TestBazelTarPath(t *testing.T) { }) } -func TestBuildImageTag(t *testing.T) { - buildTarget := "//:skaffold_example.tar" - - imageTag := buildImageTag(buildTarget) - - testutil.CheckDeepEqual(t, "bazel:skaffold_example", imageTag) -} - func fakeLocalDaemon() docker.LocalDaemon { return docker.NewLocalDaemon(&testutil.FakeAPIClient{}, nil, false, nil) } diff --git a/testutil/fake_image_api.go b/testutil/fake_image_api.go index a63916aaf99..d01887f9ba8 100644 --- a/testutil/fake_image_api.go +++ b/testutil/fake_image_api.go @@ -23,7 +23,6 @@ import ( "fmt" "io" "math" - "os" "strings" "sync" "sync/atomic" @@ -32,6 +31,9 @@ import ( "github.com/docker/docker/api/types/registry" "github.com/docker/docker/client" reg "github.com/docker/docker/registry" + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/random" + "github.com/google/go-containerregistry/pkg/v1/tarball" "github.com/opencontainers/go-digest" v1 "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -307,16 +309,25 @@ func (f *FakeAPIClient) DiskUsage(ctx context.Context) (types.DiskUsage, error) func (f *FakeAPIClient) Close() error { return nil } -// TODO(dgageot): create something that looks more like an actual tar file. func CreateFakeImageTar(ref string, path string) error { - return os.WriteFile(path, []byte(ref), os.ModePerm) + image, err := random.Image(1024, 1) + if err != nil { + return fmt.Errorf("failed to create fake image %w", err) + } + reference, err := name.ParseReference(ref) + if err != nil { + return fmt.Errorf("failed to parse reference %w", err) + } + return tarball.WriteToFile(path, reference, image) } func ReadRefFromFakeTar(input io.Reader) (string, error) { - buf, err := io.ReadAll(input) + manifest, err := tarball.LoadManifest(func() (io.ReadCloser, error) { + return io.NopCloser(input), nil + }) if err != nil { - return "", fmt.Errorf("reading tar") + return "", fmt.Errorf("loading manifest %w", err) } - return string(buf), nil + return manifest[0].RepoTags[0], nil }