From 7fca0bef94b715dff6ac749e6392814cba02c328 Mon Sep 17 00:00:00 2001 From: Salah Al Saleh Date: Fri, 12 Jan 2024 17:49:36 +0000 Subject: [PATCH] Detect MinIO image from the 'ha-minio' StatefulSet if exists --- pkg/image/minio.go | 26 ++++++-- pkg/image/minio_test.go | 131 +++++++++++++++++++++++++++++++++++++ pkg/kurl/configmap.go | 10 +-- pkg/kurl/configmap_test.go | 6 +- 4 files changed, 158 insertions(+), 15 deletions(-) create mode 100644 pkg/image/minio_test.go diff --git a/pkg/image/minio.go b/pkg/image/minio.go index 2db1eb774f..313eb0020f 100644 --- a/pkg/image/minio.go +++ b/pkg/image/minio.go @@ -25,16 +25,28 @@ func GetMinioImage(clientset kubernetes.Interface, kotsadmNamespace string) (str } deployment, err := clientset.AppsV1().Deployments("minio").Get(context.TODO(), "minio", metav1.GetOptions{}) - if err != nil { - if kuberneteserrors.IsNotFound(err) { - return "", nil - } + if err != nil && !kuberneteserrors.IsNotFound(err) { return "", errors.Wrap(err, "failed to get minio deployment") } + if err == nil { + for _, container := range deployment.Spec.Template.Spec.Containers { + if strings.Contains(container.Image, "minio/minio:RELEASE.") { + return container.Image, nil + } + } + } - for _, container := range deployment.Spec.Template.Spec.Containers { - if strings.Contains(container.Image, "minio/minio:RELEASE.") { - return container.Image, nil + // minio deployment doesn't exist, check if ha-minio statefulset exists + + statefulset, err := clientset.AppsV1().StatefulSets("minio").Get(context.TODO(), "ha-minio", metav1.GetOptions{}) + if err != nil && !kuberneteserrors.IsNotFound(err) { + return "", errors.Wrap(err, "failed to get ha-minio statefulset") + } + if err == nil { + for _, container := range statefulset.Spec.Template.Spec.Containers { + if strings.Contains(container.Image, "minio/minio:RELEASE.") { + return container.Image, nil + } } } diff --git a/pkg/image/minio_test.go b/pkg/image/minio_test.go new file mode 100644 index 0000000000..64208040c6 --- /dev/null +++ b/pkg/image/minio_test.go @@ -0,0 +1,131 @@ +package image + +import ( + "testing" + + "github.com/replicatedhq/kots/pkg/kurl" + "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/fake" +) + +func Test_GetMinioImage(t *testing.T) { + tests := []struct { + name string + clientset kubernetes.Interface + kotsadmNamespace string + wantImage string + wantErr bool + }{ + { + name: "should return static image for non-kurl instance", + clientset: fake.NewSimpleClientset(), + kotsadmNamespace: metav1.NamespaceDefault, + wantImage: Minio, + wantErr: false, + }, + { + name: "should return static image for kurl instance with non-default namespace", + clientset: fake.NewSimpleClientset(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: kurl.ConfigMapName, + Namespace: kurl.ConfigMapNamespace, + }, + }), + kotsadmNamespace: "custom-namespace", + wantImage: Minio, + wantErr: false, + }, + { + name: "should return minio image from deployment for kurl instance with default namespace", + clientset: fake.NewSimpleClientset( + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: kurl.ConfigMapName, + Namespace: kurl.ConfigMapNamespace, + }, + }, + &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "minio", + Namespace: "minio", + }, + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "minio", + Image: "minio/minio:RELEASE.2020-10-27T00-54-19Z", + }, + }, + }, + }, + }, + }, + ), + kotsadmNamespace: metav1.NamespaceDefault, + wantImage: "minio/minio:RELEASE.2020-10-27T00-54-19Z", + wantErr: false, + }, + { + name: "should return minio image from statefulset for kurl instance with default namespace", + clientset: fake.NewSimpleClientset( + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: kurl.ConfigMapName, + Namespace: kurl.ConfigMapNamespace, + }, + }, + &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ha-minio", + Namespace: "minio", + }, + Spec: appsv1.StatefulSetSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "minio", + Image: "minio/minio:RELEASE.2020-10-27T00-54-19Z", + }, + }, + }, + }, + }, + }), + kotsadmNamespace: metav1.NamespaceDefault, + wantImage: "minio/minio:RELEASE.2020-10-27T00-54-19Z", + wantErr: false, + }, + { + name: "should return empty image if deployment and statefulset don't exist for kurl instance with default namespace", + clientset: fake.NewSimpleClientset(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: kurl.ConfigMapName, + Namespace: kurl.ConfigMapNamespace, + }, + }), + kotsadmNamespace: metav1.NamespaceDefault, + wantImage: "", + wantErr: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + req := require.New(t) + gotImage, err := GetMinioImage(test.clientset, test.kotsadmNamespace) + if test.wantErr { + req.Error(err) + } else { + req.NoError(err) + } + req.Equal(test.wantImage, gotImage) + }) + } +} diff --git a/pkg/kurl/configmap.go b/pkg/kurl/configmap.go index 7213c7301a..bfbe5275f5 100644 --- a/pkg/kurl/configmap.go +++ b/pkg/kurl/configmap.go @@ -13,8 +13,8 @@ import ( "k8s.io/client-go/kubernetes" ) -const configMapName = "kurl-config" -const configMapNamespace = "kube-system" +const ConfigMapName = "kurl-config" +const ConfigMapNamespace = "kube-system" const bootstrapTokenKey = "bootstrap_token" const bootstrapTokenExpirationKey = "bootstrap_token_expiration" @@ -46,12 +46,12 @@ func IsKurl(clientset kubernetes.Interface) (bool, error) { // ReadConfigMap will read the Kurl config from a configmap func ReadConfigMap(client kubernetes.Interface) (*corev1.ConfigMap, error) { - return client.CoreV1().ConfigMaps(configMapNamespace).Get(context.TODO(), configMapName, metav1.GetOptions{}) + return client.CoreV1().ConfigMaps(ConfigMapNamespace).Get(context.TODO(), ConfigMapName, metav1.GetOptions{}) } // UpdateConfigMap will save the Kurl config in a configmap func UpdateConfigMap(client kubernetes.Interface, generateBootstrapToken, uploadCerts bool) (*corev1.ConfigMap, error) { - cm, err := client.CoreV1().ConfigMaps(configMapNamespace).Get(context.TODO(), configMapName, metav1.GetOptions{}) + cm, err := client.CoreV1().ConfigMaps(ConfigMapNamespace).Get(context.TODO(), ConfigMapName, metav1.GetOptions{}) if err != nil { return nil, errors.Wrap(err, "get configmap") } @@ -94,7 +94,7 @@ func UpdateConfigMap(client kubernetes.Interface, generateBootstrapToken, upload cm.Data[certsExpirationKey] = certsExpiration.Format(time.RFC3339) } - cm, err = client.CoreV1().ConfigMaps(configMapNamespace).Update(context.TODO(), cm, metav1.UpdateOptions{}) + cm, err = client.CoreV1().ConfigMaps(ConfigMapNamespace).Update(context.TODO(), cm, metav1.UpdateOptions{}) if err != nil { return nil, errors.Wrap(err, "update configmap") } diff --git a/pkg/kurl/configmap_test.go b/pkg/kurl/configmap_test.go index 976470a269..c8e852abb2 100644 --- a/pkg/kurl/configmap_test.go +++ b/pkg/kurl/configmap_test.go @@ -36,7 +36,7 @@ func Test_IsKurl(t *testing.T) { }, { name: "expect false when configmap is not found", - args: mockClientWithError(kuberneteserrors.NewNotFound(corev1.Resource("configmaps"), configMapName)), + args: mockClientWithError(kuberneteserrors.NewNotFound(corev1.Resource("configmaps"), ConfigMapName)), want: false, }, { @@ -46,7 +46,7 @@ func Test_IsKurl(t *testing.T) { }, { name: "expect false when client is forbidden", - args: mockClientWithError(kuberneteserrors.NewForbidden(corev1.Resource("configmaps"), configMapName, errors.New("Forbidden"))), + args: mockClientWithError(kuberneteserrors.NewForbidden(corev1.Resource("configmaps"), ConfigMapName, errors.New("Forbidden"))), want: false, }, { @@ -59,7 +59,7 @@ func Test_IsKurl(t *testing.T) { name: "expect true when configmap is found", args: fake.NewSimpleClientset(&corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: configMapName, + Name: ConfigMapName, Namespace: metav1.NamespaceSystem, }, }),