/* Copyright 2021 Alibaba Group Holding Limited. Licensed 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 reconcile import ( "encoding/json" "errors" "fmt" "github.com/alibaba/polardbx-operator/pkg/hpfs/filestream" xstoreconvention "github.com/alibaba/polardbx-operator/pkg/operator/v1/xstore/convention" xstoremeta "github.com/alibaba/polardbx-operator/pkg/operator/v1/xstore/meta" "github.com/alibaba/polardbx-operator/pkg/util/name" appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/utils/pointer" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sort" "strconv" "strings" "time" polardbxv1 "github.com/alibaba/polardbx-operator/api/v1" "github.com/alibaba/polardbx-operator/pkg/k8s/cache" "github.com/alibaba/polardbx-operator/pkg/k8s/control" k8shelper "github.com/alibaba/polardbx-operator/pkg/k8s/helper" k8sselectorutil "github.com/alibaba/polardbx-operator/pkg/k8s/helper/selector" "github.com/alibaba/polardbx-operator/pkg/meta/core/gms" "github.com/alibaba/polardbx-operator/pkg/meta/core/gms/security" "github.com/alibaba/polardbx-operator/pkg/meta/core/group" "github.com/alibaba/polardbx-operator/pkg/operator/v1/config" "github.com/alibaba/polardbx-operator/pkg/operator/v1/polardbx/convention" polardbxmeta "github.com/alibaba/polardbx-operator/pkg/operator/v1/polardbx/meta" dbutil "github.com/alibaba/polardbx-operator/pkg/util/database" ) type Context struct { *control.BaseReconcileContext // Caches objectCache cache.ObjectLoadingCache polardbxKey types.NamespacedName polardbxChanged bool polardbx *polardbxv1.PolarDBXCluster primaryPolardbx *polardbxv1.PolarDBXCluster polardbxStatus *polardbxv1.PolarDBXClusterStatus cnDeployments map[string]*appsv1.Deployment cdcDeployments map[string]*appsv1.Deployment columnarDeployments map[string]*appsv1.Deployment podsByRole map[string][]corev1.Pod nodes []corev1.Node gmsStore *polardbxv1.XStore dnStores map[int]*polardbxv1.XStore primaryDnStore map[int]*polardbxv1.XStore polardbxMonitor *polardbxv1.PolarDBXMonitor polardbxMonitorKey types.NamespacedName polardbxBackup *polardbxv1.PolarDBXBackup polardbxBackupKey types.NamespacedName polardbxBackupStatusSnapshot *polardbxv1.PolarDBXBackupStatus polardbxSeekCpJob *batchv1.Job polardbxBackupScheduleKey types.NamespacedName polardbxBackupSchedule *polardbxv1.PolarDBXBackupSchedule polardbxBackupScheduleStatus *polardbxv1.PolarDBXBackupScheduleStatus polardbxParameterTemplate *polardbxv1.PolarDBXParameterTemplate polardbxTemplateParams map[string]map[string]polardbxv1.TemplateParams polardbxParamsRoleMap map[string]map[string]polardbxv1.Params roleToRestart map[string]bool polardbxParameter *polardbxv1.PolarDBXParameter polardbxParameterKey types.NamespacedName polardbxParameterStatus *polardbxv1.PolarDBXParameterStatus // Hint cache controllerHints []string // Config configLoader func() config.Config // Managers gmsManager gms.Manager groupManager group.GroupManager // xstoreManagerMap records xstore's pod name and its related group manager xstoreManagerMap map[string]group.GroupManager taskConfigMap *corev1.ConfigMap // Filestream client filestreamClient *filestream.FileClient //backup binlog backupBinlog *polardbxv1.PolarDBXBackupBinlog backupBinlogKey types.NamespacedName } func (rc *Context) Debug() bool { if rc.BaseReconcileContext.Debug() { return true } r, _ := rc.containsControllerHint("debug") return r } func (rc *Context) Config() config.Config { return rc.configLoader() } func (rc *Context) GetNodesSortedByName() ([]corev1.Node, error) { if rc.nodes == nil { var nodeList corev1.NodeList err := rc.Client().List(rc.Context(), &nodeList) if err != nil { return nil, err } rc.nodes = nodeList.Items sort.Slice(rc.nodes, func(i, j int) bool { return rc.nodes[i].Name < rc.nodes[j].Name }) } return rc.nodes, nil } func (rc *Context) GetSortedSchedulableNodes(nodeSelector *corev1.NodeSelector) ([]corev1.Node, error) { nodes, err := rc.GetNodesSortedByName() if err != nil { return nil, err } filterNodes := func(nodes []corev1.Node, nodeSelector *corev1.NodeSelector) ([]corev1.Node, error) { r := make([]corev1.Node, 0) for _, n := range nodes { if n.Spec.Unschedulable { continue } if nodeSelector == nil { r = append(r, n) } else { ok, err := k8sselectorutil.IsNodeMatches(&n, nodeSelector) if err != nil { return nil, err } if ok { r = append(r, n) } } } return r, nil } return filterNodes(nodes, nodeSelector) } func (rc *Context) SetPolarDBXKey(key types.NamespacedName) { rc.polardbxKey = key } func (rc *Context) CheckPolarDBXExist(key types.NamespacedName) bool { polardbx, _ := rc.objectCache.GetObject( rc.Context(), key, &polardbxv1.PolarDBXCluster{}, ) return polardbx != nil } func (rc *Context) GetPolarDBX() (*polardbxv1.PolarDBXCluster, error) { if rc.polardbx == nil { polardbx, err := rc.objectCache.GetObject( rc.Context(), rc.polardbxKey, &polardbxv1.PolarDBXCluster{}, ) if err != nil { return nil, err } rc.polardbx = polardbx.(*polardbxv1.PolarDBXCluster) rc.polardbxStatus = rc.polardbx.Status.DeepCopy() } return rc.polardbx, nil } func (rc *Context) MustGetPolarDBX() *polardbxv1.PolarDBXCluster { polardbx, err := rc.GetPolarDBX() if err != nil { panic(err) } return polardbx } func (rc *Context) GetPrimaryPolarDBX() (*polardbxv1.PolarDBXCluster, error) { polardbx := rc.MustGetPolarDBX() if !polardbx.Spec.Readonly { return nil, errors.New("current pxc is not readonly") } if rc.primaryPolardbx == nil { primaryPolardbxKey := types.NamespacedName{ Namespace: rc.polardbxKey.Namespace, Name: polardbx.Spec.PrimaryCluster, } primaryPolardbx, err := rc.objectCache.GetObject( rc.Context(), primaryPolardbxKey, &polardbxv1.PolarDBXCluster{}, ) if err != nil { return nil, err } rc.primaryPolardbx = primaryPolardbx.(*polardbxv1.PolarDBXCluster) } return rc.primaryPolardbx, nil } func (rc *Context) MustGetPrimaryPolarDBX() *polardbxv1.PolarDBXCluster { polardbx, err := rc.GetPrimaryPolarDBX() if err != nil { panic(err) } return polardbx } func (rc *Context) GetReadonlyPolarDBXList() ([]*polardbxv1.PolarDBXCluster, error) { polardbx := rc.MustGetPolarDBX() if polardbx.Spec.Readonly { return nil, errors.New("current pxc is readonly") } var readonlyPolardbxClusterList polardbxv1.PolarDBXClusterList var readonlyPolardbxList []*polardbxv1.PolarDBXCluster err := rc.Client().List( rc.Context(), &readonlyPolardbxClusterList, client.MatchingLabels( k8shelper.PatchLabels( map[string]string{ polardbxmeta.LabelType: polardbxmeta.TypeReadonly, polardbxmeta.LabelPrimaryName: polardbx.Name, }, ), ), ) if err != nil { return nil, fmt.Errorf("unable to get readonly polardbx: %w", err) } for _, item := range readonlyPolardbxClusterList.Items { readonlyPolardbxList = append(readonlyPolardbxList, &item) } return readonlyPolardbxList, nil } func (rc *Context) containsControllerHint(hint string) (bool, error) { if rc.controllerHints == nil { polardbx, err := rc.GetPolarDBX() if err != nil { return false, err } val, ok := polardbx.Annotations[polardbxmeta.AnnotationControllerHints] rc.controllerHints = []string{} if ok { for _, v := range strings.Split(val, ",") { rc.controllerHints = append(rc.controllerHints, strings.TrimSpace(v)) } } } for _, h := range rc.controllerHints { if h == hint { return true, nil } } return false, nil } func (rc *Context) ContainsControllerHint(hint string) bool { r, err := rc.containsControllerHint(hint) if err != nil { panic(err) } return r } // TODO(siyun): consider SetControllerRef for readonly inst func (rc *Context) SetControllerRef(obj client.Object) error { if obj == nil { return nil } polardbx := rc.MustGetPolarDBX() return ctrl.SetControllerReference(polardbx, obj, rc.Scheme()) } func (rc *Context) SetControllerRefToBackup(obj client.Object) error { if obj == nil { return nil } polardbxBackup := rc.MustGetPolarDBXBackup() return ctrl.SetControllerReference(polardbxBackup, obj, rc.Scheme()) } func (rc *Context) SetControllerRefAndCreate(obj client.Object) error { if err := rc.SetControllerRef(obj); err != nil { return err } return rc.Client().Create(rc.Context(), obj) } func (rc *Context) SetControllerRefAndCreateToBackup(obj client.Object) error { if err := rc.SetControllerRefToBackup(obj); err != nil { return err } return rc.Client().Create(rc.Context(), obj) } func (rc *Context) SetControllerRefAndUpdate(obj client.Object) error { if err := rc.SetControllerRef(obj); err != nil { return err } return rc.Client().Update(rc.Context(), obj) } func (rc *Context) SetControllerToOwnerAndCreate(owner, obj client.Object) error { if err := ctrl.SetControllerReference(owner, obj, rc.Scheme()); err != nil { return err } return rc.Client().Create(rc.Context(), obj) } func (rc *Context) IsPolarDBXStatusChanged() bool { if rc.polardbxStatus == nil { return false } return !equality.Semantic.DeepEqual(&rc.polardbx.Status, rc.polardbxStatus) } func (rc *Context) IsPolarDBXChanged() bool { return rc.polardbxChanged } func (rc *Context) MarkPolarDBXChanged() { rc.polardbxChanged = true } func (rc *Context) UpdatePolarDBX() error { if rc.polardbx == nil { return nil } // Deep copy status before updating because client.update will update // the status of object. status := rc.polardbx.Status.DeepCopy() err := rc.Client().Update(rc.Context(), rc.polardbx) if err != nil { return err } // Restore the status (shallow copy is enough) rc.polardbx.Status = *status return nil } func (rc *Context) UpdatePolarDBXStatus() error { if rc.polardbxStatus == nil { return nil } err := rc.Client().Status().Update(rc.Context(), rc.polardbx) if err != nil { return err } rc.polardbxStatus = rc.polardbx.Status.DeepCopy() return nil } func (rc *Context) GetPolarDBXService(serviceType convention.ServiceType) (*corev1.Service, error) { polardbx, err := rc.GetPolarDBX() if err != nil { return nil, fmt.Errorf("unable to get polardbx object: %w", err) } serviceKey := types.NamespacedName{ Namespace: rc.polardbxKey.Namespace, Name: convention.NewServiceName(polardbx, serviceType), } service, err := rc.objectCache.GetObject(rc.Context(), serviceKey, &corev1.Service{}) if err != nil { return nil, err } err = k8shelper.CheckControllerReference(service, polardbx) if err != nil { return nil, err } return service.(*corev1.Service), nil } func (rc *Context) GetPolarDBXClusterAddr(serviceType convention.ServiceType, port string) (string, error) { svc, err := rc.GetPolarDBXService(serviceType) if err != nil { return "", err } return k8shelper.GetClusterAddrFromService(svc, port) } func (rc *Context) getPolarDBXSecret(polardbx *polardbxv1.PolarDBXCluster, secretType convention.SecretType) (*corev1.Secret, error) { secretKey := types.NamespacedName{ Namespace: rc.polardbxKey.Namespace, Name: convention.NewSecretName(polardbx, secretType), } secret, err := rc.objectCache.GetObject(rc.Context(), secretKey, &corev1.Secret{}) if err != nil { return nil, err } if err := k8shelper.CheckControllerReference(secret, polardbx); err != nil { return nil, err } return secret.(*corev1.Secret), nil } func (rc *Context) GetPolarDBXSecretForRestore() (*corev1.Secret, error) { polardbx, err := rc.GetPolarDBX() if err != nil { return nil, fmt.Errorf("unable to get polardbx object: %w", err) } secretKey := types.NamespacedName{} if polardbx.Spec.Restore.BackupSet == "" || len(polardbx.Spec.Restore.BackupSet) == 0 { backup, err := rc.GetLastCompletedPXCBackup(map[string]string{polardbxmeta.LabelName: polardbx.Spec.Restore.From.PolarBDXName}, rc.MustParseRestoreTime()) if err != nil { return nil, err } secretKey = types.NamespacedName{ Namespace: polardbx.Namespace, Name: backup.Name, } } else { secretKey = types.NamespacedName{ Namespace: polardbx.Namespace, Name: polardbx.Spec.Restore.BackupSet, } } secret, err := rc.objectCache.GetObject(rc.Context(), secretKey, &corev1.Secret{}) if err != nil { return nil, err } return secret.(*corev1.Secret), nil } func (rc *Context) ParseRestoreTime() (time.Time, error) { polarDBX := rc.MustGetPolarDBX() if polarDBX.Spec.Restore == nil { return time.Time{}, nil } location, err := time.LoadLocation(gms.StrOrDefault(polarDBX.Spec.Restore.TimeZone, "UTC")) if err != nil { return time.Time{}, nil } return time.ParseInLocation("2006-01-02T15:04:05Z", polarDBX.Spec.Restore.Time, location) } func (rc *Context) MustParseRestoreTime() time.Time { t, err := rc.ParseRestoreTime() if err != nil { panic(err) } return t } func (rc *Context) GetPolarDBXSecret(secretType convention.SecretType) (*corev1.Secret, error) { polardbx, err := rc.GetPolarDBX() if err != nil { return nil, fmt.Errorf("unable to get polardbx object: %w", err) } return rc.getPolarDBXSecret(polardbx, secretType) } func (rc *Context) GetPolarDBXEncodeKey() (string, error) { polardbx, err := rc.GetPolarDBX() if err != nil { return "", fmt.Errorf("unable to get polardbx object: %w", err) } if polardbx.Spec.Readonly { polardbx, err = rc.GetPrimaryPolarDBX() if err != nil { return "", err } } secret, err := rc.getPolarDBXSecret(polardbx, convention.SecretTypeSecurity) if err != nil { return "", err } encodeKey, ok := secret.Data[convention.SecretKeyEncodeKey] if !ok { return "", errors.New("not found") } return string(encodeKey), nil } func (rc *Context) GetPolarDBXTlsCerts() (map[string]string, error) { secret, err := rc.GetPolarDBXSecret(convention.SecretTypeSecurity) if err != nil { return nil, err } rootCrt, ok := secret.Data[convention.SecretKeyRootCrt] if !ok { return nil, errors.New("root.crt not found") } serverKey, ok := secret.Data[convention.SecretKeyServerKey] if !ok { return nil, errors.New("server.key not found") } serverCrt, ok := secret.Data[convention.SecretKeyServerCrt] if !ok { return nil, errors.New("server.crt not found") } return map[string]string{ "root.crt": string(rootCrt), "server.key": string(serverKey), "server.crt": string(serverCrt), }, nil } func (rc *Context) GetPolarDBXPasswordCipher() (security.PasswordCipher, error) { key, err := rc.GetPolarDBXEncodeKey() if err != nil { return nil, err } return security.NewPasswordCipher(key) } func (rc *Context) GetPolarDBXAccountPassword(user string) (string, error) { secret, err := rc.GetPolarDBXSecret(convention.SecretTypeAccount) if err != nil { return "", err } passwd, ok := secret.Data[user] if !ok { return "", errors.New("not found") } return string(passwd), nil } func (rc *Context) GetSecret(name string) (*corev1.Secret, error) { secretKey := types.NamespacedName{ Namespace: rc.Namespace(), Name: name, } cm, err := rc.objectCache.GetObject(rc.Context(), secretKey, &corev1.Secret{}) if err != nil { return nil, err } return cm.(*corev1.Secret), nil } func (rc *Context) GetPolarDBXConfigMap(cmType convention.ConfigMapType) (*corev1.ConfigMap, error) { polardbx, err := rc.GetPolarDBX() if err != nil { return nil, fmt.Errorf("unable to get polardbx object: %w", err) } cm, err := rc.GetConfigMap(convention.NewConfigMapName(polardbx, cmType)) if err != nil { return nil, err } if err := k8shelper.CheckControllerReference(cm, polardbx); err != nil { return nil, err } return cm, nil } func (rc *Context) GetConfigMap(name string) (*corev1.ConfigMap, error) { cmKey := types.NamespacedName{ Namespace: rc.polardbxKey.Namespace, Name: name, } cm, err := rc.objectCache.GetObject(rc.Context(), cmKey, &corev1.ConfigMap{}) if err != nil { return nil, err } return cm.(*corev1.ConfigMap), nil } func (rc *Context) GetCNPod(polardbx *polardbxv1.PolarDBXCluster) (*corev1.Pod, error) { polardbx, err := rc.GetPolarDBX() if err != nil { return nil, fmt.Errorf("unable to get polardbx object: %w", err) } var cnPods corev1.PodList err = rc.Client().List(rc.Context(), &cnPods, client.InNamespace(rc.polardbxKey.Namespace), client.MatchingLabels(convention.ConstLabelsWithRole(polardbx, polardbxmeta.RoleCN)), ) if err != nil { return nil, err } if len(cnPods.Items) <= 0 { return nil, errors.New("there is no cnpods") } else { return &cnPods.Items[0], nil } } func (rc *Context) getDNMap(polardbx *polardbxv1.PolarDBXCluster) (map[int]*polardbxv1.XStore, error) { var xstoreList polardbxv1.XStoreList err := rc.Client().List(rc.Context(), &xstoreList, client.InNamespace(rc.polardbxKey.Namespace), client.MatchingLabels(convention.ConstLabelsWithRole(polardbx, polardbxmeta.RoleDN)), ) if err != nil { return nil, err } // Check & collect dnStores := make(map[int]*polardbxv1.XStore) for i, xstore := range xstoreList.Items { // Not owned, just ignore. if err := k8shelper.CheckControllerReference(&xstore, polardbx); err != nil { continue } // Parse index index := convention.MustParseIndexFromDN(&xstore) if index < 0 { continue } if _, found := dnStores[index]; found { return nil, errors.New("found xstore with duplicate index: " + strconv.Itoa(index)) } dnStores[index] = &xstoreList.Items[i] } return dnStores, nil } func (rc *Context) GetDNMap() (map[int]*polardbxv1.XStore, error) { if rc.dnStores == nil { polardbx, err := rc.GetPolarDBX() if err != nil { return nil, fmt.Errorf("unable to get polardbx object: %w", err) } return rc.getDNMap(polardbx) } return rc.dnStores, nil } func (rc *Context) GetPrimaryDNMap() (map[int]*polardbxv1.XStore, error) { //if rc.primaryDnStore == nil { primaryPolardbx, err := rc.GetPrimaryPolarDBX() if err != nil { return nil, fmt.Errorf("unable to get primary polardbx object: %w", err) } return rc.getDNMap(primaryPolardbx) //} //// TODO: refresh cache // //return rc.primaryDnStore, nil } func (rc *Context) GetDNMapOf(polardbx *polardbxv1.PolarDBXCluster) (map[int]*polardbxv1.XStore, error) { return rc.getDNMap(polardbx) } func (rc *Context) getOrderedDNListFromMap(dnMap map[int]*polardbxv1.XStore) []*polardbxv1.XStore { type sortKey struct { xstore *polardbxv1.XStore index int } keys := make([]sortKey, 0, len(dnMap)) for i, v := range dnMap { keys = append(keys, sortKey{ xstore: v, index: i, }) } sort.Slice(keys, func(i, j int) bool { return keys[i].index < keys[j].index }) dnList := make([]*polardbxv1.XStore, 0, len(dnMap)) for _, v := range keys { dnList = append(dnList, v.xstore) } return dnList } func (rc *Context) GetOrderedDNList() ([]*polardbxv1.XStore, error) { dnMap, err := rc.GetDNMap() if err != nil { return nil, err } return rc.getOrderedDNListFromMap(dnMap), nil } func (rc *Context) GetOrderedDNListOf(polardbx *polardbxv1.PolarDBXCluster) ([]*polardbxv1.XStore, error) { dnMap, err := rc.GetDNMapOf(polardbx) if err != nil { return nil, err } return rc.getOrderedDNListFromMap(dnMap), nil } func (rc *Context) GetDN(i int) (*polardbxv1.XStore, error) { dnMap, err := rc.GetDNMap() if err != nil { return nil, err } xstore, ok := dnMap[i] if !ok { return nil, apierrors.NewNotFound(schema.GroupResource{}, "") } return xstore, nil } func (rc *Context) GetLeaderOfDN(xstore *polardbxv1.XStore) (*corev1.Pod, error) { var leaderPod corev1.Pod leaderPodName := types.NamespacedName{Namespace: rc.Namespace(), Name: xstore.Status.LeaderPod} err := rc.Client().Get(rc.Context(), leaderPodName, &leaderPod) if err != nil { return nil, err } return &leaderPod, nil } func (rc *Context) GetXstoreByPod(pod *corev1.Pod) (*polardbxv1.XStore, error) { var xstore polardbxv1.XStore xstoreName := types.NamespacedName{Namespace: pod.Namespace, Name: pod.Labels[xstoremeta.LabelName]} err := rc.Client().Get(rc.Context(), xstoreName, &xstore) if err != nil { return nil, err } return &xstore, nil } func (rc *Context) getDeploymentMap(polardbx *polardbxv1.PolarDBXCluster, role string) (map[string]*appsv1.Deployment, error) { var deploymentList appsv1.DeploymentList err := rc.Client().List(rc.Context(), &deploymentList, client.InNamespace(rc.polardbxKey.Namespace), client.MatchingLabels(convention.ConstLabelsWithRole(polardbx, role)), ) if err != nil { return nil, err } deploymentMap := make(map[string]*appsv1.Deployment) for i, deploy := range deploymentList.Items { // Not owned, just ignore. if err := k8shelper.CheckControllerReference(&deploy, polardbx); err != nil { continue } deployGroup := convention.ParseGroupFromDeployment(&deploy) if _, found := deploymentMap[deployGroup]; found { return nil, errors.New("found deployment with duplicate group: " + deployGroup) } deploymentMap[deployGroup] = &deploymentList.Items[i] } return deploymentMap, nil } func (rc *Context) GetPrimaryDeploymentMap(role string) (map[string]*appsv1.Deployment, error) { polardbx, err := rc.GetPrimaryPolarDBX() if err != nil { return nil, fmt.Errorf("unable to get polardbx object: %w", err) } return rc.getDeploymentMap(polardbx, role) } func (rc *Context) GetDeploymentMap(role string) (map[string]*appsv1.Deployment, error) { var deploymentMapPtr *map[string]*appsv1.Deployment switch role { case polardbxmeta.RoleCN: deploymentMapPtr = &rc.cnDeployments case polardbxmeta.RoleCDC: deploymentMapPtr = &rc.cdcDeployments case polardbxmeta.RoleColumnar: deploymentMapPtr = &rc.columnarDeployments default: panic("required role to be cn, cdc or columnar, but found " + role) } if *deploymentMapPtr == nil { polardbx, err := rc.GetPolarDBX() if err != nil { return nil, fmt.Errorf("unable to get polardbx object: %w", err) } *deploymentMapPtr, err = rc.getDeploymentMap(polardbx, role) if err != nil { return nil, err } } return *deploymentMapPtr, nil } func (rc *Context) GetPods(role string) ([]corev1.Pod, error) { if role != polardbxmeta.RoleCN && role != polardbxmeta.RoleCDC && role != polardbxmeta.RoleColumnar { panic("required role to be cn, cdc or columnar, but found " + role) } pods := rc.podsByRole[role] if pods == nil { polardbx, err := rc.GetPolarDBX() if err != nil { return nil, fmt.Errorf("unable to get polardbx object: %w", err) } var podList corev1.PodList err = rc.Client().List( rc.Context(), &podList, client.InNamespace(rc.polardbxKey.Namespace), client.MatchingLabels(convention.ConstLabelsWithRole(polardbx, role)), ) if err != nil { return nil, err } if rc.podsByRole == nil { rc.podsByRole = make(map[string][]corev1.Pod) } rc.podsByRole[role] = podList.Items } return rc.podsByRole[role], nil } func (rc *Context) GetGMS() (*polardbxv1.XStore, error) { polardbx := rc.MustGetPolarDBX() var err error readonly := polardbx.Spec.Readonly if readonly { polardbx, err = rc.GetPrimaryPolarDBX() if err != nil { return nil, err } } if rc.gmsStore != nil { return rc.gmsStore, nil } gmsName := convention.NewGMSName(polardbx) if polardbx.Spec.ShareGMS { gmsName = convention.NewDNName(polardbx, 0) } gmsStore, err := rc.objectCache.GetObject( rc.Context(), types.NamespacedName{ Namespace: rc.polardbxKey.Namespace, Name: gmsName, }, &polardbxv1.XStore{}, ) if err != nil { return nil, err } if err := k8shelper.CheckControllerReference(gmsStore, polardbx); err != nil { return nil, err } if !readonly { rc.gmsStore = gmsStore.(*polardbxv1.XStore) } return gmsStore.(*polardbxv1.XStore), nil } func (rc *Context) GetService(name string) (*corev1.Service, error) { svc, err := rc.objectCache.GetObject( rc.Context(), types.NamespacedName{ Namespace: rc.polardbxKey.Namespace, Name: name, }, &corev1.Service{}, ) if err != nil { return nil, err } return svc.(*corev1.Service), nil } func (rc *Context) HasCNs() bool { polardbx := rc.MustGetPolarDBX() nodes := polardbx.Status.SpecSnapshot.Topology.Nodes if nodes.CN.Replicas == nil { return false } return *nodes.CN.Replicas > 0 } func (rc *Context) GetDnVersion() (string, error) { xstore, err := rc.GetDN(0) if err != nil { return "", fmt.Errorf("unable to get xstore object: %w", err) } return xstore.Status.EngineVersion, nil } func (rc *Context) GetPolarDBXGMSManager() (gms.Manager, error) { if rc.gmsManager == nil { polardbx, err := rc.GetPolarDBX() if err != nil { return nil, fmt.Errorf("unable to get polardbx object: %w", err) } encodeKey, err := rc.GetPolarDBXEncodeKey() if err != nil { return nil, fmt.Errorf("unable to get polardbx encode key: %w", err) } passwordCipher, err := security.NewPasswordCipher(encodeKey) if err != nil { return nil, err } gmsStore, err := rc.GetGMS() if client.IgnoreNotFound(err) != nil { return nil, fmt.Errorf("unable to get gms object: %w", err) } if gmsStore == nil { return nil, nil } gmsService, err := rc.GetService(xstoreconvention.NewServiceName(gmsStore, xstoreconvention.ServiceTypeReadWrite)) if err != nil { return nil, fmt.Errorf("unable to get gms service: %w", err) } gmsSecret, err := rc.GetSecret(xstoreconvention.NewSecretName(gmsStore)) if err != nil { return nil, fmt.Errorf("unable to get gms secret: %w", err) } xPort := 0 xContainerPort := k8shelper.GetPortFromService(gmsService, "polarx") if xContainerPort != nil { xPort = int(xContainerPort.Port) } gmsEngine := gmsStore.Spec.Engine annoStorageType, _ := polardbx.Annotations[polardbxmeta.AnnotationStorageType] storageType, err := gms.GetStorageType(gmsEngine, gmsStore.Status.EngineVersion, annoStorageType) if err != nil { return nil, err } gmsName := convention.NewGMSName(polardbx) if polardbx.Spec.Readonly { primaryPolardbx, err := rc.GetPrimaryPolarDBX() if err != nil { return nil, err } gmsName = convention.NewGMSName(primaryPolardbx) } // Mock GMS id if sharing GMS rc.gmsManager = gms.NewGmsManager( rc.Context(), polardbx.Name, &gms.MetaDB{ Id: gmsName, // Host used to create metadata in GMS. Host: k8shelper.GetServiceDNSRecordWithSvc(gmsService, true), // Host used to create connections from operator. Host4Conn: k8shelper.GetServiceDNSRecordWithSvc(gmsService, false), Port: int(k8shelper.MustGetPortFromService(gmsService, xstoreconvention.PortAccess).Port), XPort: xPort, User: xstoreconvention.SuperAccount, Passwd: string(gmsSecret.Data[xstoreconvention.SuperAccount]), Type: storageType, }, passwordCipher, ) } return rc.gmsManager, nil } func (rc *Context) GetPolarDBXGroupManager() (group.GroupManager, error) { if rc.groupManager == nil { polardbx, err := rc.GetPolarDBX() if err != nil { return nil, fmt.Errorf("unable to get polardbx object: %w", err) } serviceType := convention.ServiceTypeReadWrite if polardbx.Spec.Readonly { serviceType = convention.ServiceTypeReadOnly polardbx, err = rc.GetPrimaryPolarDBX() if err != nil { return nil, err } } service, err := rc.GetPolarDBXService(serviceType) if err != nil { return nil, err } // should get secret of primary pxc, since learner sync account and password from leader secret, err := rc.getPolarDBXSecret(polardbx, convention.SecretTypeAccount) if err != nil { return nil, err } caseInsensitive, _ := strconv.ParseBool(polardbx.Annotations[polardbxmeta.AnnotationSchemaCaseInsensitive]) rc.groupManager = group.NewGroupManager( rc.Context(), dbutil.MySQLDataSource{ Host: k8shelper.GetServiceDNSRecordWithSvc(service, false), Port: int(k8shelper.MustGetPortFromService(service, convention.PortAccess).Port), Username: convention.RootAccount, Password: string(secret.Data[convention.RootAccount]), }, caseInsensitive, ) } return rc.groupManager, nil } func (rc *Context) GetPolarDBXGroupManagerByBackup(backup *polardbxv1.PolarDBXBackup) (group.GroupManager, error) { serviceKey := types.NamespacedName{Namespace: backup.Namespace, Name: backup.Spec.Cluster.Name} service := corev1.Service{} err := rc.Client().Get(rc.Context(), serviceKey, &service) if err != nil { return nil, err } secretKey := types.NamespacedName{Namespace: backup.Namespace, Name: backup.Spec.Cluster.Name} secret := corev1.Secret{} err = rc.Client().Get(rc.Context(), secretKey, &secret) if err != nil { return nil, err } rc.groupManager = group.NewGroupManager( rc.Context(), dbutil.MySQLDataSource{ Host: k8shelper.GetServiceDNSRecordWithSvc(&service, false), Port: int(k8shelper.MustGetPortFromService(&service, convention.PortAccess).Port), Username: convention.RootAccount, Password: string(secret.Data[convention.RootAccount]), }, true, ) return rc.groupManager, nil } func (rc *Context) GetXstoreGroupManagerByPod(pod *corev1.Pod) (group.GroupManager, error) { if rc.xstoreManagerMap != nil { if mgr, ok := rc.xstoreManagerMap[pod.Name]; ok { return mgr, nil } } podService, err := rc.GetService(xstoreconvention.NewXstorePodServiceName(pod)) if err != nil { return nil, err } host, port, err := k8shelper.GetClusterIpPortFromService(podService, convention.PortAccess) if err != nil { return nil, err } xstore, err := rc.GetXstoreByPod(pod) if err != nil { return nil, err } passwd, err := rc.GetXStoreAccountPassword(xstoreconvention.SuperAccount, xstore) if err != nil { return nil, err } if rc.xstoreManagerMap == nil { rc.xstoreManagerMap = make(map[string]group.GroupManager) } rc.xstoreManagerMap[pod.Name] = group.NewGroupManager( rc.Context(), dbutil.MySQLDataSource{ Host: host, Port: int(port), Username: xstoreconvention.SuperAccount, Password: passwd, }, true, ) return rc.xstoreManagerMap[pod.Name], nil } func (rc *Context) Close() error { errs := make([]error, 0) if rc.gmsManager != nil { err := rc.gmsManager.Close() if err != nil { errs = append(errs, err) } } if rc.groupManager != nil { err := rc.groupManager.Close() if err != nil { errs = append(errs, err) } } if rc.xstoreManagerMap != nil { for _, mgr := range rc.xstoreManagerMap { err := mgr.Close() if err != nil { errs = append(errs, err) } } } if err := rc.BaseReconcileContext.Close(); err != nil { errs = append(errs, err) } if len(errs) > 0 { return errs[0] } return nil } func (rc *Context) PolardbxMonitorKey() types.NamespacedName { return rc.polardbxMonitorKey } func (rc *Context) SetPolardbxMonitorKey(polardbxMonitorKey types.NamespacedName) { rc.polardbxMonitorKey = polardbxMonitorKey } func (rc *Context) GetPolarDBXMonitor() (*polardbxv1.PolarDBXMonitor, error) { if rc.polardbxMonitor == nil { polardbxMonitor, err := rc.objectCache.GetObject( rc.Context(), rc.polardbxMonitorKey, &polardbxv1.PolarDBXMonitor{}, ) if err != nil { return nil, err } rc.polardbxMonitor = polardbxMonitor.(*polardbxv1.PolarDBXMonitor) } return rc.polardbxMonitor, nil } func (rc *Context) MustGetPolarDBXMonitor() *polardbxv1.PolarDBXMonitor { polardbxMonitor, err := rc.GetPolarDBXMonitor() if err != nil { panic(err) } return polardbxMonitor } func (rc *Context) SetPolarDBXBackupKey(polardbxBackupKey types.NamespacedName) { rc.polardbxBackupKey = polardbxBackupKey } func (rc *Context) GetPolarDBXBackup() (*polardbxv1.PolarDBXBackup, error) { if rc.polardbxBackup == nil { var polardbxBackup polardbxv1.PolarDBXBackup err := rc.Client().Get(rc.Context(), rc.Request().NamespacedName, &polardbxBackup) if err != nil { return nil, err } rc.polardbxBackup = &polardbxBackup rc.polardbxBackupStatusSnapshot = rc.polardbxBackup.Status.DeepCopy() } return rc.polardbxBackup, nil } func (rc *Context) MustGetPolarDBXBackup() *polardbxv1.PolarDBXBackup { polardbxBackup, err := rc.GetPolarDBXBackup() if err != nil { panic(err) } return polardbxBackup } func (rc *Context) UpdatePolarDBXBackup() error { if rc.polardbxBackup == nil { return nil } err := rc.Client().Update(rc.Context(), rc.polardbxBackup) if err != nil { return err } rc.polardbxBackupStatusSnapshot = rc.polardbxBackup.Status.DeepCopy() return nil } func (rc *Context) UpdatePolarDBXBackupStatus() error { if rc.polardbxBackupStatusSnapshot == nil { return nil } err := rc.Client().Status().Update(rc.Context(), rc.polardbxBackup) if err != nil { return err } rc.polardbxBackupStatusSnapshot = rc.polardbxBackup.Status.DeepCopy() return nil } func (rc *Context) IsPXCBackupStatusChanged() bool { if rc.polardbxBackupStatusSnapshot == nil { return false } return !equality.Semantic.DeepEqual(rc.polardbxBackup.Status, *rc.polardbxBackupStatusSnapshot) } func (rc *Context) GetXStoreBackups() (*polardbxv1.XStoreBackupList, error) { backup := rc.MustGetPolarDBXBackup() var xstoreBackups polardbxv1.XStoreBackupList err := rc.Client().List(rc.Context(), &xstoreBackups, client.InNamespace(rc.Namespace()), client.MatchingLabels{ polardbxmeta.LabelName: backup.Spec.Cluster.Name, polardbxmeta.LabelTopBackup: backup.Name, }) if err != nil { return nil, err } return &xstoreBackups, nil } func (rc *Context) GetXstoreBackupByName(xstoreBackupName string) (*polardbxv1.XStoreBackup, error) { var xstoreBackup polardbxv1.XStoreBackup xstoreBackupKey := types.NamespacedName{ Namespace: rc.Namespace(), Name: xstoreBackupName, } err := rc.Client().Get(rc.Context(), xstoreBackupKey, &xstoreBackup) if err != nil { return nil, err } return &xstoreBackup, nil } func (rc *Context) GetXStoreBackupPods() ([]corev1.Pod, error) { xstoreBackups, err := rc.GetXStoreBackups() if err != nil { return nil, err } pods := make([]corev1.Pod, 0) for _, xstoreBackup := range xstoreBackups.Items { var targetPod corev1.Pod targetPodSpec := types.NamespacedName{Namespace: xstoreBackup.Namespace, Name: xstoreBackup.Status.TargetPod} err = rc.Client().Get(rc.Context(), targetPodSpec, &targetPod) if err != nil { return nil, err } pods = append(pods, targetPod) } return pods, nil } func (rc *Context) GetXStoreAccountPassword(user string, xstore *polardbxv1.XStore) (string, error) { secret, err := rc.GetXStoreSecret(xstore) if err != nil { return "", err } passwd, ok := secret.Data[user] if !ok { return "", errors.New("not found") } return string(passwd), nil } func (rc *Context) GetXStoreSecret(xstore *polardbxv1.XStore) (*corev1.Secret, error) { secretKey := types.NamespacedName{Namespace: xstore.Namespace, Name: xstore.Name} secret, err := rc.objectCache.GetObject(rc.Context(), secretKey, &corev1.Secret{}) if err != nil { return nil, err } if err := k8shelper.CheckControllerReference(secret, xstore); err != nil { return nil, err } return secret.(*corev1.Secret), nil } func (rc *Context) GetSeekCpJob() (*batchv1.Job, error) { if rc.polardbxSeekCpJob == nil { pxcBackup := rc.MustGetPolarDBXBackup() var jobList batchv1.JobList err := rc.Client().List(rc.Context(), &jobList, client.InNamespace(rc.Request().Namespace), client.MatchingLabels{ polardbxmeta.SeekCpJobLabelBackupName: pxcBackup.Name, }) if err != nil { return nil, err } if len(jobList.Items) == 0 { return nil, nil } ownedJobs := make([]*batchv1.Job, 0) for i := range jobList.Items { job := &jobList.Items[i] if err = k8shelper.CheckControllerReference(job, pxcBackup); err == nil { ownedJobs = append(ownedJobs, job) } } if len(ownedJobs) == 0 { return nil, nil } if len(ownedJobs) > 1 { panic("multiple owned jobs found, must not happen") } rc.polardbxSeekCpJob = ownedJobs[0] } return rc.polardbxSeekCpJob, nil } func (rc *Context) GetCompletedPXCBackup(matchLabels map[string]string) (*polardbxv1.PolarDBXBackup, error) { polardbxBackupList := &polardbxv1.PolarDBXBackupList{} err := rc.Client().List(rc.Context(), polardbxBackupList, client.InNamespace(rc.Namespace()), client.MatchingLabels(matchLabels)) if err != nil || len(polardbxBackupList.Items) == 0 { return nil, err } return &polardbxBackupList.Items[0], nil } func (rc *Context) GetLastCompletedPXCBackup(matchLabels map[string]string, beforeTime time.Time) (*polardbxv1.PolarDBXBackup, error) { polardbxBackupList := &polardbxv1.PolarDBXBackupList{} err := rc.Client().List(rc.Context(), polardbxBackupList, client.InNamespace(rc.Namespace()), client.MatchingLabels(matchLabels)) if err != nil || len(polardbxBackupList.Items) == 0 { return nil, err } var lastBackup *polardbxv1.PolarDBXBackup = nil var lastBackupRestoreTime *time.Time = nil for i := range polardbxBackupList.Items { backup := &polardbxBackupList.Items[i] if backup.Status.Phase != polardbxv1.BackupFinished { continue } if backup.Status.LatestRecoverableTimestamp.After(beforeTime) { continue } if lastBackupRestoreTime == nil || lastBackupRestoreTime.Before(backup.Status.LatestRecoverableTimestamp.Time) { lastBackupRestoreTime = &backup.Status.LatestRecoverableTimestamp.Time lastBackup = backup } } return lastBackup, nil } func (rc *Context) GetPXCBackupByName(name string) (*polardbxv1.PolarDBXBackup, error) { polardbxBackup := &polardbxv1.PolarDBXBackup{} pxcBackupKey := types.NamespacedName{ Namespace: rc.Namespace(), Name: name, } err := rc.Client().Get(rc.Context(), pxcBackupKey, polardbxBackup) if err != nil { return nil, err } return polardbxBackup, nil } func (rc *Context) SaveTaskContext(key string, t interface{}) error { b, err := json.MarshalIndent(t, "", " ") if err != nil { return err } cm, err := rc.GetOrCreatePolarDBXBackupTaskConfigMap() if err != nil { return err } if cm.Data == nil { cm.Data = make(map[string]string) } if s, ok := cm.Data[key]; ok { if s == string(b) { return nil } } cm.Data[key] = string(b) return rc.Client().Update(rc.Context(), cm) } func (rc *Context) GetOrCreatePolarDBXBackupTaskConfigMap() (*corev1.ConfigMap, error) { if rc.taskConfigMap == nil { backup := rc.MustGetPolarDBXBackup() var cm corev1.ConfigMap err := rc.Client().Get(rc.Context(), types.NamespacedName{Namespace: rc.Namespace(), Name: name.PolarDBXBackupStableName(backup, "seekcp")}, &cm) if err != nil { if apierrors.IsNotFound(err) { rc.taskConfigMap = NewTaskConfigMap(backup) err = rc.SetControllerRefAndCreateToBackup(rc.taskConfigMap) if err != nil { return nil, err } return rc.taskConfigMap, nil } return nil, err } rc.taskConfigMap = &cm } return rc.taskConfigMap, nil } func NewTaskConfigMap(polardbxBackup *polardbxv1.PolarDBXBackup) *corev1.ConfigMap { return &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: convention.NewConfigMapNameForBackup(polardbxBackup, "seekcp"), Namespace: polardbxBackup.Namespace, }, Immutable: pointer.Bool(false), } } func (rc *Context) IsTaskContextExists(key string) (bool, error) { cm, err := rc.GetOrCreatePolarDBXBackupTaskConfigMap() if err != nil { return false, err } _, ok := cm.Data[key] return ok, nil } func (rc *Context) GetTaskContext(key string, t interface{}) error { cm, err := rc.GetOrCreatePolarDBXBackupTaskConfigMap() if err != nil { return err } return json.Unmarshal([]byte(cm.Data[key]), t) } func (rc *Context) GetPolarDBXTemplateName() (parameterTemplateName string) { polardbx := rc.MustGetPolarDBX() return polardbx.Spec.ParameterTemplate.Name } func (rc *Context) SetPolarDBXParams(param map[string]map[string]polardbxv1.Params) { rc.polardbxParamsRoleMap = param } func (rc *Context) GetPolarDBXParams() (params map[string]map[string]polardbxv1.Params) { if rc.polardbxParamsRoleMap == nil { param := make(map[string]map[string]polardbxv1.Params) param[polardbxv1.CNReadOnly] = make(map[string]polardbxv1.Params) param[polardbxv1.CNReadWrite] = make(map[string]polardbxv1.Params) param[polardbxv1.CNRestart] = make(map[string]polardbxv1.Params) param[polardbxv1.DNReadOnly] = make(map[string]polardbxv1.Params) param[polardbxv1.DNReadWrite] = make(map[string]polardbxv1.Params) param[polardbxv1.DNRestart] = make(map[string]polardbxv1.Params) param[polardbxv1.GMSReadOnly] = make(map[string]polardbxv1.Params) param[polardbxv1.GMSReadWrite] = make(map[string]polardbxv1.Params) param[polardbxv1.GMSRestart] = make(map[string]polardbxv1.Params) rc.polardbxParamsRoleMap = param } return rc.polardbxParamsRoleMap } func (rc *Context) SetPolarDBXTemplateParams(templateParams map[string]map[string]polardbxv1.TemplateParams) { rc.polardbxTemplateParams = templateParams } func (rc *Context) GetPolarDBXTemplateParams() (templateParams map[string]map[string]polardbxv1.TemplateParams) { if rc.polardbxTemplateParams == nil { templateParam := make(map[string]map[string]polardbxv1.TemplateParams) templateParam[polardbxmeta.RoleCN] = make(map[string]polardbxv1.TemplateParams) templateParam[polardbxmeta.RoleDN] = make(map[string]polardbxv1.TemplateParams) templateParam[polardbxmeta.RoleGMS] = make(map[string]polardbxv1.TemplateParams) rc.polardbxTemplateParams = templateParam } return rc.polardbxTemplateParams } func (rc *Context) GetPolarDBXParameterTemplate(name string) (*polardbxv1.PolarDBXParameterTemplate, error) { tmKey := types.NamespacedName{ Namespace: rc.polardbxKey.Namespace, Name: name, } if rc.polardbxParameterTemplate == nil { polardbxParameterTemplate, err := rc.objectCache.GetObject( rc.Context(), tmKey, &polardbxv1.PolarDBXParameterTemplate{}, ) if err != nil { return nil, err } rc.polardbxParameterTemplate = polardbxParameterTemplate.(*polardbxv1.PolarDBXParameterTemplate) } return rc.polardbxParameterTemplate, nil } func (rc *Context) MustGetPolarDBXParameterTemplate(name string) *polardbxv1.PolarDBXParameterTemplate { polardbxParameterTemplate, err := rc.GetPolarDBXParameterTemplate(name) if err != nil { panic(err) } return polardbxParameterTemplate } func (rc *Context) PolardbxParameterKey() types.NamespacedName { return rc.polardbxParameterKey } func (rc *Context) SetPolardbxParameterKey(polardbxParameterKey types.NamespacedName) { rc.polardbxParameterKey = polardbxParameterKey } func (rc *Context) GetPolarDBXParameter() (*polardbxv1.PolarDBXParameter, error) { if rc.polardbxParameter == nil { polardbxParameter, err := rc.objectCache.GetObject( rc.Context(), rc.polardbxParameterKey, &polardbxv1.PolarDBXParameter{}, ) if err != nil { return nil, err } rc.polardbxParameter = polardbxParameter.(*polardbxv1.PolarDBXParameter) rc.polardbxParameterStatus = rc.polardbxParameter.Status.DeepCopy() } return rc.polardbxParameter, nil } func (rc *Context) MustGetPolarDBXParameter() *polardbxv1.PolarDBXParameter { polardbxparameter, err := rc.GetPolarDBXParameter() if err != nil { panic(err) } return polardbxparameter } func (rc *Context) GetPolarDBXParameterTemplateName() (templateName map[string]string) { polardbxParameter := rc.MustGetPolarDBXParameter() templateName = make(map[string]string) if polardbxParameter.Spec.NodeType.CN.Name != "" { templateName[polardbxmeta.RoleCN] = polardbxParameter.Spec.NodeType.CN.Name } if polardbxParameter.Spec.NodeType.DN.Name != "" { templateName[polardbxmeta.RoleDN] = polardbxParameter.Spec.NodeType.DN.Name } if polardbxParameter.Status.ParameterSpecSnapshot.NodeType.GMS.Name != "" { templateName[polardbxmeta.RoleGMS] = polardbxParameter.Status.ParameterSpecSnapshot.NodeType.GMS.Name } return templateName } func (rc *Context) UpdatePolarDBXParameter() error { if rc.polardbxParameter == nil { return nil } // Deep copy status before updating because client.update will update // the status of object. status := rc.polardbxParameter.Status.DeepCopy() err := rc.Client().Update(rc.Context(), rc.polardbxParameter) if err != nil { return err } // Restore the status (shallow copy is enough) rc.polardbxParameter.Status = *status return nil } func (rc *Context) UpdatePolarDBXParameterStatus() error { if rc.polardbxParameterStatus == nil { return nil } err := rc.Client().Status().Update(rc.Context(), rc.polardbxParameter) if err != nil { return err } rc.polardbxParameterStatus = rc.polardbxParameter.Status.DeepCopy() return nil } func (rc *Context) IsPolarDBXParameterStatusChanged() bool { if rc.polardbxParameterStatus == nil { return false } return !equality.Semantic.DeepEqual(&rc.polardbxParameter.Status, rc.polardbxParameterStatus) } func (rc *Context) IsPolarDBXParameterChanged() bool { if rc.polardbxParameter == nil { return false } return !equality.Semantic.DeepEqual(&rc.polardbxParameter, rc.polardbxParameter) } func (rc *Context) GetPolarDBXRestarting() bool { polardbx := rc.MustGetPolarDBX() return polardbx.Status.Restarting } func (rc *Context) GetRoleToRestart() map[string]bool { if rc.roleToRestart == nil { rc.roleToRestart = make(map[string]bool) } return rc.roleToRestart } func (rc *Context) GetRoleToRestartByRole(role string) bool { return rc.roleToRestart[role] } func (rc *Context) SetRoleToRestart(roleToRestart map[string]bool) { rc.roleToRestart = roleToRestart } func NewContext(base *control.BaseReconcileContext, configLoader func() config.Config) *Context { return &Context{ BaseReconcileContext: base, configLoader: configLoader, objectCache: cache.NewObjectCache(base.Client(), base.Scheme()), } } func (rc *Context) NewSecretFromPolarDBX(secret *corev1.Secret) (*corev1.Secret, error) { backup := rc.MustGetPolarDBXBackup() data := make(map[string][]byte) for user, passwd := range secret.Data { data[user] = passwd } return &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: backup.Name, Namespace: backup.Namespace, }, Data: data, }, nil } func (rc *Context) GetFilestreamClient() (*filestream.FileClient, error) { if rc.filestreamClient == nil { hostPort := strings.SplitN(rc.Config().Store().FilestreamServiceEndpoint(), ":", 2) if len(hostPort) < 2 { return nil, errors.New("invalid filestream endpoint: " + rc.Config().Store().FilestreamServiceEndpoint()) } port, err := strconv.Atoi(hostPort[1]) if err != nil { return nil, errors.New("invalid filestream port: " + hostPort[1]) } rc.filestreamClient = filestream.NewFileClient(hostPort[0], port, nil) } return rc.filestreamClient, nil } func (rc *Context) SetPolarDBXBackupScheduleKey(key types.NamespacedName) { rc.polardbxBackupScheduleKey = key } func (rc *Context) GetPolarDBXBackupSchedule() (*polardbxv1.PolarDBXBackupSchedule, error) { if rc.polardbxBackupSchedule == nil { schedule, err := rc.objectCache.GetObject( rc.Context(), rc.polardbxBackupScheduleKey, &polardbxv1.PolarDBXBackupSchedule{}, ) if err != nil { return nil, err } rc.polardbxBackupSchedule = schedule.(*polardbxv1.PolarDBXBackupSchedule) rc.polardbxBackupScheduleStatus = rc.polardbxBackupSchedule.Status.DeepCopy() } return rc.polardbxBackupSchedule, nil } func (rc *Context) MustGetPolarDBXBackupSchedule() *polardbxv1.PolarDBXBackupSchedule { schedule, err := rc.GetPolarDBXBackupSchedule() if err != nil { panic(err) } return schedule } func (rc *Context) IsPolarDBXBackupScheduleStatusChanged() bool { if rc.polardbxBackupScheduleStatus == nil { return false } return !equality.Semantic.DeepEqual(rc.polardbxBackupScheduleStatus, &rc.polardbxBackupSchedule.Status) } func (rc *Context) UpdatePolarDBXBackupScheduleStatus() error { if rc.polardbxBackupScheduleStatus == nil { return nil } err := rc.Client().Status().Update(rc.Context(), rc.polardbxBackupSchedule) if err != nil { return err } rc.polardbxBackupScheduleStatus = rc.polardbxBackupSchedule.Status.DeepCopy() return nil } func (rc *Context) GetPolarDBXBackupListByPolarDBXName(polardbxName string) (*polardbxv1.PolarDBXBackupList, error) { return rc.GetPolarDBXBackupListByLabels(map[string]string{polardbxmeta.LabelName: polardbxName}) } func (rc *Context) GetPolarDBXBackupListByScheduleName(scheduleName string) (*polardbxv1.PolarDBXBackupList, error) { return rc.GetPolarDBXBackupListByLabels(map[string]string{polardbxmeta.LabelBackupSchedule: scheduleName}) } func (rc *Context) GetPolarDBXBackupListByLabels(labels map[string]string) (*polardbxv1.PolarDBXBackupList, error) { var backupList polardbxv1.PolarDBXBackupList err := rc.Client().List(rc.Context(), &backupList, client.InNamespace(rc.Namespace()), client.MatchingLabels(labels)) if err != nil { return nil, err } return &backupList, nil } func (rc *Context) SetBackupBinlogKey(key types.NamespacedName) { rc.backupBinlogKey = key } func (rc *Context) GetPolarDBXBackupBinlog() (*polardbxv1.PolarDBXBackupBinlog, error) { if rc.backupBinlog == nil { var backupBinlog polardbxv1.PolarDBXBackupBinlog err := rc.Client().Get(rc.Context(), rc.backupBinlogKey, &backupBinlog) if err != nil { return nil, err } rc.backupBinlog = &backupBinlog } return rc.backupBinlog, nil } func (rc *Context) MustGetPolarDBXBackupBinlog() *polardbxv1.PolarDBXBackupBinlog { backupBinlog, err := rc.GetPolarDBXBackupBinlog() if err != nil { panic(err) } return backupBinlog } func (rc *Context) UpdatePolarDbXBackupBinlog() error { backupBinlog := rc.MustGetPolarDBXBackupBinlog() err := rc.Client().Update(rc.Context(), backupBinlog) return err }