Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .odoignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Containerfile
helm
test
10 changes: 10 additions & 0 deletions .s2i/bin/assemble
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash

set -x
set -eo pipefail
shopt -s dotglob

# Install customized kopf version with label selectors and deprecated finalizer support.
pip install git+https://github.com/rhpds/kopf.git@add-deprecated-finalizer-support

/usr/libexec/s2i/assemble
4 changes: 2 additions & 2 deletions Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ RUN rm -rf /tmp/src/.git* && \
chown -R 1001 /tmp/src && \
chgrp -R 0 /tmp/src && \
chmod -R g+w /tmp/src && \
pip install git+https://github.com/rhpds/kopf.git@add-deprecated-finalizer-support
cp -rp /tmp/src/.s2i/bin /tmp/scripts

USER 1001

RUN /usr/libexec/s2i/assemble
RUN /tmp/scripts/assemble

CMD ["/usr/libexec/s2i/run"]
3 changes: 2 additions & 1 deletion devfile.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
commands:
- exec:
commandLine: /usr/libexec/s2i/assemble
commandLine: >-
rm -rf /tmp/src && cp /tmp/projects -r /tmp/src && /tmp/src/.s2i/bin/assemble
component: s2i-builder
group:
isDefault: true
Expand Down
5 changes: 5 additions & 0 deletions helm/crds/resourceclaims.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ spec:
Condition to check which triggers detach of ResourceHandle from the ResourceClaim.
Condition is given in Jinja2 syntax similar to ansible "when" clauses.
type: string
disableCreation:
description: >-
If set to true, then ResourceHandle creation is disabled for this ResourceClaim
and it will only match to existing ResourceHandles such as are created by ResourcePools.
type: boolean
lifespan:
description: >-
Lifespan configuration for the ResourceClaim.
Expand Down
5 changes: 5 additions & 0 deletions helm/templates/crds/resourceclaims.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ spec:
Condition to check which triggers detach of ResourceHandle from the ResourceClaim.
Condition is given in Jinja2 syntax similar to ansible "when" clauses.
type: string
disableCreation:
description: >-
If set to true, then ResourceHandle creation is disabled for this ResourceClaim
and it will only match to existing ResourceHandles such as are created by ResourcePools.
type: boolean
lifespan:
description: >-
Lifespan configuration for the ResourceClaim.
Expand Down
28 changes: 19 additions & 9 deletions operator/resourceclaim.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ def auto_detach_when(self) -> str|None:
def claim_is_initialized(self) -> bool:
return f"{Poolboy.operator_domain}/resource-claim-init-timestamp" in self.annotations

@property
def create_disabled(self) -> bool:
return self.spec.get('disableCreation', False)

@property
def has_resource_handle(self) -> bool:
"""Return whether this ResourceClaim is bound to a ResourceHandle."""
Expand Down Expand Up @@ -377,21 +381,26 @@ async def assign_resource_handler(self):
async def bind_resource_handle(self,
logger: kopf.ObjectLogger,
resource_claim_resources: List[Mapping],
):
) -> ResourceHandleT|None:
resource_handle = await resourcehandle.ResourceHandle.bind_handle_to_claim(
logger = logger,
resource_claim = self,
resource_claim_resources = resource_claim_resources,
)

if not resource_handle:
create_disabled = self.create_disabled
for provider in await self.get_resource_providers(resource_claim_resources):
if provider.create_disabled:
raise kopf.TemporaryError(
f"Found no matching ResourceHandles for {self} and "
f"ResourceHandle creation is disabled for {provider}",
delay=600
)
create_disabled = True
if create_disabled:
logger.info(
"Found no matching ResourceHandles for %s and "
"ResourceHandle creation is disabled for %s",
self, provider
)
return None

