Skip to content

Commit 22353b7

Browse files
Merge pull request #436 from sapcc/concourse-resource-support
2 parents a9d956b + 932a067 commit 22353b7

File tree

6 files changed

+94
-18
lines changed

6 files changed

+94
-18
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,17 @@ The `fromPackage` is a Go module path relative to the directory containing the M
111111
If `installTo` is set for at least one binary, the `install` target is added to the Makefile, and all binaries with `installTo` are installed by it.
112112
In this case, `example` would be installed as `/usr/bin/example` by default, and `test-helper` would not be installed.
113113

114+
As a special case for building [Concourse resource types](https://concourse-ci.org/docs/resource-types/implementing/),
115+
setting `installTo: /opt/resource` will have the following effects:
116+
117+
- At build time, the binary will be written to `build/$NAME` as usual.
118+
This path will also be symlinked to `build/check`, `build/in` and `build/out`,
119+
to allow invoking the binary in one of the three operation modes for Concourse resource types.
120+
- At install time, the binary will be installed to `/opt/resource/$NAME`.
121+
This path will also be symlinked to `/opt/resource/check`, `/opt/resource/in` and `/opt/resource/out`,
122+
to make Docker images that were built with `make install` work as a Concourse resource type.
123+
See [`dockerfile.enabled`](#dockerfile) below for how to build such images.
124+
114125
### `controllerGen`
115126

116127
```yaml

internal/core/config.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
_ "embed"
88
"os"
99
"os/exec"
10+
"path/filepath"
1011
"slices"
1112
"strings"
1213
"time"
@@ -335,6 +336,17 @@ func (c *Configuration) Validate() {
335336
}
336337
}
337338

339+
// because of the special logic for `installTo: /opt/resource`, only one binary is allowed to install there
340+
optResourceBinaryCount := 0
341+
for _, bin := range c.Binaries {
342+
if filepath.Clean(bin.InstallTo) == "/opt/resource" {
343+
optResourceBinaryCount++
344+
}
345+
}
346+
if optResourceBinaryCount > 1 {
347+
logg.Fatal("cannot have more than one entry in 'binaries' with `installTo: /opt/resource`")
348+
}
349+
338350
// Validate GolangciLintConfiguration.
339351
if len(c.GolangciLint.ErrcheckExcludes) > 0 && !c.GolangciLint.CreateConfig {
340352
logg.Fatal("golangciLint.createConfig must be set to 'true' if golangciLint.errcheckExcludes is defined")

internal/dockerfile/Dockerfile.tmpl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ RUN {{ .RunCommands }}
7474

7575
COPY --from=builder /etc/ssl/certs/ /etc/ssl/certs/
7676
COPY --from=builder /etc/ssl/cert.pem /etc/ssl/cert.pem
77-
COPY --from=builder /pkg/ /usr/
77+
{{- range $src := .PathsToCopy | sortedKeys }}
78+
COPY --from=builder {{ $src }}/ {{ index $.PathsToCopy $src }}/
79+
{{- end }}
7880
# make sure all binaries can be executed
7981
{{- if .RunVersionCommands }}
8082
RUN set -x \

internal/dockerfile/docker.go

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package dockerfile
66
import (
77
_ "embed"
88
"fmt"
9+
"path/filepath"
910
"strings"
1011

1112
"github.com/sapcc/go-bits/logg"
@@ -53,16 +54,28 @@ func RenderConfig(cfg core.Configuration, sr golang.ScanResult) {
5354

5455
// these commands will be run after `make install` to see that all installed commands can be executed
5556
// (e.g. that all required shared libraries can be loaded correctly)
56-
var runVersionCommands []string
57+
var (
58+
runVersionCommands []string
59+
pathsToCopy = make(map[string]string)
60+
)
5761
for _, binary := range cfg.Binaries {
58-
if binary.InstallTo != "" {
62+
switch {
63+
case binary.InstallTo == "":
64+
continue
65+
case binary.InstallTo == "bin/":
5966
// The binaries need to be in PATH which is only the case if they are installed to bin/
60-
if binary.InstallTo != "bin/" {
61-
logg.Error("dockerfile: ignoring binary %q with custom install path %q, only 'bin/' is supported at the moment", binary.Name, binary.InstallTo)
62-
continue
63-
}
64-
67+
pathsToCopy["/pkg"] = "/usr"
6568
runVersionCommands = append(runVersionCommands, binary.Name+" --version 2>/dev/null")
69+
case filepath.Clean(binary.InstallTo) == "/opt/resource":
70+
// special case: Concourse resource type binaries are accessed through symlinks in /opt/resource that `make install` generates
71+
pathsToCopy["/opt/resource"] = "/opt/resource"
72+
runVersionCommands = append(runVersionCommands,
73+
"/opt/resource/check --version 2>/dev/null",
74+
"/opt/resource/in --version 2>/dev/null",
75+
"/opt/resource/out --version 2>/dev/null",
76+
)
77+
default:
78+
logg.Error("dockerfile: ignoring binary %q with custom install path %q, only 'bin/' and '/opt/resource' are supported at the moment", binary.Name, binary.InstallTo)
6679
}
6780
}
6881

@@ -90,6 +103,7 @@ func RenderConfig(cfg core.Configuration, sr golang.ScanResult) {
90103
"CheckEnv": cfg.Dockerfile.CheckEnv,
91104
"ExtraTestPackages": extraTestPackages,
92105
"Entrypoint": entrypoint,
106+
"PathsToCopy": pathsToCopy,
93107
"ReuseEnabled": reuseEnabled,
94108
"RunCommands": strings.Join(commands, " \\\n && "),
95109
"RunVersionCommands": strings.Join(runVersionCommands, " \\\n && "),

internal/makefile/makefile.go

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,20 @@ func buildTargets(binaries []core.BinaryConfiguration, sr golang.ScanResult, run
724724

725725
result = append(result, r)
726726
allPrerequisites = append(allPrerequisites, r.target)
727+
728+
// special handling for Concourse resource type binaries
729+
if filepath.Clean(bin.InstallTo) == "/opt/resource" {
730+
for _, alias := range []string{"check", "in", "out"} {
731+
r := rule{
732+
description: fmt.Sprintf("Build %s.", alias),
733+
phony: true,
734+
target: "build/" + alias,
735+
recipe: []string{fmt.Sprintf("ln -sf %s build/%s", bin.Name, alias)},
736+
}
737+
result = append(result, r)
738+
allPrerequisites = append(allPrerequisites, r.target)
739+
}
740+
}
727741
}
728742
result[0].prerequisites = allPrerequisites
729743

@@ -761,16 +775,34 @@ endif
761775
`), cfg.Variable("DESTDIR", ""))
762776

