Skip to content

Conversation

@pikilisaikiran
Copy link

Add operations field to mutation ApplyTo specification

Summary

This PR introduces an operations field to the ApplyTo specification in Gatekeeper mutations, enabling granular control over which Kubernetes admission operations (CREATE, UPDATE, DELETE) trigger mutations.

Problem Solved

Fixes #4121: CronJob Pods getting stuck due to mutations attempting to modify immutable fields during UPDATE operations.

Before: Mutations always applied on both CREATE and UPDATE operations, causing failures when trying to mutate immutable Pod fields like environment variables.

After: Users can specify operations: ["CREATE"] to only mutate during resource creation, avoiding immutable field issues.

Key Changes

Core Implementation

  • ✅ Add Operations []string field to ApplyTo struct
  • ✅ Update mutation matching logic in MatchWithApplyTo function
  • ✅ Add Operation field to Mutable type for context propagation
  • ✅ Maintain 100% backward compatibility (defaults to CREATE+UPDATE)

Testing & Validation

  • ✅ Comprehensive unit tests for operation matching logic
  • ✅ Live cluster testing with existing CRDs (no breaking changes)
  • ✅ Multiple test scenarios covering all operation combinations

Documentation & Examples

  • ✅ Updated main mutation documentation with operations field explanation
  • ✅ Added practical examples showing real-world usage patterns
  • ✅ Created comprehensive README for mutation examples
  • ✅ Enhanced Go code comments for better developer experience

Usage Examples

Solving Immutable Field Issues (Main Use Case)

apiVersion: mutations.gatekeeper.sh/v1
kind: Assign
metadata:
  name: fix-cronjob-env-vars
spec:
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
    versions: ["v1"]
    operations: ["CREATE"]  # Only apply on creation
  location: "spec.containers[name:*].env[name:MY_VAR].value"
  parameters:
    assign:
      value: "production"

Other Supported Patterns

  • operations: ["CREATE"] - Creation-only mutations
  • operations: ["UPDATE"] - Update-only mutations
  • operations: ["CREATE", "UPDATE"] - Explicit default behavior
  • operations: ["DELETE"] - Advanced cleanup scenarios
  • No operations field - Backward compatible (CREATE+UPDATE)

Backward Compatibility

Zero Breaking Changes

  • Existing mutations without operations field work unchanged
  • Default behavior preserved as CREATE+UPDATE operations
  • No required updates to existing deployments
  • Gradual adoption - users can migrate at their own pace

Testing Performed

  1. Unit Tests: All new operation matching logic
  2. Integration Tests: Live cluster deployment with existing CRDs
  3. Backward Compatibility: Verified existing mutations unchanged
  4. Schema Validation: Confirmed optional field works correctly
  5. Multiple Mutation Types: Tested Assign, ModifySet, etc.

Related Issue

Closes #4121

Checklist

  • Added unit tests for new functionality
  • Updated documentation
  • Verified backward compatibility
  • Added practical examples
  • Tested on live cluster
  • No breaking changes introduced

@pikilisaikiran pikilisaikiran requested a review from a team as a code owner September 1, 2025 10:22
Add granular operation control to Gatekeeper mutations by introducing
an 'operations' field in the ApplyTo specification. This allows users
to specify which Kubernetes admission operations (CREATE, UPDATE, DELETE)
should trigger mutations.

Key Changes:
- Add Operations []string field to ApplyTo struct
- Update mutation matching logic to check operation compatibility
- Add Operation field to Mutable type for context propagation
- Maintain backward compatibility (defaults to CREATE+UPDATE)
- Add comprehensive unit tests for operation matching
- Update documentation with usage examples and best practices
- Include practical examples demonstrating various operation patterns

Fixes: Resolves issues with mutations attempting to modify immutable
fields during UPDATE operations, such as Pod environment variables
in CronJob scenarios.

Backward Compatibility: Existing mutations without operations field
continue working unchanged with default CREATE+UPDATE behavior.

Testing: Added unit tests, live cluster validation, and comprehensive
examples covering all supported operation combinations.

Signed-off-by: sai kiran pikili <saikiranpikili@localhost.localdomain>
@pikilisaikiran pikilisaikiran force-pushed the feat/mutation-operations-field branch from 6dc29a7 to 3babba5 Compare September 1, 2025 10:30
@alex-berger
Copy link
Contributor

@JaydipGabani Any chance we can continue with this PR? It's solving a real pain point in our production environments and I would love to see this land in one of the next releases.

@JaydipGabani
Copy link
Contributor

I am planning to discuss this in today's community meeting. I will take a look today to see if anything is missing. Thanks for the ping and thr PR.

@JaydipGabani
Copy link
Contributor

@pikilisaikiran ptal - #4121 (comment)

Copy link
Member

@chewong chewong left a comment

Choose a reason for hiding this comment

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

Overall looks good!

# No operations field - defaults to CREATE and UPDATE for backward compatibility
match:
scope: Namespaced
location: "metadata.labels.legacy-mutator"
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this is a good example because Assign is not supposed to mutate metadata field per https://open-policy-agent.github.io/gatekeeper/website/docs/mutation#mutation-crds

Versions []string `json:"versions,omitempty"`
// Operations specifies which admission operations (CREATE, UPDATE, DELETE) should trigger
// this mutation. If empty, defaults to ["CREATE", "UPDATE"] for backward compatibility.
Operations []string `json:"operations,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

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

Let's create type ApplyToOperation string instead of using raw string. We could also add a kubebuilder marker for API validation purposes.

Suggested change
Operations []string `json:"operations,omitempty"`
// +kubebuilder:validation:Enum=CREATE;UPDATE;DELETE
Operations []ApplyToOperation `json:"operations,omitempty"`

Copy link
Member

Choose a reason for hiding this comment

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

Could you also run make manifests so the updated CRDs can get copied to the staging helm chart?

func (a ApplyTo) MatchesOperation(operation string) bool {
// If no operations specified, default to CREATE and UPDATE for backward compatibility
if len(a.Operations) == 0 {
return operation == "CREATE" || operation == "UPDATE"
Copy link
Member

Choose a reason for hiding this comment

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

nit: let's rename the constants from admissionv1 package, or create new constants in the match package

Suggested change
return operation == "CREATE" || operation == "UPDATE"
return operation == admissionv1.Create || operation == admissionv1.Update

@JaydipGabani JaydipGabani added this to the v3.22.0 milestone Oct 29, 2025
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.

Assign mutations apply on Pod UPDATE (illegal), need operations control

4 participants