Skip to content

Commit ff8f997

Browse files
authored
feat: implement install and uninstall flow (#68)
add node-installer and shim-downloader to install job
1 parent 480ddad commit ff8f997

File tree

10 files changed

+193
-39
lines changed

10 files changed

+193
-39
lines changed

.github/dependabot.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ updates:
1212
interval: "weekly"
1313
labels:
1414
- "area/dependencies"
15+
- package-ecosystem: docker
16+
directory: "/images/downloader"
17+
schedule:
18+
interval: "weekly"
19+
labels:
20+
- "area/dependencies"
1521
- package-ecosystem: "github-actions"
1622
directory: "/"
1723
schedule:
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: Build shim-downloader image, sign it, and generate SBOMs
2+
3+
on:
4+
workflow_call:
5+
outputs:
6+
digest:
7+
description: "Container image digest"
8+
value: ${{jobs.build.outputs.digest}}
9+
10+
push:
11+
branches:
12+
- "main"
13+
- "feat-**"
14+
15+
jobs:
16+
build:
17+
uses: ./.github/workflows/container-image.yml
18+
permissions:
19+
contents: read
20+
packages: write
21+
with:
22+
image-name: shim-downloader
23+
dockerfile: ./images/downloader/Dockerfile
24+
docker-context: ./images/downloader
25+
push-image: true
26+
27+
sign:
28+
needs: build
29+
uses: ./.github/workflows/sign-image.yml
30+
permissions:
31+
packages: write
32+
id-token: write
33+
with:
34+
image-repository: ${{ needs.build.outputs.repository }}
35+
image-digest: ${{ needs.build.outputs.digest }}
36+
37+
sbom:
38+
needs: build
39+
uses: ./.github/workflows/sbom.yml
40+
permissions:
41+
packages: write
42+
id-token: write
43+
with:
44+
image-name: shim-downloader
45+
image-digest: ${{ needs.build.outputs.digest }}

.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"type": "go",
1010
"request": "launch",
1111
"mode": "auto",
12-
"program": "./cmd/main.go"
12+
"program": "./cmd/rcm/main.go"
1313
}
1414
]
1515
}

cmd/node-installer/uninstall.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@ var uninstallCmd = &cobra.Command{
3939
restarter := containerd.NewRestarter()
4040

4141
if err := RunUninstall(config, rootFs, hostFs, restarter); err != nil {
42-
slog.Error("failed to uninstall", "error", err)
43-
os.Exit(1)
42+
slog.Error("failed to uninstall shim", "error", err)
43+
44+
// Exiting with 0 to prevent Kubernetes Jobs from running repetitively
45+
os.Exit(0)
4446
}
4547
},
4648
}
@@ -50,7 +52,7 @@ func init() {
5052
}
5153

