From 88305af404f3a7832e2982fc636e9828b54ad3eb Mon Sep 17 00:00:00 2001 From: Andrii Dema Date: Wed, 22 Oct 2025 15:05:32 +0300 Subject: [PATCH 01/13] K8SPS-567: automatic cr.yaml generation https://perconadev.atlassian.net/browse/K8SPS-567 --- Makefile | 6 +- api/v1/perconaservermysql_types.go | 4 +- api/v1/zz_generated.deepcopy.go | 10 +- build/run-backup.sh | 3 +- cmd/example-gen/defaults/manual.go | 468 +++++++ cmd/example-gen/defaults/preset.go | 223 +++ cmd/example-gen/generate.sh | 247 ++++ cmd/example-gen/internal/fill/bytype.go | 147 ++ cmd/example-gen/internal/fill/defaults.go | 464 +++++++ cmd/example-gen/internal/marshal/marshal.go | 274 ++++ cmd/example-gen/main.go | 86 ++ ...percona.com_perconaservermysqlbackups.yaml | 2 - ...ercona.com_perconaservermysqlrestores.yaml | 2 - .../ps.percona.com_perconaservermysqls.yaml | 448 ------- deploy/bundle.yaml | 452 ------- deploy/cr.yaml | 1192 ++++++++++------- deploy/crd.yaml | 452 ------- deploy/cw-bundle.yaml | 452 ------- pkg/controller/ps/version_test.go | 80 +- pkg/mysql/mysql_test.go | 38 +- pkg/xtrabackup/xtrabackup.go | 5 - pkg/xtrabackup/xtrabackup_test.go | 3 - 22 files changed, 2681 insertions(+), 2377 deletions(-) create mode 100644 cmd/example-gen/defaults/manual.go create mode 100644 cmd/example-gen/defaults/preset.go create mode 100755 cmd/example-gen/generate.sh create mode 100644 cmd/example-gen/internal/fill/bytype.go create mode 100644 cmd/example-gen/internal/fill/defaults.go create mode 100644 cmd/example-gen/internal/marshal/marshal.go create mode 100644 cmd/example-gen/main.go diff --git a/Makefile b/Makefile index 12dbc0f56..34fe4a831 100644 --- a/Makefile +++ b/Makefile @@ -103,7 +103,11 @@ kuttl-shfmt: e2e-test: kuttl-shfmt ROOT_REPO=$(ROOT_REPO) kubectl kuttl test --config e2e-tests/kuttl.yaml -manifests: kustomize generate ## Generate Kubernetes manifests (CRDs, RBAC, operator deployment) +.PHONY: generate-cr-yaml +generate-cr-yaml: + ./cmd/example-gen/generate.sh + +manifests: kustomize generate generate-cr-yaml ## Generate Kubernetes manifests (CRDs, RBAC, operator deployment) $(KUSTOMIZE) build config/crd/ > $(DEPLOYDIR)/crd.yaml echo "---" >> $(DEPLOYDIR)/crd.yaml diff --git a/api/v1/perconaservermysql_types.go b/api/v1/perconaservermysql_types.go index b760e4546..6e25bedc6 100644 --- a/api/v1/perconaservermysql_types.go +++ b/api/v1/perconaservermysql_types.go @@ -147,6 +147,8 @@ type MySQLSpec struct { VaultSecretName string `json:"vaultSecretName,omitempty"` + VolumeSpec *VolumeSpec `json:"volumeSpec,omitempty"` + PodSpec `json:",inline"` } @@ -194,7 +196,6 @@ type PodSpec struct { Size int32 `json:"size,omitempty"` Annotations map[string]string `json:"annotations,omitempty"` Labels map[string]string `json:"labels,omitempty"` - VolumeSpec *VolumeSpec `json:"volumeSpec,omitempty"` // Deprecated: not supported since v0.12.0. Use initContainer instead InitImage string `json:"initImage,omitempty"` @@ -409,7 +410,6 @@ type BackupStorageS3Spec struct { CredentialsSecret string `json:"credentialsSecret"` Region string `json:"region,omitempty"` EndpointURL string `json:"endpointUrl,omitempty"` - StorageClass string `json:"storageClass,omitempty"` } // BucketAndPrefix returns bucket name and backup prefix from Bucket concatenated with Prefix. diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 22db54e91..99fb9a3f2 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -501,6 +501,11 @@ func (in *MySQLSpec) DeepCopyInto(out *MySQLSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.VolumeSpec != nil { + in, out := &in.VolumeSpec, &out.VolumeSpec + *out = new(VolumeSpec) + (*in).DeepCopyInto(*out) + } in.PodSpec.DeepCopyInto(&out.PodSpec) } @@ -996,11 +1001,6 @@ func (in *PodSpec) DeepCopyInto(out *PodSpec) { (*out)[key] = val } } - if in.VolumeSpec != nil { - in, out := &in.VolumeSpec, &out.VolumeSpec - *out = new(VolumeSpec) - (*in).DeepCopyInto(*out) - } if in.InitContainer != nil { in, out := &in.InitContainer, &out.InitContainer *out = new(InitContainerSpec) diff --git a/build/run-backup.sh b/build/run-backup.sh index aed360ebd..c070fd5bb 100755 --- a/build/run-backup.sh +++ b/build/run-backup.sh @@ -18,8 +18,7 @@ request_data() { "endpointUrl": "$(json_escape "${AWS_ENDPOINT}")", "accessKey": "$(json_escape "${AWS_ACCESS_KEY_ID}")", "secretKey": "$(json_escape "${AWS_SECRET_ACCESS_KEY}")", - "region": "$(json_escape "${AWS_DEFAULT_REGION}")", - "storageClass": "$(json_escape "${S3_STORAGE_CLASS}")" + "region": "$(json_escape "${AWS_DEFAULT_REGION}")" } } EOF diff --git a/cmd/example-gen/defaults/manual.go b/cmd/example-gen/defaults/manual.go new file mode 100644 index 000000000..e5c75d1db --- /dev/null +++ b/cmd/example-gen/defaults/manual.go @@ -0,0 +1,468 @@ +package defaults + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/ptr" + + apiv1 "github.com/percona/percona-server-mysql-operator/api/v1" + "github.com/percona/percona-server-mysql-operator/pkg/version" +) + +// SetManual sets default values for empty fields in example cr.yaml +// that are not set by CheckNSetDefaults and cannot be set via PresetDefaults. +func SetManual(cr *apiv1.PerconaServerMySQL) { + cr.Spec.Unsafe = apiv1.UnsafeFlags{ + MySQLSize: false, + Proxy: false, + ProxySize: false, + Orchestrator: false, + OrchestratorSize: false, + } + cr.Spec.Pause = true + cr.Spec.CRVersion = version.Version() + cr.Spec.VolumeExpansionEnabled = true + cr.Spec.UpdateStrategy = "SmartUpdate" + cr.Spec.InitContainer.Image = "perconalab/percona-server-mysql-operator:main" + cr.Spec.IgnoreAnnotations = []string{"service.beta.kubernetes.io/aws-load-balancer-backend-protocol"} + cr.Spec.IgnoreLabels = []string{"rack"} + + mysqlDefaults(&cr.Spec.MySQL) + haproxyDefaults(cr.Spec.Proxy.HAProxy) + routerDefaults(cr.Spec.Proxy.Router) + orchestratorDefaults(&cr.Spec.Orchestrator) + pmmDefaults(cr.Spec.PMM) + toolkitDefaults(cr.Spec.Toolkit) + backupDefaults(cr.Spec.Backup) +} + +func mysqlDefaults(spec *apiv1.MySQLSpec) { + spec.AutoRecovery = true + spec.Size = 3 + spec.Image = "perconalab/percona-server-mysql-operator:main-psmysql8.4" + spec.RuntimeClassName = ptr.To("image-rc") + spec.Env = []corev1.EnvVar{ + { + Name: "BOOTSTRAP_READ_TIMEOUT", + Value: "600", + }, + } + spec.EnvFrom = []corev1.EnvFromSource{ + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "mysql-env-secret", + }, + }, + }, + } + spec.Labels = map[string]string{ + "rack": "rack-22", + } + spec.Annotations = map[string]string{ + "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", + "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", + } + + spec.Resources = corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("2Gi"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("4Gi"), + }, + } + spec.VolumeSpec = nil + spec.Affinity = nil + spec.ExposePrimary.Enabled = true + spec.TerminationGracePeriodSeconds = ptr.To(int64(600)) + spec.Configuration = `max_connections=250 +innodb_buffer_pool_size={{containerMemoryLimit * 3/4}}` + spec.PriorityClassName = "high-priority" + + spec.Sidecars = []corev1.Container{ + { + Name: "noop-memory", + Image: "busybox", + Command: []string{ + "sleep", "30d", + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "memory-vol", + MountPath: "/var/log/app/memory", + }, + }, + LivenessProbe: &corev1.Probe{}, + ReadinessProbe: &corev1.Probe{}, + StartupProbe: &corev1.Probe{}, + Lifecycle: &corev1.Lifecycle{}, + SecurityContext: &corev1.SecurityContext{}, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("16M"), + }, + }, + }, + { + Name: "noop-pvc", + Image: "busybox", + Command: []string{ + "sleep", "30d", + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "memory-vol", + MountPath: "/var/log/app/memory", + }, + }, + LivenessProbe: &corev1.Probe{}, + ReadinessProbe: &corev1.Probe{}, + StartupProbe: &corev1.Probe{}, + Lifecycle: &corev1.Lifecycle{}, + SecurityContext: &corev1.SecurityContext{}, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("16M"), + }, + }, + }, + } + spec.SidecarVolumes = []corev1.Volume{ + { + Name: "memory-vol", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + Medium: corev1.StorageMediumMemory, + }, + }, + }, + } + + spec.SchedulerName = "default-scheduler" + spec.PodSecurityContext = &corev1.PodSecurityContext{ + FSGroup: ptr.To(int64(1001)), + SupplementalGroups: []int64{1001, 1002, 1003}, + } + spec.ServiceAccountName = "percona-server-mysql-operator-orchestrator" + spec.NodeSelector = map[string]string{ + "topology.kubernetes.io/zone": "us-east-1a", + } +} + +func haproxyDefaults(spec *apiv1.HAProxySpec) { + spec.Image = "perconalab/percona-server-mysql-operator:main-haproxy" + spec.Enabled = true + spec.Size = 3 + spec.RuntimeClassName = ptr.To("image-rc") + spec.Resources = corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("1Gi"), + corev1.ResourceCPU: resource.MustParse("700m"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("1Gi"), + corev1.ResourceCPU: resource.MustParse("600m"), + }, + } + spec.PriorityClassName = "high-priority" + spec.Env = []corev1.EnvVar{ + { + Name: "HA_CONNECTION_TIMEOUT", + Value: "600", + }, + } + spec.EnvFrom = []corev1.EnvFromSource{ + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "haproxy-env-secret", + }, + }, + }, + } + spec.Labels = map[string]string{ + "rack": "rack-22", + } + spec.Annotations = map[string]string{ + "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", + "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", + } + spec.SchedulerName = "default-scheduler" + spec.TerminationGracePeriodSeconds = ptr.To(int64(30)) + spec.ServiceAccountName = "percona-server-mysql-operator-orchestrator" + spec.Configuration = `# the actual default configuration file can be found here https://github.com/percona/percona-server-mysql-operator/blob/main/build/haproxy-global.cfg + +global + maxconn 2048 + external-check + insecure-fork-wanted + stats socket /etc/haproxy/mysql/haproxy.sock mode 600 expose-fd listeners level admin + +defaults + default-server init-addr last,libc,none + log global + mode tcp + retries 10 + timeout client 28800s + timeout connect 100500 + timeout server 28800s + +frontend mysql-primary-in + bind *:3309 accept-proxy + bind *:3306 + mode tcp + option clitcpka + default_backend mysql-primary + +frontend mysql-replicas-in + bind *:3307 + mode tcp + option clitcpka + default_backend mysql-replicas + +frontend stats + bind *:8404 + mode http + http-request use-service prometheus-exporter if { path /metrics }` + spec.NodeSelector = map[string]string{ + "topology.kubernetes.io/zone": "us-east-1a", + } +} + +func routerDefaults(spec *apiv1.MySQLRouterSpec) { + spec.Enabled = true + spec.Size = 3 + spec.Image = "perconalab/percona-server-mysql-operator:main-router8.4" + spec.RuntimeClassName = ptr.To("image-rc") + spec.PriorityClassName = "high-priority" + spec.Labels = map[string]string{ + "rack": "rack-22", + } + spec.Annotations = map[string]string{ + "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", + "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", + } + spec.Resources = corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("256M"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("256M"), + }, + } + spec.Env = []corev1.EnvVar{ + { + Name: "ROUTER_ENV", + Value: "VALUE", + }, + } + spec.EnvFrom = []corev1.EnvFromSource{ + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "router-env-secret", + }, + }, + }, + } + spec.TerminationGracePeriodSeconds = ptr.To(int64(30)) + spec.Configuration = `[default] +logging_folder=/tmp/router/log +[logger] +level=DEBUG` + spec.ServiceAccountName = "percona-server-mysql-operator-orchestrator" + spec.Ports = []corev1.ServicePort{ + { + Name: "http", + Port: 8443, + }, + { + Name: "rw-default", + Port: 3306, + TargetPort: intstr.FromInt(6446), + }, + { + Name: "read-write", + Port: 6446, + }, + { + Name: "read-only", + Port: 6447, + }, + { + Name: "x-read-write", + Port: 6448, + }, + { + Name: "x-read-only", + Port: 6449, + }, + { + Name: "x-default", + Port: 33060, + }, + { + Name: "rw-admin", + Port: 33062, + }, + { + Name: "custom-port", + Port: 1111, + TargetPort: intstr.FromInt(6446), + }, + } + spec.SchedulerName = "default-scheduler" + spec.NodeSelector = map[string]string{ + "topology.kubernetes.io/zone": "us-east-1a", + } +} + +func orchestratorDefaults(spec *apiv1.OrchestratorSpec) { + spec.Enabled = true + spec.Image = "perconalab/percona-server-mysql-operator:main-orchestrator" + spec.Size = 3 + spec.TerminationGracePeriodSeconds = ptr.To(int64(30)) + spec.Affinity = nil + spec.ServiceAccountName = "percona-server-mysql-operator-orchestrator" + spec.PriorityClassName = "high-priority" + spec.RuntimeClassName = ptr.To("image-rc") + spec.Env = []corev1.EnvVar{ + { + Name: "ORC_ENV", + Value: "VALUE", + }, + } + spec.EnvFrom = []corev1.EnvFromSource{ + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "orc-env-secret", + }, + }, + }, + } + spec.Resources = corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("256M"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("128M"), + }, + } + spec.Labels = map[string]string{ + "rack": "rack-22", + } + spec.Annotations = map[string]string{ + "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", + "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", + } + spec.SchedulerName = "default-scheduler" + spec.NodeSelector = map[string]string{ + "topology.kubernetes.io/zone": "us-east-1a", + } +} + +func pmmDefaults(spec *apiv1.PMMSpec) { + spec.Resources = corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("256M"), + corev1.ResourceCPU: resource.MustParse("400m"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("150M"), + corev1.ResourceCPU: resource.MustParse("300m"), + }, + } + + spec.Image = "perconalab/pmm-client:3-dev-latest" + spec.ServerHost = "monitoring-service" + spec.MySQLParams = "PMM_ADMIN_CUSTOM_PARAMS" +} + +func backupDefaults(spec *apiv1.BackupSpec) { + spec.Enabled = true + spec.SourcePod = "ps-cluster1-mysql-1" + spec.Image = "perconalab/percona-server-mysql-operator:main-backup8.4" + spec.Schedule = []apiv1.BackupSchedule{ + { + Name: "sat-night-backup", + Schedule: "0 0 * * 6", + Keep: 3, + StorageName: "s3-us-west", + }, + { + Name: "daily-backup", + Schedule: "0 0 * * *", + Keep: 5, + StorageName: "s3", + }, + } + spec.BackoffLimit = ptr.To(int32(6)) + spec.Storages = map[string]*apiv1.BackupStorageSpec{ + "azure-blob": { + Type: apiv1.BackupStorageAzure, + Azure: &apiv1.BackupStorageAzureSpec{ + ContainerName: "CONTAINER-NAME", + Prefix: "PREFIX-NAME", + CredentialsSecret: "SECRET-NAME", + EndpointURL: "https://accountName.blob.core.windows.net", + StorageClass: "Cool", + }, + }, + "s3-us-west": { + Type: apiv1.BackupStorageS3, + S3: &apiv1.BackupStorageS3Spec{ + Bucket: "S3-BACKUP-BUCKET-NAME-HERE", + Prefix: "PREFIX_NAME", + CredentialsSecret: "ps-cluster1-s3-credentials", + Region: "us-west-2", + EndpointURL: "https://s3.amazonaws.com", + }, + Annotations: map[string]string{ + "testName": "scheduled-backup", + }, + Labels: map[string]string{ + "backupWorker": "True", + }, + PriorityClassName: "high-priority", + RuntimeClassName: ptr.To("image-rc"), + SchedulerName: "default-scheduler", + VerifyTLS: ptr.To(true), + NodeSelector: map[string]string{ + "topology.kubernetes.io/zone": "us-east-1a", + }, + }, + } + spec.ServiceAccountName = "percona-server-mysql-operator-orchestrator" +} + +func toolkitDefaults(spec *apiv1.ToolkitSpec) { + spec.Image = "perconalab/percona-server-mysql-operator:main-toolkit" + spec.Resources = corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("256M"), + corev1.ResourceCPU: resource.MustParse("400m"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("150M"), + corev1.ResourceCPU: resource.MustParse("100m"), + }, + } + spec.Env = []corev1.EnvVar{ + { + Name: "TOOLKIT_ENV", + Value: "VALUE", + }, + } + spec.EnvFrom = []corev1.EnvFromSource{ + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "toolkit-env-secret", + }, + }, + }, + } +} diff --git a/cmd/example-gen/defaults/preset.go b/cmd/example-gen/defaults/preset.go new file mode 100644 index 000000000..9afdf5c1e --- /dev/null +++ b/cmd/example-gen/defaults/preset.go @@ -0,0 +1,223 @@ +package defaults + +import ( + cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/ptr" + + apiv1 "github.com/percona/percona-server-mysql-operator/api/v1" + "github.com/percona/percona-server-mysql-operator/cmd/example-gen/internal/fill" +) + +// SetManual assigns default values to fields in example cr.yaml based on their types. +// The internal presets slice defines the default value associated with each type. +func PresetDefaults(cr *apiv1.PerconaServerMySQL) error { + presets := []any{ + apiv1.Metadata{ + Labels: map[string]string{ + "rack": "rack-22", + }, + Annotations: map[string]string{ + "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", + "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", + }, + }, + corev1.SecurityContext{ + Privileged: ptr.To(false), + RunAsUser: ptr.To(int64(1001)), + RunAsGroup: ptr.To(int64(1001)), + }, + corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("100M"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("200m"), + corev1.ResourceMemory: resource.MustParse("200M"), + }, + }, + apiv1.TLSSpec{ + SANs: []string{ + "mysql-1.example.com", + "mysql-2.example.com", + "mysql-3.example.com", + }, + IssuerConf: &cmmeta.ObjectReference{ + Name: "special-selfsigned-issuer", + Kind: "ClusterIssuer", + Group: "cert-manager.io", + }, + }, + apiv1.PodAffinity{ + TopologyKey: ptr.To("kubernetes.io/hostname"), + Advanced: &corev1.Affinity{ + NodeAffinity: &corev1.NodeAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{ + NodeSelectorTerms: []corev1.NodeSelectorTerm{ + { + MatchExpressions: []corev1.NodeSelectorRequirement{ + { + Key: "kubernetes.io/e2e-az-name", + Operator: "In", + Values: []string{ + "e2e-az1", + "e2e-az2", + }, + }, + }, + }, + }, + }, + }, + }, + }, + []corev1.TopologySpreadConstraint{ + { + MaxSkew: 1, + TopologyKey: "kubernetes.io/hostname", + WhenUnsatisfiable: corev1.DoNotSchedule, + LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/name": "percona-server", + }, + }, + }, + }, + apiv1.ServiceExpose{ + Type: corev1.ServiceTypeClusterIP, + LoadBalancerSourceRanges: []string{ + "10.0.0.0/8", + }, + Annotations: map[string]string{ + "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", + "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", + }, + Labels: map[string]string{ + "rack": "rack-22", + }, + InternalTrafficPolicy: ptr.To(corev1.ServiceInternalTrafficPolicyCluster), + ExternalTrafficPolicy: corev1.ServiceExternalTrafficPolicyCluster, + }, + apiv1.VolumeSpec{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + HostPath: &corev1.HostPathVolumeSource{ + Path: "/data", + Type: ptr.To(corev1.HostPathDirectory), + }, + PersistentVolumeClaim: &corev1.PersistentVolumeClaimSpec{ + StorageClassName: ptr.To("standard"), + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteOnce, + }, + Resources: corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse("2Gi"), + }, + }, + }, + }, + corev1.Probe{ + TimeoutSeconds: 3, + PeriodSeconds: 5, + SuccessThreshold: 1, + FailureThreshold: 3, + }, + apiv1.PodDisruptionBudgetSpec{ + MinAvailable: ptr.To(intstr.FromInt(0)), + MaxUnavailable: ptr.To(intstr.FromInt(1)), + }, + corev1.PodSecurityContext{ + FSGroup: ptr.To(int64(1001)), + SupplementalGroups: []int64{1001, 1002, 1003}, + }, + apiv1.UnsafeFlags{ + MySQLSize: false, + Proxy: false, + ProxySize: false, + Orchestrator: false, + OrchestratorSize: false, + }, + apiv1.UpgradeOptions{ + VersionServiceEndpoint: "https://check.percona.com", + Apply: "disabled", + }, + []corev1.Toleration{ + { + Key: "node.alpha.kubernetes.io/unreachable", + Operator: "Exists", + Effect: "NoExecute", + TolerationSeconds: ptr.To(int64(6000)), + }, + }, + []corev1.LocalObjectReference{ + { + Name: "my-secret-1", + }, + { + Name: "my-secret-2", + }, + }, + corev1.PullAlways, + apiv1.InitContainerSpec{ + Image: "perconalab/percona-server-mysql-operator:main", + }, + []apiv1.SidecarPVC{ + { + Name: "pvc-vol", + Spec: corev1.PersistentVolumeClaimSpec{ + Resources: corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse("1Gi"), + }, + }, + }, + }, + }, + + corev1.Affinity{ + NodeAffinity: &corev1.NodeAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{ + NodeSelectorTerms: []corev1.NodeSelectorTerm{ + { + MatchExpressions: []corev1.NodeSelectorRequirement{ + { + Key: "kubernetes.io/e2e-az-name", + Operator: "In", + Values: []string{ + "e2e-az1", + "e2e-az2", + }, + }, + }, + }, + }, + }, + }, + }, + apiv1.BackupContainerOptions{ + Env: []corev1.EnvVar{ + { + Name: "CUSTOM_VAR", + Value: "false", + }, + }, + Args: apiv1.BackupContainerArgs{ + Xtrabackup: []string{ + "--someflag=abc", + }, + Xbcloud: []string{ + "--someflag=abc", + }, + Xbstream: []string{ + "--someflag=abc", + }, + }, + }, + } + + return fill.ByType(cr, presets...) +} diff --git a/cmd/example-gen/generate.sh b/cmd/example-gen/generate.sh new file mode 100755 index 000000000..1b6181dc5 --- /dev/null +++ b/cmd/example-gen/generate.sh @@ -0,0 +1,247 @@ +#!/usr/bin/env bash + +sort_cr_yaml() { + GENERAL_ORDER='"metadata", "unsafeFlags", "pause", "crVersion", "enableVolumeExpansion", "secretsName", "sslSecretName", "updateStrategy", "upgradeOptions", "initContainer", "ignoreAnnotations", "ignoreLabels", "tls", "mysql", "proxy", "orchestrator", "pmm", "backup", "toolkit"' + + POD_SPEC_ORDER='"size", "image", "imagePullPolicy","imagePullSecrets", "runtimeClassName", "tolerations", "annotations", "labels", "nodeSelector", "priorityClassName", "schedulerName", "serviceAccountName","gracePeriod", "initContainer", "env", "envFrom", "podDisruptionBudget", "resources","startupProbe", "readinessProbe", "livenessProbe", "affinity", "topologySpreadConstraints", "containerSecurityContext", "podSecurityContext"' + MYSQL_ORDER='"clusterType", "autoRecovery", "vaultSecretName", '"$POD_SPEC_ORDER"',"exposePrimary", "expose", "volumeSpec", "configuration", "sidecars", "sidecarVolumes", "sidecarPVCs"' + HAPROXY_ORDER='"enabled", "expose", '"$POD_SPEC_ORDER" + ROUTER_ORDER='"enabled", "expose", '"$POD_SPEC_ORDER"', "ports"' + ORCHESTRATOR_ORDER='"enabled", "expose", '"$POD_SPEC_ORDER" + + PMM_ORDER='"enabled","image","imagePullPolicy","serverHost","mysqlParams","containerSecurityContext", "resources", "readinessProbes", "livenessProbes"' + BACKUP_ORDER='"enabled","pitr","sourcePod","image","imagePullPolicy","imagePullSecrets","schedule","backoffLimit", "serviceAccountName", "initContainer", "containerSecurityContext", "resources","storages","pitr"' + TOOLKIT_ORDER='"image","imagePullPolicy","imagePullSecrets","env","envFrom","resources","containerSecurityContext", "startupProbe", "readinessProbe", "livenessProbe"' + + yq - "$@" \ + | yq '.spec |= pick((['"$GENERAL_ORDER"'] + keys) | unique)' \ + | yq '.spec.mysql |= pick((['"$MYSQL_ORDER"'] + keys) | unique)' \ + | yq '.spec.proxy.haproxy |= pick((['"$HAPROXY_ORDER"'] + keys) | unique)' \ + | yq '.spec.proxy.router |= pick((['"$ROUTER_ORDER"'] + keys) | unique)' \ + | yq '.spec.orchestrator |= pick((['"$ORCHESTRATOR_ORDER"'] + keys) | unique)' \ + | yq '.spec.pmm |= pick((['"$PMM_ORDER"'] + keys) | unique)' \ + | yq '.spec.backup |= pick((['"$BACKUP_ORDER"'] + keys) | unique)' \ + | yq '.spec.toolkit |= pick((['"$TOOLKIT_ORDER"'] + keys) | unique)' +} + +remove_fields() { + # - removing initImage as it is deprecated + # - removing binlogServer is not used + # - removing azure-blob fields to reduce size + # - removing non-s3 fields in s3-us-west + yq - "$@" \ + | yq 'del(.status)' \ + | yq 'del(.spec.backup.initImage)' \ + | yq 'del(.spec.initImage)' \ + | yq 'del(.spec.mysql.initImage)' \ + | yq 'del(.spec.orchestrator.initImage)' \ + | yq 'del(.spec.proxy.haproxy.initImage)' \ + | yq 'del(.spec.proxy.router.initImage)' \ + | yq 'del(.spec.backup.pitr.binlogServer)' \ + | yq 'del(.spec.backup.storages.azure-blob.affinity)' \ + | yq 'del(.spec.backup.storages.azure-blob.annotations)' \ + | yq 'del(.spec.backup.storages.azure-blob.gcs)' \ + | yq 'del(.spec.backup.storages.azure-blob.labels)' \ + | yq 'del(.spec.backup.storages.azure-blob.nodeSelector)' \ + | yq 'del(.spec.backup.storages.azure-blob.priorityClassName)' \ + | yq 'del(.spec.backup.storages.azure-blob.runtimeClassName)' \ + | yq 'del(.spec.backup.storages.azure-blob.s3)' \ + | yq 'del(.spec.backup.storages.azure-blob.schedulerName)' \ + | yq 'del(.spec.backup.storages.azure-blob.volumeSpec)' \ + | yq 'del(.spec.backup.storages.s3-us-west.azure)' \ + | yq 'del(.spec.backup.storages.s3-us-west.gcs)' +} + +comment_field() { + local field_path="$1" + shift || true + + yq - "$@" \ + | yq "($field_path | key) head_comment = (($field_path | key) as \$key | (($field_path | parent) | pick([\$key])) | to_yaml)" \ + | yq "$field_path=null" \ + | yq "$field_path=\"SEDSHOULDDELETEIT\"" \ + | sed '/SEDSHOULDDELETEIT/d' +} + +comment_arr_value() { + local value_path="$1" + shift || true + + query=' +('$value_path' | parent | key) foot_comment = +( + ('$value_path' | parent | key | foot_comment) as $existing_comment | + (($existing_comment | select(length > 1) | from_yaml) // []) + ['$value_path'] as $resulting_list | + {"SEDSHOULDDELETEIT": $resulting_list} + | to_yaml +) +' + + yq - "$@" \ + | yq "$query" \ + | yq "$value_path=\"SEDSHOULDDELETEIT\"" \ + | sed '/SEDSHOULDDELETEIT/d' +} + +normalize_comments() { + sed -r 's/^([[:space:]]*)# (.*)$/#\1\2/' - "$@" +} + +# TODO: Refactor this function. We should introduce an allowlist of fields to keep uncommented and comment everything else. +comment_cr_yaml() { + yq - "$@" \ + | comment_arr_value ".metadata.finalizers[1]" \ + | comment_arr_value ".metadata.finalizers[1]" \ + | comment_field ".spec.metadata" \ + | comment_field ".spec.unsafeFlags" \ + | comment_field ".spec.pause" \ + | comment_field ".spec.enableVolumeExpansion" \ + | comment_field ".spec.initContainer" \ + | comment_field ".spec.ignoreAnnotations" \ + | comment_field ".spec.ignoreLabels" \ + | comment_field ".spec.tls" \ + | comment_field ".spec.mysql.runtimeClassName" \ + | comment_field ".spec.mysql.tolerations" \ + | comment_field ".spec.mysql.annotations" \ + | comment_field ".spec.mysql.labels" \ + | comment_field ".spec.mysql.nodeSelector" \ + | comment_field ".spec.mysql.priorityClassName" \ + | comment_field ".spec.mysql.schedulerName" \ + | comment_field ".spec.mysql.serviceAccountName" \ + | comment_field ".spec.mysql.imagePullSecrets" \ + | comment_field ".spec.mysql.initContainer" \ + | comment_field ".spec.mysql.vaultSecretName" \ + | comment_field ".spec.mysql.env" \ + | comment_field ".spec.mysql.envFrom" \ + | comment_field ".spec.mysql.podDisruptionBudget.minAvailable" \ + | comment_field ".spec.mysql.startupProbe" \ + | comment_field ".spec.mysql.readinessProbe" \ + | comment_field ".spec.mysql.livenessProbe" \ + | comment_field ".spec.mysql.affinity.advanced" \ + | comment_field ".spec.mysql.topologySpreadConstraints" \ + | comment_field ".spec.mysql.expose" \ + | comment_field ".spec.mysql.exposePrimary.annotations" \ + | comment_field ".spec.mysql.exposePrimary.labels" \ + | comment_field ".spec.mysql.exposePrimary.loadBalancerSourceRanges" \ + | comment_field ".spec.mysql.exposePrimary.type" \ + | comment_field ".spec.mysql.exposePrimary.internalTrafficPolicy" \ + | comment_field ".spec.mysql.exposePrimary.externalTrafficPolicy" \ + | comment_field ".spec.mysql.containerSecurityContext" \ + | comment_field ".spec.mysql.podSecurityContext" \ + | comment_field ".spec.mysql.configuration" \ + | comment_field ".spec.mysql.sidecars" \ + | comment_field ".spec.mysql.sidecarVolumes" \ + | comment_field ".spec.mysql.sidecarPVCs" \ + | comment_field ".spec.mysql.volumeSpec.emptyDir" \ + | comment_field ".spec.mysql.volumeSpec.hostPath" \ + | comment_field ".spec.mysql.volumeSpec.persistentVolumeClaim.storageClassName" \ + | comment_field ".spec.mysql.volumeSpec.persistentVolumeClaim.accessModes" \ + | comment_field ".spec.proxy.haproxy.runtimeClassName" \ + | comment_field ".spec.proxy.haproxy.tolerations" \ + | comment_field ".spec.proxy.haproxy.annotations" \ + | comment_field ".spec.proxy.haproxy.labels" \ + | comment_field ".spec.proxy.haproxy.nodeSelector" \ + | comment_field ".spec.proxy.haproxy.priorityClassName" \ + | comment_field ".spec.proxy.haproxy.schedulerName" \ + | comment_field ".spec.proxy.haproxy.serviceAccountName" \ + | comment_field ".spec.proxy.haproxy.imagePullSecrets" \ + | comment_field ".spec.proxy.haproxy.podDisruptionBudget.minAvailable" \ + | comment_field ".spec.proxy.haproxy.resources.limits" \ + | comment_field ".spec.proxy.haproxy.env" \ + | comment_field ".spec.proxy.haproxy.envFrom" \ + | comment_field ".spec.proxy.haproxy.startupProbe" \ + | comment_field ".spec.proxy.haproxy.readinessProbe" \ + | comment_field ".spec.proxy.haproxy.livenessProbe" \ + | comment_field ".spec.proxy.haproxy.affinity.advanced" \ + | comment_field ".spec.proxy.haproxy.expose" \ + | comment_field ".spec.proxy.haproxy.topologySpreadConstraints" \ + | comment_field ".spec.proxy.haproxy.initContainer" \ + | comment_field ".spec.proxy.haproxy.containerSecurityContext" \ + | comment_field ".spec.proxy.haproxy.podSecurityContext" \ + | comment_field ".spec.proxy.haproxy.configuration" \ + | comment_field ".spec.proxy.router.runtimeClassName" \ + | comment_field ".spec.proxy.router.tolerations" \ + | comment_field ".spec.proxy.router.annotations" \ + | comment_field ".spec.proxy.router.labels" \ + | comment_field ".spec.proxy.router.nodeSelector" \ + | comment_field ".spec.proxy.router.priorityClassName" \ + | comment_field ".spec.proxy.router.schedulerName" \ + | comment_field ".spec.proxy.router.serviceAccountName" \ + | comment_field ".spec.proxy.router.imagePullSecrets" \ + | comment_field ".spec.proxy.router.podDisruptionBudget.minAvailable" \ + | comment_field ".spec.proxy.router.resources.limits" \ + | comment_field ".spec.proxy.router.env" \ + | comment_field ".spec.proxy.router.envFrom" \ + | comment_field ".spec.proxy.router.startupProbe" \ + | comment_field ".spec.proxy.router.readinessProbe" \ + | comment_field ".spec.proxy.router.livenessProbe" \ + | comment_field ".spec.proxy.router.affinity.advanced" \ + | comment_field ".spec.proxy.router.expose" \ + | comment_field ".spec.proxy.router.topologySpreadConstraints" \ + | comment_field ".spec.proxy.router.initContainer" \ + | comment_field ".spec.proxy.router.containerSecurityContext" \ + | comment_field ".spec.proxy.router.podSecurityContext" \ + | comment_field ".spec.proxy.router.configuration" \ + | comment_field ".spec.proxy.router.ports" \ + | comment_field ".spec.orchestrator.runtimeClassName" \ + | comment_field ".spec.orchestrator.tolerations" \ + | comment_field ".spec.orchestrator.annotations" \ + | comment_field ".spec.orchestrator.labels" \ + | comment_field ".spec.orchestrator.nodeSelector" \ + | comment_field ".spec.orchestrator.priorityClassName" \ + | comment_field ".spec.orchestrator.schedulerName" \ + | comment_field ".spec.orchestrator.serviceAccountName" \ + | comment_field ".spec.orchestrator.imagePullSecrets" \ + | comment_field ".spec.orchestrator.podDisruptionBudget.minAvailable" \ + | comment_field ".spec.orchestrator.resources.limits" \ + | comment_field ".spec.orchestrator.env" \ + | comment_field ".spec.orchestrator.envFrom" \ + | comment_field ".spec.orchestrator.startupProbe" \ + | comment_field ".spec.orchestrator.readinessProbe" \ + | comment_field ".spec.orchestrator.livenessProbe" \ + | comment_field ".spec.orchestrator.affinity.advanced" \ + | comment_field ".spec.orchestrator.expose" \ + | comment_field ".spec.orchestrator.topologySpreadConstraints" \ + | comment_field ".spec.orchestrator.initContainer" \ + | comment_field ".spec.orchestrator.containerSecurityContext" \ + | comment_field ".spec.orchestrator.podSecurityContext" \ + | comment_field ".spec.pmm.mysqlParams" \ + | comment_field ".spec.pmm.readinessProbes" \ + | comment_field ".spec.pmm.livenessProbes" \ + | comment_field ".spec.pmm.containerSecurityContext" \ + | comment_field ".spec.backup.sourcePod" \ + | comment_field ".spec.backup.schedule" \ + | comment_field ".spec.backup.backoffLimit" \ + | comment_field ".spec.backup.imagePullSecrets" \ + | comment_field ".spec.backup.initContainer" \ + | comment_field ".spec.backup.containerSecurityContext" \ + | comment_field ".spec.backup.storages.azure-blob" \ + | comment_field ".spec.backup.storages.s3-us-west.resources" \ + | comment_field ".spec.backup.storages.s3-us-west.topologySpreadConstraints" \ + | comment_field ".spec.backup.storages.s3-us-west.tolerations" \ + | comment_field ".spec.backup.storages.s3-us-west.containerSecurityContext" \ + | comment_field ".spec.backup.storages.s3-us-west.labels" \ + | comment_field ".spec.backup.storages.s3-us-west.nodeSelector" \ + | comment_field ".spec.backup.storages.s3-us-west.podSecurityContext" \ + | comment_field ".spec.backup.storages.s3-us-west.priorityClassName" \ + | comment_field ".spec.backup.storages.s3-us-west.annotations" \ + | comment_field ".spec.backup.storages.s3-us-west.containerOptions" \ + | comment_field ".spec.backup.storages.s3-us-west.volumeSpec" \ + | comment_field ".spec.toolkit.imagePullSecrets" \ + | comment_field ".spec.toolkit.env" \ + | comment_field ".spec.toolkit.envFrom" \ + | comment_field ".spec.toolkit.resources" \ + | comment_field ".spec.toolkit.containerSecurityContext" \ + | comment_field ".spec.toolkit.startupProbe" \ + | comment_field ".spec.toolkit.readinessProbe" \ + | comment_field ".spec.toolkit.livenessProbe" \ + | normalize_comments +} + +yq --version + +go run cmd/example-gen/main.go \ + | sort_cr_yaml \ + | remove_fields \ + | comment_cr_yaml \ + | cat >deploy/cr.yaml diff --git a/cmd/example-gen/internal/fill/bytype.go b/cmd/example-gen/internal/fill/bytype.go new file mode 100644 index 000000000..3b41bd84b --- /dev/null +++ b/cmd/example-gen/internal/fill/bytype.go @@ -0,0 +1,147 @@ +package fill + +import ( + "fmt" + "reflect" +) + +// ByType recursively fills zero or nil fields in the given pointer value `v` +// using provided preset values matched by type. It modifies `v` in place. +func ByType(v any, presets ...any) error { + rv := reflect.ValueOf(v) + if !rv.IsValid() { + return fmt.Errorf("nil value") + } + if rv.Kind() != reflect.Pointer || rv.IsNil() { + return fmt.Errorf("value must be a non-nil pointer") + } + fillValue(rv, buildRules(presets...)) + return nil +} + +type rules map[reflect.Type]reflect.Value + +func buildRules(presets ...any) rules { + r := make(rules, len(presets)) + for _, p := range presets { + if p == nil { + continue + } + v := reflect.ValueOf(p) + r[v.Type()] = v + } + return r +} + +func fillValue(v reflect.Value, r rules) { + for v.Kind() == reflect.Pointer { + if v.IsNil() { + if ok, def := lookup(v.Type(), r); ok { + v.Set(def) + } else { + return + } + } + v = v.Elem() + } + + switch v.Kind() { + case reflect.Struct: + t := v.Type() + for i := 0; i < v.NumField(); i++ { + sf := t.Field(i) + if sf.PkgPath != "" { // not exported + continue + } + fv := v.Field(i) + + setIfZero(fv, r) + + switch fv.Kind() { + case reflect.Pointer, reflect.Struct, reflect.Slice, reflect.Map, reflect.Interface: + fillValue(fv, r) + } + } + + case reflect.Slice: + for i := 0; i < v.Len(); i++ { + fillValue(v.Index(i), r) + } + + case reflect.Map: + iter := v.MapRange() + for iter.Next() { + k, val := iter.Key(), iter.Value() + + elem := reflect.New(v.Type().Elem()).Elem() + elem.Set(val) + + setIfZero(elem, r) + + switch elem.Kind() { + case reflect.Pointer, reflect.Struct, reflect.Slice, reflect.Map, reflect.Interface: + fillValue(elem, r) + } + v.SetMapIndex(k, elem) + } + + case reflect.Interface: + if !v.IsNil() { + cur := v.Elem() + tmp := reflect.New(cur.Type()).Elem() + tmp.Set(cur) + fillValue(tmp, r) + v.Set(tmp) + } + } +} + +func setIfZero(fv reflect.Value, r rules) { + need := false + switch fv.Kind() { + case reflect.Pointer, reflect.Map, reflect.Slice, reflect.Interface: + need = fv.IsNil() + default: + need = fv.IsZero() + } + if !need { + return + } + if ok, def := lookup(fv.Type(), r); ok { + fv.Set(def) + return + } +} + +func lookup(t reflect.Type, r rules) (bool, reflect.Value) { + if dv, ok := r[t]; ok { + if dv.Type().AssignableTo(t) { + return true, dv + } + if dv.Type().ConvertibleTo(t) { + return true, dv.Convert(t) + } + } + + if t.Kind() == reflect.Pointer { + elem := t.Elem() + if dv, ok := r[elem]; ok { + ptr := reflect.New(elem) + if dv.Type().AssignableTo(elem) { + ptr.Elem().Set(dv) + return true, ptr + } + if dv.Type().ConvertibleTo(elem) { + ptr.Elem().Set(dv.Convert(elem)) + return true, ptr + } + } + } + + if t.Kind() != reflect.Interface { + if dv, ok := r[reflect.PointerTo(t)]; ok && dv.Kind() == reflect.Pointer && !dv.IsNil() && dv.Type().Elem().AssignableTo(t) { + return true, dv.Elem() + } + } + return false, reflect.Value{} +} diff --git a/cmd/example-gen/internal/fill/defaults.go b/cmd/example-gen/internal/fill/defaults.go new file mode 100644 index 000000000..d0aaaa997 --- /dev/null +++ b/cmd/example-gen/internal/fill/defaults.go @@ -0,0 +1,464 @@ +package fill + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/ptr" + + apiv1 "github.com/percona/percona-server-mysql-operator/api/v1" + "github.com/percona/percona-server-mysql-operator/pkg/version" +) + +func SetDefaults(cr *apiv1.PerconaServerMySQL) { + cr.Spec.Unsafe = apiv1.UnsafeFlags{ + MySQLSize: false, + Proxy: false, + ProxySize: false, + Orchestrator: false, + OrchestratorSize: false, + } + cr.Spec.Pause = true + cr.Spec.CRVersion = version.Version() + cr.Spec.VolumeExpansionEnabled = true + cr.Spec.UpdateStrategy = "SmartUpdate" + cr.Spec.InitContainer.Image = "perconalab/percona-server-mysql-operator:main" + + mysqlDefaults(&cr.Spec.MySQL) + haproxyDefaults(cr.Spec.Proxy.HAProxy) + routerDefaults(cr.Spec.Proxy.Router) + orchestratorDefaults(&cr.Spec.Orchestrator) + pmmDefaults(cr.Spec.PMM) + toolkitDefaults(cr.Spec.Toolkit) + backupDefaults(cr.Spec.Backup) +} + +func mysqlDefaults(spec *apiv1.MySQLSpec) { + spec.AutoRecovery = true + spec.Size = 3 + spec.Image = "perconalab/percona-server-mysql-operator:main-psmysql8.4" + spec.RuntimeClassName = ptr.To("image-rc") + spec.Env = []corev1.EnvVar{ + { + Name: "BOOTSTRAP_READ_TIMEOUT", + Value: "600", + }, + } + spec.EnvFrom = []corev1.EnvFromSource{ + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "mysql-env-secret", + }, + }, + }, + } + spec.Labels = map[string]string{ + "rack": "rack-22", + } + spec.Annotations = map[string]string{ + "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", + "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", + } + + spec.Resources = corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("2Gi"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("4Gi"), + }, + } + spec.VolumeSpec = nil + spec.Affinity = nil + spec.ExposePrimary.Enabled = true + spec.TerminationGracePeriodSeconds = ptr.To(int64(600)) + spec.Configuration = `max_connections=250 +innodb_buffer_pool_size={{containerMemoryLimit * 3/4}}` + spec.PriorityClassName = "high-priority" + + spec.Sidecars = []corev1.Container{ + { + Name: "noop-memory", + Image: "busybox", + Command: []string{ + "sleep", "30d", + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "memory-vol", + MountPath: "/var/log/app/memory", + }, + }, + LivenessProbe: &corev1.Probe{}, + ReadinessProbe: &corev1.Probe{}, + StartupProbe: &corev1.Probe{}, + Lifecycle: &corev1.Lifecycle{}, + SecurityContext: &corev1.SecurityContext{}, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("16M"), + }, + }, + }, + { + Name: "noop-pvc", + Image: "busybox", + Command: []string{ + "sleep", "30d", + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "memory-vol", + MountPath: "/var/log/app/memory", + }, + }, + LivenessProbe: &corev1.Probe{}, + ReadinessProbe: &corev1.Probe{}, + StartupProbe: &corev1.Probe{}, + Lifecycle: &corev1.Lifecycle{}, + SecurityContext: &corev1.SecurityContext{}, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("16M"), + }, + }, + }, + } + spec.SidecarVolumes = []corev1.Volume{ + { + Name: "memory-vol", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + Medium: corev1.StorageMediumMemory, + }, + }, + }, + } + + spec.SchedulerName = "default-scheduler" + spec.PodSecurityContext = &corev1.PodSecurityContext{ + FSGroup: ptr.To(int64(1001)), + SupplementalGroups: []int64{1001, 1002, 1003}, + } + spec.ServiceAccountName = "percona-server-mysql-operator-orchestrator" + spec.NodeSelector = map[string]string{ + "topology.kubernetes.io/zone": "us-east-1a", + } +} + +func haproxyDefaults(spec *apiv1.HAProxySpec) { + spec.Image = "perconalab/percona-server-mysql-operator:main-haproxy" + spec.Enabled = true + spec.Size = 3 + spec.RuntimeClassName = ptr.To("image-rc") + spec.Resources = corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("1Gi"), + corev1.ResourceCPU: resource.MustParse("700m"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("1Gi"), + corev1.ResourceCPU: resource.MustParse("600m"), + }, + } + spec.PriorityClassName = "high-priority" + spec.Env = []corev1.EnvVar{ + { + Name: "HA_CONNECTION_TIMEOUT", + Value: "600", + }, + } + spec.EnvFrom = []corev1.EnvFromSource{ + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "haproxy-env-secret", + }, + }, + }, + } + spec.Labels = map[string]string{ + "rack": "rack-22", + } + spec.Annotations = map[string]string{ + "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", + "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", + } + spec.SchedulerName = "default-scheduler" + spec.TerminationGracePeriodSeconds = ptr.To(int64(30)) + spec.ServiceAccountName = "percona-server-mysql-operator-orchestrator" + spec.Configuration = `# the actual default configuration file can be found here https://github.com/percona/percona-server-mysql-operator/blob/main/build/haproxy-global.cfg + +global + maxconn 2048 + external-check + insecure-fork-wanted + stats socket /etc/haproxy/mysql/haproxy.sock mode 600 expose-fd listeners level admin + +defaults + default-server init-addr last,libc,none + log global + mode tcp + retries 10 + timeout client 28800s + timeout connect 100500 + timeout server 28800s + +frontend mysql-primary-in + bind *:3309 accept-proxy + bind *:3306 + mode tcp + option clitcpka + default_backend mysql-primary + +frontend mysql-replicas-in + bind *:3307 + mode tcp + option clitcpka + default_backend mysql-replicas + +frontend stats + bind *:8404 + mode http + http-request use-service prometheus-exporter if { path /metrics }` + spec.NodeSelector = map[string]string{ + "topology.kubernetes.io/zone": "us-east-1a", + } +} + +func routerDefaults(spec *apiv1.MySQLRouterSpec) { + spec.Enabled = true + spec.Size = 3 + spec.Image = "perconalab/percona-server-mysql-operator:main-router8.4" + spec.RuntimeClassName = ptr.To("image-rc") + spec.PriorityClassName = "high-priority" + spec.Labels = map[string]string{ + "rack": "rack-22", + } + spec.Annotations = map[string]string{ + "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", + "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", + } + spec.Resources = corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("256M"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("256M"), + }, + } + spec.Env = []corev1.EnvVar{ + { + Name: "ROUTER_ENV", + Value: "VALUE", + }, + } + spec.EnvFrom = []corev1.EnvFromSource{ + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "router-env-secret", + }, + }, + }, + } + spec.TerminationGracePeriodSeconds = ptr.To(int64(30)) + spec.Configuration = `[default] +logging_folder=/tmp/router/log +[logger] +level=DEBUG` + spec.ServiceAccountName = "percona-server-mysql-operator-orchestrator" + spec.Ports = []corev1.ServicePort{ + { + Name: "http", + Port: 8443, + }, + { + Name: "rw-default", + Port: 3306, + TargetPort: intstr.FromInt(6446), + }, + { + Name: "read-write", + Port: 6446, + }, + { + Name: "read-only", + Port: 6447, + }, + { + Name: "x-read-write", + Port: 6448, + }, + { + Name: "x-read-only", + Port: 6449, + }, + { + Name: "x-default", + Port: 33060, + }, + { + Name: "rw-admin", + Port: 33062, + }, + { + Name: "custom-port", + Port: 1111, + TargetPort: intstr.FromInt(6446), + }, + } + spec.SchedulerName = "default-scheduler" + spec.NodeSelector = map[string]string{ + "topology.kubernetes.io/zone": "us-east-1a", + } +} + +func orchestratorDefaults(spec *apiv1.OrchestratorSpec) { + spec.Enabled = true + spec.Image = "perconalab/percona-server-mysql-operator:main-orchestrator" + spec.Size = 3 + spec.TerminationGracePeriodSeconds = ptr.To(int64(30)) + spec.Affinity = nil + spec.ServiceAccountName = "percona-server-mysql-operator-orchestrator" + spec.PriorityClassName = "high-priority" + spec.RuntimeClassName = ptr.To("image-rc") + spec.Env = []corev1.EnvVar{ + { + Name: "ORC_ENV", + Value: "VALUE", + }, + } + spec.EnvFrom = []corev1.EnvFromSource{ + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "orc-env-secret", + }, + }, + }, + } + spec.Resources = corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("256M"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("128M"), + }, + } + spec.Labels = map[string]string{ + "rack": "rack-22", + } + spec.Annotations = map[string]string{ + "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", + "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", + } + spec.SchedulerName = "default-scheduler" + spec.NodeSelector = map[string]string{ + "topology.kubernetes.io/zone": "us-east-1a", + } +} + +func pmmDefaults(spec *apiv1.PMMSpec) { + spec.Resources = corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("256M"), + corev1.ResourceCPU: resource.MustParse("400m"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("150M"), + corev1.ResourceCPU: resource.MustParse("300m"), + }, + } + + spec.Image = "perconalab/pmm-client:3-dev-latest" + spec.ServerHost = "monitoring-service" + spec.MySQLParams = "PMM_ADMIN_CUSTOM_PARAMS" +} + +func backupDefaults(spec *apiv1.BackupSpec) { + spec.Enabled = true + spec.SourcePod = "ps-cluster1-mysql-1" + spec.Image = "perconalab/percona-server-mysql-operator:main-backup8.4" + spec.Schedule = []apiv1.BackupSchedule{ + { + Name: "sat-night-backup", + Schedule: "0 0 * * 6", + Keep: 3, + StorageName: "s3-us-west", + }, + { + Name: "daily-backup", + Schedule: "0 0 * * *", + Keep: 5, + StorageName: "s3", + }, + } + spec.BackoffLimit = ptr.To(int32(6)) + spec.Storages = map[string]*apiv1.BackupStorageSpec{ + "azure-blob": { + Type: apiv1.BackupStorageAzure, + Azure: &apiv1.BackupStorageAzureSpec{ + ContainerName: "CONTAINER-NAME", + Prefix: "PREFIX-NAME", + CredentialsSecret: "SECRET-NAME", + EndpointURL: "https://accountName.blob.core.windows.net", + StorageClass: "Cool", + }, + }, + "s3-us-west": { + Type: apiv1.BackupStorageS3, + S3: &apiv1.BackupStorageS3Spec{ + Bucket: "S3-BACKUP-BUCKET-NAME-HERE", + Prefix: "PREFIX_NAME", + CredentialsSecret: "ps-cluster1-s3-credentials", + Region: "us-west-2", + EndpointURL: "https://s3.amazonaws.com", + }, + Annotations: map[string]string{ + "testName": "scheduled-backup", + }, + Labels: map[string]string{ + "backupWorker": "True", + }, + PriorityClassName: "high-priority", + RuntimeClassName: ptr.To("image-rc"), + SchedulerName: "default-scheduler", + VerifyTLS: ptr.To(true), + NodeSelector: map[string]string{ + "topology.kubernetes.io/zone": "us-east-1a", + }, + }, + } + spec.ServiceAccountName = "percona-server-mysql-operator-orchestrator" +} + +func toolkitDefaults(spec *apiv1.ToolkitSpec) { + spec.Image = "perconalab/percona-server-mysql-operator:main-toolkit" + spec.Resources = corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("256M"), + corev1.ResourceCPU: resource.MustParse("400m"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("150M"), + corev1.ResourceCPU: resource.MustParse("100m"), + }, + } + spec.Env = []corev1.EnvVar{ + { + Name: "TOOLKIT_ENV", + Value: "VALUE", + }, + } + spec.EnvFrom = []corev1.EnvFromSource{ + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "toolkit-env-secret", + }, + }, + }, + } +} diff --git a/cmd/example-gen/internal/marshal/marshal.go b/cmd/example-gen/internal/marshal/marshal.go new file mode 100644 index 000000000..7bb670c34 --- /dev/null +++ b/cmd/example-gen/internal/marshal/marshal.go @@ -0,0 +1,274 @@ +package marshal + +import ( + "encoding/json" + "errors" + "fmt" + "maps" + "reflect" + "slices" + "strconv" + "strings" +) + +// MarshalIgnoreOmitEmpty marshals v to JSON ignoring `omitempty` tags. +// Types in passthrough are encoded with standard json.Marshal. +func MarshalIgnoreOmitEmpty(v any, passthrough ...any) ([]byte, error) { + rules := buildPassRules(passthrough...) + seen := make(map[uintptr]struct{}) + tree, err := marshalValue(reflect.ValueOf(v), rules, seen) + if err != nil { + return nil, err + } + return json.Marshal(tree) +} + +func marshalValue(rv reflect.Value, rules passRules, seen map[uintptr]struct{}) (any, error) { + if !rv.IsValid() { + return nil, nil + } + + if rules.isPassthrough(rv) { + b, err := json.Marshal(rv.Interface()) + if err != nil { + return nil, err + } + return json.RawMessage(b), nil + } + + switch rv.Kind() { + case reflect.Pointer: + if rv.IsNil() { + return nil, nil + } + ptr := rv.Pointer() + if _, ok := seen[ptr]; ok { + return nil, errors.New("cycle detected while marshaling struct") + } + seen[ptr] = struct{}{} + defer delete(seen, ptr) + return marshalValue(rv.Elem(), rules, seen) + case reflect.Interface: + if rv.IsNil() { + return nil, nil + } + return marshalValue(rv.Elem(), rules, seen) + } + + switch rv.Kind() { + case reflect.Bool: + return rv.Bool(), nil + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return rv.Int(), nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return rv.Uint(), nil + case reflect.Float32, reflect.Float64: + return rv.Float(), nil + case reflect.String: + return rv.String(), nil + + case reflect.Slice, reflect.Array: + n := rv.Len() + out := make([]any, n) + for i := range n { + x, err := marshalValue(rv.Index(i), rules, seen) + if err != nil { + return nil, err + } + out[i] = x + } + return out, nil + + case reflect.Map: + if rv.Type().Key().Kind() != reflect.String { + return nil, fmt.Errorf("map key type %s is not supported by JSON (must be string)", rv.Type().Key()) + } + if rv.IsNil() { + return nil, nil + } + out := make(map[string]any, rv.Len()) + iter := rv.MapRange() + for iter.Next() { + k := iter.Key().String() + val, err := marshalValue(iter.Value(), rules, seen) + if err != nil { + return nil, err + } + out[k] = val + } + return out, nil + + case reflect.Struct: + return marshalStruct(rv, rules, seen) + + default: + b, err := json.Marshal(rv.Interface()) + if err != nil { + return nil, err + } + return json.RawMessage(b), nil + } +} + +func marshalStruct(rv reflect.Value, rules passRules, seen map[uintptr]struct{}) (any, error) { + t := rv.Type() + out := make(map[string]any, t.NumField()) + + for i := 0; i < t.NumField(); i++ { + sf := t.Field(i) + if sf.PkgPath != "" { // unexported + continue + } + + name, opts := parseJSONTag(sf.Tag.Get("json")) + if name == "-" { + continue + } + if name == "" { + name = sf.Name + } + + fv := rv.Field(i) + + if opts["inline"] { + v := derefAll(fv) + if !v.IsValid() { + continue + } + switch v.Kind() { + case reflect.Struct: + objAny, err := marshalStruct(v, rules, seen) + if err != nil { + return nil, err + } + if obj, ok := objAny.(map[string]any); ok { + maps.Copy(out, obj) + } + case reflect.Map: + if v.Type().Key().Kind() != reflect.String { + continue + } + objAny, err := marshalValue(v, rules, seen) + if err != nil { + return nil, err + } + if obj, ok := objAny.(map[string]any); ok { + maps.Copy(out, obj) + } + } + continue + } + + val, err := marshalValue(fv, rules, seen) + if err != nil { + return nil, err + } + + if opts["string"] { + switch v := val.(type) { + case int64, uint64, float64, bool, string, nil: + val = toJSONStringScalar(v) + case json.RawMessage: + val = json.RawMessage(strconv.AppendQuote(nil, string(v))) + } + } + + out[name] = val + } + + return out, nil +} + +func derefAll(v reflect.Value) reflect.Value { + for v.IsValid() && (v.Kind() == reflect.Pointer || v.Kind() == reflect.Interface) { + if v.IsNil() { + return reflect.Value{} + } + v = v.Elem() + } + return v +} + +func parseJSONTag(tag string) (name string, opts map[string]bool) { + opts = make(map[string]bool) + if tag == "" { + return "", opts + } + if i := strings.IndexByte(tag, ','); i >= 0 { + name = tag[:i] + for p := range strings.SplitSeq(tag[i+1:], ",") { + if p != "" { + opts[p] = true + } + } + return name, opts + } + return tag, opts +} + +func toJSONStringScalar(v any) string { + switch x := v.(type) { + case int64: + return strconv.FormatInt(x, 10) + case uint64: + return strconv.FormatUint(x, 10) + case float64: + b, _ := json.Marshal(x) + return string(b) + case bool: + if x { + return "true" + } + return "false" + case string: + return x + case nil: + return "null" + default: + b, _ := json.Marshal(x) + return string(b) + } +} + +type passRules struct { + exact map[reflect.Type]struct{} + ifaces []reflect.Type +} + +func (p passRules) isPassthrough(v reflect.Value) bool { + vt := v.Type() + if _, ok := p.exact[vt]; ok { + return true + } + return slices.ContainsFunc(p.ifaces, vt.Implements) +} + +func buildPassRules(passthrough ...any) passRules { + r := passRules{exact: make(map[reflect.Type]struct{})} + addType := func(t reflect.Type) { + if t == nil { + return + } + if t.Kind() == reflect.Interface { + r.ifaces = append(r.ifaces, t) + return + } + r.exact[t] = struct{}{} + if t.Kind() == reflect.Pointer { + r.exact[t.Elem()] = struct{}{} + } else { + r.exact[reflect.PointerTo(t)] = struct{}{} + } + } + for _, p := range passthrough { + if p == nil { + continue + } + if rt, ok := p.(reflect.Type); ok { + addType(rt) + continue + } + addType(reflect.TypeOf(p)) + } + return r +} diff --git a/cmd/example-gen/main.go b/cmd/example-gen/main.go new file mode 100644 index 000000000..cbc704a7e --- /dev/null +++ b/cmd/example-gen/main.go @@ -0,0 +1,86 @@ +package main + +import ( + "context" + "os" + + "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "sigs.k8s.io/yaml" + + apiv1 "github.com/percona/percona-server-mysql-operator/api/v1" + "github.com/percona/percona-server-mysql-operator/cmd/example-gen/defaults" + "github.com/percona/percona-server-mysql-operator/cmd/example-gen/internal/marshal" +) + +func main() { + if err := printExample(); err != nil { + panic(err) + } +} + +func printExample() error { + cr := &apiv1.PerconaServerMySQL{ + TypeMeta: metav1.TypeMeta{ + Kind: "PerconaServerMySQL", + APIVersion: "ps.percona.com/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "ps-cluster1", + Finalizers: []string{ + "percona.com/delete-mysql-pods-in-order", + "percona.com/delete-ssl", + "percona.com/delete-mysql-pvc", + }, + }, + Spec: apiv1.PerconaServerMySQLSpec{ + Backup: &apiv1.BackupSpec{ + Image: "some-backup", + }, + MySQL: apiv1.MySQLSpec{ + VolumeSpec: &apiv1.VolumeSpec{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + Status: apiv1.PerconaServerMySQLStatus{}, + } + + if err := cr.CheckNSetDefaults(context.TODO(), nil); err != nil { + return errors.Wrap(err, "check and set defaults") + } + defaults.SetManual(cr) + defaults.PresetDefaults(cr) + + jsonBytes, err := marshal.MarshalIgnoreOmitEmpty(cr, + metav1.ObjectMeta{}, + corev1.ResourceRequirements{}, + corev1.SecurityContext{}, + corev1.Probe{}, + corev1.Affinity{}, + corev1.ServicePort{}, + corev1.TopologySpreadConstraint{}, + corev1.Toleration{}, + corev1.EnvVar{}, + corev1.EnvFromSource{}, + corev1.PodSecurityContext{}, + intstr.IntOrString{}, + corev1.EmptyDirVolumeSource{}, + corev1.HostPathVolumeSource{}, + corev1.PersistentVolumeClaimSpec{}, + corev1.Volume{}, + corev1.Container{}, + ) + if err != nil { + return err + } + b, err := yaml.JSONToYAML(jsonBytes) + if err != nil { + return err + } + + _, err = os.Stdout.Write(b) + return err +} diff --git a/config/crd/bases/ps.percona.com_perconaservermysqlbackups.yaml b/config/crd/bases/ps.percona.com_perconaservermysqlbackups.yaml index 6b7c3e3ad..d66dd5f6c 100644 --- a/config/crd/bases/ps.percona.com_perconaservermysqlbackups.yaml +++ b/config/crd/bases/ps.percona.com_perconaservermysqlbackups.yaml @@ -957,8 +957,6 @@ spec: type: string region: type: string - storageClass: - type: string required: - bucket - credentialsSecret diff --git a/config/crd/bases/ps.percona.com_perconaservermysqlrestores.yaml b/config/crd/bases/ps.percona.com_perconaservermysqlrestores.yaml index 42d5dd914..6ea227042 100644 --- a/config/crd/bases/ps.percona.com_perconaservermysqlrestores.yaml +++ b/config/crd/bases/ps.percona.com_perconaservermysqlrestores.yaml @@ -838,8 +838,6 @@ spec: type: string region: type: string - storageClass: - type: string required: - bucket - credentialsSecret diff --git a/config/crd/bases/ps.percona.com_perconaservermysqls.yaml b/config/crd/bases/ps.percona.com_perconaservermysqls.yaml index 2366ab22d..e59eccae1 100644 --- a/config/crd/bases/ps.percona.com_perconaservermysqls.yaml +++ b/config/crd/bases/ps.percona.com_perconaservermysqls.yaml @@ -1432,8 +1432,6 @@ spec: type: string region: type: string - storageClass: - type: string required: - bucket - credentialsSecret @@ -1509,117 +1507,6 @@ spec: - whenUnsatisfiable type: object type: array - volumeSpec: - properties: - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - persistentVolumeClaim: - properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - type: object writeTimeout: format: int32 type: integer @@ -2477,8 +2364,6 @@ spec: type: string region: type: string - storageClass: - type: string required: - bucket - credentialsSecret @@ -7056,117 +6941,6 @@ spec: - whenUnsatisfiable type: object type: array - volumeSpec: - properties: - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - persistentVolumeClaim: - properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - type: object required: - image - size @@ -8697,117 +8471,6 @@ spec: - whenUnsatisfiable type: object type: array - volumeSpec: - properties: - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - persistentVolumeClaim: - properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - type: object required: - image - size @@ -10077,117 +9740,6 @@ spec: - whenUnsatisfiable type: object type: array - volumeSpec: - properties: - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - persistentVolumeClaim: - properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - type: object required: - image - size diff --git a/deploy/bundle.yaml b/deploy/bundle.yaml index 15da3c1e4..2ce836652 100644 --- a/deploy/bundle.yaml +++ b/deploy/bundle.yaml @@ -961,8 +961,6 @@ spec: type: string region: type: string - storageClass: - type: string required: - bucket - credentialsSecret @@ -2008,8 +2006,6 @@ spec: type: string region: type: string - storageClass: - type: string required: - bucket - credentialsSecret @@ -3765,8 +3761,6 @@ spec: type: string region: type: string - storageClass: - type: string required: - bucket - credentialsSecret @@ -3842,117 +3836,6 @@ spec: - whenUnsatisfiable type: object type: array - volumeSpec: - properties: - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - persistentVolumeClaim: - properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - type: object writeTimeout: format: int32 type: integer @@ -4810,8 +4693,6 @@ spec: type: string region: type: string - storageClass: - type: string required: - bucket - credentialsSecret @@ -9389,117 +9270,6 @@ spec: - whenUnsatisfiable type: object type: array - volumeSpec: - properties: - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - persistentVolumeClaim: - properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - type: object required: - image - size @@ -11030,117 +10800,6 @@ spec: - whenUnsatisfiable type: object type: array - volumeSpec: - properties: - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - persistentVolumeClaim: - properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - type: object required: - image - size @@ -12410,117 +12069,6 @@ spec: - whenUnsatisfiable type: object type: array - volumeSpec: - properties: - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - persistentVolumeClaim: - properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - type: object required: - image - size diff --git a/deploy/cr.yaml b/deploy/cr.yaml index b1657b04d..73b9abb29 100644 --- a/deploy/cr.yaml +++ b/deploy/cr.yaml @@ -1,11 +1,12 @@ apiVersion: ps.percona.com/v1 kind: PerconaServerMySQL metadata: - name: ps-cluster1 finalizers: - percona.com/delete-mysql-pods-in-order - # - percona.com/delete-ssl - # - percona.com/delete-mysql-pvc +# - percona.com/delete-ssl +# - percona.com/delete-mysql-pvc + + name: ps-cluster1 spec: # metadata: # annotations: @@ -19,28 +20,28 @@ spec: # orchestratorSize: false # proxy: false # proxySize: false -# pause: false +# pause: true crVersion: 1.0.0 -# enableVolumeExpansion: false +# enableVolumeExpansion: true secretsName: ps-cluster1-secrets sslSecretName: ps-cluster1-ssl updateStrategy: SmartUpdate upgradeOptions: - versionServiceEndpoint: https://check.percona.com apply: disabled + versionServiceEndpoint: https://check.percona.com # initContainer: -# image: perconalab/percona-server-mysql-operator:main # containerSecurityContext: # privileged: false -# runAsUser: 1001 # runAsGroup: 1001 +# runAsUser: 1001 +# image: perconalab/percona-server-mysql-operator:main # resources: -# requests: -# memory: 100M -# cpu: 100m # limits: -# memory: 200M +# cpu: 100m +# memory: 100M +# requests: # cpu: 200m +# memory: 200M # ignoreAnnotations: # - service.beta.kubernetes.io/aws-load-balancer-backend-protocol # ignoreLabels: @@ -51,294 +52,407 @@ spec: # - mysql-2.example.com # - mysql-3.example.com # issuerConf: -# name: special-selfsigned-issuer -# kind: ClusterIssuer # group: cert-manager.io - +# kind: ClusterIssuer +# name: special-selfsigned-issuer mysql: clusterType: group-replication autoRecovery: true +# vaultSecretName: ps-cluster1-vault + size: 3 image: perconalab/percona-server-mysql-operator:main-psmysql8.4 imagePullPolicy: Always +# imagePullSecrets: +# - name: my-secret-1 +# - name: my-secret-2 # runtimeClassName: image-rc -# schedulerName: mycustom-scheduler -# priorityClassName: high-priority +# tolerations: +# - effect: NoExecute +# key: node.alpha.kubernetes.io/unreachable +# operator: Exists +# tolerationSeconds: 6000 +# annotations: +# service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp +# service.beta.kubernetes.io/aws-load-balancer-type: nlb +# labels: +# rack: rack-22 # nodeSelector: -# disktype: ssd +# topology.kubernetes.io/zone: us-east-1a +# priorityClassName: high-priority +# schedulerName: default-scheduler # serviceAccountName: percona-server-mysql-operator-orchestrator -# tolerations: -# - key: "node.alpha.kubernetes.io/unreachable" -# operator: "Exists" -# effect: "NoExecute" -# tolerationSeconds: 6000 -# imagePullSecrets: -# - name: "my-secret-1" -# - name: "my-secret-2" + gracePeriod: 600 # initContainer: -# image: perconalab/percona-server-mysql-operator:main # containerSecurityContext: # privileged: false -# runAsUser: 1001 # runAsGroup: 1001 +# runAsUser: 1001 +# image: perconalab/percona-server-mysql-operator:main # resources: -# requests: -# memory: 100M -# cpu: 100m # limits: -# memory: 200M +# cpu: 100m +# memory: 100M +# requests: # cpu: 200m -# vaultSecretName: ps-cluster1-vault - size: 3 - +# memory: 200M # env: -# - name: BOOTSTRAP_READ_TIMEOUT -# value: "600" +# - name: BOOTSTRAP_READ_TIMEOUT +# value: "600" +# envFrom: +# - secretRef: +# name: mysql-env-secret podDisruptionBudget: maxUnavailable: 1 # minAvailable: 0 resources: - requests: - memory: 2Gi limits: + memory: 2Gi + requests: memory: 4Gi - # startupProbe: +# failureThreshold: 1 # initialDelaySeconds: 15 -# timeoutSeconds: 43200 # periodSeconds: 10 -# failureThreshold: 1 # successThreshold: 1 -# +# timeoutSeconds: 43200 # readinessProbe: -# initialDelaySeconds: 30 -# timeoutSeconds: 10 -# periodSeconds: 10 # failureThreshold: 3 +# initialDelaySeconds: 30 +# periodSeconds: 5 # successThreshold: 1 -# +# timeoutSeconds: 3 # livenessProbe: +# failureThreshold: 3 # initialDelaySeconds: 15 -# timeoutSeconds: 10 # periodSeconds: 10 -# failureThreshold: 3 # successThreshold: 1 - +# timeoutSeconds: 10 affinity: - antiAffinityTopologyKey: "kubernetes.io/hostname" # advanced: # nodeAffinity: # requiredDuringSchedulingIgnoredDuringExecution: # nodeSelectorTerms: -# - matchExpressions: -# - key: kubernetes.io/e2e-az-name -# operator: In -# values: -# - e2e-az1 -# - e2e-az2 +# - matchExpressions: +# - key: kubernetes.io/e2e-az-name +# operator: In +# values: +# - e2e-az1 +# - e2e-az2 + antiAffinityTopologyKey: kubernetes.io/hostname # topologySpreadConstraints: -# - labelSelector: -# matchLabels: -# app.kubernetes.io/name: percona-server -# maxSkew: 1 -# topologyKey: kubernetes.io/hostname -# whenUnsatisfiable: DoNotSchedule - +# - labelSelector: +# matchLabels: +# app.kubernetes.io/name: percona-server +# maxSkew: 1 +# topologyKey: kubernetes.io/hostname +# whenUnsatisfiable: DoNotSchedule +# containerSecurityContext: +# privileged: false +# runAsGroup: 1001 +# runAsUser: 1001 +# podSecurityContext: +# fsGroup: 1001 +# supplementalGroups: +# - 1001 +# - 1002 +# - 1003 exposePrimary: - enabled: true -# type: ClusterIP # annotations: # service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp # service.beta.kubernetes.io/aws-load-balancer-type: nlb + enabled: true # externalTrafficPolicy: Cluster # internalTrafficPolicy: Cluster # labels: # rack: rack-22 # loadBalancerSourceRanges: # - 10.0.0.0/8 - -# expose: -# enabled: false # type: ClusterIP +# labels: +# rack: rack-22 +# loadBalancerSourceRanges: +# - 10.0.0.0/8 +# type: ClusterIP +# internalTrafficPolicy: Cluster +# labels: +# rack: rack-22 +# loadBalancerSourceRanges: +# - 10.0.0.0/8 +# type: ClusterIP +# labels: +# rack: rack-22 +# loadBalancerSourceRanges: +# - 10.0.0.0/8 +# type: ClusterIP +# expose: # annotations: # service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp # service.beta.kubernetes.io/aws-load-balancer-type: nlb +# enabled: false # externalTrafficPolicy: Cluster # internalTrafficPolicy: Cluster # labels: # rack: rack-22 # loadBalancerSourceRanges: # - 10.0.0.0/8 - +# type: ClusterIP volumeSpec: -# emptyDir: { } +# emptyDir: {} # hostPath: # path: /data # type: Directory persistentVolumeClaim: -# storageClassName: standard -# accessModes: [ "ReadWriteOnce" ] +# accessModes: +# - ReadWriteOnce resources: requests: storage: 2Gi - gracePeriod: 600 -# configuration: | -# max_connections=250 -# innodb_buffer_pool_size={{containerMemoryLimit * 3/4}} - -# sidecars: -# - name: noop-memory -# image: busybox -# command: ["sleep", "30d"] -# volumeMounts: -# - name: "memory-vol" -# mountPath: "/var/log/app/memory" -# resources: -# requests: -# memory: 16M -# - name: noop-pvc -# image: busybox -# command: ["sleep", "30d"] -# volumeMounts: -# - name: "pvc-vol" -# mountPath: "/var/log/app/pvc" -# sidecarVolumes: -# - name: "memory-vol" -# emptyDir: -# medium: "Memory" -# sidecarPVCs: -# - name: pvc-vol -# spec: -# resources: -# requests: -# storage: 1Gi -# containerSecurityContext: -# privileged: true -# podSecurityContext: -# fsGroup: 1001 -# supplementalGroups: [1001, 1002, 1003] - +# storageClassName: standard +# configuration: |- +# max_connections=250 +# innodb_buffer_pool_size={{containerMemoryLimit * 3/4}} +# sidecars: +# - command: +# - sleep +# - 30d +# image: busybox +# imagePullPolicy: Always +# lifecycle: {} +# livenessProbe: {} +# name: noop-memory +# readinessProbe: {} +# resources: +# requests: +# memory: 16M +# securityContext: {} +# startupProbe: {} +# volumeMounts: +# - mountPath: /var/log/app/memory +# name: memory-vol +# - command: +# - sleep +# - 30d +# image: busybox +# imagePullPolicy: Always +# lifecycle: {} +# livenessProbe: {} +# name: noop-pvc +# readinessProbe: {} +# resources: +# requests: +# memory: 16M +# securityContext: {} +# startupProbe: {} +# volumeMounts: +# - mountPath: /var/log/app/memory +# name: memory-vol +# sidecarVolumes: +# - emptyDir: +# medium: Memory +# name: memory-vol +# sidecarPVCs: +# - name: pvc-vol +# spec: +# resources: +# requests: +# storage: 1Gi +# configuration: |- +# max_connections=250 +# innodb_buffer_pool_size={{containerMemoryLimit * 3/4}} +# sidecars: +# - command: +# - sleep +# - 30d +# image: busybox +# imagePullPolicy: Always +# lifecycle: {} +# livenessProbe: {} +# name: noop-memory +# readinessProbe: {} +# resources: +# requests: +# memory: 16M +# securityContext: {} +# startupProbe: {} +# volumeMounts: +# - mountPath: /var/log/app/memory +# name: memory-vol +# - command: +# - sleep +# - 30d +# image: busybox +# imagePullPolicy: Always +# lifecycle: {} +# livenessProbe: {} +# name: noop-pvc +# readinessProbe: {} +# resources: +# requests: +# memory: 16M +# securityContext: {} +# startupProbe: {} +# volumeMounts: +# - mountPath: /var/log/app/memory +# name: memory-vol +# sidecarVolumes: +# - emptyDir: +# medium: Memory +# name: memory-vol +# sidecarPVCs: +# - name: pvc-vol +# spec: +# resources: +# requests: +# storage: 1Gi proxy: haproxy: enabled: true - +# expose: +# annotations: +# service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp +# service.beta.kubernetes.io/aws-load-balancer-type: nlb +# externalTrafficPolicy: Cluster +# internalTrafficPolicy: Cluster +# labels: +# rack: rack-22 +# loadBalancerSourceRanges: +# - 10.0.0.0/8 +# type: ClusterIP size: 3 - image: perconalab/percona-server-mysql-operator:main-haproxy imagePullPolicy: Always +# imagePullSecrets: +# - name: my-secret-1 +# - name: my-secret-2 # runtimeClassName: image-rc -# schedulerName: mycustom-scheduler -# priorityClassName: high-priority +# tolerations: +# - effect: NoExecute +# key: node.alpha.kubernetes.io/unreachable +# operator: Exists +# tolerationSeconds: 6000 +# annotations: +# service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp +# service.beta.kubernetes.io/aws-load-balancer-type: nlb +# labels: +# rack: rack-22 # nodeSelector: -# disktype: ssd +# topology.kubernetes.io/zone: us-east-1a +# priorityClassName: high-priority +# schedulerName: default-scheduler # serviceAccountName: percona-server-mysql-operator-orchestrator -# tolerations: -# - key: "node.alpha.kubernetes.io/unreachable" -# operator: "Exists" -# effect: "NoExecute" -# tolerationSeconds: 6000 -# imagePullSecrets: -# - name: "my-secret-1" -# - name: "my-secret-2" - + gracePeriod: 30 +# initContainer: +# containerSecurityContext: +# privileged: false +# runAsGroup: 1001 +# runAsUser: 1001 +# image: perconalab/percona-server-mysql-operator:main +# resources: +# limits: +# cpu: 100m +# memory: 100M +# requests: +# cpu: 200m +# memory: 200M +# env: +# - name: HA_CONNECTION_TIMEOUT +# value: "600" +# envFrom: +# - secretRef: +# name: haproxy-env-secret podDisruptionBudget: maxUnavailable: 1 # minAvailable: 0 resources: - requests: - memory: 1Gi - cpu: 600m # limits: -# memory: 1Gi # cpu: 700m - -# env: -# - name: HA_CONNECTION_TIMEOUT -# value: "1000" -# -# envFrom: -# - secretRef: -# name: haproxy-env-secret - +# memory: 1Gi + requests: + cpu: 600m + memory: 1Gi # startupProbe: -# initialDelaySeconds: 15 -# timeoutSeconds: 43200 -# periodSeconds: 10 -# failureThreshold: 1 +# failureThreshold: 3 +# periodSeconds: 5 # successThreshold: 1 -# -# readinessProbe: # timeoutSeconds: 3 -# periodSeconds: 5 +# readinessProbe: # failureThreshold: 3 +# initialDelaySeconds: 15 +# periodSeconds: 5 # successThreshold: 1 -# +# timeoutSeconds: 1 # livenessProbe: -# timeoutSeconds: 3 -# periodSeconds: 5 -# failureThreshold: 3 +# failureThreshold: 4 +# initialDelaySeconds: 60 +# periodSeconds: 30 # successThreshold: 1 - - gracePeriod: 30 - -# configuration: | -# -# the actual default configuration file can be found here https://github.com/percona/percona-server-mysql-operator/blob/main/build/haproxy-global.cfg -# -# global -# maxconn 2048 -# external-check -# insecure-fork-wanted -# stats socket /etc/haproxy/mysql/haproxy.sock mode 600 expose-fd listeners level admin -# -# defaults -# default-server init-addr last,libc,none -# log global -# mode tcp -# retries 10 -# timeout client 28800s -# timeout connect 100500 -# timeout server 28800s -# -# frontend mysql-primary-in -# bind *:3309 accept-proxy -# bind *:3306 -# mode tcp -# option clitcpka -# default_backend mysql-primary -# -# frontend mysql-replicas-in -# bind *:3307 -# mode tcp -# option clitcpka -# default_backend mysql-replicas -# -# frontend stats -# bind *:8404 -# mode http -# http-request use-service prometheus-exporter if { path /metrics } - +# timeoutSeconds: 3 affinity: - antiAffinityTopologyKey: "kubernetes.io/hostname" # advanced: # nodeAffinity: # requiredDuringSchedulingIgnoredDuringExecution: # nodeSelectorTerms: -# - matchExpressions: -# - key: kubernetes.io/e2e-az-name -# operator: In -# values: -# - e2e-az1 -# - e2e-az2 -# topologySpreadConstraints: -# - labelSelector: -# matchLabels: -# app.kubernetes.io/name: percona-server -# maxSkew: 1 -# topologyKey: kubernetes.io/hostname -# whenUnsatisfiable: DoNotSchedule - +# - matchExpressions: +# - key: kubernetes.io/e2e-az-name +# operator: In +# values: +# - e2e-az1 +# - e2e-az2 + antiAffinityTopologyKey: kubernetes.io/hostname +# topologySpreadConstraints: +# - labelSelector: +# matchLabels: +# app.kubernetes.io/name: percona-server +# maxSkew: 1 +# topologyKey: kubernetes.io/hostname +# whenUnsatisfiable: DoNotSchedule +# containerSecurityContext: +# privileged: false +# runAsGroup: 1001 +# runAsUser: 1001 +# podSecurityContext: +# fsGroup: 1001 +# supplementalGroups: +# - 1001 +# - 1002 +# - 1003 +# configuration: |- +# # the actual default configuration file can be found here https://github.com/percona/percona-server-mysql-operator/blob/main/build/haproxy-global.cfg +# global +# maxconn 2048 +# external-check +# insecure-fork-wanted +# stats socket /etc/haproxy/mysql/haproxy.sock mode 600 expose-fd listeners level admin + +# defaults +# default-server init-addr last,libc,none +# log global +# mode tcp +# retries 10 +# timeout client 28800s +# timeout connect 100500 +# timeout server 28800s + +# frontend mysql-primary-in +# bind *:3309 accept-proxy +# bind *:3306 +# mode tcp +# option clitcpka +# default_backend mysql-primary + +# frontend mysql-replicas-in +# bind *:3307 +# mode tcp +# option clitcpka +# default_backend mysql-replicas + +# frontend stats +# bind *:8404 +# mode http +# http-request use-service prometheus-exporter if { path /metrics } + router: + enabled: true # expose: -# type: ClusterIP # annotations: # service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp # service.beta.kubernetes.io/aws-load-balancer-type: nlb @@ -348,383 +462,477 @@ spec: # rack: rack-22 # loadBalancerSourceRanges: # - 10.0.0.0/8 -# containerSecurityContext: -# privileged: true -# podSecurityContext: -# fsGroup: 1001 -# supplementalGroups: [1001, 1002, 1003] - - router: - enabled: false +# type: ClusterIP + size: 3 image: perconalab/percona-server-mysql-operator:main-router8.4 imagePullPolicy: Always +# imagePullSecrets: +# - name: my-secret-1 +# - name: my-secret-2 # runtimeClassName: image-rc -# schedulerName: mycustom-scheduler -# priorityClassName: high-priority +# tolerations: +# - effect: NoExecute +# key: node.alpha.kubernetes.io/unreachable +# operator: Exists +# tolerationSeconds: 6000 +# annotations: +# service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp +# service.beta.kubernetes.io/aws-load-balancer-type: nlb +# labels: +# rack: rack-22 # nodeSelector: -# disktype: ssd +# topology.kubernetes.io/zone: us-east-1a +# priorityClassName: high-priority +# schedulerName: default-scheduler # serviceAccountName: percona-server-mysql-operator-orchestrator -# startupProbe: -# initialDelaySeconds: 15 -# timeoutSeconds: 43200 -# periodSeconds: 10 -# failureThreshold: 1 -# successThreshold: 1 -# readinessProbe: -# timeoutSeconds: 3 -# periodSeconds: 5 -# failureThreshold: 3 -# successThreshold: 1 -# livenessProbe: -# timeoutSeconds: 3 -# periodSeconds: 5 -# failureThreshold: 3 -# successThreshold: 1 -# tolerations: -# - key: "node.alpha.kubernetes.io/unreachable" -# operator: "Exists" -# effect: "NoExecute" -# tolerationSeconds: 6000 -# imagePullSecrets: -# - name: "my-secret-1" -# - name: "my-secret-2" + gracePeriod: 30 # initContainer: -# image: perconalab/percona-server-mysql-operator:main # containerSecurityContext: # privileged: false -# runAsUser: 1001 # runAsGroup: 1001 +# runAsUser: 1001 +# image: perconalab/percona-server-mysql-operator:main # resources: -# requests: -# memory: 100M -# cpu: 100m # limits: -# memory: 200M -# cpu: 200m -# ports: -# - name: http -# port: 8443 -# targetPort: 0 -# - name: rw-default -# port: 3306 -# targetPort: 6446 -# - name: read-write -# port: 6446 -# targetPort: 0 -# - name: read-only -# port: 6447 -# targetPort: 0 -# - name: x-read-write -# port: 6448 -# targetPort: 0 -# - name: x-read-only -# port: 6449 -# targetPort: 0 -# - name: x-default -# port: 33060 -# targetPort: 0 -# - name: rw-admin -# port: 33062 -# targetPort: 0 -# - name: custom-port -# port: 1111 -# targetPort: 6446 - - size: 3 - +# cpu: 100m +# memory: 100M +# requests: +# cpu: 200m +# memory: 200M +# env: +# - name: ROUTER_ENV +# value: VALUE +# envFrom: +# - secretRef: +# name: router-env-secret podDisruptionBudget: maxUnavailable: 1 # minAvailable: 0 resources: +# limits: +# memory: 256M requests: memory: 256M - limits: - memory: 256M - +# startupProbe: +# failureThreshold: 1 +# initialDelaySeconds: 5 +# periodSeconds: 5 +# successThreshold: 1 +# timeoutSeconds: 3 +# readinessProbe: +# failureThreshold: 3 +# periodSeconds: 5 +# successThreshold: 1 +# timeoutSeconds: 3 +# livenessProbe: +# failureThreshold: 3 +# periodSeconds: 5 +# successThreshold: 1 +# timeoutSeconds: 3 affinity: - antiAffinityTopologyKey: "kubernetes.io/hostname" # advanced: # nodeAffinity: # requiredDuringSchedulingIgnoredDuringExecution: # nodeSelectorTerms: -# - matchExpressions: -# - key: kubernetes.io/e2e-az-name -# operator: In -# values: -# - e2e-az1 -# - e2e-az2 -# topologySpreadConstraints: -# - labelSelector: -# matchLabels: -# app.kubernetes.io/name: percona-server -# maxSkew: 1 -# topologyKey: kubernetes.io/hostname -# whenUnsatisfiable: DoNotSchedule - - gracePeriod: 30 - -# configuration: | -# [default] -# logging_folder=/tmp/router/log -# [logger] -# level=DEBUG - -# expose: -# type: ClusterIP -# annotations: -# service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp -# service.beta.kubernetes.io/aws-load-balancer-type: nlb -# externalTrafficPolicy: Cluster -# internalTrafficPolicy: Cluster -# labels: -# rack: rack-22 -# loadBalancerSourceRanges: -# - 10.0.0.0/8 -# containerSecurityContext: -# privileged: false -# runAsUser: 1001 -# runAsGroup: 1001 -# podSecurityContext: -# fsGroup: 1001 -# supplementalGroups: [1001, 1002, 1003] - +# - matchExpressions: +# - key: kubernetes.io/e2e-az-name +# operator: In +# values: +# - e2e-az1 +# - e2e-az2 + antiAffinityTopologyKey: kubernetes.io/hostname +# topologySpreadConstraints: +# - labelSelector: +# matchLabels: +# app.kubernetes.io/name: percona-server +# maxSkew: 1 +# topologyKey: kubernetes.io/hostname +# whenUnsatisfiable: DoNotSchedule +# containerSecurityContext: +# privileged: false +# runAsGroup: 1001 +# runAsUser: 1001 +# podSecurityContext: +# fsGroup: 1001 +# supplementalGroups: +# - 1001 +# - 1002 +# - 1003 +# ports: +# - name: http +# port: 8443 +# targetPort: 0 +# - name: rw-default +# port: 3306 +# targetPort: 6446 +# - name: read-write +# port: 6446 +# targetPort: 0 +# - name: read-only +# port: 6447 +# targetPort: 0 +# - name: x-read-write +# port: 6448 +# targetPort: 0 +# - name: x-read-only +# port: 6449 +# targetPort: 0 +# - name: x-default +# port: 33060 +# targetPort: 0 +# - name: rw-admin +# port: 33062 +# targetPort: 0 +# - name: custom-port +# port: 1111 +# targetPort: 6446 +# # configuration: |- +# # [default] +# # logging_folder=/tmp/router/log +# # [logger] +# # level=DEBUG orchestrator: - enabled: false + enabled: true +# expose: +# annotations: +# service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp +# service.beta.kubernetes.io/aws-load-balancer-type: nlb +# externalTrafficPolicy: Cluster +# internalTrafficPolicy: Cluster +# labels: +# rack: rack-22 +# loadBalancerSourceRanges: +# - 10.0.0.0/8 +# type: ClusterIP + size: 3 image: perconalab/percona-server-mysql-operator:main-orchestrator imagePullPolicy: Always +# imagePullSecrets: +# - name: my-secret-1 +# - name: my-secret-2 # runtimeClassName: image-rc -# schedulerName: mycustom-scheduler -# priorityClassName: high-priority -# nodeSelector: -# disktype: ssd -# startupProbe: -# initialDelaySeconds: 15 -# timeoutSeconds: 43200 -# periodSeconds: 10 -# failureThreshold: 1 -# successThreshold: 1 -# readinessProbe: -# timeoutSeconds: 3 -# periodSeconds: 5 -# failureThreshold: 3 -# successThreshold: 1 -# livenessProbe: -# timeoutSeconds: 3 -# periodSeconds: 5 -# failureThreshold: 3 -# successThreshold: 1 # tolerations: -# - key: "node.alpha.kubernetes.io/unreachable" -# operator: "Exists" -# effect: "NoExecute" -# tolerationSeconds: 6000 -# imagePullSecrets: -# - name: "my-secret-1" -# - name: "my-secret-2" +# - effect: NoExecute +# key: node.alpha.kubernetes.io/unreachable +# operator: Exists +# tolerationSeconds: 6000 +# annotations: +# service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp +# service.beta.kubernetes.io/aws-load-balancer-type: nlb +# labels: +# rack: rack-22 +# nodeSelector: +# topology.kubernetes.io/zone: us-east-1a +# priorityClassName: high-priority +# schedulerName: default-scheduler # serviceAccountName: percona-server-mysql-operator-orchestrator + gracePeriod: 30 # initContainer: -# image: perconalab/percona-server-mysql-operator:main # containerSecurityContext: # privileged: false -# runAsUser: 1001 # runAsGroup: 1001 +# runAsUser: 1001 +# image: perconalab/percona-server-mysql-operator:main # resources: -# requests: -# memory: 100M -# cpu: 100m # limits: -# memory: 200M +# cpu: 100m +# memory: 100M +# requests: # cpu: 200m - - size: 3 - +# memory: 200M +# env: +# - name: ORC_ENV +# value: VALUE +# envFrom: +# - secretRef: +# name: orc-env-secret + podDisruptionBudget: + maxUnavailable: 1 +# minAvailable: 0 + resources: +# limits: +# memory: 256M + requests: + memory: 128M +# startupProbe: +# failureThreshold: 3 +# periodSeconds: 5 +# successThreshold: 1 +# timeoutSeconds: 3 +# readinessProbe: +# failureThreshold: 3 +# periodSeconds: 5 +# successThreshold: 1 +# timeoutSeconds: 3 +# livenessProbe: +# failureThreshold: 3 +# periodSeconds: 5 +# successThreshold: 1 +# timeoutSeconds: 3 affinity: - antiAffinityTopologyKey: "kubernetes.io/hostname" # advanced: # nodeAffinity: # requiredDuringSchedulingIgnoredDuringExecution: # nodeSelectorTerms: -# - matchExpressions: -# - key: kubernetes.io/e2e-az-name -# operator: In -# values: -# - e2e-az1 -# - e2e-az2 +# - matchExpressions: +# - key: kubernetes.io/e2e-az-name +# operator: In +# values: +# - e2e-az1 +# - e2e-az2 + antiAffinityTopologyKey: kubernetes.io/hostname # topologySpreadConstraints: -# - labelSelector: -# matchLabels: -# app.kubernetes.io/name: percona-server -# maxSkew: 1 -# topologyKey: kubernetes.io/hostname -# whenUnsatisfiable: DoNotSchedule - -# expose: -# type: ClusterIP -# annotations: -# service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp -# service.beta.kubernetes.io/aws-load-balancer-type: nlb -# externalTrafficPolicy: Cluster -# internalTrafficPolicy: Cluster -# labels: -# rack: rack-22 -# loadBalancerSourceRanges: -# - 10.0.0.0/8 +# - labelSelector: +# matchLabels: +# app.kubernetes.io/name: percona-server +# maxSkew: 1 +# topologyKey: kubernetes.io/hostname +# whenUnsatisfiable: DoNotSchedule # containerSecurityContext: # privileged: false -# runAsUser: 1001 # runAsGroup: 1001 +# runAsUser: 1001 # podSecurityContext: -# fsGroup: 1001 -# supplementalGroups: [1001, 1002, 1003] - - podDisruptionBudget: - maxUnavailable: 1 -# minAvailable: 0 - gracePeriod: 30 - - resources: - requests: - memory: 128M - limits: - memory: 256M - +# supplementalGroups: +# - 1001 + configuration: "" pmm: enabled: false image: perconalab/pmm-client:3-dev-latest imagePullPolicy: Always -# readinessProbes: -# initialDelaySeconds: 15 -# timeoutSeconds: 15 -# periodSeconds: 30 -# successThreshold: 1 -# failureThreshold: 5 -# livenessProbes: -# initialDelaySeconds: 300 -# timeoutSeconds: 5 -# periodSeconds: 10 -# successThreshold: 1 -# failureThreshold: 3 -# mysqlParams: "--disable-tablestats-limit=2000" + serverHost: monitoring-service +# mysqlParams: PMM_ADMIN_CUSTOM_PARAMS +# containerSecurityContext: +# privileged: false +# runAsGroup: 1001 +# runAsUser: 1001 resources: + limits: + cpu: 400m + memory: 256M requests: - memory: 150M cpu: 300m -# limits: -# memory: 256M -# cpu: 400m - - serverHost: monitoring-service - + memory: 150M +# readinessProbes: +# failureThreshold: 3 +# periodSeconds: 5 +# successThreshold: 1 +# timeoutSeconds: 3 +# livenessProbes: +# failureThreshold: 3 +# periodSeconds: 5 +# successThreshold: 1 +# timeoutSeconds: 3 backup: enabled: true + pitr: + enabled: false # sourcePod: ps-cluster1-mysql-1 image: perconalab/percona-server-mysql-operator:main-backup8.4 + imagePullPolicy: Always +# imagePullSecrets: +# - name: my-secret-1 +# - name: my-secret-2 # schedule: -# - name: "sat-night-backup" -# schedule: "0 0 * * 6" -# keep: 3 +# - keep: 3 +# name: sat-night-backup +# schedule: 0 0 * * 6 # storageName: s3-us-west -# - name: "daily-backup" -# schedule: "0 0 * * *" -# keep: 5 +# - keep: 5 +# name: daily-backup +# schedule: 0 0 * * * # storageName: s3 # backoffLimit: 6 - imagePullPolicy: Always -# imagePullSecrets: -# - name: "my-secret-1" -# - name: "my-secret-2" + serviceAccountName: percona-server-mysql-operator-orchestrator # initContainer: -# image: perconalab/percona-server-mysql-operator:main # containerSecurityContext: # privileged: false -# runAsUser: 1001 # runAsGroup: 1001 +# runAsUser: 1001 +# image: perconalab/percona-server-mysql-operator:main # resources: -# requests: -# memory: 100M -# cpu: 100m # limits: -# memory: 200M +# cpu: 100m +# memory: 100M +# requests: # cpu: 200m +# memory: 200M # containerSecurityContext: -# privileged: true +# privileged: false +# runAsGroup: 1001 +# runAsUser: 1001 + resources: + limits: + cpu: 100m + memory: 100M + requests: + cpu: 200m + memory: 200M storages: # azure-blob: -# type: azure # azure: # container: CONTAINER-NAME -# prefix: PREFIX-NAME -# endpointUrl: https://accountName.blob.core.windows.net # credentialsSecret: SECRET-NAME - s3-us-west: - type: s3 - verifyTLS: true -# nodeSelector: -# storage: tape -# backupWorker: 'True' +# endpointUrl: https://accountName.blob.core.windows.net +# prefix: PREFIX-NAME +# storageClass: Cool +# containerOptions: +# args: +# xbcloud: +# - --someflag=abc +# xbstream: +# - --someflag=abc +# xtrabackup: +# - --someflag=abc +# env: +# - name: CUSTOM_VAR +# value: "false" +# containerSecurityContext: +# privileged: false +# runAsGroup: 1001 +# runAsUser: 1001 +# podSecurityContext: +# fsGroup: 1001 +# supplementalGroups: +# - 1001 +# - 1002 +# - 1003 # resources: +# limits: +# cpu: 100m +# memory: 100M # requests: -# memory: 1Gi -# cpu: 600m -# affinity: -# nodeAffinity: -# requiredDuringSchedulingIgnoredDuringExecution: -# nodeSelectorTerms: -# - matchExpressions: -# - key: backupWorker -# operator: In -# values: -# - 'True' -# topologySpreadConstraints: -# - labelSelector: -# matchLabels: -# app.kubernetes.io/name: percona-server -# maxSkew: 1 -# topologyKey: kubernetes.io/hostname -# whenUnsatisfiable: DoNotSchedule +# cpu: 200m +# memory: 200M # tolerations: -# - key: "backupWorker" -# operator: "Equal" -# value: "True" -# effect: "NoSchedule" +# - effect: NoExecute +# key: node.alpha.kubernetes.io/unreachable +# operator: Exists +# tolerationSeconds: 6000 +# topologySpreadConstraints: +# - labelSelector: +# matchLabels: +# app.kubernetes.io/name: percona-server +# maxSkew: 1 +# topologyKey: kubernetes.io/hostname +# whenUnsatisfiable: DoNotSchedule +# type: azure +# verifyTLS: null + s3-us-west: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/e2e-az-name + operator: In + values: + - e2e-az1 + - e2e-az2 # annotations: # testName: scheduled-backup -# labels: -# backupWorker: 'True' -# schedulerName: 'default-scheduler' -# priorityClassName: 'high-priority' -# containerSecurityContext: -# privileged: true -# podSecurityContext: -# fsGroup: 1001 -# supplementalGroups: [1001, 1002, 1003] -# runtimeClassName: image-rc # containerOptions: -# env: -# - name: CUSTOM_VAR -# value: "false" # args: -# xtrabackup: -# - "--someflag=abc" # xbcloud: -# - "--someflag=abc" +# - --someflag=abc # xbstream: -# - "--someflag=abc" +# - --someflag=abc +# xtrabackup: +# - --someflag=abc +# env: +# - name: CUSTOM_VAR +# value: "false" +# containerSecurityContext: +# privileged: false +# runAsGroup: 1001 +# runAsUser: 1001 +# labels: +# backupWorker: "True" +# nodeSelector: +# topology.kubernetes.io/zone: us-east-1a +# podSecurityContext: +# fsGroup: 1001 +# supplementalGroups: +# - 1001 +# - 1002 +# - 1003 +# priorityClassName: high-priority +# resources: +# limits: +# cpu: 100m +# memory: 100M +# requests: +# cpu: 200m +# memory: 200M + runtimeClassName: image-rc s3: bucket: S3-BACKUP-BUCKET-NAME-HERE credentialsSecret: ps-cluster1-s3-credentials -# - "--someflag=abc" + endpointUrl: https://s3.amazonaws.com + prefix: PREFIX_NAME region: us-west-2 -# prefix: "" - + schedulerName: default-scheduler +# tolerations: +# - effect: NoExecute +# key: node.alpha.kubernetes.io/unreachable +# operator: Exists +# tolerationSeconds: 6000 +# topologySpreadConstraints: +# - labelSelector: +# matchLabels: +# app.kubernetes.io/name: percona-server +# maxSkew: 1 +# topologyKey: kubernetes.io/hostname +# whenUnsatisfiable: DoNotSchedule + type: s3 + verifyTLS: true +# volumeSpec: +# emptyDir: {} +# hostPath: +# path: /data +# type: Directory +# persistentVolumeClaim: +# accessModes: +# - ReadWriteOnce +# resources: +# requests: +# storage: 2Gi +# storageClassName: standard toolkit: image: perconalab/percona-server-mysql-operator:main-toolkit imagePullPolicy: Always +# imagePullSecrets: +# - name: my-secret-1 +# - name: my-secret-2 +# env: +# - name: TOOLKIT_ENV +# value: VALUE +# envFrom: +# - secretRef: +# name: toolkit-env-secret # resources: -# requests: -# memory: 150M -# cpu: 100m # limits: -# memory: 256M # cpu: 400m - +# memory: 256M +# requests: +# cpu: 100m +# memory: 150M +# containerSecurityContext: +# privileged: false +# runAsGroup: 1001 +# runAsUser: 1001 +# startupProbe: +# failureThreshold: 3 +# periodSeconds: 5 +# successThreshold: 1 +# timeoutSeconds: 3 +# readinessProbe: +# failureThreshold: 3 +# periodSeconds: 5 +# successThreshold: 1 +# timeoutSeconds: 3 +# livenessProbe: +# failureThreshold: 3 +# periodSeconds: 5 +# successThreshold: 1 +# timeoutSeconds: 3 diff --git a/deploy/crd.yaml b/deploy/crd.yaml index e8fceeede..3e13aabe0 100644 --- a/deploy/crd.yaml +++ b/deploy/crd.yaml @@ -961,8 +961,6 @@ spec: type: string region: type: string - storageClass: - type: string required: - bucket - credentialsSecret @@ -2008,8 +2006,6 @@ spec: type: string region: type: string - storageClass: - type: string required: - bucket - credentialsSecret @@ -3765,8 +3761,6 @@ spec: type: string region: type: string - storageClass: - type: string required: - bucket - credentialsSecret @@ -3842,117 +3836,6 @@ spec: - whenUnsatisfiable type: object type: array - volumeSpec: - properties: - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - persistentVolumeClaim: - properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - type: object writeTimeout: format: int32 type: integer @@ -4810,8 +4693,6 @@ spec: type: string region: type: string - storageClass: - type: string required: - bucket - credentialsSecret @@ -9389,117 +9270,6 @@ spec: - whenUnsatisfiable type: object type: array - volumeSpec: - properties: - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - persistentVolumeClaim: - properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - type: object required: - image - size @@ -11030,117 +10800,6 @@ spec: - whenUnsatisfiable type: object type: array - volumeSpec: - properties: - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - persistentVolumeClaim: - properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - type: object required: - image - size @@ -12410,117 +12069,6 @@ spec: - whenUnsatisfiable type: object type: array - volumeSpec: - properties: - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - persistentVolumeClaim: - properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - type: object required: - image - size diff --git a/deploy/cw-bundle.yaml b/deploy/cw-bundle.yaml index 8199f7207..5c633a427 100644 --- a/deploy/cw-bundle.yaml +++ b/deploy/cw-bundle.yaml @@ -961,8 +961,6 @@ spec: type: string region: type: string - storageClass: - type: string required: - bucket - credentialsSecret @@ -2008,8 +2006,6 @@ spec: type: string region: type: string - storageClass: - type: string required: - bucket - credentialsSecret @@ -3765,8 +3761,6 @@ spec: type: string region: type: string - storageClass: - type: string required: - bucket - credentialsSecret @@ -3842,117 +3836,6 @@ spec: - whenUnsatisfiable type: object type: array - volumeSpec: - properties: - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - persistentVolumeClaim: - properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - type: object writeTimeout: format: int32 type: integer @@ -4810,8 +4693,6 @@ spec: type: string region: type: string - storageClass: - type: string required: - bucket - credentialsSecret @@ -9389,117 +9270,6 @@ spec: - whenUnsatisfiable type: object type: array - volumeSpec: - properties: - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - persistentVolumeClaim: - properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - type: object required: - image - size @@ -11030,117 +10800,6 @@ spec: - whenUnsatisfiable type: object type: array - volumeSpec: - properties: - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - persistentVolumeClaim: - properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - type: object required: - image - size @@ -12410,117 +12069,6 @@ spec: - whenUnsatisfiable type: object type: array - volumeSpec: - properties: - emptyDir: - properties: - medium: - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - hostPath: - properties: - path: - type: string - type: - type: string - required: - - path - type: object - persistentVolumeClaim: - properties: - accessModes: - items: - type: string - type: array - x-kubernetes-list-type: atomic - dataSource: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - properties: - apiGroup: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - required: - - kind - - name - type: object - resources: - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - type: object - selector: - properties: - matchExpressions: - items: - properties: - key: - type: string - operator: - type: string - values: - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - type: string - volumeAttributesClassName: - type: string - volumeMode: - type: string - volumeName: - type: string - type: object - type: object required: - image - size diff --git a/pkg/controller/ps/version_test.go b/pkg/controller/ps/version_test.go index 3d83ae60f..faa4d7304 100644 --- a/pkg/controller/ps/version_test.go +++ b/pkg/controller/ps/version_test.go @@ -74,18 +74,18 @@ func TestReconcileVersions(t *testing.T) { }, MySQL: apiv1.MySQLSpec{ ClusterType: apiv1.ClusterTypeGR, - PodSpec: apiv1.PodSpec{ - Size: 3, - VolumeSpec: &apiv1.VolumeSpec{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimSpec{ - Resources: corev1.VolumeResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - corev1.ResourceStorage: q, - }, + VolumeSpec: &apiv1.VolumeSpec{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimSpec{ + Resources: corev1.VolumeResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: q, }, }, }, }, + PodSpec: apiv1.PodSpec{ + Size: 3, + }, }, Proxy: apiv1.ProxySpec{ HAProxy: &apiv1.HAProxySpec{ @@ -118,18 +118,18 @@ func TestReconcileVersions(t *testing.T) { }, MySQL: apiv1.MySQLSpec{ ClusterType: apiv1.ClusterTypeGR, - PodSpec: apiv1.PodSpec{ - Size: 3, - VolumeSpec: &apiv1.VolumeSpec{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimSpec{ - Resources: corev1.VolumeResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - corev1.ResourceStorage: q, - }, + VolumeSpec: &apiv1.VolumeSpec{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimSpec{ + Resources: corev1.VolumeResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: q, }, }, }, }, + PodSpec: apiv1.PodSpec{ + Size: 3, + }, }, Proxy: apiv1.ProxySpec{ HAProxy: &apiv1.HAProxySpec{ @@ -162,18 +162,18 @@ func TestReconcileVersions(t *testing.T) { }, MySQL: apiv1.MySQLSpec{ ClusterType: apiv1.ClusterTypeGR, - PodSpec: apiv1.PodSpec{ - Size: 3, - VolumeSpec: &apiv1.VolumeSpec{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimSpec{ - Resources: corev1.VolumeResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - corev1.ResourceStorage: q, - }, + VolumeSpec: &apiv1.VolumeSpec{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimSpec{ + Resources: corev1.VolumeResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: q, }, }, }, }, + PodSpec: apiv1.PodSpec{ + Size: 3, + }, }, Proxy: apiv1.ProxySpec{ HAProxy: &apiv1.HAProxySpec{ @@ -207,18 +207,18 @@ func TestReconcileVersions(t *testing.T) { }, MySQL: apiv1.MySQLSpec{ ClusterType: apiv1.ClusterTypeGR, - PodSpec: apiv1.PodSpec{ - Size: 3, - VolumeSpec: &apiv1.VolumeSpec{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimSpec{ - Resources: corev1.VolumeResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - corev1.ResourceStorage: q, - }, + VolumeSpec: &apiv1.VolumeSpec{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimSpec{ + Resources: corev1.VolumeResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: q, }, }, }, }, + PodSpec: apiv1.PodSpec{ + Size: 3, + }, }, Proxy: apiv1.ProxySpec{ HAProxy: &apiv1.HAProxySpec{ @@ -349,18 +349,18 @@ func TestGetVersion(t *testing.T) { }, MySQL: apiv1.MySQLSpec{ ClusterType: apiv1.ClusterTypeGR, - PodSpec: apiv1.PodSpec{ - Size: 3, - VolumeSpec: &apiv1.VolumeSpec{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimSpec{ - Resources: corev1.VolumeResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - corev1.ResourceStorage: q, - }, + VolumeSpec: &apiv1.VolumeSpec{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimSpec{ + Resources: corev1.VolumeResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: q, }, }, }, }, + PodSpec: apiv1.PodSpec{ + Size: 3, + }, }, Proxy: apiv1.ProxySpec{ HAProxy: &apiv1.HAProxySpec{ diff --git a/pkg/mysql/mysql_test.go b/pkg/mysql/mysql_test.go index 67177c9b8..702869748 100644 --- a/pkg/mysql/mysql_test.go +++ b/pkg/mysql/mysql_test.go @@ -182,19 +182,19 @@ func TestStatefulsetVolumes(t *testing.T) { }{ "pvc configured": { mysqlSpec: apiv1.MySQLSpec{ - PodSpec: apiv1.PodSpec{ - Size: 3, - TerminationGracePeriodSeconds: ptr.To(int64(30)), - VolumeSpec: &apiv1.VolumeSpec{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimSpec{ - Resources: corev1.VolumeResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - corev1.ResourceStorage: resource.MustParse("1Gi"), - }, + VolumeSpec: &apiv1.VolumeSpec{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimSpec{ + Resources: corev1.VolumeResourceRequirements{ + Requests: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceStorage: resource.MustParse("1Gi"), }, }, }, }, + PodSpec: apiv1.PodSpec{ + Size: 3, + TerminationGracePeriodSeconds: ptr.To(int64(30)), + }, }, expectedStatefulSet: appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{ @@ -224,12 +224,12 @@ func TestStatefulsetVolumes(t *testing.T) { }, "host path configured": { mysqlSpec: apiv1.MySQLSpec{ + VolumeSpec: &apiv1.VolumeSpec{ + HostPath: &corev1.HostPathVolumeSource{}, + }, PodSpec: apiv1.PodSpec{ Size: 3, TerminationGracePeriodSeconds: ptr.To(int64(30)), - VolumeSpec: &apiv1.VolumeSpec{ - HostPath: &corev1.HostPathVolumeSource{}, - }, }, }, expectedStatefulSet: appsv1.StatefulSet{ @@ -266,12 +266,12 @@ func TestStatefulsetVolumes(t *testing.T) { }, "empty dir configured": { mysqlSpec: apiv1.MySQLSpec{ + VolumeSpec: &apiv1.VolumeSpec{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, PodSpec: apiv1.PodSpec{ Size: 3, TerminationGracePeriodSeconds: ptr.To(int64(30)), - VolumeSpec: &apiv1.VolumeSpec{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, }, }, expectedStatefulSet: appsv1.StatefulSet{ @@ -308,13 +308,13 @@ func TestStatefulsetVolumes(t *testing.T) { }, "both entry dir and host path provided - only host path is actually configured due to higher priority": { mysqlSpec: apiv1.MySQLSpec{ + VolumeSpec: &apiv1.VolumeSpec{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + HostPath: &corev1.HostPathVolumeSource{}, + }, PodSpec: apiv1.PodSpec{ Size: 3, TerminationGracePeriodSeconds: ptr.To(int64(30)), - VolumeSpec: &apiv1.VolumeSpec{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - HostPath: &corev1.HostPathVolumeSource{}, - }, }, }, expectedStatefulSet: appsv1.StatefulSet{ diff --git a/pkg/xtrabackup/xtrabackup.go b/pkg/xtrabackup/xtrabackup.go index 76cb2c435..83d18981f 100644 --- a/pkg/xtrabackup/xtrabackup.go +++ b/pkg/xtrabackup/xtrabackup.go @@ -670,10 +670,6 @@ func SetStorageS3(job *batchv1.Job, s3 *apiv1.BackupStorageS3Spec) error { Name: "S3_BUCKET", Value: bucket, }, - { - Name: "S3_STORAGE_CLASS", - Value: s3.StorageClass, - }, } for i := range spec.Containers { @@ -880,7 +876,6 @@ func GetBackupConfig(ctx context.Context, cl client.Client, cr *apiv1.PerconaSer conf.S3.Bucket = bucket conf.S3.Region = s3.Region conf.S3.EndpointURL = s3.EndpointURL - conf.S3.StorageClass = s3.StorageClass conf.Type = apiv1.BackupStorageS3 case apiv1.BackupStorageGCS: gcs := storage.GCS diff --git a/pkg/xtrabackup/xtrabackup_test.go b/pkg/xtrabackup/xtrabackup_test.go index 38c090dd6..9fb12fd50 100644 --- a/pkg/xtrabackup/xtrabackup_test.go +++ b/pkg/xtrabackup/xtrabackup_test.go @@ -37,7 +37,6 @@ func TestJob(t *testing.T) { CredentialsSecret: "secret", Region: "region", EndpointURL: "endpoint", - StorageClass: "storage-class", }, }, } @@ -233,7 +232,6 @@ func TestDeleteJob(t *testing.T) { CredentialsSecret: "secret", Region: "region", EndpointURL: "endpoint", - StorageClass: "storage-class", }, }, } @@ -403,7 +401,6 @@ func TestRestoreJob(t *testing.T) { CredentialsSecret: "secret", Region: "region", EndpointURL: "endpoint", - StorageClass: "storage-class", }, }, } From afa24cedb3cf71e8346867afe08d3c96dc672184 Mon Sep 17 00:00:00 2001 From: Andrii Dema Date: Wed, 22 Oct 2025 15:40:10 +0300 Subject: [PATCH 02/13] Update cmd/example-gen/defaults/preset.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- cmd/example-gen/defaults/preset.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/example-gen/defaults/preset.go b/cmd/example-gen/defaults/preset.go index 9afdf5c1e..c06af4381 100644 --- a/cmd/example-gen/defaults/preset.go +++ b/cmd/example-gen/defaults/preset.go @@ -12,7 +12,7 @@ import ( "github.com/percona/percona-server-mysql-operator/cmd/example-gen/internal/fill" ) -// SetManual assigns default values to fields in example cr.yaml based on their types. +// PresetDefaults assigns default values to fields in example cr.yaml based on their types. // The internal presets slice defines the default value associated with each type. func PresetDefaults(cr *apiv1.PerconaServerMySQL) error { presets := []any{ From a4d6c96ca695543e39d9a5176fd2a600fa678ed7 Mon Sep 17 00:00:00 2001 From: Andrii Dema Date: Mon, 27 Oct 2025 14:18:01 +0200 Subject: [PATCH 03/13] fix commenting --- cmd/example-gen/defaults/manual.go | 4 +- cmd/example-gen/defaults/preset.go | 4 +- cmd/example-gen/generate.sh | 384 +++++++++++++++------------- cmd/example-gen/main.go | 23 +- deploy/cr.yaml | 386 ++++++++++++----------------- 5 files changed, 388 insertions(+), 413 deletions(-) diff --git a/cmd/example-gen/defaults/manual.go b/cmd/example-gen/defaults/manual.go index e5c75d1db..65cee0442 100644 --- a/cmd/example-gen/defaults/manual.go +++ b/cmd/example-gen/defaults/manual.go @@ -10,9 +10,9 @@ import ( "github.com/percona/percona-server-mysql-operator/pkg/version" ) -// SetManual sets default values for empty fields in example cr.yaml +// ManualCluster sets default values for empty fields in example cr.yaml // that are not set by CheckNSetDefaults and cannot be set via PresetDefaults. -func SetManual(cr *apiv1.PerconaServerMySQL) { +func ManualCluster(cr *apiv1.PerconaServerMySQL) { cr.Spec.Unsafe = apiv1.UnsafeFlags{ MySQLSize: false, Proxy: false, diff --git a/cmd/example-gen/defaults/preset.go b/cmd/example-gen/defaults/preset.go index c06af4381..c59cd71b5 100644 --- a/cmd/example-gen/defaults/preset.go +++ b/cmd/example-gen/defaults/preset.go @@ -12,9 +12,9 @@ import ( "github.com/percona/percona-server-mysql-operator/cmd/example-gen/internal/fill" ) -// PresetDefaults assigns default values to fields in example cr.yaml based on their types. +// PresetCluster assigns default values to fields in example cr.yaml based on their types. // The internal presets slice defines the default value associated with each type. -func PresetDefaults(cr *apiv1.PerconaServerMySQL) error { +func PresetCluster(cr *apiv1.PerconaServerMySQL) error { presets := []any{ apiv1.Metadata{ Labels: map[string]string{ diff --git a/cmd/example-gen/generate.sh b/cmd/example-gen/generate.sh index 1b6181dc5..88727d804 100755 --- a/cmd/example-gen/generate.sh +++ b/cmd/example-gen/generate.sh @@ -52,190 +52,222 @@ remove_fields() { | yq 'del(.spec.backup.storages.s3-us-west.gcs)' } -comment_field() { - local field_path="$1" - shift || true - +del_fields_to_comment() { yq - "$@" \ - | yq "($field_path | key) head_comment = (($field_path | key) as \$key | (($field_path | parent) | pick([\$key])) | to_yaml)" \ - | yq "$field_path=null" \ - | yq "$field_path=\"SEDSHOULDDELETEIT\"" \ - | sed '/SEDSHOULDDELETEIT/d' + | yq "del(.metadata.finalizers[1])" \ + | yq "del(.metadata.finalizers[1])" \ + | yq "del(.spec.metadata)" \ + | yq "del(.spec.unsafeFlags)" \ + | yq "del(.spec.pause)" \ + | yq "del(.spec.enableVolumeExpansion)" \ + | yq "del(.spec.initContainer)" \ + | yq "del(.spec.ignoreAnnotations)" \ + | yq "del(.spec.ignoreLabels)" \ + | yq "del(.spec.tls)" \ + | yq "del(.spec.mysql.runtimeClassName)" \ + | yq "del(.spec.mysql.tolerations)" \ + | yq "del(.spec.mysql.annotations)" \ + | yq "del(.spec.mysql.labels)" \ + | yq "del(.spec.mysql.nodeSelector)" \ + | yq "del(.spec.mysql.priorityClassName)" \ + | yq "del(.spec.mysql.schedulerName)" \ + | yq "del(.spec.mysql.serviceAccountName)" \ + | yq "del(.spec.mysql.imagePullSecrets)" \ + | yq "del(.spec.mysql.initContainer)" \ + | yq "del(.spec.mysql.vaultSecretName)" \ + | yq "del(.spec.mysql.env)" \ + | yq "del(.spec.mysql.envFrom)" \ + | yq "del(.spec.mysql.podDisruptionBudget.minAvailable)" \ + | yq "del(.spec.mysql.startupProbe)" \ + | yq "del(.spec.mysql.readinessProbe)" \ + | yq "del(.spec.mysql.livenessProbe)" \ + | yq "del(.spec.mysql.affinity.advanced)" \ + | yq "del(.spec.mysql.topologySpreadConstraints)" \ + | yq "del(.spec.mysql.expose)" \ + | yq "del(.spec.mysql.exposePrimary.annotations)" \ + | yq "del(.spec.mysql.exposePrimary.labels)" \ + | yq "del(.spec.mysql.exposePrimary.loadBalancerSourceRanges)" \ + | yq "del(.spec.mysql.exposePrimary.type)" \ + | yq "del(.spec.mysql.exposePrimary.internalTrafficPolicy)" \ + | yq "del(.spec.mysql.exposePrimary.externalTrafficPolicy)" \ + | yq "del(.spec.mysql.containerSecurityContext)" \ + | yq "del(.spec.mysql.podSecurityContext)" \ + | yq "del(.spec.mysql.configuration)" \ + | yq "del(.spec.mysql.sidecars)" \ + | yq "del(.spec.mysql.sidecarVolumes)" \ + | yq "del(.spec.mysql.sidecarPVCs)" \ + | yq "del(.spec.mysql.volumeSpec.emptyDir)" \ + | yq "del(.spec.mysql.volumeSpec.hostPath)" \ + | yq "del(.spec.mysql.volumeSpec.persistentVolumeClaim.storageClassName)" \ + | yq "del(.spec.mysql.volumeSpec.persistentVolumeClaim.accessModes)" \ + | yq "del(.spec.proxy.haproxy.runtimeClassName)" \ + | yq "del(.spec.proxy.haproxy.tolerations)" \ + | yq "del(.spec.proxy.haproxy.annotations)" \ + | yq "del(.spec.proxy.haproxy.labels)" \ + | yq "del(.spec.proxy.haproxy.nodeSelector)" \ + | yq "del(.spec.proxy.haproxy.priorityClassName)" \ + | yq "del(.spec.proxy.haproxy.schedulerName)" \ + | yq "del(.spec.proxy.haproxy.serviceAccountName)" \ + | yq "del(.spec.proxy.haproxy.imagePullSecrets)" \ + | yq "del(.spec.proxy.haproxy.podDisruptionBudget.minAvailable)" \ + | yq "del(.spec.proxy.haproxy.resources.limits)" \ + | yq "del(.spec.proxy.haproxy.env)" \ + | yq "del(.spec.proxy.haproxy.envFrom)" \ + | yq "del(.spec.proxy.haproxy.startupProbe)" \ + | yq "del(.spec.proxy.haproxy.readinessProbe)" \ + | yq "del(.spec.proxy.haproxy.livenessProbe)" \ + | yq "del(.spec.proxy.haproxy.affinity.advanced)" \ + | yq "del(.spec.proxy.haproxy.expose)" \ + | yq "del(.spec.proxy.haproxy.topologySpreadConstraints)" \ + | yq "del(.spec.proxy.haproxy.initContainer)" \ + | yq "del(.spec.proxy.haproxy.containerSecurityContext)" \ + | yq "del(.spec.proxy.haproxy.podSecurityContext)" \ + | yq "del(.spec.proxy.haproxy.configuration)" \ + | yq "del(.spec.proxy.router.runtimeClassName)" \ + | yq "del(.spec.proxy.router.tolerations)" \ + | yq "del(.spec.proxy.router.annotations)" \ + | yq "del(.spec.proxy.router.labels)" \ + | yq "del(.spec.proxy.router.nodeSelector)" \ + | yq "del(.spec.proxy.router.priorityClassName)" \ + | yq "del(.spec.proxy.router.schedulerName)" \ + | yq "del(.spec.proxy.router.serviceAccountName)" \ + | yq "del(.spec.proxy.router.imagePullSecrets)" \ + | yq "del(.spec.proxy.router.podDisruptionBudget.minAvailable)" \ + | yq "del(.spec.proxy.router.resources.limits)" \ + | yq "del(.spec.proxy.router.env)" \ + | yq "del(.spec.proxy.router.envFrom)" \ + | yq "del(.spec.proxy.router.startupProbe)" \ + | yq "del(.spec.proxy.router.readinessProbe)" \ + | yq "del(.spec.proxy.router.livenessProbe)" \ + | yq "del(.spec.proxy.router.affinity.advanced)" \ + | yq "del(.spec.proxy.router.expose)" \ + | yq "del(.spec.proxy.router.topologySpreadConstraints)" \ + | yq "del(.spec.proxy.router.initContainer)" \ + | yq "del(.spec.proxy.router.containerSecurityContext)" \ + | yq "del(.spec.proxy.router.podSecurityContext)" \ + | yq "del(.spec.proxy.router.configuration)" \ + | yq "del(.spec.proxy.router.ports)" \ + | yq "del(.spec.orchestrator.runtimeClassName)" \ + | yq "del(.spec.orchestrator.tolerations)" \ + | yq "del(.spec.orchestrator.annotations)" \ + | yq "del(.spec.orchestrator.labels)" \ + | yq "del(.spec.orchestrator.nodeSelector)" \ + | yq "del(.spec.orchestrator.priorityClassName)" \ + | yq "del(.spec.orchestrator.schedulerName)" \ + | yq "del(.spec.orchestrator.serviceAccountName)" \ + | yq "del(.spec.orchestrator.imagePullSecrets)" \ + | yq "del(.spec.orchestrator.podDisruptionBudget.minAvailable)" \ + | yq "del(.spec.orchestrator.resources.limits)" \ + | yq "del(.spec.orchestrator.env)" \ + | yq "del(.spec.orchestrator.envFrom)" \ + | yq "del(.spec.orchestrator.startupProbe)" \ + | yq "del(.spec.orchestrator.readinessProbe)" \ + | yq "del(.spec.orchestrator.livenessProbe)" \ + | yq "del(.spec.orchestrator.affinity.advanced)" \ + | yq "del(.spec.orchestrator.expose)" \ + | yq "del(.spec.orchestrator.topologySpreadConstraints)" \ + | yq "del(.spec.orchestrator.initContainer)" \ + | yq "del(.spec.orchestrator.containerSecurityContext)" \ + | yq "del(.spec.orchestrator.podSecurityContext)" \ + | yq "del(.spec.pmm.mysqlParams)" \ + | yq "del(.spec.pmm.readinessProbes)" \ + | yq "del(.spec.pmm.livenessProbes)" \ + | yq "del(.spec.pmm.containerSecurityContext)" \ + | yq "del(.spec.backup.sourcePod)" \ + | yq "del(.spec.backup.schedule)" \ + | yq "del(.spec.backup.backoffLimit)" \ + | yq "del(.spec.backup.imagePullSecrets)" \ + | yq "del(.spec.backup.initContainer)" \ + | yq "del(.spec.backup.containerSecurityContext)" \ + | yq "del(.spec.backup.storages.azure-blob)" \ + | yq "del(.spec.backup.storages.s3-us-west.resources)" \ + | yq "del(.spec.backup.storages.s3-us-west.topologySpreadConstraints)" \ + | yq "del(.spec.backup.storages.s3-us-west.tolerations)" \ + | yq "del(.spec.backup.storages.s3-us-west.containerSecurityContext)" \ + | yq "del(.spec.backup.storages.s3-us-west.labels)" \ + | yq "del(.spec.backup.storages.s3-us-west.nodeSelector)" \ + | yq "del(.spec.backup.storages.s3-us-west.podSecurityContext)" \ + | yq "del(.spec.backup.storages.s3-us-west.priorityClassName)" \ + | yq "del(.spec.backup.storages.s3-us-west.annotations)" \ + | yq "del(.spec.backup.storages.s3-us-west.containerOptions)" \ + | yq "del(.spec.backup.storages.s3-us-west.volumeSpec)" \ + | yq "del(.spec.toolkit.imagePullSecrets)" \ + | yq "del(.spec.toolkit.env)" \ + | yq "del(.spec.toolkit.envFrom)" \ + | yq "del(.spec.toolkit.resources)" \ + | yq "del(.spec.toolkit.containerSecurityContext)" \ + | yq "del(.spec.toolkit.startupProbe)" \ + | yq "del(.spec.toolkit.readinessProbe)" \ + | yq "del(.spec.toolkit.livenessProbe)" } -comment_arr_value() { - local value_path="$1" - shift || true +# TODO: Refactor this function. We should introduce an allowlist of fields to keep uncommented and comment everything else. +comment_cr_yaml() { + local tmp_old tmp_new tmp_diff tmp_out - query=' -('$value_path' | parent | key) foot_comment = -( - ('$value_path' | parent | key | foot_comment) as $existing_comment | - (($existing_comment | select(length > 1) | from_yaml) // []) + ['$value_path'] as $resulting_list | - {"SEDSHOULDDELETEIT": $resulting_list} - | to_yaml -) -' + tmp_old=$(mktemp) + tmp_new=$(mktemp) + tmp_diff=$(mktemp) + tmp_out=$(mktemp) - yq - "$@" \ - | yq "$query" \ - | yq "$value_path=\"SEDSHOULDDELETEIT\"" \ - | sed '/SEDSHOULDDELETEIT/d' -} + yq - "$@" >"$tmp_old" + del_fields_to_comment <"$tmp_old" >"$tmp_new" + + diff "$tmp_old" "$tmp_new" >"$tmp_diff" -normalize_comments() { - sed -r 's/^([[:space:]]*)# (.*)$/#\1\2/' - "$@" + # convert delete-only hunks into change hunks with identical new-range, + # and duplicate "< ..." lines as "> # ..." + awk ' +/^[0-9]+(,[0-9]+)?[acd][0-9]+(,[0-9]+)?$/ { + if (inblock) process() + hdr = $0; inblock = 1; n = 0 + next } +inblock { block[++n] = $0; next } +{ print } -# TODO: Refactor this function. We should introduce an allowlist of fields to keep uncommented and comment everything else. -comment_cr_yaml() { - yq - "$@" \ - | comment_arr_value ".metadata.finalizers[1]" \ - | comment_arr_value ".metadata.finalizers[1]" \ - | comment_field ".spec.metadata" \ - | comment_field ".spec.unsafeFlags" \ - | comment_field ".spec.pause" \ - | comment_field ".spec.enableVolumeExpansion" \ - | comment_field ".spec.initContainer" \ - | comment_field ".spec.ignoreAnnotations" \ - | comment_field ".spec.ignoreLabels" \ - | comment_field ".spec.tls" \ - | comment_field ".spec.mysql.runtimeClassName" \ - | comment_field ".spec.mysql.tolerations" \ - | comment_field ".spec.mysql.annotations" \ - | comment_field ".spec.mysql.labels" \ - | comment_field ".spec.mysql.nodeSelector" \ - | comment_field ".spec.mysql.priorityClassName" \ - | comment_field ".spec.mysql.schedulerName" \ - | comment_field ".spec.mysql.serviceAccountName" \ - | comment_field ".spec.mysql.imagePullSecrets" \ - | comment_field ".spec.mysql.initContainer" \ - | comment_field ".spec.mysql.vaultSecretName" \ - | comment_field ".spec.mysql.env" \ - | comment_field ".spec.mysql.envFrom" \ - | comment_field ".spec.mysql.podDisruptionBudget.minAvailable" \ - | comment_field ".spec.mysql.startupProbe" \ - | comment_field ".spec.mysql.readinessProbe" \ - | comment_field ".spec.mysql.livenessProbe" \ - | comment_field ".spec.mysql.affinity.advanced" \ - | comment_field ".spec.mysql.topologySpreadConstraints" \ - | comment_field ".spec.mysql.expose" \ - | comment_field ".spec.mysql.exposePrimary.annotations" \ - | comment_field ".spec.mysql.exposePrimary.labels" \ - | comment_field ".spec.mysql.exposePrimary.loadBalancerSourceRanges" \ - | comment_field ".spec.mysql.exposePrimary.type" \ - | comment_field ".spec.mysql.exposePrimary.internalTrafficPolicy" \ - | comment_field ".spec.mysql.exposePrimary.externalTrafficPolicy" \ - | comment_field ".spec.mysql.containerSecurityContext" \ - | comment_field ".spec.mysql.podSecurityContext" \ - | comment_field ".spec.mysql.configuration" \ - | comment_field ".spec.mysql.sidecars" \ - | comment_field ".spec.mysql.sidecarVolumes" \ - | comment_field ".spec.mysql.sidecarPVCs" \ - | comment_field ".spec.mysql.volumeSpec.emptyDir" \ - | comment_field ".spec.mysql.volumeSpec.hostPath" \ - | comment_field ".spec.mysql.volumeSpec.persistentVolumeClaim.storageClassName" \ - | comment_field ".spec.mysql.volumeSpec.persistentVolumeClaim.accessModes" \ - | comment_field ".spec.proxy.haproxy.runtimeClassName" \ - | comment_field ".spec.proxy.haproxy.tolerations" \ - | comment_field ".spec.proxy.haproxy.annotations" \ - | comment_field ".spec.proxy.haproxy.labels" \ - | comment_field ".spec.proxy.haproxy.nodeSelector" \ - | comment_field ".spec.proxy.haproxy.priorityClassName" \ - | comment_field ".spec.proxy.haproxy.schedulerName" \ - | comment_field ".spec.proxy.haproxy.serviceAccountName" \ - | comment_field ".spec.proxy.haproxy.imagePullSecrets" \ - | comment_field ".spec.proxy.haproxy.podDisruptionBudget.minAvailable" \ - | comment_field ".spec.proxy.haproxy.resources.limits" \ - | comment_field ".spec.proxy.haproxy.env" \ - | comment_field ".spec.proxy.haproxy.envFrom" \ - | comment_field ".spec.proxy.haproxy.startupProbe" \ - | comment_field ".spec.proxy.haproxy.readinessProbe" \ - | comment_field ".spec.proxy.haproxy.livenessProbe" \ - | comment_field ".spec.proxy.haproxy.affinity.advanced" \ - | comment_field ".spec.proxy.haproxy.expose" \ - | comment_field ".spec.proxy.haproxy.topologySpreadConstraints" \ - | comment_field ".spec.proxy.haproxy.initContainer" \ - | comment_field ".spec.proxy.haproxy.containerSecurityContext" \ - | comment_field ".spec.proxy.haproxy.podSecurityContext" \ - | comment_field ".spec.proxy.haproxy.configuration" \ - | comment_field ".spec.proxy.router.runtimeClassName" \ - | comment_field ".spec.proxy.router.tolerations" \ - | comment_field ".spec.proxy.router.annotations" \ - | comment_field ".spec.proxy.router.labels" \ - | comment_field ".spec.proxy.router.nodeSelector" \ - | comment_field ".spec.proxy.router.priorityClassName" \ - | comment_field ".spec.proxy.router.schedulerName" \ - | comment_field ".spec.proxy.router.serviceAccountName" \ - | comment_field ".spec.proxy.router.imagePullSecrets" \ - | comment_field ".spec.proxy.router.podDisruptionBudget.minAvailable" \ - | comment_field ".spec.proxy.router.resources.limits" \ - | comment_field ".spec.proxy.router.env" \ - | comment_field ".spec.proxy.router.envFrom" \ - | comment_field ".spec.proxy.router.startupProbe" \ - | comment_field ".spec.proxy.router.readinessProbe" \ - | comment_field ".spec.proxy.router.livenessProbe" \ - | comment_field ".spec.proxy.router.affinity.advanced" \ - | comment_field ".spec.proxy.router.expose" \ - | comment_field ".spec.proxy.router.topologySpreadConstraints" \ - | comment_field ".spec.proxy.router.initContainer" \ - | comment_field ".spec.proxy.router.containerSecurityContext" \ - | comment_field ".spec.proxy.router.podSecurityContext" \ - | comment_field ".spec.proxy.router.configuration" \ - | comment_field ".spec.proxy.router.ports" \ - | comment_field ".spec.orchestrator.runtimeClassName" \ - | comment_field ".spec.orchestrator.tolerations" \ - | comment_field ".spec.orchestrator.annotations" \ - | comment_field ".spec.orchestrator.labels" \ - | comment_field ".spec.orchestrator.nodeSelector" \ - | comment_field ".spec.orchestrator.priorityClassName" \ - | comment_field ".spec.orchestrator.schedulerName" \ - | comment_field ".spec.orchestrator.serviceAccountName" \ - | comment_field ".spec.orchestrator.imagePullSecrets" \ - | comment_field ".spec.orchestrator.podDisruptionBudget.minAvailable" \ - | comment_field ".spec.orchestrator.resources.limits" \ - | comment_field ".spec.orchestrator.env" \ - | comment_field ".spec.orchestrator.envFrom" \ - | comment_field ".spec.orchestrator.startupProbe" \ - | comment_field ".spec.orchestrator.readinessProbe" \ - | comment_field ".spec.orchestrator.livenessProbe" \ - | comment_field ".spec.orchestrator.affinity.advanced" \ - | comment_field ".spec.orchestrator.expose" \ - | comment_field ".spec.orchestrator.topologySpreadConstraints" \ - | comment_field ".spec.orchestrator.initContainer" \ - | comment_field ".spec.orchestrator.containerSecurityContext" \ - | comment_field ".spec.orchestrator.podSecurityContext" \ - | comment_field ".spec.pmm.mysqlParams" \ - | comment_field ".spec.pmm.readinessProbes" \ - | comment_field ".spec.pmm.livenessProbes" \ - | comment_field ".spec.pmm.containerSecurityContext" \ - | comment_field ".spec.backup.sourcePod" \ - | comment_field ".spec.backup.schedule" \ - | comment_field ".spec.backup.backoffLimit" \ - | comment_field ".spec.backup.imagePullSecrets" \ - | comment_field ".spec.backup.initContainer" \ - | comment_field ".spec.backup.containerSecurityContext" \ - | comment_field ".spec.backup.storages.azure-blob" \ - | comment_field ".spec.backup.storages.s3-us-west.resources" \ - | comment_field ".spec.backup.storages.s3-us-west.topologySpreadConstraints" \ - | comment_field ".spec.backup.storages.s3-us-west.tolerations" \ - | comment_field ".spec.backup.storages.s3-us-west.containerSecurityContext" \ - | comment_field ".spec.backup.storages.s3-us-west.labels" \ - | comment_field ".spec.backup.storages.s3-us-west.nodeSelector" \ - | comment_field ".spec.backup.storages.s3-us-west.podSecurityContext" \ - | comment_field ".spec.backup.storages.s3-us-west.priorityClassName" \ - | comment_field ".spec.backup.storages.s3-us-west.annotations" \ - | comment_field ".spec.backup.storages.s3-us-west.containerOptions" \ - | comment_field ".spec.backup.storages.s3-us-west.volumeSpec" \ - | comment_field ".spec.toolkit.imagePullSecrets" \ - | comment_field ".spec.toolkit.env" \ - | comment_field ".spec.toolkit.envFrom" \ - | comment_field ".spec.toolkit.resources" \ - | comment_field ".spec.toolkit.containerSecurityContext" \ - | comment_field ".spec.toolkit.startupProbe" \ - | comment_field ".spec.toolkit.readinessProbe" \ - | comment_field ".spec.toolkit.livenessProbe" \ - | normalize_comments +END { if (inblock) process() } + +function process(i, ln, has_lt, has_gt, has_sep, pos, ch, left) { + has_lt = has_gt = has_sep = 0 + for (i = 1; i <= n; i++) { + ln = block[i] + if (ln ~ /^/) has_gt = 1 + else if (ln ~ /^---$/) has_sep = 1 + } + + if (has_lt && !has_gt) { + # if header was a delete, make it a change with identical new-range + pos = 0 + for (i = 1; i <= length(hdr); i++) { ch = substr(hdr, i, 1); if (ch=="a"||ch=="c"||ch=="d"){ pos = i; break } } + if (pos && substr(hdr, pos, 1) == "d") { + left = substr(hdr, 1, pos-1) + hdr = left "c" left + } else if (match(hdr, /[acd]/)) { + pos = RSTART + if (substr(hdr, pos, 1) != "c") hdr = substr(hdr,1,pos-1) "c" substr(hdr,pos+1) + } + + print hdr + for (i = 1; i <= n; i++) print block[i] + if (!has_sep) print "---" + for (i = 1; i <= n; i++) { + ln = block[i] + if (ln ~ /^ #" ln } + } + } else { + print hdr + for (i = 1; i <= n; i++) print block[i] + } + inblock = 0; n = 0 +}' "$tmp_diff" >"$tmp_out" + + mv "$tmp_out" "$tmp_diff" + + patch -s -i "$tmp_diff" "$tmp_old" + + cat "$tmp_old" } yq --version diff --git a/cmd/example-gen/main.go b/cmd/example-gen/main.go index cbc704a7e..e23583e0f 100644 --- a/cmd/example-gen/main.go +++ b/cmd/example-gen/main.go @@ -3,6 +3,8 @@ package main import ( "context" "os" + "os/signal" + "syscall" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" @@ -16,12 +18,15 @@ import ( ) func main() { - if err := printExample(); err != nil { + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer cancel() + + if err := printPerconaServerMySQL(ctx); err != nil { panic(err) } } -func printExample() error { +func printPerconaServerMySQL(ctx context.Context) error { cr := &apiv1.PerconaServerMySQL{ TypeMeta: metav1.TypeMeta{ Kind: "PerconaServerMySQL", @@ -48,11 +53,13 @@ func printExample() error { Status: apiv1.PerconaServerMySQLStatus{}, } - if err := cr.CheckNSetDefaults(context.TODO(), nil); err != nil { + if err := cr.CheckNSetDefaults(ctx, nil); err != nil { return errors.Wrap(err, "check and set defaults") } - defaults.SetManual(cr) - defaults.PresetDefaults(cr) + defaults.ManualCluster(cr) + if err := defaults.PresetCluster(cr); err != nil { + return errors.Wrap(err, "preset defaults") + } jsonBytes, err := marshal.MarshalIgnoreOmitEmpty(cr, metav1.ObjectMeta{}, @@ -74,13 +81,13 @@ func printExample() error { corev1.Container{}, ) if err != nil { - return err + return errors.Wrap(err, "marshal") } b, err := yaml.JSONToYAML(jsonBytes) if err != nil { - return err + return errors.Wrap(err, "json to yaml") } _, err = os.Stdout.Write(b) - return err + return errors.Wrap(err, "write") } diff --git a/deploy/cr.yaml b/deploy/cr.yaml index 73b9abb29..3fc46baa3 100644 --- a/deploy/cr.yaml +++ b/deploy/cr.yaml @@ -5,7 +5,6 @@ metadata: - percona.com/delete-mysql-pods-in-order # - percona.com/delete-ssl # - percona.com/delete-mysql-pvc - name: ps-cluster1 spec: # metadata: @@ -168,22 +167,6 @@ spec: # loadBalancerSourceRanges: # - 10.0.0.0/8 # type: ClusterIP -# labels: -# rack: rack-22 -# loadBalancerSourceRanges: -# - 10.0.0.0/8 -# type: ClusterIP -# internalTrafficPolicy: Cluster -# labels: -# rack: rack-22 -# loadBalancerSourceRanges: -# - 10.0.0.0/8 -# type: ClusterIP -# labels: -# rack: rack-22 -# loadBalancerSourceRanges: -# - 10.0.0.0/8 -# type: ClusterIP # expose: # annotations: # service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp @@ -207,103 +190,55 @@ spec: resources: requests: storage: 2Gi -# storageClassName: standard -# configuration: |- -# max_connections=250 -# innodb_buffer_pool_size={{containerMemoryLimit * 3/4}} -# sidecars: -# - command: -# - sleep -# - 30d -# image: busybox -# imagePullPolicy: Always -# lifecycle: {} -# livenessProbe: {} -# name: noop-memory -# readinessProbe: {} -# resources: -# requests: -# memory: 16M -# securityContext: {} -# startupProbe: {} -# volumeMounts: -# - mountPath: /var/log/app/memory -# name: memory-vol -# - command: -# - sleep -# - 30d -# image: busybox -# imagePullPolicy: Always -# lifecycle: {} -# livenessProbe: {} -# name: noop-pvc -# readinessProbe: {} -# resources: -# requests: -# memory: 16M -# securityContext: {} -# startupProbe: {} -# volumeMounts: -# - mountPath: /var/log/app/memory -# name: memory-vol -# sidecarVolumes: -# - emptyDir: -# medium: Memory -# name: memory-vol -# sidecarPVCs: -# - name: pvc-vol -# spec: -# resources: -# requests: -# storage: 1Gi -# configuration: |- -# max_connections=250 -# innodb_buffer_pool_size={{containerMemoryLimit * 3/4}} -# sidecars: -# - command: -# - sleep -# - 30d -# image: busybox -# imagePullPolicy: Always -# lifecycle: {} -# livenessProbe: {} -# name: noop-memory -# readinessProbe: {} -# resources: -# requests: -# memory: 16M -# securityContext: {} -# startupProbe: {} -# volumeMounts: -# - mountPath: /var/log/app/memory -# name: memory-vol -# - command: -# - sleep -# - 30d -# image: busybox -# imagePullPolicy: Always -# lifecycle: {} -# livenessProbe: {} -# name: noop-pvc -# readinessProbe: {} -# resources: -# requests: -# memory: 16M -# securityContext: {} -# startupProbe: {} -# volumeMounts: -# - mountPath: /var/log/app/memory -# name: memory-vol -# sidecarVolumes: -# - emptyDir: -# medium: Memory -# name: memory-vol -# sidecarPVCs: -# - name: pvc-vol -# spec: -# resources: -# requests: -# storage: 1Gi +# storageClassName: standard +# configuration: |- +# max_connections=250 +# innodb_buffer_pool_size={{containerMemoryLimit * 3/4}} +# sidecars: +# - command: +# - sleep +# - 30d +# image: busybox +# imagePullPolicy: Always +# lifecycle: {} +# livenessProbe: {} +# name: noop-memory +# readinessProbe: {} +# resources: +# requests: +# memory: 16M +# securityContext: {} +# startupProbe: {} +# volumeMounts: +# - mountPath: /var/log/app/memory +# name: memory-vol +# - command: +# - sleep +# - 30d +# image: busybox +# imagePullPolicy: Always +# lifecycle: {} +# livenessProbe: {} +# name: noop-pvc +# readinessProbe: {} +# resources: +# requests: +# memory: 16M +# securityContext: {} +# startupProbe: {} +# volumeMounts: +# - mountPath: /var/log/app/memory +# name: memory-vol +# sidecarVolumes: +# - emptyDir: +# medium: Memory +# name: memory-vol +# sidecarPVCs: +# - name: pvc-vol +# spec: +# resources: +# requests: +# storage: 1Gi proxy: haproxy: enabled: true @@ -399,57 +334,58 @@ spec: # - e2e-az1 # - e2e-az2 antiAffinityTopologyKey: kubernetes.io/hostname -# topologySpreadConstraints: -# - labelSelector: -# matchLabels: -# app.kubernetes.io/name: percona-server -# maxSkew: 1 -# topologyKey: kubernetes.io/hostname -# whenUnsatisfiable: DoNotSchedule -# containerSecurityContext: -# privileged: false -# runAsGroup: 1001 -# runAsUser: 1001 -# podSecurityContext: -# fsGroup: 1001 -# supplementalGroups: -# - 1001 -# - 1002 -# - 1003 -# configuration: |- -# # the actual default configuration file can be found here https://github.com/percona/percona-server-mysql-operator/blob/main/build/haproxy-global.cfg -# global -# maxconn 2048 -# external-check -# insecure-fork-wanted -# stats socket /etc/haproxy/mysql/haproxy.sock mode 600 expose-fd listeners level admin - -# defaults -# default-server init-addr last,libc,none -# log global -# mode tcp -# retries 10 -# timeout client 28800s -# timeout connect 100500 -# timeout server 28800s - -# frontend mysql-primary-in -# bind *:3309 accept-proxy -# bind *:3306 -# mode tcp -# option clitcpka -# default_backend mysql-primary - -# frontend mysql-replicas-in -# bind *:3307 -# mode tcp -# option clitcpka -# default_backend mysql-replicas - -# frontend stats -# bind *:8404 -# mode http -# http-request use-service prometheus-exporter if { path /metrics } +# topologySpreadConstraints: +# - labelSelector: +# matchLabels: +# app.kubernetes.io/name: percona-server +# maxSkew: 1 +# topologyKey: kubernetes.io/hostname +# whenUnsatisfiable: DoNotSchedule +# containerSecurityContext: +# privileged: false +# runAsGroup: 1001 +# runAsUser: 1001 +# podSecurityContext: +# fsGroup: 1001 +# supplementalGroups: +# - 1001 +# - 1002 +# - 1003 +# configuration: |- +# # the actual default configuration file can be found here https://github.com/percona/percona-server-mysql-operator/blob/main/build/haproxy-global.cfg +# +# global +# maxconn 2048 +# external-check +# insecure-fork-wanted +# stats socket /etc/haproxy/mysql/haproxy.sock mode 600 expose-fd listeners level admin +# +# defaults +# default-server init-addr last,libc,none +# log global +# mode tcp +# retries 10 +# timeout client 28800s +# timeout connect 100500 +# timeout server 28800s +# +# frontend mysql-primary-in +# bind *:3309 accept-proxy +# bind *:3306 +# mode tcp +# option clitcpka +# default_backend mysql-primary +# +# frontend mysql-replicas-in +# bind *:3307 +# mode tcp +# option clitcpka +# default_backend mysql-replicas +# +# frontend stats +# bind *:8404 +# mode http +# http-request use-service prometheus-exporter if { path /metrics } router: enabled: true # expose: @@ -541,56 +477,56 @@ spec: # - e2e-az1 # - e2e-az2 antiAffinityTopologyKey: kubernetes.io/hostname -# topologySpreadConstraints: -# - labelSelector: -# matchLabels: -# app.kubernetes.io/name: percona-server -# maxSkew: 1 -# topologyKey: kubernetes.io/hostname -# whenUnsatisfiable: DoNotSchedule -# containerSecurityContext: -# privileged: false -# runAsGroup: 1001 -# runAsUser: 1001 -# podSecurityContext: -# fsGroup: 1001 -# supplementalGroups: -# - 1001 -# - 1002 -# - 1003 -# ports: -# - name: http -# port: 8443 -# targetPort: 0 -# - name: rw-default -# port: 3306 -# targetPort: 6446 -# - name: read-write -# port: 6446 -# targetPort: 0 -# - name: read-only -# port: 6447 -# targetPort: 0 -# - name: x-read-write -# port: 6448 -# targetPort: 0 -# - name: x-read-only -# port: 6449 -# targetPort: 0 -# - name: x-default -# port: 33060 -# targetPort: 0 -# - name: rw-admin -# port: 33062 -# targetPort: 0 -# - name: custom-port -# port: 1111 -# targetPort: 6446 -# # configuration: |- -# # [default] -# # logging_folder=/tmp/router/log -# # [logger] -# # level=DEBUG +# topologySpreadConstraints: +# - labelSelector: +# matchLabels: +# app.kubernetes.io/name: percona-server +# maxSkew: 1 +# topologyKey: kubernetes.io/hostname +# whenUnsatisfiable: DoNotSchedule +# containerSecurityContext: +# privileged: false +# runAsGroup: 1001 +# runAsUser: 1001 +# podSecurityContext: +# fsGroup: 1001 +# supplementalGroups: +# - 1001 +# - 1002 +# - 1003 +# ports: +# - name: http +# port: 8443 +# targetPort: 0 +# - name: rw-default +# port: 3306 +# targetPort: 6446 +# - name: read-write +# port: 6446 +# targetPort: 0 +# - name: read-only +# port: 6447 +# targetPort: 0 +# - name: x-read-write +# port: 6448 +# targetPort: 0 +# - name: x-read-only +# port: 6449 +# targetPort: 0 +# - name: x-default +# port: 33060 +# targetPort: 0 +# - name: rw-admin +# port: 33062 +# targetPort: 0 +# - name: custom-port +# port: 1111 +# targetPort: 6446 +# configuration: |- +# [default] +# logging_folder=/tmp/router/log +# [logger] +# level=DEBUG orchestrator: enabled: true # expose: @@ -713,16 +649,16 @@ spec: requests: cpu: 300m memory: 150M -# readinessProbes: -# failureThreshold: 3 -# periodSeconds: 5 -# successThreshold: 1 -# timeoutSeconds: 3 -# livenessProbes: -# failureThreshold: 3 -# periodSeconds: 5 -# successThreshold: 1 -# timeoutSeconds: 3 +# readinessProbes: +# failureThreshold: 3 +# periodSeconds: 5 +# successThreshold: 1 +# timeoutSeconds: 3 +# livenessProbes: +# failureThreshold: 3 +# periodSeconds: 5 +# successThreshold: 1 +# timeoutSeconds: 3 backup: enabled: true pitr: From a57677271bb10f6802e8cb49fe4719e1335e70d1 Mon Sep 17 00:00:00 2001 From: Andrii Dema Date: Mon, 27 Oct 2025 14:32:10 +0200 Subject: [PATCH 04/13] small fixes --- cmd/example-gen/defaults/manual.go | 6 +- cmd/example-gen/generate.sh | 9 +- cmd/example-gen/internal/fill/defaults.go | 464 ---------------------- deploy/cr.yaml | 62 +-- 4 files changed, 41 insertions(+), 500 deletions(-) delete mode 100644 cmd/example-gen/internal/fill/defaults.go diff --git a/cmd/example-gen/defaults/manual.go b/cmd/example-gen/defaults/manual.go index 65cee0442..70cc9952b 100644 --- a/cmd/example-gen/defaults/manual.go +++ b/cmd/example-gen/defaults/manual.go @@ -20,7 +20,7 @@ func ManualCluster(cr *apiv1.PerconaServerMySQL) { Orchestrator: false, OrchestratorSize: false, } - cr.Spec.Pause = true + cr.Spec.Pause = false cr.Spec.CRVersion = version.Version() cr.Spec.VolumeExpansionEnabled = true cr.Spec.UpdateStrategy = "SmartUpdate" @@ -232,7 +232,7 @@ frontend stats } func routerDefaults(spec *apiv1.MySQLRouterSpec) { - spec.Enabled = true + spec.Enabled = false spec.Size = 3 spec.Image = "perconalab/percona-server-mysql-operator:main-router8.4" spec.RuntimeClassName = ptr.To("image-rc") @@ -320,7 +320,7 @@ level=DEBUG` } func orchestratorDefaults(spec *apiv1.OrchestratorSpec) { - spec.Enabled = true + spec.Enabled = false spec.Image = "perconalab/percona-server-mysql-operator:main-orchestrator" spec.Size = 3 spec.TerminationGracePeriodSeconds = ptr.To(int64(30)) diff --git a/cmd/example-gen/generate.sh b/cmd/example-gen/generate.sh index 88727d804..a5566bd3d 100755 --- a/cmd/example-gen/generate.sh +++ b/cmd/example-gen/generate.sh @@ -133,7 +133,6 @@ del_fields_to_comment() { | yq "del(.spec.proxy.router.serviceAccountName)" \ | yq "del(.spec.proxy.router.imagePullSecrets)" \ | yq "del(.spec.proxy.router.podDisruptionBudget.minAvailable)" \ - | yq "del(.spec.proxy.router.resources.limits)" \ | yq "del(.spec.proxy.router.env)" \ | yq "del(.spec.proxy.router.envFrom)" \ | yq "del(.spec.proxy.router.startupProbe)" \ @@ -157,7 +156,6 @@ del_fields_to_comment() { | yq "del(.spec.orchestrator.serviceAccountName)" \ | yq "del(.spec.orchestrator.imagePullSecrets)" \ | yq "del(.spec.orchestrator.podDisruptionBudget.minAvailable)" \ - | yq "del(.spec.orchestrator.resources.limits)" \ | yq "del(.spec.orchestrator.env)" \ | yq "del(.spec.orchestrator.envFrom)" \ | yq "del(.spec.orchestrator.startupProbe)" \ @@ -173,12 +171,14 @@ del_fields_to_comment() { | yq "del(.spec.pmm.readinessProbes)" \ | yq "del(.spec.pmm.livenessProbes)" \ | yq "del(.spec.pmm.containerSecurityContext)" \ + | yq "del(.spec.pmm.resources.limits)" \ | yq "del(.spec.backup.sourcePod)" \ | yq "del(.spec.backup.schedule)" \ | yq "del(.spec.backup.backoffLimit)" \ | yq "del(.spec.backup.imagePullSecrets)" \ | yq "del(.spec.backup.initContainer)" \ | yq "del(.spec.backup.containerSecurityContext)" \ + | yq "del(.spec.backup.resources)" \ | yq "del(.spec.backup.storages.azure-blob)" \ | yq "del(.spec.backup.storages.s3-us-west.resources)" \ | yq "del(.spec.backup.storages.s3-us-west.topologySpreadConstraints)" \ @@ -191,6 +191,11 @@ del_fields_to_comment() { | yq "del(.spec.backup.storages.s3-us-west.annotations)" \ | yq "del(.spec.backup.storages.s3-us-west.containerOptions)" \ | yq "del(.spec.backup.storages.s3-us-west.volumeSpec)" \ + | yq "del(.spec.backup.storages.s3-us-west.affinity)" \ + | yq "del(.spec.backup.storages.s3-us-west.s3.prefix)" \ + | yq "del(.spec.backup.storages.s3-us-west.s3.endpointUrl)" \ + | yq "del(.spec.backup.storages.s3-us-west.schedulerName)" \ + | yq "del(.spec.backup.storages.s3-us-west.runtimeClassName)" \ | yq "del(.spec.toolkit.imagePullSecrets)" \ | yq "del(.spec.toolkit.env)" \ | yq "del(.spec.toolkit.envFrom)" \ diff --git a/cmd/example-gen/internal/fill/defaults.go b/cmd/example-gen/internal/fill/defaults.go deleted file mode 100644 index d0aaaa997..000000000 --- a/cmd/example-gen/internal/fill/defaults.go +++ /dev/null @@ -1,464 +0,0 @@ -package fill - -import ( - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/utils/ptr" - - apiv1 "github.com/percona/percona-server-mysql-operator/api/v1" - "github.com/percona/percona-server-mysql-operator/pkg/version" -) - -func SetDefaults(cr *apiv1.PerconaServerMySQL) { - cr.Spec.Unsafe = apiv1.UnsafeFlags{ - MySQLSize: false, - Proxy: false, - ProxySize: false, - Orchestrator: false, - OrchestratorSize: false, - } - cr.Spec.Pause = true - cr.Spec.CRVersion = version.Version() - cr.Spec.VolumeExpansionEnabled = true - cr.Spec.UpdateStrategy = "SmartUpdate" - cr.Spec.InitContainer.Image = "perconalab/percona-server-mysql-operator:main" - - mysqlDefaults(&cr.Spec.MySQL) - haproxyDefaults(cr.Spec.Proxy.HAProxy) - routerDefaults(cr.Spec.Proxy.Router) - orchestratorDefaults(&cr.Spec.Orchestrator) - pmmDefaults(cr.Spec.PMM) - toolkitDefaults(cr.Spec.Toolkit) - backupDefaults(cr.Spec.Backup) -} - -func mysqlDefaults(spec *apiv1.MySQLSpec) { - spec.AutoRecovery = true - spec.Size = 3 - spec.Image = "perconalab/percona-server-mysql-operator:main-psmysql8.4" - spec.RuntimeClassName = ptr.To("image-rc") - spec.Env = []corev1.EnvVar{ - { - Name: "BOOTSTRAP_READ_TIMEOUT", - Value: "600", - }, - } - spec.EnvFrom = []corev1.EnvFromSource{ - { - SecretRef: &corev1.SecretEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "mysql-env-secret", - }, - }, - }, - } - spec.Labels = map[string]string{ - "rack": "rack-22", - } - spec.Annotations = map[string]string{ - "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", - "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", - } - - spec.Resources = corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("2Gi"), - }, - Requests: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("4Gi"), - }, - } - spec.VolumeSpec = nil - spec.Affinity = nil - spec.ExposePrimary.Enabled = true - spec.TerminationGracePeriodSeconds = ptr.To(int64(600)) - spec.Configuration = `max_connections=250 -innodb_buffer_pool_size={{containerMemoryLimit * 3/4}}` - spec.PriorityClassName = "high-priority" - - spec.Sidecars = []corev1.Container{ - { - Name: "noop-memory", - Image: "busybox", - Command: []string{ - "sleep", "30d", - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: "memory-vol", - MountPath: "/var/log/app/memory", - }, - }, - LivenessProbe: &corev1.Probe{}, - ReadinessProbe: &corev1.Probe{}, - StartupProbe: &corev1.Probe{}, - Lifecycle: &corev1.Lifecycle{}, - SecurityContext: &corev1.SecurityContext{}, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("16M"), - }, - }, - }, - { - Name: "noop-pvc", - Image: "busybox", - Command: []string{ - "sleep", "30d", - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: "memory-vol", - MountPath: "/var/log/app/memory", - }, - }, - LivenessProbe: &corev1.Probe{}, - ReadinessProbe: &corev1.Probe{}, - StartupProbe: &corev1.Probe{}, - Lifecycle: &corev1.Lifecycle{}, - SecurityContext: &corev1.SecurityContext{}, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("16M"), - }, - }, - }, - } - spec.SidecarVolumes = []corev1.Volume{ - { - Name: "memory-vol", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{ - Medium: corev1.StorageMediumMemory, - }, - }, - }, - } - - spec.SchedulerName = "default-scheduler" - spec.PodSecurityContext = &corev1.PodSecurityContext{ - FSGroup: ptr.To(int64(1001)), - SupplementalGroups: []int64{1001, 1002, 1003}, - } - spec.ServiceAccountName = "percona-server-mysql-operator-orchestrator" - spec.NodeSelector = map[string]string{ - "topology.kubernetes.io/zone": "us-east-1a", - } -} - -func haproxyDefaults(spec *apiv1.HAProxySpec) { - spec.Image = "perconalab/percona-server-mysql-operator:main-haproxy" - spec.Enabled = true - spec.Size = 3 - spec.RuntimeClassName = ptr.To("image-rc") - spec.Resources = corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("1Gi"), - corev1.ResourceCPU: resource.MustParse("700m"), - }, - Requests: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("1Gi"), - corev1.ResourceCPU: resource.MustParse("600m"), - }, - } - spec.PriorityClassName = "high-priority" - spec.Env = []corev1.EnvVar{ - { - Name: "HA_CONNECTION_TIMEOUT", - Value: "600", - }, - } - spec.EnvFrom = []corev1.EnvFromSource{ - { - SecretRef: &corev1.SecretEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "haproxy-env-secret", - }, - }, - }, - } - spec.Labels = map[string]string{ - "rack": "rack-22", - } - spec.Annotations = map[string]string{ - "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", - "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", - } - spec.SchedulerName = "default-scheduler" - spec.TerminationGracePeriodSeconds = ptr.To(int64(30)) - spec.ServiceAccountName = "percona-server-mysql-operator-orchestrator" - spec.Configuration = `# the actual default configuration file can be found here https://github.com/percona/percona-server-mysql-operator/blob/main/build/haproxy-global.cfg - -global - maxconn 2048 - external-check - insecure-fork-wanted - stats socket /etc/haproxy/mysql/haproxy.sock mode 600 expose-fd listeners level admin - -defaults - default-server init-addr last,libc,none - log global - mode tcp - retries 10 - timeout client 28800s - timeout connect 100500 - timeout server 28800s - -frontend mysql-primary-in - bind *:3309 accept-proxy - bind *:3306 - mode tcp - option clitcpka - default_backend mysql-primary - -frontend mysql-replicas-in - bind *:3307 - mode tcp - option clitcpka - default_backend mysql-replicas - -frontend stats - bind *:8404 - mode http - http-request use-service prometheus-exporter if { path /metrics }` - spec.NodeSelector = map[string]string{ - "topology.kubernetes.io/zone": "us-east-1a", - } -} - -func routerDefaults(spec *apiv1.MySQLRouterSpec) { - spec.Enabled = true - spec.Size = 3 - spec.Image = "perconalab/percona-server-mysql-operator:main-router8.4" - spec.RuntimeClassName = ptr.To("image-rc") - spec.PriorityClassName = "high-priority" - spec.Labels = map[string]string{ - "rack": "rack-22", - } - spec.Annotations = map[string]string{ - "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", - "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", - } - spec.Resources = corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("256M"), - }, - Requests: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("256M"), - }, - } - spec.Env = []corev1.EnvVar{ - { - Name: "ROUTER_ENV", - Value: "VALUE", - }, - } - spec.EnvFrom = []corev1.EnvFromSource{ - { - SecretRef: &corev1.SecretEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "router-env-secret", - }, - }, - }, - } - spec.TerminationGracePeriodSeconds = ptr.To(int64(30)) - spec.Configuration = `[default] -logging_folder=/tmp/router/log -[logger] -level=DEBUG` - spec.ServiceAccountName = "percona-server-mysql-operator-orchestrator" - spec.Ports = []corev1.ServicePort{ - { - Name: "http", - Port: 8443, - }, - { - Name: "rw-default", - Port: 3306, - TargetPort: intstr.FromInt(6446), - }, - { - Name: "read-write", - Port: 6446, - }, - { - Name: "read-only", - Port: 6447, - }, - { - Name: "x-read-write", - Port: 6448, - }, - { - Name: "x-read-only", - Port: 6449, - }, - { - Name: "x-default", - Port: 33060, - }, - { - Name: "rw-admin", - Port: 33062, - }, - { - Name: "custom-port", - Port: 1111, - TargetPort: intstr.FromInt(6446), - }, - } - spec.SchedulerName = "default-scheduler" - spec.NodeSelector = map[string]string{ - "topology.kubernetes.io/zone": "us-east-1a", - } -} - -func orchestratorDefaults(spec *apiv1.OrchestratorSpec) { - spec.Enabled = true - spec.Image = "perconalab/percona-server-mysql-operator:main-orchestrator" - spec.Size = 3 - spec.TerminationGracePeriodSeconds = ptr.To(int64(30)) - spec.Affinity = nil - spec.ServiceAccountName = "percona-server-mysql-operator-orchestrator" - spec.PriorityClassName = "high-priority" - spec.RuntimeClassName = ptr.To("image-rc") - spec.Env = []corev1.EnvVar{ - { - Name: "ORC_ENV", - Value: "VALUE", - }, - } - spec.EnvFrom = []corev1.EnvFromSource{ - { - SecretRef: &corev1.SecretEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "orc-env-secret", - }, - }, - }, - } - spec.Resources = corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("256M"), - }, - Requests: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("128M"), - }, - } - spec.Labels = map[string]string{ - "rack": "rack-22", - } - spec.Annotations = map[string]string{ - "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", - "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", - } - spec.SchedulerName = "default-scheduler" - spec.NodeSelector = map[string]string{ - "topology.kubernetes.io/zone": "us-east-1a", - } -} - -func pmmDefaults(spec *apiv1.PMMSpec) { - spec.Resources = corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("256M"), - corev1.ResourceCPU: resource.MustParse("400m"), - }, - Requests: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("150M"), - corev1.ResourceCPU: resource.MustParse("300m"), - }, - } - - spec.Image = "perconalab/pmm-client:3-dev-latest" - spec.ServerHost = "monitoring-service" - spec.MySQLParams = "PMM_ADMIN_CUSTOM_PARAMS" -} - -func backupDefaults(spec *apiv1.BackupSpec) { - spec.Enabled = true - spec.SourcePod = "ps-cluster1-mysql-1" - spec.Image = "perconalab/percona-server-mysql-operator:main-backup8.4" - spec.Schedule = []apiv1.BackupSchedule{ - { - Name: "sat-night-backup", - Schedule: "0 0 * * 6", - Keep: 3, - StorageName: "s3-us-west", - }, - { - Name: "daily-backup", - Schedule: "0 0 * * *", - Keep: 5, - StorageName: "s3", - }, - } - spec.BackoffLimit = ptr.To(int32(6)) - spec.Storages = map[string]*apiv1.BackupStorageSpec{ - "azure-blob": { - Type: apiv1.BackupStorageAzure, - Azure: &apiv1.BackupStorageAzureSpec{ - ContainerName: "CONTAINER-NAME", - Prefix: "PREFIX-NAME", - CredentialsSecret: "SECRET-NAME", - EndpointURL: "https://accountName.blob.core.windows.net", - StorageClass: "Cool", - }, - }, - "s3-us-west": { - Type: apiv1.BackupStorageS3, - S3: &apiv1.BackupStorageS3Spec{ - Bucket: "S3-BACKUP-BUCKET-NAME-HERE", - Prefix: "PREFIX_NAME", - CredentialsSecret: "ps-cluster1-s3-credentials", - Region: "us-west-2", - EndpointURL: "https://s3.amazonaws.com", - }, - Annotations: map[string]string{ - "testName": "scheduled-backup", - }, - Labels: map[string]string{ - "backupWorker": "True", - }, - PriorityClassName: "high-priority", - RuntimeClassName: ptr.To("image-rc"), - SchedulerName: "default-scheduler", - VerifyTLS: ptr.To(true), - NodeSelector: map[string]string{ - "topology.kubernetes.io/zone": "us-east-1a", - }, - }, - } - spec.ServiceAccountName = "percona-server-mysql-operator-orchestrator" -} - -func toolkitDefaults(spec *apiv1.ToolkitSpec) { - spec.Image = "perconalab/percona-server-mysql-operator:main-toolkit" - spec.Resources = corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("256M"), - corev1.ResourceCPU: resource.MustParse("400m"), - }, - Requests: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("150M"), - corev1.ResourceCPU: resource.MustParse("100m"), - }, - } - spec.Env = []corev1.EnvVar{ - { - Name: "TOOLKIT_ENV", - Value: "VALUE", - }, - } - spec.EnvFrom = []corev1.EnvFromSource{ - { - SecretRef: &corev1.SecretEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "toolkit-env-secret", - }, - }, - }, - } -} diff --git a/deploy/cr.yaml b/deploy/cr.yaml index 3fc46baa3..a85983e58 100644 --- a/deploy/cr.yaml +++ b/deploy/cr.yaml @@ -19,7 +19,7 @@ spec: # orchestratorSize: false # proxy: false # proxySize: false -# pause: true +# pause: false crVersion: 1.0.0 # enableVolumeExpansion: true secretsName: ps-cluster1-secrets @@ -387,7 +387,7 @@ spec: # mode http # http-request use-service prometheus-exporter if { path /metrics } router: - enabled: true + enabled: false # expose: # annotations: # service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp @@ -445,8 +445,8 @@ spec: maxUnavailable: 1 # minAvailable: 0 resources: -# limits: -# memory: 256M + limits: + memory: 256M requests: memory: 256M # startupProbe: @@ -528,7 +528,7 @@ spec: # [logger] # level=DEBUG orchestrator: - enabled: true + enabled: false # expose: # annotations: # service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp @@ -586,8 +586,8 @@ spec: maxUnavailable: 1 # minAvailable: 0 resources: -# limits: -# memory: 256M + limits: + memory: 256M requests: memory: 128M # startupProbe: @@ -643,9 +643,9 @@ spec: # runAsGroup: 1001 # runAsUser: 1001 resources: - limits: - cpu: 400m - memory: 256M +# limits: +# cpu: 400m +# memory: 256M requests: cpu: 300m memory: 150M @@ -697,13 +697,13 @@ spec: # privileged: false # runAsGroup: 1001 # runAsUser: 1001 - resources: - limits: - cpu: 100m - memory: 100M - requests: - cpu: 200m - memory: 200M +# resources: +# limits: +# cpu: 100m +# memory: 100M +# requests: +# cpu: 200m +# memory: 200M storages: # azure-blob: # azure: @@ -755,16 +755,16 @@ spec: # type: azure # verifyTLS: null s3-us-west: - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: kubernetes.io/e2e-az-name - operator: In - values: - - e2e-az1 - - e2e-az2 +# affinity: +# nodeAffinity: +# requiredDuringSchedulingIgnoredDuringExecution: +# nodeSelectorTerms: +# - matchExpressions: +# - key: kubernetes.io/e2e-az-name +# operator: In +# values: +# - e2e-az1 +# - e2e-az2 # annotations: # testName: scheduled-backup # containerOptions: @@ -800,14 +800,14 @@ spec: # requests: # cpu: 200m # memory: 200M - runtimeClassName: image-rc +# runtimeClassName: image-rc s3: bucket: S3-BACKUP-BUCKET-NAME-HERE credentialsSecret: ps-cluster1-s3-credentials - endpointUrl: https://s3.amazonaws.com - prefix: PREFIX_NAME +# endpointUrl: https://s3.amazonaws.com +# prefix: PREFIX_NAME region: us-west-2 - schedulerName: default-scheduler +# schedulerName: default-scheduler # tolerations: # - effect: NoExecute # key: node.alpha.kubernetes.io/unreachable From 48ef7b806dc3db4517911955d73c68db8d462262 Mon Sep 17 00:00:00 2001 From: Andrii Dema Date: Mon, 27 Oct 2025 16:47:33 +0200 Subject: [PATCH 05/13] reduce size of `manual.go` --- cmd/example-gen/defaults/configs.go | 46 ++++ cmd/example-gen/defaults/images.go | 12 + cmd/example-gen/defaults/manual.go | 401 +++++----------------------- cmd/example-gen/defaults/util.go | 58 ++++ cmd/example-gen/generate.sh | 1 + deploy/cr.yaml | 12 +- 6 files changed, 195 insertions(+), 335 deletions(-) create mode 100644 cmd/example-gen/defaults/configs.go create mode 100644 cmd/example-gen/defaults/images.go create mode 100644 cmd/example-gen/defaults/util.go diff --git a/cmd/example-gen/defaults/configs.go b/cmd/example-gen/defaults/configs.go new file mode 100644 index 000000000..5629a96df --- /dev/null +++ b/cmd/example-gen/defaults/configs.go @@ -0,0 +1,46 @@ +package defaults + +const ( + ConfigurationHAProxy = `# the actual default configuration file can be found here https://github.com/percona/percona-server-mysql-operator/blob/main/build/haproxy-global.cfg + +global + maxconn 2048 + external-check + insecure-fork-wanted + stats socket /etc/haproxy/mysql/haproxy.sock mode 600 expose-fd listeners level admin + +defaults + default-server init-addr last,libc,none + log global + mode tcp + retries 10 + timeout client 28800s + timeout connect 100500 + timeout server 28800s + +frontend mysql-primary-in + bind *:3309 accept-proxy + bind *:3306 + mode tcp + option clitcpka + default_backend mysql-primary + +frontend mysql-replicas-in + bind *:3307 + mode tcp + option clitcpka + default_backend mysql-replicas + +frontend stats + bind *:8404 + mode http + http-request use-service prometheus-exporter if { path /metrics }` + + ConfigurationMySQL = `max_connections=250 +innodb_buffer_pool_size={{containerMemoryLimit * 3/4}}` + + ConfigurationRouter = `[default] +logging_folder=/tmp/router/log +[logger] +level=DEBUG` +) diff --git a/cmd/example-gen/defaults/images.go b/cmd/example-gen/defaults/images.go new file mode 100644 index 000000000..08eb2edfb --- /dev/null +++ b/cmd/example-gen/defaults/images.go @@ -0,0 +1,12 @@ +package defaults + +const ( + ImageInitContainer = "perconalab/percona-server-mysql-operator:main" + ImageMySQL = "perconalab/percona-server-mysql-operator:main-psmysql8.4" + ImageHAProxy = "perconalab/percona-server-mysql-operator:main-haproxy" + ImageRouter = "perconalab/percona-server-mysql-operator:main-router8.4" + ImageOrchestrator = "perconalab/percona-server-mysql-operator:main-orchestrator" + ImagePMM = "perconalab/pmm-client:3-dev-latest" + ImageBackup = "perconalab/percona-server-mysql-operator:main-backup8.4" + ImageToolkit = "perconalab/percona-server-mysql-operator:main-toolkit" +) diff --git a/cmd/example-gen/defaults/manual.go b/cmd/example-gen/defaults/manual.go index 70cc9952b..d62ea7a65 100644 --- a/cmd/example-gen/defaults/manual.go +++ b/cmd/example-gen/defaults/manual.go @@ -2,7 +2,6 @@ package defaults import ( corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" @@ -13,18 +12,10 @@ import ( // ManualCluster sets default values for empty fields in example cr.yaml // that are not set by CheckNSetDefaults and cannot be set via PresetDefaults. func ManualCluster(cr *apiv1.PerconaServerMySQL) { - cr.Spec.Unsafe = apiv1.UnsafeFlags{ - MySQLSize: false, - Proxy: false, - ProxySize: false, - Orchestrator: false, - OrchestratorSize: false, - } - cr.Spec.Pause = false cr.Spec.CRVersion = version.Version() cr.Spec.VolumeExpansionEnabled = true cr.Spec.UpdateStrategy = "SmartUpdate" - cr.Spec.InitContainer.Image = "perconalab/percona-server-mysql-operator:main" + cr.Spec.InitContainer.Image = ImageInitContainer cr.Spec.IgnoreAnnotations = []string{"service.beta.kubernetes.io/aws-load-balancer-backend-protocol"} cr.Spec.IgnoreLabels = []string{"rack"} @@ -38,79 +29,17 @@ func ManualCluster(cr *apiv1.PerconaServerMySQL) { } func mysqlDefaults(spec *apiv1.MySQLSpec) { - spec.AutoRecovery = true - spec.Size = 3 - spec.Image = "perconalab/percona-server-mysql-operator:main-psmysql8.4" - spec.RuntimeClassName = ptr.To("image-rc") - spec.Env = []corev1.EnvVar{ - { - Name: "BOOTSTRAP_READ_TIMEOUT", - Value: "600", - }, - } - spec.EnvFrom = []corev1.EnvFromSource{ - { - SecretRef: &corev1.SecretEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "mysql-env-secret", - }, - }, - }, - } - spec.Labels = map[string]string{ - "rack": "rack-22", - } - spec.Annotations = map[string]string{ - "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", - "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", - } + podSpecDefaults(&spec.PodSpec, ImageMySQL, resources("2Gi", "", "4Gi", ""), ConfigurationMySQL, 600, envList("BOOTSTRAP_READ_TIMEOUT", "600"), envFromList("mysql-env-secret")) - spec.Resources = corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("2Gi"), - }, - Requests: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("4Gi"), - }, - } + spec.AutoRecovery = true spec.VolumeSpec = nil - spec.Affinity = nil spec.ExposePrimary.Enabled = true - spec.TerminationGracePeriodSeconds = ptr.To(int64(600)) - spec.Configuration = `max_connections=250 -innodb_buffer_pool_size={{containerMemoryLimit * 3/4}}` - spec.PriorityClassName = "high-priority" - spec.Sidecars = []corev1.Container{ - { - Name: "noop-memory", - Image: "busybox", - Command: []string{ - "sleep", "30d", - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: "memory-vol", - MountPath: "/var/log/app/memory", - }, - }, - LivenessProbe: &corev1.Probe{}, - ReadinessProbe: &corev1.Probe{}, - StartupProbe: &corev1.Probe{}, - Lifecycle: &corev1.Lifecycle{}, - SecurityContext: &corev1.SecurityContext{}, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("16M"), - }, - }, - }, - { - Name: "noop-pvc", - Image: "busybox", - Command: []string{ - "sleep", "30d", - }, + c := func(name string) corev1.Container { + return corev1.Container{ + Name: name, + Image: "busybox", + Command: []string{"sleep", "30d"}, VolumeMounts: []corev1.VolumeMount{ { Name: "memory-vol", @@ -122,12 +51,12 @@ innodb_buffer_pool_size={{containerMemoryLimit * 3/4}}` StartupProbe: &corev1.Probe{}, Lifecycle: &corev1.Lifecycle{}, SecurityContext: &corev1.SecurityContext{}, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("16M"), - }, - }, - }, + Resources: resources("16M", "", "", ""), + } + } + spec.Sidecars = []corev1.Container{ + c("noop-memory"), + c("noop-pvc"), } spec.SidecarVolumes = []corev1.Volume{ { @@ -139,252 +68,61 @@ innodb_buffer_pool_size={{containerMemoryLimit * 3/4}}` }, }, } - - spec.SchedulerName = "default-scheduler" - spec.PodSecurityContext = &corev1.PodSecurityContext{ - FSGroup: ptr.To(int64(1001)), - SupplementalGroups: []int64{1001, 1002, 1003}, - } - spec.ServiceAccountName = "percona-server-mysql-operator-orchestrator" - spec.NodeSelector = map[string]string{ - "topology.kubernetes.io/zone": "us-east-1a", - } } func haproxyDefaults(spec *apiv1.HAProxySpec) { - spec.Image = "perconalab/percona-server-mysql-operator:main-haproxy" - spec.Enabled = true - spec.Size = 3 - spec.RuntimeClassName = ptr.To("image-rc") - spec.Resources = corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("1Gi"), - corev1.ResourceCPU: resource.MustParse("700m"), - }, - Requests: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("1Gi"), - corev1.ResourceCPU: resource.MustParse("600m"), - }, - } - spec.PriorityClassName = "high-priority" - spec.Env = []corev1.EnvVar{ - { - Name: "HA_CONNECTION_TIMEOUT", - Value: "600", - }, - } - spec.EnvFrom = []corev1.EnvFromSource{ - { - SecretRef: &corev1.SecretEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "haproxy-env-secret", - }, - }, - }, - } - spec.Labels = map[string]string{ - "rack": "rack-22", - } - spec.Annotations = map[string]string{ - "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", - "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", - } - spec.SchedulerName = "default-scheduler" - spec.TerminationGracePeriodSeconds = ptr.To(int64(30)) - spec.ServiceAccountName = "percona-server-mysql-operator-orchestrator" - spec.Configuration = `# the actual default configuration file can be found here https://github.com/percona/percona-server-mysql-operator/blob/main/build/haproxy-global.cfg - -global - maxconn 2048 - external-check - insecure-fork-wanted - stats socket /etc/haproxy/mysql/haproxy.sock mode 600 expose-fd listeners level admin - -defaults - default-server init-addr last,libc,none - log global - mode tcp - retries 10 - timeout client 28800s - timeout connect 100500 - timeout server 28800s - -frontend mysql-primary-in - bind *:3309 accept-proxy - bind *:3306 - mode tcp - option clitcpka - default_backend mysql-primary - -frontend mysql-replicas-in - bind *:3307 - mode tcp - option clitcpka - default_backend mysql-replicas + podSpecDefaults(&spec.PodSpec, ImageHAProxy, resources("1Gi", "600m", "1Gi", "700m"), ConfigurationHAProxy, 30, envList("HA_CONNECTION_TIMEOUT", "600"), envFromList("haproxy-env-secret")) -frontend stats - bind *:8404 - mode http - http-request use-service prometheus-exporter if { path /metrics }` - spec.NodeSelector = map[string]string{ - "topology.kubernetes.io/zone": "us-east-1a", - } + spec.Enabled = true } func routerDefaults(spec *apiv1.MySQLRouterSpec) { + podSpecDefaults(&spec.PodSpec, ImageRouter, resources("256M", "", "256M", ""), ConfigurationRouter, 30, envList("ROUTER_ENV", "VALUE"), envFromList("router-env-secret")) + spec.Enabled = false - spec.Size = 3 - spec.Image = "perconalab/percona-server-mysql-operator:main-router8.4" - spec.RuntimeClassName = ptr.To("image-rc") - spec.PriorityClassName = "high-priority" - spec.Labels = map[string]string{ - "rack": "rack-22", - } - spec.Annotations = map[string]string{ - "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", - "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", - } - spec.Resources = corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("256M"), - }, - Requests: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("256M"), - }, - } - spec.Env = []corev1.EnvVar{ - { - Name: "ROUTER_ENV", - Value: "VALUE", - }, - } - spec.EnvFrom = []corev1.EnvFromSource{ - { - SecretRef: &corev1.SecretEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "router-env-secret", - }, - }, - }, + p := func(name string, port int32, targetPort int) corev1.ServicePort { + servicePort := corev1.ServicePort{Name: name, Port: port} + if targetPort != 0 { + servicePort.TargetPort = intstr.FromInt(targetPort) + } + return servicePort } - spec.TerminationGracePeriodSeconds = ptr.To(int64(30)) - spec.Configuration = `[default] -logging_folder=/tmp/router/log -[logger] -level=DEBUG` - spec.ServiceAccountName = "percona-server-mysql-operator-orchestrator" spec.Ports = []corev1.ServicePort{ - { - Name: "http", - Port: 8443, - }, - { - Name: "rw-default", - Port: 3306, - TargetPort: intstr.FromInt(6446), - }, - { - Name: "read-write", - Port: 6446, - }, - { - Name: "read-only", - Port: 6447, - }, - { - Name: "x-read-write", - Port: 6448, - }, - { - Name: "x-read-only", - Port: 6449, - }, - { - Name: "x-default", - Port: 33060, - }, - { - Name: "rw-admin", - Port: 33062, - }, - { - Name: "custom-port", - Port: 1111, - TargetPort: intstr.FromInt(6446), - }, - } - spec.SchedulerName = "default-scheduler" - spec.NodeSelector = map[string]string{ - "topology.kubernetes.io/zone": "us-east-1a", + p("http", 8443, 0), + p("rw-default", 3306, 6446), + p("read-write", 6446, 0), + p("read-only", 6447, 0), + p("x-read-write", 6448, 0), + p("x-read-only", 6449, 0), + p("x-default", 33060, 0), + p("rw-admin", 33062, 0), + p("custom-port", 1111, 6446), } } func orchestratorDefaults(spec *apiv1.OrchestratorSpec) { + podSpecDefaults(&spec.PodSpec, ImageOrchestrator, resources("128M", "", "256M", ""), "", 30, envList("ORC_ENV", "VALUE"), envFromList("orc-env-secret")) + spec.Enabled = false - spec.Image = "perconalab/percona-server-mysql-operator:main-orchestrator" - spec.Size = 3 - spec.TerminationGracePeriodSeconds = ptr.To(int64(30)) - spec.Affinity = nil spec.ServiceAccountName = "percona-server-mysql-operator-orchestrator" - spec.PriorityClassName = "high-priority" - spec.RuntimeClassName = ptr.To("image-rc") - spec.Env = []corev1.EnvVar{ - { - Name: "ORC_ENV", - Value: "VALUE", - }, - } - spec.EnvFrom = []corev1.EnvFromSource{ - { - SecretRef: &corev1.SecretEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "orc-env-secret", - }, - }, - }, - } - spec.Resources = corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("256M"), - }, - Requests: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("128M"), - }, - } - spec.Labels = map[string]string{ - "rack": "rack-22", - } - spec.Annotations = map[string]string{ - "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", - "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", - } - spec.SchedulerName = "default-scheduler" - spec.NodeSelector = map[string]string{ - "topology.kubernetes.io/zone": "us-east-1a", + spec.PodSecurityContext = &corev1.PodSecurityContext{ + SupplementalGroups: []int64{1001}, } } func pmmDefaults(spec *apiv1.PMMSpec) { - spec.Resources = corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("256M"), - corev1.ResourceCPU: resource.MustParse("400m"), - }, - Requests: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("150M"), - corev1.ResourceCPU: resource.MustParse("300m"), - }, - } - - spec.Image = "perconalab/pmm-client:3-dev-latest" + spec.Image = ImagePMM + spec.Resources = resources("150M", "300m", "256M", "400m") spec.ServerHost = "monitoring-service" spec.MySQLParams = "PMM_ADMIN_CUSTOM_PARAMS" } func backupDefaults(spec *apiv1.BackupSpec) { + spec.Image = ImageBackup spec.Enabled = true spec.SourcePod = "ps-cluster1-mysql-1" - spec.Image = "perconalab/percona-server-mysql-operator:main-backup8.4" + spec.ServiceAccountName = "some-service-account" + spec.BackoffLimit = ptr.To(int32(6)) spec.Schedule = []apiv1.BackupSchedule{ { Name: "sat-night-backup", @@ -399,7 +137,6 @@ func backupDefaults(spec *apiv1.BackupSpec) { StorageName: "s3", }, } - spec.BackoffLimit = ptr.To(int32(6)) spec.Storages = map[string]*apiv1.BackupStorageSpec{ "azure-blob": { Type: apiv1.BackupStorageAzure, @@ -435,34 +172,40 @@ func backupDefaults(spec *apiv1.BackupSpec) { }, }, } - spec.ServiceAccountName = "percona-server-mysql-operator-orchestrator" } func toolkitDefaults(spec *apiv1.ToolkitSpec) { - spec.Image = "perconalab/percona-server-mysql-operator:main-toolkit" - spec.Resources = corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("256M"), - corev1.ResourceCPU: resource.MustParse("400m"), - }, - Requests: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("150M"), - corev1.ResourceCPU: resource.MustParse("100m"), - }, + spec.Image = ImageToolkit + spec.Resources = resources("150M", "100m", "256M", "400m") + spec.Env = envList("TOOLKIT_ENV", "VALUE") + spec.EnvFrom = envFromList("toolkit-env-secret") +} + +func podSpecDefaults(spec *apiv1.PodSpec, image string, resources corev1.ResourceRequirements, configuration string, gracePeriod int64, env []corev1.EnvVar, envFrom []corev1.EnvFromSource) { + spec.Image = image + spec.Resources = resources + spec.Configuration = configuration + spec.TerminationGracePeriodSeconds = ptr.To(gracePeriod) + spec.Env = env + spec.EnvFrom = envFrom + + spec.Size = 3 + spec.RuntimeClassName = ptr.To("image-rc") + spec.Labels = map[string]string{ + "rack": "rack-22", } - spec.Env = []corev1.EnvVar{ - { - Name: "TOOLKIT_ENV", - Value: "VALUE", - }, + spec.Annotations = map[string]string{ + "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", + "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", } - spec.EnvFrom = []corev1.EnvFromSource{ - { - SecretRef: &corev1.SecretEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "toolkit-env-secret", - }, - }, - }, + + spec.PriorityClassName = "high-priority" + + spec.SchedulerName = "default-scheduler" + spec.ServiceAccountName = "some-service-account" + spec.NodeSelector = map[string]string{ + "topology.kubernetes.io/zone": "us-east-1a", } + + spec.PodSecurityContext = nil } diff --git a/cmd/example-gen/defaults/util.go b/cmd/example-gen/defaults/util.go new file mode 100644 index 000000000..8fe6e073b --- /dev/null +++ b/cmd/example-gen/defaults/util.go @@ -0,0 +1,58 @@ +package defaults + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" +) + +func resources(requestsMemory, requestsCPU, limitsMemory, limitsCPU string) corev1.ResourceRequirements { + r := corev1.ResourceRequirements{} + + if requestsMemory != "" { + if r.Requests == nil { + r.Requests = make(corev1.ResourceList) + } + r.Requests[corev1.ResourceMemory] = resource.MustParse(requestsMemory) + } + if requestsCPU != "" { + if r.Requests == nil { + r.Requests = make(corev1.ResourceList) + } + r.Requests[corev1.ResourceCPU] = resource.MustParse(requestsCPU) + } + if limitsMemory != "" { + if r.Limits == nil { + r.Limits = make(corev1.ResourceList) + } + r.Limits[corev1.ResourceMemory] = resource.MustParse(limitsMemory) + } + if limitsCPU != "" { + if r.Limits == nil { + r.Limits = make(corev1.ResourceList) + } + r.Limits[corev1.ResourceCPU] = resource.MustParse(limitsCPU) + } + + return r +} + +func envList(name, value string) []corev1.EnvVar { + return []corev1.EnvVar{ + { + Name: name, + Value: value, + }, + } +} + +func envFromList(name string) []corev1.EnvFromSource { + return []corev1.EnvFromSource{ + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: name, + }, + }, + }, + } +} diff --git a/cmd/example-gen/generate.sh b/cmd/example-gen/generate.sh index a5566bd3d..157e857aa 100755 --- a/cmd/example-gen/generate.sh +++ b/cmd/example-gen/generate.sh @@ -179,6 +179,7 @@ del_fields_to_comment() { | yq "del(.spec.backup.initContainer)" \ | yq "del(.spec.backup.containerSecurityContext)" \ | yq "del(.spec.backup.resources)" \ + | yq "del(.spec.backup.serviceAccountName)" \ | yq "del(.spec.backup.storages.azure-blob)" \ | yq "del(.spec.backup.storages.s3-us-west.resources)" \ | yq "del(.spec.backup.storages.s3-us-west.topologySpreadConstraints)" \ diff --git a/deploy/cr.yaml b/deploy/cr.yaml index a85983e58..bb877f222 100644 --- a/deploy/cr.yaml +++ b/deploy/cr.yaml @@ -79,7 +79,7 @@ spec: # topology.kubernetes.io/zone: us-east-1a # priorityClassName: high-priority # schedulerName: default-scheduler -# serviceAccountName: percona-server-mysql-operator-orchestrator +# serviceAccountName: some-service-account gracePeriod: 600 # initContainer: # containerSecurityContext: @@ -105,9 +105,9 @@ spec: # minAvailable: 0 resources: limits: - memory: 2Gi - requests: memory: 4Gi + requests: + memory: 2Gi # startupProbe: # failureThreshold: 1 # initialDelaySeconds: 15 @@ -274,7 +274,7 @@ spec: # topology.kubernetes.io/zone: us-east-1a # priorityClassName: high-priority # schedulerName: default-scheduler -# serviceAccountName: percona-server-mysql-operator-orchestrator +# serviceAccountName: some-service-account gracePeriod: 30 # initContainer: # containerSecurityContext: @@ -420,7 +420,7 @@ spec: # topology.kubernetes.io/zone: us-east-1a # priorityClassName: high-priority # schedulerName: default-scheduler -# serviceAccountName: percona-server-mysql-operator-orchestrator +# serviceAccountName: some-service-account gracePeriod: 30 # initContainer: # containerSecurityContext: @@ -679,7 +679,7 @@ spec: # schedule: 0 0 * * * # storageName: s3 # backoffLimit: 6 - serviceAccountName: percona-server-mysql-operator-orchestrator +# serviceAccountName: some-service-account # initContainer: # containerSecurityContext: # privileged: false From 75a86cb29e27900012633fc1f9f737c308a999d0 Mon Sep 17 00:00:00 2001 From: Andrii Dema Date: Tue, 28 Oct 2025 11:02:20 +0200 Subject: [PATCH 06/13] generation of backups and restores --- Makefile | 12 +- cmd/example-gen/defaults/images.go | 12 -- cmd/example-gen/main.go | 136 +++++++++++++++++- cmd/example-gen/{ => pkg}/defaults/configs.go | 6 +- cmd/example-gen/{ => pkg}/defaults/manual.go | 44 +++--- cmd/example-gen/{ => pkg}/defaults/preset.go | 39 ++--- cmd/example-gen/{ => pkg}/defaults/util.go | 0 cmd/example-gen/pkg/defaults/values.go | 44 ++++++ cmd/example-gen/scripts/generate.sh | 22 +++ cmd/example-gen/scripts/lib/ps-backup.sh | 25 ++++ cmd/example-gen/scripts/lib/ps-restore.sh | 32 +++++ .../{generate.sh => scripts/lib/ps.sh} | 83 +---------- cmd/example-gen/scripts/lib/util.sh | 69 +++++++++ deploy/backup/backup.yaml | 14 +- deploy/backup/restore.yaml | 89 +++++++++++- 15 files changed, 455 insertions(+), 172 deletions(-) delete mode 100644 cmd/example-gen/defaults/images.go rename cmd/example-gen/{ => pkg}/defaults/configs.go (86%) rename cmd/example-gen/{ => pkg}/defaults/manual.go (84%) rename cmd/example-gen/{ => pkg}/defaults/preset.go (83%) rename cmd/example-gen/{ => pkg}/defaults/util.go (100%) create mode 100644 cmd/example-gen/pkg/defaults/values.go create mode 100755 cmd/example-gen/scripts/generate.sh create mode 100644 cmd/example-gen/scripts/lib/ps-backup.sh create mode 100644 cmd/example-gen/scripts/lib/ps-restore.sh rename cmd/example-gen/{generate.sh => scripts/lib/ps.sh} (84%) mode change 100755 => 100644 create mode 100644 cmd/example-gen/scripts/lib/util.sh diff --git a/Makefile b/Makefile index 34fe4a831..c5853646a 100644 --- a/Makefile +++ b/Makefile @@ -105,9 +105,17 @@ e2e-test: kuttl-shfmt .PHONY: generate-cr-yaml generate-cr-yaml: - ./cmd/example-gen/generate.sh + ./cmd/example-gen/scripts/generate.sh ps -manifests: kustomize generate generate-cr-yaml ## Generate Kubernetes manifests (CRDs, RBAC, operator deployment) +.PHONY: generate-restore-yaml +generate-restore-yaml: + ./cmd/example-gen/scripts/generate.sh ps-restore + +.PHONY: generate-backup-yaml +generate-backup-yaml: + ./cmd/example-gen/scripts/generate.sh ps-backup + +manifests: kustomize generate generate-cr-yaml generate-restore-yaml generate-backup-yaml ## Generate Kubernetes manifests (CRDs, RBAC, operator deployment) $(KUSTOMIZE) build config/crd/ > $(DEPLOYDIR)/crd.yaml echo "---" >> $(DEPLOYDIR)/crd.yaml diff --git a/cmd/example-gen/defaults/images.go b/cmd/example-gen/defaults/images.go deleted file mode 100644 index 08eb2edfb..000000000 --- a/cmd/example-gen/defaults/images.go +++ /dev/null @@ -1,12 +0,0 @@ -package defaults - -const ( - ImageInitContainer = "perconalab/percona-server-mysql-operator:main" - ImageMySQL = "perconalab/percona-server-mysql-operator:main-psmysql8.4" - ImageHAProxy = "perconalab/percona-server-mysql-operator:main-haproxy" - ImageRouter = "perconalab/percona-server-mysql-operator:main-router8.4" - ImageOrchestrator = "perconalab/percona-server-mysql-operator:main-orchestrator" - ImagePMM = "perconalab/pmm-client:3-dev-latest" - ImageBackup = "perconalab/percona-server-mysql-operator:main-backup8.4" - ImageToolkit = "perconalab/percona-server-mysql-operator:main-toolkit" -) diff --git a/cmd/example-gen/main.go b/cmd/example-gen/main.go index e23583e0f..70dba8dd7 100644 --- a/cmd/example-gen/main.go +++ b/cmd/example-gen/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "fmt" "os" "os/signal" "syscall" @@ -13,27 +14,45 @@ import ( "sigs.k8s.io/yaml" apiv1 "github.com/percona/percona-server-mysql-operator/api/v1" - "github.com/percona/percona-server-mysql-operator/cmd/example-gen/defaults" "github.com/percona/percona-server-mysql-operator/cmd/example-gen/internal/marshal" + "github.com/percona/percona-server-mysql-operator/cmd/example-gen/pkg/defaults" ) func main() { ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer cancel() - if err := printPerconaServerMySQL(ctx); err != nil { - panic(err) + if len(os.Args) != 2 { + fmt.Println("Expected arguments are: `ps`, `ps-backup` or `ps-restore`") + os.Exit(1) + } + switch os.Args[1] { + case "ps": + if err := printCluster(ctx); err != nil { + panic(err) + } + case "ps-backup": + if err := printBackup(); err != nil { + panic(err) + } + case "ps-restore": + if err := printRestore(); err != nil { + panic(err) + } + default: + fmt.Println("Unexpected argument. Expected arguments are: `ps`, `ps-backup` or `ps-restore`") + os.Exit(1) } } -func printPerconaServerMySQL(ctx context.Context) error { +func printCluster(ctx context.Context) error { cr := &apiv1.PerconaServerMySQL{ TypeMeta: metav1.TypeMeta{ Kind: "PerconaServerMySQL", APIVersion: "ps.percona.com/v1", }, ObjectMeta: metav1.ObjectMeta{ - Name: "ps-cluster1", + Name: defaults.NameCluster, Finalizers: []string{ "percona.com/delete-mysql-pods-in-order", "percona.com/delete-ssl", @@ -42,7 +61,7 @@ func printPerconaServerMySQL(ctx context.Context) error { }, Spec: apiv1.PerconaServerMySQLSpec{ Backup: &apiv1.BackupSpec{ - Image: "some-backup", + Image: defaults.ImageBackup, }, MySQL: apiv1.MySQLSpec{ VolumeSpec: &apiv1.VolumeSpec{ @@ -57,7 +76,7 @@ func printPerconaServerMySQL(ctx context.Context) error { return errors.Wrap(err, "check and set defaults") } defaults.ManualCluster(cr) - if err := defaults.PresetCluster(cr); err != nil { + if err := defaults.FromPresets(cr); err != nil { return errors.Wrap(err, "preset defaults") } @@ -91,3 +110,106 @@ func printPerconaServerMySQL(ctx context.Context) error { _, err = os.Stdout.Write(b) return errors.Wrap(err, "write") } + +func printBackup() error { + cr := &apiv1.PerconaServerMySQLBackup{ + TypeMeta: metav1.TypeMeta{ + Kind: "PerconaServerMySQLBackup", + APIVersion: "ps.percona.com/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: defaults.NameBackup, + Finalizers: []string{ + "percona.com/delete-backup", + }, + }, + Spec: apiv1.PerconaServerMySQLBackupSpec{ + ClusterName: defaults.NameCluster, + StorageName: "minio", + SourcePod: defaults.SourcePod, + }, + } + + if err := defaults.FromPresets(cr); err != nil { + return errors.Wrap(err, "preset defaults") + } + jsonBytes, err := marshal.MarshalIgnoreOmitEmpty(cr, + corev1.EnvVar{}, + corev1.EnvFromSource{}, + metav1.ObjectMeta{}) + if err != nil { + return errors.Wrap(err, "marshal") + } + b, err := yaml.JSONToYAML(jsonBytes) + if err != nil { + return errors.Wrap(err, "json to yaml") + } + + _, err = os.Stdout.Write(b) + return errors.Wrap(err, "write") +} + +func printRestore() error { + cr := &apiv1.PerconaServerMySQLRestore{ + TypeMeta: metav1.TypeMeta{ + Kind: "PerconaServerMySQLRestore", + APIVersion: "ps.percona.com/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: defaults.NameRestore, + }, + Spec: apiv1.PerconaServerMySQLRestoreSpec{ + ClusterName: defaults.NameCluster, + BackupName: defaults.NameBackup, + BackupSource: &apiv1.PerconaServerMySQLBackupStatus{ + Destination: "s3://S3-BACKUP-BUCKET-NAME-HERE/backup-path", + Storage: &apiv1.BackupStorageSpec{ + Type: apiv1.BackupStorageS3, + S3: &apiv1.BackupStorageS3Spec{ + Bucket: "S3-BACKUP-BUCKET-NAME-HERE", + Prefix: "PREFIX_NAME", + CredentialsSecret: fmt.Sprintf("%s-s3-credentials", defaults.NameCluster), + Region: "us-west-2", + EndpointURL: "https://s3.amazonaws.com", + }, + Labels: defaults.Labels, + Annotations: defaults.Annotations, + SchedulerName: defaults.SchedulerName, + PriorityClassName: defaults.PriorityClassName, + NodeSelector: defaults.NodeSelector, + VerifyTLS: defaults.VerifyTLS, + RuntimeClassName: defaults.RuntimeClassName, + }, + }, + }, + } + + if err := defaults.FromPresets(cr); err != nil { + return errors.Wrap(err, "preset defaults") + } + jsonBytes, err := marshal.MarshalIgnoreOmitEmpty(cr, + corev1.EnvVar{}, + corev1.EnvFromSource{}, + metav1.ObjectMeta{}, + corev1.Affinity{}, + corev1.PodSecurityContext{}, + corev1.SecurityContext{}, + corev1.TopologySpreadConstraint{}, + corev1.Volume{}, + corev1.PersistentVolumeClaimSpec{}, + corev1.EmptyDirVolumeSource{}, + corev1.HostPathVolumeSource{}, + corev1.ResourceRequirements{}, + corev1.Toleration{}, + ) + if err != nil { + return errors.Wrap(err, "marshal") + } + b, err := yaml.JSONToYAML(jsonBytes) + if err != nil { + return errors.Wrap(err, "json to yaml") + } + + _, err = os.Stdout.Write(b) + return errors.Wrap(err, "write") +} diff --git a/cmd/example-gen/defaults/configs.go b/cmd/example-gen/pkg/defaults/configs.go similarity index 86% rename from cmd/example-gen/defaults/configs.go rename to cmd/example-gen/pkg/defaults/configs.go index 5629a96df..04d14cd90 100644 --- a/cmd/example-gen/defaults/configs.go +++ b/cmd/example-gen/pkg/defaults/configs.go @@ -1,7 +1,7 @@ package defaults const ( - ConfigurationHAProxy = `# the actual default configuration file can be found here https://github.com/percona/percona-server-mysql-operator/blob/main/build/haproxy-global.cfg + configurationHAProxy = `# the actual default configuration file can be found here https://github.com/percona/percona-server-mysql-operator/blob/main/build/haproxy-global.cfg global maxconn 2048 @@ -36,10 +36,10 @@ frontend stats mode http http-request use-service prometheus-exporter if { path /metrics }` - ConfigurationMySQL = `max_connections=250 + configurationMySQL = `max_connections=250 innodb_buffer_pool_size={{containerMemoryLimit * 3/4}}` - ConfigurationRouter = `[default] + configurationRouter = `[default] logging_folder=/tmp/router/log [logger] level=DEBUG` diff --git a/cmd/example-gen/defaults/manual.go b/cmd/example-gen/pkg/defaults/manual.go similarity index 84% rename from cmd/example-gen/defaults/manual.go rename to cmd/example-gen/pkg/defaults/manual.go index d62ea7a65..6bde25532 100644 --- a/cmd/example-gen/defaults/manual.go +++ b/cmd/example-gen/pkg/defaults/manual.go @@ -1,6 +1,8 @@ package defaults import ( + "fmt" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" @@ -29,7 +31,7 @@ func ManualCluster(cr *apiv1.PerconaServerMySQL) { } func mysqlDefaults(spec *apiv1.MySQLSpec) { - podSpecDefaults(&spec.PodSpec, ImageMySQL, resources("2Gi", "", "4Gi", ""), ConfigurationMySQL, 600, envList("BOOTSTRAP_READ_TIMEOUT", "600"), envFromList("mysql-env-secret")) + podSpecDefaults(&spec.PodSpec, ImageMySQL, resources("2Gi", "", "4Gi", ""), configurationMySQL, 600, envList("BOOTSTRAP_READ_TIMEOUT", "600"), envFromList("mysql-env-secret")) spec.AutoRecovery = true spec.VolumeSpec = nil @@ -71,13 +73,13 @@ func mysqlDefaults(spec *apiv1.MySQLSpec) { } func haproxyDefaults(spec *apiv1.HAProxySpec) { - podSpecDefaults(&spec.PodSpec, ImageHAProxy, resources("1Gi", "600m", "1Gi", "700m"), ConfigurationHAProxy, 30, envList("HA_CONNECTION_TIMEOUT", "600"), envFromList("haproxy-env-secret")) + podSpecDefaults(&spec.PodSpec, ImageHAProxy, resources("1Gi", "600m", "1Gi", "700m"), configurationHAProxy, 30, envList("HA_CONNECTION_TIMEOUT", "600"), envFromList("haproxy-env-secret")) spec.Enabled = true } func routerDefaults(spec *apiv1.MySQLRouterSpec) { - podSpecDefaults(&spec.PodSpec, ImageRouter, resources("256M", "", "256M", ""), ConfigurationRouter, 30, envList("ROUTER_ENV", "VALUE"), envFromList("router-env-secret")) + podSpecDefaults(&spec.PodSpec, ImageRouter, resources("256M", "", "256M", ""), configurationRouter, 30, envList("ROUTER_ENV", "VALUE"), envFromList("router-env-secret")) spec.Enabled = false p := func(name string, port int32, targetPort int) corev1.ServicePort { @@ -120,7 +122,7 @@ func pmmDefaults(spec *apiv1.PMMSpec) { func backupDefaults(spec *apiv1.BackupSpec) { spec.Image = ImageBackup spec.Enabled = true - spec.SourcePod = "ps-cluster1-mysql-1" + spec.SourcePod = SourcePod spec.ServiceAccountName = "some-service-account" spec.BackoffLimit = ptr.To(int32(6)) spec.Schedule = []apiv1.BackupSchedule{ @@ -153,7 +155,7 @@ func backupDefaults(spec *apiv1.BackupSpec) { S3: &apiv1.BackupStorageS3Spec{ Bucket: "S3-BACKUP-BUCKET-NAME-HERE", Prefix: "PREFIX_NAME", - CredentialsSecret: "ps-cluster1-s3-credentials", + CredentialsSecret: fmt.Sprintf("%s-s3-credentials", NameCluster), Region: "us-west-2", EndpointURL: "https://s3.amazonaws.com", }, @@ -163,13 +165,11 @@ func backupDefaults(spec *apiv1.BackupSpec) { Labels: map[string]string{ "backupWorker": "True", }, - PriorityClassName: "high-priority", - RuntimeClassName: ptr.To("image-rc"), - SchedulerName: "default-scheduler", - VerifyTLS: ptr.To(true), - NodeSelector: map[string]string{ - "topology.kubernetes.io/zone": "us-east-1a", - }, + PriorityClassName: PriorityClassName, + RuntimeClassName: RuntimeClassName, + SchedulerName: SchedulerName, + VerifyTLS: VerifyTLS, + NodeSelector: NodeSelector, }, } } @@ -190,22 +190,14 @@ func podSpecDefaults(spec *apiv1.PodSpec, image string, resources corev1.Resourc spec.EnvFrom = envFrom spec.Size = 3 - spec.RuntimeClassName = ptr.To("image-rc") - spec.Labels = map[string]string{ - "rack": "rack-22", - } - spec.Annotations = map[string]string{ - "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", - "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", - } - spec.PriorityClassName = "high-priority" + spec.RuntimeClassName = RuntimeClassName + spec.Labels = Labels + spec.Annotations = Annotations + spec.PriorityClassName = PriorityClassName + spec.SchedulerName = SchedulerName + spec.NodeSelector = NodeSelector - spec.SchedulerName = "default-scheduler" spec.ServiceAccountName = "some-service-account" - spec.NodeSelector = map[string]string{ - "topology.kubernetes.io/zone": "us-east-1a", - } - spec.PodSecurityContext = nil } diff --git a/cmd/example-gen/defaults/preset.go b/cmd/example-gen/pkg/defaults/preset.go similarity index 83% rename from cmd/example-gen/defaults/preset.go rename to cmd/example-gen/pkg/defaults/preset.go index c59cd71b5..5c832e7b7 100644 --- a/cmd/example-gen/defaults/preset.go +++ b/cmd/example-gen/pkg/defaults/preset.go @@ -12,18 +12,13 @@ import ( "github.com/percona/percona-server-mysql-operator/cmd/example-gen/internal/fill" ) -// PresetCluster assigns default values to fields in example cr.yaml based on their types. +// FromPresets assigns default values to fields in provided custom resource based on their types. // The internal presets slice defines the default value associated with each type. -func PresetCluster(cr *apiv1.PerconaServerMySQL) error { +func FromPresets(cr any) error { presets := []any{ apiv1.Metadata{ - Labels: map[string]string{ - "rack": "rack-22", - }, - Annotations: map[string]string{ - "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", - "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", - }, + Labels: Labels, + Annotations: Annotations, }, corev1.SecurityContext{ Privileged: ptr.To(false), @@ -92,13 +87,8 @@ func PresetCluster(cr *apiv1.PerconaServerMySQL) error { LoadBalancerSourceRanges: []string{ "10.0.0.0/8", }, - Annotations: map[string]string{ - "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", - "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", - }, - Labels: map[string]string{ - "rack": "rack-22", - }, + Annotations: Annotations, + Labels: Labels, InternalTrafficPolicy: ptr.To(corev1.ServiceInternalTrafficPolicyCluster), ExternalTrafficPolicy: corev1.ServiceExternalTrafficPolicyCluster, }, @@ -134,13 +124,6 @@ func PresetCluster(cr *apiv1.PerconaServerMySQL) error { FSGroup: ptr.To(int64(1001)), SupplementalGroups: []int64{1001, 1002, 1003}, }, - apiv1.UnsafeFlags{ - MySQLSize: false, - Proxy: false, - ProxySize: false, - Orchestrator: false, - OrchestratorSize: false, - }, apiv1.UpgradeOptions{ VersionServiceEndpoint: "https://check.percona.com", Apply: "disabled", @@ -154,16 +137,12 @@ func PresetCluster(cr *apiv1.PerconaServerMySQL) error { }, }, []corev1.LocalObjectReference{ - { - Name: "my-secret-1", - }, - { - Name: "my-secret-2", - }, + {Name: "my-secret-1"}, + {Name: "my-secret-2"}, }, corev1.PullAlways, apiv1.InitContainerSpec{ - Image: "perconalab/percona-server-mysql-operator:main", + Image: ImageInitContainer, }, []apiv1.SidecarPVC{ { diff --git a/cmd/example-gen/defaults/util.go b/cmd/example-gen/pkg/defaults/util.go similarity index 100% rename from cmd/example-gen/defaults/util.go rename to cmd/example-gen/pkg/defaults/util.go diff --git a/cmd/example-gen/pkg/defaults/values.go b/cmd/example-gen/pkg/defaults/values.go new file mode 100644 index 000000000..1160c836e --- /dev/null +++ b/cmd/example-gen/pkg/defaults/values.go @@ -0,0 +1,44 @@ +package defaults + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + + apiv1 "github.com/percona/percona-server-mysql-operator/api/v1" + "github.com/percona/percona-server-mysql-operator/pkg/mysql" +) + +const ( + ImageInitContainer = "perconalab/percona-server-mysql-operator:main" + ImageMySQL = "perconalab/percona-server-mysql-operator:main-psmysql8.4" + ImageHAProxy = "perconalab/percona-server-mysql-operator:main-haproxy" + ImageRouter = "perconalab/percona-server-mysql-operator:main-router8.4" + ImageOrchestrator = "perconalab/percona-server-mysql-operator:main-orchestrator" + ImagePMM = "perconalab/pmm-client:3-dev-latest" + ImageBackup = "perconalab/percona-server-mysql-operator:main-backup8.4" + ImageToolkit = "perconalab/percona-server-mysql-operator:main-toolkit" +) + +const ( + NameCluster = "ps-cluster1" + NameBackup = "backup1" + NameRestore = "restore1" +) + +var ( + Labels = map[string]string{ + "rack": "rack-22", + } + Annotations = map[string]string{ + "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", + "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", + } + NodeSelector = map[string]string{ + "topology.kubernetes.io/zone": "us-east-1a", + } + VerifyTLS = ptr.To(true) + RuntimeClassName = ptr.To("image-rc") + SchedulerName = "default-scheduler" + PriorityClassName = "high-priority" + SourcePod = mysql.PodName(&apiv1.PerconaServerMySQL{ObjectMeta: metav1.ObjectMeta{Name: NameCluster}}, 1) +) diff --git a/cmd/example-gen/scripts/generate.sh b/cmd/example-gen/scripts/generate.sh new file mode 100755 index 000000000..10e4b7712 --- /dev/null +++ b/cmd/example-gen/scripts/generate.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +resource=${1:-} + +SCRIPT_DIR="$(dirname ${BASH_SOURCE[0]})" + +case "$resource" in + ps | ps-backup | ps-restore) + . "$SCRIPT_DIR/lib/$resource.sh" + ;; + + *) + echo "Usage: $0 {ps|ps-backup|ps-restore}" >&2 + exit 2 + ;; +esac + +go run cmd/example-gen/main.go "$resource" \ + | sort_yaml \ + | remove_fields \ + | comment_fields \ + >$RESOURCE_PATH diff --git a/cmd/example-gen/scripts/lib/ps-backup.sh b/cmd/example-gen/scripts/lib/ps-backup.sh new file mode 100644 index 000000000..b580d6ddc --- /dev/null +++ b/cmd/example-gen/scripts/lib/ps-backup.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +. "$SCRIPT_DIR/lib/util.sh" + +RESOURCE_PATH="deploy/backup/backup.yaml" + +sort_yaml() { + SPEC_ORDER='"clusterName", "storageName", "sourcePod", "containerOptions"' + CONTAINER_OPTS_ORDER='"env", "args"' + + yq - "$@" \ + | yq '.spec |= pick((['"$SPEC_ORDER"'] + keys) | unique)' \ + | yq '.spec.containerOptions |= pick((['"$CONTAINER_OPTS_ORDER"'] + keys) | unique)' +} + +remove_fields() { + yq - "$@" \ + | yq "del(.status)" +} + +del_fields_to_comment() { + yq - "$@" \ + | yq "del(.spec.containerOptions)" \ + | yq "del(.spec.sourcePod)" +} diff --git a/cmd/example-gen/scripts/lib/ps-restore.sh b/cmd/example-gen/scripts/lib/ps-restore.sh new file mode 100644 index 000000000..1a1f5f44d --- /dev/null +++ b/cmd/example-gen/scripts/lib/ps-restore.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +. "$SCRIPT_DIR/lib/util.sh" + +RESOURCE_PATH="deploy/backup/restore.yaml" + +sort_yaml() { + SPEC_ORDER='"clusterName", "backupName", "containerOptions", "backupSource"' + CONTAINER_OPTS_ORDER='"env", "args"' + + yq - "$@" \ + | yq '.spec |= pick((['"$SPEC_ORDER"'] + keys) | unique)' \ + | yq '.spec.containerOptions |= pick((['"$CONTAINER_OPTS_ORDER"'] + keys) | unique)' +} + +remove_fields() { + yq - "$@" \ + | yq "del(.status)" \ + | yq "del(.spec.backupSource.backupSource)" \ + | yq "del(.spec.backupSource.completed)" \ + | yq "del(.spec.backupSource.image)" \ + | yq "del(.spec.backupSource.state)" \ + | yq "del(.spec.backupSource.stateDescription)" \ + | yq "del(.spec.backupSource.storage.azure)" \ + | yq "del(.spec.backupSource.storage.gcs)" +} + +del_fields_to_comment() { + yq - "$@" \ + | yq "del(.spec.containerOptions)" \ + | yq "del(.spec.backupSource)" +} diff --git a/cmd/example-gen/generate.sh b/cmd/example-gen/scripts/lib/ps.sh old mode 100755 new mode 100644 similarity index 84% rename from cmd/example-gen/generate.sh rename to cmd/example-gen/scripts/lib/ps.sh index 157e857aa..177f8a74f --- a/cmd/example-gen/generate.sh +++ b/cmd/example-gen/scripts/lib/ps.sh @@ -1,6 +1,10 @@ #!/usr/bin/env bash -sort_cr_yaml() { +. "$SCRIPT_DIR/lib/util.sh" + +RESOURCE_PATH="deploy/cr.yaml" + +sort_yaml() { GENERAL_ORDER='"metadata", "unsafeFlags", "pause", "crVersion", "enableVolumeExpansion", "secretsName", "sslSecretName", "updateStrategy", "upgradeOptions", "initContainer", "ignoreAnnotations", "ignoreLabels", "tls", "mysql", "proxy", "orchestrator", "pmm", "backup", "toolkit"' POD_SPEC_ORDER='"size", "image", "imagePullPolicy","imagePullSecrets", "runtimeClassName", "tolerations", "annotations", "labels", "nodeSelector", "priorityClassName", "schedulerName", "serviceAccountName","gracePeriod", "initContainer", "env", "envFrom", "podDisruptionBudget", "resources","startupProbe", "readinessProbe", "livenessProbe", "affinity", "topologySpreadConstraints", "containerSecurityContext", "podSecurityContext"' @@ -206,80 +210,3 @@ del_fields_to_comment() { | yq "del(.spec.toolkit.readinessProbe)" \ | yq "del(.spec.toolkit.livenessProbe)" } - -# TODO: Refactor this function. We should introduce an allowlist of fields to keep uncommented and comment everything else. -comment_cr_yaml() { - local tmp_old tmp_new tmp_diff tmp_out - - tmp_old=$(mktemp) - tmp_new=$(mktemp) - tmp_diff=$(mktemp) - tmp_out=$(mktemp) - - yq - "$@" >"$tmp_old" - del_fields_to_comment <"$tmp_old" >"$tmp_new" - - diff "$tmp_old" "$tmp_new" >"$tmp_diff" - - # convert delete-only hunks into change hunks with identical new-range, - # and duplicate "< ..." lines as "> # ..." - awk ' -/^[0-9]+(,[0-9]+)?[acd][0-9]+(,[0-9]+)?$/ { - if (inblock) process() - hdr = $0; inblock = 1; n = 0 - next -} -inblock { block[++n] = $0; next } -{ print } - -END { if (inblock) process() } - -function process(i, ln, has_lt, has_gt, has_sep, pos, ch, left) { - has_lt = has_gt = has_sep = 0 - for (i = 1; i <= n; i++) { - ln = block[i] - if (ln ~ /^/) has_gt = 1 - else if (ln ~ /^---$/) has_sep = 1 - } - - if (has_lt && !has_gt) { - # if header was a delete, make it a change with identical new-range - pos = 0 - for (i = 1; i <= length(hdr); i++) { ch = substr(hdr, i, 1); if (ch=="a"||ch=="c"||ch=="d"){ pos = i; break } } - if (pos && substr(hdr, pos, 1) == "d") { - left = substr(hdr, 1, pos-1) - hdr = left "c" left - } else if (match(hdr, /[acd]/)) { - pos = RSTART - if (substr(hdr, pos, 1) != "c") hdr = substr(hdr,1,pos-1) "c" substr(hdr,pos+1) - } - - print hdr - for (i = 1; i <= n; i++) print block[i] - if (!has_sep) print "---" - for (i = 1; i <= n; i++) { - ln = block[i] - if (ln ~ /^ #" ln } - } - } else { - print hdr - for (i = 1; i <= n; i++) print block[i] - } - inblock = 0; n = 0 -}' "$tmp_diff" >"$tmp_out" - - mv "$tmp_out" "$tmp_diff" - - patch -s -i "$tmp_diff" "$tmp_old" - - cat "$tmp_old" -} - -yq --version - -go run cmd/example-gen/main.go \ - | sort_cr_yaml \ - | remove_fields \ - | comment_cr_yaml \ - | cat >deploy/cr.yaml diff --git a/cmd/example-gen/scripts/lib/util.sh b/cmd/example-gen/scripts/lib/util.sh new file mode 100644 index 000000000..2da697d3c --- /dev/null +++ b/cmd/example-gen/scripts/lib/util.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash + +comment_fields() { + local tmp_old tmp_new tmp_diff tmp_out + + tmp_old=$(mktemp) + tmp_new=$(mktemp) + tmp_diff=$(mktemp) + tmp_out=$(mktemp) + + yq - "$@" >"$tmp_old" + del_fields_to_comment <"$tmp_old" >"$tmp_new" + + diff "$tmp_old" "$tmp_new" >"$tmp_diff" + + # convert delete-only hunks into change hunks with identical new-range, + # and duplicate "< ..." lines as "> # ..." + awk ' +/^[0-9]+(,[0-9]+)?[acd][0-9]+(,[0-9]+)?$/ { + if (inblock) process() + hdr = $0; inblock = 1; n = 0 + next +} +inblock { block[++n] = $0; next } +{ print } + +END { if (inblock) process() } + +function process(i, ln, has_lt, has_gt, has_sep, pos, ch, left) { + has_lt = has_gt = has_sep = 0 + for (i = 1; i <= n; i++) { + ln = block[i] + if (ln ~ /^/) has_gt = 1 + else if (ln ~ /^---$/) has_sep = 1 + } + + if (has_lt && !has_gt) { + # if header was a delete, make it a change with identical new-range + pos = 0 + for (i = 1; i <= length(hdr); i++) { ch = substr(hdr, i, 1); if (ch=="a"||ch=="c"||ch=="d"){ pos = i; break } } + if (pos && substr(hdr, pos, 1) == "d") { + left = substr(hdr, 1, pos-1) + hdr = left "c" left + } else if (match(hdr, /[acd]/)) { + pos = RSTART + if (substr(hdr, pos, 1) != "c") hdr = substr(hdr,1,pos-1) "c" substr(hdr,pos+1) + } + + print hdr + for (i = 1; i <= n; i++) print block[i] + if (!has_sep) print "---" + for (i = 1; i <= n; i++) { + ln = block[i] + if (ln ~ /^ #" ln } + } + } else { + print hdr + for (i = 1; i <= n; i++) print block[i] + } + inblock = 0; n = 0 +}' "$tmp_diff" >"$tmp_out" + + mv "$tmp_out" "$tmp_diff" + + patch -s -i "$tmp_diff" "$tmp_old" + + cat "$tmp_old" +} diff --git a/deploy/backup/backup.yaml b/deploy/backup/backup.yaml index 4fab3c5ac..3917f6b9f 100644 --- a/deploy/backup/backup.yaml +++ b/deploy/backup/backup.yaml @@ -1,21 +1,21 @@ apiVersion: ps.percona.com/v1 kind: PerconaServerMySQLBackup metadata: - name: backup1 finalizers: - percona.com/delete-backup + name: backup1 spec: clusterName: ps-cluster1 storageName: minio # sourcePod: ps-cluster1-mysql-1 # containerOptions: # env: -# - name: CUSTOM_VAR -# value: "false" +# - name: CUSTOM_VAR +# value: "false" # args: -# xtrabackup: -# - "--someflag=abc" # xbcloud: -# - "--someflag=abc" +# - --someflag=abc # xbstream: -# - "--someflag=abc" +# - --someflag=abc +# xtrabackup: +# - --someflag=abc diff --git a/deploy/backup/restore.yaml b/deploy/backup/restore.yaml index b8f7cd204..6790fa2ad 100644 --- a/deploy/backup/restore.yaml +++ b/deploy/backup/restore.yaml @@ -7,20 +7,95 @@ spec: backupName: backup1 # containerOptions: # env: -# - name: CUSTOM_VAR -# value: "false" +# - name: CUSTOM_VAR +# value: "false" # args: -# xtrabackup: -# - "--someflag=abc" # xbcloud: -# - "--someflag=abc" +# - --someflag=abc # xbstream: -# - "--someflag=abc" +# - --someflag=abc +# xtrabackup: +# - --someflag=abc # backupSource: # destination: s3://S3-BACKUP-BUCKET-NAME-HERE/backup-path # storage: +# affinity: +# nodeAffinity: +# requiredDuringSchedulingIgnoredDuringExecution: +# nodeSelectorTerms: +# - matchExpressions: +# - key: kubernetes.io/e2e-az-name +# operator: In +# values: +# - e2e-az1 +# - e2e-az2 +# annotations: +# service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp +# service.beta.kubernetes.io/aws-load-balancer-type: nlb +# containerOptions: +# args: +# xbcloud: +# - --someflag=abc +# xbstream: +# - --someflag=abc +# xtrabackup: +# - --someflag=abc +# env: +# - name: CUSTOM_VAR +# value: "false" +# containerSecurityContext: +# privileged: false +# runAsGroup: 1001 +# runAsUser: 1001 +# labels: +# rack: rack-22 +# nodeSelector: +# topology.kubernetes.io/zone: us-east-1a +# podSecurityContext: +# fsGroup: 1001 +# supplementalGroups: +# - 1001 +# - 1002 +# - 1003 +# priorityClassName: high-priority +# resources: +# limits: +# cpu: 100m +# memory: 100M +# requests: +# cpu: 200m +# memory: 200M +# runtimeClassName: image-rc # s3: # bucket: S3-BACKUP-BUCKET-NAME-HERE -# credentialsSecret: cluster1-s3-credentials +# credentialsSecret: ps-cluster1-s3-credentials +# endpointUrl: https://s3.amazonaws.com +# prefix: PREFIX_NAME # region: us-west-2 +# schedulerName: default-scheduler +# tolerations: +# - effect: NoExecute +# key: node.alpha.kubernetes.io/unreachable +# operator: Exists +# tolerationSeconds: 6000 +# topologySpreadConstraints: +# - labelSelector: +# matchLabels: +# app.kubernetes.io/name: percona-server +# maxSkew: 1 +# topologyKey: kubernetes.io/hostname +# whenUnsatisfiable: DoNotSchedule # type: s3 +# verifyTLS: true +# volumeSpec: +# emptyDir: {} +# hostPath: +# path: /data +# type: Directory +# persistentVolumeClaim: +# accessModes: +# - ReadWriteOnce +# resources: +# requests: +# storage: 2Gi +# storageClassName: standard From 31368edb64f81f3a9312ea74035da67c10c4e478 Mon Sep 17 00:00:00 2001 From: Andrii Dema Date: Tue, 28 Oct 2025 11:13:53 +0200 Subject: [PATCH 07/13] fix shellcheck --- cmd/example-gen/scripts/generate.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/example-gen/scripts/generate.sh b/cmd/example-gen/scripts/generate.sh index 10e4b7712..2678a0a2c 100755 --- a/cmd/example-gen/scripts/generate.sh +++ b/cmd/example-gen/scripts/generate.sh @@ -2,7 +2,7 @@ resource=${1:-} -SCRIPT_DIR="$(dirname ${BASH_SOURCE[0]})" +SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")" case "$resource" in ps | ps-backup | ps-restore) @@ -19,4 +19,4 @@ go run cmd/example-gen/main.go "$resource" \ | sort_yaml \ | remove_fields \ | comment_fields \ - >$RESOURCE_PATH + >"$RESOURCE_PATH" From 231116db6ad20c1f427d92aca9851a56267e218e Mon Sep 17 00:00:00 2001 From: Andrii Dema Date: Tue, 28 Oct 2025 11:25:46 +0200 Subject: [PATCH 08/13] fix shellcheck --- cmd/example-gen/scripts/generate.sh | 14 +++++++++++--- cmd/example-gen/scripts/lib/ps-backup.sh | 9 +++++---- cmd/example-gen/scripts/lib/ps-restore.sh | 9 +++++---- cmd/example-gen/scripts/lib/ps.sh | 9 +++++---- 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/cmd/example-gen/scripts/generate.sh b/cmd/example-gen/scripts/generate.sh index 2678a0a2c..d0c1a9540 100755 --- a/cmd/example-gen/scripts/generate.sh +++ b/cmd/example-gen/scripts/generate.sh @@ -5,10 +5,18 @@ resource=${1:-} SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")" case "$resource" in - ps | ps-backup | ps-restore) - . "$SCRIPT_DIR/lib/$resource.sh" + ps) + # shellcheck source=lib/ps.sh + . "$SCRIPT_DIR/lib/ps.sh" + ;; + ps-backup) + # shellcheck source=lib/ps-backup.sh + . "$SCRIPT_DIR/lib/ps-backup.sh" + ;; + ps-restore) + # shellcheck source=lib/ps-restore.sh + . "$SCRIPT_DIR/lib/ps-restore.sh" ;; - *) echo "Usage: $0 {ps|ps-backup|ps-restore}" >&2 exit 2 diff --git a/cmd/example-gen/scripts/lib/ps-backup.sh b/cmd/example-gen/scripts/lib/ps-backup.sh index b580d6ddc..be3ec276a 100644 --- a/cmd/example-gen/scripts/lib/ps-backup.sh +++ b/cmd/example-gen/scripts/lib/ps-backup.sh @@ -1,25 +1,26 @@ #!/usr/bin/env bash +# shellcheck source=util.sh . "$SCRIPT_DIR/lib/util.sh" -RESOURCE_PATH="deploy/backup/backup.yaml" +export RESOURCE_PATH="deploy/backup/backup.yaml" sort_yaml() { SPEC_ORDER='"clusterName", "storageName", "sourcePod", "containerOptions"' CONTAINER_OPTS_ORDER='"env", "args"' - yq - "$@" \ + yq - \ | yq '.spec |= pick((['"$SPEC_ORDER"'] + keys) | unique)' \ | yq '.spec.containerOptions |= pick((['"$CONTAINER_OPTS_ORDER"'] + keys) | unique)' } remove_fields() { - yq - "$@" \ + yq - \ | yq "del(.status)" } del_fields_to_comment() { - yq - "$@" \ + yq - \ | yq "del(.spec.containerOptions)" \ | yq "del(.spec.sourcePod)" } diff --git a/cmd/example-gen/scripts/lib/ps-restore.sh b/cmd/example-gen/scripts/lib/ps-restore.sh index 1a1f5f44d..fb6eb1428 100644 --- a/cmd/example-gen/scripts/lib/ps-restore.sh +++ b/cmd/example-gen/scripts/lib/ps-restore.sh @@ -1,20 +1,21 @@ #!/usr/bin/env bash +# shellcheck source=util.sh . "$SCRIPT_DIR/lib/util.sh" -RESOURCE_PATH="deploy/backup/restore.yaml" +export RESOURCE_PATH="deploy/backup/restore.yaml" sort_yaml() { SPEC_ORDER='"clusterName", "backupName", "containerOptions", "backupSource"' CONTAINER_OPTS_ORDER='"env", "args"' - yq - "$@" \ + yq - \ | yq '.spec |= pick((['"$SPEC_ORDER"'] + keys) | unique)' \ | yq '.spec.containerOptions |= pick((['"$CONTAINER_OPTS_ORDER"'] + keys) | unique)' } remove_fields() { - yq - "$@" \ + yq - \ | yq "del(.status)" \ | yq "del(.spec.backupSource.backupSource)" \ | yq "del(.spec.backupSource.completed)" \ @@ -26,7 +27,7 @@ remove_fields() { } del_fields_to_comment() { - yq - "$@" \ + yq - \ | yq "del(.spec.containerOptions)" \ | yq "del(.spec.backupSource)" } diff --git a/cmd/example-gen/scripts/lib/ps.sh b/cmd/example-gen/scripts/lib/ps.sh index 177f8a74f..adb139b73 100644 --- a/cmd/example-gen/scripts/lib/ps.sh +++ b/cmd/example-gen/scripts/lib/ps.sh @@ -1,8 +1,9 @@ #!/usr/bin/env bash +# shellcheck source=util.sh . "$SCRIPT_DIR/lib/util.sh" -RESOURCE_PATH="deploy/cr.yaml" +export RESOURCE_PATH="deploy/cr.yaml" sort_yaml() { GENERAL_ORDER='"metadata", "unsafeFlags", "pause", "crVersion", "enableVolumeExpansion", "secretsName", "sslSecretName", "updateStrategy", "upgradeOptions", "initContainer", "ignoreAnnotations", "ignoreLabels", "tls", "mysql", "proxy", "orchestrator", "pmm", "backup", "toolkit"' @@ -17,7 +18,7 @@ sort_yaml() { BACKUP_ORDER='"enabled","pitr","sourcePod","image","imagePullPolicy","imagePullSecrets","schedule","backoffLimit", "serviceAccountName", "initContainer", "containerSecurityContext", "resources","storages","pitr"' TOOLKIT_ORDER='"image","imagePullPolicy","imagePullSecrets","env","envFrom","resources","containerSecurityContext", "startupProbe", "readinessProbe", "livenessProbe"' - yq - "$@" \ + yq - \ | yq '.spec |= pick((['"$GENERAL_ORDER"'] + keys) | unique)' \ | yq '.spec.mysql |= pick((['"$MYSQL_ORDER"'] + keys) | unique)' \ | yq '.spec.proxy.haproxy |= pick((['"$HAPROXY_ORDER"'] + keys) | unique)' \ @@ -33,7 +34,7 @@ remove_fields() { # - removing binlogServer is not used # - removing azure-blob fields to reduce size # - removing non-s3 fields in s3-us-west - yq - "$@" \ + yq - \ | yq 'del(.status)' \ | yq 'del(.spec.backup.initImage)' \ | yq 'del(.spec.initImage)' \ @@ -57,7 +58,7 @@ remove_fields() { } del_fields_to_comment() { - yq - "$@" \ + yq - \ | yq "del(.metadata.finalizers[1])" \ | yq "del(.metadata.finalizers[1])" \ | yq "del(.spec.metadata)" \ From de4116b9fff0c33643d51f944650868ef2718ba0 Mon Sep 17 00:00:00 2001 From: Andrii Dema Date: Tue, 28 Oct 2025 11:38:22 +0200 Subject: [PATCH 09/13] fix shellcheck --- .github/workflows/reviewdog.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reviewdog.yml b/.github/workflows/reviewdog.yml index a2014ea9d..4d1c4f644 100644 --- a/.github/workflows/reviewdog.yml +++ b/.github/workflows/reviewdog.yml @@ -52,7 +52,7 @@ jobs: with: github_token: ${{ secrets.github_token }} reporter: github-pr-check - shellcheck_flags: -e SC2155 + shellcheck_flags: -e SC2155 --external-sources exclude: "./vendor/*" misspell: From 168ea4242a8c9c04144334219eb6f5cd29e22ccb Mon Sep 17 00:00:00 2001 From: Andrii Dema Date: Tue, 28 Oct 2025 11:44:35 +0200 Subject: [PATCH 10/13] fix shellcheck --- cmd/example-gen/scripts/generate.sh | 6 +++--- cmd/example-gen/scripts/lib/ps-backup.sh | 2 +- cmd/example-gen/scripts/lib/ps-restore.sh | 2 +- cmd/example-gen/scripts/lib/ps.sh | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/example-gen/scripts/generate.sh b/cmd/example-gen/scripts/generate.sh index d0c1a9540..4850e60bb 100755 --- a/cmd/example-gen/scripts/generate.sh +++ b/cmd/example-gen/scripts/generate.sh @@ -6,15 +6,15 @@ SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")" case "$resource" in ps) - # shellcheck source=lib/ps.sh + # shellcheck source=cmd/example-gen/scripts/lib/ps.sh . "$SCRIPT_DIR/lib/ps.sh" ;; ps-backup) - # shellcheck source=lib/ps-backup.sh + # shellcheck source=cmd/example-gen/scripts/lib/ps-backup.sh . "$SCRIPT_DIR/lib/ps-backup.sh" ;; ps-restore) - # shellcheck source=lib/ps-restore.sh + # shellcheck source=cmd/example-gen/scripts/lib/ps-restore.sh . "$SCRIPT_DIR/lib/ps-restore.sh" ;; *) diff --git a/cmd/example-gen/scripts/lib/ps-backup.sh b/cmd/example-gen/scripts/lib/ps-backup.sh index be3ec276a..129b7d1f2 100644 --- a/cmd/example-gen/scripts/lib/ps-backup.sh +++ b/cmd/example-gen/scripts/lib/ps-backup.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# shellcheck source=util.sh +# shellcheck source=cmd/example-gen/scripts/lib/util.sh . "$SCRIPT_DIR/lib/util.sh" export RESOURCE_PATH="deploy/backup/backup.yaml" diff --git a/cmd/example-gen/scripts/lib/ps-restore.sh b/cmd/example-gen/scripts/lib/ps-restore.sh index fb6eb1428..cf7ef2a54 100644 --- a/cmd/example-gen/scripts/lib/ps-restore.sh +++ b/cmd/example-gen/scripts/lib/ps-restore.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# shellcheck source=util.sh +# shellcheck source=cmd/example-gen/scripts/lib/util.sh . "$SCRIPT_DIR/lib/util.sh" export RESOURCE_PATH="deploy/backup/restore.yaml" diff --git a/cmd/example-gen/scripts/lib/ps.sh b/cmd/example-gen/scripts/lib/ps.sh index adb139b73..1f0243ef9 100644 --- a/cmd/example-gen/scripts/lib/ps.sh +++ b/cmd/example-gen/scripts/lib/ps.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# shellcheck source=util.sh +# shellcheck source=cmd/example-gen/scripts/lib/util.sh . "$SCRIPT_DIR/lib/util.sh" export RESOURCE_PATH="deploy/cr.yaml" From 918120ea37803afa99991d511be60166c8eeb135 Mon Sep 17 00:00:00 2001 From: Andrii Dema Date: Tue, 28 Oct 2025 11:49:09 +0200 Subject: [PATCH 11/13] fix shellcheck --- cmd/example-gen/scripts/lib/util.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/example-gen/scripts/lib/util.sh b/cmd/example-gen/scripts/lib/util.sh index 2da697d3c..3821eb702 100644 --- a/cmd/example-gen/scripts/lib/util.sh +++ b/cmd/example-gen/scripts/lib/util.sh @@ -8,7 +8,7 @@ comment_fields() { tmp_diff=$(mktemp) tmp_out=$(mktemp) - yq - "$@" >"$tmp_old" + yq - >"$tmp_old" del_fields_to_comment <"$tmp_old" >"$tmp_new" diff "$tmp_old" "$tmp_new" >"$tmp_diff" From ed6a0a015ce8d322cee842885e366d74ae148838 Mon Sep 17 00:00:00 2001 From: Andrii Dema Date: Tue, 28 Oct 2025 14:00:18 +0200 Subject: [PATCH 12/13] check --- deploy/cr.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/deploy/cr.yaml b/deploy/cr.yaml index bb877f222..8bd83182c 100644 --- a/deploy/cr.yaml +++ b/deploy/cr.yaml @@ -4,7 +4,6 @@ metadata: finalizers: - percona.com/delete-mysql-pods-in-order # - percona.com/delete-ssl -# - percona.com/delete-mysql-pvc name: ps-cluster1 spec: # metadata: From 3c20bca755aa8ec0594dcd81799efe27c12fda7c Mon Sep 17 00:00:00 2001 From: Andrii Dema Date: Tue, 28 Oct 2025 14:04:12 +0200 Subject: [PATCH 13/13] Revert "check" This reverts commit ed6a0a015ce8d322cee842885e366d74ae148838. --- deploy/cr.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/deploy/cr.yaml b/deploy/cr.yaml index 8bd83182c..bb877f222 100644 --- a/deploy/cr.yaml +++ b/deploy/cr.yaml @@ -4,6 +4,7 @@ metadata: finalizers: - percona.com/delete-mysql-pods-in-order # - percona.com/delete-ssl +# - percona.com/delete-mysql-pvc name: ps-cluster1 spec: # metadata: