This repository has been archived on 2024-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
dagger/plan/task/dockerfile.go
Marcos Nils 27d878456f
Improve tty error logging when buildkit vertex is unknown (#2188)
* Improve tty error logging when buildkit vertex is unknown

Creates a generic "system" group in the tty output which captures
buildkit events that report a non-dagger vertex name. This happens
currently when using core.#Dockerfile actions since Dagger delegates the
LLB generation to buildkit through it's frontend and we don't get
meaningful events that we can correlate from Dagger's side

Signed-off-by: Marcos Lilljedahl <marcosnils@gmail.com>
2022-04-18 21:50:34 -03:00

235 lines
5.2 KiB
Go

package task
import (
"context"
"encoding/json"
"errors"
"fmt"
"strings"
bkplatforms "github.com/containerd/containerd/platforms"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/exporter/containerimage/exptypes"
dockerfilebuilder "github.com/moby/buildkit/frontend/dockerfile/builder"
"github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb"
bkgw "github.com/moby/buildkit/frontend/gateway/client"
bkpb "github.com/moby/buildkit/solver/pb"
"github.com/rs/zerolog/log"
"go.dagger.io/dagger/compiler"
"go.dagger.io/dagger/plancontext"
"go.dagger.io/dagger/solver"
)
func init() {
Register("Dockerfile", func() Task { return &DockerfileTask{} })
}
type DockerfileTask struct{}
func (t *DockerfileTask) Run(ctx context.Context, pctx *plancontext.Context, s *solver.Solver, v *compiler.Value) (*compiler.Value, error) {
lg := log.Ctx(ctx)
auths, err := v.Lookup("auth").Fields()
if err != nil {
return nil, err
}
for _, auth := range auths {
// Read auth info
a, err := decodeAuthValue(pctx, auth.Value)
if err != nil {
return nil, err
}
// Extract registry target from dest
target, err := solver.ParseAuthHost(auth.Label())
if err != nil {
return nil, err
}
s.AddCredentials(target, a.Username, a.Secret.PlainText())
lg.Debug().Str("target", target).Msg("add target credentials")
}
source, err := pctx.FS.FromValue(v.Lookup("source"))
if err != nil {
return nil, err
}
sourceSt, err := source.State()
if err != nil {
return nil, err
}
// docker build context
contextDef, err := s.Marshal(ctx, sourceSt)
if err != nil {
return nil, err
}
// Dockerfile context, default to docker build context
dockerfileDef := contextDef
// Support inlined dockerfile
if dockerfile := v.Lookup("dockerfile.contents"); dockerfile.Exists() {
contents, err := dockerfile.String()
if err != nil {
return nil, err
}
dockerfileDef, err = s.Marshal(ctx,
llb.Scratch().File(
llb.Mkfile("/Dockerfile", 0o644, []byte(contents)),
),
)
if err != nil {
return nil, err
}
}
opts, err := t.dockerBuildOpts(v, pctx)
if err != nil {
return nil, err
}
// Handle --no-cache
if s.NoCache() {
opts["no-cache"] = ""
}
req := bkgw.SolveRequest{
Frontend: "dockerfile.v0",
FrontendOpt: opts,
FrontendInputs: map[string]*bkpb.Definition{
dockerfilebuilder.DefaultLocalNameContext: contextDef,
dockerfilebuilder.DefaultLocalNameDockerfile: dockerfileDef,
},
}
res, err := s.SolveRequest(ctx, req)
if err != nil {
return nil, err
}
ref, err := res.SingleRef()
if err != nil {
return nil, err
}
solvedRef := ref
if ref != nil {
st, err := ref.ToState()
if err != nil {
return nil, err
}
solvedRef, err = s.Solve(ctx, st, pctx.Platform.Get())
if err != nil {
return nil, err
}
}
// Image metadata
meta, ok := res.Metadata[exptypes.ExporterImageConfigKey]
if !ok {
return nil, errors.New("build returned no image config")
}
var image dockerfile2llb.Image
if err := json.Unmarshal(meta, &image); err != nil {
return nil, fmt.Errorf("failed to unmarshal image config: %w", err)
}
return compiler.NewValue().FillFields(map[string]interface{}{
"output": pctx.FS.New(solvedRef).MarshalCUE(),
"config": ConvertImageConfig(image.Config),
})
}
func (t *DockerfileTask) dockerBuildOpts(v *compiler.Value, pctx *plancontext.Context) (map[string]string, error) {
opts := map[string]string{}
if dockerfilePath := v.Lookup("dockerfile.path"); dockerfilePath.Exists() {
filename, err := dockerfilePath.String()
if err != nil {
return nil, err
}
opts["filename"] = filename
}
if target := v.Lookup("target"); target.Exists() {
tgr, err := target.String()
if err != nil {
return nil, err
}
opts["target"] = tgr
}
if hosts := v.Lookup("hosts"); hosts.Exists() {
p := []string{}
fields, err := hosts.Fields()
if err != nil {
return nil, err
}
for _, host := range fields {
s, err := host.Value.String()
if err != nil {
return nil, err
}
p = append(p, host.Label()+"="+s)
}
if len(p) > 0 {
opts["add-hosts"] = strings.Join(p, ",")
}
}
if buildArgs := v.Lookup("buildArg"); buildArgs.Exists() {
fields, err := buildArgs.Fields()
if err != nil {
return nil, err
}
for _, buildArg := range fields {
s, err := buildArg.Value.String()
if err != nil {
return nil, err
}
opts["build-arg:"+buildArg.Label()] = s
}
}
if labels := v.Lookup("label"); labels.Exists() {
fields, err := labels.Fields()
if err != nil {
return nil, err
}
for _, label := range fields {
s, err := label.Value.String()
if err != nil {
return nil, err
}
opts["label:"+label.Label()] = s
}
}
if platforms := v.Lookup("platforms"); platforms.Exists() {
p := []string{}
list, err := platforms.List()
if err != nil {
return nil, err
}
for _, platform := range list {
s, err := platform.String()
if err != nil {
return nil, err
}
p = append(p, s)
}
if len(p) > 0 {
opts["platform"] = strings.Join(p, ",")
}
if len(p) > 1 {
opts["multi-platform"] = "true"
}
}
// Set platform to configured one if no one is defined
if opts["platform"] == "" {
opts["platform"] = bkplatforms.Format(pctx.Platform.Get())
}
return opts, nil
}