diff --git a/Makefile b/Makefile index a351bd3..c09bf2d 100644 --- a/Makefile +++ b/Makefile @@ -80,7 +80,7 @@ lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes .PHONY: build build: manifests generate fmt vet ## Build manager binary. - go build -o bin/manager cmd/main.go + go build -o bin/manager cmd/manager/main.go .PHONY: run run: manifests generate fmt vet ## Run a controller from your host. diff --git a/internal/cnpgi/operator/config/config.go b/internal/cnpgi/operator/config/config.go index f040aee..53a8e67 100644 --- a/internal/cnpgi/operator/config/config.go +++ b/internal/cnpgi/operator/config/config.go @@ -1,6 +1,7 @@ package config import ( + "strconv" "strings" cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1" @@ -57,6 +58,29 @@ type PluginConfiguration struct { ReplicaSourceBarmanObjectName string ReplicaSourceServerName string + + // Probe configuration + StartupProbeConfig *ProbeConfig +} + +// ProbeConfig holds configuration for Kubernetes probes +type ProbeConfig struct { + InitialDelaySeconds int32 + TimeoutSeconds int32 + PeriodSeconds int32 + FailureThreshold int32 + SuccessThreshold int32 +} + +// DefaultProbeConfig returns the default probe configuration +func DefaultProbeConfig() *ProbeConfig { + return &ProbeConfig{ + InitialDelaySeconds: 0, + TimeoutSeconds: 10, + PeriodSeconds: 10, + FailureThreshold: 10, + SuccessThreshold: 1, + } } // GetBarmanObjectKey gets the namespaced name of the barman object @@ -166,11 +190,50 @@ func NewFromCluster(cluster *cnpgv1.Cluster) *PluginConfiguration { // used for wal_restore in the designed primary of a replica cluster ReplicaSourceServerName: replicaSourceServerName, ReplicaSourceBarmanObjectName: replicaSourceBarmanObjectName, + // probe configuration + StartupProbeConfig: parseProbeConfig(helper.Parameters), } return result } +// parseProbeConfig parses probe configuration from plugin parameters +func parseProbeConfig(parameters map[string]string) *ProbeConfig { + config := DefaultProbeConfig() + + if val, ok := parameters["startupProbe.initialDelaySeconds"]; ok { + if parsed, err := strconv.ParseInt(val, 10, 32); err == nil { + config.InitialDelaySeconds = int32(parsed) + } + } + + if val, ok := parameters["startupProbe.timeoutSeconds"]; ok { + if parsed, err := strconv.ParseInt(val, 10, 32); err == nil { + config.TimeoutSeconds = int32(parsed) + } + } + + if val, ok := parameters["startupProbe.periodSeconds"]; ok { + if parsed, err := strconv.ParseInt(val, 10, 32); err == nil { + config.PeriodSeconds = int32(parsed) + } + } + + if val, ok := parameters["startupProbe.failureThreshold"]; ok { + if parsed, err := strconv.ParseInt(val, 10, 32); err == nil { + config.FailureThreshold = int32(parsed) + } + } + + if val, ok := parameters["startupProbe.successThreshold"]; ok { + if parsed, err := strconv.ParseInt(val, 10, 32); err == nil { + config.SuccessThreshold = int32(parsed) + } + } + + return config +} + func getRecoveryParameters(cluster *cnpgv1.Cluster) map[string]string { recoveryPluginConfiguration := getRecoverySourcePlugin(cluster) if recoveryPluginConfiguration == nil { diff --git a/internal/cnpgi/operator/lifecycle.go b/internal/cnpgi/operator/lifecycle.go index d5c9918..143d746 100644 --- a/internal/cnpgi/operator/lifecycle.go +++ b/internal/cnpgi/operator/lifecycle.go @@ -129,6 +129,7 @@ func (impl LifecycleImplementation) reconcileJob( env: env, certificates: certificates, resources: resources, + probeConfig: pluginConfiguration.StartupProbeConfig, }) } @@ -136,6 +137,7 @@ type sidecarConfiguration struct { env []corev1.EnvVar certificates []corev1.VolumeProjection resources corev1.ResourceRequirements + probeConfig *config.ProbeConfig } func reconcileJob( @@ -221,6 +223,7 @@ func (impl LifecycleImplementation) reconcilePod( env: env, certificates: certificates, resources: resources, + probeConfig: pluginConfiguration.StartupProbeConfig, }) } @@ -304,8 +307,6 @@ func reconcilePodSpec( envs = append(envs, config.env...) baseProbe := &corev1.Probe{ - FailureThreshold: 10, - TimeoutSeconds: 10, ProbeHandler: corev1.ProbeHandler{ Exec: &corev1.ExecAction{ Command: []string{"/manager", "healthcheck", "unix"}, @@ -313,6 +314,19 @@ func reconcilePodSpec( }, } + // Apply configurable probe settings if available + if config.probeConfig != nil { + baseProbe.InitialDelaySeconds = config.probeConfig.InitialDelaySeconds + baseProbe.TimeoutSeconds = config.probeConfig.TimeoutSeconds + baseProbe.PeriodSeconds = config.probeConfig.PeriodSeconds + baseProbe.FailureThreshold = config.probeConfig.FailureThreshold + baseProbe.SuccessThreshold = config.probeConfig.SuccessThreshold + } else { + // Fallback to default values + baseProbe.FailureThreshold = 10 + baseProbe.TimeoutSeconds = 10 + } + // fixed values sidecarTemplate.Name = "plugin-barman-cloud" sidecarTemplate.Image = viper.GetString("sidecar-image") diff --git a/internal/cnpgi/operator/lifecycle_test.go b/internal/cnpgi/operator/lifecycle_test.go index c3235de..24139b7 100644 --- a/internal/cnpgi/operator/lifecycle_test.go +++ b/internal/cnpgi/operator/lifecycle_test.go @@ -172,6 +172,53 @@ var _ = Describe("LifecycleImplementation", func() { }) Describe("reconcilePod", func() { + It("returns a patch for a valid pod with probe configuration", func(ctx SpecContext) { + // Configure plugin with custom probe settings + pluginConfiguration.StartupProbeConfig = &config.ProbeConfig{ + InitialDelaySeconds: 1, + TimeoutSeconds: 15, + PeriodSeconds: 2, + FailureThreshold: 5, + SuccessThreshold: 1, + } + + pod := &corev1.Pod{ + TypeMeta: podTypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-pod", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "postgres", + }, + }, + }, + } + + podJSON, err := json.Marshal(pod) + Expect(err).NotTo(HaveOccurred()) + + request := &lifecycle.OperatorLifecycleRequest{ + ObjectDefinition: podJSON, + } + + response, err := reconcilePod(ctx, cluster, request, pluginConfiguration, sidecarConfiguration{ + probeConfig: pluginConfiguration.StartupProbeConfig, + }) + Expect(err).NotTo(HaveOccurred()) + Expect(response).NotTo(BeNil()) + Expect(response.JsonPatch).NotTo(BeEmpty()) + + // Verify the patch contains the expected probe configuration + Expect(string(response.JsonPatch)).To(ContainSubstring("startupProbe")) + Expect(string(response.JsonPatch)).To(ContainSubstring("\"initialDelaySeconds\":1")) + Expect(string(response.JsonPatch)).To(ContainSubstring("\"timeoutSeconds\":15")) + Expect(string(response.JsonPatch)).To(ContainSubstring("\"periodSeconds\":2")) + Expect(string(response.JsonPatch)).To(ContainSubstring("\"failureThreshold\":5")) + Expect(string(response.JsonPatch)).To(ContainSubstring("\"successThreshold\":1")) + }) + It("returns a patch for a valid pod", func(ctx SpecContext) { pod := &corev1.Pod{ TypeMeta: podTypeMeta,