From d6b3e8f02d59728f14222db1cbe8114023d5f12c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=83=9C=ED=99=98=20=EB=B0=95?= Date: Sat, 1 Aug 2026 16:09:24 +0900 Subject: [PATCH] =?UTF-8?q?fix(api):=20v1alpha2=20Hub=20=ED=95=84=EB=93=9C?= =?UTF-8?q?=20=EC=9C=A0=EC=8B=A4=203=EA=B1=B4=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B3=B5=EC=9B=90=20+=20=EC=A0=84=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EB=AC=B4=EC=86=90=EC=8B=A4=20=EC=99=95=EB=B3=B5=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #314 에서 ValkeyBackup/ValkeyRestore 의 유실 필드 2건을 고쳤으나, 그때는 "이미 테스트가 있던" Valkey/ValkeyCluster/BackupTarget 을 재검사하지 않았다. 필드 단위 수동 단언은 **빠뜨린 필드를 잡지 못한다** — 실제로 3건이 더 숨어 있었다: ValkeySpec.slowLog → Hub 에 SlowLogSpec 타입 자체가 없음 ValkeyClusterSpec.slowLog → 동일 ValkeyBackupTargetSpec.gcs → Hub 에 GCSSpec 계열 타입 없음 ValkeyBackupTargetSpec.azure → Hub 에 AzureSpec 계열 타입 없음 백업 target 이 GCS/Azure 면 **대상 자체가 사라진다**. 변환이 JSON byte-copy 라 대응 태그가 없으면 에러 없이 소실된다 (#314 와 동일 기전). 누락 타입·필드를 Hub 에 복원해 **5 타입 전 필드 패리티** 를 달성했다(순수 추가, 기존 CR 영향 0). 그리고 같은 실수가 재발하지 않도록 테스트를 필드 열거형에서 **전 필드 왕복 비교**로 바꾼다 — v1alpha1 객체를 JSON 으로 채워 왕복시킨 뒤 원본 JSON 과 완전 일치를 요구하므로, 앞으로 v1alpha1 에 필드가 추가되면 Hub 에 함께 넣지 않는 한 이 테스트가 막는다. verify: - 수정 전: 6 케이스 중 Valkey/ValkeyCluster/BackupTarget 왕복 불일치 검출 (FAIL) - 수정 후: 6/6 PASS - 필드 패리티 스캔: 5 타입 전부 "유실 필드 None" - make test 회귀 0 (총 41.5%) Signed-off-by: 태환 박 Co-Authored-By: Claude Opus 5 --- api/v1alpha1/conversion_parity_test.go | 126 ++++++++++++++++++ api/v1alpha2/common_types.go | 19 +++ api/v1alpha2/valkey_types.go | 5 + api/v1alpha2/valkeybackuptarget_types.go | 70 ++++++++++ api/v1alpha2/valkeycluster_types.go | 5 + api/v1alpha2/zz_generated.deepcopy.go | 97 ++++++++++++++ ...cache.keiailab.io_valkeybackuptargets.yaml | 61 +++++++++ .../cache.keiailab.io_valkeyclusters.yaml | 17 +++ .../crds/cache.keiailab.io_valkeys.yaml | 17 +++ ...cache.keiailab.io_valkeybackuptargets.yaml | 61 +++++++++ .../cache.keiailab.io_valkeyclusters.yaml | 17 +++ .../crd/bases/cache.keiailab.io_valkeys.yaml | 17 +++ 12 files changed, 512 insertions(+) create mode 100644 api/v1alpha1/conversion_parity_test.go diff --git a/api/v1alpha1/conversion_parity_test.go b/api/v1alpha1/conversion_parity_test.go new file mode 100644 index 00000000..08e7e8da --- /dev/null +++ b/api/v1alpha1/conversion_parity_test.go @@ -0,0 +1,126 @@ +/* +Copyright 2026 Keiailab. + +Licensed under the MIT License. See the LICENSE file for details. +*/ + +// 필드 단위 수동 단언은 **빠뜨린 필드를 잡지 못한다** — 실제로 그렇게 놓쳤다: +// conversion_test.go 가 Valkey/ValkeyCluster 를 "테스트하고 있었는데도" +// spec.slowLog 가 Hub(v1alpha2)에 없어 조용히 유실되고 있었고, +// ValkeyBackupTarget 의 gcs/azure 도 마찬가지였다. +// +// 그래서 이 파일은 필드를 **열거하지 않는다**. v1alpha1 객체를 JSON 으로 채운 뒤 +// v1alpha2 로 변환하고 되돌려서, 원본 JSON 과 **완전히 같은지**만 본다. 변환이 +// JSON byte-copy 이므로 Hub 에 대응 태그가 없는 필드는 왕복에서 사라지고 즉시 실패한다. +// 앞으로 v1alpha1 에 필드가 추가되면 Hub 에 함께 넣지 않는 한 이 테스트가 막는다. +package v1alpha1_test + +import ( + "encoding/json" + "testing" + + "sigs.k8s.io/controller-runtime/pkg/conversion" + + "github.com/keiailab/valkey-operator/api/v1alpha1" + "github.com/keiailab/valkey-operator/api/v1alpha2" +) + +// spoke — v1alpha1 쪽 타입이 만족해야 하는 최소 인터페이스. +type spoke interface { + ConvertTo(conversion.Hub) error + ConvertFrom(conversion.Hub) error +} + +// assertLosslessRoundTrip — src(JSON 으로 채워진 v1alpha1) → hub → back 왕복 후 +// JSON 이 원본과 동일한지 검증한다. +func assertLosslessRoundTrip(t *testing.T, name string, srcJSON string, src, back spoke, hub conversion.Hub) { + t.Helper() + + if err := json.Unmarshal([]byte(srcJSON), src); err != nil { + t.Fatalf("%s: 픽스처 unmarshal: %v", name, err) + } + if err := src.ConvertTo(hub); err != nil { + t.Fatalf("%s: ConvertTo: %v", name, err) + } + if err := back.ConvertFrom(hub); err != nil { + t.Fatalf("%s: ConvertFrom: %v", name, err) + } + + want, err := json.Marshal(src) + if err != nil { + t.Fatalf("%s: marshal src: %v", name, err) + } + got, err := json.Marshal(back) + if err != nil { + t.Fatalf("%s: marshal back: %v", name, err) + } + if string(want) != string(got) { + t.Errorf("%s: 왕복에서 내용이 바뀌었다 — Hub(v1alpha2)에 대응 필드가 없을 가능성이 높다.\n원본: %s\n왕복: %s", + name, want, got) + } +} + +func TestConversion_전_타입_왕복이_무손실이다(t *testing.T) { + cases := []struct { + name string + srcJSON string + src spoke + back spoke + hub conversion.Hub + }{ + { + name: "Valkey", + srcJSON: `{"metadata":{"name":"vk","namespace":"ns"},"spec":{ + "mode":"Replication","replicas":3, + "version":{"version":"8.1.7","image":"docker.io/valkey/valkey"}, + "slowLog":{"thresholdMicros":5000,"maxEntries":256}, + "autoFailover":true}}`, + src: &v1alpha1.Valkey{}, back: &v1alpha1.Valkey{}, hub: &v1alpha2.Valkey{}, + }, + { + name: "ValkeyCluster", + srcJSON: `{"metadata":{"name":"vkc","namespace":"ns"},"spec":{ + "shards":3,"replicasPerShard":0, + "version":{"version":"8.1.7"}, + "slowLog":{"thresholdMicros":7500,"maxEntries":64}}}`, + src: &v1alpha1.ValkeyCluster{}, back: &v1alpha1.ValkeyCluster{}, hub: &v1alpha2.ValkeyCluster{}, + }, + { + name: "ValkeyBackup", + srcJSON: `{"metadata":{"name":"b","namespace":"ns"},"spec":{ + "clusterRef":{"kind":"ValkeyCluster","name":"vkc"}, + "type":"Full","storageSize":"10Gi","retainPVC":true,"ttl":"168h", + "volumeSnapshotClassName":"csi-snapclass"}}`, + src: &v1alpha1.ValkeyBackup{}, back: &v1alpha1.ValkeyBackup{}, hub: &v1alpha2.ValkeyBackup{}, + }, + { + name: "ValkeyBackupTarget/GCS", + srcJSON: `{"metadata":{"name":"t-gcs","namespace":"ns"},"spec":{ + "type":"GCS", + "gcs":{"bucket":"bk","prefix":"c/","credentialsSecretRef":{"name":"s","serviceAccountJSONKey":"key.json"}}}}`, + src: &v1alpha1.ValkeyBackupTarget{}, back: &v1alpha1.ValkeyBackupTarget{}, hub: &v1alpha2.ValkeyBackupTarget{}, + }, + { + name: "ValkeyBackupTarget/Azure", + srcJSON: `{"metadata":{"name":"t-az","namespace":"ns"},"spec":{ + "type":"Azure", + "azure":{"accountName":"acct","container":"c","prefix":"p/","serviceURL":"https://x", + "credentialsSecretRef":{"name":"s","accountKeyKey":"AZURE_STORAGE_ACCOUNT_KEY"}}}}`, + src: &v1alpha1.ValkeyBackupTarget{}, back: &v1alpha1.ValkeyBackupTarget{}, hub: &v1alpha2.ValkeyBackupTarget{}, + }, + { + name: "ValkeyRestore", + srcJSON: `{"metadata":{"name":"r","namespace":"ns"},"spec":{ + "clusterRef":{"kind":"ValkeyCluster","name":"vkc"}, + "restoreType":"AOF","pointInTime":"2026-07-31T10:00:00Z", + "source":{"pvc":{"name":"snap"}}}}`, + src: &v1alpha1.ValkeyRestore{}, back: &v1alpha1.ValkeyRestore{}, hub: &v1alpha2.ValkeyRestore{}, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assertLosslessRoundTrip(t, tc.name, tc.srcJSON, tc.src, tc.back, tc.hub) + }) + } +} diff --git a/api/v1alpha2/common_types.go b/api/v1alpha2/common_types.go index 0f3619ba..3fb4acc2 100644 --- a/api/v1alpha2/common_types.go +++ b/api/v1alpha2/common_types.go @@ -485,3 +485,22 @@ type PersistencePolicy struct { // +kubebuilder:default="everysec" AOFAppendFsync string `json:"aofAppendFsync,omitempty"` } + +// SlowLogSpec — Valkey SLOWLOG 임계값 + 보존 entry 수. +// +// Threshold 보다 오래 걸린 명령은 SLOWLOG 에 기록 — `valkey-cli SLOWLOG GET` 로 조회. +// redis_exporter sidecar 가 자동으로 redis_slowlog_length metric 으로 노출. +// +// v1alpha1 에 있으나 Hub 에 없어 변환 시 조용히 유실되던 타입이다 +// (conversion 은 JSON byte-copy 라 대응 태그가 없으면 그냥 사라진다). +type SlowLogSpec struct { + // 단위: microseconds. 0 = SLOWLOG 비활성. -1 = 모든 명령 기록 (debug 만). + // +kubebuilder:default=10000 + // +optional + ThresholdMicros int64 `json:"thresholdMicros,omitempty"` + + // 보존 entry 수 — FIFO. 초과 시 가장 오래된 entry 폐기. + // +kubebuilder:default=128 + // +optional + MaxEntries int32 `json:"maxEntries,omitempty"` +} diff --git a/api/v1alpha2/valkey_types.go b/api/v1alpha2/valkey_types.go index 85b44497..000664d1 100644 --- a/api/v1alpha2/valkey_types.go +++ b/api/v1alpha2/valkey_types.go @@ -107,6 +107,11 @@ type ValkeySpec struct { // +optional Autoscaling *AutoscalingSpec `json:"autoscaling,omitempty"` + // SlowLog — Valkey SLOWLOG 임계값 + 보존 entry 수 설정. + // nil 이면 valkey 기본값 (10ms / 128 entries) 사용. + // +optional + SlowLog *SlowLogSpec `json:"slowLog,omitempty"` + // +optional ExternalReplica *ExternalReplicaSpec `json:"externalReplica,omitempty"` diff --git a/api/v1alpha2/valkeybackuptarget_types.go b/api/v1alpha2/valkeybackuptarget_types.go index 222ed81c..3aa9acac 100644 --- a/api/v1alpha2/valkeybackuptarget_types.go +++ b/api/v1alpha2/valkeybackuptarget_types.go @@ -89,6 +89,14 @@ type ValkeyBackupTargetSpec struct { // +optional S3 *S3Spec `json:"s3,omitempty"` + // Type=GCS 시 필수. + // +optional + GCS *GCSSpec `json:"gcs,omitempty"` + + // Type=Azure 시 필수. + // +optional + Azure *AzureSpec `json:"azure,omitempty"` + // Retention — 이 target 을 참조하는 완료된 ValkeyBackup 의 자동 보존 정책 // (cross-region backup lifecycle, ROADMAP 2.x). v1alpha1 reconcile hub 미러. // +optional @@ -166,3 +174,65 @@ func init() { } func (t *ValkeyBackupTarget) GetConditions() *[]metav1.Condition { return &t.Status.Conditions } + +// 아래 GCS/Azure 계열 타입은 v1alpha1 에 있으나 Hub 에 없어 변환 시 조용히 +// 유실되던 것들이다 — 백업 target 이 GCS/Azure 면 대상 자체가 사라진다. +// GCSSpec — Google Cloud Storage 외부 저장 정의 (cloud.google.com/go/storage v1.62.1). +// +// 자격증명: service account JSON key 를 Secret 에 저장. backup Job 의 환경변수 +// GOOGLE_APPLICATION_CREDENTIALS 가 가리키는 파일에 mount. +type GCSSpec struct { + // 버킷 이름. 사전 생성 필요. + Bucket string `json:"bucket"` + + // object key prefix. 예: "cluster-A/". + // +optional + Prefix string `json:"prefix,omitempty"` + + // 자격증명 Secret 참조 (service account JSON). + CredentialsSecretRef GCSCredentialsSecretRef `json:"credentialsSecretRef"` +} + +// GCSCredentialsSecretRef — service account JSON 이 들어 있는 Secret. +type GCSCredentialsSecretRef struct { + // Secret 이름. + Name string `json:"name"` + + // service account JSON 이 들어 있는 key 이름. 기본 "key.json". + // +kubebuilder:default="key.json" + // +optional + ServiceAccountJSONKey string `json:"serviceAccountJSONKey,omitempty"` +} + +// AzureSpec — Azure Blob Storage 외부 저장 정의 +// (github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.x). +type AzureSpec struct { + // storage account 이름. e.g. "mystorageacct". + AccountName string `json:"accountName"` + + // container 이름 (S3 의 bucket 등가). 사전 생성 필요. + Container string `json:"container"` + + // blob name prefix. 예: "cluster-A/". + // +optional + Prefix string `json:"prefix,omitempty"` + + // service URL override. 기본: https://.blob.core.windows.net. + // Azure China / Government cloud / Azurite (test) 용. + // +optional + ServiceURL string `json:"serviceURL,omitempty"` + + // 자격증명 Secret 참조 (account key 또는 SAS token). + CredentialsSecretRef AzureCredentialsSecretRef `json:"credentialsSecretRef"` +} + +// AzureCredentialsSecretRef — Azure storage account key 또는 SAS. +type AzureCredentialsSecretRef struct { + // Secret 이름. + Name string `json:"name"` + + // account key 가 들어 있는 key 이름. 기본 "AZURE_STORAGE_ACCOUNT_KEY". + // +kubebuilder:default="AZURE_STORAGE_ACCOUNT_KEY" + // +optional + AccountKeyKey string `json:"accountKeyKey,omitempty"` +} diff --git a/api/v1alpha2/valkeycluster_types.go b/api/v1alpha2/valkeycluster_types.go index 5384c334..d1af383b 100644 --- a/api/v1alpha2/valkeycluster_types.go +++ b/api/v1alpha2/valkeycluster_types.go @@ -74,6 +74,11 @@ type ValkeyClusterSpec struct { Auth AuthSpec `json:"auth,omitempty"` // +optional Monitoring *MonitoringSpec `json:"monitoring,omitempty"` + + // SlowLog — Valkey SLOWLOG 임계값 + 보존 entry 수 설정. + // nil 이면 valkey 기본값 (10ms / 128 entries) 사용. + // +optional + SlowLog *SlowLogSpec `json:"slowLog,omitempty"` // +optional Pod *PodSpec `json:"pod,omitempty"` // +optional diff --git a/api/v1alpha2/zz_generated.deepcopy.go b/api/v1alpha2/zz_generated.deepcopy.go index b5905733..6dd083fa 100644 --- a/api/v1alpha2/zz_generated.deepcopy.go +++ b/api/v1alpha2/zz_generated.deepcopy.go @@ -79,6 +79,37 @@ func (in *AutoscalingSpec) DeepCopy() *AutoscalingSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AzureCredentialsSecretRef) DeepCopyInto(out *AzureCredentialsSecretRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AzureCredentialsSecretRef. +func (in *AzureCredentialsSecretRef) DeepCopy() *AzureCredentialsSecretRef { + if in == nil { + return nil + } + out := new(AzureCredentialsSecretRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AzureSpec) DeepCopyInto(out *AzureSpec) { + *out = *in + out.CredentialsSecretRef = in.CredentialsSecretRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AzureSpec. +func (in *AzureSpec) DeepCopy() *AzureSpec { + if in == nil { + return nil + } + out := new(AzureSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BackupDestination) DeepCopyInto(out *BackupDestination) { *out = *in @@ -231,6 +262,37 @@ func (in *ExternalReplicaSpec) DeepCopy() *ExternalReplicaSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GCSCredentialsSecretRef) DeepCopyInto(out *GCSCredentialsSecretRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GCSCredentialsSecretRef. +func (in *GCSCredentialsSecretRef) DeepCopy() *GCSCredentialsSecretRef { + if in == nil { + return nil + } + out := new(GCSCredentialsSecretRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GCSSpec) DeepCopyInto(out *GCSSpec) { + *out = *in + out.CredentialsSecretRef = in.CredentialsSecretRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GCSSpec. +func (in *GCSSpec) DeepCopy() *GCSSpec { + if in == nil { + return nil + } + out := new(GCSSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ModuleSpec) DeepCopyInto(out *ModuleSpec) { *out = *in @@ -737,6 +799,21 @@ func (in *ShardStatus) DeepCopy() *ShardStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SlowLogSpec) DeepCopyInto(out *SlowLogSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SlowLogSpec. +func (in *SlowLogSpec) DeepCopy() *SlowLogSpec { + if in == nil { + return nil + } + out := new(SlowLogSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *StorageSpec) DeepCopyInto(out *StorageSpec) { *out = *in @@ -1006,6 +1083,16 @@ func (in *ValkeyBackupTargetSpec) DeepCopyInto(out *ValkeyBackupTargetSpec) { *out = new(S3Spec) **out = **in } + if in.GCS != nil { + in, out := &in.GCS, &out.GCS + *out = new(GCSSpec) + **out = **in + } + if in.Azure != nil { + in, out := &in.Azure, &out.Azure + *out = new(AzureSpec) + **out = **in + } if in.Retention != nil { in, out := &in.Retention, &out.Retention *out = new(RetentionSpec) @@ -1130,6 +1217,11 @@ func (in *ValkeyClusterSpec) DeepCopyInto(out *ValkeyClusterSpec) { *out = new(MonitoringSpec) (*in).DeepCopyInto(*out) } + if in.SlowLog != nil { + in, out := &in.SlowLog, &out.SlowLog + *out = new(SlowLogSpec) + **out = **in + } if in.Pod != nil { in, out := &in.Pod, &out.Pod *out = new(PodSpec) @@ -1443,6 +1535,11 @@ func (in *ValkeySpec) DeepCopyInto(out *ValkeySpec) { *out = new(AutoscalingSpec) **out = **in } + if in.SlowLog != nil { + in, out := &in.SlowLog, &out.SlowLog + *out = new(SlowLogSpec) + **out = **in + } if in.ExternalReplica != nil { in, out := &in.ExternalReplica, &out.ExternalReplica *out = new(ExternalReplicaSpec) diff --git a/charts/valkey-operator/crds/cache.keiailab.io_valkeybackuptargets.yaml b/charts/valkey-operator/crds/cache.keiailab.io_valkeybackuptargets.yaml index 3cd0398d..569ae5c9 100644 --- a/charts/valkey-operator/crds/cache.keiailab.io_valkeybackuptargets.yaml +++ b/charts/valkey-operator/crds/cache.keiailab.io_valkeybackuptargets.yaml @@ -322,6 +322,67 @@ spec: ValkeyBackup.Spec.Destination.TargetRef + ValkeyRestore.Spec.Source.TargetRef 가 본 CR 을 참조 (대칭성, ADR-0016). properties: + azure: + description: Type=Azure 시 필수. + properties: + accountName: + description: storage account 이름. e.g. "mystorageacct". + type: string + container: + description: container 이름 (S3 의 bucket 등가). 사전 생성 필요. + type: string + credentialsSecretRef: + description: 자격증명 Secret 참조 (account key 또는 SAS token). + properties: + accountKeyKey: + default: AZURE_STORAGE_ACCOUNT_KEY + description: account key 가 들어 있는 key 이름. 기본 "AZURE_STORAGE_ACCOUNT_KEY". + type: string + name: + description: Secret 이름. + type: string + required: + - name + type: object + prefix: + description: 'blob name prefix. 예: "cluster-A/".' + type: string + serviceURL: + description: |- + service URL override. 기본: https://.blob.core.windows.net. + Azure China / Government cloud / Azurite (test) 용. + type: string + required: + - accountName + - container + - credentialsSecretRef + type: object + gcs: + description: Type=GCS 시 필수. + properties: + bucket: + description: 버킷 이름. 사전 생성 필요. + type: string + credentialsSecretRef: + description: 자격증명 Secret 참조 (service account JSON). + properties: + name: + description: Secret 이름. + type: string + serviceAccountJSONKey: + default: key.json + description: service account JSON 이 들어 있는 key 이름. 기본 "key.json". + type: string + required: + - name + type: object + prefix: + description: 'object key prefix. 예: "cluster-A/".' + type: string + required: + - bucket + - credentialsSecretRef + type: object retention: description: |- Retention — 이 target 을 참조하는 완료된 ValkeyBackup 의 자동 보존 정책 diff --git a/charts/valkey-operator/crds/cache.keiailab.io_valkeyclusters.yaml b/charts/valkey-operator/crds/cache.keiailab.io_valkeyclusters.yaml index dfdc7a6b..a3468698 100644 --- a/charts/valkey-operator/crds/cache.keiailab.io_valkeyclusters.yaml +++ b/charts/valkey-operator/crds/cache.keiailab.io_valkeyclusters.yaml @@ -5760,6 +5760,23 @@ spec: - Auto - Manual type: string + slowLog: + description: |- + SlowLog — Valkey SLOWLOG 임계값 + 보존 entry 수 설정. + nil 이면 valkey 기본값 (10ms / 128 entries) 사용. + properties: + maxEntries: + default: 128 + description: 보존 entry 수 — FIFO. 초과 시 가장 오래된 entry 폐기. + format: int32 + type: integer + thresholdMicros: + default: 10000 + description: '단위: microseconds. 0 = SLOWLOG 비활성. -1 = 모든 명령 기록 + (debug 만).' + format: int64 + type: integer + type: object storage: description: StorageSpec — Valkey 데이터 디렉터리(/data) 마운트용 PVC. properties: diff --git a/charts/valkey-operator/crds/cache.keiailab.io_valkeys.yaml b/charts/valkey-operator/crds/cache.keiailab.io_valkeys.yaml index c388ef0f..05197825 100644 --- a/charts/valkey-operator/crds/cache.keiailab.io_valkeys.yaml +++ b/charts/valkey-operator/crds/cache.keiailab.io_valkeys.yaml @@ -5883,6 +5883,23 @@ spec: - LoadBalancer type: string type: object + slowLog: + description: |- + SlowLog — Valkey SLOWLOG 임계값 + 보존 entry 수 설정. + nil 이면 valkey 기본값 (10ms / 128 entries) 사용. + properties: + maxEntries: + default: 128 + description: 보존 entry 수 — FIFO. 초과 시 가장 오래된 entry 폐기. + format: int32 + type: integer + thresholdMicros: + default: 10000 + description: '단위: microseconds. 0 = SLOWLOG 비활성. -1 = 모든 명령 기록 + (debug 만).' + format: int64 + type: integer + type: object storage: description: StorageSpec — Valkey 데이터 디렉터리(/data) 마운트용 PVC. properties: diff --git a/config/crd/bases/cache.keiailab.io_valkeybackuptargets.yaml b/config/crd/bases/cache.keiailab.io_valkeybackuptargets.yaml index 3cd0398d..569ae5c9 100644 --- a/config/crd/bases/cache.keiailab.io_valkeybackuptargets.yaml +++ b/config/crd/bases/cache.keiailab.io_valkeybackuptargets.yaml @@ -322,6 +322,67 @@ spec: ValkeyBackup.Spec.Destination.TargetRef + ValkeyRestore.Spec.Source.TargetRef 가 본 CR 을 참조 (대칭성, ADR-0016). properties: + azure: + description: Type=Azure 시 필수. + properties: + accountName: + description: storage account 이름. e.g. "mystorageacct". + type: string + container: + description: container 이름 (S3 의 bucket 등가). 사전 생성 필요. + type: string + credentialsSecretRef: + description: 자격증명 Secret 참조 (account key 또는 SAS token). + properties: + accountKeyKey: + default: AZURE_STORAGE_ACCOUNT_KEY + description: account key 가 들어 있는 key 이름. 기본 "AZURE_STORAGE_ACCOUNT_KEY". + type: string + name: + description: Secret 이름. + type: string + required: + - name + type: object + prefix: + description: 'blob name prefix. 예: "cluster-A/".' + type: string + serviceURL: + description: |- + service URL override. 기본: https://.blob.core.windows.net. + Azure China / Government cloud / Azurite (test) 용. + type: string + required: + - accountName + - container + - credentialsSecretRef + type: object + gcs: + description: Type=GCS 시 필수. + properties: + bucket: + description: 버킷 이름. 사전 생성 필요. + type: string + credentialsSecretRef: + description: 자격증명 Secret 참조 (service account JSON). + properties: + name: + description: Secret 이름. + type: string + serviceAccountJSONKey: + default: key.json + description: service account JSON 이 들어 있는 key 이름. 기본 "key.json". + type: string + required: + - name + type: object + prefix: + description: 'object key prefix. 예: "cluster-A/".' + type: string + required: + - bucket + - credentialsSecretRef + type: object retention: description: |- Retention — 이 target 을 참조하는 완료된 ValkeyBackup 의 자동 보존 정책 diff --git a/config/crd/bases/cache.keiailab.io_valkeyclusters.yaml b/config/crd/bases/cache.keiailab.io_valkeyclusters.yaml index dfdc7a6b..a3468698 100644 --- a/config/crd/bases/cache.keiailab.io_valkeyclusters.yaml +++ b/config/crd/bases/cache.keiailab.io_valkeyclusters.yaml @@ -5760,6 +5760,23 @@ spec: - Auto - Manual type: string + slowLog: + description: |- + SlowLog — Valkey SLOWLOG 임계값 + 보존 entry 수 설정. + nil 이면 valkey 기본값 (10ms / 128 entries) 사용. + properties: + maxEntries: + default: 128 + description: 보존 entry 수 — FIFO. 초과 시 가장 오래된 entry 폐기. + format: int32 + type: integer + thresholdMicros: + default: 10000 + description: '단위: microseconds. 0 = SLOWLOG 비활성. -1 = 모든 명령 기록 + (debug 만).' + format: int64 + type: integer + type: object storage: description: StorageSpec — Valkey 데이터 디렉터리(/data) 마운트용 PVC. properties: diff --git a/config/crd/bases/cache.keiailab.io_valkeys.yaml b/config/crd/bases/cache.keiailab.io_valkeys.yaml index c388ef0f..05197825 100644 --- a/config/crd/bases/cache.keiailab.io_valkeys.yaml +++ b/config/crd/bases/cache.keiailab.io_valkeys.yaml @@ -5883,6 +5883,23 @@ spec: - LoadBalancer type: string type: object + slowLog: + description: |- + SlowLog — Valkey SLOWLOG 임계값 + 보존 entry 수 설정. + nil 이면 valkey 기본값 (10ms / 128 entries) 사용. + properties: + maxEntries: + default: 128 + description: 보존 entry 수 — FIFO. 초과 시 가장 오래된 entry 폐기. + format: int32 + type: integer + thresholdMicros: + default: 10000 + description: '단위: microseconds. 0 = SLOWLOG 비활성. -1 = 모든 명령 기록 + (debug 만).' + format: int64 + type: integer + type: object storage: description: StorageSpec — Valkey 데이터 디렉터리(/data) 마운트용 PVC. properties: