-
Notifications
You must be signed in to change notification settings - Fork 22
Expand file tree
/
Copy pathcommon.go
More file actions
428 lines (360 loc) · 16 KB
/
common.go
File metadata and controls
428 lines (360 loc) · 16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
package v1alpha1
import (
"errors"
"fmt"
"strings"
corev1 "k8s.io/api/core/v1"
policyv1 "k8s.io/api/policy/v1"
"k8s.io/apimachinery/pkg/util/intstr"
)
// ContainerImage defines a container image with repository, tag or hash.
type ContainerImage struct {
// Container image registry name
// Example: docker.io/clickhouse/clickhouse
// +optional
Repository string `json:"repository,omitempty"`
// Container image tag, mutually exclusive with 'hash'.
// Example: 25.3
// +optional
Tag string `json:"tag,omitempty"`
// Container image hash, mutually exclusive with 'tag'.
// +optional
Hash string `json:"hash,omitempty"`
}
func (c *ContainerImage) String() string {
if c.Tag != "" {
return fmt.Sprintf("%s:%s", c.Repository, c.Tag)
}
if c.Hash != "" {
return fmt.Sprintf("%s@%s", c.Repository, c.Hash)
}
return c.Repository
}
// LoggerConfig defines server logging configuration.
type LoggerConfig struct {
// If false then disable all logging to file.
// +optional
// +kubebuilder:default:=true
LogToFile *bool `json:"logToFile,omitempty"`
// If true, then log in JSON format.
// +optional
// +kubebuilder:default:=false
JSONLogs bool `json:"jsonLogs,omitempty"`
// Server logger verbosity level.
// +optional
// +kubebuilder:validation:Enum:=test;trace;debug;information;notice;warning;error;critical;fatal
// +kubebuilder:default:=trace
Level string `json:"level,omitempty"`
// Maximum log file size.
// +optional
// +kubebuilder:default:="1000M"
Size string `json:"size,omitempty"`
// Maximum number of log files to keep.
// +optional
// +kubebuilder:default:=50
Count int64 `json:"count,omitempty"`
}
// PDBPolicy controls whether PodDisruptionBudgets are created.
// +kubebuilder:validation:Enum=Enabled;Disabled;Ignored
type PDBPolicy string
const (
// PDBPolicyEnabled enables PodDisruptionBudgets creation by the operator.
PDBPolicyEnabled PDBPolicy = "Enabled"
// PDBPolicyDisabled disables PodDisruptionBudgets, operator will delete resource with matching labels.
PDBPolicyDisabled PDBPolicy = "Disabled"
// PDBPolicyIgnored ignores PodDisruptionBudgets, operator will not create or delete any PDBs, existing PDBs will be left unchanged.
PDBPolicyIgnored PDBPolicy = "Ignored"
)
// PodDisruptionBudgetSpec configures the PDB created for the cluster.
// Exactly one of MinAvailable or MaxUnavailable may be set.
// When neither is set, the operator picks a safe default based on replica count.
type PodDisruptionBudgetSpec struct {
// Policy controls whether the operator creates PodDisruptionBudgets.
// Defaults to "Enabled" when unset. Set it to "Disabled" to skip PDB creation (e.g. for development environments).
// +optional
// +kubebuilder:default:=Enabled
Policy PDBPolicy `json:"policy,omitempty"`
// MinAvailable is the minimum number of pods that must remain available during a disruption.
// +optional
MinAvailable *intstr.IntOrString `json:"minAvailable,omitempty"`
// MaxUnavailable is the maximum number of pods that can be unavailable during a disruption.
// +optional
MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"`
// UnhealthyPodEvictionPolicy defines the criteria for when unhealthy pods
// should be considered for eviction.
// Valid values are "IfReady" and "AlwaysAllow".
// +optional
UnhealthyPodEvictionPolicy *policyv1.UnhealthyPodEvictionPolicyType `json:"unhealthyPodEvictionPolicy,omitempty"`
}
// Enabled returns true if the PodDisruptionBudgets should be created.
func (s *PodDisruptionBudgetSpec) Enabled() bool {
return s == nil || s.Policy == PDBPolicyEnabled || s.Policy == ""
}
// Ignored returns true if the PodDisruptionBudgets should be ignored.
func (s *PodDisruptionBudgetSpec) Ignored() bool {
return s != nil && s.Policy == PDBPolicyIgnored
}
// Validate validates the PodDisruptionBudgetSpec configuration.
func (s *PodDisruptionBudgetSpec) Validate() error {
if s == nil {
return nil
}
if s.MinAvailable != nil && s.MaxUnavailable != nil {
return errors.New("only one of podDisruptionBudget.minAvailable or podDisruptionBudget.maxUnavailable can be set")
}
return nil
}
// ApplyOverrides applies the PodDisruptionBudgetSpec configuration to the given PodDisruptionBudgetSpec.
func (s *PodDisruptionBudgetSpec) ApplyOverrides(pdb *policyv1.PodDisruptionBudgetSpec) {
if s == nil {
return
}
if s.MinAvailable != nil {
pdb.MaxUnavailable = nil
pdb.MinAvailable = s.MinAvailable
}
if s.MaxUnavailable != nil {
pdb.MinAvailable = nil
pdb.MaxUnavailable = s.MaxUnavailable
}
if s.UnhealthyPodEvictionPolicy != nil {
pdb.UnhealthyPodEvictionPolicy = s.UnhealthyPodEvictionPolicy
}
}
// PodTemplateSpec describes the pod configuration overrides for the cluster's pods.
type PodTemplateSpec struct {
// Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request.
// Value must be non-negative integer. The value zero indicates stop immediately via
// the kill signal (no opportunity to shut down).
// If this value is nil, the default grace period will be used instead.
// The grace period is the duration in seconds after the processes running in the pod are sent
// a termination signal and the time when the processes are forcibly halted with a kill signal.
// Set this value longer than the expected cleanup time for your process.
// Defaults to 30 seconds.
// +optional
TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"`
// TopologySpreadConstraints describes how a group of pods ought to spread across topology
// domains. Scheduler will schedule pods in a way which abides by the constraints.
// All topologySpreadConstraints are ANDed.
// Merged with operator defaults by `topologyKey`.
// +optional
// +patchMergeKey=topologyKey
// +patchStrategy=merge
// +listType=map
// +listMapKey=topologyKey
// +listMapKey=whenUnsatisfiable
TopologySpreadConstraints []corev1.TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty" patchMergeKey:"topologyKey" patchStrategy:"merge"`
// ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec.
// If specified, these secrets will be passed to individual puller implementations for them to use.
// More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod
// Merged with operator defaults by name.
// +optional
// +patchMergeKey=name
// +patchStrategy=merge
// +listType=map
// +listMapKey=name
ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty" patchMergeKey:"name" patchStrategy:"merge"`
// NodeSelector is a selector which must be true for the pod to fit on a node.
// Selector which must match a node's labels for the pod to be scheduled on that node.
// More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
// +optional
// +mapType=atomic
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
// If specified, the pod's scheduling constraints.
// Appended to operator defaults: scheduling term lists are concatenated.
// +optional
Affinity *corev1.Affinity `json:"affinity,omitempty"`
// If specified, the pod's Tolerations.
// +optional
// +listType=atomic
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
// If specified, the pod will be dispatched by specified scheduler.
// If not specified, the pod will be dispatched by default scheduler.
// +optional
SchedulerName string `json:"schedulerName,omitempty"`
// ServiceAccountName is the name of the ServiceAccount to use to run this pod.
// More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/
// +optional
ServiceAccountName string `json:"serviceAccountName,omitempty"`
// PriorityClassName is the name of the PriorityClass to use for the pod.
// +optional
PriorityClassName *string `json:"priorityClassName,omitempty"`
// RuntimeClassName is the name of the RuntimeClass to use for the pod.
// +optional
RuntimeClassName *string `json:"runtimeClassName,omitempty"`
// Volumes defines the list of volumes that can be mounted by containers belonging to the pod.
// More info: https://kubernetes.io/docs/concepts/storage/volumes
// Merged with operator defaults by name; a user volume replaces any operator volume with the same name.
// +optional
// +patchMergeKey=name
// +patchStrategy=merge
// +listType=map
// +listMapKey=name
Volumes []corev1.Volume `json:"volumes,omitempty" patchMergeKey:"name" patchStrategy:"merge"`
// SecurityContext holds pod-level security attributes and common container settings.
// Deep-merged with operator defaults via SMP. When nil, operator defaults are preserved.
// +optional
SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"`
// TopologyZoneKey is the key of node labels.
// Nodes that have a label with this key and identical values are considered to be in the same topology zone.
// Set it to enable default TopologySpreadConstraints and Affinity rules to spread pods across zones.
// Recommended to be set to "topology.kubernetes.io/zone"
// +optional
TopologyZoneKey *string `json:"topologyZoneKey,omitempty"`
// NodeHostnameKey is the key of node labels.
// Nodes that have a label with this key and identical values are considered to be on the same node.
// Set it to enable default AntiAffinity rules to spread replicas from the different shards across nodes.
// Recommended to be set to "kubernetes.io/hostname"
// +optional
NodeHostnameKey *string `json:"nodeHostnameKey,omitempty"`
// InitContainers is the list of init containers to run before the main server container starts.
// Merged with operator defaults by name.
// with the same name.
// +optional
// +patchMergeKey=name
// +patchStrategy=merge
// +listType=map
// +listMapKey=name
InitContainers []corev1.Container `json:"initContainers,omitempty" patchMergeKey:"name" patchStrategy:"merge"`
}
// ContainerTemplateSpec describes the container configuration overrides for the cluster's containers.
type ContainerTemplateSpec struct {
// Image is the container image to be deployed.
Image ContainerImage `json:"image,omitempty"`
// ImagePullPolicy for the image, which defaults to IfNotPresent.
// +optional
// +kubebuilder:validation:Enum="Always";"Never";"IfNotPresent"
ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"`
// Resources is the resource requirements for the server container.
// Deep-merged with operator defaults via SMP. Individual limits and requests override only matching
// keys; unset fields preserve operator defaults.
// +optional
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
// VolumeMounts is the list of volume mounts for the container.
// Concatenated with operator-generated mounts. Entries sharing a `mountPath` with an operator
// mount are merged into a projected volume.
// +optional
// +patchMergeKey=mountPath
// +patchStrategy=merge
// +listType=map
// +listMapKey=mountPath
VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty" patchMergeKey:"mountPath" patchStrategy:"merge"`
// Env is the list of environment variables to set in the container.
// Merged with operator defaults by name.
// +optional
// +patchMergeKey=name
// +patchStrategy=merge
// +listType=map
// +listMapKey=name
Env []corev1.EnvVar `json:"env,omitempty" patchMergeKey:"name" patchStrategy:"merge"`
// SecurityContext defines the security options the container should be run with.
// Deep-merged with operator defaults via SMP. When nil, operator defaults are preserved.
// More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/
// +optional
SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"`
// LivenessProbe overrides the operator's default liveness probe.
// +optional
LivenessProbe *corev1.Probe `json:"livenessProbe,omitempty"`
// ReadinessProbe overrides the operator's default readiness probe.
// +optional
ReadinessProbe *corev1.Probe `json:"readinessProbe,omitempty"`
}
// ClusterTLSSpec defines cluster TLS configuration.
type ClusterTLSSpec struct {
// Enabled indicates whether TLS is enabled, determining if secure ports should be opened.
// +kubebuilder:default:=false
// +optional
Enabled bool `json:"enabled"`
// Required specifies whether TLS must be enforced for all connections. Disables not secure ports.
// +kubebuilder:default:=false
// +optional
Required bool `json:"required,omitempty"`
// ServerCertSecretRef is a reference to a TLS Secret containing the server certificate.
// It is expected that the Secret has the same structure as certificates generated by cert-manager,
// with the certificate and private key stored under "tls.crt" and "tls.key" keys respectively.
// +optional
ServerCertSecret *corev1.LocalObjectReference `json:"serverCertSecret,omitempty"`
// CABundle is a reference to a TLS Secret containing the CA bundle.
// If empty and ServerCertSecret is specified, the CA bundle from certificate will be used.
// Otherwise, system trusted CA bundle will be used.
// Key is defaulted to "ca.crt" if not specified.
// +optional
CABundle *SecretKeySelector `json:"caBundle,omitempty"`
}
// Validate validates the ClusterTLSSpec configuration.
func (s *ClusterTLSSpec) Validate() error {
if !s.Enabled {
if s.Required {
return errors.New("TLS cannot be required if it is not enabled")
}
return nil
}
if s.ServerCertSecret == nil || s.ServerCertSecret.Name == "" {
return errors.New("serverCertSecret must be specified when TLS is enabled")
}
return nil
}
// SecretKeySelector selects a key of a Secret.
type SecretKeySelector struct {
// The name of the secret in the cluster's namespace to select from.
// +kubebuilder:validation:Required
Name string `json:"name,omitempty"`
// The key of the secret to select from. Must be a valid secret key.
// +kubebuilder:validation:Required
Key string `json:"key,omitempty"`
}
// ConfigMapKeySelector selects a key of a ConfigMap.
type ConfigMapKeySelector struct {
// The name of the ConfigMap in the cluster's namespace to select from.
// +kubebuilder:validation:Required
Name string `json:"name,omitempty"`
// The key of the ConfigMap to select from. Must be a valid key.
// +kubebuilder:validation:Required
Key string `json:"key,omitempty"`
}
// DefaultPasswordSelector selects the source for the default user's password.
type DefaultPasswordSelector struct {
// Type of the provided password. Consider documentation for possible values https://clickhouse.com/docs/operations/settings/settings-users#user-namepassword
// +kubebuilder:default:=password
PasswordType string `json:"passwordType,omitempty"`
// Select password value from a Secret key
// +optional
Secret *SecretKeySelector `json:"secret,omitempty"`
// Select password value from a ConfigMap key
// +optional
ConfigMap *ConfigMapKeySelector `json:"configMap,omitempty"`
}
// Validate validates the DefaultPasswordSelector configuration.
func (s *DefaultPasswordSelector) Validate() error {
if s == nil {
return nil
}
// Ensure exactly one source is specified
hasSecret := s.Secret != nil
hasConfigMap := s.ConfigMap != nil
if hasSecret == hasConfigMap { // both set or both nil
return errors.New("exactly one of secret or configMap must be specified")
}
if hasSecret {
if s.Secret.Name == "" || s.Secret.Key == "" {
return errors.New("default user secret name and key must be specified when using secret")
}
}
if hasConfigMap {
if s.ConfigMap.Name == "" || s.ConfigMap.Key == "" {
return errors.New("default user configMap name and key must be specified when using ConfigMap")
}
}
return nil
}
// normalizeName removes dots from name to make it valid for use as a hostname or label value, where dots are not allowed.
func normalizeName(name string) string {
return strings.ReplaceAll(name, ".", "-")
}
// formatPodHostname returns hostname for the first pod in the StatefulSet.
func formatPodHostname(stsName, serviceName, namespace, domain string) string {
if domain == "" {
domain = DefaultClusterDomain
}
return fmt.Sprintf("%s-0.%s.%s.svc.%s", stsName, serviceName, namespace, domain)
}