polardbxoperator/pkg/operator/v1/polardbx/reconcile/context.go

1816 lines
50 KiB
Go

/*
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
}