diff --git a/operator/resourceclaim.py b/operator/resourceclaim.py index 39f1a40..ec0ddf3 100644 --- a/operator/resourceclaim.py +++ b/operator/resourceclaim.py @@ -614,7 +614,17 @@ async def manage(self, logger) -> None: delay = 600 ) - provider = await self.get_resource_provider() + try: + provider = await self.get_resource_provider() + except kubernetes_asyncio.client.exceptions.ApiException as e: + if e.status == 404: + raise kopf.TemporaryError( + f"ResourceProvider ({self.resource_provider_name_from_spec}) not found", + delay = 600 + ) + else: + raise + if provider.approval_required: if not 'approval' in self.status: await self.merge_patch_status({ diff --git a/operator/resourceprovider.py b/operator/resourceprovider.py index c705bbb..10f98bb 100644 --- a/operator/resourceprovider.py +++ b/operator/resourceprovider.py @@ -217,6 +217,10 @@ def approval_required(self) -> bool: def create_disabled(self) -> bool: return self.spec.get('disableCreation', False) + @property + def has_template_definition(self) -> bool: + return 'template' in self.spec and 'definition' in self.spec['template'] + @property def lifespan_maximum(self) -> Optional[str]: return self.spec.get('lifespan', {}).get('maximum') @@ -401,6 +405,8 @@ async def get_claim_resources(self, resource_name: Optional[str] = None, ) -> List[Mapping]: """Return list of resources for managed ResourceClaim""" + resources = [] + if parameter_values == None: parameter_values = { **self.parameter_defaults, @@ -417,20 +423,10 @@ async def get_claim_resources(self, "resource_provider": self, } - resource = { - "name": resource_name or self.resource_name, - "provider": self.as_reference(), - "template": self.processed_template( - parameter_values = parameter_values, - resource_claim = resource_claim, - resource_handle = resource_handle, - ) - } - - linked_resources = [] + resources = [] for linked_resource_provider in self.linked_resource_providers: resource_provider = await ResourceProvider.get(linked_resource_provider.name) - linked_resources.extend( + resources.extend( await resource_provider.get_claim_resources( resource_claim = resource_claim, resource_handle = resource_handle, @@ -442,7 +438,18 @@ async def get_claim_resources(self, ) ) - return [*linked_resources, resource] + if self.has_template_definition: + resources.append({ + "name": resource_name or self.resource_name, + "provider": self.as_reference(), + "template": self.processed_template( + parameter_values = parameter_values, + resource_claim = resource_claim, + resource_handle = resource_handle, + ) + }) + + return resources def is_match_for_template(self, template: Mapping) -> bool: """ diff --git a/test/roles/poolboy_test_simple/tasks/cleanup.yaml b/test/roles/poolboy_test_simple/tasks/cleanup.yaml index 4c5efc8..b75a06a 100644 --- a/test/roles/poolboy_test_simple/tasks/cleanup.yaml +++ b/test/roles/poolboy_test_simple/tasks/cleanup.yaml @@ -32,4 +32,4 @@ register: r_get_namespace until: r_get_namespace.resources | default([]) | length == 0 delay: 5 - retries: 10 + retries: 30 diff --git a/test/roles/poolboy_test_simple/tasks/test-linked-01.yaml b/test/roles/poolboy_test_simple/tasks/test-linked-01.yaml new file mode 100644 index 0000000..3999b70 --- /dev/null +++ b/test/roles/poolboy_test_simple/tasks/test-linked-01.yaml @@ -0,0 +1,303 @@ +--- +# Test basic linked resource provider +- name: Create ResourceProvider test-linked-01-base + kubernetes.core.k8s: + definition: + apiVersion: "{{ poolboy_domain }}/v1" + kind: ResourceProvider + metadata: + name: test-linked-01-base + namespace: "{{ poolboy_namespace }}" + labels: >- + {{ { + poolboy_domain ~ "/test": "simple" + } }} + spec: + override: + apiVersion: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + metadata: + name: test-linked-01-{% 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 }}{% endraw %}" + stringvalue: "{% raw %}{{ stringvar }}{% endraw %}" + enable: true + updateFilters: + - pathMatch: /spec/.* + allowedOps: + - replace + +- name: Create ResourceProvider test-linked-01-binder + kubernetes.core.k8s: + definition: + apiVersion: "{{ poolboy_domain }}/v1" + kind: ResourceProvider + metadata: + name: test-linked-01-binder + namespace: "{{ poolboy_namespace }}" + labels: >- + {{ { + poolboy_domain ~ "/test": "simple" + } }} + spec: + linkedResourceProviders: + - name: test-linked-01-base + parameterValues: + numbervar: "{% raw %}{{ (numbervar * 10) | int }}{% endraw %}" + stringvar: "{% raw %}{{ stringvar | upper }}{% endraw %}" + resourceName: base + override: + apiVersion: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + metadata: + name: test-linked-01-{% raw %}{{ guid }}{% endraw %}-binder + 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 + resourceName: binder + template: + definition: + spec: + numbervalue: "{% raw %}{{ numbervar | int }}{% endraw %}" + stringvalue: "{% raw %}{{ stringvar }}{% endraw %}" + enable: true + updateFilters: + - pathMatch: /spec/.* + allowedOps: + - replace + +- name: Create ResourceClaim test-linked-01-a + kubernetes.core.k8s: + definition: + apiVersion: "{{ poolboy_domain }}/v1" + kind: ResourceClaim + metadata: + name: test-linked-01-a + namespace: "{{ poolboy_test_namespace }}" + labels: >- + {{ { + poolboy_domain ~ "/test": "simple" + } }} + spec: + provider: + name: test-linked-01-binder + parameterValues: + stringvar: one + numbervar: 1 + +- name: Verify handling of ResourceClaim test-linked-01-a + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaim + name: test-linked-01-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 != 2 or + __resource_claim.status.resources[0].name != 'base' or + __resource_claim.status.resources[0].provider.name != 'test-linked-01-base' or + __resource_claim.status.resources[0].state is undefined or + __resource_claim.status.resources[1].name != 'binder' or + __resource_claim.status.resources[1].provider.name != 'test-linked-01-binder' or + __resource_claim.status.resources[1].state is undefined + until: r_get_resource_claim is success + delay: 1 + retries: 10 + +- name: Save facts from for ResourceClaim test-linked-01-a + vars: + __name: >- + {{ r_get_resource_claim.resources[0].status.resourceHandle.name }} + set_fact: + resource_claim_test_linked_01_a_resource_handle_name: "{{ __name }}" + resource_claim_test_linked_01_a_base_resource_name: test-linked-01-{{ __name[5:] }}-base + resource_claim_test_linked_01_a_binder_resource_name: test-linked-01-{{ __name[5:] }}-binder + +- name: Verify state of ResourceClaim test-linked-01-a binder + vars: + __state: "{{ r_get_resource_claim.resources[0] }}" + assert: + that: + - __state.status.resources[0].state.metadata.name == resource_claim_test_linked_01_a_base_resource_name + - __state.status.resources[1].state.metadata.name == resource_claim_test_linked_01_a_binder_resource_name + +- name: Verify creation of ResourceClaimTest test-linked-01-a-base + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + name: "{{ resource_claim_test_linked_01_a_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 state of ResourceClaimTest for test-linked-01-a-base + vars: + __state: "{{ r_get_resource_claim_test.resources[0] }}" + assert: + that: + - __state.spec.numbervalue | int == 10 + - __state.spec.stringvalue == 'ONE' + +- name: Verify creation of ResourceClaimTest test-linked-01-a-binder + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + name: "{{ resource_claim_test_linked_01_a_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: Verify state of ResourceClaimTest for test-linked-01-a-binder + vars: + __state: "{{ r_get_resource_claim_test.resources[0] }}" + assert: + that: + - __state.spec.numbervalue | int == 1 + - __state.spec.stringvalue == 'one' + +- name: Update parameters of ResourceClaim test-linked-01-a + kubernetes.core.k8s: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaim + name: test-linked-01-a + namespace: "{{ poolboy_test_namespace }}" + definition: + spec: + provider: + parameterValues: + stringvar: two + numbervar: 2 + +- name: Verify update of ResourceClaimTest test-linked-01-a-base + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + name: "{{ resource_claim_test_linked_01_a_base_resource_name }}" + namespace: "{{ poolboy_test_namespace }}" + register: r_get_resource_claim_test + failed_when: >- + r_get_resource_claim_test.resources | length != 1 or + r_get_resource_claim_test.resources[0].spec.stringvalue != 'TWO' or + r_get_resource_claim_test.resources[0].spec.numbervalue != 20 + until: r_get_resource_claim_test is success + delay: 1 + retries: 10 + +- name: Verify update of ResourceClaimTest test-linked-01-a-binder + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + name: "{{ resource_claim_test_linked_01_a_binder_resource_name }}" + namespace: "{{ poolboy_test_namespace }}" + register: r_get_resource_claim_test + failed_when: >- + r_get_resource_claim_test.resources | length != 1 or + r_get_resource_claim_test.resources[0].spec.stringvalue != 'two' or + r_get_resource_claim_test.resources[0].spec.numbervalue != 2 + until: r_get_resource_claim_test is success + delay: 1 + retries: 10 + +- name: Delete ResourceClaim test-linked-01-a + kubernetes.core.k8s: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaim + name: test-linked-01-a + namespace: "{{ poolboy_test_namespace }}" + state: absent + +- name: Verify delete of ResourceClaim test-linked-01-a + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaim + name: test-linked-01-a + 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-01-a + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceHandle + name: "{{ resource_claim_test_linked_01_a_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 ResourceClaimTest test-linked-01-a-base + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + name: "{{ resource_claim_test_linked_01_a_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 ResourceClaimTest test-linked-01-a-binder + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + name: "{{ resource_claim_test_linked_01_a_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-linked-02.yaml b/test/roles/poolboy_test_simple/tasks/test-linked-02.yaml new file mode 100644 index 0000000..8fbedf8 --- /dev/null +++ b/test/roles/poolboy_test_simple/tasks/test-linked-02.yaml @@ -0,0 +1,234 @@ +--- +# Test linked resource provider which creates no resources of its own +- name: Create ResourceProvider test-linked-02-base + kubernetes.core.k8s: + definition: + apiVersion: "{{ poolboy_domain }}/v1" + kind: ResourceProvider + metadata: + name: test-linked-02-base + namespace: "{{ poolboy_namespace }}" + labels: >- + {{ { + poolboy_domain ~ "/test": "simple" + } }} + spec: + override: + apiVersion: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + metadata: + name: test-linked-02-{% 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 }}{% endraw %}" + stringvalue: "{% raw %}{{ stringvar }}{% endraw %}" + enable: true + updateFilters: + - pathMatch: /spec/.* + allowedOps: + - replace + +- name: Create ResourceProvider test-linked-02-no-template + kubernetes.core.k8s: + definition: + apiVersion: "{{ poolboy_domain }}/v1" + kind: ResourceProvider + metadata: + name: test-linked-02-no-template + namespace: "{{ poolboy_namespace }}" + labels: >- + {{ { + poolboy_domain ~ "/test": "simple" + } }} + spec: + linkedResourceProviders: + - name: test-linked-02-base + parameterValues: + numbervar: "{% raw %}{{ (numbervar * 10) | int }}{% endraw %}" + stringvar: "{% raw %}{{ stringvar | upper }}{% endraw %}" + resourceName: base + 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 + +- name: Create ResourceClaim test-linked-02-a + kubernetes.core.k8s: + definition: + apiVersion: "{{ poolboy_domain }}/v1" + kind: ResourceClaim + metadata: + name: test-linked-02-a + namespace: "{{ poolboy_test_namespace }}" + labels: >- + {{ { + poolboy_domain ~ "/test": "simple" + } }} + spec: + provider: + name: test-linked-02-no-template + parameterValues: + stringvar: one + numbervar: 1 + +- name: Verify handling of ResourceClaim test-linked-02-a + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaim + name: test-linked-02-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 != 'base' or + __resource_claim.status.resources[0].provider.name != 'test-linked-02-base' or + __resource_claim.status.resources[0].state is undefined + until: r_get_resource_claim is success + delay: 1 + retries: 10 + +- name: Save facts from for ResourceClaim test-linked-02-a + vars: + __name: >- + {{ r_get_resource_claim.resources[0].status.resourceHandle.name }} + set_fact: + resource_claim_test_linked_02_a_resource_handle_name: "{{ __name }}" + resource_claim_test_linked_02_a_base_resource_name: test-linked-02-{{ __name[5:] }}-base + +- name: Verify state of ResourceClaim test-linked-02-a + vars: + __state: "{{ r_get_resource_claim.resources[0] }}" + assert: + that: + - __state.status.resources[0].state.metadata.name == resource_claim_test_linked_02_a_base_resource_name + +- name: Verify creation of ResourceClaimTest test-linked-02-a-base + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + name: "{{ resource_claim_test_linked_02_a_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 state of ResourceClaimTest for test-linked-02-a-base + vars: + __state: "{{ r_get_resource_claim_test.resources[0] }}" + assert: + that: + - __state.spec.numbervalue | int == 10 + - __state.spec.stringvalue == 'ONE' + +- name: Update parameters of ResourceClaim test-linked-02-a + kubernetes.core.k8s: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaim + name: test-linked-02-a + namespace: "{{ poolboy_test_namespace }}" + definition: + spec: + provider: + parameterValues: + stringvar: two + numbervar: 2 + +- name: Verify update of ResourceClaimTest test-linked-02-a-base + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + name: "{{ resource_claim_test_linked_02_a_base_resource_name }}" + namespace: "{{ poolboy_test_namespace }}" + register: r_get_resource_claim_test + failed_when: >- + r_get_resource_claim_test.resources | length != 1 or + r_get_resource_claim_test.resources[0].spec.stringvalue != 'TWO' or + r_get_resource_claim_test.resources[0].spec.numbervalue != 20 + until: r_get_resource_claim_test is success + delay: 1 + retries: 10 + +- name: Delete ResourceClaim test-linked-02-a + kubernetes.core.k8s: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaim + name: test-linked-02-a + namespace: "{{ poolboy_test_namespace }}" + state: absent + +- name: Verify delete of ResourceClaim test-linked-02-a + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaim + name: test-linked-02-a + 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-02-a + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceHandle + name: "{{ resource_claim_test_linked_02_a_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 ResourceClaimTest test-linked-02-a-base + kubernetes.core.k8s_info: + api_version: "{{ poolboy_domain }}/v1" + kind: ResourceClaimTest + name: "{{ resource_claim_test_linked_02_a_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 +... diff --git a/test/roles/poolboy_test_simple/tasks/test.yaml b/test/roles/poolboy_test_simple/tasks/test.yaml index e77d7fb..98faf1b 100644 --- a/test/roles/poolboy_test_simple/tasks/test.yaml +++ b/test/roles/poolboy_test_simple/tasks/test.yaml @@ -6,6 +6,8 @@ - test-01.yaml - test-02.yaml - test-approval-01.yaml + - test-linked-01.yaml + - test-linked-02.yaml - test-pool-01.yaml - test-pool-02.yaml - test-vars-01.yaml @@ -1050,7 +1052,7 @@ failed_when: >- "resourceHandle" not in __claim.status until: r_get_disable_creation is success - delay: 2 + delay: 3 retries: 30 - name: Delete ResourceClaim test-disable-creation