Skip to content

Commit 0d0882b

Browse files
committed
feat: complete docker sdk docker build
Signed-off-by: Vasek - Tom C <tom@quartz.technology>
1 parent 3ff030b commit 0d0882b

File tree

6 files changed

+149
-28
lines changed

6 files changed

+149
-28
lines changed

docker_sdk/main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,8 @@ func (m *Dockersdk) ModuleRuntime(ctx context.Context, modSource *dagger.ModuleS
6161
// The Docker SDK does not generate any code.
6262
func (m *Dockersdk) Codegen(ctx context.Context, modSource *dagger.ModuleSource, introspectionJSON *dagger.File) (*dagger.GeneratedCode, error) {
6363
return dag.GeneratedCode(dag.Directory()), nil
64+
}
65+
66+
func (m *Dockersdk) Test(ctx context.Context, test string) (string, error) {
67+
return test, nil
6468
}

docker_sdk/src/dockerfile/dockerfile.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dockerfile
33
import (
44
"fmt"
55
"os"
6+
"strings"
67

78
"github.com/moby/buildkit/frontend/dockerfile/parser"
89
)
@@ -52,9 +53,61 @@ func (d *Dockerfile) Stages() []string {
5253
return stages
5354
}
5455

56+
// Args does not handle self interpolation for simplicity.
57+
// TODO: handle self interpolation (ARG XXX="XX-${XXXX}")
58+
func (d *Dockerfile) Args() map[string]string {
59+
args := map[string]string{}
60+
61+
for _, child := range d.content.AST.Children {
62+
if child.Value != "ARG" {
63+
continue
64+
}
65+
66+
entry := strings.SplitN(child.Next.Value, "=", 2)
67+
switch len(entry) {
68+
case 1:
69+
args[entry[0]] = ""
70+
case 2:
71+
args[entry[0]] = entry[1]
72+
default:
73+
panic(fmt.Errorf("invalid ARG: %s", child.Next.Value))
74+
}
75+
}
76+
77+
return args
78+
}
79+
80+
func (d *Dockerfile) Secrets() []string {
81+
secrets := []string{}
82+
83+
for _, child := range d.content.AST.Children {
84+
if child.Value != "RUN" {
85+
continue
86+
}
87+
88+
for _, flag := range child.Flags {
89+
if !strings.Contains(flag, "--mount=type=secret,id=") {
90+
continue
91+
}
92+
93+
secrets = append(secrets, strings.TrimPrefix(flag, "--mount=type=secret,id="))
94+
}
95+
}
96+
97+
return secrets
98+
}
99+
55100
func (d *Dockerfile) String() string {
56101
var result string
57102

103+
result += fmt.Sprintf("Filename: %s\n", d.filename)
104+
result += fmt.Sprintf("Stages: %s\n", strings.Join(d.Stages(), ", "))
105+
result += fmt.Sprintf("Secrets: %s\n", strings.Join(d.Secrets(), ", "))
106+
107+
for key, value := range d.Args() {
108+
result += fmt.Sprintf("ARG %s=%s\n", key, value)
109+
}
110+
58111
for _, child := range d.content.AST.Children {
59112
result += fmt.Sprintf("Command: %s\n", child.Value)
60113
for _, flag := range child.Flags {

docker_sdk/src/integrations/docker/build.go

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ import (
99
"dagger.io/dockersdk/utils"
1010
)
1111

12-
func (d *Docker) build(platform *dagger.Platform, target *string, dockerfile *string) *dagger.Container {
13-
opts := dagger.DirectoryDockerBuildOpts{}
12+
func (d *Docker) build(platform *dagger.Platform, target *string, dockerfile *string, buildArgs []dagger.BuildArg, secrets []*dagger.Secret) *dagger.Container {
13+
opts := dagger.DirectoryDockerBuildOpts{
14+
BuildArgs: buildArgs,
15+
Secrets: secrets,
16+
}
1417

1518
if platform != nil {
1619
opts.Platform = *platform
@@ -30,11 +33,38 @@ func (d *Docker) build(platform *dagger.Platform, target *string, dockerfile *st
3033
func (d *Docker) buildFctTypeDef(ctx context.Context) (*dagger.Function, *dagger.TypeDef) {
3134
typedef := dag.Function("Build", dag.TypeDef().WithObject("Container")).
3235
WithDescription("Build a container from the Dockerfile in the current directory").
33-
WithArg("dockerfile", dag.TypeDef().WithKind(dagger.TypeDefKindStringKind).WithOptional(true), dagger.FunctionWithArgOpts{
34-
DefaultValue: utils.LoadDefaultValue(d.dockerfile.Filename()),
35-
Description: "Path to the Dockerfile to use.",
36-
})
36+
WithArg("dockerfile",
37+
dag.TypeDef().WithKind(dagger.TypeDefKindStringKind).WithOptional(true),
38+
dagger.FunctionWithArgOpts{
39+
DefaultValue: utils.LoadDefaultValue(d.dockerfile.Filename()),
40+
Description: "Path to the Dockerfile to use.",
41+
})
42+
43+
/////
44+
// Add the build arguments
45+
for key, value := range d.dockerfile.Args() {
46+
buildArgOpts := dagger.FunctionWithArgOpts{
47+
Description: fmt.Sprintf("Set %s build argument", key),
48+
}
49+
50+
if value != "" {
51+
buildArgOpts.DefaultValue = utils.LoadDefaultValue(value)
52+
}
53+
54+
typedef = typedef.WithArg(key, dag.TypeDef().WithKind(dagger.TypeDefKindStringKind), buildArgOpts)
55+
}
56+
57+
//////
58+
// Add the secrets arguments
59+
for _, secret := range d.dockerfile.Secrets() {
60+
typedef = typedef.WithArg(secret,
61+
dag.TypeDef().WithObject("Secret"),
62+
dagger.FunctionWithArgOpts{
63+
Description: fmt.Sprintf("Set %s secret", secret),
64+
})
65+
}
3766

67+
/////
3868
// Add the platform argument
3969
defaultPlatformArgOpts := dagger.FunctionWithArgOpts{
4070
Description: "Platform to build.",
@@ -55,6 +85,7 @@ func (d *Docker) buildFctTypeDef(ctx context.Context) (*dagger.Function, *dagger
5585
defaultPlatformArgOpts,
5686
)
5787

88+
/////
5889
// If stages are declared in the Dockerfile, we add an enum and the stage option to the function.
5990
if len(d.dockerfile.Stages()) != 0 {
6091
stageTypeDef := dag.TypeDef().WithEnum(fmt.Sprintf("%sStage", d.name))

docker_sdk/src/integrations/docker/object.go

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type Docker struct {
2424

2525
func New(name string, code *codebase.Codebase) object.Object {
2626
return &Docker{
27-
name: name,
27+
name: name,
2828
dockerfile: code.Dockerfile(),
2929
}
3030
}
@@ -84,31 +84,38 @@ func (d *Docker) New(input object.InputArgs) object.Object {
8484
func (d *Docker) Invoke(ctx context.Context, fnName string, input object.InputArgs) (object.Result, error) {
8585
switch fnName {
8686
case "Build":
87-
var platform dagger.Platform
88-
if input["platform"] != nil {
89-
err := json.Unmarshal([]byte(input["platform"]), &platform)
90-
if err != nil {
91-
return nil, fmt.Errorf("%s: %w", "failed to unmarshal input arg platform", err)
87+
platform := utils.LoadArgument[dagger.Platform]("platform", input)
88+
target := utils.LoadArgument[string]("target", input)
89+
dockerfile := utils.LoadArgument[string]("dockerfile", input)
90+
91+
buildArgs := []dagger.BuildArg{}
92+
for key := range d.dockerfile.Args() {
93+
if input[key] != nil {
94+
buildArgs = append(buildArgs, dagger.BuildArg{
95+
Name: key,
96+
Value: utils.LoadArgument[string](key, input),
97+
})
9298
}
9399
}
94100

95-
var target string
96-
if input["target"] != nil {
97-
err := json.Unmarshal([]byte(input["target"]), &target)
98-
if err != nil {
99-
return nil, fmt.Errorf("%s: %w", "failed to unmarshal input arg target", err)
100-
}
101-
}
101+
// To load secret we need to load the value first and then reassign a secret
102+
// with the right value since it's obfuscated by the CLI.
103+
// TODO: find a better way to do this.
104+
secrets := []*dagger.Secret{}
105+
for _, secretKey := range d.dockerfile.Secrets() {
106+
if input[secretKey] != nil {
107+
cliSecret := utils.LoadSecretFromID([]byte(input[secretKey]))
108+
109+
secretValue, err := cliSecret.Plaintext(ctx)
110+
if err != nil {
111+
return nil, fmt.Errorf("failed to add secret value: %w", err)
112+
}
102113

103-
var dockerfile string
104-
if input["dockerfile"] != nil {
105-
err := json.Unmarshal([]byte(input["dockerfile"]), &dockerfile)
106-
if err != nil {
107-
return nil, fmt.Errorf("%s: %w", "failed to unmarshal input arg dockerfile", err)
114+
secrets = append(secrets, dag.SetSecret(secretKey, secretValue))
108115
}
109116
}
110117

111-
return d.build(&platform, &target, &dockerfile), nil
118+
return d.build(&platform, &target, &dockerfile, buildArgs, secrets), nil
112119
default:
113120
return nil, fmt.Errorf("unknown function %s", fnName)
114121
}

docker_sdk/src/utils/loader.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,30 @@ package utils
33
import (
44
"dagger.io/dagger"
55
"dagger.io/dagger/dag"
6+
"dagger.io/dockersdk/integrations/object"
67

78
"encoding/json"
89
"fmt"
910
)
1011

12+
// LoadArgument is a generic function to load an argument from
13+
// the input map.
14+
// If the argument is not set, it returns the zero value of the type.
15+
func LoadArgument[K any](name string, args object.InputArgs) K {
16+
var res K
17+
18+
if args[name] == nil {
19+
return res
20+
}
21+
22+
err := json.Unmarshal(args[name], &res)
23+
if err != nil {
24+
panic(fmt.Errorf("failed to unmarshal input arg %s: %w", name, err))
25+
}
26+
27+
return res
28+
}
29+
1130
func LoadDefaultValue(value interface{}) dagger.JSON {
1231
jsonValue, err := json.Marshal(value)
1332
if err != nil {

docker_sdk_test/Dockerfile

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
FROM golang:1.23.2-alpine AS app
1+
ARG BASE_IMAGE=golang:1.23.2-alpine
2+
3+
FROM ${BASE_IMAGE} AS app
24

35
WORKDIR /app
46

@@ -10,8 +12,13 @@ RUN go build -o /app/main .
1012

1113
FROM golang:1.23.2-alpine AS runtime
1214

15+
ARG BIN_NAME
16+
1317
WORKDIR /runtime
1418

15-
COPY --from=app /app/main /runtime/main
19+
COPY --from=app /app/main /runtime/${BIN_NAME}
20+
21+
RUN --mount=type=secret,id=my-super-secret \
22+
cat /run/secrets/my-super-secret > /runtime/secret.txt
1623

17-
ENTRYPOINT ["/runtime/main"]
24+
ENTRYPOINT ["/runtime/${BIN_NAME}"]

0 commit comments

Comments
 (0)