Repository navigation
feat(admission): validate adoption annotations with admission policies #64
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -26,9 +26,11 @@ type Namespace struct { | |||||||||
| // +optional | ||||||||||
| metav1.ObjectMeta `json:"metadata,omitempty"` | ||||||||||
|
|
||||||||||
| // Spec defines the desired state of the Namespace. | ||||||||||
| // +required | ||||||||||
| Spec NamespaceSpec `json:"spec"` | ||||||||||
| // Spec defines the desired state of the Namespace. It may be omitted only | ||||||||||
| // when adopting an existing Temporal Cloud Namespace, i.e. when the | ||||||||||
| // cloud.temporal.io/adoption-policy annotation is "adopt". | ||||||||||
| // +optional | ||||||||||
| Spec NamespaceSpec `json:"spec,omitzero"` | ||||||||||
| // +optional | ||||||||||
| // Status describes the current status of a Namespace. | ||||||||||
| Status NamespaceStatus `json:"status,omitempty"` | ||||||||||
|
|
@@ -87,16 +89,18 @@ type NamespaceSpec struct { | |||||||||
| Tags map[string]string `json:"tags,omitempty"` | ||||||||||
| // Replicas are the regions where the Namespace is placed. | ||||||||||
| // Maps to Temporal Cloud proto `NamespaceSpec.replicas`. | ||||||||||
| // +required | ||||||||||
| // Required and is enforced by the temporal-cloud-namespace-validation admission policy. | ||||||||||
| // +optional | ||||||||||
| // +listType=map | ||||||||||
| // +listMapKey=region | ||||||||||
| // +kubebuilder:validation:MinItems=1 | ||||||||||
| Replicas []ReplicaSpec `json:"replicas"` | ||||||||||
| Replicas []ReplicaSpec `json:"replicas,omitempty"` | ||||||||||
| // RetentionDays is the number of days that closed workflow histories are | ||||||||||
| // kept. | ||||||||||
| // Maps to Temporal Cloud proto `NamespaceSpec.retention_days`. | ||||||||||
| // +required | ||||||||||
| RetentionDays int32 `json:"retentionDays"` | ||||||||||
| // Required and is enforced by the temporal-cloud-namespace-validation admission policy. | ||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
| // +optional | ||||||||||
| RetentionDays int32 `json:"retentionDays,omitempty"` | ||||||||||
| // MTLSAuth configures mTLS client authentication. | ||||||||||
| // Maps to Temporal Cloud proto `NamespaceSpec.mtls_auth`. | ||||||||||
| // +optional | ||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| apiVersion: admissionregistration.k8s.io/v1 | ||
| kind: ValidatingAdmissionPolicy | ||
| metadata: | ||
| name: temporal-cloud-resource-validation | ||
| spec: | ||
| failurePolicy: Fail | ||
| matchConstraints: | ||
| resourceRules: | ||
| - apiGroups: ["cloud.temporal.io"] | ||
| apiVersions: ["*"] | ||
| operations: ["CREATE", "UPDATE"] | ||
| resources: ["namespaces"] | ||
| variables: | ||
| - name: adoptionPolicy | ||
| expression: "object.?metadata.annotations['cloud.temporal.io/adoption-policy']" | ||
| - name: adopt | ||
| expression: "variables.adoptionPolicy.orValue('') == 'adopt'" | ||
|
Comment on lines
+14
to
+17
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The operator lowercases and trims the annotation before validating so
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @nmechack27 we could lowercase the variable expression: "object.?metadata.annotations['cloud.temporal.io/adoption-policy'].lowerAscii()" |
||
| - name: resourceID | ||
| expression: "object.?metadata.annotations['cloud.temporal.io/resource-id'].orValue('')" | ||
|
Comment on lines
+18
to
+19
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keeping I think this should either move the resource-id rules into the per-resource policies and leave them out of namespace_validation_policy.yaml, or skip them for namespaces here (e.g. `request.resource.resource == 'namespaces' || ...)? |
||
| validations: | ||
| # No adoption annotation or if there is, it has to be either of the two values | ||
| - expression: >- | ||
| !variables.adoptionPolicy.hasValue() || | ||
| variables.adoptionPolicy.value() in ['adopt', 'adopt-or-create'] | ||
| messageExpression: >- | ||
| 'metadata.annotations[cloud.temporal.io/adoption-policy]: Unsupported value: "' + | ||
| variables.adoptionPolicy.value() + '": supported values: "adopt", "adopt-or-create"' | ||
| reason: Invalid | ||
|
Comment on lines
+22
to
+28
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My pr only supports |
||
| # If adoption is requested, a resource ID must be set. | ||
| - expression: >- | ||
| !(variables.adoptionPolicy.orValue('') in ['adopt', 'adopt-or-create']) || | ||
| variables.resourceID != '' | ||
| message: >- | ||
| metadata.annotations[cloud.temporal.io/resource-id]: Required value when the | ||
| cloud.temporal.io/adoption-policy annotation is "adopt" or "adopt-or-create" | ||
| reason: Invalid | ||
| # A resource ID is only used for adoption. | ||
| - expression: "variables.adoptionPolicy.hasValue() || variables.resourceID == ''" | ||
| message: >- | ||
| metadata.annotations[cloud.temporal.io/resource-id]: Forbidden: requires the | ||
| cloud.temporal.io/adoption-policy annotation | ||
| reason: Invalid | ||
| - expression: "variables.adopt || has(object.spec)" | ||
| message: >- | ||
| spec: Required value unless the cloud.temporal.io/adoption-policy | ||
| annotation is "adopt" | ||
| reason: Invalid | ||
| --- | ||
| apiVersion: admissionregistration.k8s.io/v1 | ||
| kind: ValidatingAdmissionPolicyBinding | ||
| metadata: | ||
| name: temporal-cloud-resource-validation | ||
| spec: | ||
| policyName: temporal-cloud-resource-validation | ||
| validationActions: [Deny] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| resources: | ||
| - cloud_resource_validation_policy.yaml | ||
| - namespace_validation_policy.yaml | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| apiVersion: admissionregistration.k8s.io/v1 | ||
| kind: ValidatingAdmissionPolicy | ||
| metadata: | ||
| name: temporal-cloud-namespace-validation | ||
| spec: | ||
| failurePolicy: Fail | ||
| matchConstraints: | ||
| resourceRules: | ||
| - apiGroups: ["cloud.temporal.io"] | ||
| apiVersions: ["*"] | ||
| operations: ["CREATE", "UPDATE"] | ||
| resources: ["namespaces"] | ||
| variables: | ||
| - name: adopt | ||
| expression: "object.?metadata.annotations['cloud.temporal.io/adoption-policy'].orValue('') == 'adopt'" | ||
| - name: missingSpecFields | ||
| expression: >- | ||
| !has(object.spec) ? [] : | ||
| (has(object.spec.replicas) ? [] : ['spec.replicas']) + | ||
| (has(object.spec.retentionDays) ? [] : ['spec.retentionDays']) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this would let |
||
| validations: | ||
| - expression: "size(variables.missingSpecFields) == 0" | ||
| messageExpression: >- | ||
| variables.missingSpecFields.join(', ') + ': Required value' + | ||
| (variables.adopt ? '; to adopt the Namespace as is, omit spec' : '') | ||
| reason: Invalid | ||
| --- | ||
| apiVersion: admissionregistration.k8s.io/v1 | ||
| kind: ValidatingAdmissionPolicyBinding | ||
| metadata: | ||
| name: temporal-cloud-namespace-validation | ||
| spec: | ||
| policyName: temporal-cloud-namespace-validation | ||
| validationActions: [Deny] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,5 +11,6 @@ labels: | |
| resources: | ||
| - namespace.yaml | ||
| - ../crd | ||
| - ../admission | ||
| - ../rbac | ||
| - ../cloud-operator | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| # Fails: adopt-or-create may create the Namespace, so spec is required. | ||
| apiVersion: cloud.temporal.io/v1alpha1 | ||
| kind: Namespace | ||
| metadata: | ||
| name: adopt-or-create-without-spec | ||
| annotations: | ||
| cloud.temporal.io/adoption-policy: adopt-or-create | ||
| cloud.temporal.io/resource-id: adopt-or-create-without-spec.abc12 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| # Passes: adopt-or-create with a resource ID and a full spec to create from. | ||
| apiVersion: cloud.temporal.io/v1alpha1 | ||
| kind: Namespace | ||
| metadata: | ||
| name: adopt-or-create | ||
| annotations: | ||
| cloud.temporal.io/adoption-policy: adopt-or-create | ||
| cloud.temporal.io/resource-id: adopt-or-create.abc12 | ||
| spec: | ||
| replicas: | ||
| - region: aws-us-west-2 | ||
| retentionDays: 30 | ||
| apiKeyAuth: | ||
| enabled: true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # Fails: once spec is set, replicas and retentionDays are required, even when adopting. | ||
| apiVersion: cloud.temporal.io/v1alpha1 | ||
| kind: Namespace | ||
| metadata: | ||
| name: adopt-with-spec | ||
| annotations: | ||
| cloud.temporal.io/adoption-policy: adopt | ||
| cloud.temporal.io/resource-id: adopt-with-spec.abc12 | ||
| spec: | ||
| description: Payments namespace | ||
| tags: | ||
| team: payments |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| # Fails: adopting requires the cloud.temporal.io/resource-id annotation. | ||
| apiVersion: cloud.temporal.io/v1alpha1 | ||
| kind: Namespace | ||
| metadata: | ||
| name: adopt-without-resource-id | ||
| annotations: | ||
| cloud.temporal.io/adoption-policy: adopt |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| # Passes: spec may be omitted when adopting with a resource ID. | ||
| apiVersion: cloud.temporal.io/v1alpha1 | ||
| kind: Namespace | ||
| metadata: | ||
| name: adopt-without-spec | ||
| annotations: | ||
| cloud.temporal.io/adoption-policy: adopt | ||
| cloud.temporal.io/resource-id: adopt-without-spec.abc12 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| # Fails: the adoption policy must be "adopt" or "adopt-or-create". | ||
| apiVersion: cloud.temporal.io/v1alpha1 | ||
| kind: Namespace | ||
| metadata: | ||
| name: invalid-adoption-policy | ||
| annotations: | ||
| cloud.temporal.io/adoption-policy: create-new | ||
| spec: | ||
| replicas: | ||
| - region: aws-us-west-2 | ||
| retentionDays: 30 | ||
| apiKeyAuth: | ||
| enabled: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.