Skip to content

Commit e44fc1a

Browse files
sgalsalehclaude
andcommitted
fix(snapshots): detect kURL minio add-on from any repository
GetMinioImage only recognized the add-on when its image came from minio/minio, so the kurlsh/minio image kURL now ships returned "". That made IsFileSystemMinioDisabled report minio as disabled, which put NFS/host path snapshots into LVP mode and hid both destinations from the Admin Console unless the local-volume-provider plugin was installed. Match on the image name and RELEASE tag instead of the registry and organization. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 4e03eb7 commit e44fc1a

2 files changed

Lines changed: 112 additions & 3 deletions

File tree

pkg/image/minio.go

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

33
import (
44
"context"
5-
"strings"
5+
"regexp"
66

77
"github.com/pkg/errors"
88
"github.com/replicatedhq/kots/pkg/kurl"
@@ -11,6 +11,11 @@ import (
1111
"k8s.io/client-go/kubernetes"
1212
)
1313

14+
// minioReleaseImageRegexp matches any repository whose image name is "minio" with an
15+
// upstream RELEASE tag, e.g. "minio/minio:RELEASE.2025-10-15T17-29-55Z" and
16+
// "kurlsh/minio:RELEASE.2025-10-15T17-29-55Z".
17+
var minioReleaseImageRegexp = regexp.MustCompile(`(^|/)minio:RELEASE\.`)
18+
1419
// MinioImage looks through the nodes in the cluster and finds nodes that have already pulled Minio, and then finds the latest image tag listed
1520
func GetMinioImage(clientset kubernetes.Interface, kotsadmNamespace string) (string, error) {
1621
/*
@@ -30,7 +35,7 @@ func GetMinioImage(clientset kubernetes.Interface, kotsadmNamespace string) (str
3035
}
3136
if err == nil {
3237
for _, container := range deployment.Spec.Template.Spec.Containers {
33-
if strings.Contains(container.Image, "minio/minio:RELEASE.") {
38+
if isMinioReleaseImage(container.Image) {
3439
return container.Image, nil
3540
}
3641
}
@@ -44,11 +49,19 @@ func GetMinioImage(clientset kubernetes.Interface, kotsadmNamespace string) (str
4449
}
4550
if err == nil {
4651
for _, container := range statefulset.Spec.Template.Spec.Containers {
47-
if strings.Contains(container.Image, "minio/minio:RELEASE.") {
52+
if isMinioReleaseImage(container.Image) {
4853
return container.Image, nil
4954
}
5055
}
5156
}
5257

5358
return "", nil
5459
}
60+
61+
// isMinioReleaseImage returns true if the image is a minio image with an upstream
62+
// RELEASE tag, regardless of which repository it was published to. The add-on has
63+
// shipped from more than one repository (minio/minio, kurlsh/minio), so only the
64+
// image name and tag are matched, not the registry or organization.
65+
func isMinioReleaseImage(image string) bool {
66+
return minioReleaseImageRegexp.MatchString(image)
67+
}

pkg/image/minio_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,102 @@ func Test_GetMinioImage(t *testing.T) {
7171
wantImage: "minio/minio:RELEASE.2020-10-27T00-54-19Z",
7272
wantErr: false,
7373
},
74+
{
75+
name: "should return minio image from deployment when the add-on ships from the kurlsh repository",
76+
clientset: fake.NewSimpleClientset(
77+
&corev1.ConfigMap{
78+
ObjectMeta: metav1.ObjectMeta{
79+
Name: kurl.ConfigMapName,
80+
Namespace: kurl.ConfigMapNamespace,
81+
},
82+
},
83+
&appsv1.Deployment{
84+
ObjectMeta: metav1.ObjectMeta{
85+
Name: "minio",
86+
Namespace: "minio",
87+
},
88+
Spec: appsv1.DeploymentSpec{
89+
Template: corev1.PodTemplateSpec{
90+
Spec: corev1.PodSpec{
91+
Containers: []corev1.Container{
92+
{
93+
Name: "minio",
94+
Image: "kurlsh/minio:RELEASE.2025-10-15T17-29-55Z",
95+
},
96+
},
97+
},
98+
},
99+
},
100+
},
101+
),
102+
kotsadmNamespace: metav1.NamespaceDefault,
103+
wantImage: "kurlsh/minio:RELEASE.2025-10-15T17-29-55Z",
104+
wantErr: false,
105+
},
106+
{
107+
name: "should return minio image from deployment when the add-on is pulled from a private registry",
108+
clientset: fake.NewSimpleClientset(
109+
&corev1.ConfigMap{
110+
ObjectMeta: metav1.ObjectMeta{
111+
Name: kurl.ConfigMapName,
112+
Namespace: kurl.ConfigMapNamespace,
113+
},
114+
},
115+
&appsv1.Deployment{
116+
ObjectMeta: metav1.ObjectMeta{
117+
Name: "minio",
118+
Namespace: "minio",
119+
},
120+
Spec: appsv1.DeploymentSpec{
121+
Template: corev1.PodTemplateSpec{
122+
Spec: corev1.PodSpec{
123+
Containers: []corev1.Container{
124+
{
125+
Name: "minio",
126+
Image: "registry.example.com:5000/kurlsh/minio:RELEASE.2025-10-15T17-29-55Z",
127+
},
128+
},
129+
},
130+
},
131+
},
132+
},
133+
),
134+
kotsadmNamespace: metav1.NamespaceDefault,
135+
wantImage: "registry.example.com:5000/kurlsh/minio:RELEASE.2025-10-15T17-29-55Z",
136+
wantErr: false,
137+
},
138+
{
139+
name: "should return empty image when the minio container is not a RELEASE build",
140+
clientset: fake.NewSimpleClientset(
141+
&corev1.ConfigMap{
142+
ObjectMeta: metav1.ObjectMeta{
143+
Name: kurl.ConfigMapName,
144+
Namespace: kurl.ConfigMapNamespace,
145+
},
146+
},
147+
&appsv1.Deployment{
148+
ObjectMeta: metav1.ObjectMeta{
149+
Name: "minio",
150+
Namespace: "minio",
151+
},
152+
Spec: appsv1.DeploymentSpec{
153+
Template: corev1.PodTemplateSpec{
154+
Spec: corev1.PodSpec{
155+
Containers: []corev1.Container{
156+
{
157+
Name: "minio",
158+
Image: "kurlsh/minio:0.20250101.0",
159+
},
160+
},
161+
},
162+
},
163+
},
164+
},
165+
),
166+
kotsadmNamespace: metav1.NamespaceDefault,
167+
wantImage: "",
168+
wantErr: false,
169+
},
74170
{
75171
name: "should return minio image from statefulset for kurl instance with default namespace",
76172
clientset: fake.NewSimpleClientset(

0 commit comments

Comments
 (0)