pkg/common/utils/resource/persistent_volume_claim.go (131 lines of code) (raw):
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package resource
import (
dorisv1 "github.com/apache/doris-operator/api/doris/v1"
"github.com/apache/doris-operator/pkg/common/utils/doris"
"github.com/apache/doris-operator/pkg/common/utils/hash"
"github.com/apache/doris-operator/pkg/common/utils/set"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog/v2"
"strings"
)
var (
pvc_finalizer = "selectdb.doris.com/pvc-finalizer"
pvc_manager_annotation = "selectdb.doris.com/pvc-manager"
)
func BuildPVCName(stsName, ordinal, volumeName string) string {
pvcName := stsName + "-" + ordinal
if volumeName != "" {
pvcName = volumeName + "-" + pvcName
}
return pvcName
}
func BuildPVC(volume dorisv1.PersistentVolume, labels map[string]string, namespace, stsName, ordinal string) corev1.PersistentVolumeClaim {
annotations := buildPVCAnnotations(volume)
pvc := corev1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: BuildPVCName(stsName, ordinal, volume.Name),
Namespace: namespace,
Labels: labels,
Annotations: annotations,
Finalizers: []string{pvc_finalizer},
},
Spec: volume.PersistentVolumeClaimSpec,
}
return pvc
}
// finalAnnotations is a combination of user annotations and operator default annotations
func buildPVCAnnotations(volume dorisv1.PersistentVolume) Annotations {
annotations := Annotations{}
if volume.PVCProvisioner == dorisv1.PVCProvisionerOperator {
annotations.Add(pvc_manager_annotation, "operator")
annotations.Add(dorisv1.ComponentResourceHash, hash.HashObject(volume.PersistentVolumeClaimSpec))
}
if volume.Annotations != nil && len(volume.Annotations) > 0 {
annotations.AddAnnotation(volume.Annotations)
}
return annotations
}
func getDefaultDorisHome(componentType dorisv1.ComponentType) string {
switch componentType {
case dorisv1.Component_FE:
return DEFAULT_ROOT_PATH + "/fe"
case dorisv1.Component_BE, dorisv1.Component_CN:
return DEFAULT_ROOT_PATH + "/be"
case dorisv1.Component_Broker:
return DEFAULT_ROOT_PATH + "/apache_hdfs_broker"
default:
klog.Infof("the componentType: %s have not default DORIS_HOME", componentType)
}
return ""
}
// GenerateEveryoneMountPathDorisPersistentVolume is used to process the pvc template configuration in CRD.
// The template is defined as follows:
// - PersistentVolume.MountPath is "", it`s template configuration.
// - PersistentVolume.MountPath is not "", it`s actual pvc configuration.
// The Explain rules are as follows:
// 1. Non-templated PersistentVolumes are returned directly in the result list.
// 2. If there is a pvc template, return the actual list of pvcs after processing.
// 3. The template needs to parse the configuration of the doris config file to create the pvc.
// 4. If there are multiple templates, the last valid template will be used.
func GenerateEveryoneMountPathDorisPersistentVolume(spec *dorisv1.BaseSpec, excludePaths []string, config map[string]interface{}, componentType dorisv1.ComponentType) ([]dorisv1.PersistentVolume, error) {
// Only the last data pvc template configuration takes effect
var template *dorisv1.PersistentVolume
// dorisPersistentVolumes is the pvc that needs to be actually created, specified by the user
var dorisPersistentVolumes []dorisv1.PersistentVolume
for i := range spec.PersistentVolumes {
if spec.PersistentVolumes[i].MountPath != "" {
path := spec.PersistentVolumes[i].MountPath
if strings.HasSuffix(path, "/") {
path = path[:len(path)-1]
}
if !set.ArrayContains(excludePaths, path) {
dorisPersistentVolumes = append(dorisPersistentVolumes, spec.PersistentVolumes[i])
} else {
klog.Errorf("GenerateEveryoneMountPathDorisPersistentVolume SharedPersistentVolumeClaim.MountPath (%s) conflicts with the MountPath configured in BaseSpec.PersistentVolumes, "+
"and the SharedPersistentVolumeClaims configuration takes precedence, skipping the processing of the BaseSpec.PersistentVolumes for the PVC. "+
"If it does not meet expectations, please handle the conflict and rebuild the cluster.", path)
}
} else {
template = (&spec.PersistentVolumes[i]).DeepCopy()
}
}
if template == nil {
return dorisPersistentVolumes, nil
}
// Processing pvc template
var dataPathKey, dataDefaultPath string
var dataPaths []string
dorisHome := getDefaultDorisHome(componentType)
switch componentType {
case dorisv1.Component_FE:
dataPathKey = "meta_dir"
dataDefaultPath = dorisHome + "/doris-meta"
case dorisv1.Component_BE, dorisv1.Component_CN:
dataPathKey = "storage_root_path"
dataDefaultPath = dorisHome + "/storage"
default:
klog.Infof("GenerateEveryoneMountPathDorisPersistentVolume the componentType: %s is not supported, PersistentVolume template will not work ", componentType)
return dorisPersistentVolumes, nil
}
dataPathValue, dataExist := config[dataPathKey]
if !dataExist {
klog.Infof("GenerateEveryoneMountPathDorisPersistentVolume: dataPathKey '%s' not found in config, default value will effect", dataPathKey)
dataPaths = append(dataPaths, dataDefaultPath)
} else {
dataPaths = doris.ResolveStorageRootPath(dataPathValue.(string))
}
if len(dataPaths) == 1 {
tmp := *template.DeepCopy()
tmp.MountPath = dataPaths[0]
if !set.ArrayContains(excludePaths, dataPaths[0]) {
dorisPersistentVolumes = append(dorisPersistentVolumes, tmp)
} else {
klog.Errorf("GenerateEveryoneMountPathDorisPersistentVolume SharedPersistentVolumeClaims.MountPath (%s) conflicts with the MountPath configured in BaseSpec.PersistentVolumes, "+
"and the SharedPersistentVolumeClaims configuration takes precedence, skipping the processing of the BaseSpec.PersistentVolumes for the PVC. "+
"If it does not meet expectations, please handle the conflict and rebuild the cluster.", dataPaths[0])
}
} else {
pathNames := doris.GetNameOfEachPath(dataPaths)
for i := range dataPaths {
tmp := *template.DeepCopy()
tmp.Name = tmp.Name + "-" + pathNames[i]
tmp.MountPath = dataPaths[i]
if !set.ArrayContains(excludePaths, dataPaths[i]) {
dorisPersistentVolumes = append(dorisPersistentVolumes, tmp)
} else {
klog.Errorf("GenerateEveryoneMountPathDorisPersistentVolume SharedPersistentVolumeClaims.MountPath (%s) conflicts with the MountPath configured in BaseSpec.PersistentVolumes, "+
"and the SharedPersistentVolumeClaims configuration takes precedence, skipping the processing of the BaseSpec.PersistentVolumes for the PVC. "+
"If it does not meet expectations, please handle the conflict and rebuild the cluster.", dataPaths[i])
}
}
}
return dorisPersistentVolumes, nil
}