Skip to content

Commit

Permalink
Add support for linked resource providers with no template (#98)
Browse files Browse the repository at this point in the history
- This feature allows a ResourceProvider to reference another with
  different parameters or to combine resources without using a top-level
  binding resource.
  • Loading branch information
jkupferer authored Nov 13, 2023
1 parent a977d90 commit 0776cfc
Show file tree
Hide file tree
Showing 6 changed files with 572 additions and 16 deletions.
12 changes: 11 additions & 1 deletion operator/resourceclaim.py
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
33 changes: 20 additions & 13 deletions operator/resourceprovider.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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:
"""
Expand Down
2 changes: 1 addition & 1 deletion test/roles/poolboy_test_simple/tasks/cleanup.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@
register: r_get_namespace
until: r_get_namespace.resources | default([]) | length == 0
delay: 5
retries: 10
retries: 30
303 changes: 303 additions & 0 deletions test/roles/poolboy_test_simple/tasks/test-linked-01.yaml
Original file line number Diff line number Diff line change
@@ -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
...
Loading

0 comments on commit 0776cfc

Please sign in to comment.