From 1ad3af3efdcd19ba0ec6c2af5f7a334fb5a74115 Mon Sep 17 00:00:00 2001 From: "Per G. da Silva" Date: Mon, 19 Oct 2026 16:43:23 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=8C=B1=20add=20ClusterObjectSet=20object?= =?UTF-8?q?=20collision=20and=20adoption=20e2e=20scenarios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a cos_object_collision_and_adoption.feature suite exercising how the revision's collisionProtection setting governs ownership of pre-existing objects: - Prevent reports Ready=False/ObjectCollision for a pre-existing unowned object and for an object owned by another revision; - IfNoController adopts an unowned object but still reports ObjectCollision for an object owned by another revision; - None adopts in both cases, taking over ownership; and - a Prevent collision recovers to Ready=True/AllObjectsReady once the conflicting object is removed. Objects owned by "another revision" are set up with a second ClusterObjectSet placed in a different spec.group so the two revisions are not treated as siblings. Co-Authored-By: Claude Opus 4.8 Signed-off-by: Per G. da Silva --- .../cos_object_collision_and_adoption.feature | 363 ++++++++++++++++++ 1 file changed, 363 insertions(+) create mode 100644 test/e2e/features/cos_object_collision_and_adoption.feature diff --git a/test/e2e/features/cos_object_collision_and_adoption.feature b/test/e2e/features/cos_object_collision_and_adoption.feature new file mode 100644 index 0000000000..ae38d4b0a5 --- /dev/null +++ b/test/e2e/features/cos_object_collision_and_adoption.feature @@ -0,0 +1,363 @@ +@BoxcutterRuntime +Feature: ClusterObjectSet object collision and adoption + + As an OLM user applying a ClusterObjectSet directly, I want the revision's + collisionProtection setting to control whether it refuses to manage objects + that already exist (reporting ObjectCollision) or adopts them, so that I can + choose how aggressively a revision takes ownership of pre-existing objects. + + Background: + Given OLM is available + + # --------------------------------------------------------------------------- + # Collisions: the revision refuses to take ownership (Ready=False/ObjectCollision) + # --------------------------------------------------------------------------- + + Scenario: Prevent reports ObjectCollision for a pre-existing unowned object + Given namespace "${TEST_NAMESPACE}" is available + And resource is applied + """ + apiVersion: v1 + kind: ConfigMap + metadata: + name: shared-cm + namespace: ${TEST_NAMESPACE} + data: + k: pre-existing + """ + When ClusterObjectSet is applied + """ + apiVersion: olm.operatorframework.io/v1 + kind: ClusterObjectSet + metadata: + name: ${COS_NAME} + spec: + lifecycleState: Active + group: ${NAME} + collisionProtection: Prevent + phases: + - name: resources + objects: + - object: + apiVersion: v1 + kind: ConfigMap + metadata: + name: shared-cm + namespace: ${TEST_NAMESPACE} + data: + k: managed + revision: 1 + """ + Then ClusterObjectSet "${COS_NAME}" reports Ready as False with Reason ObjectCollision and Message includes: + """ + Cannot take ownership of 1 object(s) in phase "resources" because they are owned by another controller or already exist. + """ + And resource "configmap/shared-cm" matches + """ + data: + k: pre-existing + """ + + Scenario: Prevent reports ObjectCollision for an object owned by another revision + Given namespace "${TEST_NAMESPACE}" is available + And ClusterObjectSet is applied + """ + apiVersion: olm.operatorframework.io/v1 + kind: ClusterObjectSet + metadata: + name: owner-${TEST_NAMESPACE} + spec: + lifecycleState: Active + group: owner-${NAME} + collisionProtection: Prevent + phases: + - name: resources + objects: + - object: + apiVersion: v1 + kind: ConfigMap + metadata: + name: shared-cm + namespace: ${TEST_NAMESPACE} + data: + k: owned-by-owner + revision: 1 + """ + And ClusterObjectSet "owner-${TEST_NAMESPACE}" reports Ready as True with Reason AllObjectsReady + When ClusterObjectSet is applied + """ + apiVersion: olm.operatorframework.io/v1 + kind: ClusterObjectSet + metadata: + name: main-${TEST_NAMESPACE} + spec: + lifecycleState: Active + group: main-${NAME} + collisionProtection: Prevent + phases: + - name: resources + objects: + - object: + apiVersion: v1 + kind: ConfigMap + metadata: + name: shared-cm + namespace: ${TEST_NAMESPACE} + data: + k: contested + revision: 1 + """ + Then ClusterObjectSet "main-${TEST_NAMESPACE}" reports Ready as False with Reason ObjectCollision and Message includes: + """ + Cannot take ownership of 1 object(s) in phase "resources" because they are owned by another controller or already exist. + """ + + Scenario: IfNoController reports ObjectCollision for an object owned by another revision + Given namespace "${TEST_NAMESPACE}" is available + And ClusterObjectSet is applied + """ + apiVersion: olm.operatorframework.io/v1 + kind: ClusterObjectSet + metadata: + name: owner-${TEST_NAMESPACE} + spec: + lifecycleState: Active + group: owner-${NAME} + collisionProtection: Prevent + phases: + - name: resources + objects: + - object: + apiVersion: v1 + kind: ConfigMap + metadata: + name: shared-cm + namespace: ${TEST_NAMESPACE} + data: + k: owned-by-owner + revision: 1 + """ + And ClusterObjectSet "owner-${TEST_NAMESPACE}" reports Ready as True with Reason AllObjectsReady + When ClusterObjectSet is applied + """ + apiVersion: olm.operatorframework.io/v1 + kind: ClusterObjectSet + metadata: + name: main-${TEST_NAMESPACE} + spec: + lifecycleState: Active + group: main-${NAME} + collisionProtection: IfNoController + phases: + - name: resources + objects: + - object: + apiVersion: v1 + kind: ConfigMap + metadata: + name: shared-cm + namespace: ${TEST_NAMESPACE} + data: + k: contested + revision: 1 + """ + Then ClusterObjectSet "main-${TEST_NAMESPACE}" reports Ready as False with Reason ObjectCollision and Message includes: + """ + Cannot take ownership of 1 object(s) in phase "resources" because they are owned by another controller or already exist. + """ + + # --------------------------------------------------------------------------- + # Adoption: the revision takes ownership (Ready=True/AllObjectsReady) + # --------------------------------------------------------------------------- + + Scenario: IfNoController adopts a pre-existing unowned object + Given namespace "${TEST_NAMESPACE}" is available + And resource is applied + """ + apiVersion: v1 + kind: ConfigMap + metadata: + name: shared-cm + namespace: ${TEST_NAMESPACE} + data: + k: pre-existing + """ + When ClusterObjectSet is applied + """ + apiVersion: olm.operatorframework.io/v1 + kind: ClusterObjectSet + metadata: + name: ${COS_NAME} + spec: + lifecycleState: Active + group: ${NAME} + collisionProtection: IfNoController + phases: + - name: resources + objects: + - object: + apiVersion: v1 + kind: ConfigMap + metadata: + name: shared-cm + namespace: ${TEST_NAMESPACE} + data: + k: managed + revision: 1 + """ + Then ClusterObjectSet "${COS_NAME}" reports Ready as True with Reason AllObjectsReady + And resource "configmap/shared-cm" matches + """ + data: + k: managed + """ + + Scenario: None adopts a pre-existing unowned object + Given namespace "${TEST_NAMESPACE}" is available + And resource is applied + """ + apiVersion: v1 + kind: ConfigMap + metadata: + name: shared-cm + namespace: ${TEST_NAMESPACE} + data: + k: pre-existing + """ + When ClusterObjectSet is applied + """ + apiVersion: olm.operatorframework.io/v1 + kind: ClusterObjectSet + metadata: + name: ${COS_NAME} + spec: + lifecycleState: Active + group: ${NAME} + collisionProtection: None + phases: + - name: resources + objects: + - object: + apiVersion: v1 + kind: ConfigMap + metadata: + name: shared-cm + namespace: ${TEST_NAMESPACE} + data: + k: managed + revision: 1 + """ + Then ClusterObjectSet "${COS_NAME}" reports Ready as True with Reason AllObjectsReady + And resource "configmap/shared-cm" matches + """ + data: + k: managed + """ + + Scenario: None adopts an object owned by another revision + Given namespace "${TEST_NAMESPACE}" is available + And ClusterObjectSet is applied + """ + apiVersion: olm.operatorframework.io/v1 + kind: ClusterObjectSet + metadata: + name: owner-${TEST_NAMESPACE} + spec: + lifecycleState: Active + group: owner-${NAME} + collisionProtection: Prevent + phases: + - name: resources + objects: + - object: + apiVersion: v1 + kind: ConfigMap + metadata: + name: shared-cm + namespace: ${TEST_NAMESPACE} + data: + k: owned-by-owner + revision: 1 + """ + And ClusterObjectSet "owner-${TEST_NAMESPACE}" reports Ready as True with Reason AllObjectsReady + When ClusterObjectSet is applied + """ + apiVersion: olm.operatorframework.io/v1 + kind: ClusterObjectSet + metadata: + name: main-${TEST_NAMESPACE} + spec: + lifecycleState: Active + group: main-${NAME} + collisionProtection: None + phases: + - name: resources + objects: + - object: + apiVersion: v1 + kind: ConfigMap + metadata: + name: shared-cm + namespace: ${TEST_NAMESPACE} + data: + k: contested + revision: 1 + """ + Then ClusterObjectSet "main-${TEST_NAMESPACE}" reports Ready as True with Reason AllObjectsReady + And resource "configmap/shared-cm" matches + """ + data: + k: contested + """ + + # --------------------------------------------------------------------------- + # Recovery: a collision clears once the conflicting object is removed + # --------------------------------------------------------------------------- + + Scenario: ClusterObjectSet recovers from ObjectCollision once the conflicting object is removed + Given namespace "${TEST_NAMESPACE}" is available + And resource is applied + """ + apiVersion: v1 + kind: ConfigMap + metadata: + name: shared-cm + namespace: ${TEST_NAMESPACE} + data: + k: pre-existing + """ + When ClusterObjectSet is applied + """ + apiVersion: olm.operatorframework.io/v1 + kind: ClusterObjectSet + metadata: + name: ${COS_NAME} + spec: + lifecycleState: Active + group: ${NAME} + collisionProtection: Prevent + phases: + - name: resources + objects: + - object: + apiVersion: v1 + kind: ConfigMap + metadata: + name: shared-cm + namespace: ${TEST_NAMESPACE} + data: + k: managed + revision: 1 + """ + Then ClusterObjectSet "${COS_NAME}" reports Ready as False with Reason ObjectCollision and Message includes: + """ + Cannot take ownership of 1 object(s) in phase "resources" because they are owned by another controller or already exist. + """ + When resource "configmap/shared-cm" is removed + And ClusterObjectSet "${COS_NAME}" reconciliation is triggered + Then ClusterObjectSet "${COS_NAME}" reports Ready as True with Reason AllObjectsReady + And resource "configmap/shared-cm" matches + """ + data: + k: managed + """