Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions api/cloud/v1alpha1/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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.

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.

Suggested change
// Required and is enforced by the temporal-cloud-namespace-validation admission policy.
// Required unless the cloud.temporal.io/adoption-policy annotation is
// "adopt"; enforced by the temporal-cloud-namespace-validation admission
// policy, and by the operator before it creates or updates a Namespace.

// +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.

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.

Suggested change
// Required and is enforced by the temporal-cloud-namespace-validation admission policy.
// Required unless the cloud.temporal.io/adoption-policy annotation is
// "adopt"; enforced by the temporal-cloud-namespace-validation admission
// policy, and by the operator before it creates or updates a Namespace.

// +optional
RetentionDays int32 `json:"retentionDays,omitempty"`
// MTLSAuth configures mTLS client authentication.
// Maps to Temporal Cloud proto `NamespaceSpec.mtls_auth`.
// +optional
Expand Down
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

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.

The operator lowercases and trims the annotation before validating so Adopt or adopt work here but these comparisons are exact the policy would reject them.
There's probably a way to by case sensitivity here.

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.

@nmechack27 we could lowercase the variable adoptionPolicy by changing its expression to:

    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

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.

Keeping resource-id here makes sense for resources that need an ID to adopt. For namespaces, though, adoption path looks the Namespace up by name in ResourceSetLatest, so it shouldn't require resource-id. Since this policy currently matches namespaces, the "resource-id is required when adopting" rule would reject a namespace adopt CR without one.

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

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.

My pr only supports adopt for now so adopt-or-create would be treated as an invalid value. We can add it when it lands.

# 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]
3 changes: 3 additions & 0 deletions deploy/kustomize/admission/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
resources:
- cloud_resource_validation_policy.yaml
- namespace_validation_policy.yaml

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.

Is this admission/ folder included in the top-level kustomization that make install uses?

34 changes: 34 additions & 0 deletions deploy/kustomize/admission/namespace_validation_policy.yaml
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'])

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 this would let retentionDays: 0 through. We can do object.spec.retentionDays > 0 here.

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]
12 changes: 6 additions & 6 deletions deploy/kustomize/crd/bases/cloud.temporal.io_namespaces.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ spec:
metadata:
type: object
spec:
description: Spec defines the desired state of the Namespace.
description: |-
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".
properties:
apiKeyAuth:
description: |-
Expand Down Expand Up @@ -294,6 +297,7 @@ spec:
description: |-
Replicas are the regions where the Namespace is placed.
Maps to Temporal Cloud proto `NamespaceSpec.replicas`.
Required and is enforced by the temporal-cloud-namespace-validation admission policy.
items:
description: ReplicaSpec places a replica of the Namespace in a
region.
Expand All @@ -316,6 +320,7 @@ spec:
RetentionDays is the number of days that closed workflow histories are
kept.
Maps to Temporal Cloud proto `NamespaceSpec.retention_days`.
Required and is enforced by the temporal-cloud-namespace-validation admission policy.
format: int32
type: integer
searchAttributes:
Expand Down Expand Up @@ -343,9 +348,6 @@ spec:
Tags are key-value pairs to attach to the Namespace.
Maps to Temporal Cloud proto `Namespace.tags`.
type: object
required:
- replicas
- retentionDays
type: object
x-kubernetes-validations:
- fieldPath: .project
Expand Down Expand Up @@ -470,8 +472,6 @@ spec:
- Expired
type: string
type: object
required:
- spec
type: object
served: true
storage: true
Expand Down
1 change: 1 addition & 0 deletions deploy/kustomize/default/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
14 changes: 14 additions & 0 deletions examples/adoption/namespace-adopt-or-create.yaml
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
12 changes: 12 additions & 0 deletions examples/adoption/namespace-adopt-with-spec.yaml
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
7 changes: 7 additions & 0 deletions examples/adoption/namespace-adopt-without-resource-id.yaml
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
8 changes: 8 additions & 0 deletions examples/adoption/namespace-adopt-without-spec.yaml
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
13 changes: 13 additions & 0 deletions examples/adoption/namespace-invalid-adoption-policy.yaml
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
Loading