Skip to content

Validate cross-referenced parameters between PostgresCluster and PostgresClusterClass#1858

Closed
limak9182 wants to merge 11 commits intofeature/database-controllersfrom
feature/cross-referenced-parameters-validation
Closed

Validate cross-referenced parameters between PostgresCluster and PostgresClusterClass#1858
limak9182 wants to merge 11 commits intofeature/database-controllersfrom
feature/cross-referenced-parameters-validation

Conversation

@limak9182
Copy link
Copy Markdown

@limak9182 limak9182 commented Apr 20, 2026

Description

  • Adds admission-time cross-resource validation for PostgresCluster against its referenced PostgresClusterClass
  • Uses mgr.GetAPIReader() (live API read, not cached) to avoid false rejections during cache propagation

Key Changes

Rule Field Condition
Class existence spec.class Referenced PostgresClusterClass must exist
Storage floor spec.storage Cannot be lower than class default
Version floor spec.postgresVersion Major version cannot be lower than class default. If minor version specified in class cluster's minor cannot be lower.
Pooler enablement spec.connectionPoolerEnabled Cannot be true when class has it disabled
Merge completeness spec.instances, spec.postgresVersion, spec.storage After overlay, all three must be resolvable from cluster or class

Testing and Verification

  • unit test cases with fake client.Reader covering all rules and edge cases
  • Existing pg_hba validation tests updated to pass nil reader (no behavioral change)
  • go build ./... passes
  • go test ./pkg/splunk/enterprise/validation/... passes

Related Issues

Jira tickets, GitHub issues, Support tickets...

PR Checklist

  • Code changes adhere to the project's coding standards.
  • Relevant unit and integration tests are included.
  • Documentation has been updated accordingly.
  • All tests pass locally.
  • The PR description follows the project's guidelines.

@github-actions
Copy link
Copy Markdown
Contributor

CLA Assistant Lite bot:
Thank you for your submission, we really appreciate it. Like many open-source projects, we ask that you sign our Contribution License Agreement before we can accept your contribution. You can sign the CLA by just posting a Pull Request Comment with the exact sentence copied from below.


I have read the CLA Document and I hereby sign the CLA


You can retrigger this bot by commenting recheck in this Pull Request

@limak9182 limak9182 force-pushed the feature/pg_hba-validation branch from dbafebc to fb47985 Compare April 22, 2026 07:22
@limak9182 limak9182 force-pushed the feature/cross-referenced-parameters-validation branch from 21145a6 to 74dca92 Compare April 22, 2026 10:53
wantMessage: "PostgresClusterClass not found",
},
{
name: "rejected - storage below class floor",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

storage below class floor - I assume we can override this value on a cluster lever even with lower ones, am I wrong?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

yes, correct

wantMessage: "storage cannot be lower than class default",
},
{
name: "rejected - version below class floor",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

version below class floor - same as above - can't we set lower versions on cluster level now?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

For the compliance we prohibit setting lower version than in class

},
},
wantAllowed: false,
wantMessage: "connectionPoolerEnabled cannot be enabled",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

"connectionPoolerEnabled cannot be enabled" - sounds strange (enabled cannot be enabled 😸 )

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

fixed

wantMessage: "connectionPoolerEnabled cannot be enabled",
},
{
name: "allowed - storage equal to class",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

"allowed - storage equal to class" - and if higher?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

removed

// ValidatePostgresClusterUpdate validates a PostgresCluster on UPDATE.
func ValidatePostgresClusterUpdate(obj, oldObj *enterpriseApi.PostgresCluster) field.ErrorList {
return ValidatePostgresClusterCreate(obj)
func ValidatePostgresClusterUpdate(obj, oldObj *enterpriseApi.PostgresCluster, reader client.Reader) field.ErrorList {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

oldObj is ignored on purpose?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

yes, we don't care about oldObj

return allErrs
}

if obj.Spec.Storage != nil && classConfig.Storage != nil {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Same point for storage and postgresVersion: the merge code treats class values as defaults, but this webhook now treats them as minimum allowed values. If that policy shift is intentional, I’d update the API docs/comments because “overrides” reads differently today.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think there is no contradiction here i.e our default logic is there and it is used in the runtime reconciler but business rules validation is done here in the validation webhook. So we have "overrides" with guardrails in place. I agree we need to document this logic properly though

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

added comment for clarity

Comment on lines +58 to +65
class := &enterpriseApi.PostgresClusterClass{}
if err := reader.Get(context.Background(), client.ObjectKey{Name: obj.Spec.Class}, class); err != nil {
allErrs = append(allErrs, field.Invalid(
field.NewPath("spec").Child("class"),
obj.Spec.Class,
"referenced PostgresClusterClass not found"))
return allErrs
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

reader.Get() collapses every error into “referenced PostgresClusterClass not found”. That will hide real issues like API/RBAC failures and return a misleading validation error. I’d special-case IsNotFound and surface other errors separately.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

removed API reader

Comment on lines 90 to 92
if k8sClient != nil || reader != nil {
vc = NewValidationContext(k8sClient, reader, req.Namespace)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cross-resource validation now depends entirely on vc.Reader. If a caller provides only a cached client, or calls the validator directly, the checks silently disappear. I’d prefer a Reader -> Client fallback so the behavior is less wiring-sensitive.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

removed API reader

},
}

for _, tt := range tests {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The new tests cover the happy path well, but they mostly assert error count/field or Allowed. I’d add message assertions for the unit tests and one update-path integration case for the cross-resource checks, so semantic regressions in the comparison logic are easier to catch.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

done

return b
}

func TestCrossResourceValidationIntegration(t *testing.T) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The new tests cover the happy path well, but they mostly assert error count/field or Allowed. I’d add message assertions for the unit tests and one update-path integration case for the cross-resource checks, so semantic regressions in the comparison logic are easier to catch.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

done

Comment thread cmd/main.go Outdated
ReadTimeout: readTimeout,
WriteTimeout: writeTimeout,
Client: mgr.GetClient(),
Reader: mgr.GetAPIReader(),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

could you elab more, why we need this reader? What problem do we solve?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

removed

}
}

if reader != nil {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

could you elab why we expect some reader to validate against class logic?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

removed

// Reader is a live API reader that bypasses the cache.
// Use this for cross-resource lookups where eventual consistency
// could cause false rejections.
Reader client.Reader
Copy link
Copy Markdown
Collaborator

@mploski mploski Apr 23, 2026

Choose a reason for hiding this comment

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

Is this really a problem in our case? if we for example deploy new cluster CR and we need to check latest deployed clusterClass isnt it already populated within the cache? Why we need to bypass it? Why this isnt a problem for other CR's checked by this?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

removed

Base automatically changed from feature/pg_hba-validation to feature/database-controllers April 23, 2026 13:50
@limak9182 limak9182 force-pushed the feature/cross-referenced-parameters-validation branch from 62dfaba to 19b975f Compare April 27, 2026 07:19
@limak9182 limak9182 force-pushed the feature/cross-referenced-parameters-validation branch from 19b975f to 96b28d3 Compare April 27, 2026 07:40
@limak9182 limak9182 closed this Apr 27, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 27, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants