From 16a966e99b1d72bea194fe4eaac8fdb063cdacd4 Mon Sep 17 00:00:00 2001 From: Johnathan Kupferer Date: Tue, 15 Oct 2024 21:56:37 -0400 Subject: [PATCH] Add conditional components --- Development.adoc | 6 + helm/crds/resourceproviders.yaml | 4 + helm/templates/crds/resourceproviders.yaml | 4 + operator/resourcehandle.py | 6 +- operator/resourceprovider.py | 37 ++ .../tasks/test-linked-03.yaml | 485 ++++++++++++++++++ .../roles/poolboy_test_simple/tasks/test.yaml | 1 + 7 files changed, 540 insertions(+), 3 deletions(-) create mode 100644 test/roles/poolboy_test_simple/tasks/test-linked-03.yaml diff --git a/Development.adoc b/Development.adoc index 9f7555e..d133b95 100644 --- a/Development.adoc +++ b/Development.adoc @@ -30,6 +30,12 @@ helm template helm \ oc create namespace poolboy-dev ------------------------------ +. Change project to `poolboy-dev` namespace: ++ +---------------------- +oc project poolboy-dev +---------------------- + . Grant privileges for cluster role `poolboy-dev` to default service account: + ------------------------------------------------------------- diff --git a/helm/crds/resourceproviders.yaml b/helm/crds/resourceproviders.yaml index af90bee..9db39bb 100644 --- a/helm/crds/resourceproviders.yaml +++ b/helm/crds/resourceproviders.yaml @@ -165,6 +165,10 @@ spec: Condition is given in Jinja2 syntax similar to ansible "when" clauses. The linked provider's resource state may be referenced with "resource_state". type: string + when: + description: >- + Condition which is used to determine if this provider should be used. + type: string match: description: >- Partial resource definition used to check if a resource in a handle or claim diff --git a/helm/templates/crds/resourceproviders.yaml b/helm/templates/crds/resourceproviders.yaml index cf45bc8..0558b4b 100644 --- a/helm/templates/crds/resourceproviders.yaml +++ b/helm/templates/crds/resourceproviders.yaml @@ -166,6 +166,10 @@ spec: Condition is given in Jinja2 syntax similar to ansible "when" clauses. The linked provider's resource state may be referenced with "resource_state". type: string + when: + description: >- + Condition which is used to determine if this provider should be used. + type: string match: description: >- Partial resource definition used to check if a resource in a handle or claim diff --git a/operator/resourcehandle.py b/operator/resourcehandle.py index 308f56e..8920658 100644 --- a/operator/resourcehandle.py +++ b/operator/resourcehandle.py @@ -974,12 +974,12 @@ async def manage(self, logger: kopf.ObjectLogger) -> None: linked_resource_state = resource_states[pn] break else: - raise kopf.TemporaryError( + logger.debug( f"{self} uses {resource_provider} which has " f"linked ResourceProvider {resource_provider.name} but no resource in this " - f"ResourceHandle use this provider.", - delay=600 + f"ResourceHandle uses this provider." ) + continue if not linked_provider.check_wait_for( linked_resource_provider = linked_resource_provider, diff --git a/operator/resourceprovider.py b/operator/resourceprovider.py index e7935a4..34da3dc 100644 --- a/operator/resourceprovider.py +++ b/operator/resourceprovider.py @@ -29,6 +29,7 @@ def __init__(self, spec): self.parameter_values = spec.get('parameterValues', {}) self.resource_name = spec.get('resourceName', self.name) self.wait_for = spec.get('waitFor') + self.when = spec.get('when') self.template_vars = [ _TemplateVar(item) for item in spec.get('templateVars', []) ] @@ -72,6 +73,35 @@ def check_wait_for(self, '{{(' + self.wait_for + ')|bool}}', resource_provider.template_style, vars_ ) + def check_when(self, + parameter_values: Optional[Mapping] = None, + resource_claim: Optional[ResourceClaimT] = None, + resource_handle: Optional[ResourceHandleT] = None, + resource_provider: Optional[ResourceProviderT] = None, + ) -> bool: + if not self.when: + return True + + if parameter_values == None: + parameter_values = {**self.parameter_defaults} + if resource_claim: + parameter_values.update(resource_claim.parameter_values) + elif resource_handle: + parameter_values.update(resource_handle.parameter_values) + + resource_handle_vars = resource_handle.vars if resource_handle else {} + + return recursive_process_template_strings( + '{{(' + self.when + ')|bool}}', resource_provider.template_style, { + **resource_provider.vars, + **resource_handle_vars, + **parameter_values, + "resource_claim": resource_claim, + "resource_handle": resource_handle, + "resource_provider": self, + } + ) + class _Parameter: def __init__(self, definition): @@ -512,6 +542,13 @@ async def get_resources(self, resources = [] for linked_resource_provider in self.linked_resource_providers: resource_provider = await self.get(linked_resource_provider.name) + if not linked_resource_provider.check_when( + parameter_values=parameter_values, + resource_claim=resource_claim, + resource_handle=resource_handle, + resource_provider=self, + ): + continue resources.extend( await resource_provider.get_resources( resource_claim = resource_claim, diff --git a/test/roles/poolboy_test_simple/tasks/test-linked-03.yaml b/test/roles/poolboy_test_simple/tasks/test-linked-03.yaml new file mode 100644 index 0000000..2a4ccb2 --- /dev/null +++ b/test/roles/poolboy_test_simple/tasks/test-linked-03.yaml @@ -0,0 +1,485 @@ +--- +# Test linked resource provider which creates no resources of its own +- name: Create ResourceProvider test-linked-03-base-a + kubernetes.core.k8s: + definition: + apiVersion: "{{ poolboy_domain }}/v1" + kind: ResourceProvider + metadata: + name: test-linked-03-base-a + namespace: "{{ poolboy_namespace }}" + labels: >- + {{ { + poolboy_domain ~ "/test": "simple" + } }} + spec: + override: + apiVersion: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + metadata: + name: test-linked-03-{% raw %}{{ guid }}-{{ resource_name }}{% endraw %} + namespace: "{{ poolboy_test_namespace }}" + parameters: + - name: stringvar + allowUpdate: true + required: true + validation: + openAPIV3Schema: + type: string + default: one + enum: + - one + - two + - three + - name: numbervar + allowUpdate: false + validation: + openAPIV3Schema: + type: integer + default: 0 + minimum: 0 + template: + definition: + spec: + numbervalue: "{% raw %}{{ numbervar | int }}{% endraw %}" + stringvalue: "{% raw %}{{ stringvar }}-a{% endraw %}" + enable: true + updateFilters: + - pathMatch: /spec/.* + allowedOps: + - replace + +- name: Create ResourceProvider test-linked-03-base-b + kubernetes.core.k8s: + definition: + apiVersion: "{{ poolboy_domain }}/v1" + kind: ResourceProvider + metadata: + name: test-linked-03-base-b + namespace: "{{ poolboy_namespace }}" + labels: >- + {{ { + poolboy_domain ~ "/test": "simple" + } }} + spec: + override: + apiVersion: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + metadata: + name: test-linked-03-{% raw %}{{ guid }}-{{ resource_name }}{% endraw %} + namespace: "{{ poolboy_test_namespace }}" + parameters: + - name: stringvar + allowUpdate: true + required: true + validation: + openAPIV3Schema: + type: string + default: one + enum: + - one + - two + - three + - name: numbervar + allowUpdate: true + validation: + openAPIV3Schema: + type: integer + default: 0 + minimum: 0 + template: + definition: + spec: + numbervalue: "{% raw %}{{ (numbervar | int * 10) | int }}{% endraw %}" + stringvalue: "{% raw %}{{ stringvar }}-b{% endraw %}" + enable: true + updateFilters: + - pathMatch: /spec/.* + allowedOps: + - replace + +- name: Create ResourceProvider test-linked-03-conditional + kubernetes.core.k8s: + definition: + apiVersion: "{{ poolboy_domain }}/v1" + kind: ResourceProvider + metadata: + name: test-linked-03-conditional + namespace: "{{ poolboy_namespace }}" + labels: >- + {{ { + poolboy_domain ~ "/test": "simple" + } }} + spec: + linkedResourceProviders: + - name: test-linked-03-base-a + parameterValues: + numbervar: "{% raw %}{{ numbervar | int }}{% endraw %}" + stringvar: "{% raw %}{{ stringvar | upper }}{% endraw %}" + resourceName: base + templateVars: + - from: /spec/numbervalue + name: base_numbervalue + - from: /spec/stringvalue + name: base_stringvalue + waitFor: base_numbervalue | default(0) | int > 0 + when: numbervar | default(0) | int == 1 + - name: test-linked-03-base-b + parameterValues: + numbervar: "{% raw %}{{ numbervar | int }}{% endraw %}" + stringvar: "{% raw %}{{ stringvar | upper }}{% endraw %}" + resourceName: base + templateVars: + - from: /spec/numbervalue + name: base_numbervalue + - from: /spec/stringvalue + name: base_stringvalue + waitFor: base_numbervalue | default(0) | int > 0 + when: numbervar | default(0) | int > 1 + override: + apiVersion: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + metadata: + name: test-linked-01-{% raw %}{{ guid }}{% endraw %}-binder + namespace: "{{ poolboy_test_namespace }}" + spec: + numbervalue: "{% raw %}{{ (base_numbervalue | default(0) | int * 10) | int }}{% endraw %}" + stringvalue: "{% raw %}{{ base_stringvalue | default('no base') | upper }}{% endraw %}" + parameters: + - name: stringvar + allowUpdate: true + required: true + validation: + openAPIV3Schema: + type: string + default: one + enum: + - one + - two + - three + - name: numbervar + allowUpdate: true + validation: + openAPIV3Schema: + type: integer + default: 0 + minimum: 0 + resourceName: conditional_binder + template: + enable: true + updateFilters: + - pathMatch: /spec/.* + allowedOps: + - replace + +- name: Create ResourceClaim test-linked-03-a + kubernetes.core.k8s: + definition: + apiVersion: "{{ poolboy_domain }}/v1" + kind: ResourceClaim + metadata: + name: test-linked-03-a + namespace: "{{ poolboy_test_namespace }}" + labels: >- + {{ { + poolboy_domain ~ "/test": "simple" + } }} + spec: + provider: + name: test-linked-03-conditional + parameterValues: + stringvar: one + numbervar: 0 + +- name: Verify handling of ResourceClaim test-linked-03-a + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaim + name: test-linked-03-a + namespace: "{{ poolboy_test_namespace }}" + register: r_get_resource_claim + vars: + __resource_claim: "{{ r_get_resource_claim.resources[0] }}" + failed_when: >- + __resource_claim.status.resources | length != 1 or + __resource_claim.status.resources[0].name != 'conditional_binder' or + __resource_claim.status.resources[0].provider.name != 'test-linked-03-conditional' or + __resource_claim.status.resources[0].state is undefined or + __resource_claim.status.resources[0].state.spec.numbervalue != 0 or + __resource_claim.status.resources[0].state.spec.stringvalue != 'NO BASE' + until: r_get_resource_claim is success + delay: 1 + retries: 10 + +- name: Create ResourceClaim test-linked-03-b + kubernetes.core.k8s: + definition: + apiVersion: "{{ poolboy_domain }}/v1" + kind: ResourceClaim + metadata: + name: test-linked-03-b + namespace: "{{ poolboy_test_namespace }}" + labels: >- + {{ { + poolboy_domain ~ "/test": "simple" + } }} + spec: + provider: + name: test-linked-03-conditional + parameterValues: + stringvar: one + numbervar: 1 + +- name: Verify handling of ResourceClaim test-linked-03-b + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaim + name: test-linked-03-b + namespace: "{{ poolboy_test_namespace }}" + register: r_get_resource_claim + vars: + __resource_claim: "{{ r_get_resource_claim.resources[0] }}" + failed_when: >- + __resource_claim.status.resources | length != 2 or + __resource_claim.status.resources[0].name != 'base' or + __resource_claim.status.resources[0].provider.name != 'test-linked-03-base-a' or + __resource_claim.status.resources[0].state is undefined or + __resource_claim.status.resources[0].state.spec.numbervalue != 1 or + __resource_claim.status.resources[0].state.spec.stringvalue != 'ONE-a' or + __resource_claim.status.resources[1].name != 'conditional_binder' or + __resource_claim.status.resources[1].provider.name != 'test-linked-03-conditional' or + __resource_claim.status.resources[1].state is undefined or + __resource_claim.status.resources[1].state.spec.numbervalue != 10 or + __resource_claim.status.resources[1].state.spec.stringvalue != 'ONE-A' + until: r_get_resource_claim is success + delay: 1 + retries: 10 + +- name: Save facts from for ResourceClaim test-linked-03-b + vars: + __resource_claim: >- + {{ r_get_resource_claim.resources[0] }} + set_fact: + resource_claim_test_linked_03_b_resource_handle_name: >- + {{ __resource_claim.status.resourceHandle.name }} + resource_claim_test_linked_03_b_base_resource_name: >- + {{ __resource_claim.status.resources[0].state.metadata.name }} + resource_claim_test_linked_03_b_binder_resource_name: >- + {{ __resource_claim.status.resources[1].state.metadata.name }} + +- name: Verify creation of base ResourceClaimTest for test-linked-03-b + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + name: "{{ resource_claim_test_linked_03_b_base_resource_name }}" + namespace: "{{ poolboy_test_namespace }}" + register: r_get_resource_claim_test + failed_when: r_get_resource_claim_test.resources | length != 1 + until: r_get_resource_claim_test is success + delay: 1 + retries: 10 + +- name: Verify creation of binder ResourceClaimTest for test-linked-03-b + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + name: "{{ resource_claim_test_linked_03_b_binder_resource_name }}" + namespace: "{{ poolboy_test_namespace }}" + register: r_get_resource_claim_test + failed_when: r_get_resource_claim_test.resources | length != 1 + until: r_get_resource_claim_test is success + delay: 1 + retries: 10 + +- name: Delete ResourceClaim test-linked-03-b + kubernetes.core.k8s: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaim + name: test-linked-03-b + namespace: "{{ poolboy_test_namespace }}" + state: absent + +- name: Verify delete of ResourceClaim test-linked-03-b + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaim + name: test-linked-03-b + namespace: "{{ poolboy_test_namespace }}" + register: r_get_resource_claim + failed_when: r_get_resource_claim.resources | length != 0 + until: r_get_resource_claim is success + retries: 5 + delay: 1 + +- name: Verify delete of ResourceHandle for test-linked-03-b + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceHandle + name: "{{ resource_claim_test_linked_03_b_resource_handle_name }}" + namespace: "{{ poolboy_namespace }}" + register: r_get_resource_handle + failed_when: r_get_resource_handle.resources | length != 0 + until: r_get_resource_handle is success + retries: 5 + delay: 1 + +- name: Verify delete of base ResourceClaimTest test-linked-03-b + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + name: "{{ resource_claim_test_linked_03_b_base_resource_name }}" + namespace: "{{ poolboy_test_namespace }}" + register: r_get_resource_claim_test + failed_when: r_get_resource_claim_test.resources | length != 0 + until: r_get_resource_claim_test is success + delay: 1 + retries: 10 + +- name: Verify delete of binder ResourceClaimTest test-linked-03-b + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + name: "{{ resource_claim_test_linked_03_b_binder_resource_name }}" + namespace: "{{ poolboy_test_namespace }}" + register: r_get_resource_claim_test + failed_when: r_get_resource_claim_test.resources | length != 0 + until: r_get_resource_claim_test is success + delay: 1 + retries: 10 + +- name: Create ResourceClaim test-linked-03-c + kubernetes.core.k8s: + definition: + apiVersion: "{{ poolboy_domain }}/v1" + kind: ResourceClaim + metadata: + name: test-linked-03-c + namespace: "{{ poolboy_test_namespace }}" + labels: >- + {{ { + poolboy_domain ~ "/test": "simple" + } }} + spec: + provider: + name: test-linked-03-conditional + parameterValues: + stringvar: two + numbervar: 2 + +- name: Verify handling of ResourceClaim test-linked-03-c + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaim + name: test-linked-03-c + namespace: "{{ poolboy_test_namespace }}" + register: r_get_resource_claim + vars: + __resource_claim: "{{ r_get_resource_claim.resources[0] }}" + failed_when: >- + __resource_claim.status.resources | length != 2 or + __resource_claim.status.resources[0].name != 'base' or + __resource_claim.status.resources[0].provider.name != 'test-linked-03-base-b' or + __resource_claim.status.resources[0].state is undefined or + __resource_claim.status.resources[0].state.spec.numbervalue != 20 or + __resource_claim.status.resources[0].state.spec.stringvalue != 'TWO-b' or + __resource_claim.status.resources[1].name != 'conditional_binder' or + __resource_claim.status.resources[1].provider.name != 'test-linked-03-conditional' or + __resource_claim.status.resources[1].state is undefined or + __resource_claim.status.resources[1].state.spec.numbervalue != 200 or + __resource_claim.status.resources[1].state.spec.stringvalue != 'TWO-B' + until: r_get_resource_claim is success + delay: 1 + retries: 10 + +- name: Save facts from for ResourceClaim test-linked-03-c + vars: + __resource_claim: >- + {{ r_get_resource_claim.resources[0] }} + set_fact: + resource_claim_test_linked_03_c_resource_handle_name: >- + {{ __resource_claim.status.resourceHandle.name }} + resource_claim_test_linked_03_c_base_resource_name: >- + {{ __resource_claim.status.resources[0].state.metadata.name }} + resource_claim_test_linked_03_c_binder_resource_name: >- + {{ __resource_claim.status.resources[1].state.metadata.name }} + +- name: Verify creation of base ResourceClaimTest for test-linked-03-c + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + name: "{{ resource_claim_test_linked_03_c_base_resource_name }}" + namespace: "{{ poolboy_test_namespace }}" + register: r_get_resource_claim_test + failed_when: r_get_resource_claim_test.resources | length != 1 + until: r_get_resource_claim_test is success + delay: 1 + retries: 10 + +- name: Verify creation of binder ResourceClaimTest for test-linked-03-c + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + name: "{{ resource_claim_test_linked_03_c_binder_resource_name }}" + namespace: "{{ poolboy_test_namespace }}" + register: r_get_resource_claim_test + failed_when: r_get_resource_claim_test.resources | length != 1 + until: r_get_resource_claim_test is success + delay: 1 + retries: 10 + +- name: Delete ResourceClaim test-linked-03-c + kubernetes.core.k8s: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaim + name: test-linked-03-c + namespace: "{{ poolboy_test_namespace }}" + state: absent + +- name: Verify delete of ResourceClaim test-linked-03-c + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaim + name: test-linked-03-c + namespace: "{{ poolboy_test_namespace }}" + register: r_get_resource_claim + failed_when: r_get_resource_claim.resources | length != 0 + until: r_get_resource_claim is success + retries: 5 + delay: 1 + +- name: Verify delete of ResourceHandle for test-linked-03-c + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceHandle + name: "{{ resource_claim_test_linked_03_c_resource_handle_name }}" + namespace: "{{ poolboy_namespace }}" + register: r_get_resource_handle + failed_when: r_get_resource_handle.resources | length != 0 + until: r_get_resource_handle is success + retries: 5 + delay: 1 + +- name: Verify delete of base ResourceClaimTest test-linked-03-c + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + name: "{{ resource_claim_test_linked_03_c_base_resource_name }}" + namespace: "{{ poolboy_test_namespace }}" + register: r_get_resource_claim_test + failed_when: r_get_resource_claim_test.resources | length != 0 + until: r_get_resource_claim_test is success + delay: 1 + retries: 10 + +- name: Verify delete of binder ResourceClaimTest test-linked-03-c + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + name: "{{ resource_claim_test_linked_03_c_binder_resource_name }}" + namespace: "{{ poolboy_test_namespace }}" + register: r_get_resource_claim_test + failed_when: r_get_resource_claim_test.resources | length != 0 + until: r_get_resource_claim_test is success + delay: 1 + retries: 10 +... diff --git a/test/roles/poolboy_test_simple/tasks/test.yaml b/test/roles/poolboy_test_simple/tasks/test.yaml index 4a509a7..5fdd468 100644 --- a/test/roles/poolboy_test_simple/tasks/test.yaml +++ b/test/roles/poolboy_test_simple/tasks/test.yaml @@ -9,6 +9,7 @@ - test-ignore-01.yaml - test-linked-01.yaml - test-linked-02.yaml + - test-linked-03.yaml - test-pool-01.yaml - test-pool-02.yaml - test-pool-03.yaml