diff --git a/frontend/azlinux/handle_rpm.go b/frontend/azlinux/handle_rpm.go index 87e89f54..fe1a3eab 100644 --- a/frontend/azlinux/handle_rpm.go +++ b/frontend/azlinux/handle_rpm.go @@ -133,5 +133,5 @@ func specToRpmLLB(ctx context.Context, w worker, client gwclient.Client, spec *d specPath := filepath.Join("SPECS", spec.Name, spec.Name+".spec") st := rpm.Build(br, base, specPath, opts...) - return frontend.MaybeSign(ctx, client, st, spec, targetKey, sOpt) + return frontend.MaybeSign(ctx, client, st, spec, targetKey, sOpt, opts...) } diff --git a/frontend/jammy/handle_deb.go b/frontend/jammy/handle_deb.go index 96993f41..fb933ae2 100644 --- a/frontend/jammy/handle_deb.go +++ b/frontend/jammy/handle_deb.go @@ -96,7 +96,7 @@ func buildDeb(ctx context.Context, client gwclient.Client, spec *dalec.Spec, sOp return llb.Scratch(), err } - signed, err := frontend.MaybeSign(ctx, client, st, spec, targetKey, sOpt) + signed, err := frontend.MaybeSign(ctx, client, st, spec, targetKey, sOpt, opts...) if err != nil { return llb.Scratch(), err } diff --git a/frontend/request.go b/frontend/request.go index 5701fe7f..3c2aaf6c 100644 --- a/frontend/request.go +++ b/frontend/request.go @@ -2,7 +2,6 @@ package frontend import ( "context" - "fmt" "strconv" "github.com/Azure/dalec" @@ -141,9 +140,9 @@ func marshalDockerfile(ctx context.Context, dt []byte, opts ...llb.ConstraintsOp return st.Marshal(ctx) } -func getSigningConfigFromContext(ctx context.Context, client gwclient.Client, cfgPath string, configCtxName string, sOpt dalec.SourceOpts) (*dalec.PackageSigner, error) { +func getSigningConfigFromContext(ctx context.Context, client gwclient.Client, cfgPath string, configCtxName string, sOpt dalec.SourceOpts, opts ...llb.ConstraintsOpt) (*dalec.PackageSigner, error) { sc := dalec.SourceContext{Name: configCtxName} - signConfigState, err := sc.AsState([]string{cfgPath}, nil, sOpt) + signConfigState, err := sc.AsState([]string{cfgPath}, nil, sOpt, opts...) if err != nil { return nil, err } @@ -180,7 +179,7 @@ func getSigningConfigFromContext(ctx context.Context, client gwclient.Client, cf return pc.Signer, nil } -func MaybeSign(ctx context.Context, client gwclient.Client, st llb.State, spec *dalec.Spec, targetKey string, sOpt dalec.SourceOpts) (llb.State, error) { +func MaybeSign(ctx context.Context, client gwclient.Client, st llb.State, spec *dalec.Spec, targetKey string, sOpt dalec.SourceOpts, opts ...llb.ConstraintsOpt) (llb.State, error) { if signingDisabled(client) { Warnf(ctx, client, st, "Signing disabled by build-arg %q", keySkipSigningArg) return st, nil @@ -198,7 +197,7 @@ func MaybeSign(ctx context.Context, client gwclient.Client, st llb.State, spec * Warnf(ctx, client, st, "Root signing spec overridden by target signing spec: target %q", targetKey) } - return forwardToSigner(ctx, client, cfg, st) + return forwardToSigner(ctx, client, cfg, st, opts...) } configCtxName := getSignContextNameWithDefault(client) @@ -211,7 +210,7 @@ func MaybeSign(ctx context.Context, client gwclient.Client, st llb.State, spec * return llb.Scratch(), err } - return forwardToSigner(ctx, client, cfg, st) + return forwardToSigner(ctx, client, cfg, st, opts...) } func getSignContextNameWithDefault(client gwclient.Client) string { @@ -245,21 +244,20 @@ func getSignConfigCtxName(client gwclient.Client) string { return client.BuildOpts().Opts["build-arg:"+buildArgDalecSigningConfigContextName] } -func forwardToSigner(ctx context.Context, client gwclient.Client, cfg *dalec.PackageSigner, s llb.State) (llb.State, error) { +func forwardToSigner(ctx context.Context, client gwclient.Client, cfg *dalec.PackageSigner, s llb.State, opts ...llb.ConstraintsOpt) (llb.State, error) { const ( - sourceKey = "source" - contextKey = "context" - inputKey = "input" + // See https://github.com/moby/buildkit/blob/d8d946b85c52095d34a52ce210960832f4e06775/frontend/dockerui/context.go#L29 + contextKey = "contextkey" ) - opts := client.BuildOpts().Opts + bopts := client.BuildOpts().Opts req, err := newSolveRequest(toFrontend(cfg.Frontend), withBuildArgs(cfg.Args)) if err != nil { return llb.Scratch(), err } - for k, v := range opts { + for k, v := range bopts { if k == "source" || k == "cmdline" { continue } @@ -282,18 +280,19 @@ func forwardToSigner(ctx context.Context, client gwclient.Client, cfg *dalec.Pac } req.FrontendInputs = m - stateDef, err := s.Marshal(ctx) + opts = append(opts, dalec.ProgressGroup("Sign package")) + stateDef, err := s.Marshal(ctx, opts...) if err != nil { return llb.Scratch(), err } - req.FrontendOpt[contextKey] = compound(inputKey, contextKey) - req.FrontendInputs[contextKey] = stateDef.ToPB() - req.FrontendOpt["dalec.target"] = opts["dalec.target"] + req.FrontendOpt[contextKey] = dockerui.DefaultLocalNameContext + req.FrontendInputs[dockerui.DefaultLocalNameContext] = stateDef.ToPB() + req.FrontendOpt["dalec.target"] = bopts["dalec.target"] res, err := client.Solve(ctx, req) if err != nil { - return llb.Scratch(), err + return llb.Scratch(), errors.Wrap(err, "error signing packages") } ref, err := res.SingleRef() @@ -303,7 +302,3 @@ func forwardToSigner(ctx context.Context, client gwclient.Client, cfg *dalec.Pac return ref.ToState() } - -func compound(k, v string) string { - return fmt.Sprintf("%s:%s", k, v) -} diff --git a/frontend/windows/handle_zip.go b/frontend/windows/handle_zip.go index cc95b05a..e52a8d77 100644 --- a/frontend/windows/handle_zip.go +++ b/frontend/windows/handle_zip.go @@ -38,12 +38,12 @@ func handleZip(ctx context.Context, client gwclient.Client) (*gwclient.Result, e return nil, nil, err } - bin, err := buildBinaries(ctx, spec, worker, client, sOpt, targetKey) + bin, err := buildBinaries(ctx, spec, worker, client, sOpt, targetKey, pg) if err != nil { return nil, nil, fmt.Errorf("unable to build binaries: %w", err) } - st := getZipLLB(worker, spec.Name, bin) + st := getZipLLB(worker, spec.Name, bin, pg) def, err := st.Marshal(ctx) if err != nil { @@ -82,7 +82,7 @@ func specToSourcesLLB(worker llb.State, spec *dalec.Spec, sOpt dalec.SourceOpts, return out, nil } -func installBuildDeps(deps []string) llb.StateOption { +func installBuildDeps(deps []string, opts ...llb.ConstraintsOpt) llb.StateOption { return func(s llb.State) llb.State { if len(deps) == 0 { return s @@ -94,6 +94,7 @@ func installBuildDeps(deps []string) llb.StateOption { return s.Run( dalec.ShArgs("apt-get update && apt-get install -y "+strings.Join(sorted, " ")), dalec.WithMountedAptCache(aptCachePrefix), + dalec.WithConstraints(opts...), ).Root() } } @@ -127,13 +128,13 @@ func withSourcesMounted(dst string, states map[string]llb.State, sources map[str return dalec.WithRunOptions(ordered...) } -func buildBinaries(ctx context.Context, spec *dalec.Spec, worker llb.State, client gwclient.Client, sOpt dalec.SourceOpts, targetKey string) (llb.State, error) { +func buildBinaries(ctx context.Context, spec *dalec.Spec, worker llb.State, client gwclient.Client, sOpt dalec.SourceOpts, targetKey string, opts ...llb.ConstraintsOpt) (llb.State, error) { deps := dalec.SortMapKeys(spec.GetBuildDeps(targetKey)) // note: we do not yet support pinning build dependencies for windows workers - worker = worker.With(installBuildDeps(deps)) + worker = worker.With(installBuildDeps(deps, opts...)) - sources, err := specToSourcesLLB(worker, spec, sOpt) + sources, err := specToSourcesLLB(worker, spec, sOpt, opts...) if err != nil { return llb.Scratch(), errors.Wrap(err, "could not generate sources") } @@ -149,17 +150,19 @@ func buildBinaries(ctx context.Context, spec *dalec.Spec, worker llb.State, clie withSourcesMounted("/build", patched, spec.Sources), llb.AddMount("/tmp/scripts", buildScript), llb.Network(llb.NetModeNone), + dalec.WithConstraints(opts...), ).AddMount(outputDir, llb.Scratch()) return frontend.MaybeSign(ctx, client, st, spec, targetKey, sOpt) } -func getZipLLB(worker llb.State, name string, artifacts llb.State) llb.State { +func getZipLLB(worker llb.State, name string, artifacts llb.State, opts ...llb.ConstraintsOpt) llb.State { outName := filepath.Join(outputDir, name+".zip") zipped := worker.Run( dalec.ShArgs("zip "+outName+" *"), llb.Dir("/tmp/artifacts"), llb.AddMount("/tmp/artifacts", artifacts), + dalec.WithConstraints(opts...), ).AddMount(outputDir, llb.Scratch()) return zipped } @@ -197,6 +200,7 @@ func workerImg(sOpt dalec.SourceOpts, opts ...llb.ConstraintsOpt) (llb.State, er Run( dalec.ShArgs("apt-get update && apt-get install -y build-essential binutils-mingw-w64 g++-mingw-w64-x86-64 gcc git make pkg-config quilt zip"), dalec.WithMountedAptCache(aptCachePrefix), + dalec.WithConstraints(opts...), ).Root(), nil } diff --git a/test/azlinux_test.go b/test/azlinux_test.go index f0e44372..6108cb8a 100644 --- a/test/azlinux_test.go +++ b/test/azlinux_test.go @@ -13,6 +13,7 @@ import ( "github.com/moby/buildkit/client/llb" gwclient "github.com/moby/buildkit/frontend/gateway/client" moby_buildkit_v1_frontend "github.com/moby/buildkit/frontend/gateway/pb" + ocispecs "github.com/opencontainers/image-spec/specs-go/v1" ) var azlinuxConstraints = constraintsSymbols{ @@ -35,6 +36,7 @@ func TestMariner2(t *testing.T) { FormatDepEqual: func(v, _ string) string { return v }, + ListExpectedSignFiles: azlinuxListSignFiles("cm2"), }, LicenseDir: "/usr/share/licenses", SystemdDir: struct { @@ -62,9 +64,10 @@ func TestAzlinux3(t *testing.T) { ctx := startTestSpan(baseCtx, t) testLinuxDistro(ctx, t, testLinuxConfig{ Target: targetConfig{ - Package: "azlinux3/rpm", - Container: "azlinux3/container", - Worker: "azlinux3/worker", + Package: "azlinux3/rpm", + Container: "azlinux3/container", + Worker: "azlinux3/worker", + ListExpectedSignFiles: azlinuxListSignFiles("azl3"), }, LicenseDir: "/usr/share/licenses", SystemdDir: struct { @@ -86,6 +89,27 @@ func TestAzlinux3(t *testing.T) { }) } +func azlinuxListSignFiles(ver string) func(*dalec.Spec, ocispecs.Platform) []string { + return func(spec *dalec.Spec, platform ocispecs.Platform) []string { + base := fmt.Sprintf("%s-%s-%s.%s", spec.Name, spec.Version, spec.Revision, ver) + + var arch string + switch platform.Architecture { + case "amd64": + arch = "x86_64" + case "arm64": + arch = "aarch64" + default: + arch = platform.Architecture + } + + return []string{ + filepath.Join("SRPMS", fmt.Sprintf("%s.src.rpm", base)), + filepath.Join("RPMS", arch, fmt.Sprintf("%s.%s.rpm", base, arch)), + } + } +} + func azlinuxWithRepo(rpms llb.State) llb.StateOption { return func(in llb.State) llb.State { localRepo := []byte(` @@ -145,6 +169,10 @@ type targetConfig struct { // what is neccessary for the target distro to set a dependency for an equals // operator. FormatDepEqual func(ver, rev string) string + + // Given a spec, list all files (including the full path) that are expected + // to be sent to be signed. + ListExpectedSignFiles func(*dalec.Spec, ocispecs.Platform) []string } type testLinuxConfig struct { @@ -491,7 +519,7 @@ echo "$BAR" > bar.txt }) }) - t.Run("test signing", linuxSigningTests(ctx, testConfig)) + t.Run("signing", linuxSigningTests(ctx, testConfig)) t.Run("test systemd unit single", func(t *testing.T) { t.Parallel() diff --git a/test/fixtures/signer/main.go b/test/fixtures/signer/main.go index fb4b1c46..3d6c7540 100644 --- a/test/fixtures/signer/main.go +++ b/test/fixtures/signer/main.go @@ -5,15 +5,18 @@ import ( _ "embed" "encoding/json" "fmt" + "io/fs" "os" "strings" - "github.com/Azure/dalec/frontend" + "github.com/Azure/dalec/frontend/pkg/bkfs" "github.com/moby/buildkit/client/llb" + "github.com/moby/buildkit/frontend/dockerui" gwclient "github.com/moby/buildkit/frontend/gateway/client" "github.com/moby/buildkit/frontend/gateway/grpcclient" "github.com/moby/buildkit/util/appcontext" "github.com/moby/buildkit/util/bklog" + "github.com/pkg/errors" "github.com/sirupsen/logrus" "google.golang.org/grpc/grpclog" ) @@ -28,11 +31,6 @@ func main() { bopts := c.BuildOpts().Opts target := bopts["dalec.target"] - inputs, err := c.Inputs(ctx) - if err != nil { - return nil, err - } - type config struct { OS string } @@ -46,33 +44,56 @@ func main() { cfg.OS = "linux" } - curFrontend, ok := c.(frontend.CurrentFrontend) - if !ok { - return nil, fmt.Errorf("cast to currentFrontend failed") + dc, err := dockerui.NewClient(c) + if err != nil { + return nil, err } - basePtr, err := curFrontend.CurrentFrontend() - if err != nil || basePtr == nil { - if err == nil { - err = fmt.Errorf("base frontend ptr was nil") - } + bctx, err := dc.MainContext(ctx) + if err != nil { return nil, err } - inputId := strings.TrimPrefix(bopts["context"], "input:") - _, ok = inputs[inputId] - if !ok { + if bctx == nil { return nil, fmt.Errorf("no artifact state provided to signer") } + artifactsFS, err := bkfs.FromState(ctx, bctx, c) + if err != nil { + return nil, err + } + configBytes, err := json.Marshal(&cfg) if err != nil { return nil, err } + var files []string + err = fs.WalkDir(artifactsFS, ".", func(p string, info fs.DirEntry, err error) error { + if err != nil { + return err + } + + if info.IsDir() { + return nil + } + + files = append(files, p) + return nil + }) + if err != nil { + return nil, errors.Wrap(err, "error walking artifacts") + } + + mfst, err := json.Marshal(files) + if err != nil { + return nil, errors.Wrap(err, "error marshalling file manifest") + } + output := llb.Scratch(). File(llb.Mkfile("/target", 0o600, []byte(target))). - File(llb.Mkfile("/config.json", 0o600, configBytes)) + File(llb.Mkfile("/config.json", 0o600, configBytes)). + File(llb.Mkfile("/manifest.json", 0o600, mfst)) // For any build-arg seen, write a file to /env/ with the contents // being the value of the arg. diff --git a/test/jammy_test.go b/test/jammy_test.go index 04eb0e97..0544af77 100644 --- a/test/jammy_test.go +++ b/test/jammy_test.go @@ -1,11 +1,13 @@ package test import ( + "fmt" "testing" "github.com/Azure/dalec" "github.com/Azure/dalec/frontend/jammy" "github.com/moby/buildkit/client/llb" + ocispecs "github.com/opencontainers/image-spec/specs-go/v1" ) func TestJammy(t *testing.T) { @@ -20,6 +22,25 @@ func TestJammy(t *testing.T) { FormatDepEqual: func(ver, rev string) string { return ver + "-ubuntu22.04u" + rev }, + ListExpectedSignFiles: func(spec *dalec.Spec, platform ocispecs.Platform) []string { + base := fmt.Sprintf("%s_%s-%su%s", spec.Name, spec.Version, "ubuntu22.04", spec.Revision) + sourceBase := fmt.Sprintf("%s_%s.orig", spec.Name, spec.Version) + + out := []string{ + base + ".debian.tar.xz", + base + ".dsc", + fmt.Sprintf("%s_%s.deb", base, platform.Architecture), + base + "_source.buildinfo", + base + "_source.changes", + sourceBase + ".tar.xz", + } + + for src := range spec.Sources { + out = append(out, fmt.Sprintf("%s-%s.tar.gz", sourceBase, src)) + } + + return out + }, }, LicenseDir: "/usr/share/doc", SystemdDir: struct { diff --git a/test/signing_test.go b/test/signing_test.go index fcf19bdd..06cf2f91 100644 --- a/test/signing_test.go +++ b/test/signing_test.go @@ -2,16 +2,20 @@ package test import ( "context" + "encoding/json" "fmt" + "slices" "strings" "testing" "github.com/Azure/dalec" "github.com/Azure/dalec/test/testenv" + "github.com/containerd/containerd/platforms" "github.com/moby/buildkit/client" "github.com/moby/buildkit/client/llb" gwclient "github.com/moby/buildkit/frontend/gateway/client" "gotest.tools/v3/assert" + "gotest.tools/v3/assert/cmp" ) func runTest(t *testing.T, f testenv.TestFunc, opts ...testenv.TestRunnerOpt) { @@ -66,7 +70,7 @@ func linuxSigningTests(ctx context.Context, testConfig testLinuxConfig) func(*te t.Run("root config", func(t *testing.T) { t.Parallel() spec := newSigningSpec() - runTest(t, distroSigningTest(t, spec, testConfig.Target.Package)) + runTest(t, distroSigningTest(t, spec, testConfig.Target.Package, testConfig)) }) t.Run("with target config", func(t *testing.T) { @@ -82,7 +86,7 @@ func linuxSigningTests(ctx context.Context, testConfig testLinuxConfig) func(*te } spec.PackageConfig.Signer = nil - runTest(t, distroSigningTest(t, spec, testConfig.Target.Package)) + runTest(t, distroSigningTest(t, spec, testConfig.Target.Package, testConfig)) }) t.Run("target config takes precedence when root config is there", func(t *testing.T) { @@ -116,7 +120,7 @@ func linuxSigningTests(ctx context.Context, testConfig testLinuxConfig) func(*te } spec.PackageConfig.Signer.Image = "notexist" - runTest(t, distroSigningTest(t, spec, testConfig.Target.Package), testenv.WithSolveStatusFn(handleStatus)) + runTest(t, distroSigningTest(t, spec, testConfig.Target.Package, testConfig), testenv.WithSolveStatusFn(handleStatus)) assert.Assert(t, found, "Spec signing override warning message not emitted") }) @@ -129,7 +133,7 @@ func linuxSigningTests(ctx context.Context, testConfig testLinuxConfig) func(*te "HELLO": "world", "FOO": "bar", } - runTest(t, distroSigningTest(t, spec, testConfig.Target.Package)) + runTest(t, distroSigningTest(t, spec, testConfig.Target.Package, testConfig)) }) t.Run("with path build arg and build context", func(t *testing.T) { @@ -146,6 +150,7 @@ signer: t, spec, testConfig.Target.Package, + testConfig, withBuildContext(ctx, t, "dalec_signing_config", signConfig), withBuildArg("DALEC_SIGNING_CONFIG_CONTEXT_NAME", "dalec_signing_config"), withBuildArg("DALEC_SIGNING_CONFIG_PATH", "/unusual_place.yml"), @@ -180,6 +185,7 @@ signer: t, spec, testConfig.Target.Package, + testConfig, withBuildContext(ctx, t, "dalec_signing_config", signConfig), withBuildArg("DALEC_SIGNING_CONFIG_CONTEXT_NAME", "dalec_signing_config"), withBuildArg("DALEC_SIGNING_CONFIG_PATH", "/unusual_place.yml"), @@ -204,6 +210,7 @@ signer: t, spec, testConfig.Target.Package, + testConfig, withBuildContext(ctx, t, "dalec_signing_config", signConfig), withBuildArg("DALEC_SIGNING_CONFIG_CONTEXT_NAME", "dalec_signing_config"), withBuildArg("DALEC_SIGNING_CONFIG_PATH", "/unusual_place.yml"), @@ -225,6 +232,7 @@ signer: t, spec, testConfig.Target.Package, + testConfig, withMainContext(ctx, t, signConfig), withBuildArg("DALEC_SIGNING_CONFIG_PATH", "/sign_config.yml"), )) @@ -257,6 +265,7 @@ signer: t, spec, testConfig.Target.Package, + testConfig, withMainContext(ctx, t, signConfig), withBuildArg("DALEC_SIGNING_CONFIG_PATH", "/sign_config.yml"), ), testenv.WithSolveStatusFn(handleStatus)) @@ -361,7 +370,35 @@ signer: } } -func windowsSigningTests(t *testing.T) { +func windowsSigningTests(t *testing.T, tcfg targetConfig) { + runBuild := func(ctx context.Context, t *testing.T, gwc gwclient.Client, spec *dalec.Spec, srOpts ...srOpt) { + st := prepareWindowsSigningState(ctx, t, gwc, spec, srOpts...) + + def, err := st.Marshal(ctx) + if err != nil { + t.Fatal(err) + } + + res := solveT(ctx, t, gwc, gwclient.SolveRequest{ + Definition: def.ToPB(), + }) + + tgt := readFile(ctx, t, "target", res) + cfg := readFile(ctx, t, "config.json", res) + mfst := readFile(ctx, t, "manifest.json", res) + + assert.Check(t, cmp.Equal(string(tgt), "windowscross")) + assert.Check(t, cmp.Contains(string(cfg), "windows")) + + var files []string + assert.NilError(t, json.Unmarshal(mfst, &files)) + slices.Sort(files) + + expectedFiles := tcfg.ListExpectedSignFiles(spec, platforms.DefaultSpec()) + slices.Sort(expectedFiles) + assert.Assert(t, cmp.DeepEqual(files, expectedFiles)) + } + t.Run("target spec config", func(t *testing.T) { t.Parallel() runTest(t, func(ctx context.Context, gwc gwclient.Client) { @@ -462,7 +499,7 @@ signer: runTest(t, func(ctx context.Context, gwc gwclient.Client) { spec := newSimpleSpec() - st := prepareSigningState(ctx, t, gwc, spec, withBuildArg("DALEC_SKIP_SIGNING", "1")) + st := prepareWindowsSigningState(ctx, t, gwc, spec, withBuildArg("DALEC_SKIP_SIGNING", "1")) def, err := st.Marshal(ctx) if err != nil { @@ -486,7 +523,7 @@ signer: }) } -func distroSigningTest(t *testing.T, spec *dalec.Spec, buildTarget string, extraSrOpts ...srOpt) testenv.TestFunc { +func distroSigningTest(t *testing.T, spec *dalec.Spec, buildTarget string, tcfg testLinuxConfig, extraSrOpts ...srOpt) testenv.TestFunc { return func(ctx context.Context, gwc gwclient.Client) { topTgt, _, _ := strings.Cut(buildTarget, "/") @@ -501,6 +538,7 @@ func distroSigningTest(t *testing.T, spec *dalec.Spec, buildTarget string, extra tgt := readFile(ctx, t, "/target", res) cfg := readFile(ctx, t, "/config.json", res) + mfst := readFile(ctx, t, "/manifest.json", res) if string(tgt) != topTgt { t.Fatal(fmt.Errorf("target incorrect; either not sent to signer or not received back from signer")) @@ -516,6 +554,19 @@ func distroSigningTest(t *testing.T, spec *dalec.Spec, buildTarget string, extra assert.Equal(t, v, string(dt)) } } + + if tcfg.Target.ListExpectedSignFiles == nil { + t.Fatal("missing function to get list of expected files to sign") + } + + expectedFiles := tcfg.Target.ListExpectedSignFiles(spec, platforms.DefaultSpec()) + slices.Sort(expectedFiles) + + var files []string + assert.NilError(t, json.Unmarshal(mfst, &files)) + slices.Sort(files) + + assert.Assert(t, cmp.DeepEqual(files, expectedFiles)) } } diff --git a/test/windows_test.go b/test/windows_test.go index 1a17a12c..928e0649 100644 --- a/test/windows_test.go +++ b/test/windows_test.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "strings" "testing" "github.com/Azure/dalec" @@ -12,54 +11,62 @@ import ( "github.com/moby/buildkit/client/llb" gwclient "github.com/moby/buildkit/frontend/gateway/client" moby_buildkit_v1_frontend "github.com/moby/buildkit/frontend/gateway/pb" + ocispecs "github.com/opencontainers/image-spec/specs-go/v1" + "golang.org/x/exp/maps" ) func TestWindows(t *testing.T) { t.Parallel() ctx := startTestSpan(baseCtx, t) - testWindows(ctx, t, "windowscross/container") - t.Run("custom worker", func(t *testing.T) { - t.Parallel() - ctx := startTestSpan(baseCtx, t) - testCustomWindowscrossWorker(ctx, t, targetConfig{ - Container: "windowscross/container", - // The way the test uses the package target is to generate a package which - // it then feeds back into a custom repo and adds that package as a build dep - // to another package. - // We don't build system packages for the windowscross base image. - // There's also no .deb support (currently) - // So... use a mariner2 rpm and then in CreateRepo, convert the rpm to a deb package - // which we'll use to create the repo... - // We can switch to this jammy/deb when that is available. - Package: "mariner2/rpm", - Worker: "windowscross/worker", - }, workerConfig{ - ContextName: windows.WindowscrossWorkerContextName, - CreateRepo: func(pkg llb.State) llb.StateOption { - return func(in llb.State) llb.State { - dt := []byte(` + tcfg := targetConfig{ + Container: "windowscross/container", + // The way the test uses the package target is to generate a package which + // it then feeds back into a custom repo and adds that package as a build dep + // to another package. + // We don't build system packages for the windowscross base image. + // There's also no .deb support (currently) + // So... use a mariner2 rpm and then in CreateRepo, convert the rpm to a deb package + // which we'll use to create the repo... + // We can switch to this jammy/deb when that is available. + Package: "mariner2/rpm", + Worker: "windowscross/worker", + ListExpectedSignFiles: func(spec *dalec.Spec, platform ocispecs.Platform) []string { + return maps.Keys(spec.Artifacts.Binaries) + }, + } + wcfg := workerConfig{ + ContextName: windows.WindowscrossWorkerContextName, + CreateRepo: func(pkg llb.State) llb.StateOption { + return func(in llb.State) llb.State { + dt := []byte(` deb [trusted=yes] copy:/tmp/repo / `) - repo := in. - Run( - dalec.ShArgs("apt-get update && apt-get install -y apt-utils alien"), - dalec.WithMountedAptCache("test-windowscross"), - ). - Run( - llb.Dir("/tmp/repo"), - dalec.ShArgs("set -e; for i in ./RPMS/*/*.rpm; do alien --to-deb \"$i\"; done; rm -rf ./RPMS; rm -rf ./SRPMS; apt-ftparchive packages . | gzip -1 > Packages.gz"), - ). - AddMount("/tmp/repo", pkg) - - return in. - File(llb.Mkfile("/etc/apt/sources.list.d/windowscross.list", 0o644, dt)). - File(llb.Copy(repo, "/", "/tmp/repo")) - } - }, - }) + repo := in. + Run( + dalec.ShArgs("apt-get update && apt-get install -y apt-utils alien"), + dalec.WithMountedAptCache("test-windowscross"), + ). + Run( + llb.Dir("/tmp/repo"), + dalec.ShArgs("set -e; for i in ./RPMS/*/*.rpm; do alien --to-deb \"$i\"; done; rm -rf ./RPMS; rm -rf ./SRPMS; apt-ftparchive packages . | gzip -1 > Packages.gz"), + ). + AddMount("/tmp/repo", pkg) + + return in. + File(llb.Mkfile("/etc/apt/sources.list.d/windowscross.list", 0o644, dt)). + File(llb.Copy(repo, "/", "/tmp/repo")) + } + }, + } + + testWindows(ctx, t, tcfg) + t.Run("custom worker", func(t *testing.T) { + t.Parallel() + ctx := startTestSpan(baseCtx, t) + testCustomWindowscrossWorker(ctx, t, tcfg, wcfg) }) } @@ -75,7 +82,7 @@ func withWindowsAmd64(cfg *newSolveRequestConfig) { cfg.req.FrontendOpt["platform"] = "windows/amd64" } -func testWindows(ctx context.Context, t *testing.T, buildTarget string) { +func testWindows(ctx context.Context, t *testing.T, tcfg targetConfig) { t.Run("Fail when non-zero exit code during build", func(t *testing.T) { t.Parallel() spec := dalec.Spec{ @@ -97,7 +104,7 @@ func testWindows(ctx context.Context, t *testing.T, buildTarget string) { } testEnv.RunTest(ctx, t, func(ctx context.Context, gwc gwclient.Client) { - sr := newSolveRequest(withSpec(ctx, t, &spec), withBuildTarget(buildTarget), withWindowsAmd64) + sr := newSolveRequest(withSpec(ctx, t, &spec), withBuildTarget(tcfg.Container), withWindowsAmd64) sr.Evaluate = true _, err := gwc.Solve(ctx, sr) var xErr *moby_buildkit_v1_frontend.ExitError @@ -128,7 +135,7 @@ func testWindows(ctx context.Context, t *testing.T, buildTarget string) { } testEnv.RunTest(ctx, t, func(ctx context.Context, gwc gwclient.Client) { - sr := newSolveRequest(withSpec(ctx, t, &spec), withBuildTarget(buildTarget), withWindowsAmd64) + sr := newSolveRequest(withSpec(ctx, t, &spec), withBuildTarget(tcfg.Container), withWindowsAmd64) sr.Evaluate = true _, err := gwc.Solve(ctx, sr) @@ -262,7 +269,7 @@ echo "$BAR" > bar.txt } testEnv.RunTest(ctx, t, func(ctx context.Context, gwc gwclient.Client) { - sr := newSolveRequest(withSpec(ctx, t, &spec), withBuildTarget(buildTarget), withWindowsAmd64) + sr := newSolveRequest(withSpec(ctx, t, &spec), withBuildTarget(tcfg.Container), withWindowsAmd64) sr.Evaluate = true res := solveT(ctx, t, gwc, sr) @@ -300,7 +307,9 @@ echo "$BAR" > bar.txt }) }) - t.Run("test signing", windowsSigningTests) + t.Run("signing", func(t *testing.T) { + windowsSigningTests(t, tcfg) + }) t.Run("go module", func(t *testing.T) { t.Parallel() @@ -353,41 +362,13 @@ echo "$BAR" > bar.txt } testEnv.RunTest(ctx, t, func(ctx context.Context, client gwclient.Client) { - req := newSolveRequest(withBuildTarget(buildTarget), withSpec(ctx, t, spec), withWindowsAmd64) + req := newSolveRequest(withBuildTarget(tcfg.Container), withSpec(ctx, t, spec), withWindowsAmd64) solveT(ctx, t, client, req) }) }) } -func runBuild(ctx context.Context, t *testing.T, gwc gwclient.Client, spec *dalec.Spec, srOpts ...srOpt) { - st := prepareSigningState(ctx, t, gwc, spec, srOpts...) - - def, err := st.Marshal(ctx) - if err != nil { - t.Fatal(err) - } - - res := solveT(ctx, t, gwc, gwclient.SolveRequest{ - Definition: def.ToPB(), - }) - - verifySigning(ctx, t, res) -} - -func verifySigning(ctx context.Context, t *testing.T, res *gwclient.Result) { - tgt := readFile(ctx, t, "/target", res) - cfg := readFile(ctx, t, "/config.json", res) - - if string(tgt) != "windowscross" { - t.Fatal(fmt.Errorf("target incorrect; either not sent to signer or not received back from signer")) - } - - if !strings.Contains(string(cfg), "windows") { - t.Fatal(fmt.Errorf("configuration incorrect")) - } -} - -func prepareSigningState(ctx context.Context, t *testing.T, gwc gwclient.Client, spec *dalec.Spec, extraSrOpts ...srOpt) llb.State { +func prepareWindowsSigningState(ctx context.Context, t *testing.T, gwc gwclient.Client, spec *dalec.Spec, extraSrOpts ...srOpt) llb.State { zipper := getZipperState(ctx, t, gwc) srOpts := []srOpt{withSpec(ctx, t, spec), withBuildTarget("windowscross/zip"), withWindowsAmd64}