From 492852609bfcf1b8984345de6e3958a2fada8ad2 Mon Sep 17 00:00:00 2001 From: yandongxiao Date: Mon, 27 Nov 2023 17:22:09 +0800 Subject: [PATCH] [Enhancement] Support to mount emptyDir in storageVolumes Signed-off-by: yandongxiao --- .../starrocks.com_starrocksclusters.yaml | 8 +- .../starrocks.com_starrockswarehouses.yaml | 2 +- deploy/starrocks.com_starrocksclusters.yaml | 8 +- deploy/starrocks.com_starrockswarehouses.yaml | 2 +- doc/mount_persistent_volume_howto.md | 2 +- ...a_starrocks_cluster_with_all_features.yaml | 2 +- .../starrocks/templates/starrockscluster.yaml | 74 ++- .../charts/starrocks/values.yaml | 25 +- helm-charts/charts/kube-starrocks/values.yaml | 25 +- .../starrocks/v1/starrockscluster_types.go | 5 +- pkg/common/common.go | 9 + pkg/k8sutils/templates/pod/mount.go | 145 +++++ pkg/k8sutils/templates/pod/mount_test.go | 298 ++++++++++ pkg/k8sutils/templates/pod/probe.go | 52 ++ pkg/k8sutils/templates/pod/probe_test.go | 248 ++++++++ pkg/k8sutils/templates/pod/spec.go | 148 ----- pkg/k8sutils/templates/pod/spec_test.go | 529 ------------------ pkg/k8sutils/templates/statefulset/spec.go | 21 +- pkg/subcontrollers/be/be_pod.go | 25 +- pkg/subcontrollers/cn/cn_pod.go | 11 +- pkg/subcontrollers/fe/fe_pod.go | 26 +- 21 files changed, 879 insertions(+), 786 deletions(-) create mode 100644 pkg/common/common.go create mode 100644 pkg/k8sutils/templates/pod/mount.go create mode 100644 pkg/k8sutils/templates/pod/mount_test.go create mode 100644 pkg/k8sutils/templates/pod/probe.go create mode 100644 pkg/k8sutils/templates/pod/probe_test.go diff --git a/config/crd/bases/starrocks.com_starrocksclusters.yaml b/config/crd/bases/starrocks.com_starrocksclusters.yaml index eb836165..5f652ee6 100644 --- a/config/crd/bases/starrocks.com_starrocksclusters.yaml +++ b/config/crd/bases/starrocks.com_starrocksclusters.yaml @@ -1289,8 +1289,8 @@ spec: volume should be mounted. Defaults to "" (volume's root). type: string required: + - mountPath - name - - storageSize type: object type: array terminationGracePeriodSeconds: @@ -3267,8 +3267,8 @@ spec: volume should be mounted. Defaults to "" (volume's root). type: string required: + - mountPath - name - - storageSize type: object type: array terminationGracePeriodSeconds: @@ -4392,8 +4392,8 @@ spec: volume should be mounted. Defaults to "" (volume's root). type: string required: + - mountPath - name - - storageSize type: object type: array tolerations: @@ -5677,8 +5677,8 @@ spec: volume should be mounted. Defaults to "" (volume's root). type: string required: + - mountPath - name - - storageSize type: object type: array terminationGracePeriodSeconds: diff --git a/config/crd/bases/starrocks.com_starrockswarehouses.yaml b/config/crd/bases/starrocks.com_starrockswarehouses.yaml index 0c36fb21..42210c57 100644 --- a/config/crd/bases/starrocks.com_starrockswarehouses.yaml +++ b/config/crd/bases/starrocks.com_starrockswarehouses.yaml @@ -1966,8 +1966,8 @@ spec: volume should be mounted. Defaults to "" (volume's root). type: string required: + - mountPath - name - - storageSize type: object type: array terminationGracePeriodSeconds: diff --git a/deploy/starrocks.com_starrocksclusters.yaml b/deploy/starrocks.com_starrocksclusters.yaml index 8eba56ef..5c07113e 100644 --- a/deploy/starrocks.com_starrocksclusters.yaml +++ b/deploy/starrocks.com_starrocksclusters.yaml @@ -598,8 +598,8 @@ spec: subPath: type: string required: + - mountPath - name - - storageSize type: object type: array terminationGracePeriodSeconds: @@ -1507,8 +1507,8 @@ spec: subPath: type: string required: + - mountPath - name - - storageSize type: object type: array terminationGracePeriodSeconds: @@ -1995,8 +1995,8 @@ spec: subPath: type: string required: + - mountPath - name - - storageSize type: object type: array tolerations: @@ -2573,8 +2573,8 @@ spec: subPath: type: string required: + - mountPath - name - - storageSize type: object type: array terminationGracePeriodSeconds: diff --git a/deploy/starrocks.com_starrockswarehouses.yaml b/deploy/starrocks.com_starrockswarehouses.yaml index 2c60a76a..3c091ede 100644 --- a/deploy/starrocks.com_starrockswarehouses.yaml +++ b/deploy/starrocks.com_starrockswarehouses.yaml @@ -923,8 +923,8 @@ spec: subPath: type: string required: + - mountPath - name - - storageSize type: object type: array terminationGracePeriodSeconds: diff --git a/doc/mount_persistent_volume_howto.md b/doc/mount_persistent_volume_howto.md index 2077b6b5..61fe7865 100644 --- a/doc/mount_persistent_volume_howto.md +++ b/doc/mount_persistent_volume_howto.md @@ -76,7 +76,7 @@ The following is a snippet of the values.yaml file: ```yaml starrocks: starrocksFESpec: - # fe storageSpec for persistent meta data. + # fe storageSpec for persistent metadata. storageSpec: name: "" # the storageClassName represent the used storageclass name. if not set will use k8s cluster default storageclass. diff --git a/examples/starrocks/deploy_a_starrocks_cluster_with_all_features.yaml b/examples/starrocks/deploy_a_starrocks_cluster_with_all_features.yaml index 89b3223d..109d8864 100644 --- a/examples/starrocks/deploy_a_starrocks_cluster_with_all_features.yaml +++ b/examples/starrocks/deploy_a_starrocks_cluster_with_all_features.yaml @@ -22,7 +22,7 @@ spec: configMapInfo: configMapName: starrockscluster-sample-fe-cm resolveKey: fe.conf - # fe storage volumes for persistent meta data and log + # fe storage volumes for persistent metadata and log storageVolumes: - name: fe-storage-meta # the storageClassName represent the used storageclass name. if not set will use k8s cluster default storageclass. diff --git a/helm-charts/charts/kube-starrocks/charts/starrocks/templates/starrockscluster.yaml b/helm-charts/charts/kube-starrocks/charts/starrocks/templates/starrockscluster.yaml index a98fdfdc..b54ca383 100644 --- a/helm-charts/charts/kube-starrocks/charts/starrocks/templates/starrockscluster.yaml +++ b/helm-charts/charts/kube-starrocks/charts/starrocks/templates/starrockscluster.yaml @@ -134,27 +134,26 @@ spec: configMapInfo: configMapName: {{ template "starrockscluster.fe.configmap.name" . }} resolveKey: fe.conf -{{- if .Values.starrocksFESpec.storageSpec.name }} + {{- if or .Values.starrocksFESpec.storageSpec.name .Values.starrocksFESpec.emptyDirs }} storageVolumes: + {{- if .Values.starrocksFESpec.storageSpec.name }} - name: {{ .Values.starrocksFESpec.storageSpec.name }}{{ template "starrockscluster.fe.meta.suffix" . }} -{{- if .Values.starrocksFESpec.storageSpec.storageClassName }} storageClassName: {{ .Values.starrocksFESpec.storageSpec.storageClassName }} -{{- end }} -{{- if .Values.starrocksFESpec.storageSpec.storageSize }} storageSize: {{ .Values.starrocksFESpec.storageSpec.storageSize }} -{{- end }} mountPath: {{ template "starrockscluster.fe.meta.path" . }} -{{- if .Values.starrocksFESpec.storageSpec.logStorageSize }} - name: {{ .Values.starrocksFESpec.storageSpec.name }}{{ template "starrockscluster.fe.log.suffix" . }} -{{- if .Values.starrocksFESpec.storageSpec.storageClassName }} storageClassName: {{ .Values.starrocksFESpec.storageSpec.storageClassName }} -{{- end }} -{{- if .Values.starrocksFESpec.storageSpec.logStorageSize }} storageSize: {{ .Values.starrocksFESpec.storageSpec.logStorageSize }} -{{- end }} mountPath: {{ template "starrockscluster.fe.log.path" . }} -{{- end }} -{{- end }} + {{- end }} + {{- if .Values.starrocksFESpec.emptyDirs }} + {{- range .Values.starrocksFESpec.emptyDirs }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + storageClassName: "emptyDir" + {{- end }} + {{- end }} + {{- end }} {{- if .Values.starrocksCluster.enabledBe }} starRocksBeSpec: @@ -280,29 +279,27 @@ spec: configMapInfo: configMapName: {{template "starrockscluster.be.configmap.name" . }} resolveKey: be.conf -{{- if .Values.starrocksBeSpec.storageSpec.name }} + {{- if or .Values.starrocksBeSpec.storageSpec.name .Values.starrocksBeSpec.emptyDirs }} + {{- if .Values.starrocksBeSpec.storageSpec.name }} storageVolumes: - name: {{ .Values.starrocksBeSpec.storageSpec.name }}{{template "starrockscluster.be.data.suffix" . }} -{{- if .Values.starrocksBeSpec.storageSpec.storageClassName }} storageClassName: {{ .Values.starrocksBeSpec.storageSpec.storageClassName }} -{{- end }} -{{- if .Values.starrocksBeSpec.storageSpec.storageSize }} storageSize: {{ .Values.starrocksBeSpec.storageSpec.storageSize }} -{{- end }} mountPath: {{template "starrockscluster.be.data.path" . }} -{{- if .Values.starrocksBeSpec.storageSpec.logStorageSize }} - name: {{ .Values.starrocksBeSpec.storageSpec.name }}{{template "starrockscluster.be.log.suffix" . }} -{{- if .Values.starrocksBeSpec.storageSpec.storageClassName }} storageClassName: {{ .Values.starrocksBeSpec.storageSpec.storageClassName }} -{{- end }} -{{- if .Values.starrocksBeSpec.storageSpec.logStorageSize }} storageSize: {{ .Values.starrocksBeSpec.storageSpec.logStorageSize }} -{{- end }} mountPath: {{template "starrockscluster.be.log.path" . }} -{{- end }} -{{- end }} -{{- end }} - + {{- end }} + {{- if .Values.starrocksBeSpec.emptyDirs }} + {{- range .Values.starrocksBeSpec.emptyDirs }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + storageClassName: "emptyDir" + {{- end }} + {{- end }} + {{- end }} + {{- end }} {{- if .Values.starrocksCluster.enabledCn }} starRocksCnSpec: image: "{{ .Values.starrocksCnSpec.image.repository }}:{{ .Values.starrocksCnSpec.image.tag }}" @@ -432,8 +429,9 @@ spec: configMapInfo: configMapName: {{template "starrockscluster.cn.configmap.name" . }} resolveKey: cn.conf - {{- if .Values.starrocksCnSpec.storageSpec.name }} + {{- if or .Values.starrocksCnSpec.storageSpec.namer .Values.starrocksCnSpec.emptyDirs }} storageVolumes: + {{- if .Values.starrocksCnSpec.storageSpec.name }} - name: {{ .Values.starrocksCnSpec.storageSpec.name }}{{template "starrockscluster.cn.data.suffix" . }} storageClassName: {{ .Values.starrocksCnSpec.storageSpec.storageClassName }} storageSize: {{ .Values.starrocksCnSpec.storageSpec.storageSize }} @@ -442,10 +440,18 @@ spec: storageClassName: {{ .Values.starrocksCnSpec.storageSpec.storageClassName }} storageSize: {{ .Values.starrocksCnSpec.storageSpec.logStorageSize }} mountPath: {{template "starrockscluster.cn.log.path" . }} + {{- end }} + {{- if .Values.starrocksCnSpec.emptyDirs }} + {{- range .Values.starrocksCnSpec.emptyDirs }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + storageClassName: "emptyDir" + {{- end }} + {{- end }} + {{- end }} {{- end }} -{{- end }} -{{- if .Values.starrocksFeProxySpec.enabled }} + {{- if .Values.starrocksFeProxySpec.enabled }} starRocksFeProxySpec: {{- if .Values.starrocksFeProxySpec.image.repository }} image: "{{ .Values.starrocksFeProxySpec.image.repository }}:{{ .Values.starrocksFeProxySpec.image.tag }}" @@ -484,4 +490,12 @@ spec: {{- if .Values.starrocksFeProxySpec.readinessProbeFailureSeconds }} readinessProbeFailureSeconds: {{ .Values.starrocksFeProxySpec.readinessProbeFailureSeconds }} {{- end }} -{{- end }} + {{- if .Values.starrocksFeProxySpec.emptyDirs }} + storageVolumes: + {{- range .Values.starrocksFeProxySpec.emptyDirs }} + - name: {{ .name }} + storageClassName: "emptyDir" + mountPath: {{ .mountPath }} + {{- end }} + {{- end }} + {{- end }} diff --git a/helm-charts/charts/kube-starrocks/charts/starrocks/values.yaml b/helm-charts/charts/kube-starrocks/charts/starrocks/values.yaml index 46fc7585..a8229fd7 100644 --- a/helm-charts/charts/kube-starrocks/charts/starrocks/values.yaml +++ b/helm-charts/charts/kube-starrocks/charts/starrocks/values.yaml @@ -143,7 +143,7 @@ starrocksFESpec: limits: cpu: 8 memory: 8Gi - # fe storageSpec for persistent meta data. + # fe storageSpec for persistent metadata. storageSpec: name: "" # the storageClassName represent the used storageclass name. if not set will use k8s cluster default storageclass. @@ -154,6 +154,12 @@ starrocksFESpec: storageSize: 10Gi # Setting this parameter can persist log storage logStorageSize: 5Gi + # mount other volumes if necessary. + # Note: please use storageSpec field for persistent metadata and log. + emptyDirs: [] + # e.g. mount an emptyDir volume to /tmp + # - name: tmp-data + # mountPath: /tmp # the config for start fe. the base information as follows. config: | LOG_DIR = ${STARROCKS_HOME}/log @@ -334,6 +340,12 @@ starrocksCnSpec: storageSize: 1Ti # the storage size of persistent volume for log. logStorageSize: 1Gi + # mount other volumes if necessary. + # Note: please use storageSpec field for persistent storage data and log. + emptyDirs: [] + # e.g. mount an emptyDir volume to /tmp + # - name: tmp-data + # mountPath: /tmp # the config start for cn, the base information as follows. config: | sys_log_level = INFO @@ -473,6 +485,12 @@ starrocksBeSpec: storageSize: 1Ti # Setting this parameter can persist log storage logStorageSize: 1Gi + # mount other volumes if necessary. + # Note: please use storageSpec field for persistent storage data and log. + emptyDirs: [] + # e.g. mount an emptyDir volume to /tmp + # - name: tmp-data + # mountPath: /tmp # the config for start be. the base information as follows. config: | be_port = 9060 @@ -595,3 +613,8 @@ starrocksFeProxySpec: # ReadinessProbeFailureSeconds defines the total failure seconds of readiness Probe. # default value is 15 seconds readinessProbeFailureSeconds: + # Note: will create emptyDir volume for fe proxy, PVC is not supported. + emptyDirs: [] + # e.g. mount an emptyDir volume to /tmp + # - name: tmp-data + # mountPath: /tmp diff --git a/helm-charts/charts/kube-starrocks/values.yaml b/helm-charts/charts/kube-starrocks/values.yaml index 52227690..e449145c 100644 --- a/helm-charts/charts/kube-starrocks/values.yaml +++ b/helm-charts/charts/kube-starrocks/values.yaml @@ -223,7 +223,7 @@ starrocks: limits: cpu: 8 memory: 8Gi - # fe storageSpec for persistent meta data. + # fe storageSpec for persistent metadata. storageSpec: name: "" # the storageClassName represent the used storageclass name. if not set will use k8s cluster default storageclass. @@ -234,6 +234,12 @@ starrocks: storageSize: 10Gi # Setting this parameter can persist log storage logStorageSize: 5Gi + # mount other volumes if necessary. + # Note: please use storageSpec field for persistent metadata and log. + emptyDirs: [] + # e.g. mount an emptyDir volume to /tmp + # - name: tmp-data + # mountPath: /tmp # the config for start fe. the base information as follows. config: | LOG_DIR = ${STARROCKS_HOME}/log @@ -414,6 +420,12 @@ starrocks: storageSize: 1Ti # the storage size of persistent volume for log. logStorageSize: 1Gi + # mount other volumes if necessary. + # Note: please use storageSpec field for persistent storage data and log. + emptyDirs: [] + # e.g. mount an emptyDir volume to /tmp + # - name: tmp-data + # mountPath: /tmp # the config start for cn, the base information as follows. config: | sys_log_level = INFO @@ -553,6 +565,12 @@ starrocks: storageSize: 1Ti # Setting this parameter can persist log storage logStorageSize: 1Gi + # mount other volumes if necessary. + # Note: please use storageSpec field for persistent storage data and log. + emptyDirs: [] + # e.g. mount an emptyDir volume to /tmp + # - name: tmp-data + # mountPath: /tmp # the config for start be. the base information as follows. config: | be_port = 9060 @@ -675,3 +693,8 @@ starrocks: # ReadinessProbeFailureSeconds defines the total failure seconds of readiness Probe. # default value is 15 seconds readinessProbeFailureSeconds: + # Note: will create emptyDir volume for fe proxy, PVC is not supported. + emptyDirs: [] + # e.g. mount an emptyDir volume to /tmp + # - name: tmp-data + # mountPath: /tmp diff --git a/pkg/apis/starrocks/v1/starrockscluster_types.go b/pkg/apis/starrocks/v1/starrockscluster_types.go index 75eae00a..a565b415 100644 --- a/pkg/apis/starrocks/v1/starrockscluster_types.go +++ b/pkg/apis/starrocks/v1/starrockscluster_types.go @@ -274,10 +274,11 @@ type StorageVolume struct { // StorageSize is a valid memory size type based on powers-of-2, so 1Mi is 1024Ki. // Supported units:Mi, Gi, GiB, Ti, Ti, Pi, Ei, Ex: `512Mi`. // +kubebuilder:validation:Pattern:="(^0|([0-9]*l[.])?[0-9]+((M|G|T|E|P)i))$" - StorageSize string `json:"storageSize"` + // +optional + StorageSize string `json:"storageSize,omitempty"` // MountPath specify the path of volume mount. - MountPath string `json:"mountPath,omitempty"` + MountPath string `json:"mountPath"` // SubPath within the volume from which the container's volume should be mounted. // Defaults to "" (volume's root). diff --git a/pkg/common/common.go b/pkg/common/common.go new file mode 100644 index 00000000..a02ebd99 --- /dev/null +++ b/pkg/common/common.go @@ -0,0 +1,9 @@ +package common + +import "strings" + +func EqualsIgnoreCase(a, b string) bool { + a = strings.ToLower(a) + b = strings.ToLower(b) + return a == b +} diff --git a/pkg/k8sutils/templates/pod/mount.go b/pkg/k8sutils/templates/pod/mount.go new file mode 100644 index 00000000..fb589685 --- /dev/null +++ b/pkg/k8sutils/templates/pod/mount.go @@ -0,0 +1,145 @@ +package pod + +import ( + corev1 "k8s.io/api/core/v1" + + v1 "github.com/StarRocks/starrocks-kubernetes-operator/pkg/apis/starrocks/v1" + "github.com/StarRocks/starrocks-kubernetes-operator/pkg/common" + "github.com/StarRocks/starrocks-kubernetes-operator/pkg/common/hash" +) + +const ( + EmptyDir = "emptyDir" +) + +func IsSpecialStorageClass(storageClassName *string) bool { + return storageClassName != nil && (common.EqualsIgnoreCase(*storageClassName, EmptyDir)) +} + +// MountStorageVolumes parse StorageVolumes from spec and mount them to pod. +// If StorageClassName is EmptyDir, mount an emptyDir volume to pod. +func MountStorageVolumes(spec v1.SpecInterface) ([]corev1.Volume, []corev1.VolumeMount, map[string]bool) { + var volumes []corev1.Volume + var volumeMounts []corev1.VolumeMount + vexist := make(map[string]bool) + for _, sv := range spec.GetStorageVolumes() { + vexist[sv.MountPath] = true + if IsSpecialStorageClass(sv.StorageClassName) { + volumes, volumeMounts = MountEmptyDirVolume(volumes, volumeMounts, sv.Name, sv.MountPath, sv.SubPath) + } else { + volumes, volumeMounts = MountPersistentVolumeClaim(volumes, volumeMounts, sv.Name, sv.MountPath, sv.SubPath) + } + } + return volumes, volumeMounts, vexist +} + +func MountPersistentVolumeClaim(volumes []corev1.Volume, volumeMounts []corev1.VolumeMount, + volumeName, mountPath, subPath string) ([]corev1.Volume, []corev1.VolumeMount) { + volumes = append(volumes, corev1.Volume{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: volumeName, + }, + }, + }) + volumeMounts = append(volumeMounts, corev1.VolumeMount{ + Name: volumeName, + MountPath: mountPath, + SubPath: subPath, + }) + return volumes, volumeMounts +} + +func MountEmptyDirVolume(volumes []corev1.Volume, volumeMounts []corev1.VolumeMount, + volumeName, mountPath, subPath string) ([]corev1.Volume, []corev1.VolumeMount) { + volumes = append(volumes, corev1.Volume{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }) + volumeMounts = append( + volumeMounts, corev1.VolumeMount{ + Name: volumeName, + MountPath: mountPath, + SubPath: subPath, + }) + return volumes, volumeMounts +} + +func MountConfigMaps(volumes []corev1.Volume, volumeMounts []corev1.VolumeMount, + references []v1.ConfigMapReference) ([]corev1.Volume, []corev1.VolumeMount) { + for _, reference := range references { + volumeName := getVolumeName(v1.MountInfo(reference)) + volumes = append(volumes, corev1.Volume{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: reference.Name, + }, + }, + }, + }) + volumeMounts = append(volumeMounts, corev1.VolumeMount{ + Name: volumeName, + MountPath: reference.MountPath, + SubPath: reference.SubPath, + }) + } + return volumes, volumeMounts +} + +// MountConfigMapInfo parse ConfigMapInfo from spec and mount them to pod. +// Note: we can not reuse MountConfigMaps because it generates a volume name by call getVolumeName, +func MountConfigMapInfo(volumes []corev1.Volume, volumeMounts []corev1.VolumeMount, + cmInfo v1.ConfigMapInfo, mountPath string) ([]corev1.Volume, []corev1.VolumeMount) { + if cmInfo.ConfigMapName != "" && cmInfo.ResolveKey != "" { + volumes = append(volumes, corev1.Volume{ + Name: cmInfo.ConfigMapName, + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: cmInfo.ConfigMapName, + }, + }, + }, + }) + volumeMounts = append(volumeMounts, corev1.VolumeMount{ + Name: cmInfo.ConfigMapName, + MountPath: mountPath, + }) + } + return volumes, volumeMounts +} + +func MountSecrets(volumes []corev1.Volume, volumeMounts []corev1.VolumeMount, + references []v1.SecretReference) ([]corev1.Volume, []corev1.VolumeMount) { + for _, reference := range references { + volumeName := getVolumeName(v1.MountInfo(reference)) + volumes = append(volumes, corev1.Volume{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: reference.Name, + }, + }, + }) + volumeMounts = append(volumeMounts, corev1.VolumeMount{ + Name: volumeName, + MountPath: reference.MountPath, + SubPath: reference.SubPath, + }) + } + return volumes, volumeMounts +} + +func getVolumeName(mountInfo v1.MountInfo) string { + suffixLen := 4 + suffix := hash.HashObject(mountInfo) + if len(suffix) > suffixLen { + suffix = suffix[:suffixLen] + } + return mountInfo.Name + "-" + suffix +} diff --git a/pkg/k8sutils/templates/pod/mount_test.go b/pkg/k8sutils/templates/pod/mount_test.go new file mode 100644 index 00000000..cff4621d --- /dev/null +++ b/pkg/k8sutils/templates/pod/mount_test.go @@ -0,0 +1,298 @@ +package pod + +import ( + "reflect" + "testing" + + v1 "github.com/StarRocks/starrocks-kubernetes-operator/pkg/apis/starrocks/v1" + corev1 "k8s.io/api/core/v1" +) + +func TestMountConfigMapInfo(t *testing.T) { + type args struct { + volumes []corev1.Volume + volumeMounts []corev1.VolumeMount + cmInfo v1.ConfigMapInfo + mountPath string + } + tests := []struct { + name string + args args + want []corev1.Volume + want1 []corev1.VolumeMount + }{ + { + name: "test mount configmap", + args: args{ + cmInfo: v1.ConfigMapInfo{ConfigMapName: "cm", ResolveKey: "key"}, + mountPath: "/pkg/mounts/volume", + }, + want: []corev1.Volume{ + { + Name: "cm", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "cm", + }, + }, + }, + }, + }, + want1: []corev1.VolumeMount{ + { + Name: "cm", + MountPath: "/pkg/mounts/volume", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, got1 := MountConfigMapInfo(tt.args.volumes, tt.args.volumeMounts, tt.args.cmInfo, tt.args.mountPath) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("MountConfigMapInfo() got = %v, want %v", got, tt.want) + } + if !reflect.DeepEqual(got1, tt.want1) { + t.Errorf("MountConfigMapInfo() got1 = %v, want %v", got1, tt.want1) + } + }) + } +} + +func TestMountSecrets(t *testing.T) { + type args struct { + volumes []corev1.Volume + volumeMounts []corev1.VolumeMount + secrets []v1.SecretReference + } + tests := []struct { + name string + args args + want []corev1.Volume + want1 []corev1.VolumeMount + }{ + { + name: "test mount configmaps", + args: args{ + secrets: []v1.SecretReference{ + { + Name: "s1", + MountPath: "/pkg/mounts/volumes1", + }, + { + Name: "s2", + MountPath: "/pkg/mounts/volumes2", + }, + }, + }, + want: []corev1.Volume{ + { + Name: "s1-1614", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "s1", + }, + }, + }, + { + Name: "s2-1229", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "s2", + }, + }, + }, + }, + want1: []corev1.VolumeMount{ + { + Name: "s1-1614", + MountPath: "/pkg/mounts/volumes1", + }, + { + Name: "s2-1229", + MountPath: "/pkg/mounts/volumes2", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, got1 := MountSecrets(tt.args.volumes, tt.args.volumeMounts, tt.args.secrets) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("MountSecrets() got = %v, want %v", got, tt.want) + } + if !reflect.DeepEqual(got1, tt.want1) { + t.Errorf("MountSecrets() got1 = %v, want %v", got1, tt.want1) + } + }) + } +} + +func TestMountConfigMaps(t *testing.T) { + type args struct { + volumes []corev1.Volume + volumeMounts []corev1.VolumeMount + configmaps []v1.ConfigMapReference + } + tests := []struct { + name string + args args + want []corev1.Volume + want1 []corev1.VolumeMount + }{ + { + name: "test mount configmaps", + args: args{ + configmaps: []v1.ConfigMapReference{ + { + Name: "s1", + MountPath: "/pkg/mounts/volumes1", + }, + { + Name: "s2", + MountPath: "/pkg/mounts/volumes2", + }, + }, + }, + want: []corev1.Volume{ + { + Name: "s1-1614", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "s1", + }, + }, + }, + }, + { + Name: "s2-1229", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "s2", + }, + }, + }, + }, + }, + want1: []corev1.VolumeMount{ + { + Name: "s1-1614", + MountPath: "/pkg/mounts/volumes1", + }, + { + Name: "s2-1229", + MountPath: "/pkg/mounts/volumes2", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, got1 := MountConfigMaps(tt.args.volumes, tt.args.volumeMounts, tt.args.configmaps) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("MountSecrets() got = %v, want %v", got, tt.want) + } + if !reflect.DeepEqual(got1, tt.want1) { + t.Errorf("MountSecrets() got1 = %v, want %v", got1, tt.want1) + } + }) + } +} + +func TestMountStorageVolumes(t *testing.T) { + type args struct { + spec v1.SpecInterface + } + tests := []struct { + name string + args args + want []corev1.Volume + want1 []corev1.VolumeMount + want2 map[string]bool + }{ + { + name: "test mount storage volumes", + args: args{ + spec: &v1.StarRocksFeSpec{ + StarRocksComponentSpec: v1.StarRocksComponentSpec{ + StarRocksLoadSpec: v1.StarRocksLoadSpec{ + StorageVolumes: []v1.StorageVolume{ + { + Name: "s1", + MountPath: "/pkg/mounts/volumes1", + StorageClassName: func() *string { s := "sc1"; return &s }(), + StorageSize: "1GB", + }, + }, + }, + }, + }, + }, + want: []corev1.Volume{ + { + Name: "s1", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: "s1", + }, + }, + }, + }, + want1: []corev1.VolumeMount{ + { + Name: "s1", + MountPath: "/pkg/mounts/volumes1", + }, + }, + want2: map[string]bool{ + "/pkg/mounts/volumes1": true, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, got1, got2 := MountStorageVolumes(tt.args.spec) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("MountStorageVolumes() got = %v, want %v", got, tt.want) + } + if !reflect.DeepEqual(got1, tt.want1) { + t.Errorf("MountStorageVolumes() got1 = %v, want %v", got1, tt.want1) + } + if !reflect.DeepEqual(got2, tt.want2) { + t.Errorf("MountStorageVolumes() got2 = %v, want %v", got2, tt.want2) + } + }) + } +} + +func Test_getVolumeName(t *testing.T) { + type args struct { + mountInfo v1.MountInfo + } + tests := []struct { + name string + args args + want string + }{ + { + name: "test get volume name", + args: args{ + mountInfo: v1.MountInfo{ + Name: "test", + MountPath: "/my/path", + }, + }, + want: "test-1417", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getVolumeName(tt.args.mountInfo); got != tt.want { + t.Errorf("getVolumeName() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/k8sutils/templates/pod/probe.go b/pkg/k8sutils/templates/pod/probe.go new file mode 100644 index 00000000..ed47d1f5 --- /dev/null +++ b/pkg/k8sutils/templates/pod/probe.go @@ -0,0 +1,52 @@ +package pod + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +// StartupProbe returns a startup probe. +func StartupProbe(startupProbeFailureSeconds *int32, port int32, path string) *corev1.Probe { + var defaultFailureThreshold int32 = 60 + var defaultPeriodSeconds int32 = 5 + return completeProbe(startupProbeFailureSeconds, defaultFailureThreshold, defaultPeriodSeconds, getProbe(port, path)) +} + +// LivenessProbe returns a liveness probe. +func LivenessProbe(livenessProbeFailureSeconds *int32, port int32, path string) *corev1.Probe { + var defaultFailureThreshold int32 = 3 + var defaultPeriodSeconds int32 = 5 + return completeProbe(livenessProbeFailureSeconds, defaultFailureThreshold, defaultPeriodSeconds, getProbe(port, path)) +} + +// ReadinessProbe returns a readiness probe. +func ReadinessProbe(readinessProbeFailureSeconds *int32, port int32, path string) *corev1.Probe { + var defaultFailureThreshold int32 = 3 + var defaultPeriodSeconds int32 = 5 + return completeProbe(readinessProbeFailureSeconds, defaultFailureThreshold, defaultPeriodSeconds, getProbe(port, path)) +} + +func completeProbe(failureSeconds *int32, defaultFailureThreshold int32, defaultPeriodSeconds int32, + probeHandler corev1.ProbeHandler) *corev1.Probe { + probe := &corev1.Probe{} + if failureSeconds != nil && *failureSeconds > 0 { + probe.FailureThreshold = (*failureSeconds + defaultPeriodSeconds - 1) / defaultPeriodSeconds + } else { + probe.FailureThreshold = defaultFailureThreshold + } + probe.PeriodSeconds = defaultPeriodSeconds + probe.ProbeHandler = probeHandler + return probe +} + +func getProbe(port int32, path string) corev1.ProbeHandler { + return corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: path, + Port: intstr.IntOrString{ + Type: intstr.Int, + IntVal: port, + }, + }, + } +} diff --git a/pkg/k8sutils/templates/pod/probe_test.go b/pkg/k8sutils/templates/pod/probe_test.go new file mode 100644 index 00000000..182982ca --- /dev/null +++ b/pkg/k8sutils/templates/pod/probe_test.go @@ -0,0 +1,248 @@ +package pod + +import ( + "reflect" + "testing" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +func TestMakeLivenessProbe(t *testing.T) { + type args struct { + seconds *int32 + port int32 + path string + } + tests := []struct { + name string + args args + want *corev1.Probe + }{ + { + name: "liveness probe with default seconds", + args: args{ + port: 8080, + path: "/api/health2", + }, + want: &corev1.Probe{ + PeriodSeconds: 5, + FailureThreshold: 3, + ProbeHandler: getProbe(8080, "/api/health2"), + }, + }, + { + name: "liveness probe with specified seconds", + args: args{ + seconds: func() *int32 { s := int32(50); return &s }(), + port: 8080, + path: "/api/health2", + }, + want: &corev1.Probe{ + PeriodSeconds: 5, + FailureThreshold: 10, + ProbeHandler: getProbe(8080, "/api/health2"), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := LivenessProbe(tt.args.seconds, tt.args.port, tt.args.path); !reflect.DeepEqual(got, tt.want) { + t.Errorf("LivenessProbe() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMakeReadinessProbe(t *testing.T) { + type args struct { + seconds *int32 + port int32 + path string + } + tests := []struct { + name string + args args + want *corev1.Probe + }{ + { + name: "readiness probe with default seconds", + args: args{ + port: 8080, + path: "/api/health2", + }, + want: &corev1.Probe{ + PeriodSeconds: 5, + FailureThreshold: 3, + ProbeHandler: getProbe(8080, "/api/health2"), + }, + }, + { + name: "readiness probe with specified seconds", + args: args{ + seconds: func() *int32 { s := int32(50); return &s }(), + port: 8080, + path: "/api/health2", + }, + want: &corev1.Probe{ + PeriodSeconds: 5, + FailureThreshold: 10, + ProbeHandler: getProbe(8080, "/api/health2"), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ReadinessProbe(tt.args.seconds, tt.args.port, tt.args.path); !reflect.DeepEqual(got, tt.want) { + t.Errorf("ReadinessProbe() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMakeStartupProbe(t *testing.T) { + type args struct { + port int32 + path string + } + tests := []struct { + name string + args args + want *corev1.Probe + }{ + { + name: "test", + args: args{ + port: 8080, + path: "/api/health2", + }, + want: &corev1.Probe{ + FailureThreshold: 60, + PeriodSeconds: 5, + ProbeHandler: getProbe(8080, "/api/health2"), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := StartupProbe(nil, tt.args.port, tt.args.path); !reflect.DeepEqual(got, tt.want) { + t.Errorf("StartupProbe() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_makeProbeHandler(t *testing.T) { + type args struct { + port int32 + path string + } + tests := []struct { + name string + args args + want corev1.ProbeHandler + }{ + { + name: "test", + args: args{ + port: 8080, + path: "/api/health2", + }, + want: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/api/health2", + Port: intstr.IntOrString{ + Type: intstr.Int, + IntVal: 8080, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getProbe(tt.args.port, tt.args.path); !reflect.DeepEqual(got, tt.want) { + t.Errorf("getProbe() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_completeProbe(t *testing.T) { + type args struct { + originalProbe *int32 + defaultFailureThreshold int32 + defaultPeriodSeconds int32 + probeHandler corev1.ProbeHandler + } + tests := []struct { + name string + args args + want *corev1.Probe + }{ + { + name: "test complete probe", + args: args{ + originalProbe: nil, + defaultFailureThreshold: 1, + defaultPeriodSeconds: 1, + probeHandler: corev1.ProbeHandler{}, + }, + want: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{}, + FailureThreshold: 1, + PeriodSeconds: 1, + }, + }, + { + name: "test complete probe 2", + args: args{ + originalProbe: func() *int32 { v := int32(10); return &v }(), + defaultFailureThreshold: 1, + defaultPeriodSeconds: 5, + probeHandler: corev1.ProbeHandler{}, + }, + want: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{}, + FailureThreshold: 2, + PeriodSeconds: 5, + }, + }, + { + name: "test complete probe 3", + args: args{ + originalProbe: func() *int32 { v := int32(0); return &v }(), + defaultFailureThreshold: 60, + defaultPeriodSeconds: 5, + probeHandler: corev1.ProbeHandler{}, + }, + want: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{}, + FailureThreshold: 60, + PeriodSeconds: 5, + }, + }, + { + name: "test complete probe 4", + args: args{ + originalProbe: func() *int32 { v := int32(1); return &v }(), + defaultFailureThreshold: 60, + defaultPeriodSeconds: 5, + probeHandler: corev1.ProbeHandler{}, + }, + want: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{}, + FailureThreshold: 1, + PeriodSeconds: 5, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := completeProbe(tt.args.originalProbe, tt.args.defaultFailureThreshold, + tt.args.defaultPeriodSeconds, tt.args.probeHandler); !reflect.DeepEqual(got, tt.want) { + t.Errorf("completeProbe() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/k8sutils/templates/pod/spec.go b/pkg/k8sutils/templates/pod/spec.go index 5585bba4..8a370972 100644 --- a/pkg/k8sutils/templates/pod/spec.go +++ b/pkg/k8sutils/templates/pod/spec.go @@ -20,10 +20,8 @@ import ( "strings" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/intstr" v1 "github.com/StarRocks/starrocks-kubernetes-operator/pkg/apis/starrocks/v1" - "github.com/StarRocks/starrocks-kubernetes-operator/pkg/common/hash" rutils "github.com/StarRocks/starrocks-kubernetes-operator/pkg/common/resource_utils" "github.com/StarRocks/starrocks-kubernetes-operator/pkg/k8sutils/load" ) @@ -32,27 +30,6 @@ const ( HEALTH_API_PATH = "/api/health" ) -// StartupProbe returns a startup probe. -func StartupProbe(startupProbeFailureSeconds *int32, port int32, path string) *corev1.Probe { - var defaultFailureThreshold int32 = 60 - var defaultPeriodSeconds int32 = 5 - return completeProbe(startupProbeFailureSeconds, defaultFailureThreshold, defaultPeriodSeconds, getProbe(port, path)) -} - -// LivenessProbe returns a liveness probe. -func LivenessProbe(livenessProbeFailureSeconds *int32, port int32, path string) *corev1.Probe { - var defaultFailureThreshold int32 = 3 - var defaultPeriodSeconds int32 = 5 - return completeProbe(livenessProbeFailureSeconds, defaultFailureThreshold, defaultPeriodSeconds, getProbe(port, path)) -} - -// ReadinessProbe returns a readiness probe. -func ReadinessProbe(readinessProbeFailureSeconds *int32, port int32, path string) *corev1.Probe { - var defaultFailureThreshold int32 = 3 - var defaultPeriodSeconds int32 = 5 - return completeProbe(readinessProbeFailureSeconds, defaultFailureThreshold, defaultPeriodSeconds, getProbe(port, path)) -} - // LifeCycle returns a lifecycle. func LifeCycle(preStopScriptPath string) *corev1.Lifecycle { return &corev1.Lifecycle{ @@ -64,131 +41,6 @@ func LifeCycle(preStopScriptPath string) *corev1.Lifecycle { } } -func getProbe(port int32, path string) corev1.ProbeHandler { - return corev1.ProbeHandler{ - HTTPGet: &corev1.HTTPGetAction{ - Path: path, - Port: intstr.IntOrString{ - Type: intstr.Int, - IntVal: port, - }, - }, - } -} - -func completeProbe(failureSeconds *int32, defaultFailureThreshold int32, defaultPeriodSeconds int32, - probeHandler corev1.ProbeHandler) *corev1.Probe { - probe := &corev1.Probe{} - if failureSeconds != nil && *failureSeconds > 0 { - probe.FailureThreshold = (*failureSeconds + defaultPeriodSeconds - 1) / defaultPeriodSeconds - } else { - probe.FailureThreshold = defaultFailureThreshold - } - probe.PeriodSeconds = defaultPeriodSeconds - probe.ProbeHandler = probeHandler - return probe -} - -func getVolumeName(mountInfo v1.MountInfo) string { - suffixLen := 4 - suffix := hash.HashObject(mountInfo) - if len(suffix) > suffixLen { - suffix = suffix[:suffixLen] - } - return mountInfo.Name + "-" + suffix -} - -func MountConfigMaps(volumes []corev1.Volume, volumeMounts []corev1.VolumeMount, - references []v1.ConfigMapReference) ([]corev1.Volume, []corev1.VolumeMount) { - for _, reference := range references { - volumeName := getVolumeName(v1.MountInfo(reference)) - volumes = append(volumes, corev1.Volume{ - Name: volumeName, - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: reference.Name, - }, - }, - }, - }) - volumeMounts = append(volumeMounts, corev1.VolumeMount{ - Name: volumeName, - MountPath: reference.MountPath, - SubPath: reference.SubPath, - }) - } - return volumes, volumeMounts -} - -func MountSecrets(volumes []corev1.Volume, volumeMounts []corev1.VolumeMount, - references []v1.SecretReference) ([]corev1.Volume, []corev1.VolumeMount) { - for _, reference := range references { - volumeName := getVolumeName(v1.MountInfo(reference)) - volumes = append(volumes, corev1.Volume{ - Name: volumeName, - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: reference.Name, - }, - }, - }) - volumeMounts = append(volumeMounts, corev1.VolumeMount{ - Name: volumeName, - MountPath: reference.MountPath, - SubPath: reference.SubPath, - }) - } - return volumes, volumeMounts -} - -func MountStorageVolumes(spec v1.SpecInterface) ([]corev1.Volume, []corev1.VolumeMount, map[string]bool) { - var volumes []corev1.Volume - var volumeMounts []corev1.VolumeMount - vexist := make(map[string]bool) - for _, sv := range spec.GetStorageVolumes() { - // do not use getVolumeName for backward compatibility - vexist[sv.MountPath] = true - volumeMounts = append(volumeMounts, corev1.VolumeMount{ - Name: sv.Name, - MountPath: sv.MountPath, - SubPath: sv.SubPath, - }) - - volumes = append(volumes, corev1.Volume{ - Name: sv.Name, - VolumeSource: corev1.VolumeSource{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ - ClaimName: sv.Name, - }, - }, - }) - } - return volumes, volumeMounts, vexist -} - -func MountConfigMapInfo(volumes []corev1.Volume, volumeMounts []corev1.VolumeMount, - cmInfo v1.ConfigMapInfo, mountPath string) ([]corev1.Volume, []corev1.VolumeMount) { - if cmInfo.ConfigMapName != "" && cmInfo.ResolveKey != "" { - // do not use getVolumeName for backward compatibility - volumes = append(volumes, corev1.Volume{ - Name: cmInfo.ConfigMapName, - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: cmInfo.ConfigMapName, - }, - }, - }, - }) - volumeMounts = append(volumeMounts, corev1.VolumeMount{ - Name: cmInfo.ConfigMapName, - MountPath: mountPath, - }) - } - return volumes, volumeMounts -} - func Labels(clusterName string, spec v1.SpecInterface) map[string]string { labels := load.Selector(clusterName, spec) switch v := spec.(type) { diff --git a/pkg/k8sutils/templates/pod/spec_test.go b/pkg/k8sutils/templates/pod/spec_test.go index ec5b5900..4c368a92 100644 --- a/pkg/k8sutils/templates/pod/spec_test.go +++ b/pkg/k8sutils/templates/pod/spec_test.go @@ -21,137 +21,12 @@ import ( "testing" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/intstr" v1 "github.com/StarRocks/starrocks-kubernetes-operator/pkg/apis/starrocks/v1" rutils "github.com/StarRocks/starrocks-kubernetes-operator/pkg/common/resource_utils" "github.com/StarRocks/starrocks-kubernetes-operator/pkg/k8sutils/templates/service" ) -func TestMakeLivenessProbe(t *testing.T) { - type args struct { - seconds *int32 - port int32 - path string - } - tests := []struct { - name string - args args - want *corev1.Probe - }{ - { - name: "liveness probe with default seconds", - args: args{ - port: 8080, - path: "/api/health2", - }, - want: &corev1.Probe{ - PeriodSeconds: 5, - FailureThreshold: 3, - ProbeHandler: getProbe(8080, "/api/health2"), - }, - }, - { - name: "liveness probe with specified seconds", - args: args{ - seconds: func() *int32 { s := int32(50); return &s }(), - port: 8080, - path: "/api/health2", - }, - want: &corev1.Probe{ - PeriodSeconds: 5, - FailureThreshold: 10, - ProbeHandler: getProbe(8080, "/api/health2"), - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := LivenessProbe(tt.args.seconds, tt.args.port, tt.args.path); !reflect.DeepEqual(got, tt.want) { - t.Errorf("LivenessProbe() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestMakeReadinessProbe(t *testing.T) { - type args struct { - seconds *int32 - port int32 - path string - } - tests := []struct { - name string - args args - want *corev1.Probe - }{ - { - name: "readiness probe with default seconds", - args: args{ - port: 8080, - path: "/api/health2", - }, - want: &corev1.Probe{ - PeriodSeconds: 5, - FailureThreshold: 3, - ProbeHandler: getProbe(8080, "/api/health2"), - }, - }, - { - name: "readiness probe with specified seconds", - args: args{ - seconds: func() *int32 { s := int32(50); return &s }(), - port: 8080, - path: "/api/health2", - }, - want: &corev1.Probe{ - PeriodSeconds: 5, - FailureThreshold: 10, - ProbeHandler: getProbe(8080, "/api/health2"), - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := ReadinessProbe(tt.args.seconds, tt.args.port, tt.args.path); !reflect.DeepEqual(got, tt.want) { - t.Errorf("ReadinessProbe() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestMakeStartupProbe(t *testing.T) { - type args struct { - port int32 - path string - } - tests := []struct { - name string - args args - want *corev1.Probe - }{ - { - name: "test", - args: args{ - port: 8080, - path: "/api/health2", - }, - want: &corev1.Probe{ - FailureThreshold: 60, - PeriodSeconds: 5, - ProbeHandler: getProbe(8080, "/api/health2"), - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := StartupProbe(nil, tt.args.port, tt.args.path); !reflect.DeepEqual(got, tt.want) { - t.Errorf("StartupProbe() = %v, want %v", got, tt.want) - } - }) - } -} - func TestMakeLifeCycle(t *testing.T) { type args struct { preStopScriptPath string @@ -185,302 +60,6 @@ func TestMakeLifeCycle(t *testing.T) { } } -func Test_makeProbeHandler(t *testing.T) { - type args struct { - port int32 - path string - } - tests := []struct { - name string - args args - want corev1.ProbeHandler - }{ - { - name: "test", - args: args{ - port: 8080, - path: "/api/health2", - }, - want: corev1.ProbeHandler{ - HTTPGet: &corev1.HTTPGetAction{ - Path: "/api/health2", - Port: intstr.IntOrString{ - Type: intstr.Int, - IntVal: 8080, - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := getProbe(tt.args.port, tt.args.path); !reflect.DeepEqual(got, tt.want) { - t.Errorf("getProbe() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestMountConfigMapInfo(t *testing.T) { - type args struct { - volumes []corev1.Volume - volumeMounts []corev1.VolumeMount - cmInfo v1.ConfigMapInfo - mountPath string - } - tests := []struct { - name string - args args - want []corev1.Volume - want1 []corev1.VolumeMount - }{ - { - name: "test mount configmap", - args: args{ - cmInfo: v1.ConfigMapInfo{ConfigMapName: "cm", ResolveKey: "key"}, - mountPath: "/pkg/mounts/volume", - }, - want: []corev1.Volume{ - { - Name: "cm", - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "cm", - }, - }, - }, - }, - }, - want1: []corev1.VolumeMount{ - { - Name: "cm", - MountPath: "/pkg/mounts/volume", - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, got1 := MountConfigMapInfo(tt.args.volumes, tt.args.volumeMounts, tt.args.cmInfo, tt.args.mountPath) - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("MountConfigMapInfo() got = %v, want %v", got, tt.want) - } - if !reflect.DeepEqual(got1, tt.want1) { - t.Errorf("MountConfigMapInfo() got1 = %v, want %v", got1, tt.want1) - } - }) - } -} - -func TestMountSecrets(t *testing.T) { - type args struct { - volumes []corev1.Volume - volumeMounts []corev1.VolumeMount - secrets []v1.SecretReference - } - tests := []struct { - name string - args args - want []corev1.Volume - want1 []corev1.VolumeMount - }{ - { - name: "test mount configmaps", - args: args{ - secrets: []v1.SecretReference{ - { - Name: "s1", - MountPath: "/pkg/mounts/volumes1", - }, - { - Name: "s2", - MountPath: "/pkg/mounts/volumes2", - }, - }, - }, - want: []corev1.Volume{ - { - Name: "s1-1614", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: "s1", - }, - }, - }, - { - Name: "s2-1229", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: "s2", - }, - }, - }, - }, - want1: []corev1.VolumeMount{ - { - Name: "s1-1614", - MountPath: "/pkg/mounts/volumes1", - }, - { - Name: "s2-1229", - MountPath: "/pkg/mounts/volumes2", - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, got1 := MountSecrets(tt.args.volumes, tt.args.volumeMounts, tt.args.secrets) - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("MountSecrets() got = %v, want %v", got, tt.want) - } - if !reflect.DeepEqual(got1, tt.want1) { - t.Errorf("MountSecrets() got1 = %v, want %v", got1, tt.want1) - } - }) - } -} - -func TestMountConfigMaps(t *testing.T) { - type args struct { - volumes []corev1.Volume - volumeMounts []corev1.VolumeMount - configmaps []v1.ConfigMapReference - } - tests := []struct { - name string - args args - want []corev1.Volume - want1 []corev1.VolumeMount - }{ - { - name: "test mount configmaps", - args: args{ - configmaps: []v1.ConfigMapReference{ - { - Name: "s1", - MountPath: "/pkg/mounts/volumes1", - }, - { - Name: "s2", - MountPath: "/pkg/mounts/volumes2", - }, - }, - }, - want: []corev1.Volume{ - { - Name: "s1-1614", - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "s1", - }, - }, - }, - }, - { - Name: "s2-1229", - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "s2", - }, - }, - }, - }, - }, - want1: []corev1.VolumeMount{ - { - Name: "s1-1614", - MountPath: "/pkg/mounts/volumes1", - }, - { - Name: "s2-1229", - MountPath: "/pkg/mounts/volumes2", - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, got1 := MountConfigMaps(tt.args.volumes, tt.args.volumeMounts, tt.args.configmaps) - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("MountSecrets() got = %v, want %v", got, tt.want) - } - if !reflect.DeepEqual(got1, tt.want1) { - t.Errorf("MountSecrets() got1 = %v, want %v", got1, tt.want1) - } - }) - } -} - -func TestMountStorageVolumes(t *testing.T) { - type args struct { - spec v1.SpecInterface - } - tests := []struct { - name string - args args - want []corev1.Volume - want1 []corev1.VolumeMount - want2 map[string]bool - }{ - { - name: "test mount storage volumes", - args: args{ - spec: &v1.StarRocksFeSpec{ - StarRocksComponentSpec: v1.StarRocksComponentSpec{ - StarRocksLoadSpec: v1.StarRocksLoadSpec{ - StorageVolumes: []v1.StorageVolume{ - { - Name: "s1", - MountPath: "/pkg/mounts/volumes1", - StorageClassName: func() *string { s := "sc1"; return &s }(), - StorageSize: "1GB", - }, - }, - }, - }, - }, - }, - want: []corev1.Volume{ - { - Name: "s1", - VolumeSource: corev1.VolumeSource{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ - ClaimName: "s1", - }, - }, - }, - }, - want1: []corev1.VolumeMount{ - { - Name: "s1", - MountPath: "/pkg/mounts/volumes1", - }, - }, - want2: map[string]bool{ - "/pkg/mounts/volumes1": true, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, got1, got2 := MountStorageVolumes(tt.args.spec) - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("MountStorageVolumes() got = %v, want %v", got, tt.want) - } - if !reflect.DeepEqual(got1, tt.want1) { - t.Errorf("MountStorageVolumes() got1 = %v, want %v", got1, tt.want1) - } - if !reflect.DeepEqual(got2, tt.want2) { - t.Errorf("MountStorageVolumes() got2 = %v, want %v", got2, tt.want2) - } - }) - } -} - func TestLabels(t *testing.T) { type args struct { clusterName string @@ -825,111 +404,3 @@ func TestAnnotations(t *testing.T) { }) } } - -func Test_getVolumeName(t *testing.T) { - type args struct { - mountInfo v1.MountInfo - } - tests := []struct { - name string - args args - want string - }{ - { - name: "test get volume name", - args: args{ - mountInfo: v1.MountInfo{ - Name: "test", - MountPath: "/my/path", - }, - }, - want: "test-1417", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := getVolumeName(tt.args.mountInfo); got != tt.want { - t.Errorf("getVolumeName() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_completeProbe(t *testing.T) { - type args struct { - originalProbe *int32 - defaultFailureThreshold int32 - defaultPeriodSeconds int32 - probeHandler corev1.ProbeHandler - } - tests := []struct { - name string - args args - want *corev1.Probe - }{ - { - name: "test complete probe", - args: args{ - originalProbe: nil, - defaultFailureThreshold: 1, - defaultPeriodSeconds: 1, - probeHandler: corev1.ProbeHandler{}, - }, - want: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{}, - FailureThreshold: 1, - PeriodSeconds: 1, - }, - }, - { - name: "test complete probe 2", - args: args{ - originalProbe: func() *int32 { v := int32(10); return &v }(), - defaultFailureThreshold: 1, - defaultPeriodSeconds: 5, - probeHandler: corev1.ProbeHandler{}, - }, - want: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{}, - FailureThreshold: 2, - PeriodSeconds: 5, - }, - }, - { - name: "test complete probe 3", - args: args{ - originalProbe: func() *int32 { v := int32(0); return &v }(), - defaultFailureThreshold: 60, - defaultPeriodSeconds: 5, - probeHandler: corev1.ProbeHandler{}, - }, - want: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{}, - FailureThreshold: 60, - PeriodSeconds: 5, - }, - }, - { - name: "test complete probe 4", - args: args{ - originalProbe: func() *int32 { v := int32(1); return &v }(), - defaultFailureThreshold: 60, - defaultPeriodSeconds: 5, - probeHandler: corev1.ProbeHandler{}, - }, - want: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{}, - FailureThreshold: 1, - PeriodSeconds: 5, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := completeProbe(tt.args.originalProbe, tt.args.defaultFailureThreshold, - tt.args.defaultPeriodSeconds, tt.args.probeHandler); !reflect.DeepEqual(got, tt.want) { - t.Errorf("completeProbe() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/pkg/k8sutils/templates/statefulset/spec.go b/pkg/k8sutils/templates/statefulset/spec.go index fb2b6c4a..81843310 100644 --- a/pkg/k8sutils/templates/statefulset/spec.go +++ b/pkg/k8sutils/templates/statefulset/spec.go @@ -24,6 +24,7 @@ import ( rutils "github.com/StarRocks/starrocks-kubernetes-operator/pkg/common/resource_utils" "github.com/StarRocks/starrocks-kubernetes-operator/pkg/k8sutils/load" srobject "github.com/StarRocks/starrocks-kubernetes-operator/pkg/k8sutils/templates/object" + "github.com/StarRocks/starrocks-kubernetes-operator/pkg/k8sutils/templates/pod" "github.com/StarRocks/starrocks-kubernetes-operator/pkg/k8sutils/templates/service" ) @@ -32,20 +33,26 @@ const STARROCKS_WAREHOUSE_FINALIZER = "starrocks.com.starrockswarehouse/protecti func PVCList(volumes []v1.StorageVolume) []corev1.PersistentVolumeClaim { var pvcs []corev1.PersistentVolumeClaim for _, vm := range volumes { - pvcs = append(pvcs, corev1.PersistentVolumeClaim{ + if pod.IsSpecialStorageClass(vm.StorageClassName) { + continue + } + pvc := corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{Name: vm.Name}, Spec: corev1.PersistentVolumeClaimSpec{ AccessModes: []corev1.PersistentVolumeAccessMode{ corev1.ReadWriteOnce, }, StorageClassName: vm.StorageClassName, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceStorage: resource.MustParse(vm.StorageSize), - }, - }, }, - }) + } + if vm.StorageSize != "" { + pvc.Spec.Resources = corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse(vm.StorageSize), + }, + } + } + pvcs = append(pvcs, pvc) } return pvcs } diff --git a/pkg/subcontrollers/be/be_pod.go b/pkg/subcontrollers/be/be_pod.go index 3b21b219..a56fa308 100644 --- a/pkg/subcontrollers/be/be_pod.go +++ b/pkg/subcontrollers/be/be_pod.go @@ -43,31 +43,10 @@ func (be *BeController) buildPodTemplate(src *srapi.StarRocksCluster, config map vols, volumeMounts, vexist := pod.MountStorageVolumes(beSpec) // add default volume about log, if meta not configure. if _, ok := vexist[_logPath]; !ok { - volumeMounts = append(volumeMounts, corev1.VolumeMount{ - // use storage volume. - Name: _logName, - MountPath: _logPath, - }) - vols = append(vols, corev1.Volume{ - Name: _logName, - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }) + vols, volumeMounts = pod.MountEmptyDirVolume(vols, volumeMounts, _logName, _logPath, "") } if _, ok := vexist[_storagePath]; !ok { - volumeMounts = append(volumeMounts, corev1.VolumeMount{ - // use storage volume. - Name: _storageName, - MountPath: _storagePath, - }) - - vols = append(vols, corev1.Volume{ - Name: _storageName, - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }) + vols, volumeMounts = pod.MountEmptyDirVolume(vols, volumeMounts, _storageName, _storagePath, "") } // mount configmap, secrets to pod if needed diff --git a/pkg/subcontrollers/cn/cn_pod.go b/pkg/subcontrollers/cn/cn_pod.go index fbd165c5..b0c60ee9 100644 --- a/pkg/subcontrollers/cn/cn_pod.go +++ b/pkg/subcontrollers/cn/cn_pod.go @@ -49,16 +49,7 @@ func (cc *CnController) buildPodTemplate(ctx context.Context, object srobject.St vols, volumeMounts, vexist := pod.MountStorageVolumes(cnSpec) // add default volume about log if _, ok := vexist[_logPath]; !ok { - volumeMounts = append(volumeMounts, corev1.VolumeMount{ - Name: _logName, - MountPath: _logPath, - }) - vols = append(vols, corev1.Volume{ - Name: _logName, - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }) + vols, volumeMounts = pod.MountEmptyDirVolume(vols, volumeMounts, _logName, _logPath, "") } // mount configmap, secrets to pod if needed diff --git a/pkg/subcontrollers/fe/fe_pod.go b/pkg/subcontrollers/fe/fe_pod.go index deba9575..66b33297 100644 --- a/pkg/subcontrollers/fe/fe_pod.go +++ b/pkg/subcontrollers/fe/fe_pod.go @@ -41,32 +41,12 @@ func (fc *FeController) buildPodTemplate(src *srapi.StarRocksCluster, config map feSpec := src.Spec.StarRocksFeSpec vols, volMounts, vexist := pod.MountStorageVolumes(feSpec) - // add default volume about log ,meta if not configure. + // add default volume about log, meta if not configure. if _, ok := vexist[_metaPath]; !ok { - volMounts = append( - volMounts, corev1.VolumeMount{ - Name: _metaName, - MountPath: _metaPath, - }) - vols = append(vols, corev1.Volume{ - Name: _metaName, - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }) + vols, volMounts = pod.MountEmptyDirVolume(vols, volMounts, _metaName, _metaPath, "") } - if _, ok := vexist[_logPath]; !ok { - volMounts = append(volMounts, corev1.VolumeMount{ - Name: _logName, - MountPath: _logPath, - }) - vols = append(vols, corev1.Volume{ - Name: _logName, - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }) + vols, volMounts = pod.MountEmptyDirVolume(vols, volMounts, _logName, _logPath, "") } // mount configmap, secrets to pod if needed