From e2693086b2debc48a90ec80290729f6355f41b3b Mon Sep 17 00:00:00 2001 From: Alexey Masolov Date: Mon, 4 May 2026 16:15:57 +1000 Subject: [PATCH 1/8] feat: add configurable namespace for activation job pods Add activation_job_namespace field to the activation_worker section of the EDA CRD. When set, the operator injects EDA_ACTIVATION_JOB_NAMESPACE into the activation worker ConfigMap and creates a Role + RoleBinding in the target namespace so the EDA ServiceAccount can manage Jobs, Pods, Secrets, and Services there. Changes: - CRD: new optional string field activation_worker.activation_job_namespace - Role defaults: activation_job_namespace defaults to empty string - ConfigMap template: conditionally sets EDA_ACTIVATION_JOB_NAMESPACE - New RBAC template for cross-namespace Role and RoleBinding - Deploy task: applies/removes cross-namespace RBAC conditionally - ClusterRole + ClusterRoleBinding for operator to manage RBAC in the target namespace Closes ansible/eda-server-operator#344 Signed-off-by: Alexey Masolov Co-authored-by: Cursor --- config/crd/bases/eda.ansible.com_edas.yaml | 7 ++ .../rbac/activation_job_namespace_role.yaml | 30 +++++++++ ...activation_job_namespace_role_binding.yaml | 13 ++++ config/rbac/kustomization.yaml | 2 + roles/eda/defaults/main.yml | 1 + roles/eda/tasks/deploy_eda.yml | 38 +++++++++++ .../eda-activation-job-namespace-rbac.yaml.j2 | 65 +++++++++++++++++++ roles/eda/templates/eda.configmap.yaml.j2 | 4 ++ 8 files changed, 160 insertions(+) create mode 100644 config/rbac/activation_job_namespace_role.yaml create mode 100644 config/rbac/activation_job_namespace_role_binding.yaml create mode 100644 roles/eda/templates/eda-activation-job-namespace-rbac.yaml.j2 diff --git a/config/crd/bases/eda.ansible.com_edas.yaml b/config/crd/bases/eda.ansible.com_edas.yaml index b6549d22..4adf0787 100644 --- a/config/crd/bases/eda.ansible.com_edas.yaml +++ b/config/crd/bases/eda.ansible.com_edas.yaml @@ -2943,6 +2943,13 @@ spec: activation_worker: description: Defines desired state of eda-activation-worker resources properties: + activation_job_namespace: + description: Kubernetes namespace where activation job pods are + created. When set, jobs run in this namespace instead of the + EDA operator namespace. The operator creates the necessary + RBAC to allow the EDA service account to manage resources + in the target namespace. + type: string node_selector: additionalProperties: type: string diff --git a/config/rbac/activation_job_namespace_role.yaml b/config/rbac/activation_job_namespace_role.yaml new file mode 100644 index 00000000..e3bccf57 --- /dev/null +++ b/config/rbac/activation_job_namespace_role.yaml @@ -0,0 +1,30 @@ +--- +# Cluster-scoped because the target namespace is user-configurable at +# runtime via spec.activation_worker.activation_job_namespace. The +# operator must be able to create Role/RoleBinding resources in whatever +# namespace the user specifies. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: eda-activation-job-namespace-manager +rules: + - apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + - rolebindings + verbs: + - create + - delete + - get + - list + - patch + - update + - watch diff --git a/config/rbac/activation_job_namespace_role_binding.yaml b/config/rbac/activation_job_namespace_role_binding.yaml new file mode 100644 index 00000000..4e1ebee7 --- /dev/null +++ b/config/rbac/activation_job_namespace_role_binding.yaml @@ -0,0 +1,13 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: eda-activation-job-namespace-manager-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: eda-activation-job-namespace-manager +subjects: + - kind: ServiceAccount + name: controller-manager + namespace: system diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 02139c5c..88a7d201 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -12,3 +12,5 @@ resources: - metrics_auth_role.yaml - metrics_auth_role_binding.yaml - metrics_reader_role.yaml +- activation_job_namespace_role.yaml +- activation_job_namespace_role_binding.yaml diff --git a/roles/eda/defaults/main.yml b/roles/eda/defaults/main.yml index 21b8c3f3..79d70c80 100644 --- a/roles/eda/defaults/main.yml +++ b/roles/eda/defaults/main.yml @@ -61,6 +61,7 @@ _activation_worker: node_selector: {} tolerations: [] affinity: {} + activation_job_namespace: "" # Note: Deprecated "worker: {}" is intentionally excluded here so we know if the user set it _worker: {} diff --git a/roles/eda/tasks/deploy_eda.yml b/roles/eda/tasks/deploy_eda.yml index eef89c73..fe12f625 100644 --- a/roles/eda/tasks/deploy_eda.yml +++ b/roles/eda/tasks/deploy_eda.yml @@ -46,6 +46,44 @@ wait: yes when: public_base_url is defined +- name: Look up existing ConfigMap for previous activation_job_namespace + kubernetes.core.k8s_info: + api_version: v1 + kind: ConfigMap + namespace: "{{ ansible_operator_meta.namespace }}" + name: "{{ ansible_operator_meta.name }}-{{ deployment_type }}-env-properties" + register: _eda_env_cm + +- name: Record previous activation_job_namespace from ConfigMap + ansible.builtin.set_fact: + _previous_activation_job_namespace: >- + {{ (_eda_env_cm.resources | first).data.EDA_ACTIVATION_JOB_NAMESPACE | default('') }} + when: + - _eda_env_cm.resources | length > 0 + - (_eda_env_cm.resources | first).data is defined + +- name: Apply cross-namespace RBAC for activation job pods + k8s: + apply: yes + definition: "{{ lookup('template', 'eda-activation-job-namespace-rbac.yaml.j2') }}" + wait: yes + when: combined_activation_worker.activation_job_namespace | default('') | length > 0 + +- name: Remove cross-namespace RBAC when activation_job_namespace is unset + k8s: + state: absent + api_version: rbac.authorization.k8s.io/v1 + kind: "{{ item.kind }}" + name: "{{ ansible_operator_meta.name }}-activation-job-manager" + namespace: "{{ _previous_activation_job_namespace }}" + loop: + - { kind: RoleBinding } + - { kind: Role } + when: + - combined_activation_worker.activation_job_namespace | default('') | length == 0 + - _previous_activation_job_namespace | default('') | length > 0 + ignore_errors: yes + - name: Apply Backend deployment resources k8s: apply: yes diff --git a/roles/eda/templates/eda-activation-job-namespace-rbac.yaml.j2 b/roles/eda/templates/eda-activation-job-namespace-rbac.yaml.j2 new file mode 100644 index 00000000..eeb90bfd --- /dev/null +++ b/roles/eda/templates/eda-activation-job-namespace-rbac.yaml.j2 @@ -0,0 +1,65 @@ +# RBAC resources for cross-namespace activation job pods. +# Created when activation_job_namespace is set on the EDA CR. +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: '{{ ansible_operator_meta.name }}-activation-job-manager' + namespace: '{{ combined_activation_worker.activation_job_namespace }}' + labels: + {{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }} +rules: + - apiGroups: + - "" + resources: + - secrets + - pods + - pods/log + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: '{{ ansible_operator_meta.name }}-activation-job-manager' + namespace: '{{ combined_activation_worker.activation_job_namespace }}' + labels: + {{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: '{{ ansible_operator_meta.name }}-activation-job-manager' +subjects: + - kind: ServiceAccount + name: '{{ ansible_operator_meta.name }}' + namespace: '{{ ansible_operator_meta.namespace }}' diff --git a/roles/eda/templates/eda.configmap.yaml.j2 b/roles/eda/templates/eda.configmap.yaml.j2 index ecb439ff..19dc077e 100644 --- a/roles/eda/templates/eda.configmap.yaml.j2 +++ b/roles/eda/templates/eda.configmap.yaml.j2 @@ -45,6 +45,10 @@ data: {% endif %} {% endif %} +{% if combined_activation_worker.activation_job_namespace | default('') | length > 0 %} + EDA_ACTIVATION_JOB_NAMESPACE: "{{ combined_activation_worker.activation_job_namespace }}" +{% endif %} + # Custom user variables {% for item in extra_settings | default([]) %} {{ item.setting | upper }}: "{{ item.value }}" From 223e9171fdcaa132adfa18cb52ea65f2d61e2305 Mon Sep 17 00:00:00 2001 From: Alexey Masolov Date: Mon, 18 May 2026 22:05:52 +1000 Subject: [PATCH 2/8] ci: add activation_job_namespace scenario to PR workflow Add a CI test scenario for the new activation_job_namespace parameter. Creates a dedicated CR fixture and pre-creates the target namespace before applying the CR. Signed-off-by: Alexey Masolov Co-authored-by: Cursor --- .ci/eda_v1alpha1_eda.activation_job_namespace.ci.yaml | 11 +++++++++++ .github/workflows/pr.yml | 5 +++++ 2 files changed, 16 insertions(+) create mode 100644 .ci/eda_v1alpha1_eda.activation_job_namespace.ci.yaml diff --git a/.ci/eda_v1alpha1_eda.activation_job_namespace.ci.yaml b/.ci/eda_v1alpha1_eda.activation_job_namespace.ci.yaml new file mode 100644 index 00000000..77d1b39d --- /dev/null +++ b/.ci/eda_v1alpha1_eda.activation_job_namespace.ci.yaml @@ -0,0 +1,11 @@ +apiVersion: eda.ansible.com/v1alpha1 +kind: EDA +metadata: + name: eda-demo + annotations: + "ansible.sdk.operatorframework.io/verbosity": "5" +spec: + no_log: false + automation_server_url: http://foo.bar + activation_worker: + activation_job_namespace: "eda-jobs" diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 6feadce6..820a7fb1 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -16,6 +16,7 @@ jobs: - SCENARIO: externaldb - SCENARIO: ingress - SCENARIO: event_persistence + - SCENARIO: activation_job_namespace steps: - name: Checkout sources uses: actions/checkout@v4 @@ -68,6 +69,10 @@ jobs: run: kubectl apply -f .ci/eda-event-stream-external-database.secret.yaml if: ${{ matrix.SCENARIO == 'externaldb' }} + - name: Create activation job namespace + run: kubectl create namespace eda-jobs + if: ${{ matrix.SCENARIO == 'activation_job_namespace' }} + - name: Create the EDA demo CR run: | kubectl apply -f .ci/eda_v1alpha1_eda.${{ matrix.SCENARIO }}.ci.yaml From 2a9417afd0d0431d1a9dadfa526cbfead1986c69 Mon Sep 17 00:00:00 2001 From: Alexey Masolov Date: Mon, 18 May 2026 22:11:57 +1000 Subject: [PATCH 3/8] fix: capture previous namespace before ConfigMap apply and handle namespace changes Move the lookup of _previous_activation_job_namespace to run before the ConfigMap is applied, so the old value is read before it gets overwritten. Also widen the RBAC cleanup condition to trigger whenever the previous namespace differs from the new one (not just when the new value is empty), so changing from namespace A to B correctly removes orphaned Role and RoleBinding from A. Signed-off-by: Alexey Masolov Co-authored-by: Cursor --- roles/eda/tasks/deploy_eda.yml | 36 +++++++++++++++++----------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/roles/eda/tasks/deploy_eda.yml b/roles/eda/tasks/deploy_eda.yml index fe12f625..5b21d9be 100644 --- a/roles/eda/tasks/deploy_eda.yml +++ b/roles/eda/tasks/deploy_eda.yml @@ -19,6 +19,22 @@ event_stream_mtls_base_url: "{{ public_base_url.rstrip('/') }}/{{ event_stream_mtls_prefix_path }}" when: public_base_url | default('') | length > 0 +- name: Look up existing ConfigMap for previous activation_job_namespace + kubernetes.core.k8s_info: + api_version: v1 + kind: ConfigMap + namespace: "{{ ansible_operator_meta.namespace }}" + name: "{{ ansible_operator_meta.name }}-{{ deployment_type }}-env-properties" + register: _eda_env_cm + +- name: Record previous activation_job_namespace from ConfigMap + ansible.builtin.set_fact: + _previous_activation_job_namespace: >- + {{ (_eda_env_cm.resources | first).data.EDA_ACTIVATION_JOB_NAMESPACE | default('') }} + when: + - _eda_env_cm.resources | length > 0 + - (_eda_env_cm.resources | first).data is defined + - name: Apply ConfigMap resources k8s: apply: yes @@ -46,22 +62,6 @@ wait: yes when: public_base_url is defined -- name: Look up existing ConfigMap for previous activation_job_namespace - kubernetes.core.k8s_info: - api_version: v1 - kind: ConfigMap - namespace: "{{ ansible_operator_meta.namespace }}" - name: "{{ ansible_operator_meta.name }}-{{ deployment_type }}-env-properties" - register: _eda_env_cm - -- name: Record previous activation_job_namespace from ConfigMap - ansible.builtin.set_fact: - _previous_activation_job_namespace: >- - {{ (_eda_env_cm.resources | first).data.EDA_ACTIVATION_JOB_NAMESPACE | default('') }} - when: - - _eda_env_cm.resources | length > 0 - - (_eda_env_cm.resources | first).data is defined - - name: Apply cross-namespace RBAC for activation job pods k8s: apply: yes @@ -69,7 +69,7 @@ wait: yes when: combined_activation_worker.activation_job_namespace | default('') | length > 0 -- name: Remove cross-namespace RBAC when activation_job_namespace is unset +- name: Remove cross-namespace RBAC from previous namespace k8s: state: absent api_version: rbac.authorization.k8s.io/v1 @@ -80,8 +80,8 @@ - { kind: RoleBinding } - { kind: Role } when: - - combined_activation_worker.activation_job_namespace | default('') | length == 0 - _previous_activation_job_namespace | default('') | length > 0 + - combined_activation_worker.activation_job_namespace | default('') != _previous_activation_job_namespace ignore_errors: yes - name: Apply Backend deployment resources From 3637db1e25b89864cd08fa3d2e40fd9e79d41822 Mon Sep 17 00:00:00 2001 From: Alexey Masolov Date: Tue, 19 May 2026 07:59:46 +1000 Subject: [PATCH 4/8] fix: add escalate verb to ClusterRole and fix RBAC task ordering Add the escalate verb on the roles resource in the eda-activation-job-namespace-manager ClusterRole. Kubernetes RBAC escalation prevention blocks creating a Role that grants permissions the creator doesn't hold. The escalate verb is the standard mechanism to permit this without granting the operator broad cluster-wide access to pods, secrets, services, and jobs. Also moves the _previous_activation_job_namespace lookup to run before the ConfigMap is applied (so the old value is captured before overwrite) and widens the RBAC cleanup condition to trigger on any namespace change, not just removal. Signed-off-by: Alexey Masolov Co-authored-by: Cursor --- config/rbac/activation_job_namespace_role.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/config/rbac/activation_job_namespace_role.yaml b/config/rbac/activation_job_namespace_role.yaml index e3bccf57..94be3e0c 100644 --- a/config/rbac/activation_job_namespace_role.yaml +++ b/config/rbac/activation_job_namespace_role.yaml @@ -19,6 +19,18 @@ rules: - rbac.authorization.k8s.io resources: - roles + verbs: + - create + - delete + - escalate + - get + - list + - patch + - update + - watch + - apiGroups: + - rbac.authorization.k8s.io + resources: - rolebindings verbs: - create From 9111203c2809cc17170ed100765dd42341fa7ecc Mon Sep 17 00:00:00 2001 From: Alexey Masolov Date: Tue, 19 May 2026 08:55:52 +1000 Subject: [PATCH 5/8] fix: add bind verb to ClusterRole for RoleBinding creation The escalate verb fixed Role creation, but creating a RoleBinding that references a Role with elevated permissions also requires the bind verb on the roles resource. Without it, Kubernetes RBAC escalation prevention blocks the RoleBinding creation. Signed-off-by: Alexey Masolov Co-authored-by: Cursor --- config/rbac/activation_job_namespace_role.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/rbac/activation_job_namespace_role.yaml b/config/rbac/activation_job_namespace_role.yaml index 94be3e0c..09054130 100644 --- a/config/rbac/activation_job_namespace_role.yaml +++ b/config/rbac/activation_job_namespace_role.yaml @@ -20,6 +20,7 @@ rules: resources: - roles verbs: + - bind - create - delete - escalate From 4596b3c38c6c63c66c202517a34016226a72aff3 Mon Sep 17 00:00:00 2001 From: Alexey Masolov Date: Tue, 2 Jun 2026 08:56:09 +1000 Subject: [PATCH 6/8] fix: remove ignore_errors from RBAC cleanup task The k8s module with state=absent already handles 404 gracefully (treats "not found" as "already absent"), so ignore_errors is unnecessary and could mask legitimate permission or connectivity failures. Signed-off-by: Alexey Masolov --- roles/eda/tasks/deploy_eda.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/roles/eda/tasks/deploy_eda.yml b/roles/eda/tasks/deploy_eda.yml index 5b21d9be..438b6609 100644 --- a/roles/eda/tasks/deploy_eda.yml +++ b/roles/eda/tasks/deploy_eda.yml @@ -82,7 +82,6 @@ when: - _previous_activation_job_namespace | default('') | length > 0 - combined_activation_worker.activation_job_namespace | default('') != _previous_activation_job_namespace - ignore_errors: yes - name: Apply Backend deployment resources k8s: From 18e3ce758c61aad52026c691637dc14e350b9057 Mon Sep 17 00:00:00 2001 From: Alexey Masolov Date: Tue, 25 Aug 2026 06:55:17 +1000 Subject: [PATCH 7/8] fix: add namespaceSelector to NetworkPolicies for cross-namespace activation jobs When activation_job_namespace is set, activation Job pods run in a different namespace. The default-deny NetworkPolicies from #362 use bare podSelector which only matches pods in the same namespace, blocking websocket, API, and pg_notify traffic from cross-namespace job pods. Add conditional namespaceSelector to eda-api and postgres NetworkPolicy templates so ingress rules match activation job pods regardless of which namespace they run in. Co-authored-by: Cursor --- roles/eda/templates/eda-api.networkpolicy.yaml.j2 | 11 ++++++++++- .../postgres/templates/postgres.networkpolicy.yaml.j2 | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/roles/eda/templates/eda-api.networkpolicy.yaml.j2 b/roles/eda/templates/eda-api.networkpolicy.yaml.j2 index 97764c31..1f4ab1bd 100644 --- a/roles/eda/templates/eda-api.networkpolicy.yaml.j2 +++ b/roles/eda/templates/eda-api.networkpolicy.yaml.j2 @@ -47,13 +47,22 @@ spec: - protocol: TCP port: {{ websocket_port }} # Allow traffic from activation Job pods (created at runtime by activation - # workers, not operator-managed — carry only app: eda). + # workers, not operator-managed, carry only app: eda). # ansible_rulebook connects to daphne (websocket_port) for rulebook # execution and to the API (api_nginx_port) for token refresh. - from: +{% if combined_activation_worker.activation_job_namespace | default('') | length > 0 %} + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: '{{ combined_activation_worker.activation_job_namespace }}' + podSelector: + matchLabels: + app: eda +{% else %} - podSelector: matchLabels: app: eda +{% endif %} ports: - protocol: TCP port: {{ websocket_port }} diff --git a/roles/postgres/templates/postgres.networkpolicy.yaml.j2 b/roles/postgres/templates/postgres.networkpolicy.yaml.j2 index fb0e2a8f..19595841 100644 --- a/roles/postgres/templates/postgres.networkpolicy.yaml.j2 +++ b/roles/postgres/templates/postgres.networkpolicy.yaml.j2 @@ -34,11 +34,20 @@ spec: matchLabels: control-plane: controller-manager # EDA activation Job pods (created at runtime by activation workers, - # not operator-managed — carry only app: eda, no managed-by label). + # not operator-managed, carry only app: eda, no managed-by label). # Event stream activations connect to Postgres via pg_notify. +{% if combined_activation_worker.activation_job_namespace | default('') | length > 0 %} + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: '{{ combined_activation_worker.activation_job_namespace }}' + podSelector: + matchLabels: + app: eda +{% else %} - podSelector: matchLabels: app: eda +{% endif %} ports: - protocol: TCP port: {{ eda_postgres_port | default('5432') }} From 38b2d9d4a74c984fceb5228c7cc726f9f7a3e8f1 Mon Sep 17 00:00:00 2001 From: Alexey Masolov Date: Tue, 25 Aug 2026 07:02:58 +1000 Subject: [PATCH 8/8] fix: apply ConfigMap after RBAC migration to preserve rollback state Move the env-properties ConfigMap update to run after the cross-namespace RBAC create/delete tasks. Previously, the ConfigMap was updated first, so if the RBAC operations failed the next reconciliation would read the new namespace as _previous_activation_job_namespace and skip cleanup of the old Role and RoleBinding. Co-authored-by: Cursor --- roles/eda/tasks/deploy_eda.yml | 42 +++++++++++++++++----------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/roles/eda/tasks/deploy_eda.yml b/roles/eda/tasks/deploy_eda.yml index 438b6609..5997a91d 100644 --- a/roles/eda/tasks/deploy_eda.yml +++ b/roles/eda/tasks/deploy_eda.yml @@ -35,6 +35,27 @@ - _eda_env_cm.resources | length > 0 - (_eda_env_cm.resources | first).data is defined +- name: Apply cross-namespace RBAC for activation job pods + k8s: + apply: yes + definition: "{{ lookup('template', 'eda-activation-job-namespace-rbac.yaml.j2') }}" + wait: yes + when: combined_activation_worker.activation_job_namespace | default('') | length > 0 + +- name: Remove cross-namespace RBAC from previous namespace + k8s: + state: absent + api_version: rbac.authorization.k8s.io/v1 + kind: "{{ item.kind }}" + name: "{{ ansible_operator_meta.name }}-activation-job-manager" + namespace: "{{ _previous_activation_job_namespace }}" + loop: + - { kind: RoleBinding } + - { kind: Role } + when: + - _previous_activation_job_namespace | default('') | length > 0 + - combined_activation_worker.activation_job_namespace | default('') != _previous_activation_job_namespace + - name: Apply ConfigMap resources k8s: apply: yes @@ -62,27 +83,6 @@ wait: yes when: public_base_url is defined -- name: Apply cross-namespace RBAC for activation job pods - k8s: - apply: yes - definition: "{{ lookup('template', 'eda-activation-job-namespace-rbac.yaml.j2') }}" - wait: yes - when: combined_activation_worker.activation_job_namespace | default('') | length > 0 - -- name: Remove cross-namespace RBAC from previous namespace - k8s: - state: absent - api_version: rbac.authorization.k8s.io/v1 - kind: "{{ item.kind }}" - name: "{{ ansible_operator_meta.name }}-activation-job-manager" - namespace: "{{ _previous_activation_job_namespace }}" - loop: - - { kind: RoleBinding } - - { kind: Role } - when: - - _previous_activation_job_namespace | default('') | length > 0 - - combined_activation_worker.activation_job_namespace | default('') != _previous_activation_job_namespace - - name: Apply Backend deployment resources k8s: apply: yes