763777
for _, bin := range binaries {
764-
if bin.InstallTo != "" {
765-
r.prerequisites = append(r.prerequisites, "build/"+bin.Name)
766-
// stupid MacOS does not have -D
767-
r.recipe = append(r.recipe, fmt.Sprintf(
768-
`install -d -m 0755 "$(DESTDIR)$(PREFIX)/%s"`, filepath.Clean(bin.InstallTo),
769-
))
770-
r.recipe = append(r.recipe, fmt.Sprintf(
771-
`install -m 0755 build/%s "$(DESTDIR)$(PREFIX)/%s/%s"`,
772-
bin.Name, filepath.Clean(bin.InstallTo), bin.Name,
773-
))
778+
if bin.InstallTo == "" {
779+
continue
780+
}
781+
var installPath string
782+
if strings.HasPrefix(bin.InstallTo, "/") {
783+
installPath = filepath.Clean(bin.InstallTo)
784+
} else {
785+
installPath = filepath.Join("$(PREFIX)", bin.InstallTo)
786+
}
787+
788+
r.prerequisites = append(r.prerequisites, "build/"+bin.Name)
789+
// stupid MacOS does not have -D
790+
r.recipe = append(r.recipe, fmt.Sprintf(
791+
`install -d -m 0755 "$(DESTDIR)%s"`, installPath,
792+
))
793+
r.recipe = append(r.recipe, fmt.Sprintf(
794+
`install -m 0755 build/%s "$(DESTDIR)%s/%s"`,
795+
bin.Name, installPath, bin.Name,
796+
))
797+
798+
// special handling for Concourse resource type binaries
799+
if installPath == "/opt/resource" {
800+
for _, alias := range []string{"check", "in", "out"} {
801+
r.recipe = append(r.recipe, fmt.Sprintf(
802+
`ln -sf %s $(DESTDIR)%s/%s`,
803+
bin.Name, installPath, alias,
804+
))
805+
}
774806
}
775807
}
776808
if len(r.recipe) == 0 {

internal/util/template.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ package util
66
import (
77
"bytes"
88
"fmt"
9+
"maps"
910
"os"
11+
"slices"
1012
"strings"
1113
"text/template"
1214

@@ -23,6 +25,9 @@ func WriteFileFromTemplate(fileName, templateCode string, data map[string]any) e
2325
"containsIgnoreCase": func(s, substr string) bool {
2426
return strings.Contains(strings.ToLower(s), strings.ToLower(substr))
2527
},
28+
"sortedKeys": func(m map[string]string) []string {
29+
return slices.Sorted(maps.Keys(m))
30+
},
2631
"trimSpace": strings.TrimSpace,
2732
}
2833

0 commit comments

Comments
 (0)