Skip to content

Commit fe8ffa5

Browse files
committed
feat: add support for DockerfileInline
As nerdctl currently uses "nerdctl build" to build the service images, write the inline file to a temporary file and use "-f" to specify the temporary dockerfile. Signed-off-by: Tushar Gupta <[email protected]>
1 parent d775a8c commit fe8ffa5

File tree

5 files changed

+77
-8
lines changed

5 files changed

+77
-8
lines changed

cmd/nerdctl/compose/compose_build_linux_test.go

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ func TestComposeBuild(t *testing.T) {
3939
// Make sure we shard the image name to something unique to the test to avoid conflicts with other tests
4040
imageSvc0 := data.Identifier("svc0")
4141
imageSvc1 := data.Identifier("svc1")
42+
imageSvc2 := data.Identifier("svc2")
4243

4344
// We are not going to run them, so, ports conflicts should not matter here
4445
dockerComposeYAML := fmt.Sprintf(`
@@ -51,14 +52,21 @@ services:
5152
svc1:
5253
build: .
5354
image: %s
54-
`, imageSvc0, imageSvc1)
55+
svc2:
56+
image: %s
57+
build:
58+
context: .
59+
dockerfile_inline: |
60+
FROM %s
61+
`, imageSvc0, imageSvc1, imageSvc2, testutil.CommonImage)
5562

5663
data.Temp().Save(dockerComposeYAML, "compose.yaml")
5764
data.Temp().Save(dockerfile, "Dockerfile")
5865

5966
data.Labels().Set("composeYaml", data.Temp().Path("compose.yaml"))
6067
data.Labels().Set("imageSvc0", imageSvc0)
6168
data.Labels().Set("imageSvc1", imageSvc1)
69+
data.Labels().Set("imageSvc2", imageSvc2)
6270
}
6371

6472
testCase.SubTests = []*test.Case{
@@ -76,22 +84,41 @@ services:
7684
Output: expect.All(
7785
expect.Contains(data.Labels().Get("imageSvc0")),
7886
expect.DoesNotContain(data.Labels().Get("imageSvc1")),
87+
expect.DoesNotContain(data.Labels().Get("imageSvc2")),
88+
),
89+
}
90+
},
91+
},
92+
{
93+
Description: "build svc2",
94+
NoParallel: true,
95+
Setup: func(data test.Data, helpers test.Helpers) {
96+
helpers.Ensure("compose", "-f", data.Labels().Get("composeYaml"), "build", "svc2")
97+
},
98+
99+
Command: test.Command("images"),
100+
101+
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
102+
return &test.Expected{
103+
Output: expect.All(
104+
expect.Contains(data.Labels().Get("imageSvc2")),
105+
expect.DoesNotContain(data.Labels().Get("imageSvc1")),
79106
),
80107
}
81108
},
82109
},
83110
{
84-
Description: "build svc0 and svc1",
111+
Description: "build svc0, svc1, svc2",
85112
NoParallel: true,
86113
Setup: func(data test.Data, helpers test.Helpers) {
87-
helpers.Ensure("compose", "-f", data.Labels().Get("composeYaml"), "build", "svc0", "svc1")
114+
helpers.Ensure("compose", "-f", data.Labels().Get("composeYaml"), "build", "svc0", "svc1", "svc2")
88115
},
89116

90117
Command: test.Command("images"),
91118

92119
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
93120
return &test.Expected{
94-
Output: expect.Contains(data.Labels().Get("imageSvc0"), data.Labels().Get("imageSvc1")),
121+
Output: expect.Contains(data.Labels().Get("imageSvc0"), data.Labels().Get("imageSvc1"), data.Labels().Get("imageSvc2")),
95122
}
96123
},
97124
},
@@ -122,7 +149,7 @@ services:
122149

123150
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
124151
if data.Labels().Get("imageSvc0") != "" {
125-
helpers.Anyhow("rmi", data.Labels().Get("imageSvc0"), data.Labels().Get("imageSvc1"))
152+
helpers.Anyhow("rmi", data.Labels().Get("imageSvc0"), data.Labels().Get("imageSvc1"), data.Labels().Get("imageSvc2"))
126153
}
127154
}
128155

pkg/composer/build.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,23 @@ func (c *Composer) buildServiceImage(ctx context.Context, image string, b *servi
6363
if bo.Progress != "" {
6464
args = append(args, "--progress="+bo.Progress)
6565
}
66+
67+
if b.DockerfileInline != "" {
68+
// if DockerfileInline is specified, write it to a temporary file
69+
// and use -f flag to use that docker file with project's ctxdir
70+
tmpFile, err := os.CreateTemp("", "inline-dockerfile-*.Dockerfile")
71+
if err != nil {
72+
return fmt.Errorf("failed to create temp file for DockerfileInline: %w", err)
73+
}
74+
defer os.Remove(tmpFile.Name())
75+
defer tmpFile.Close()
76+
77+
if _, err := tmpFile.Write([]byte(b.DockerfileInline)); err != nil {
78+
return fmt.Errorf("failed to write DockerfileInline: %w", err)
79+
}
80+
b.BuildArgs = append(b.BuildArgs, "-f="+tmpFile.Name())
81+
}
82+
6683
args = append(args, b.BuildArgs...)
6784

6885
cmd := c.createNerdctlCmd(ctx, append([]string{"build"}, args...)...)

pkg/composer/serviceparser/build.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import (
3434

3535
func parseBuildConfig(c *types.BuildConfig, project *types.Project, imageName string) (*Build, error) {
3636
if unknown := reflectutil.UnknownNonEmptyFields(c,
37-
"Context", "Dockerfile", "Args", "CacheFrom", "Target", "Labels", "Secrets", "AdditionalContexts",
37+
"Context", "Dockerfile", "Args", "CacheFrom", "Target", "Labels", "Secrets", "DockerfileInline", "AdditionalContexts",
3838
); len(unknown) > 0 {
3939
log.L.Warnf("Ignoring: build: %+v", unknown)
4040
}
@@ -60,6 +60,10 @@ func parseBuildConfig(c *types.BuildConfig, project *types.Project, imageName st
6060
}
6161
}
6262

63+
if c.DockerfileInline != "" {
64+
b.DockerfileInline = c.DockerfileInline
65+
}
66+
6367
for k, v := range c.Args {
6468
if v == nil {
6569
b.BuildArgs = append(b.BuildArgs, "--build-arg="+k)

pkg/composer/serviceparser/build_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package serviceparser
1818

1919
import (
2020
"runtime"
21+
"strings"
2122
"testing"
2223

2324
"gotest.tools/v3/assert"
@@ -54,6 +55,12 @@ services:
5455
target: tgt_secret
5556
- simple_secret
5657
- absolute_secret
58+
baz:
59+
image: bazimg
60+
build:
61+
context: ./bazctx
62+
dockerfile_inline: |
63+
FROM random
5764
secrets:
5865
src_secret:
5966
file: test_secret1
@@ -95,4 +102,17 @@ secrets:
95102
assert.Assert(t, in(bar.Build.BuildArgs, "--secret=id=tgt_secret,src="+secretPath+"/test_secret1"))
96103
assert.Assert(t, in(bar.Build.BuildArgs, "--secret=id=simple_secret,src="+secretPath+"/test_secret2"))
97104
assert.Assert(t, in(bar.Build.BuildArgs, "--secret=id=absolute_secret,src=/tmp/absolute_secret"))
105+
106+
bazSvc, err := project.GetService("baz")
107+
assert.NilError(t, err)
108+
109+
baz, err := Parse(project, bazSvc)
110+
assert.NilError(t, err)
111+
112+
t.Logf("baz: %+v", baz)
113+
t.Logf("baz.Build.BuildArgs: %+v", baz.Build.BuildArgs)
114+
t.Logf("baz.Build.DockerfileInline: %q", baz.Build.DockerfileInline)
115+
assert.Assert(t, func() bool {
116+
return strings.TrimSpace(baz.Build.DockerfileInline) == "FROM random"
117+
}())
98118
}

pkg/composer/serviceparser/serviceparser.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,9 @@ type Container struct {
195195
}
196196

197197
type Build struct {
198-
Force bool // force build even if already present
199-
BuildArgs []string // {"-t", "example.com/foo", "--target", "foo", "/path/to/ctx"}
198+
Force bool // force build even if already present
199+
BuildArgs []string // {"-t", "example.com/foo", "--target", "foo", "/path/to/ctx"}
200+
DockerfileInline string // store contents of dockerfile_inline field is specified
200201
// TODO: call BuildKit API directly without executing `nerdctl build`
201202
}
202203

0 commit comments

Comments
 (0)