5254
func RunUninstall(config Config, rootFs, hostFs afero.Fs, restarter containerd.Restarter) error {
53-
slog.Info("uninstall called")
55+
slog.Info("uninstall called", "shim", config.Runtime.Name)
5456
shimName := config.Runtime.Name
5557
runtimeName := path.Join(config.Kwasm.Path, "bin", shimName)
5658

@@ -64,7 +66,7 @@ func RunUninstall(config Config, rootFs, hostFs afero.Fs, restarter containerd.R
6466

6567
configChanged, err := containerdConfig.RemoveRuntime(binPath)
6668
if err != nil {
67-
return fmt.Errorf("failed to write conteainerd config for shim '%s': %w", runtimeName, err)
69+
return fmt.Errorf("failed to write containerd config for shim '%s': %w", runtimeName, err)
6870
}
6971

7072
if !configChanged {

config/samples/test_shim_spin.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
apiVersion: runtime.kwasm.sh/v1alpha1
22
kind: Shim
33
metadata:
4-
name: wasmtime-spin-v2
4+
name: spin-v2
55
labels:
6-
app.kubernetes.io/name: wasmtime-spin-v2
7-
app.kubernetes.io/instance: wasmtime-spin-v2
6+
app.kubernetes.io/name: spin-v2
7+
app.kubernetes.io/instance: spin-v2
88
app.kubernetes.io/part-of: kwasm-operator
99
app.kubernetes.io/managed-by: kustomize
1010
app.kubernetes.io/created-by: kwasm-operator
@@ -15,10 +15,10 @@ spec:
1515
fetchStrategy:
1616
type: annonymousHttp
1717
anonHttp:
18-
location: "https://github.com/deislabs/containerd-wasm-shims/releases/download/v0.10.0/containerd-wasm-shims-v2-spin-linux-aarch64.tar.gz"
18+
location: "https://github.com/spinkube/containerd-shim-spin/releases/download/v0.14.1/containerd-shim-spin-v2-linux-aarch64.tar.gz"
1919

2020
runtimeClass:
21-
name: wasmtime-spin-v2
21+
name: spin-v2
2222
handler: spin
2323

2424
rolloutStrategy:

images/downloader/Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM alpine:3.19.1
2+
3+
RUN apk add --no-cache curl bash tar
4+
COPY download_shim.sh /download_shim.sh
5+
CMD bash /download_shim.sh

images/downloader/download_shim.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
declare -A levels=([DEBUG]=0 [INFO]=1 [WARN]=2 [ERROR]=3)
5+
script_logging_level="INFO"
6+
7+
log() {
8+
local log_message=$1
9+
local log_priority=$2
10+
11+
#check if level exists
12+
[[ ${levels[$log_priority]} ]] || return 1
13+
14+
#check if level is enough
15+
(( ${levels[$log_priority]} < ${levels[$script_logging_level]} )) && return 2
16+
17+
#log here
18+
d=$(date '+%Y-%m-%dT%H:%M:%S')
19+
echo -e "${d}\t${log_priority}\t${log_message}"
20+
}
21+
22+
log "start downloading shim from ${SHIM_LOCATION}..." "INFO"
23+
24+
mkdir -p /assets
25+
26+
# overwrite default name of shim binary; use the name of shim resource instead
27+
# to enable installing multiple versions of the same shim
28+
curl -sL "${SHIM_LOCATION}" | tar --transform "s/containerd-shim-.*/containerd-shim-${SHIM_NAME}/" -xzf - -C /assets
29+
log "download successful:" "INFO"
30+
31+
ls -lah /assets

internal/controller/shim_controller.go

Lines changed: 90 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ type ShimReconciler struct {
5656
Scheme *runtime.Scheme
5757
}
5858

59+
// configuration for INSTALL or UNINSTALL jobs
60+
type opConfig struct {
61+
operation string
62+
privileged bool
63+
initContainer []corev1.Container
64+
args []string
65+
}
66+
5967
//+kubebuilder:rbac:groups=runtime.kwasm.sh,resources=shims,verbs=get;list;watch;create;update;patch;delete
6068
//+kubebuilder:rbac:groups=runtime.kwasm.sh,resources=shims/status,verbs=get;update;patch
6169
//+kubebuilder:rbac:groups=runtime.kwasm.sh,resources=shims/finalizers,verbs=update
@@ -301,7 +309,7 @@ func (sr *ShimReconciler) deployJobOnNode(ctx context.Context, shim *rcmv1.Shim,
301309

302310
// We rely on controller-runtime to rate limit us.
303311
if err := sr.Client.Patch(ctx, job, patchMethod, patchOptions); err != nil {
304-
log.Error().Msgf("Unable to reconcile Job %s", err)
312+
log.Error().Msgf("Unable to reconcile Job: %s", err)
305313
if err := sr.updateNodeLabels(ctx, &node, shim, "failed"); err != nil {
306314
log.Error().Msgf("Unable to update node label %s: %s", shim.Name, err)
307315
}
@@ -321,13 +329,69 @@ func (sr *ShimReconciler) updateNodeLabels(ctx context.Context, node *corev1.Nod
321329
return nil
322330
}
323331

332+
// setOperationConfiguration sets operation specific configuration for the job manifest
333+
func (sr *ShimReconciler) setOperationConfiguration(shim *rcmv1.Shim, opConfig *opConfig) {
334+
if opConfig.operation == INSTALL {
335+
opConfig.initContainer = []corev1.Container{{
336+
Image: os.Getenv("SHIM_DOWNLOADER_IMAGE"),
337+
Name: "downloader",
338+
SecurityContext: &corev1.SecurityContext{
339+
Privileged: &opConfig.privileged,
340+
},
341+
Env: []corev1.EnvVar{
342+
{
343+
Name: "SHIM_NAME",
344+
Value: shim.Name,
345+
},
346+
{
347+
Name: "SHIM_LOCATION",
348+
Value: shim.Spec.FetchStrategy.AnonHTTP.Location,
349+
},
350+
},
351+
VolumeMounts: []corev1.VolumeMount{
352+
{
353+
Name: "shim-download",
354+
MountPath: "/assets",
355+
},
356+
},
357+
}}
358+
opConfig.args = []string{
359+
"install",
360+
"-H",
361+
"/mnt/node-root",
362+
"-r",
363+
shim.Name,
364+
}
365+
}
366+
367+
if opConfig.operation == UNINSTALL {
368+
opConfig.initContainer = nil
369+
opConfig.args = []string{
370+
"uninstall",
371+
"-H",
372+
"/mnt/node-root",
373+
"-r",
374+
shim.Name,
375+
}
376+
}
377+
}
378+
324379
// createJobManifest creates a Job manifest for a Shim.
325380
func (sr *ShimReconciler) createJobManifest(shim *rcmv1.Shim, node *corev1.Node, operation string) (*batchv1.Job, error) {
326-
priv := true
381+
opConfig := opConfig{
382+
operation: operation,
383+
privileged: true,
384+
}
385+
sr.setOperationConfiguration(shim, &opConfig)
386+
327387
name := node.Name + "-" + shim.Name + "-" + operation
328388
nameMax := int(math.Min(float64(len(name)), 63))
329389

330390
job := &batchv1.Job{
391+
TypeMeta: metav1.TypeMeta{
392+
APIVersion: "batch/v1",
393+
Kind: "Job",
394+
},
331395
ObjectMeta: metav1.ObjectMeta{
332396
Name: name[:nameMax],
333397
Namespace: os.Getenv("CONTROLLER_NAMESPACE"),
@@ -348,37 +412,32 @@ func (sr *ShimReconciler) createJobManifest(shim *rcmv1.Shim, node *corev1.Node,
348412
Spec: corev1.PodSpec{
349413
NodeName: node.Name,
350414
HostPID: true,
351-
Volumes: []corev1.Volume{{
352-
Name: "root-mount",
353-
VolumeSource: corev1.VolumeSource{
354-
HostPath: &corev1.HostPathVolumeSource{
355-
Path: "/",
415+
Volumes: []corev1.Volume{
416+
{
417+
Name: "shim-download",
418+
},
419+
{
420+
Name: "root-mount",
421+
VolumeSource: corev1.VolumeSource{
422+
HostPath: &corev1.HostPathVolumeSource{
423+
Path: "/",
424+
},
356425
},
357426
},
358-
}},
427+
},
428+
InitContainers: opConfig.initContainer,
359429
Containers: []corev1.Container{{
360-
Image: "voigt/kwasm-node-installer:" + operation,
430+
Image: os.Getenv("SHIM_NODE_INSTALLER_IMAGE"),
431+
Args: opConfig.args,
361432
Name: "provisioner",
362433
SecurityContext: &corev1.SecurityContext{
363-
Privileged: &priv,
434+
Privileged: &opConfig.privileged,
364435
},
365436
Env: []corev1.EnvVar{
366437
{
367-
Name: "NODE_ROOT",
438+
Name: "HOST_ROOT",
368439
Value: "/mnt/node-root",
369440
},
370-
{
371-
Name: "SHIM_LOCATION",
372-
Value: shim.Spec.FetchStrategy.AnonHTTP.Location,
373-
},
374-
{
375-
Name: "RUNTIMECLASS_NAME",
376-
Value: shim.Spec.RuntimeClass.Name,
377-
},
378-
{
379-
Name: "RUNTIMECLASS_HANDLER",
380-
Value: shim.Spec.RuntimeClass.Handler,
381-
},
382441
{
383442
Name: "SHIM_FETCH_STRATEGY",
384443
Value: "/mnt/node-root",
@@ -389,6 +448,10 @@ func (sr *ShimReconciler) createJobManifest(shim *rcmv1.Shim, node *corev1.Node,
389448
Name: "root-mount",
390449
MountPath: "/mnt/node-root",
391450
},
451+
{
452+
Name: "shim-download",
453+
MountPath: "/assets",
454+
},
392455
},
393456
}},
394457
RestartPolicy: corev1.RestartPolicyNever,
@@ -443,6 +506,10 @@ func (sr *ShimReconciler) createRuntimeClassManifest(shim *rcmv1.Shim) (*nodev1.
443506
}
444507

445508
runtimeClass := &nodev1.RuntimeClass{
509+
TypeMeta: metav1.TypeMeta{
510+
APIVersion: "node.k8s.io/v1",
511+
Kind: "RuntimeClass",
512+
},
446513
ObjectMeta: metav1.ObjectMeta{
447514
Name: name[:nameMax],
448515
Labels: map[string]string{name[:nameMax]: "true"},

internal/shim/uninstall.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package shim
22

33
import (
44
"errors"
5-
"log/slog"
5+
"fmt"
66
"os"
77

88
"github.com/spinkube/runtime-class-manager/internal/state"
@@ -15,17 +15,15 @@ func (c *Config) Uninstall(shimName string) (string, error) {
1515
}
1616
s, ok := st.Shims[shimName]
1717
if !ok {
18-
slog.Warn("shim not installed", "shim", shimName)
19-
return "", nil
18+
return "", fmt.Errorf("shim %s not installed", shimName)
2019
}
2120
filePath := s.Path
2221

2322
err = c.hostFs.Remove(filePath)
2423
if err != nil {
2524
if !errors.Is(err, os.ErrNotExist) {
26-
return "", err
25+
return "", fmt.Errorf("shim binary at %s does not exist, nothing to delete", filePath)
2726
}
28-
slog.Warn("shim binary did not exist, nothing to delete")
2927
}
3028
st.RemoveShim(shimName)
3129
if err = st.Write(); err != nil {

internal/shim/uninstall_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func TestConfig_Uninstall(t *testing.T) {
4646
},
4747
args{"not-existing-shim"},
4848
"",
49-
false,
49+
true,
5050
},
5151
{
5252
"missing shim binary",

0 commit comments

Comments
 (0)