From c2046af936c4be6e6b3b9779ff7080b372d03771 Mon Sep 17 00:00:00 2001 From: Blai Peidro Date: Thu, 17 Sep 2026 01:05:59 +0200 Subject: [PATCH 1/2] refactor: name the operator's own objects after Ascender The deployment, its database, its labels and its configmaps are Ascender now. The operator managing them was still awx-operator-controller-manager, which is the most visible awx left in a namespace. The kustomize namePrefix moves to ascender-operator-, and the three base names that said awx before the prefix was applied move with it: the manager Role, its RoleBinding and its config map. The container in the Deployment becomes ascender-manager. Every patch that selects on those names follows, including the ones under config/testing. hack/rename-operator-objects.sh removes what an upgrade leaves behind. Kubernetes has no rename and kubectl apply only touches the names it is given, so an upgrade creates eleven new objects beside eleven old ones, including a second operator Deployment. The script deletes the old set by name, refuses to run before the new Deployment exists, and refuses to delete anything that is not prefixed or that is a CRD. That last guard is the point: config/default includes ../crd, so reaching for kubectl delete -k would take the CRDs and cascade to every deployment in the cluster. --leader-election-id stays awx-operator deliberately. Both operators pass the same id, so they contend for one lease and only one is ever active: an upgrade that has not been cleaned up yet is an idle standby rather than two controllers reconciling the same objects. It can be retired once no old operator is left anywhere. The namespace also stays awx here, for a different reason. The operator is namespace scoped, so changing the default would leave a raw-manifest user with a second operator in an empty namespace while their deployment carried on under the old one, and their upgrade would silently do nothing. That wants its own change, with a migration and release notes. awx-devel stays too: it is a development-mode volume name tied to the hostPath /awx_devel, and renaming one without the other only creates an inconsistency. Tested on a kind cluster by installing the old manifests, applying the new ones over them, and running the script: it refuses before the new Deployment exists, finds exactly the eleven duplicates, and after --apply leaves one operator, all eight CRDs, and the AWX resource untouched. --- config/default/kustomization.yaml | 2 +- config/default/manager_auth_proxy_patch.yaml | 2 +- config/default/manager_config_patch.yaml | 8 +- config/manager/kustomization.yaml | 2 +- config/manager/manager.yaml | 2 +- config/rbac/role.yaml | 2 +- config/rbac/role_binding.yaml | 4 +- config/testing/debug_logs_patch.yaml | 2 +- config/testing/manager_image.yaml | 2 +- config/testing/pull_policy/Always.yaml | 2 +- config/testing/pull_policy/IfNotPresent.yaml | 2 +- config/testing/pull_policy/Never.yaml | 2 +- docs/installation/basic-install.md | 10 +- docs/troubleshooting/debugging.md | 4 +- hack/rename-operator-objects.sh | 125 +++++++++++++++++++ roles/backup/meta/main.yml | 2 +- roles/common/meta/main.yml | 2 +- roles/installer/meta/main.yml | 2 +- roles/restore/meta/main.yml | 2 +- 19 files changed, 152 insertions(+), 27 deletions(-) create mode 100755 hack/rename-operator-objects.sh diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 1bc1b049..05c4f2e2 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -6,7 +6,7 @@ namespace: awx # "wordpress" becomes "alices-wordpress". # Note that it should also match with the prefix (text before '-') of the namespace # field above. -namePrefix: awx-operator- +namePrefix: ascender-operator- # Labels to add to all resources and selectors. #labels: diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml index d4ff9d85..ece52bc8 100644 --- a/config/default/manager_auth_proxy_patch.yaml +++ b/config/default/manager_auth_proxy_patch.yaml @@ -32,7 +32,7 @@ spec: requests: cpu: 5m memory: 64Mi - - name: awx-manager + - name: ascender-manager args: - "--health-probe-bind-address=:6789" - "--metrics-bind-address=127.0.0.1:8080" diff --git a/config/default/manager_config_patch.yaml b/config/default/manager_config_patch.yaml index f99057d2..35400502 100644 --- a/config/default/manager_config_patch.yaml +++ b/config/default/manager_config_patch.yaml @@ -7,14 +7,14 @@ spec: template: spec: containers: - - name: awx-manager + - name: ascender-manager args: - "--config=controller_manager_config.yaml" volumeMounts: - - name: awx-manager-config + - name: ascender-manager-config mountPath: /controller_manager_config.yaml subPath: controller_manager_config.yaml volumes: - - name: awx-manager-config + - name: ascender-manager-config configMap: - name: awx-manager-config + name: ascender-manager-config diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 04592c32..72491804 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -7,7 +7,7 @@ generatorOptions: configMapGenerator: - files: - controller_manager_config.yaml - name: awx-manager-config + name: ascender-manager-config apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 35ec4098..5d70e16b 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -37,7 +37,7 @@ spec: - --leader-election-id=awx-operator image: controller:latest imagePullPolicy: IfNotPresent - name: awx-manager + name: ascender-manager env: - name: ANSIBLE_GATHERING value: explicit diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 3cfd9f52..f4ed0805 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -3,7 +3,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: creationTimestamp: null - name: awx-manager-role + name: ascender-manager-role rules: - apiGroups: - route.openshift.io diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml index f8ef07f0..5cc1c5af 100644 --- a/config/rbac/role_binding.yaml +++ b/config/rbac/role_binding.yaml @@ -2,11 +2,11 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: awx-manager-rolebinding + name: ascender-manager-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: Role - name: awx-manager-role + name: ascender-manager-role subjects: - kind: ServiceAccount name: controller-manager diff --git a/config/testing/debug_logs_patch.yaml b/config/testing/debug_logs_patch.yaml index 0d01b1ff..d7699fc1 100644 --- a/config/testing/debug_logs_patch.yaml +++ b/config/testing/debug_logs_patch.yaml @@ -8,7 +8,7 @@ spec: template: spec: containers: - - name: awx-manager + - name: ascender-manager env: - name: ANSIBLE_DEBUG_LOGS value: "TRUE" diff --git a/config/testing/manager_image.yaml b/config/testing/manager_image.yaml index 12d34430..57cf0f28 100644 --- a/config/testing/manager_image.yaml +++ b/config/testing/manager_image.yaml @@ -8,5 +8,5 @@ spec: template: spec: containers: - - name: awx-manager + - name: ascender-manager image: testing diff --git a/config/testing/pull_policy/Always.yaml b/config/testing/pull_policy/Always.yaml index 2c704ba0..e029d011 100644 --- a/config/testing/pull_policy/Always.yaml +++ b/config/testing/pull_policy/Always.yaml @@ -8,5 +8,5 @@ spec: template: spec: containers: - - name: awx-manager + - name: ascender-manager imagePullPolicy: Always diff --git a/config/testing/pull_policy/IfNotPresent.yaml b/config/testing/pull_policy/IfNotPresent.yaml index a6c5b682..1e1d2e84 100644 --- a/config/testing/pull_policy/IfNotPresent.yaml +++ b/config/testing/pull_policy/IfNotPresent.yaml @@ -8,5 +8,5 @@ spec: template: spec: containers: - - name: awx-manager + - name: ascender-manager imagePullPolicy: IfNotPresent diff --git a/config/testing/pull_policy/Never.yaml b/config/testing/pull_policy/Never.yaml index b20700d4..f85aa92a 100644 --- a/config/testing/pull_policy/Never.yaml +++ b/config/testing/pull_policy/Never.yaml @@ -8,5 +8,5 @@ spec: template: spec: containers: - - name: awx-manager + - name: ascender-manager imagePullPolicy: Never diff --git a/docs/installation/basic-install.md b/docs/installation/basic-install.md index 0012fa37..bb0a969c 100644 --- a/docs/installation/basic-install.md +++ b/docs/installation/basic-install.md @@ -65,7 +65,7 @@ customresourcedefinition.apiextensions.k8s.io/ascenders.ascender.ansible.com cre customresourcedefinition.apiextensions.k8s.io/awxbackups.awx.ansible.com created customresourcedefinition.apiextensions.k8s.io/awxrestores.awx.ansible.com created customresourcedefinition.apiextensions.k8s.io/awxs.awx.ansible.com created -serviceaccount/awx-operator-controller-manager created +serviceaccount/ascender-operator-controller-manager created role.rbac.authorization.k8s.io/awx-operator-awx-manager-role created role.rbac.authorization.k8s.io/awx-operator-leader-election-role created clusterrole.rbac.authorization.k8s.io/awx-operator-metrics-reader created @@ -74,8 +74,8 @@ rolebinding.rbac.authorization.k8s.io/awx-operator-awx-manager-rolebinding creat rolebinding.rbac.authorization.k8s.io/awx-operator-leader-election-rolebinding created clusterrolebinding.rbac.authorization.k8s.io/awx-operator-proxy-rolebinding created configmap/awx-operator-awx-manager-config created -service/awx-operator-controller-manager-metrics-service created -deployment.apps/awx-operator-controller-manager created +service/ascender-operator-controller-manager-metrics-service created +deployment.apps/ascender-operator-controller-manager created ``` Wait a bit and you should have the `awx-operator` running: @@ -83,7 +83,7 @@ Wait a bit and you should have the `awx-operator` running: ``` $ kubectl get pods -n awx NAME READY STATUS RESTARTS AGE -awx-operator-controller-manager-66ccd8f997-rhd4z 2/2 Running 0 11s +ascender-operator-controller-manager-66ccd8f997-rhd4z 2/2 Running 0 11s ``` So we don't have to keep repeating `-n awx`, let's set the current namespace for `kubectl`: @@ -142,7 +142,7 @@ kubectl apply -k . After a few minutes, the new Ascender instance will be deployed. You can look at the operator pod logs in order to know where the installation process is at: ``` -$ kubectl logs -f deployments/awx-operator-controller-manager -c awx-manager +$ kubectl logs -f deployments/ascender-operator-controller-manager -c ascender-manager ``` After a few seconds, you should see the operator begin to create new resources: diff --git a/docs/troubleshooting/debugging.md b/docs/troubleshooting/debugging.md index d643bb99..e7ab60a7 100644 --- a/docs/troubleshooting/debugging.md +++ b/docs/troubleshooting/debugging.md @@ -5,7 +5,7 @@ When the operator is deploying Ascender, it is running the `installer` role inside the operator container. If the AWX CR's status is `Failed`, it is often useful to look at the awx-operator container logs, which shows the output of the installer role. To see these logs, run: ``` -kubectl logs deployments/awx-operator-controller-manager -c awx-manager -f +kubectl logs deployments/ascender-operator-controller-manager -c ascender-manager -f ``` ### Inspect k8s Resources @@ -75,7 +75,7 @@ $ pip install -r molecule/requirements.txt To prevent the changes we're about to make from being overwritten, scale down any running instance of the operator: ``` -$ kubectl scale deployment awx-operator-controller-manager --replicas=0 +$ kubectl scale deployment ascender-operator-controller-manager --replicas=0 ``` Create a playbook that invokes the installer role (the operator uses ansible-runner's role execution feature): diff --git a/hack/rename-operator-objects.sh b/hack/rename-operator-objects.sh new file mode 100755 index 00000000..8942127d --- /dev/null +++ b/hack/rename-operator-objects.sh @@ -0,0 +1,125 @@ +#!/bin/bash + +# Remove the operator's old awx-operator-* objects after it has been renamed. +# +# The operator's own objects are named by a kustomize namePrefix, which moved +# from awx-operator- to ascender-operator-. Kubernetes has no rename, and +# kubectl apply only creates or updates the names in the manifest it is given, +# so an upgrade leaves the eleven old objects in place beside the eleven new +# ones, including a second operator Deployment. +# +# That overlap is not dangerous while both pass --leader-election-id=awx-operator: +# they contend for one lease and only one of them is ever active. The old set is +# still worth removing, and this removes it. +# +# The one thing it will not do is delete a CustomResourceDefinition. Reaching +# for `kubectl delete -k config/default` instead of this script would do exactly +# that, because config/default includes ../crd, and deleting those CRDs cascades +# to every AWX and Ascender in the cluster, which is every deployment. +# +# Usage: +# ./hack/rename-operator-objects.sh -n # shows what it would delete +# ./hack/rename-operator-objects.sh -n --apply # deletes it + +set -euo pipefail + +NAMESPACE="" +APPLY=false +OLD_PREFIX="awx-operator-" +NEW_PREFIX="ascender-operator-" + +log() { printf '%s\n' "$*"; } +step() { printf '\n== %s\n' "$*"; } +die() { printf 'error: %s\n' "$*" >&2; exit 1; } + +while [ $# -gt 0 ]; do + case "$1" in + -n|--namespace) NAMESPACE="$2"; shift 2 ;; + --apply) APPLY=true; shift ;; + --old-prefix) OLD_PREFIX="$2"; shift 2 ;; + --new-prefix) NEW_PREFIX="$2"; shift 2 ;; + -h|--help) sed -n '3,20p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; + *) die "unknown argument $1" ;; + esac +done + +[ -n "$NAMESPACE" ] || die "a namespace is required, pass -n" +command -v kubectl >/dev/null || die "kubectl is not on PATH" + +KC="kubectl -n $NAMESPACE" + +# Namespaced first, then the two that are not. No CRD appears here, and none +# ever should: see the note at the top. +NAMESPACED="deployment serviceaccount service role rolebinding configmap" +CLUSTER="clusterrole clusterrolebinding" + + +step "Looking for the new objects first" + +# Deleting the old set before the new one exists would leave no operator at all. +if ! $KC get "deployment/${NEW_PREFIX}controller-manager" >/dev/null 2>&1; then + die "there is no ${NEW_PREFIX}controller-manager in $NAMESPACE. Apply the new manifests first: this only removes what they replace." +fi +log " ${NEW_PREFIX}controller-manager is there" + + +step "Old objects still present" + +FOUND="" +for kind in $NAMESPACED; do + while read -r name; do + [ -n "$name" ] || continue + case "$name" in + "${OLD_PREFIX}"*) FOUND="${FOUND}${kind}/${name}"$'\n' ;; + esac + done < <($KC get "$kind" -o custom-columns=:metadata.name --no-headers 2>/dev/null || true) +done +for kind in $CLUSTER; do + while read -r name; do + [ -n "$name" ] || continue + case "$name" in + "${OLD_PREFIX}"*) FOUND="${FOUND}${kind}/${name}"$'\n' ;; + esac + done < <(kubectl get "$kind" -o custom-columns=:metadata.name --no-headers 2>/dev/null || true) +done + +FOUND=$(printf '%s' "$FOUND" | sed '/^$/d') + +if [ -z "$FOUND" ]; then + log " none, nothing to do" + exit 0 +fi + +printf '%s\n' "$FOUND" | sed 's/^/ /' + +if ! $APPLY; then + step "Plan" + log " delete each of the above, and nothing else." + log " No CustomResourceDefinition is touched, so no deployment is affected." + log " Re-run with --apply." + exit 0 +fi + + +step "Deleting" + +while read -r ref; do + [ -n "$ref" ] || continue + case "$ref" in + *customresourcedefinition*|*crd*) + die "refusing to delete $ref: deleting a CRD cascades to every deployment in the cluster" ;; + esac + case "${ref#*/}" in + "${OLD_PREFIX}"*) ;; + *) die "refusing to delete $ref: it does not start with $OLD_PREFIX" ;; + esac + case "$ref" in + clusterrole/*|clusterrolebinding/*) kubectl delete "$ref" --wait=true >/dev/null ;; + *) $KC delete "$ref" --wait=true >/dev/null ;; + esac + log " deleted $ref" +done <<< "$FOUND" + + +step "Done" +log " the old $OLD_PREFIX objects are gone, and ${NEW_PREFIX}controller-manager is the only operator left." diff --git a/roles/backup/meta/main.yml b/roles/backup/meta/main.yml index 1f223420..695402c3 100644 --- a/roles/backup/meta/main.yml +++ b/roles/backup/meta/main.yml @@ -19,7 +19,7 @@ galaxy_info: galaxy_tags: - tower - controller - - awx + - ascender - ansible - backup - automation diff --git a/roles/common/meta/main.yml b/roles/common/meta/main.yml index d4d75ee3..62e60aa4 100644 --- a/roles/common/meta/main.yml +++ b/roles/common/meta/main.yml @@ -18,7 +18,7 @@ galaxy_info: galaxy_tags: - tower - - awx + - ascender - ansible - automation - ci diff --git a/roles/installer/meta/main.yml b/roles/installer/meta/main.yml index f6c26e0e..1b4136e6 100644 --- a/roles/installer/meta/main.yml +++ b/roles/installer/meta/main.yml @@ -18,7 +18,7 @@ galaxy_info: galaxy_tags: - tower - - awx + - ascender - ansible - automation - ci diff --git a/roles/restore/meta/main.yml b/roles/restore/meta/main.yml index 22401fb3..956be50f 100644 --- a/roles/restore/meta/main.yml +++ b/roles/restore/meta/main.yml @@ -19,7 +19,7 @@ galaxy_info: galaxy_tags: - tower - controller - - awx + - ascender - ansible - restore - automation From 6bd6237698bab562302e448ab7653e59e507a086 Mon Sep 17 00:00:00 2001 From: Blai Peidro Date: Thu, 17 Sep 2026 01:15:33 +0200 Subject: [PATCH 2/2] docs: say which of the two rename scripts you want hack/ now holds two scripts with rename in their purpose, and the question they raise is which one somebody needs. They stay separate: one acts on the operator's own install and the other on a single deployment, they take different arguments, and one of them renames a PostgreSQL database and stops the deployment serving while the other touches no deployment at all. Folding them together would make the disruptive one easier to reach by accident to save twenty lines of shared logging. So a README instead. It says what each of the four scripts does, that neither rename needs the other, and that the operator goes first if both are being done, because the migration needs a working operator to rebuild what it removes and the rename never stops one running. Each script's header names the other, so whichever is opened first points across. It also repeats the rule both scripts enforce in code: nothing here deletes a CustomResourceDefinition, and neither should anyone else, because config/default includes ../crd and deleting those cascades to every deployment in the cluster. The added header lines pushed --help past the range it prints, which cut the rename script's usage off mid sentence. The range is derived from where the comment block ends now. --- hack/README.md | 52 +++++++++++++++++++++++++++++++++ hack/migrate-awx-to-ascender.sh | 3 ++ hack/rename-operator-objects.sh | 5 +++- 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 hack/README.md diff --git a/hack/README.md b/hack/README.md new file mode 100644 index 00000000..36be1163 --- /dev/null +++ b/hack/README.md @@ -0,0 +1,52 @@ +# hack + +Four scripts. Two of them exist because the operator is moving from the AWX name +to the Ascender one, and which you need depends on what you are moving. + +## Moving to Ascender + +The two are separate on purpose. They act on different things, take different +arguments, and one of them is far more disruptive than the other. + +**`rename-operator-objects.sh`** acts on the operator's own install. The operator's +objects are named by a kustomize prefix, which moved from `awx-operator-` to +`ascender-operator-`. Kubernetes has no rename and `kubectl apply` only touches the +names it is given, so an upgrade leaves eleven old objects beside eleven new ones, +including a second operator Deployment. This deletes the old set. + +- Once per cluster, after upgrading the operator. +- Takes a namespace and nothing else. +- Touches no deployment and no database. Nothing stops serving. + +**`migrate-awx-to-ascender.sh`** acts on one deployment. It moves it from the `AWX` +kind to the `Ascender` kind, which means renaming its database, swapping the custom +resource and rebuilding the workloads whose selectors cannot be changed. + +- Once per deployment, when an administrator chooses to. +- Takes a namespace and the deployment's name. +- Has a window of downtime, and a `--rollback` and `--resume` for when it does not + go to plan. + +Neither needs the other. A cluster can rename its operator and leave every +deployment on the AWX kind, or move a deployment across and leave the operator +named as it is. If you are doing both, rename the operator first: the migration +needs a working operator to rebuild what it removes, and the rename never stops +one running. + +Both print a plan and change nothing until given `--apply`. + +## The other two + +**`render-ascender-crds.py`** writes the `ascender.ansible.com` CRDs, samples and CSV +entries from the `awx.ansible.com` ones. `make crds` runs it, and CI fails if the +result differs from what is committed. Edit the AWX side; this moves the rest. + +**`publish-to-operator-hub.sh`** opens the pull requests that publish a release to +community-operators and community-operators-prod. + +## One rule worth repeating + +Neither rename script will delete a CustomResourceDefinition, and neither should you. +`config/default` includes `../crd`, so reaching for `kubectl delete -k config/default` +takes the CRDs with it, and deleting those cascades to every AWX and Ascender in the +cluster, which is every deployment. diff --git a/hack/migrate-awx-to-ascender.sh b/hack/migrate-awx-to-ascender.sh index 9ea420c7..d30b260c 100755 --- a/hack/migrate-awx-to-ascender.sh +++ b/hack/migrate-awx-to-ascender.sh @@ -20,6 +20,9 @@ # garbage_collect_secrets defaults to false, and the database is renamed rather # than dumped and reloaded. # +# This is not the script that renames the operator's own objects. That is +# rename-operator-objects.sh, and README.md says which you want. +# # Usage: # ./hack/migrate-awx-to-ascender.sh -n # shows the plan # ./hack/migrate-awx-to-ascender.sh -n --apply # performs it diff --git a/hack/rename-operator-objects.sh b/hack/rename-operator-objects.sh index 8942127d..390e481a 100755 --- a/hack/rename-operator-objects.sh +++ b/hack/rename-operator-objects.sh @@ -17,6 +17,9 @@ # that, because config/default includes ../crd, and deleting those CRDs cascades # to every AWX and Ascender in the cluster, which is every deployment. # +# This is not the script that moves a deployment across. That is +# migrate-awx-to-ascender.sh, and README.md says which you want. +# # Usage: # ./hack/rename-operator-objects.sh -n # shows what it would delete # ./hack/rename-operator-objects.sh -n --apply # deletes it @@ -38,7 +41,7 @@ while [ $# -gt 0 ]; do --apply) APPLY=true; shift ;; --old-prefix) OLD_PREFIX="$2"; shift 2 ;; --new-prefix) NEW_PREFIX="$2"; shift 2 ;; - -h|--help) sed -n '3,20p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; + -h|--help) sed -n '3,25p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; *) die "unknown argument $1" ;; esac done