Skip to content

Commit 2973163

Browse files
committed
feat(sync): always pull mutable sidecar image tags
1 parent 5f4e6a6 commit 2973163

2 files changed

Lines changed: 60 additions & 3 deletions

File tree

internal/kube/podspec.go

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ package kube
22

33
import (
44
"fmt"
5+
"regexp"
6+
"strings"
57

68
corev1 "k8s.io/api/core/v1"
79
)
810

11+
var semverTagPattern = regexp.MustCompile(`^v?\d+\.\d+\.\d+([.-][0-9A-Za-z.-]+)?$`)
12+
913
func PreparePodSpec(podSpec corev1.PodSpec, workspaceClaim, workspaceMountPath string, syncthingEnabled bool, syncthingImage string) (corev1.PodSpec, error) {
1014
spec := podSpec.DeepCopy()
1115
if spec == nil {
@@ -43,9 +47,10 @@ func PreparePodSpec(podSpec corev1.PodSpec, workspaceClaim, workspaceMountPath s
4347
})
4448
if !hasContainer(spec.Containers, "syncthing") {
4549
spec.Containers = append(spec.Containers, corev1.Container{
46-
Name: "syncthing",
47-
Image: syncthingImage,
48-
Command: []string{"sh", "-lc", "syncthing serve --home /var/syncthing --no-browser --gui-address=http://0.0.0.0:8384 --no-restart --skip-port-probing"},
50+
Name: "syncthing",
51+
Image: syncthingImage,
52+
ImagePullPolicy: syncthingImagePullPolicy(syncthingImage),
53+
Command: []string{"sh", "-lc", "syncthing serve --home /var/syncthing --no-browser --gui-address=http://0.0.0.0:8384 --no-restart --skip-port-probing"},
4954
Ports: []corev1.ContainerPort{
5055
{ContainerPort: 8384, Name: "st-gui"},
5156
{ContainerPort: 22000, Name: "st-sync"},
@@ -61,6 +66,29 @@ func PreparePodSpec(podSpec corev1.PodSpec, workspaceClaim, workspaceMountPath s
6166
return *spec, nil
6267
}
6368

69+
func syncthingImagePullPolicy(image string) corev1.PullPolicy {
70+
if strings.Contains(image, "@sha256:") {
71+
return corev1.PullIfNotPresent
72+
}
73+
tag := imageTag(image)
74+
if tag == "" {
75+
return corev1.PullAlways
76+
}
77+
if semverTagPattern.MatchString(tag) {
78+
return corev1.PullIfNotPresent
79+
}
80+
return corev1.PullAlways
81+
}
82+
83+
func imageTag(image string) string {
84+
lastSlash := strings.LastIndex(image, "/")
85+
lastColon := strings.LastIndex(image, ":")
86+
if lastColon <= lastSlash {
87+
return ""
88+
}
89+
return strings.TrimSpace(image[lastColon+1:])
90+
}
91+
6492
func ensureVolume(volumes []corev1.Volume, v corev1.Volume) []corev1.Volume {
6593
for _, item := range volumes {
6694
if item.Name == v.Name {

internal/kube/podspec_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ func TestPreparePodSpecAddsWorkspaceAndSyncthing(t *testing.T) {
1717
if !hasContainer(spec.Containers, "syncthing") {
1818
t.Fatal("expected syncthing container")
1919
}
20+
var st corev1.Container
21+
for _, c := range spec.Containers {
22+
if c.Name == "syncthing" {
23+
st = c
24+
break
25+
}
26+
}
27+
if st.ImagePullPolicy != corev1.PullAlways {
28+
t.Fatalf("expected PullAlways for mutable tag, got %s", st.ImagePullPolicy)
29+
}
2030
}
2131

2232
func TestPreparePodSpecWithoutSyncthing(t *testing.T) {
@@ -28,3 +38,22 @@ func TestPreparePodSpecWithoutSyncthing(t *testing.T) {
2838
t.Fatal("did not expect syncthing container")
2939
}
3040
}
41+
42+
func TestSyncthingImagePullPolicy(t *testing.T) {
43+
cases := []struct {
44+
image string
45+
want corev1.PullPolicy
46+
}{
47+
{image: "ghcr.io/acmore/okdev:edge", want: corev1.PullAlways},
48+
{image: "ghcr.io/acmore/okdev:latest", want: corev1.PullAlways},
49+
{image: "ghcr.io/acmore/okdev", want: corev1.PullAlways},
50+
{image: "ghcr.io/acmore/okdev:v0.1.0", want: corev1.PullIfNotPresent},
51+
{image: "ghcr.io/acmore/okdev:1.2.3", want: corev1.PullIfNotPresent},
52+
{image: "ghcr.io/acmore/okdev@sha256:deadbeef", want: corev1.PullIfNotPresent},
53+
}
54+
for _, tc := range cases {
55+
if got := syncthingImagePullPolicy(tc.image); got != tc.want {
56+
t.Fatalf("image %q: want %s, got %s", tc.image, tc.want, got)
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)