Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ vendor/
cover.out
testbin/*
custom_scripts/*
build/ss/
build/scripts/observer-prestart-wrapper.sh
insert_license.py
deploy/*secret*

Expand Down
139 changes: 139 additions & 0 deletions api/types/deepcopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,3 +285,142 @@ func (in *OBServerTemplate) DeepCopy() *OBServerTemplate {
in.DeepCopyInto(out)
return out
}

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LogServiceZoneTopology) DeepCopyInto(out *LogServiceZoneTopology) {
*out = *in
if in.Resource != nil {
in, out := &in.Resource, &out.Resource
*out = new(ResourceSpec)
(*in).DeepCopyInto(*out)
}
if in.NodeSelector != nil {
in, out := &in.NodeSelector, &out.NodeSelector
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.Affinity != nil {
in, out := &in.Affinity, &out.Affinity
*out = new(corev1.Affinity)
(*in).DeepCopyInto(*out)
}
if in.Tolerations != nil {
in, out := &in.Tolerations, &out.Tolerations
*out = make([]corev1.Toleration, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogServiceZoneTopology.
func (in *LogServiceZoneTopology) DeepCopy() *LogServiceZoneTopology {
if in == nil {
return nil
}
out := new(LogServiceZoneTopology)
in.DeepCopyInto(out)
return out
}

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LogServiceStorageSpec) DeepCopyInto(out *LogServiceStorageSpec) {
*out = *in
if in.StoreStorage != nil {
in, out := &in.StoreStorage, &out.StoreStorage
*out = new(StorageSpec)
(*in).DeepCopyInto(*out)
}
if in.LogStorage != nil {
in, out := &in.LogStorage, &out.LogStorage
*out = new(StorageSpec)
(*in).DeepCopyInto(*out)
}
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogServiceStorageSpec.
func (in *LogServiceStorageSpec) DeepCopy() *LogServiceStorageSpec {
if in == nil {
return nil
}
out := new(LogServiceStorageSpec)
in.DeepCopyInto(out)
return out
}

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ObjectStoreConfig) DeepCopyInto(out *ObjectStoreConfig) {
*out = *in
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectStoreConfig.
func (in *ObjectStoreConfig) DeepCopy() *ObjectStoreConfig {
if in == nil {
return nil
}
out := new(ObjectStoreConfig)
in.DeepCopyInto(out)
return out
}

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LogServiceZoneReplicaStatus) DeepCopyInto(out *LogServiceZoneReplicaStatus) {
*out = *in
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogServiceZoneReplicaStatus.
func (in *LogServiceZoneReplicaStatus) DeepCopy() *LogServiceZoneReplicaStatus {
if in == nil {
return nil
}
out := new(LogServiceZoneReplicaStatus)
in.DeepCopyInto(out)
return out
}

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LogServiceNodeReplicaStatus) DeepCopyInto(out *LogServiceNodeReplicaStatus) {
*out = *in
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogServiceNodeReplicaStatus.
func (in *LogServiceNodeReplicaStatus) DeepCopy() *LogServiceNodeReplicaStatus {
if in == nil {
return nil
}
out := new(LogServiceNodeReplicaStatus)
in.DeepCopyInto(out)
return out
}

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SharedStorageSpec) DeepCopyInto(out *SharedStorageSpec) {
*out = *in
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SharedStorageSpec.
func (in *SharedStorageSpec) DeepCopy() *SharedStorageSpec {
if in == nil {
return nil
}
out := new(SharedStorageSpec)
in.DeepCopyInto(out)
return out
}

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LogServiceReference) DeepCopyInto(out *LogServiceReference) {
*out = *in
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogServiceReference.
func (in *LogServiceReference) DeepCopy() *LogServiceReference {
if in == nil {
return nil
}
out := new(LogServiceReference)
in.DeepCopyInto(out)
return out
}
47 changes: 47 additions & 0 deletions api/types/logservice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
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

import corev1 "k8s.io/api/core/v1"

type LogServiceZoneTopology struct {
Zone string `json:"zone"`
Region string `json:"region"`
Replica int `json:"replica"`
Resource *ResourceSpec `json:"resource,omitempty"`
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
Affinity *corev1.Affinity `json:"affinity,omitempty"`
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
RpcPort int32 `json:"rpcPort,omitempty"`
HttpPort int32 `json:"httpPort,omitempty"`
}

type ObjectStoreConfig struct {
BucketURL string `json:"bucketURL"`
SecretRef corev1.LocalObjectReference `json:"secretRef"`
}

type LogServiceStorageSpec struct {
StoreStorage *StorageSpec `json:"storeStorage"`
LogStorage *StorageSpec `json:"logStorage"`
}

type LogServiceZoneReplicaStatus struct {
Zone string `json:"zone"`
Status string `json:"status"`
}

type LogServiceNodeReplicaStatus struct {
NodeName string `json:"nodeName"`
Status string `json:"status"`
}
2 changes: 1 addition & 1 deletion api/types/oceanbase_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ package types

type OceanbaseStorageSpec struct {
DataStorage *StorageSpec `json:"dataStorage"`
RedoLogStorage *StorageSpec `json:"redoLogStorage"`
RedoLogStorage *StorageSpec `json:"redoLogStorage,omitempty"`
LogStorage *StorageSpec `json:"logStorage"`
}
26 changes: 26 additions & 0 deletions api/types/shared_storage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
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

import corev1 "k8s.io/api/core/v1"

type SharedStorageSpec struct {
BucketURL string `json:"bucketURL"`
SecretRef corev1.LocalObjectReference `json:"secretRef"`
MaxIOPS string `json:"maxIOPS,omitempty"`
MaxBandwidth string `json:"maxBandwidth,omitempty"`
}

type LogServiceReference struct {
Name string `json:"name"`
}
6 changes: 6 additions & 0 deletions api/v1alpha1/obcluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ type OBClusterSpec struct {
ServiceAccount string `json:"serviceAccount,omitempty"`
//+kubebuilder:default=htap
Scenario string `json:"scenario,omitempty"`

//+kubebuilder:default=normal
//+kubebuilder:validation:Enum=normal;shared_storage
DeploymentMode string `json:"deploymentMode,omitempty"`
SharedStorageInfo *apitypes.SharedStorageSpec `json:"sharedStorageInfo,omitempty"`
LogServiceRef *apitypes.LogServiceReference `json:"logServiceRef,omitempty"`
}

// OBClusterStatus defines the observed state of OBCluster
Expand Down
51 changes: 39 additions & 12 deletions api/v1alpha1/obcluster_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,12 @@ func (r *OBCluster) Default() {
if r.Spec.ServiceAccount == "" {
r.Spec.ServiceAccount = "default"
}
if r.Spec.OBServerTemplate.Storage.DataStorage.StorageClass == "" ||
r.Spec.OBServerTemplate.Storage.LogStorage.StorageClass == "" ||
r.Spec.OBServerTemplate.Storage.RedoLogStorage.StorageClass == "" {
needDefaultSC := r.Spec.OBServerTemplate.Storage.DataStorage.StorageClass == "" ||
r.Spec.OBServerTemplate.Storage.LogStorage.StorageClass == ""
if r.Spec.DeploymentMode != oceanbaseconst.DeploymentModeSharedStorage && r.Spec.OBServerTemplate.Storage.RedoLogStorage != nil {
needDefaultSC = needDefaultSC || r.Spec.OBServerTemplate.Storage.RedoLogStorage.StorageClass == ""
}
if needDefaultSC {
scList := &storagev1.StorageClassList{}
err := clt.List(context.TODO(), scList)
var defaults []string
Expand All @@ -153,7 +156,7 @@ func (r *OBCluster) Default() {
if r.Spec.OBServerTemplate.Storage.LogStorage.StorageClass == "" {
r.Spec.OBServerTemplate.Storage.LogStorage.StorageClass = defaults[0]
}
if r.Spec.OBServerTemplate.Storage.RedoLogStorage.StorageClass == "" {
if r.Spec.OBServerTemplate.Storage.RedoLogStorage != nil && r.Spec.OBServerTemplate.Storage.RedoLogStorage.StorageClass == "" {
r.Spec.OBServerTemplate.Storage.RedoLogStorage.StorageClass = defaults[0]
}
}
Expand Down Expand Up @@ -183,7 +186,16 @@ func (r *OBCluster) ValidateUpdate(old runtime.Object) (admission.Warnings, erro
newResource := r.Spec.OBServerTemplate.Resource
if existOld && exist && oldMode != mode {
return nil, errors.New("mode cannot be changed")
} else if !oldCluster.SupportStaticIP() && (oldResource.Cpu != newResource.Cpu || oldResource.Memory != newResource.Memory) {
}
if oldCluster.Spec.DeploymentMode != r.Spec.DeploymentMode {
return nil, errors.New("deploymentMode cannot be changed after creation")
}
if r.Spec.DeploymentMode == oceanbaseconst.DeploymentModeSharedStorage {
if oldCluster.Spec.OBServerTemplate.Image != r.Spec.OBServerTemplate.Image {
return nil, errors.New("shared_storage mode does not support image upgrade in this version")
}
}
if !oldCluster.SupportStaticIP() && (oldResource.Cpu != newResource.Cpu || oldResource.Memory != newResource.Memory) {
return nil, errors.New("forbid to modify cpu or memory quota of non-static-ip cluster")
}
if newResource.Memory.Cmp(oldResource.Memory) < 0 || newResource.Cpu.Cmp(oldResource.Cpu) < 0 {
Expand Down Expand Up @@ -242,7 +254,7 @@ func (r *OBCluster) ValidateUpdate(old runtime.Object) (admission.Warnings, erro
if newStorage.LogStorage.Size.Cmp(oldStorage.LogStorage.Size) > 0 {
err = errors.Join(err, validateStorageClassAllowExpansion(newStorage.LogStorage.StorageClass))
}
if newStorage.RedoLogStorage.Size.Cmp(oldStorage.RedoLogStorage.Size) > 0 {
if newStorage.RedoLogStorage != nil && oldStorage.RedoLogStorage != nil && newStorage.RedoLogStorage.Size.Cmp(oldStorage.RedoLogStorage.Size) > 0 {
err = errors.Join(err, validateStorageClassAllowExpansion(newStorage.RedoLogStorage.StorageClass))
}
if err != nil {
Expand All @@ -255,7 +267,7 @@ func (r *OBCluster) ValidateUpdate(old runtime.Object) (admission.Warnings, erro
if newStorage.LogStorage.Size.Cmp(oldStorage.LogStorage.Size) < 0 {
err = errors.Join(err, field.Invalid(field.NewPath("spec").Child("observer").Child("storage").Child("logStorage").Child("size"), newStorage.LogStorage.Size.String(), "forbid to shrink log storage size"))
}
if newStorage.RedoLogStorage.Size.Cmp(oldStorage.RedoLogStorage.Size) < 0 {
if newStorage.RedoLogStorage != nil && oldStorage.RedoLogStorage != nil && newStorage.RedoLogStorage.Size.Cmp(oldStorage.RedoLogStorage.Size) < 0 {
err = errors.Join(err, field.Invalid(field.NewPath("spec").Child("observer").Child("storage").Child("redoLogStorage").Child("size"), newStorage.RedoLogStorage.Size.String(), "forbid to shrink redo log storage size"))
}
if err != nil {
Expand Down Expand Up @@ -332,8 +344,21 @@ func (r *OBCluster) validateMutation() error {
if r.Spec.OBServerTemplate.Storage.LogStorage.StorageClass == "" {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("observer").Child("storage").Child("logStorage").Child("storageClass"), "", "storageClass is required, default storage class is not found"))
}
if r.Spec.OBServerTemplate.Storage.RedoLogStorage.StorageClass == "" {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("observer").Child("storage").Child("redoLogStorage").Child("storageClass"), "", "storageClass is required, default storage class is not found"))
if r.Spec.DeploymentMode != oceanbaseconst.DeploymentModeSharedStorage {
if r.Spec.OBServerTemplate.Storage.RedoLogStorage == nil {
allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("observer").Child("storage").Child("redoLogStorage"), "redoLogStorage is required for normal deployment mode"))
} else if r.Spec.OBServerTemplate.Storage.RedoLogStorage.StorageClass == "" {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("observer").Child("storage").Child("redoLogStorage").Child("storageClass"), "", "storageClass is required, default storage class is not found"))
}
}

if r.Spec.DeploymentMode == oceanbaseconst.DeploymentModeSharedStorage {
if r.Spec.SharedStorageInfo == nil {
allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("sharedStorageInfo"), "sharedStorageInfo is required for shared_storage mode"))
}
if r.Spec.LogServiceRef == nil {
allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("logServiceRef"), "logServiceRef is required for shared_storage mode"))
}
}
if len(allErrs) != 0 {
return allErrs.ToAggregate()
Expand All @@ -343,7 +368,9 @@ func (r *OBCluster) validateMutation() error {
storageClassMapping := make(map[string]bool)
storageClassMapping[r.Spec.OBServerTemplate.Storage.DataStorage.StorageClass] = true
storageClassMapping[r.Spec.OBServerTemplate.Storage.LogStorage.StorageClass] = true
storageClassMapping[r.Spec.OBServerTemplate.Storage.RedoLogStorage.StorageClass] = true
if r.Spec.OBServerTemplate.Storage.RedoLogStorage != nil {
storageClassMapping[r.Spec.OBServerTemplate.Storage.RedoLogStorage.StorageClass] = true
}

for key := range storageClassMapping {
err := clt.Get(context.TODO(), types.NamespacedName{
Expand All @@ -370,7 +397,7 @@ func (r *OBCluster) validateMutation() error {
if r.Spec.OBServerTemplate.Storage.DataStorage.Size.Cmp(resource.MustParse(obcfg.GetConfig().Resource.MinDataDiskSize)) < 0 {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("observer").Child("storage").Child("dataStorage").Child("size"), r.Spec.OBServerTemplate.Storage.DataStorage.Size.String(), "The minimum data storage size of OBCluster is "+oceanbaseconst.MinDataDiskSize.String()))
}
if r.Spec.OBServerTemplate.Storage.RedoLogStorage.Size.Cmp(resource.MustParse(obcfg.GetConfig().Resource.MinRedoLogDiskSize)) < 0 {
if r.Spec.OBServerTemplate.Storage.RedoLogStorage != nil && r.Spec.OBServerTemplate.Storage.RedoLogStorage.Size.Cmp(resource.MustParse(obcfg.GetConfig().Resource.MinRedoLogDiskSize)) < 0 {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("observer").Child("storage").Child("redoLogStorage").Child("size"), r.Spec.OBServerTemplate.Storage.RedoLogStorage.Size.String(), "The minimum redo log storage size of OBCluster is "+oceanbaseconst.MinRedoLogDiskSize.String()))
}
if r.Spec.OBServerTemplate.Storage.LogStorage.Size.Cmp(resource.MustParse(obcfg.GetConfig().Resource.MinLogDiskSize)) < 0 {
Expand Down Expand Up @@ -401,7 +428,7 @@ func (r *OBCluster) validateMutation() error {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("observer").Child("storage").Child("dataStorage").Child("size"), r.Spec.OBServerTemplate.Storage.DataStorage.Size.String(), "The minimum size of data storage should be larger than 3 times of memory limit"))
}

if r.Spec.OBServerTemplate.Storage.RedoLogStorage.Size.AsApproximateFloat64() < 3*memoryLimit.AsApproximateFloat64() {
if r.Spec.OBServerTemplate.Storage.RedoLogStorage != nil && r.Spec.OBServerTemplate.Storage.RedoLogStorage.Size.AsApproximateFloat64() < 3*memoryLimit.AsApproximateFloat64() {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("observer").Child("storage").Child("redoLogStorage").Child("size"), r.Spec.OBServerTemplate.Storage.RedoLogStorage.Size.String(), "The minimum size of redo log storage should be larger than 3 times of memory limit"))
}
}
Expand Down
Loading
Loading