targets/linux/distro_handler.go (141 lines of code) (raw):

package linux import ( "context" "encoding/json" "fmt" "github.com/Azure/dalec" "github.com/Azure/dalec/frontend" "github.com/containerd/platforms" "github.com/moby/buildkit/client/llb" "github.com/moby/buildkit/client/llb/sourceresolver" "github.com/pkg/errors" gwclient "github.com/moby/buildkit/frontend/gateway/client" ocispecs "github.com/opencontainers/image-spec/specs-go/v1" ) type DistroConfig interface { // Validate does any distro or packaging-specific validation of a Dalec spec. Validate(*dalec.Spec) error // Worker returns the worker image for the particular distro Worker(sOpt dalec.SourceOpts, opts ...llb.ConstraintsOpt) (llb.State, error) // BuildPkg returns an llb.State containing the built package // which the passed in spec describes. This should be composable with // BuildContainer(), which can consume the returned state. BuildPkg(ctx context.Context, client gwclient.Client, worker llb.State, sOpt dalec.SourceOpts, spec *dalec.Spec, targetKey string, opts ...llb.ConstraintsOpt) (llb.State, error) // BuildContainer consumes an llb.State containing the built package from the // given *dalec.Spec, and installs it in a target container. BuildContainer(ctx context.Context, client gwclient.Client, worker llb.State, sOpt dalec.SourceOpts, spec *dalec.Spec, targetKey string, pkgState llb.State, opts ...llb.ConstraintsOpt) (llb.State, error) // RunTests runts the tests specified in a dalec spec against a built container, which may be the target container. // Some distros may need to pass in a separate worker before mounting the target container. RunTests(ctx context.Context, client gwclient.Client, worker llb.State, spec *dalec.Spec, sOpt dalec.SourceOpts, ctr llb.State, targetKey string, opts ...llb.ConstraintsOpt) (gwclient.Reference, error) } func 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 } if err := dalec.BuildImageConfig(spec, targetKey, img); err != nil { return nil, err } return img, nil } 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 := bi.ResolveImageConfig(ctx, sOpt, sourceresolver.Opt{ Platform: platform, }) if err != nil { return nil, errors.Wrap(err, "error resolving base image config") } var img dalec.DockerImageSpec if err := json.Unmarshal(dt, &img); err != nil { return nil, errors.Wrap(err, "error unmarshalling base image config") } return &img, nil } func HandleContainer(c DistroConfig) gwclient.BuildFunc { return func(ctx context.Context, client gwclient.Client) (*gwclient.Result, error) { return frontend.BuildWithPlatform(ctx, client, func(ctx context.Context, client gwclient.Client, platform *ocispecs.Platform, spec *dalec.Spec, targetKey string) (gwclient.Reference, *dalec.DockerImageSpec, error) { sOpt, err := frontend.SourceOptFromClient(ctx, client, platform) if err != nil { return nil, nil, err } pg := dalec.ProgressGroup(spec.Name) pc := dalec.Platform(platform) worker, err := c.Worker(sOpt, pg, pc) if err != nil { return nil, nil, err } deb, err := c.BuildPkg(ctx, client, worker, sOpt, spec, targetKey, pg, pc) if err != nil { return nil, nil, err } img, err := BuildImageConfig(ctx, sOpt, spec, platform, targetKey) if err != nil { return nil, nil, err } ctr, err := c.BuildContainer(ctx, client, worker, sOpt, spec, targetKey, deb, pg, pc) if err != nil { return nil, nil, err } ref, err := c.RunTests(ctx, client, worker, spec, sOpt, ctr, targetKey, pg, pc) return ref, img, err }) } } func HandlePackage(cfg DistroConfig) gwclient.BuildFunc { return func(ctx context.Context, client gwclient.Client) (*gwclient.Result, error) { return frontend.BuildWithPlatform(ctx, client, func(ctx context.Context, client gwclient.Client, platform *ocispecs.Platform, spec *dalec.Spec, targetKey string) (gwclient.Reference, *dalec.DockerImageSpec, error) { if err := cfg.Validate(spec); err != nil { return nil, nil, fmt.Errorf("rpm: invalid spec: %w", err) } pg := dalec.ProgressGroup("Building " + targetKey + " package: " + spec.Name) sOpt, err := frontend.SourceOptFromClient(ctx, client, platform) if err != nil { return nil, nil, err } pc := dalec.Platform(platform) worker, err := cfg.Worker(sOpt, pg, pc) if err != nil { return nil, nil, errors.Wrap(err, "error building worker container") } pkgSt, err := cfg.BuildPkg(ctx, client, worker, sOpt, spec, targetKey, pg, pc) if err != nil { return nil, nil, err } def, err := pkgSt.Marshal(ctx, pc) if err != nil { return nil, nil, fmt.Errorf("error marshalling llb: %w", err) } res, err := client.Solve(ctx, gwclient.SolveRequest{ Definition: def.ToPB(), }) if err != nil { return nil, nil, err } ref, err := res.SingleRef() if err != nil { return nil, nil, err } if err := ref.Evaluate(ctx); err != nil { return ref, nil, err } ctr, err := cfg.BuildContainer(ctx, client, worker, sOpt, spec, targetKey, pkgSt, pg, pc) if err != nil { return ref, nil, err } if ref, err := cfg.RunTests(ctx, client, worker, spec, sOpt, ctr, targetKey, pg, pc); err != nil { cfg, _ := BuildImageConfig(ctx, sOpt, spec, platform, targetKey) return ref, cfg, err } if platform == nil { p := platforms.DefaultSpec() platform = &p } return ref, &dalec.DockerImageSpec{Image: ocispecs.Image{Platform: *platform}}, nil }) } }