Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
4db1adc
Add shared storage and LogService support for ob-operator
Junkrat77 Jun 11, 2026
400b485
Regenerate deploy manifests and Helm chart for LogService CRDs
Junkrat77 Jun 11, 2026
a10cf33
Redact credentials from bootstrap log output
Junkrat77 Jun 11, 2026
69c9256
Rename oceanbase-ss to oceanbase-ai
Junkrat77 Jun 11, 2026
ad03780
Include LogService zone and node CRDs in kustomization
Junkrat77 Jun 11, 2026
e32616a
Untrack shared-storage build scripts and restore unrelated taskname_g…
Junkrat77 Jun 11, 2026
55f38c6
Fix DeepCopy, add Affinity/Tolerations propagation, set resource Limits
Junkrat77 Jun 12, 2026
6a2148b
Rename example/shared-storage to example/oceanbase-ai
Junkrat77 Jun 12, 2026
814c600
Change LogService default ports to rpc=50031, http=50051
Junkrat77 Jun 12, 2026
7268df0
feat: add parameters support for OBLogServiceCluster
Junkrat77 Jun 16, 2026
c5fb8ed
feat: align LogService resource, storage and parameter handling with …
Junkrat77 Jun 16, 2026
79c89c7
fix: add check before CreatePod in oblogservicenode_task
Junkrat77 Jun 17, 2026
4bd4a71
fix: quote parameter values in LogService startup command
Junkrat77 Jun 18, 2026
e6266b4
feat: implement LogService node recovery aligned with Observer pattern
Junkrat77 Jun 18, 2026
69e3466
fix: add replica modification sync from LogService Cluster to Zone
Junkrat77 Jun 18, 2026
d57c0c9
feat: add ls_ctrl add/delete ln to Zone AddNode/DeleteNode flows
Junkrat77 Jun 22, 2026
4f78910
fix: prevent duplicate add ln calls with annotation-based registratio…
Junkrat77 Jun 22, 2026
77b0304
feat: block zone add/remove for LogService via webhook validation
Junkrat77 Jun 22, 2026
e23c529
style: fix golangci-lint issues in LogService packages
Junkrat77 Jun 22, 2026
602052e
refactor: rename observer image from oceanbase-ai to oceanbase-ai-clo…
Junkrat77 Jun 22, 2026
830a296
refactor: remove region field from LogService CRDs
Junkrat77 Jun 23, 2026
98891fd
feat(helper): prepare wallet dir for shared_storage observer start
Junkrat77 Jun 25, 2026
87d1ec3
chore: sync LogService CRD schema in deploy manifests
Junkrat77 Jun 26, 2026
e3a04a7
refactor: nest image/resource/storage under spec.logService in OBLogS…
Junkrat77 Jun 26, 2026
79f4db1
feat: adapt LogService to oblogservice 1.3.0 and switch to service-IP…
Junkrat77 Jun 26, 2026
979745c
fix: retry ls_ctrl bootstrap to absorb ServiceIP DNAT latency
Junkrat77 Jun 29, 2026
d79645c
feat(logservice): add BootstrapReady status and oceanbase-helper logs…
Junkrat77 Jun 29, 2026
8e0c6ef
feat(logservice): align OBLogServiceNode with annotation-driven servi…
Junkrat77 Jun 30, 2026
3fd0ce2
fix(logservice): preserve PodIP across node recovery and wait for pod…
Junkrat77 Jul 8, 2026
1e87257
fix(logservice): keep last known PodIP when pod reports empty IP
Junkrat77 Jul 8, 2026
c6a8f2e
fix(logservice): address review findings B1-B4
Junkrat77 Jul 8, 2026
5d9d61b
fix(logservice): persist PodIP before Running and stabilize node dele…
Junkrat77 Jul 8, 2026
a32dab0
fix(logservice): eliminate RunJob false-success and harden CreatePVC
Junkrat77 Jul 8, 2026
5fabc4c
fix(lint): resolve golangci-lint issues in logservice code
Junkrat77 Jul 13, 2026
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
159 changes: 159 additions & 0 deletions api/types/deepcopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,3 +285,162 @@ 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 *LogServiceTemplate) DeepCopyInto(out *LogServiceTemplate) {
*out = *in
in.Resource.DeepCopyInto(&out.Resource)
if in.Storage != nil {
in, out := &in.Storage, &out.Storage
*out = (*in).DeepCopy()
}
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogServiceTemplate.
func (in *LogServiceTemplate) DeepCopy() *LogServiceTemplate {
if in == nil {
return nil
}
out := new(LogServiceTemplate)
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
}
52 changes: 52 additions & 0 deletions api/types/logservice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
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 LogServiceTemplate struct {
Image string `json:"image"`
Resource ResourceSpec `json:"resource"`
Storage *LogServiceStorageSpec `json:"storage"`
}

type LogServiceZoneTopology struct {
Zone string `json:"zone"`
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