From 9d3cd180a2e860098d06053cf5b1993938a78184 Mon Sep 17 00:00:00 2001 From: Sam Alba Date: Mon, 20 Dec 2021 17:16:40 -0800 Subject: [PATCH 1/2] stdlib: implemented engine.#Push Signed-off-by: Sam Alba --- plan/task/push.go | 87 +++++++++++++++++++++++++++ stdlib/europa/dagger/engine/image.cue | 1 - 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 plan/task/push.go diff --git a/plan/task/push.go b/plan/task/push.go new file mode 100644 index 00000000..17d0834d --- /dev/null +++ b/plan/task/push.go @@ -0,0 +1,87 @@ +package task + +import ( + "context" + "fmt" + + "github.com/docker/distribution/reference" + bk "github.com/moby/buildkit/client" + "github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb" + "github.com/rs/zerolog/log" + "go.dagger.io/dagger/compiler" + "go.dagger.io/dagger/plancontext" + "go.dagger.io/dagger/solver" +) + +func init() { + Register("Push", func() Task { return &pushTask{} }) +} + +type pushTask struct { +} + +func (c *pushTask) Run(ctx context.Context, pctx *plancontext.Context, s solver.Solver, v *compiler.Value) (*compiler.Value, error) { + lg := log.Ctx(ctx) + + rawDest, err := v.Lookup("dest").String() + if err != nil { + return nil, err + } + + dest, err := reference.ParseNormalizedNamed(rawDest) + if err != nil { + return nil, fmt.Errorf("failed to parse ref %s: %w", rawDest, err) + } + // Add the default tag "latest" to a reference if it only has a repo name. + dest = reference.TagNameOnly(dest) + + // Read auth info + auth, err := decodeAuthValue(pctx, v.Lookup("auth")) + if err != nil { + return nil, err + } + for _, a := range auth { + s.AddCredentials(a.Target, a.Username, a.Secret.PlainText()) + lg.Debug().Str("target", a.Target).Msg("add target credentials") + } + + // Get input state + input, err := pctx.FS.FromValue(v.Lookup("input")) + if err != nil { + return nil, err + } + st, err := input.Result().ToState() + if err != nil { + return nil, err + } + + // Decode the image config + imageConfig := dockerfile2llb.ImageConfig{} + if err := v.Lookup("config").Decode(&imageConfig); err != nil { + return nil, err + } + + // Export image + lg.Debug().Str("dest", dest.String()).Msg("export image") + resp, err := s.Export(ctx, st, &dockerfile2llb.Image{Config: imageConfig}, bk.ExportEntry{ + Type: bk.ExporterImage, + Attrs: map[string]string{ + "name": dest.String(), + "push": "true", + }, + }, pctx.Platform.Get()) + if err != nil { + return nil, err + } + + digest, hasImageDigest := resp.ExporterResponse["containerimage.digest"] + if !hasImageDigest { + return nil, fmt.Errorf("image push target %q did not return an image digest", dest.String()) + } + imageRef := fmt.Sprintf("%s@%s", resp.ExporterResponse["image.name"], digest) + + // Fill result + return compiler.NewValue().FillFields(map[string]interface{}{ + "result": imageRef, + }) +} diff --git a/stdlib/europa/dagger/engine/image.cue b/stdlib/europa/dagger/engine/image.cue index 554b4cc9..13e0fbd6 100644 --- a/stdlib/europa/dagger/engine/image.cue +++ b/stdlib/europa/dagger/engine/image.cue @@ -2,7 +2,6 @@ package engine // Upload a container image to a remote repository #Push: { - @dagger(notimplemented) $dagger: task: _name: "Push" // Target repository address From 13dafb7341db8acef5eb8c0e9615c4b0c83975af Mon Sep 17 00:00:00 2001 From: Sam Alba Date: Mon, 20 Dec 2021 17:31:04 -0800 Subject: [PATCH 2/2] tests: implemented tasks tests for engine.#Push Signed-off-by: Sam Alba --- tests/tasks.bats | 5 +++ tests/tasks/push/push.cue | 67 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 tests/tasks/push/push.cue diff --git a/tests/tasks.bats b/tests/tasks.bats index 93f74820..79d7cc4a 100644 --- a/tests/tasks.bats +++ b/tests/tasks.bats @@ -14,6 +14,11 @@ setup() { "$DAGGER" --europa up ./pull_auth.cue } +@test "task: #Push" { + cd "$TESTDIR"/tasks/push + "$DAGGER" --europa up ./push.cue +} + @test "task: #ReadFile" { cd "$TESTDIR"/tasks/readfile "$DAGGER" --europa up diff --git a/tests/tasks/push/push.cue b/tests/tasks/push/push.cue new file mode 100644 index 00000000..19eb8bbc --- /dev/null +++ b/tests/tasks/push/push.cue @@ -0,0 +1,67 @@ +package main + +import ( + "strings" + "alpha.dagger.io/europa/dagger/engine" +) + +engine.#Plan & { + inputs: secrets: dockerHubToken: envvar: "DOCKERHUB_TOKEN" + + #auth: [{ + target: "daggerio/ci-test:private-pull" + username: "daggertest" + secret: inputs.secrets.dockerHubToken.contents + }] + + actions: { + randomString: { + baseImage: engine.#Pull & { + source: "alpine:3.15.0@sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3" + } + + image: engine.#Exec & { + input: baseImage.output + args: [ + "sh", "-c", "echo -n $RANDOM > /output.txt", + ] + } + + outputFile: engine.#ReadFile & { + input: image.output + path: "/output.txt" + } + + output: outputFile.contents + } + + // Push image with random content + push: engine.#Push & { + dest: "daggerio/ci-test:\(randomString.output)" + input: randomString.image.output + config: Env: ["FOO=\(randomString.output)"] + auth: #auth + } + + // Pull same image and check the content + pull: engine.#Pull & { + source: "daggerio/ci-test:\(randomString.output)" + auth: #auth + } & { + // check digest + digest: strings.Split(push.result, "@")[1] + // check image config + config: { + Env: ["FOO=\(randomString.output)"] + } + } + + pullOutputFile: engine.#ReadFile & { + input: pull.output + path: "/output.txt" + } + + // Check output file in the pulled image + pullContent: string & pullOutputFile.contents & randomString.contents + } +}