@@ -26,6 +26,30 @@ import (
2626 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2727)
2828
29+ // PoolerPhase represents the lifecycle phase of a Pooler.
30+ // +kubebuilder:validation:Enum=active;paused;inactive;failed
31+ type PoolerPhase string
32+
33+ const (
34+ // PoolerPhaseActive means the pooler is running normally and serving traffic.
35+ PoolerPhaseActive PoolerPhase = "active"
36+
37+ // PoolerPhasePaused means PgBouncer is up and running but holding new client
38+ // connections in the queue because spec.pgbouncer.paused is true. The Deployment
39+ // keeps reconciling; lifting the pause transitions back to Active.
40+ PoolerPhasePaused PoolerPhase = "paused"
41+
42+ // PoolerPhaseInactive means the pooler cannot make progress because a
43+ // prerequisite resource is missing (cluster, secret, certificate). The
44+ // controller retries periodically until the prerequisite shows up. Check
45+ // status.phaseReason for the specific cause.
46+ PoolerPhaseInactive PoolerPhase = "inactive"
47+
48+ // PoolerPhaseFailed means the pooler cannot be reconciled due to a
49+ // configuration error. Check status.phaseReason for details.
50+ PoolerPhaseFailed PoolerPhase = "failed"
51+ )
52+
2953// PoolerType is the type of the connection pool, meaning the service
3054// we are targeting. Allowed values are `rw` and `ro`.
3155// +kubebuilder:validation:Enum=rw;ro;r
@@ -188,7 +212,21 @@ type ServiceTemplateSpec struct {
188212 Spec corev1.ServiceSpec `json:"spec,omitempty"`
189213}
190214
215+ // ImageCatalogComponentRef identifies a named image within the componentImages list of an
216+ // ImageCatalog or ClusterImageCatalog.
217+ type ImageCatalogComponentRef struct {
218+ // +kubebuilder:validation:XValidation:rule="self.kind == 'ImageCatalog' || self.kind == 'ClusterImageCatalog'",message="Only ImageCatalog and ClusterImageCatalog are supported"
219+ // +kubebuilder:validation:XValidation:rule="self.apiGroup == 'postgresql.cnpg.io'",message="apiGroup must be postgresql.cnpg.io"
220+ corev1.TypedLocalObjectReference `json:",inline"`
221+
222+ // Key identifies the entry within the catalog's componentImages list.
223+ // +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`
224+ // +kubebuilder:validation:MaxLength=63
225+ Key string `json:"key"`
226+ }
227+
191228// PgBouncerSpec defines how to configure PgBouncer
229+ // +kubebuilder:validation:XValidation:rule="!(has(self.image) && has(self.imageCatalogRef))",message="image and imageCatalogRef are mutually exclusive"
192230type PgBouncerSpec struct {
193231 // The pool mode. Default: `session`.
194232 // +kubebuilder:default:=session
@@ -250,16 +288,45 @@ type PgBouncerSpec struct {
250288 // +kubebuilder:default:=false
251289 // +optional
252290 Paused * bool `json:"paused,omitempty"`
291+
292+ // Image is the pgbouncer container image to use. When set, it takes
293+ // precedence over ImageCatalogRef and the operator default, but is
294+ // overridden by an explicit image set in the pod template.
295+ // +optional
296+ Image string `json:"image,omitempty"`
297+
298+ // ImageCatalogRef points to an entry in an ImageCatalog or ClusterImageCatalog.
299+ // Mutually exclusive with Image.
300+ // +optional
301+ ImageCatalogRef * ImageCatalogComponentRef `json:"imageCatalogRef,omitempty"`
253302}
254303
255304// PoolerStatus defines the observed state of Pooler
256305type PoolerStatus struct {
257306 // The resource version of the config object
258307 // +optional
259308 Secrets * PoolerSecrets `json:"secrets,omitempty"`
309+
260310 // The number of pods trying to be scheduled
261311 // +optional
262312 Instances int32 `json:"instances,omitempty"`
313+
314+ // Phase summarizes the overall lifecycle state of the Pooler.
315+ // +optional
316+ Phase PoolerPhase `json:"phase,omitempty"`
317+
318+ // PhaseReason is a human-readable explanation of the current Phase.
319+ // +optional
320+ PhaseReason string `json:"phaseReason,omitempty"`
321+
322+ // Image is the resolved pgbouncer container image that the operator is
323+ // using for this Pooler, including any override coming from spec.template.
324+ // While Phase is Active or Paused this field reflects what the Deployment
325+ // actually runs; while Phase is Inactive or Failed it may carry the last
326+ // successfully resolved value (or be empty if the Pooler has never reconciled
327+ // successfully).
328+ // +optional
329+ Image string `json:"image,omitempty"`
263330}
264331
265332// PoolerSecrets contains the versions of all the secrets used
@@ -310,6 +377,7 @@ type SecretVersion struct {
310377// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
311378// +kubebuilder:printcolumn:name="Cluster",type="string",JSONPath=".spec.cluster.name"
312379// +kubebuilder:printcolumn:name="Type",type="string",JSONPath=".spec.type"
380+ // +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase"
313381// +kubebuilder:subresource:scale:specpath=.spec.instances,statuspath=.status.instances
314382
315383// Pooler is the Schema for the poolers API
0 commit comments