Skip to content

Commit

Permalink
Feature: standby tenant (#81)
Browse files Browse the repository at this point in the history
Support:
Change root's password of specific tenant
Create a standby tenant from backup restore
Create an empty standby tenant and follow a primary tenant's log stream
Check whether the log of a tenant is of integrity
Failover by activating standby tenant
Switchover two tenants and over back
  • Loading branch information
powerfooI authored Oct 9, 2023
1 parent 5548768 commit a468414
Show file tree
Hide file tree
Showing 68 changed files with 3,107 additions and 305 deletions.
14 changes: 4 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -210,15 +210,9 @@ commit-hook: $(GOLANGCI_LINT) ## Install commit hook.
.PHONY: connect
connect:
ifdef TENANT
mysql -h$(shell kubectl get pods -o jsonpath='{.items[0].status.podIP}') -P2881 -A -uroot@${TENANT}
$(eval nodeHost = $(shell kubectl get pods -o jsonpath='{.items[1].status.podIP}'))
$(eval pwd = $(shell kubectl get secret $(shell kubectl get obtenant ${TENANT} -o jsonpath='{.status.credentials.root}') -o jsonpath='{.data.password}' | base64 -d))
$(if $(strip $(pwd)), mysql -h$(nodeHost) -P2881 -A -uroot@${TENANT} -p$(pwd) -Doceanbase, mysql -h$(nodeHost) -P2881 -A -uroot@${TENANT} -Doceanbase)
else
mysql -h$(shell kubectl get pods -o jsonpath='{.items[0].status.podIP}') -P2881 -A -uroot -p
endif

.PHONY: connectob
connectob:
ifdef TENANT
mysql -h$(shell kubectl get pods -o jsonpath='{.items[0].status.podIP}') -P2881 -A -uroot@${TENANT} -Doceanbase
else
mysql -h$(shell kubectl get pods -o jsonpath='{.items[0].status.podIP}') -P2881 -A -uroot -p -Doceanbase
mysql -h$(shell kubectl get pods -o jsonpath='{.items[1].status.podIP}') -P2881 -A -uroot -p -Doceanbase
endif
4 changes: 4 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,8 @@ resources:
kind: OBTenantOperation
path: github.com/oceanbase/ob-operator/api/v1alpha1
version: v1alpha1
webhooks:
defaulting: true
validation: true
webhookVersion: v1
version: "3"
67 changes: 26 additions & 41 deletions api/constants/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,62 +12,47 @@ See the Mulan PSL v2 for more details.

package constants

type BackupJobType string
import "github.com/oceanbase/ob-operator/api/types"

const (
BackupJobTypeFull BackupJobType = "FULL"
BackupJobTypeIncr BackupJobType = "INC"
BackupJobTypeClean BackupJobType = "CLEAN"
BackupJobTypeArchive BackupJobType = "ARCHIVE"
BackupJobTypeFull types.BackupJobType = "FULL"
BackupJobTypeIncr types.BackupJobType = "INC"
BackupJobTypeClean types.BackupJobType = "CLEAN"
BackupJobTypeArchive types.BackupJobType = "ARCHIVE"
)

type BackupJobStatus string

const (
BackupJobStatusRunning BackupJobStatus = "RUNNING"
BackupJobStatusInitializing BackupJobStatus = "INITIALIZING"
BackupJobStatusSuccessful BackupJobStatus = "SUCCESSFUL"
BackupJobStatusFailed BackupJobStatus = "FAILED"
BackupJobStatusCanceled BackupJobStatus = "CANCELED"
BackupJobStatusStopped BackupJobStatus = "STOPPED"
BackupJobStatusSuspend BackupJobStatus = "SUSPEND"
BackupJobStatusRunning types.BackupJobStatus = "RUNNING"
BackupJobStatusInitializing types.BackupJobStatus = "INITIALIZING"
BackupJobStatusSuccessful types.BackupJobStatus = "SUCCESSFUL"
BackupJobStatusFailed types.BackupJobStatus = "FAILED"
BackupJobStatusCanceled types.BackupJobStatus = "CANCELED"
BackupJobStatusStopped types.BackupJobStatus = "STOPPED"
BackupJobStatusSuspend types.BackupJobStatus = "SUSPEND"
)

type BackupPolicyStatusType string

const (
BackupPolicyStatusPreparing BackupPolicyStatusType = "PREPARING"
BackupPolicyStatusPrepared BackupPolicyStatusType = "PREPARED"
BackupPolicyStatusRunning BackupPolicyStatusType = "RUNNING"
BackupPolicyStatusFailed BackupPolicyStatusType = "FAILED"
BackupPolicyStatusPausing BackupPolicyStatusType = "PAUSING"
BackupPolicyStatusPaused BackupPolicyStatusType = "PAUSED"
BackupPolicyStatusStopped BackupPolicyStatusType = "STOPPED"
BackupPolicyStatusResuming BackupPolicyStatusType = "RESUMING"
BackupPolicyStatusPreparing types.BackupPolicyStatusType = "PREPARING"
BackupPolicyStatusPrepared types.BackupPolicyStatusType = "PREPARED"
BackupPolicyStatusRunning types.BackupPolicyStatusType = "RUNNING"
BackupPolicyStatusFailed types.BackupPolicyStatusType = "FAILED"
BackupPolicyStatusPausing types.BackupPolicyStatusType = "PAUSING"
BackupPolicyStatusPaused types.BackupPolicyStatusType = "PAUSED"
BackupPolicyStatusStopped types.BackupPolicyStatusType = "STOPPED"
BackupPolicyStatusResuming types.BackupPolicyStatusType = "RESUMING"
)

type BackupDestination struct {
Type BackupDestType `json:"type,omitempty"`
Path string `json:"path,omitempty"`
}

type BackupDestType string

const (
BackupDestTypeOSS BackupDestType = "OSS"
BackupDestTypeNFS BackupDestType = "NFS"
BackupDestTypeOSS types.BackupDestType = "OSS"
BackupDestTypeNFS types.BackupDestType = "NFS"
)

type LogArchiveDestState string

const (
LogArchiveDestStateEnable LogArchiveDestState = "ENABLE"
LogArchiveDestStateDefer LogArchiveDestState = "DEFER"
LogArchiveDestStateEnable types.LogArchiveDestState = "ENABLE"
LogArchiveDestStateDefer types.LogArchiveDestState = "DEFER"
)

type ArchiveBinding string

const (
ArchiveBindingOptional = "Optional"
ArchiveBindingMandatory = "Mandatory"
ArchiveBindingOptional types.ArchiveBinding = "Optional"
ArchiveBindingMandatory types.ArchiveBinding = "Mandatory"
)
16 changes: 8 additions & 8 deletions api/constants/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ See the Mulan PSL v2 for more details.

package constants

type RestoreJobStatus string
import "github.com/oceanbase/ob-operator/api/types"

const (
RestoreJobStarting RestoreJobStatus = "STARTING"
RestoreJobRunning RestoreJobStatus = "RUNNING"
RestoreJobFailed RestoreJobStatus = "FAILED"
RestoreJobSuccessful RestoreJobStatus = "SUCCESSFUL"
RestoreJobCanceled RestoreJobStatus = "CANCELED"
RestoreJobStarting types.RestoreJobStatus = "STARTING"
RestoreJobRunning types.RestoreJobStatus = "RUNNING"
RestoreJobFailed types.RestoreJobStatus = "FAILED"
RestoreJobSuccessful types.RestoreJobStatus = "SUCCESSFUL"
RestoreJobCanceled types.RestoreJobStatus = "CANCELED"

RestoreJobStatusActivating RestoreJobStatus = "ACTIVATING"
RestoreJobStatusReplaying RestoreJobStatus = "REPLAYING"
RestoreJobStatusActivating types.RestoreJobStatus = "ACTIVATING"
RestoreJobStatusReplaying types.RestoreJobStatus = "REPLAYING"
)
25 changes: 11 additions & 14 deletions api/constants/tenant.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,23 @@ See the Mulan PSL v2 for more details.

package constants

type TenantRole string
import "github.com/oceanbase/ob-operator/api/types"

const (
TenantRolePrimary TenantRole = "PRIMARY"
TenantRoleStandby TenantRole = "STANDBY"
TenantRolePrimary types.TenantRole = "PRIMARY"
TenantRoleStandby types.TenantRole = "STANDBY"
)

type TenantOperationType string

const (
TenantOpSwitchover TenantOperationType = "SWITCHOVER"
TenantOpFailover TenantOperationType = "FAILOVER"
TenantOpChangePwd TenantOperationType = "CHANGE_PASSWORD"
TenantOpSwitchover types.TenantOperationType = "SWITCHOVER"
TenantOpFailover types.TenantOperationType = "FAILOVER"
TenantOpChangePwd types.TenantOperationType = "CHANGE_PASSWORD"
)

type TenantOperationStatus string

const (
TenantOpStarting TenantOperationStatus = "STARTING"
TenantOpRunning TenantOperationStatus = "RUNNING"
TenantOpSuccessful TenantOperationStatus = "SUCCESSFUL"
TenantOpFailed TenantOperationStatus = "FAILED"
TenantOpStarting types.TenantOperationStatus = "STARTING"
TenantOpRunning types.TenantOperationStatus = "RUNNING"
TenantOpSuccessful types.TenantOperationStatus = "SUCCESSFUL"
TenantOpFailed types.TenantOperationStatus = "FAILED"
TenantOpReverting types.TenantOperationStatus = "REVERTING"
)
30 changes: 30 additions & 0 deletions api/types/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
Copyright (c) 2023 OceanBase
ob-operator is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
*/

package types

type BackupJobType string
type BackupJobStatus string
type BackupPolicyStatusType string
type BackupDestType string
type LogArchiveDestState string
type ArchiveBinding string
type BackupDestination struct {
Type BackupDestType `json:"type,omitempty"`
Path string `json:"path,omitempty"`
}

type RestoreJobStatus string

type TenantRole string
type TenantOperationStatus string
type TenantOperationType string
2 changes: 2 additions & 0 deletions api/v1alpha1/obcluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ type OBClusterStatus struct {

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.status"
//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"

// OBCluster is the Schema for the obclusters API
type OBCluster struct {
Expand Down
4 changes: 4 additions & 0 deletions api/v1alpha1/observer_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ type OBServerStatus struct {

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name="PodIP",type="string",JSONPath=".status.podIp"
//+kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.status"
//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
//+kubebuilder:printcolumn:name="OBStatus",type="string",JSONPath=".status.obStatus"

// OBServer is the Schema for the observers API
type OBServer struct {
Expand Down
14 changes: 8 additions & 6 deletions api/v1alpha1/obtenant_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/oceanbase/ob-operator/api/constants"
apitypes "github.com/oceanbase/ob-operator/api/types"
)

// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
Expand All @@ -46,9 +46,9 @@ type OBTenantSpec struct {
Pools []ResourcePoolSpec `json:"pools"`

//+kubebuilder:default=PRIMARY
TenantRole constants.TenantRole `json:"tenantRole,omitempty"`
Source *TenantSourceSpec `json:"source,omitempty"`
Credentials TenantCredentials `json:"credentials,omitempty"`
TenantRole apitypes.TenantRole `json:"tenantRole,omitempty"`
Source *TenantSourceSpec `json:"source,omitempty"`
Credentials TenantCredentials `json:"credentials,omitempty"`
}

type TenantCredentials struct {
Expand Down Expand Up @@ -113,8 +113,9 @@ type OBTenantStatus struct {
OperationContext *OperationContext `json:"operationContext,omitempty"`
TenantRecordInfo TenantRecordInfo `json:"tenantRecordInfo,omitempty"`

TenantRole constants.TenantRole `json:"tenantRole,omitempty"`
Source *TenantSourceStatus `json:"source,omitempty"`
TenantRole apitypes.TenantRole `json:"tenantRole,omitempty"`
Source *TenantSourceStatus `json:"source,omitempty"`
Credentials TenantCredentials `json:"credentials,omitempty"`
}

type TenantSourceStatus struct {
Expand Down Expand Up @@ -196,6 +197,7 @@ type TenantRecordInfo struct {
//+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name="status",type="string",JSONPath=".status.status"
//+kubebuilder:printcolumn:name="tenantName",type="string",JSONPath=".spec.tenantName"
//+kubebuilder:printcolumn:name="tenantRole",type="string",JSONPath=".status.tenantRole"
//+kubebuilder:printcolumn:name="clusterName",type="string",JSONPath=".spec.obcluster"
//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
//+kubebuilder:printcolumn:name="locality",type="string",JSONPath=".status.tenantRecordInfo.locality",priority=1
Expand Down
34 changes: 33 additions & 1 deletion api/v1alpha1/obtenant_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,15 @@ limitations under the License.
package v1alpha1

import (
"context"

apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/validation/field"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
Expand All @@ -29,9 +34,11 @@ import (
)

// log is for logging in this package.
var _ = logf.Log.WithName("obtenant-resource")
var tenantlog = logf.Log.WithName("obtenant-resource")
var tenantClt client.Client

func (r *OBTenant) SetupWebhookWithManager(mgr ctrl.Manager) error {
tenantClt = mgr.GetClient()
return ctrl.NewWebhookManagedBy(mgr).
For(r).
Complete()
Expand All @@ -43,6 +50,23 @@ var _ webhook.Defaulter = &OBTenant{}

// Default implements webhook.Defaulter so a webhook will be registered for the type
func (r *OBTenant) Default() {
cluster := &OBCluster{}
err := tenantClt.Get(context.Background(), types.NamespacedName{
Namespace: r.GetNamespace(),
Name: r.Spec.ClusterName,
}, cluster)
if err != nil {
tenantlog.Error(err, "Failed to get cluster")
} else {
tenantlog.Info("Get cluster", "cluster", cluster)
r.SetOwnerReferences([]metav1.OwnerReference{{
APIVersion: cluster.APIVersion,
Kind: cluster.Kind,
Name: cluster.GetObjectMeta().GetName(),
UID: cluster.GetObjectMeta().GetUID(),
}})
}

if r.Spec.TenantRole == "" {
r.Spec.TenantRole = constants.TenantRolePrimary
}
Expand All @@ -69,6 +93,13 @@ func (r *OBTenant) ValidateUpdate(old runtime.Object) (admission.Warnings, error
func (r *OBTenant) validateMutation() error {
var allErrs field.ErrorList

if r.Spec.Credentials.Root == "" {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("credentials").Child("root"), r.Spec.Credentials.Root, "Root password user secretref must be set"))
}
if r.Spec.Credentials.StandbyRO == "" {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("credentials").Child("standbyRo"), r.Spec.Credentials.StandbyRO, "Standby read-only user password secretref must be set"))
}

// 1. Standby tenant must have a source
if r.Spec.TenantRole == constants.TenantRoleStandby {
if r.Spec.Source == nil {
Expand All @@ -77,6 +108,7 @@ func (r *OBTenant) validateMutation() error {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("tenantRole"), r.Spec.TenantRole, "Standby must have a source option, but both restore and tenantRef are nil now"))
}
}

// 2. Restore until with some limit must have a limit key
if r.Spec.Source != nil && r.Spec.Source.Restore != nil {
untilSpec := r.Spec.Source.Restore.Until
Expand Down
28 changes: 14 additions & 14 deletions api/v1alpha1/obtenantbackup_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

constants "github.com/oceanbase/ob-operator/api/constants"
apitypes "github.com/oceanbase/ob-operator/api/types"
"github.com/oceanbase/ob-operator/pkg/oceanbase/model"
)

Expand All @@ -30,26 +30,26 @@ type OBTenantBackupSpec struct {
// Important: Run "make" to regenerate code after modifying this file

// Foo is an example field of OBTenantBackup. Edit obtenantbackup_types.go to remove/update
Type constants.BackupJobType `json:"type"`
TenantName string `json:"tenantName"`
TenantSecret string `json:"tenantSecret"`
ObClusterName string `json:"obClusterName"`
Path string `json:"path,omitempty"`
Type apitypes.BackupJobType `json:"type"`
TenantName string `json:"tenantName"`
TenantSecret string `json:"tenantSecret"`
ObClusterName string `json:"obClusterName"`
Path string `json:"path,omitempty"`
}

// +kubebuilder:object:generate=false
// OBTenantBackupStatus defines the observed state of OBTenantBackup
type OBTenantBackupStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
Status constants.BackupJobStatus `json:"status"`
Progress string `json:"progress,omitempty"`
OperationContext *OperationContext `json:"operationContext,omitempty"`
StartedAt string `json:"startedAt,omitempty"`
EndedAt string `json:"endedAt,omitempty"`
BackupJob *model.OBBackupJob `json:"backupJob,omitempty"`
ArchiveLogJob *model.OBArchiveLogJob `json:"archiveLogJob,omitempty"`
DataCleanJob *model.OBBackupCleanJob `json:"dataCleanJob,omitempty"`
Status apitypes.BackupJobStatus `json:"status"`
Progress string `json:"progress,omitempty"`
OperationContext *OperationContext `json:"operationContext,omitempty"`
StartedAt string `json:"startedAt,omitempty"`
EndedAt string `json:"endedAt,omitempty"`
BackupJob *model.OBBackupJob `json:"backupJob,omitempty"`
ArchiveLogJob *model.OBArchiveLogJob `json:"archiveLogJob,omitempty"`
DataCleanJob *model.OBBackupCleanJob `json:"dataCleanJob,omitempty"`
}

// fix: implementation of DeepCopyInto needed by zz_generated.deepcopy.go
Expand Down
Loading

0 comments on commit a468414

Please sign in to comment.