resource_handle = await resourcehandle.ResourceHandle.create_for_claim(
logger = logger,
resource_claim = self,
Expand Down Expand Up @@ -865,12 +874,13 @@ async def manage(self, logger) -> None:
else:
resource_claim_resources = self.resources

if not resource_handle:
if resource_handle is None:
resource_handle = await self.bind_resource_handle(
logger = logger,
resource_claim_resources = resource_claim_resources,
)
if resource_handle.ignore:

if resource_handle is None or resource_handle.ignore:
return

if self.check_auto_delete(logger=logger, resource_handle=resource_handle, resource_provider=resource_provider):
Expand Down Expand Up @@ -988,7 +998,7 @@ async def refetch(self) -> ResourceClaimT|None:
return self
except kubernetes_asyncio.client.exceptions.ApiException as e:
if e.status == 404:
self.unregister(name=self.name, namespace=self.namespace)
await self.unregister(name=self.name, namespace=self.namespace)
return None
raise

Expand Down
21 changes: 14 additions & 7 deletions operator/resourcehandle.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def __lt__(self, cmp):
return False

# Prefer healthy resources to unknown health state
# Unhealthy resource handles would not be considered for a potential match.
if self.resource_handle.is_healthy and cmp.resource_handle.is_healthy is None:
return True
if self.resource_handle.is_healthy is None and cmp.resource_handle.is_healthy:
Expand All @@ -63,12 +64,6 @@ def __lt__(self, cmp):
if not self.resource_handle.is_ready and cmp.resource_handle.is_ready:
return False

# Prefer unknown readiness state to known unready state
if self.resource_handle.is_ready is None and cmp.resource_handle.is_ready is False:
return True
if not self.resource_handle.is_ready is False and cmp.resource_handle.is_ready is None:
return False

# Prefer older matches
return self.resource_handle.creation_timestamp < cmp.resource_handle.creation_timestamp

Expand Down Expand Up @@ -175,7 +170,7 @@ async def bind_handle_to_claim(
# Match with (possibly empty) difference list
match.template_difference_count += len(diff_patch)

if match:
if match is not None:
matches.append(match)

# Bind the oldest ResourceHandle with the smallest difference score
Expand All @@ -185,6 +180,14 @@ async def bind_handle_to_claim(
matched_resource_handle = match.resource_handle
patch = [
{
"op": "add",
"path": f"/metadata/labels/{Poolboy.resource_claim_name_label.replace('/', '~1')}",
"value": resource_claim.name,
}, {
"op": "add",
"path": f"/metadata/labels/{Poolboy.resource_claim_namespace_label.replace('/', '~1')}",
"value": resource_claim.namespace,
}, {
"op": "add",
"path": "/spec/resourceClaim",
"value": {
Expand Down Expand Up @@ -904,6 +907,9 @@ async def __manage_init_status_resources(self,
entry['name'] = resource['name']
set_resources.append(entry)

if self.status_resources == set_resources:
return

patch = []
if not self.status:
patch.extend(({
Expand Down Expand Up @@ -943,6 +949,7 @@ async def __manage_init_status_resources(self,
if attempt > 2:
logger.exception(f"{self} failed status patch: {patch}")
raise
await self.refresh()
attempt += 1

async def __manage_check_delete(self,
Expand Down
130 changes: 130 additions & 0 deletions test/roles/poolboy_test_simple/tasks/test-disable-creation-01.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
---
# Test ResourceProvider with disableCreation set
- name: Create ResourceProvider test-disable-creation-01
kubernetes.core.k8s:
definition:
apiVersion: "{{ poolboy_domain }}/v1"
kind: ResourceProvider
metadata:
name: test-disable-creation-01
namespace: "{{ poolboy_namespace }}"
labels: >-
{{ {
poolboy_domain ~ "/test": "simple"
} }}
spec:
disableCreation: true
override:
apiVersion: "{{ poolboy_domain }}/v1"
kind: ResourceClaimTest
metadata:
name: "test-pool-02-{% raw %}{{ guid }}{% endraw %}"
namespace: "{{ poolboy_test_namespace }}"
template:
enable: true
validation:
openAPIV3Schema:
additionalProperties: false
properties:
spec:
additionalProperties: false
properties:
value:
type: string
required:
- value
type: object
required:
- spec
type: object

- name: Create ResourceClaim for test-disable-creation-01
kubernetes.core.k8s:
state: present
definition:
apiVersion: "{{ poolboy_domain }}/v1"
kind: ResourceClaim
metadata:
name: test-disable-creation-01
namespace: "{{ poolboy_test_namespace }}"
labels: >-
{{ {
poolboy_domain ~ "/test": "simple"
} }}
spec:
resources:
- provider:
apiVersion: "{{ poolboy_domain }}/v1"
kind: ResourceProvider
name: test-disable-creation-01
namespace: "{{ poolboy_namespace }}"
template:
spec:
value: "foo"

- name: Pause to avoid testing before poolboy could respond.
pause:
seconds: 1

- name: Verify ResourceClaim test-disable-creation-01 unbound
kubernetes.core.k8s_info:
api_version: "{{ poolboy_domain }}/v1"
kind: ResourceClaim
name: test-disable-creation-01
namespace: "{{ poolboy_test_namespace }}"
register: r_get_resource_claim
vars:
__claim: "{{ r_get_resource_claim.resources[0] | default({}) }}"
failed_when: >-
"provider" not in __claim.status.resources[0] | default({}) or
"resourceHandle" in __claim.status | default({})
until: r_get_resource_claim is success
delay: 2
retries: 5

- name: Create ResourceHandle for test-disable-creation-01
kubernetes.core.k8s:
definition:
apiVersion: "{{ poolboy_domain }}/v1"
kind: ResourceHandle
metadata:
name: guid-tdc01
namespace: "{{ poolboy_namespace }}"
labels: >-
{{ {
poolboy_domain ~ "/test": "simple"
} }}
spec:
resources:
- provider:
apiVersion: "{{ poolboy_domain }}/v1"
kind: ResourceProvider
name: test-disable-creation-01
namespace: "{{ poolboy_namespace }}"
template:
spec:
value: "foo"

- name: Verify ResourceClaim test-disable-creation-01 binds
kubernetes.core.k8s_info:
api_version: "{{ poolboy_domain }}/v1"
kind: ResourceClaim
name: test-disable-creation-01
namespace: "{{ poolboy_test_namespace }}"
register: r_get_resource_claim
vars:
__claim: "{{ r_get_resource_claim.resources[0] | default({}) }}"
failed_when: >-
"resourceHandle" not in __claim.status
until: r_get_resource_claim is success
delay: 1
retries: 30

- name: Delete ResourceClaim test-disable-creation-01
kubernetes.core.k8s:
state: absent
api_version: "{{ poolboy_domain }}/v1"
kind: ResourceClaim
name: test-disable-creation-01
namespace: "{{ poolboy_test_namespace }}"
...
Loading