Skip to content

Commit fefe715

Browse files
committed
tests: kubernetes worker
Signed-off-by: CrazyMax <[email protected]>
1 parent d852568 commit fefe715

File tree

6 files changed

+244
-2
lines changed

6 files changed

+244
-2
lines changed

.github/workflows/build.yml

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ jobs:
6969
- docker\+containerd # same as docker, but with containerd snapshotter
7070
- docker-container
7171
- remote
72+
- kubernetes
7273
pkg:
7374
- ./tests
7475
include:

Dockerfile

+33-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ ARG DOCKER_VERSION=24.0.6
77
ARG GOTESTSUM_VERSION=v1.9.0
88
ARG REGISTRY_VERSION=2.8.0
99
ARG BUILDKIT_VERSION=v0.11.6
10+
ARG K3S_VERSION=v1.21.2-k3s1
1011

1112
# xx is a helper for cross-compilation
1213
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
@@ -21,7 +22,7 @@ ENV CGO_ENABLED=0
2122
WORKDIR /src
2223

2324
FROM registry:$REGISTRY_VERSION AS registry
24-
25+
FROM rancher/k3s:${K3S_VERSION} AS k3s
2526
FROM moby/buildkit:$BUILDKIT_VERSION AS buildkit
2627

2728
FROM gobase AS docker
@@ -103,12 +104,43 @@ RUN apk add --no-cache \
103104
shadow-uidmap \
104105
xfsprogs \
105106
xz
107+
# k3s deps
108+
RUN apk add --no-cache \
109+
busybox-binsh \
110+
cni-plugins \
111+
cni-plugin-flannel \
112+
conntrack-tools \
113+
coreutils \
114+
dbus \
115+
findutils \
116+
ipset
117+
ENV PATH="/usr/libexec/cni:${PATH}"
106118
COPY --link --from=gotestsum /out/gotestsum /usr/bin/
107119
COPY --link --from=registry /bin/registry /usr/bin/
108120
COPY --link --from=docker /opt/docker/* /usr/bin/
121+
COPY --link --from=k3s /bin/k3s /usr/bin/
122+
COPY --link --from=k3s /bin/kubectl /usr/bin/
109123
COPY --link --from=buildkit /usr/bin/buildkitd /usr/bin/
110124
COPY --link --from=buildkit /usr/bin/buildctl /usr/bin/
111125
COPY --link --from=binaries /buildx /usr/bin/
126+
COPY <<-"EOF" /entrypoint.sh
127+
#!/bin/sh
128+
set -e
129+
# cgroup v2: enable nesting
130+
# https://github.com/moby/moby/blob/v25.0.0/hack/dind#L59-L69
131+
if [ -f /sys/fs/cgroup/cgroup.controllers ]; then
132+
# move the processes from the root group to the /init group,
133+
# otherwise writing subtree_control fails with EBUSY.
134+
# An error during moving non-existent process (i.e., "cat") is ignored.
135+
mkdir -p /sys/fs/cgroup/init
136+
xargs -rn1 < /sys/fs/cgroup/cgroup.procs > /sys/fs/cgroup/init/cgroup.procs || :
137+
# enable controllers
138+
sed -e 's/ / +/g' -e 's/^/+/' < /sys/fs/cgroup/cgroup.controllers > /sys/fs/cgroup/cgroup.subtree_control
139+
fi
140+
exec "$@"
141+
EOF
142+
RUN chmod +x /entrypoint.sh
143+
ENTRYPOINT ["/entrypoint.sh"]
112144

113145
FROM integration-test-base AS integration-test
114146
COPY . .

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.21
55
require (
66
github.com/Masterminds/semver/v3 v3.2.1
77
github.com/aws/aws-sdk-go-v2/config v1.18.16
8+
github.com/cenkalti/backoff/v4 v4.2.1
89
github.com/compose-spec/compose-go v1.20.0
910
github.com/containerd/console v1.0.3
1011
github.com/containerd/containerd v1.7.11
@@ -76,7 +77,6 @@ require (
7677
github.com/aws/aws-sdk-go-v2/service/sts v1.18.6 // indirect
7778
github.com/aws/smithy-go v1.13.5 // indirect
7879
github.com/beorn7/perks v1.0.1 // indirect
79-
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
8080
github.com/cespare/xxhash/v2 v2.2.0 // indirect
8181
github.com/containerd/ttrpc v1.2.2 // indirect
8282
github.com/davecgh/go-spew v1.1.1 // indirect

tests/helpers/k3s.go

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package helpers
2+
3+
import (
4+
"bytes"
5+
"net"
6+
"os"
7+
"os/exec"
8+
"strconv"
9+
"time"
10+
11+
"github.com/cenkalti/backoff/v4"
12+
"github.com/moby/buildkit/util/testutil/integration"
13+
"github.com/pkg/errors"
14+
)
15+
16+
const (
17+
k3sBin = "k3s"
18+
kubeCtlBin = "kubectl"
19+
)
20+
21+
func NewK3sServer(cfg *integration.BackendConfig) (kubeConfig string, cl func() error, err error) {
22+
if _, err := exec.LookPath(k3sBin); err != nil {
23+
return "", nil, errors.Wrapf(err, "failed to lookup %s binary", k3sBin)
24+
}
25+
if _, err := exec.LookPath(kubeCtlBin); err != nil {
26+
return "", nil, errors.Wrapf(err, "failed to lookup %s binary", kubeCtlBin)
27+
}
28+
29+
deferF := &integration.MultiCloser{}
30+
cl = deferF.F()
31+
32+
defer func() {
33+
if err != nil {
34+
deferF.F()()
35+
cl = nil
36+
}
37+
}()
38+
39+
cfgfile, err := os.CreateTemp("", "kubeconfig*.yml")
40+
if err != nil {
41+
return "", nil, err
42+
}
43+
kubeConfig = cfgfile.Name()
44+
deferF.Append(func() error {
45+
return os.Remove(cfgfile.Name())
46+
})
47+
48+
k3sDataDir, err := os.MkdirTemp("", "kubedata")
49+
if err != nil {
50+
return "", nil, err
51+
}
52+
deferF.Append(func() error {
53+
return os.RemoveAll(k3sDataDir)
54+
})
55+
56+
l, err := net.Listen("tcp", "localhost:0")
57+
if err != nil {
58+
return "", nil, err
59+
}
60+
_ = l.Close()
61+
62+
lport := strconv.Itoa(l.Addr().(*net.TCPAddr).Port)
63+
nodeName := "integrationk3s"
64+
65+
stop, err := integration.StartCmd(exec.Command(k3sBin, "server",
66+
"--bind-address", "127.0.0.1",
67+
"--https-listen-port", lport,
68+
"--data-dir", k3sDataDir, // write to /tmp for overlayfs support
69+
"--write-kubeconfig", cfgfile.Name(),
70+
"--write-kubeconfig-mode", "666",
71+
"--node-name", nodeName,
72+
), cfg.Logs)
73+
if err != nil {
74+
return "", nil, err
75+
}
76+
77+
if err = waitk3s(cfg, kubeConfig, nodeName); err != nil {
78+
stop()
79+
return "", nil, errors.Wrapf(err, "k3s did not start up: %s", integration.FormatLogs(cfg.Logs))
80+
}
81+
82+
deferF.Append(stop)
83+
return
84+
}
85+
86+
func waitk3s(cfg *integration.BackendConfig, kubeConfig string, nodeName string) error {
87+
logbuf := new(bytes.Buffer)
88+
defer func() {
89+
if logbuf.Len() > 0 {
90+
cfg.Logs["waitk3s: "] = logbuf
91+
}
92+
}()
93+
94+
boff := backoff.NewExponentialBackOff()
95+
boff.InitialInterval = 3 * time.Second
96+
boff.MaxInterval = 5 * time.Second
97+
boff.MaxElapsedTime = 2 * time.Minute
98+
99+
if err := backoff.Retry(func() error {
100+
cmd := exec.Command(kubeCtlBin, "--kubeconfig", kubeConfig, "wait", "--for=condition=Ready", "node/"+nodeName)
101+
out, err := cmd.CombinedOutput()
102+
if err == nil && bytes.Contains(out, []byte("condition met")) {
103+
return nil
104+
}
105+
return errors.Wrapf(err, "node is not ready: %s %s", cmd.String(), string(out))
106+
}, boff); err != nil {
107+
logbuf.WriteString(errors.Unwrap(err).Error())
108+
return err
109+
}
110+
111+
return nil
112+
}

tests/integration_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ func init() {
1616
workers.InitDockerContainerWorker()
1717
} else {
1818
workers.InitRemoteWorker()
19+
workers.InitKubernetesWorker()
1920
}
2021
}
2122

tests/workers/kubernetes.go

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package workers
2+
3+
import (
4+
"context"
5+
"os"
6+
"os/exec"
7+
"path/filepath"
8+
"sync"
9+
10+
"github.com/docker/buildx/tests/helpers"
11+
"github.com/moby/buildkit/identity"
12+
"github.com/moby/buildkit/util/testutil/integration"
13+
"github.com/pkg/errors"
14+
)
15+
16+
func InitKubernetesWorker() {
17+
integration.Register(&kubernetesWorker{
18+
id: "kubernetes",
19+
})
20+
}
21+
22+
type kubernetesWorker struct {
23+
id string
24+
25+
unsupported []string
26+
27+
k3sConfig string
28+
k3sClose func() error
29+
k3sErr error
30+
k3sOnce sync.Once
31+
}
32+
33+
func (w *kubernetesWorker) Name() string {
34+
return w.id
35+
}
36+
37+
func (w *kubernetesWorker) Rootless() bool {
38+
return false
39+
}
40+
41+
func (w *kubernetesWorker) New(ctx context.Context, cfg *integration.BackendConfig) (integration.Backend, func() error, error) {
42+
var err error
43+
44+
w.k3sOnce.Do(func() {
45+
w.k3sConfig, w.k3sClose, w.k3sErr = helpers.NewK3sServer(cfg)
46+
})
47+
if w.k3sErr != nil {
48+
return nil, w.k3sClose, w.k3sErr
49+
}
50+
51+
cfgfile, err := integration.WriteConfig(cfg.DaemonConfig)
52+
if err != nil {
53+
return nil, nil, err
54+
}
55+
defer os.RemoveAll(filepath.Dir(cfgfile))
56+
57+
name := "integration-kubernetes-" + identity.NewID()
58+
cmd := exec.Command("buildx", "create",
59+
"--bootstrap",
60+
"--name="+name,
61+
"--config="+cfgfile,
62+
"--driver=kubernetes",
63+
)
64+
cmd.Env = append(
65+
os.Environ(),
66+
"BUILDX_CONFIG=/tmp/buildx-"+name,
67+
"KUBECONFIG="+w.k3sConfig,
68+
)
69+
if err := cmd.Run(); err != nil {
70+
return nil, nil, errors.Wrapf(err, "failed to create buildx instance %s", name)
71+
}
72+
73+
cl := func() error {
74+
cmd := exec.Command("buildx", "rm", "-f", name)
75+
return cmd.Run()
76+
}
77+
78+
return &backend{
79+
builder: name,
80+
unsupportedFeatures: w.unsupported,
81+
}, cl, nil
82+
}
83+
84+
func (w *kubernetesWorker) Close() error {
85+
if c := w.k3sClose; c != nil {
86+
return c()
87+
}
88+
89+
// reset the worker to be ready to go again
90+
w.k3sConfig = ""
91+
w.k3sClose = nil
92+
w.k3sErr = nil
93+
w.k3sOnce = sync.Once{}
94+
95+
return nil
96+
}

0 commit comments

Comments
 (0)