Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds the 'environment' Field to the PostgresCluster Spec #3993

Closed

Conversation

andrewlecuyer
Copy link
Collaborator

@andrewlecuyer andrewlecuyer commented Sep 13, 2024

An environment field is now available within the PostgresCluster spec, which allows the user to specify the infrastructure and/or stage in development the PostgresCluster is associated with. Accepted values for this field are production and development. production is the default.

Additionally, a CEL validation rule has been added to the CRD that requires backups to be configured for production PostgresCluster's. This change is fully backward compatible and non-breaking with existing PostgresCluster specs.

Issue: PGO-1645

An 'environment' field is now available within the PostgresCluster spec,
which allows the user to specify the infrastructure and/or stage in
development the PostgresCluster is associated with.  Accepted values for
this field are 'production' and 'development'. 'production' is the default.

Additionally, a CEL validation rule has been added to the CRD that requires
backups to be configured for 'production' PostgresCluster's.  This change is
fully backward compatible and non-breaking with existing PostgresCluster
specs.

Issue: PGO-1645
@benjaminjb
Copy link
Contributor

Worth adjusting the backups optional test to add a test for this?

Comment on lines +69 to +71
// +kubebuilder:default=production
// +optional
Environment *string `json:"environment,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 I forget how optional + default behaves. My memory is that the API fills in the default before it saves. If so, will we "always" have a value?

👍🏻 Pointer for optional seems right. If we don't like dereferencing it, we can change the struct later.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If so, will we "always" have a value?

These semantics always trip me up too. But yea, like you mentioned, the field should/will always have a value, since the API will will in the default if nothing is provided by the user.

@@ -13,6 +13,7 @@ import (
)

// PostgresClusterSpec defines the desired state of PostgresCluster
// +kubebuilder:validation:XValidation:rule="(self.environment == 'production') ? self.backups.pgbackrest != null : true", message="Backups must be enabled for production PostgresCluster's."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⛏️ I wanna drop "PostgresCluster" here, too. Not sure why. I don't like the singular "environment" in my proposed message, so double not-sure.

🔧 I think we should use has() to check for the presence of a field. Is pgbackrest required within the backups section? If so, we can trust/defer to its own validation rules.

Suggested change
// +kubebuilder:validation:XValidation:rule="(self.environment == 'production') ? self.backups.pgbackrest != null : true", message="Backups must be enabled for production PostgresCluster's."
// +kubebuilder:validation:XValidation:rule="(self.environment == 'production') ? has(self.backups) : true", message="Backups must be enabled for production environment."

🌱 With controller-gen >= 0.16.0, we can use fieldPath to indicate more clearly that spec.backups is what's missing.

📝 If you have any doubts about the logic of the rule, you can add some tests like the ones in internal/testing/validation/postgrescluster_test.go.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with the following message:

Backups must be enabled in a production environment.

I also switch the null check to has(). However, I did go one layer deeper with has(self.backups.pgbackrest) to protect against an empty backups section (backups: {}). I don't love the error messages displayed when this does fail, since the validation rule itself essentially fails due to the missing field. But for now this does appear to protect against everything we need it to, and it does seem like there is a fine line with keeping these errors pretty, and keeping the rules from getting too complex.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did attempt a bump to controller-gen 0.16.3, but saw something a bit strange with our RBAC (I'll capture in a story if no thoughts in the Slack thread I started). This did give me fieldPath and reason, though I found it was really only helpful if the user actually included the section (and the reason, while returned from the API, is displayed by kubectl). But this could again be tied to further expanding the rule a bit (e.g. per my last comment) to get some better validation error messages/behavior.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went ahead and made the bump to 0.16.3. fieldRef and reason have now been added to the CEL validation marker as a result.

Additionally, I made one final tweak to the validation rule:

(self.environment == 'production') ? (has(self.backups) && has(self.backups.pgbackrest)) : true

Now I get a clean/consistent error whether backups is empty or missing:

Missing backups:

$ kubectl apply -n postgres-operator -f - <<EOF
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
  name: hippo
spec:
  environment: production                                                            
  image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.3-1
  postgresVersion: 16
  instances:
    - name: instance1
      dataVolumeClaimSpec:
        accessModes:
        - "ReadWriteOnce"
        resources:
          requests:
            storage: 1Gi
EOF          
The PostgresCluster "hippo" is invalid: spec.backups: Required value: Backups must be enabled in a production environment.

Empty backups:

$ kubectl apply -n postgres-operator -f - <<EOF
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
  name: hippo
spec:
  environment: production                                                            
  image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.3-1
  postgresVersion: 16
  instances:
    - name: instance1
      dataVolumeClaimSpec:
        accessModes:
        - "ReadWriteOnce"
        resources:
          requests:
            storage: 1Gi
  backups: {}
EOF
The PostgresCluster "hippo" is invalid: spec.backups: Required value: Backups must be enabled in a production environment.

Updates the description for 'PostgresCluster.spec.environment'.  Also updates
updates the CEL validation error message for 'environment', while also switching
to has() vs. a null check for the associated validation rule.
@andrewlecuyer andrewlecuyer requested review from cbandy and removed request for tony-landreth September 26, 2024 18:26
Updates to the latest controller-gen release.  CRD's and RBAC have been
regenerated, and "namespace" has been removed from the markers in the
Patroni and pgBackRest rbac.go files (it was no longer providing much benefit
since the go code already cleanly organizes the RBAC, and changes to controller
controller-gen had the potential to break RBAC generation as a result of its use).
Updates the validation rule for environment to make the error message consistent
across missing and empty 'backup' sections.  Additionally, adds "fieldPath" and
"reason" to the CEL validation marker.
'pgbackrest' is now required when 'spec.backups' is provided.  Additionally, the CEL
validation rule to make backups required in production environments no longer checks
for 'pgbackrest'.  Although there is no functional difference with this change, it better
aligns backup API behavior/validation with the backups portion of the spec.  Additionally,
'fieldPath' and 'reason' have been removed from CEL validation since they are
only supported in Kubernetes v1.28+.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants