Skip to content

Commit 3c04826

Browse files
author
Kamal Nasser
authored
appdev: correct source_dir and dockerfile_path interaction (#1257)
* add dockerfile/buildpacks indicator to component list * dockerfile builds: correct dockerfile_path and source_dir interaction * support dockerfile_path outside of source_dir * update tests * fix windows path handling
1 parent b1ec7cf commit 3c04826

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+2749
-742
lines changed

commands/apps_charm.go

+7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/digitalocean/doctl/commands/charm"
77
"github.com/digitalocean/doctl/commands/charm/template"
8+
"github.com/digitalocean/doctl/internal/apps/builder"
89
"github.com/digitalocean/godo"
910
)
1011

@@ -22,6 +23,12 @@ func (i componentListItem) Description() string {
2223
}
2324

2425
if buildable, ok := i.spec.(godo.AppBuildableComponentSpec); ok {
26+
if builder.IsDockerBuild(buildable) {
27+
desc[0] += " [dockerfile]"
28+
} else if builder.IsCNBBuild(buildable) {
29+
desc[0] += " [buildpacks]"
30+
}
31+
2532
if sourceDir := buildable.GetSourceDir(); sourceDir != "" {
2633
desc = append(desc, template.String(`located in ./{{highlight .}}`, sourceDir))
2734
}

go.mod

+7-5
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ require (
2323
github.com/mattn/go-isatty v0.0.14
2424
github.com/mitchellh/copystructure v1.0.0
2525
github.com/natefinch/pie v0.0.0-20170715172608-9a0d72014007
26-
github.com/opencontainers/image-spec v1.0.2
26+
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799
2727
github.com/opencontainers/runc v1.1.3 // indirect
2828
github.com/pkg/errors v0.9.1
2929
github.com/sclevine/spec v1.3.0
@@ -47,7 +47,6 @@ require (
4747
require (
4848
github.com/MakeNowJust/heredoc v1.0.0
4949
github.com/apache/openwhisk-client-go v0.0.0-20211007130743-38709899040b
50-
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
5150
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2
5251
github.com/charmbracelet/bubbles v0.13.1-0.20220731172002-8f6516082803
5352
github.com/charmbracelet/bubbletea v0.22.0
@@ -57,17 +56,18 @@ require (
5756
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
5857
github.com/muesli/reflow v0.3.0
5958
github.com/muesli/termenv v0.12.0
59+
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
6060
gopkg.in/yaml.v3 v3.0.1
6161
)
6262

6363
require (
64-
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
64+
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
6565
github.com/Microsoft/go-winio v0.5.2 // indirect
6666
github.com/atotto/clipboard v0.1.4 // indirect
6767
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 // indirect
6868
github.com/containerd/cgroups v1.0.3 // indirect
6969
github.com/containerd/console v1.0.3 // indirect
70-
github.com/containerd/containerd v1.5.13 // indirect
70+
github.com/containerd/containerd v1.6.3-0.20220401172941-5ff8fce1fcc6 // indirect
7171
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
7272
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
7373
github.com/docker/distribution v2.8.1+incompatible // indirect
@@ -91,9 +91,11 @@ require (
9191
github.com/mattn/go-runewidth v0.0.14 // indirect
9292
github.com/mitchellh/mapstructure v1.4.3 // indirect
9393
github.com/mitchellh/reflectwalk v1.0.0 // indirect
94+
github.com/moby/buildkit v0.10.4 // indirect
9495
github.com/moby/sys/mount v0.3.3 // indirect
9596
github.com/moby/sys/mountinfo v0.6.2 // indirect
96-
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd // indirect
97+
github.com/moby/sys/symlink v0.2.0 // indirect
98+
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
9799
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
98100
github.com/modern-go/reflect2 v1.0.2 // indirect
99101
github.com/morikuni/aec v1.0.0 // indirect

go.sum

+12-6
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
3838
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
3939
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
4040
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
41-
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
4241
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
42+
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
43+
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
4344
github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
4445
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
4546
github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
@@ -186,8 +187,8 @@ github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09Zvgq
186187
github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s=
187188
github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=
188189
github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c=
189-
github.com/containerd/containerd v1.5.13 h1:XqvKw9i4P7/mFrC3TSM7yV5cwFZ9avXe6M3YANKnzEE=
190-
github.com/containerd/containerd v1.5.13/go.mod h1:3AlCrzKROjIuP3JALsY14n8YtntaUDBu7vek+rPN5Vc=
190+
github.com/containerd/containerd v1.6.3-0.20220401172941-5ff8fce1fcc6 h1:nig7zto6cp3Wt1lPMK8EmyP6f/ZNmn/tL6ASQ7stews=
191+
github.com/containerd/containerd v1.6.3-0.20220401172941-5ff8fce1fcc6/go.mod h1:WSt2SnDLAGWlu+Vl+EWay37seZLKqgRt6XLjIMy8SYM=
191192
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
192193
github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
193194
github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
@@ -590,6 +591,8 @@ github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
590591
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
591592
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
592593
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
594+
github.com/moby/buildkit v0.10.4 h1:FvC+buO8isGpUFZ1abdSLdGHZVqg9sqI4BbFL8tlzP4=
595+
github.com/moby/buildkit v0.10.4/go.mod h1:Yajz9vt1Zw5q9Pp4pdb3TCSUXJBIroIQGQ3TTs/sLug=
593596
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
594597
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
595598
github.com/moby/sys/mount v0.3.3 h1:fX1SVkXFJ47XWDoeFW4Sq7PdQJnV2QIDZAqjNqgEjUs=
@@ -600,8 +603,11 @@ github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdx
600603
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
601604
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
602605
github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ=
603-
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd h1:aY7OQNf2XqY/JQ6qREWamhI/81os/agb2BAGpcx5yWI=
606+
github.com/moby/sys/symlink v0.2.0 h1:tk1rOM+Ljp0nFmfOIBtlV3rTDlWOwFRhjEeAhZB0nZc=
607+
github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs=
604608
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
609+
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=
610+
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
605611
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
606612
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
607613
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -671,8 +677,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
671677
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
672678
github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
673679
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
674-
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
675-
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
680+
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec=
681+
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
676682
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
677683
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
678684
github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=

internal/apps/builder/builder.go

+10
Original file line numberDiff line numberDiff line change
@@ -322,3 +322,13 @@ func IsCNBBuild(spec godo.AppBuildableComponentSpec) bool {
322322

323323
return true
324324
}
325+
326+
// IsDockerBuild indicates whether the component will be built using the Docker builder.
327+
func IsDockerBuild(spec godo.AppBuildableComponentSpec) bool {
328+
dockerBuildable, ok := spec.(godo.AppDockerBuildableComponentSpec)
329+
if !ok {
330+
return false
331+
}
332+
333+
return dockerBuildable.GetDockerfilePath() != ""
334+
}

internal/apps/builder/docker.go

+69-11
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ import (
1616

1717
"github.com/digitalocean/doctl/commands/charm/template"
1818
"github.com/digitalocean/godo"
19+
"github.com/docker/cli/cli/command/image/build"
1920
dockertypes "github.com/docker/docker/api/types"
2021
"github.com/docker/docker/pkg/archive"
22+
"github.com/docker/docker/pkg/idtools"
2123
)
2224

2325
// DockerComponentBuilder builds components using a Dockerfile.
@@ -58,23 +60,20 @@ func (b *DockerComponentBuilder) Build(ctx context.Context) (ComponentBuilderRes
5860
return ComponentBuilderResult{}, fmt.Errorf("configuring environment variables: %w", err)
5961
}
6062

61-
buildContext := b.contextDir
62-
if sd := filepath.Clean(b.component.GetSourceDir()); sd != "." && sd != "/" {
63-
buildContext = filepath.Join(buildContext, sd)
64-
}
65-
// TODO Dockerfile must be relative to the source dir.
66-
// Make it relative and if it's outside the source dir add it to the archive.
67-
// ref: https://github.com/docker/cli/blob/9400e3dbe8ebd0bede3ab7023f744a8d7f4397d2/cli/command/image/build.go#L280-L286
68-
template.Render(lw, `{{success checkmark}} building image using dockerfile {{highlight .}}{{nl 2}}`, b.dockerComponent.GetDockerfilePath())
63+
template.Render(lw,
64+
`{{success checkmark}} building image using dockerfile {{highlight .}}{{nl 2}}`,
65+
b.dockerComponent.GetDockerfilePath(),
66+
)
6967
start := time.Now()
70-
tar, err := archive.TarWithOptions(buildContext, &archive.TarOptions{})
68+
69+
imageBuildContext, imageBuildDockerfile, err := b.getImageBuildContext(ctx)
7170
if err != nil {
7271
return ComponentBuilderResult{}, fmt.Errorf("preparing build context: %w", err)
7372
}
7473

7574
res := ComponentBuilderResult{}
76-
dockerRes, err := b.cli.ImageBuild(ctx, tar, dockertypes.ImageBuildOptions{
77-
Dockerfile: b.dockerComponent.GetDockerfilePath(),
75+
dockerRes, err := b.cli.ImageBuild(ctx, imageBuildContext, dockertypes.ImageBuildOptions{
76+
Dockerfile: imageBuildDockerfile,
7877
Tags: []string{
7978
b.AppImageOutputName(),
8079
},
@@ -106,6 +105,65 @@ func (b *DockerComponentBuilder) Build(ctx context.Context) (ComponentBuilderRes
106105
return res, nil
107106
}
108107

108+
func (b *DockerComponentBuilder) getImageBuildContext(ctx context.Context) (io.Reader, string, error) {
109+
// this assembles the build context in a way that fits cli.ImageBuild's expectations around
110+
// dockerfiles and .dockerignore.
111+
// much of this logic is copied from the `docker` cli implementation:
112+
// https://github.com/docker/cli/blob/9400e3dbe8ebd0bede3ab7023f744a8d7f4397d2/cli/command/image/build.go#L180
113+
// specifically the "build context is a local directory" flow.
114+
115+
absSourceDir, err := filepath.Abs(filepath.Join(b.contextDir, b.dockerComponent.GetSourceDir()))
116+
if err != nil {
117+
return nil, "", fmt.Errorf("parsing source_dir: %w", err)
118+
}
119+
absDockerfile, err := filepath.Abs(filepath.Join(b.contextDir, b.dockerComponent.GetDockerfilePath()))
120+
if err != nil {
121+
return nil, "", fmt.Errorf("parsing dockerfile_path: %w", err)
122+
}
123+
relDockerfile, err := filepath.Rel(absSourceDir, absDockerfile)
124+
if err != nil {
125+
return nil, "", err
126+
}
127+
128+
excludes, err := build.ReadDockerignore(absSourceDir)
129+
if err != nil {
130+
return nil, "", fmt.Errorf("reading .dockerignore: %w", err)
131+
}
132+
133+
if err := build.ValidateContextDirectory(absSourceDir, excludes); err != nil {
134+
return nil, "", err
135+
}
136+
137+
// canonicalize dockerfile name to a platform-independent one
138+
relDockerfile = archive.CanonicalTarNameForPath(relDockerfile)
139+
excludes = build.TrimBuildFilesFromExcludes(excludes, relDockerfile, false)
140+
tar, err := archive.TarWithOptions(absSourceDir, &archive.TarOptions{
141+
ExcludePatterns: excludes,
142+
ChownOpts: &idtools.Identity{UID: 0, GID: 0},
143+
})
144+
if err != nil {
145+
return nil, "", fmt.Errorf("preparing build context: %w", err)
146+
}
147+
148+
// NOTE: archive.CanonicalTarNameForPath normalizes path separators so the relative path will use /
149+
// even on windows.
150+
if strings.HasPrefix(relDockerfile, "../") {
151+
dockerfileReader, err := os.Open(absDockerfile)
152+
if err != nil {
153+
return nil, "", fmt.Errorf("opening dockerfile: %w", err)
154+
}
155+
defer dockerfileReader.Close()
156+
// dockerfile_path is outside of source_dir. we need to copy it inside the build context
157+
// so that the docker engine can access it.
158+
tar, relDockerfile, err = build.AddDockerfileToBuildContext(dockerfileReader, tar)
159+
if err != nil {
160+
return nil, "", fmt.Errorf("copying external dockerfile inside build context: %w", err)
161+
}
162+
}
163+
164+
return tar, relDockerfile, nil
165+
}
166+
109167
// buildStaticSiteImage builds a container image that runs a webserver hosting the static site content
110168
func (b *DockerComponentBuilder) buildStaticSiteImage(ctx context.Context) error {
111169
c, ok := b.component.(*godo.AppStaticSiteSpec)

0 commit comments

Comments
 (0)