Compare commits

...

68 Commits

Author SHA1 Message Date
Christian Adams
d53c93c864 Document the need for quotes on pg secret port value 2023-03-15 13:35:49 -04:00
aurel333
fe77f1deae Fix awx-manager-role bad match between some apiGroups and resources (#1262)
Co-authored-by: Aurelien Potin <aurelien.potin@michelin.com>
2023-03-08 16:24:03 -05:00
Hao Liu
9156909023 Merge pull request #1263 from rh-dluong/date-format
change date format to avoid :
2023-03-08 15:58:58 -05:00
Christian Adams
03e3b68eeb Helm Release: Need to use shell, not command for multi-line (#1280) 2023-03-08 15:55:30 -05:00
Ravi Teja
6cae8dfa32 fix: use sha1 instead of md5 to make the installer work in FIPS (#1260)
Co-authored-by: RaviTeja Buddabathuni (rbuddaba) <rbuddaba@cisco.com>
2023-03-08 15:10:27 -05:00
Hao Liu
b1ecc89759 Merge pull request #1274 from TheRealHaoLiu/feature-branch-build
Add github workflow for publishing feature branch images
2023-03-07 17:05:08 -05:00
Hao Liu
bb85b31411 Add github workflow to publish feature branch img 2023-03-07 16:50:28 -05:00
Christian Adams
7e7c23e176 Fix helm-release task to work in ansible, not GHA env (#1267) 2023-03-06 15:25:04 -05:00
John Westcott IV
8442cc7ae8 Merge pull request #1268 from john-westcott-iv/ubuntu-change
Changing github actions from ubuntu-18 to ubuntu-latest
2023-03-06 15:06:02 -05:00
John Westcott IV
3faf76d7c9 Changing github actions from ubuntu-18 to ubuntu-latest
See https://github.com/actions/runner-images/issues/6002 for deprication warnings
2023-03-06 14:55:32 -05:00
David Luong
61ef5ef005 change date format to avoid : 2023-03-02 13:54:00 -05:00
Peter Hoffmann
86043361c6 Small fix/adjustment in README (#1257)
* Fix minor typo
2023-02-28 16:20:47 -05:00
Ravi Teja
06194960e9 Bump ansible operator SDK version to v1.26.0 (#1258)
Co-authored-by: RaviTeja Buddabathuni (rbuddaba) <rbuddaba@cisco.com>
2023-02-28 15:45:02 -05:00
Hao Liu
46da413585 Merge pull request #1193 from stanislav-zaprudskiy/add_termination_grace_period_seconds
AWX: Add `termination_grace_period_seconds`
2023-02-28 15:37:51 -05:00
Hao Liu
b5f255cf00 add nodeport_port to instantiate playbook (#1252) 2023-02-22 21:18:12 +00:00
Maxence Button
f328b0adb6 Customization of the init_projects_container_image is now possible (#1248) 2023-02-22 15:05:23 -05:00
Shane McDonald
1bfd3b2028 Merge pull request #1201 from thedoubl3j/debugging_doc_update
Update Installer Debugging Documentation
2023-02-22 14:51:05 -05:00
Christian Adams
edf01f009e Stage and push helm index where gitconfig is set (#1251) 2023-02-22 14:09:35 -05:00
Joel
a3c28be98a [enhancement] restore postgres database if external db (#1211)
* restore postgres fix resolvable_db_host

set the resolvable_db_host ansible variable based on the db deployment
2023-02-22 13:56:38 -05:00
Stanislav Zaprudskiy
49d1f00dbd Do not keep web container running during termination
We could let the web container terminate as usual, as there are no
reasons to keep it running as it doesn't participate in
job control. Additionally, it stops receiving traffic with the beginning
of termination

> At the same time as the kubelet is starting graceful shutdown, the
> control plane removes that shutting-down Pod from EndpointSlice (and
> Endpoints) objects where these represent a Service with a configured
> selector

@ https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-termination
2023-02-22 10:40:40 +01:00
Christian Adams
81939cb521 K8s cpu value example in README.md needs quotes to be valid (#1220) 2023-02-20 17:37:53 -05:00
Shane McDonald
6b10a8141c Merge pull request #1231 from rooftopcellist/auto-assign-nodeport 2023-02-09 20:27:26 -05:00
Christian M. Adams
1ef1f00b3d Auto-assign NodePort port by default rather than hardcoding a default value
- previously, there was no way to auto-assign a port by default
   which led to conflicts with other deployments at times
 - nodeport_port param can still be used to specify a port if desired
2023-02-09 19:49:46 -05:00
Stanislav Zaprudskiy
336ea58a0a AWX: Add termination_grace_period_seconds 2023-02-07 16:33:00 +01:00
Stanislav Zaprudskiy
f042cb3d00 Fix lint warnings 2023-02-07 16:31:26 +01:00
Stanislav Zaprudskiy
94d68bf382 Make Deployment to be rolled out on CM and Secrets changes
With the previous approach, not all associated (mounted) CM/Secrets
changes caused the Deployment to be rolled out, but also the Deployment
could have been rolled out unnecessary during e.g. Ingress or Service
changes (which do not require Pod restarts).

Previously existing Pod removal (state: absent) was not complete as
other pods continued to exist, but also is not needed with this commit
change due to added Pods annotations.

The added Deployment Pod annotations now cause the new ReplicaSet
version to be rolled out, effectively causing replacement of the
previously existing Pods in accordance with the deployment `strategy`
(https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#deploymentstrategy-v1-apps,
`RollingUpdate`) whenever there is a change in the associated CMs or
Secrets referenced in annotations. This implementation is quite standard
and widely used for Helm workflows -
https://helm.sh/docs/howto/charts_tips_and_tricks/#automatically-roll-deployments
2023-02-07 11:58:47 +01:00
Stanislav Zaprudskiy
b3a74362af Make AWX Pod variable to be calculated respecting creationTimestamp and deletionTimestamp
Do not consider Pods marked for deletion when calculating tower_pod to
address replicas scale down case - where normally Pods spawned recently
are being taken for removal. As well as the case when operator kicked
off but some old replicas are still terminating.

Respect `creationTimestamp` so to make sure that the newest Pod is taken
after Deployment application, in which case multiple RS Pods (from old
RS and new RS) could be running simultaneously while the rollout is
happening.
2023-02-07 11:47:49 +01:00
Stanislav Zaprudskiy
ad531c8dce Do not wait for a new Pod name after Deployment change
Proper waiting is already performed earlier during Deplyment{apply: yes, wait: yes} -
e6ac874098/plugins/module_utils/k8s/waiter.py (L27).

And also not every Deployment change produces new RS/Pods. For example,
changing Deployment labels won't cause new rollout, but will cause
`until` loop to be invoked unnecessarily (when replicas=1).
2023-02-07 11:43:34 +01:00
Stanislav Zaprudskiy
e589ceb661 When applying Deployment wait up to (timeout * replicas)
There are cases when having a new Deployment may be taking above the
default timeout of 120s.
For instance, when a Deployment has multiple replicas, and each replica
starts on a separate node, and the Deployment specifies new images, then
just pulling these new images for each replica may be taking above the
default timeout of 120s.

Having the default time multiplied by the number of replicas should
provide generally enough time for all replicas to start
2023-02-07 11:41:32 +01:00
Christian Adams
234a10d185 Add a more descriptive AWX CRD description (#1208) 2023-01-31 10:50:12 -05:00
Christian Adams
e0fa1610ca Fix minor typos in test task names (#1209) 2023-01-31 00:05:37 +00:00
Stanislav Zaprudskiy
5a856eeba8 Add additional_labels parameter (#1160)
* Move label templates into `common` role

So that there is single source of labels management, and labels are
unified across the other roles

* Introduce `additional_labels`
* Fix paths for labels templates
* Return `additional_labels_items` as list
* Add molecule tests
2023-01-30 18:51:08 -05:00
Christian Adams
d26a6bf641 Fix minor helm make target syntax error (#1206) 2023-01-27 09:56:40 -05:00
janorn
8d6db0934f Fix helm chart URLs (#1204) 2023-01-27 09:32:46 -05:00
John Westcott IV
c1b2e73123 Merge pull request #1202 from john-westcott-iv/security_requested_change
Nominal change to the pr body check
2023-01-25 17:29:50 -05:00
John Westcott IV
6ac8668ead Nominal change to the pr body check 2023-01-25 17:12:08 -05:00
thedoubl3j
d0516bcdc1 update vars file entry to include over ride examples 2023-01-25 14:41:18 -05:00
Christian Adams
ac5a2538bc Fix helm index.yaml generation error (#1199) 2023-01-24 21:40:42 -05:00
Christian Adams
2bdc87d0a8 Add docs for ingress resource apiVersion configuration (#1198) 2023-01-24 22:42:02 +00:00
Joel
ab566c2530 backup role use k8s_cp module to write files (#1111) 2023-01-24 17:02:05 -05:00
rakesh561
6a7c3ca2de Add docs for disabling IPv6
* Added procedure to disable ipv6 for AWX deployments when needed.
2023-01-24 04:26:25 +00:00
Miles
f6f58d5c6d Setup make index for testing (#1183)
- Reconfigure index file generation
- checkout gh-pages branch in promote.yaml
- fix helm-index make target
- add gh-pages folder in .gitignore

Signed-off-by: Miles Wilson <wilson.mil@icloud.com>
Co-authored-by: Hao Liu <haoli@redhat.com>
Co-authored-by: Christian Adams <rooftopcellist@gmail.com>
2023-01-18 17:53:57 -05:00
kurokobo
b1a547d2a6 fix: add quotes for PGPASSWORD in upgrade_postgres.yml (fixes #1166) (#1167) 2023-01-18 11:59:03 -05:00
Christian Adams
43f98eda77 Fix pr_body_check (#1187) 2023-01-11 22:16:58 -05:00
Christian Adams
007e47e35c Add pre-reqs to the backup and restore docs (#1186) 2023-01-11 17:18:49 -05:00
David Hageman
21eb83b052 Correct admin password updating (#1179)
Corrects an issue with admin passwords failing to be updated due to shell escaping. This aligns the operator with the logic in the normal installer.
2023-01-11 11:41:35 -05:00
Dimitri Savineau
1f8414b8cb molecule: set no_log to false (#1185)
In order to get information during CI debugging then turning off the
no_log statement will help with non hidden output.

FAILED! => {"censored": "the output has been hidden due to the fact that
            'no_log: true' was specified for this result"}

Signed-off-by: Dimitri Savineau <dsavinea@redhat.com>
2023-01-11 11:21:53 -05:00
Shane McDonald
a199a8c104 Merge pull request #1172 from orcema/orcema-patch-readme-CA
update Trusting a Custom Certificate Authority
2023-01-04 13:18:37 +08:00
Orce MARINKOVSKI
fb1639a5d4 update Trusting a Custom Certificate Authority
I had hard times to identify how to declare to include statement for a custom certifcate bundle within the Kustomize file.
The tricky part for me was to spot the option "disableNameSuffixHash: true" in order to avoid renaming the secret name with an has suffix
2022-12-27 10:43:56 +01:00
Christian Adams
19c6c77c41 Add ability to configmap and include config files for nginx.conf (#1145) 2022-12-16 10:36:29 -05:00
Mathijsvw
4ee523ed69 Added pg_dump_suffix doc and crd property (#1006)
* Added pg_dump_suffix crd property

Signed-off-by: Mathijs van Willigen <mathijs.vanwilligen@student.hu.nl>
2022-12-14 14:56:40 -05:00
Stanislav Zaprudskiy
4c5bae69ef Add possibility specifying affinity of AWX Pods. (#1139)
* Add an option to specify affinity rules for the awx pod

In some cases, you may want to use affinity rules instead of a
 node selector so you can have more flexbility. For example if you want
to have "soft" rules i.e. run my pod on this node if possible otherwise
run it anywhere

* Rename `node_affinity` to `affinity`
* Maintain defaults and CSV
* Add fields validation

Co-authored-by: Olivier <oliverf1ca@yahoo.com>
2022-12-14 10:39:41 -05:00
Dax Kelson
9bf896e37c Update README.md (#1147)
Use awx namespace whent getting AWX instance URL
2022-12-07 14:31:34 -05:00
Christian Adams
cb50f4f3ac Add docs for Openshift install configuration (#1140) 2022-12-07 14:18:06 -05:00
Christian Adams
066e55e179 Bump Ansible Operator SDK version to v1.25.3 (#1146) 2022-12-05 16:04:04 -05:00
dru90i
f35bd7cf83 add default cluster name (#1129)
* add default cluster name

* Update awx.ansible.com_awxrestores.yaml

Co-authored-by: Kirill Smirnov <kirilsmirnov@x5.ru>
2022-11-30 14:29:52 -05:00
Karsten Heymann
95a1fc082b README.md: Add newline after decoded passwords (#1137)
`base64 --decode` prints no new line after its output, resulting in the next shell prompt starting immediately after the password. This makes copying the password difficult. Adding an `echo` at the end of the command fixes this.
2022-11-30 15:59:38 +00:00
dale-mittleman
dbd1e59a55 Added flag to disable ipv6 listener (#1135) 2022-11-30 10:38:44 -05:00
Christian Adams
61f45147f6 Set Minimum K8s cluster version we expect to work (#1126) 2022-11-18 10:27:47 -05:00
Michael Cristina
c20f9b4128 fix default container name (#1048) 2022-11-16 20:33:50 -05:00
Seth Foster
15568fe564 docs show extra setting double quotes (#1123) 2022-11-16 20:25:28 -05:00
Florian LAUNAY
1baf417504 remove helm chart version from labels (#1114) 2022-11-15 16:32:33 -05:00
janorn
7fbf1c42aa Get tags from remote. Local copy not complete. (#1075) 2022-11-10 00:25:51 -05:00
Christian Adams
a5e21b56ae Backup and restore receptor tls secret with expected generated name (#1107) 2022-11-07 11:04:22 -05:00
John Westcott IV
1399504142 Adding community labels on PRs and Issues (#1108) 2022-11-04 11:55:47 -04:00
Shane McDonald
e5896d15ed Merge pull request #1078 from FlorianLaunay/devel
use dedicated image for projects persistence PVC init tasks
2022-11-03 19:37:58 -04:00
Christian Adams
6b01ff09ce Amend permissions for backup dir (#1109) 2022-11-03 12:52:48 -04:00
Florian LAUNAY
c708cef4dc use dedicated image for projects persistence PVC init tasks 2022-11-03 17:26:00 +01:00
77 changed files with 1584 additions and 336 deletions

View File

@@ -11,7 +11,7 @@ on:
jobs:
molecule:
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
name: molecule
env:
DOCKER_API_VERSION: "1.38"
@@ -40,7 +40,7 @@ jobs:
make kustomize
KUSTOMIZE_PATH=$(readlink -f bin/kustomize) molecule test -s kind
helm:
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
name: helm
steps:
- uses: actions/checkout@v2

View File

@@ -8,7 +8,7 @@ on:
jobs:
release:
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
name: Push devel image
steps:
- uses: actions/checkout@v2

75
.github/workflows/feature.yml vendored Normal file
View File

@@ -0,0 +1,75 @@
---
name: Feature Branch Image Build and Push
on:
push:
branches: [feature_*]
jobs:
release:
runs-on: ubuntu-18.04
name: Push devel image
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # needed so that git describe --tag works
- name: Set VERSION
run: |
echo "VERSION=$(git describe --tags)" >>${GITHUB_ENV}
- name: Set lower case owner name
run: |
echo "OWNER_LC=${OWNER,,}" >>${GITHUB_ENV}
env:
OWNER: '${{ github.repository_owner }}'
- name: Set IMAGE_TAG_BASE
run: |
echo "IMAGE_TAG_BASE=ghcr.io/${OWNER_LC}/awx-operator" >>${GITHUB_ENV}
- name: Set ARCH environment variable
run: |
echo "ARCH=$(case $(uname -m) in x86_64) echo -n amd64 ;; aarch64) echo -n arm64 ;; *) echo -n $(uname -m) ;; esac)" >>${GITHUB_ENV}
- name: Set OS environment variable
run: |
echo "OS=$(uname | awk '{print tolower($0)}')" >>${GITHUB_ENV}
- name: Install operator-sdk
run: |
echo "Installing operator-sdk ${OPERATOR_SDK_DL_URL}" && \
curl -LO ${OPERATOR_SDK_DL_URL}/operator-sdk_${OS}_${ARCH} && \
chmod +x operator-sdk_${OS}_${ARCH} && \
sudo mkdir -p /usr/local/bin/ && \
sudo mv operator-sdk_${OS}_${ARCH} /usr/local/bin/operator-sdk && \
operator-sdk version
env:
OPERATOR_SDK_DL_URL: https://github.com/operator-framework/operator-sdk/releases/download/v1.26.0
- name: Log in to registry
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Build and Push awx-operator Image
run: |
make docker-build docker-push
docker tag ${IMAGE_TAG_BASE}:${VERSION} ${IMAGE_TAG_BASE}:${GITHUB_REF##*/}
docker push ${IMAGE_TAG_BASE}:${GITHUB_REF##*/}
- name: Build bundle manifests
run: |
make bundle
- name: Build and Push awx-operator Bundle
run: |
make bundle-build bundle-push
docker tag ${IMAGE_TAG_BASE}-bundle:v${VERSION} ${IMAGE_TAG_BASE}-bundle:${GITHUB_REF##*/}
docker push ${IMAGE_TAG_BASE}-bundle:${GITHUB_REF##*/}
- name: Build and Push awx-operator Catalog
run: |
make catalog-build catalog-push
docker tag ${IMAGE_TAG_BASE}-catalog:v${VERSION} ${IMAGE_TAG_BASE}-catalog:${GITHUB_REF##*/}
docker push ${IMAGE_TAG_BASE}-catalog:${GITHUB_REF##*/}

54
.github/workflows/label_issue.yml vendored Normal file
View File

@@ -0,0 +1,54 @@
---
name: Label Issues
on:
issues:
types:
- opened
- reopened
jobs:
triage:
runs-on: ubuntu-latest
name: Label
steps:
- name: Label Issue - Needs Triage
uses: github/issue-labeler@v2.4.1
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
not-before: 2021-12-07T07:00:00Z
configuration-path: .github/issue_labeler.yml
enable-versioned-regex: 0
if: github.event_name == 'issues'
community:
runs-on: ubuntu-latest
name: Label Issue - Community
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v4
- name: Install python requests
run: pip install requests
- name: Check if user is a member of Ansible org
uses: jannekem/run-python-script-action@v1
id: check_user
with:
script: |
import requests
headers = {'Accept': 'application/vnd.github+json', 'Authorization': 'token ${{ secrets.GITHUB_TOKEN }}'}
response = requests.get('${{ fromJson(toJson(github.event.issue.user.url)) }}/orgs?per_page=100', headers=headers)
is_member = False
for org in response.json():
if org['login'] == 'ansible':
is_member = True
if is_member:
print("User is member")
else:
print("User is community")
- name: Add community label if not a member
if: contains(steps.check_user.outputs.stdout, 'community')
uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90
with:
add-labels: "community"
repo-token: ${{ secrets.GITHUB_TOKEN }}

40
.github/workflows/label_pr.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: Label PR
on:
pull_request_target:
types:
- opened
- reopened
- synchronize
jobs:
community:
runs-on: ubuntu-latest
name: Label PR - Community
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v4
- name: Install python requests
run: pip install requests
- name: Check if user is a member of Ansible org
uses: jannekem/run-python-script-action@v1
id: check_user
with:
script: |
import requests
headers = {'Accept': 'application/vnd.github+json', 'Authorization': 'token ${{ secrets.GITHUB_TOKEN }}'}
response = requests.get('${{ fromJson(toJson(github.event.pull_request.user.url)) }}/orgs?per_page=100', headers=headers)
is_member = False
for org in response.json():
if org['login'] == 'ansible':
is_member = True
if is_member:
print("User is member")
else:
print("User is community")
- name: Add community label if not a member
if: contains(steps.check_user.outputs.stdout, 'community')
uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90
with:
add-labels: "community"
repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -13,21 +13,13 @@ jobs:
packages: write
contents: read
steps:
- name: Write PR body to a file
run: |
cat >> pr.body << __SOME_RANDOM_PR_EOF__
${{ github.event.pull_request.body }}
__SOME_RANDOM_PR_EOF__
- name: Display the received body for troubleshooting
run: cat pr.body
# We want to write these out individually just incase the options were joined on a single line
- name: Check for each of the lines
env:
PR_BODY: ${{ github.event.pull_request.body }}
run: |
grep "Bug, Docs Fix or other nominal change" pr.body > Z
grep "New or Enhanced Feature" pr.body > Y
grep "Breaking Change" pr.body > X
echo "$PR_BODY" | grep "Bug, Docs Fix or other nominal change" > Z
echo "$PR_BODY" | grep "New or Enhanced Feature" > Y
echo "$PR_BODY" | grep "Breaking Change" > X
exit 0
# We exit 0 and set the shell to prevent the returns from the greps from failing this step
# See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#exit-codes-and-error-action-preference

View File

@@ -8,7 +8,14 @@ jobs:
promote:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
depth: 0
- uses: actions/checkout@v3
with:
ref: gh-pages
path: gh-pages
- name: Log in to GHCR
run: |
@@ -26,11 +33,6 @@ jobs:
docker push quay.io/${{ github.repository }}:${{ github.event.release.tag_name }}
docker push quay.io/${{ github.repository }}:latest
- name: Configure git
run: |
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Release Helm chart
run: |
ansible-playbook ansible/helm-release.yml -v \
@@ -38,3 +40,4 @@ jobs:
-e chart_owner=${{ github.repository_owner }} \
-e tag=${{ github.event.release.tag_name }} \
-e gh_token=${{ secrets.GITHUB_TOKEN }}
-e gh_user=${{ github.actor }}

View File

@@ -1,22 +0,0 @@
---
name: Triage
on:
issues:
types:
- opened
jobs:
triage:
runs-on: ubuntu-latest
name: Label
steps:
- name: Label issues
uses: github/issue-labeler@v2.4.1
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
not-before: 2021-12-07T07:00:00Z
configuration-path: .github/issue_labeler.yml
enable-versioned-regex: 0
if: github.event_name == 'issues'

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
*~
gh-pages/
.cache/
/bin
/bundle

View File

@@ -1,6 +1,6 @@
# AWX Operator Helm Chart
This chart installs the AWX Operator resources configured in [this](https://github.com/ansible/awx-operator) repository.
This chart installs the AWX Operator resources configured in [this](https://github.com/ansible/awx-operator) repository.
## Getting Started
To configure your AWX resource using this chart, create your own `yaml` values file. The name is up to personal preference since it will explicitly be passed into the helm chart. Helm will merge whatever values you specify in your file with the default `values.yaml`, overriding any settings you've changed while allowing you to fall back on defaults. Because of this functionality, `values.yaml` should not be edited directly.
@@ -8,7 +8,7 @@ To configure your AWX resource using this chart, create your own `yaml` values f
In your values config, enable `AWX.enabled` and add `AWX.spec` values based on the awx operator's [documentation](https://github.com/ansible/awx-operator/blob/devel/README.md). Consult the docs below for additional functionality.
### Installing
The operator's [helm install](https://github.com/ansible/awx-operator/blob/devel/README.md#helm-install-on-existing-cluster) guide provides key installation instructions.
The operator's [helm install](https://github.com/ansible/awx-operator/blob/devel/README.md#helm-install-on-existing-cluster) guide provides key installation instructions.
Example:
```
@@ -27,10 +27,10 @@ To update an existing installation, use `helm upgrade` instead of `install`. The
## Configuration
The goal of adding helm configurations is to abstract out and simplify the creation of multi-resource configs. The `AWX.spec` field maps directly to the spec configs of the `AWX` resource that the operator provides, which are detailed in the [main README](https://github.com/ansible/awx-operator/blob/devel/README.md). Other sub-config can be added with the goal of simplifying more involved setups that require additional resources to be specified.
These sub-headers aim to be a more intuitive entrypoint into customizing your deployment, and are easier to manage in the long-term. By design, the helm templates will defer to the manually defined specs to avoid configuration conflicts. For example, if `AWX.spec.postgres_configuration_secret` is being used, the `AWX.postgres` settings will not be applied, even if enabled.
These sub-headers aim to be a more intuitive entrypoint into customizing your deployment, and are easier to manage in the long-term. By design, the helm templates will defer to the manually defined specs to avoid configuration conflicts. For example, if `AWX.spec.postgres_configuration_secret` is being used, the `AWX.postgres` settings will not be applied, even if enabled.
### External Postgres
The `AWX.postgres` section simplifies the creation of the external postgres secret. If enabled, the configs provided will automatically be placed in a `postgres-config` secret and linked to the `AWX` resource. For proper secret management, the `AWX.postgres.password` value, and any other sensitive values, can be passed in at the command line rather than specified in code. Use the `--set` argument with `helm install`.
The `AWX.postgres` section simplifies the creation of the external postgres secret. If enabled, the configs provided will automatically be placed in a `postgres-config` secret and linked to the `AWX` resource. For proper secret management, the `AWX.postgres.password` value, and any other sensitive values, can be passed in at the command line rather than specified in code. Use the `--set` argument with `helm install`. Supplying the password this way is not recommended for production use, but may be helpful for initial PoC.
## Values Summary
@@ -44,13 +44,24 @@ The `AWX.postgres` section simplifies the creation of the external postgres secr
| `AWX.postgres` | configurations for the external postgres secret | - |
# Contributing
# Contributing
## Adding abstracted sections
Where possible, defer to `AWX.spec` configs before applying the abstracted configs to avoid collision. This can be facilitated by the `(hasKey .spec what_i_will_abstract)` check.
Where possible, defer to `AWX.spec` configs before applying the abstracted configs to avoid collision. This can be facilitated by the `(hasKey .spec what_i_will_abstract)` check.
## Building and Testing
This chart is built using the Makefile in the [awx-operator repo](https://github.com/ansible/awx-operator). Clone the repo and run `make helm-chart`. This will create the awx-operator chart in the `charts/awx-operator` directory. In this process, the contents of the `.helm/starter` directory will be added to the chart.
## Future Goals
All values under the `AWX` header are focused on configurations that use the operator. Configurations that relate to the Operator itself could be placed under an `Operator` heading, but that may add a layer of complication over current development.
# Chart Publishing
The chart is currently hosted on the gh-pages branch of the repo. During the release pipeline, the `index.yaml` stored in that branch is generated with helm chart entries from all valid tags. We are currently unable to use the `chart-releaser` pipeline due to the fact that the complete helm chart is not committed to the repo and is instead built during the release process. Therefore, the cr action is unable to compare against previous versions.
Instead of CR, we use `helm repo index` to generate an index from all locally pulled chart versions. Since we build from scratch every time, the timestamps of all entries will be updated. This could be improved by using yq or something similar to detect which tags are already in the index.yaml file, and only merge in tags that are not present.
Not using CR could be addressed in the future by keeping the chart built as a part of releases, as long as CR compares the chart to previous release packages rather than previous commits. If the latter is the case, then we would not have the necessary history for comparison.

View File

@@ -1,4 +1,4 @@
FROM quay.io/operator-framework/ansible-operator:v1.23.0
FROM quay.io/operator-framework/ansible-operator:v1.26.0
USER 0

View File

@@ -140,7 +140,7 @@ ifeq (,$(shell which kustomize 2>/dev/null))
@{ \
set -e ;\
mkdir -p $(dir $(KUSTOMIZE)) ;\
curl -sSLo - https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v4.5.5/kustomize_v4.5.5_$(OS)_$(ARCHA).tar.gz | \
curl -sSLo - https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v4.5.7/kustomize_v4.5.7_$(OS)_$(ARCHA).tar.gz | \
tar xzf - -C bin/ ;\
}
else
@@ -156,7 +156,7 @@ ifeq (,$(shell which ansible-operator 2>/dev/null))
@{ \
set -e ;\
mkdir -p $(dir $(ANSIBLE_OPERATOR)) ;\
curl -sSLo $(ANSIBLE_OPERATOR) https://github.com/operator-framework/operator-sdk/releases/download/v1.23.0/ansible-operator_$(OS)_$(ARCHA) ;\
curl -sSLo $(ANSIBLE_OPERATOR) https://github.com/operator-framework/operator-sdk/releases/download/v1.26.0/ansible-operator_$(OS)_$(ARCHA) ;\
chmod +x $(ANSIBLE_OPERATOR) ;\
}
else
@@ -187,7 +187,7 @@ ifeq (,$(shell which opm 2>/dev/null))
@{ \
set -e ;\
mkdir -p $(dir $(OPM)) ;\
curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.23.0/$(OS)-$(ARCHA)-opm ;\
curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.26.0/$(OS)-$(ARCHA)-opm ;\
chmod +x $(OPM) ;\
}
else
@@ -296,8 +296,8 @@ helm-chart: helm-chart-generate
helm-chart-generate: kustomize helm kubectl-slice yq charts
@echo "== KUSTOMIZE: Set image and chart label =="
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
cd config/manager && $(KUSTOMIZE) edit set label helm.sh/chart:$(CHART_NAME)-$(VERSION)
cd config/default && $(KUSTOMIZE) edit set label helm.sh/chart:$(CHART_NAME)-$(VERSION)
cd config/manager && $(KUSTOMIZE) edit set label helm.sh/chart:$(CHART_NAME)
cd config/default && $(KUSTOMIZE) edit set label helm.sh/chart:$(CHART_NAME)
@echo "== Gather Helm Chart Metadata =="
# remove the existing chart if it exists
@@ -350,43 +350,48 @@ helm-chart-generate: kustomize helm kubectl-slice yq charts
.PHONY: helm-package
helm-package: cr helm-chart
@echo "== CHART RELEASER (package) =="
$(CR) package ./charts/awx-operator
helm-package: helm-chart
@echo "== Package Current Chart Version =="
mkdir -p .cr-release-packages
# package the chart and put it in .cr-release-packages dir
$(HELM) package ./charts/awx-operator -d .cr-release-packages
# List all tags oldest to newest.
TAGS := $(shell git tag -l --sort=creatordate)
TAGS := $(shell git ls-remote --tags --sort=version:refname --refs -q | cut -d/ -f3)
# The actual release happens in ansible/helm-release.yml
# until https://github.com/helm/chart-releaser/issues/122 happens
# The actual release happens in ansible/helm-release.yml, which calls this targer
# until https://github.com/helm/chart-releaser/issues/122 happens, chart-releaser is not ideal for a chart
# that is contained within a larger repo, where a tag may not require a new chart version
.PHONY: helm-index
helm-index: cr helm-chart
@echo "== CHART RELEASER (httpsorigin) =="
git remote add httpsorigin "https://github.com/$(CHART_OWNER)/$(CHART_REPO).git"
git fetch httpsorigin
helm-index:
# when running in CI this gh-pages are already checked out with github action to 'gh-pages' directory
# TODO: test if gh-pages directory exists and if not exist
@echo "== GENERATE INDEX FILE =="
# This step to workaround issues with old releases being dropped.
# Until https://github.com/helm/chart-releaser/issues/133 happens
@echo "== CHART FETCH previous releases =="
# Download all old releases
cd .cr-release-packages;\
mkdir -p .cr-release-packages
for tag in $(TAGS); do\
dl_url="https://github.com/$${CHART_OWNER}/$${CHART_REPO}/releases/download/$${tag}/$${CHART_REPO}-$${tag}.tgz";\
curl -RLOs -z "$${CHART_REPO}-$${tag}.tgz" --fail $${dl_url};\
dl_url="https://github.com/$(CHART_OWNER)/$(CHART_REPO)/releases/download/$${tag}/$(CHART_REPO)-$${tag}.tgz";\
echo "Downloading $${tag} from $${dl_url}";\
curl -RLOs -z "$(CHART_REPO)-$${tag}.tgz" --fail $${dl_url};\
result=$$?;\
if [ $${result} -eq 0 ]; then\
echo "Downloaded $$dl_url";\
echo "Downloaded $${dl_url}";\
mkdir -p .cr-release-packages/$${tag};\
mv ./$(CHART_REPO)-$${tag}.tgz .cr-release-packages/$${tag};\
else\
echo "Skipping release $${tag}; No helm chart present";\
rm -rf "$(CHART_REPO)-$${tag}.tgz";\
fi;\
done
done;\
@echo "== CHART RELEASER (index) =="
$(CR) index \
--owner "$(CHART_OWNER)" \
--git-repo "$(CHART_REPO)" \
--token "$(CR_TOKEN)" \
--pages-branch "$(CHART_BRANCH)" \
--index-path "./charts/$(CHART_INDEX)" \
--charts-repo "https://$(CHART_OWNER).github.io/$(CHART_REPO)/$(CHART_INDEX)" \
--remote httpsorigin \
--release-name-template="{{ .Version }}" \
--push
# generate the index file in the root of the gh-pages branch
# --merge will leave any values in index.yaml that don't get generated by this command, but
# it is likely that all values are overridden
$(HELM) repo index .cr-release-packages --url https://github.com/$(CHART_OWNER)/$(CHART_REPO)/releases/download/ --merge gh-pages/index.yaml
mv .cr-release-packages/index.yaml gh-pages/index.yaml

205
README.md
View File

@@ -48,6 +48,8 @@ An [Ansible AWX](https://github.com/ansible/awx) operator for Kubernetes built w
* [Auto Upgrade](#auto-upgrade)
* [Upgrade of instances without auto upgrade](#upgrade-of-instances-without-auto-upgrade)
* [Service Account](#service-account)
* [Labeling operator managed objects](#labeling-operator-managed-objects)
* [Pods termination grace period](#pods-termination-grace-period)
* [Uninstall](#uninstall)
* [Upgrading](#upgrading)
* [Backup](#backup)
@@ -55,6 +57,7 @@ An [Ansible AWX](https://github.com/ansible/awx) operator for Kubernetes built w
* [Cluster-scope to Namespace-scope considerations](#cluster-scope-to-namespace-scope-considerations)
* [Project is now based on v1.x of the operator-sdk project](#project-is-now-based-on-v1x-of-the-operator-sdk-project)
* [Steps to upgrade](#steps-to-upgrade)
* [Disable IPV6](#disable-ipv6)
* [Add Execution Nodes](#adding-execution-nodes)
* [Custom Receptor CA](#custom-receptor-ca)
* [Contributing](#contributing)
@@ -197,12 +200,24 @@ metadata:
name: awx-demo
spec:
service_type: nodeport
# default nodeport_port is 30080
nodeport_port: <nodeport_port>
```
> It may make sense to create and specify your own secret key for your deployment so that if the k8s secret gets deleted, it can be re-created if needed. If it is not provided, one will be auto-generated, but cannot be recovered if lost. Read more [here](#secret-key-configuration).
If you are on Openshift, you can take advantage of Routes by specifying the following your spec. This will automatically create a Route for you with a custom hostname. This can be found on the Route section of the Openshift Console.
```yaml
---
apiVersion: awx.ansible.com/v1beta1
kind: AWX
metadata:
name: awx-demo
spec:
service_type: clusterip
ingress_type: Route
```
Make sure to add this new file to the list of "resources" in your `kustomization.yaml` file:
```yaml
@@ -243,19 +258,19 @@ awx-demo-service NodePort 10.109.40.38 <none> 80:31006/TCP 3m56
Once deployed, the AWX instance will be accessible by running:
```
$ minikube service awx-demo-service --url
$ minikube service -n awx awx-demo-service --url
```
By default, the admin user is `admin` and the password is available in the `<resourcename>-admin-password` secret. To retrieve the admin password, run:
```
$ kubectl get secret awx-demo-admin-password -o jsonpath="{.data.password}" | base64 --decode
$ kubectl get secret awx-demo-admin-password -o jsonpath="{.data.password}" | base64 --decode ; echo
yDL2Cx5Za94g9MvBP6B73nzVLlmfgPjR
```
You just completed the most basic install of an AWX instance via this operator. Congratulations!!!
For an example using the Nginx Controller in Minukube, don't miss our [demo video](https://asciinema.org/a/416946).
For an example using the Nginx Ingress Controller in Minikube, don't miss our [demo video](https://asciinema.org/a/416946).
### Helm Install on existing cluster
@@ -303,7 +318,7 @@ There are three variables that are customizable for the admin user account creat
If `admin_password_secret` is not provided, the operator will look for a secret named `<resourcename>-admin-password` for the admin password. If it is not present, the operator will generate a password and create a Secret from it named `<resourcename>-admin-password`.
To retrieve the admin password, run `kubectl get secret <resourcename>-admin-password -o jsonpath="{.data.password}" | base64 --decode`
To retrieve the admin password, run `kubectl get secret <resourcename>-admin-password -o jsonpath="{.data.password}" | base64 --decode ; echo`
The secret that is expected to be passed should be formatted as follow:
@@ -448,6 +463,7 @@ The following variables are customizable when `ingress_type=ingress`. The `ingre
| hostname | Define the FQDN | {{ meta.name }}.example.com |
| ingress_path | Define the ingress path to the service | / |
| ingress_path_type | Define the type of the path (for LBs) | Prefix |
| ingress_api_version | Define the Ingress resource apiVersion | 'networking.k8s.io/v1' |
```yaml
---
@@ -468,6 +484,7 @@ The following variables are customizable when `ingress_type=route`
| route_host | Common name the route answers for | `<instance-name>-<namespace>-<routerCanonicalHostname>` |
| route_tls_termination_mechanism | TLS Termination mechanism (Edge, Passthrough) | Edge |
| route_tls_secret | Secret that contains the TLS information | Empty string |
| route_api_version | Define the Route resource apiVersion | 'route.openshift.io/v1' |
```yaml
---
@@ -558,7 +575,7 @@ spec:
cpu: 500m
memory: 2Gi
limits:
cpu: 1
cpu: '1'
memory: 4Gi
postgres_storage_requirements:
requests:
@@ -704,18 +721,20 @@ You can constrain the AWX pods created by the operator to run on a certain subse
the AWX pods to run only on the nodes that match all the specified key/value pairs. `tolerations` and `postgres_tolerations` allow the AWX
pods to be scheduled onto nodes with matching taints.
The ability to specify topologySpreadConstraints is also allowed through `topology_spread_constraints`
If you want to use affinity rules for your AWX pod you can use the `affinity` option.
| Name | Description | Default |
| --------------------------- | ----------------------------------- | ------- |
| postgres_image | Path of the image to pull | postgres |
| postgres_image_version | Image version to pull | 13 |
| node_selector | AWX pods' nodeSelector | '' |
| topology_spread_constraints | AWX pods' topologySpreadConstraints | '' |
| tolerations | AWX pods' tolerations | '' |
| annotations | AWX pods' annotations | '' |
| postgres_selector | Postgres pods' nodeSelector | '' |
| postgres_tolerations | Postgres pods' tolerations | '' |
| Name | Description | Default |
| --------------------------- | ----------------------------------- | ------- |
| postgres_image | Path of the image to pull | postgres |
| postgres_image_version | Image version to pull | 13 |
| node_selector | AWX pods' nodeSelector | '' |
| topology_spread_constraints | AWX pods' topologySpreadConstraints | '' |
| affinity | AWX pods' affinity rules | '' |
| tolerations | AWX pods' tolerations | '' |
| annotations | AWX pods' annotations | '' |
| postgres_selector | Postgres pods' nodeSelector | '' |
| postgres_tolerations | Postgres pods' tolerations | '' |
Example of customization could be:
@@ -748,6 +767,28 @@ spec:
operator: "Equal"
value: "AWX"
effect: "NoSchedule"
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key
operator: In
values:
- another-node-label-value
- another-node-label-value
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
topologyKey: topology.kubernetes.io/zone
```
#### Trusting a Custom Certificate Authority
@@ -775,7 +816,22 @@ spec:
bundle_cacert_secret: <resourcename>-custom-certs
```
To create the secrets, you can use the commands below:
Create the secret with `kustomization.yaml` file:
```yaml
....
secretGenerator:
- name: <resourcename>-custom-certs
files:
- bundle-ca.crt=<path+filename>
options:
disableNameSuffixHash: true
...
```
Create the secret with CLI:
* Certificate Authority secret
@@ -957,6 +1013,17 @@ Example spec file for volumes and volume mounts
> :warning: **Volume and VolumeMount names cannot contain underscores(_)**
##### Custom Nginx Configuration
Using the [extra_volumes feature](#custom-volume-and-volume-mount-options), it is possible to extend the nginx.conf.
1. Create a ConfigMap with the extra settings you want to include in the nginx.conf
2. Create an extra_volumes entry in the AWX spec for this ConfigMap
3. Create an web_extra_volume_mounts entry in the AWX spec to mount this volume
The AWX nginx config automatically includes /etc/nginx/conf.d/*.conf if present.
#### Default execution environments from private registries
In order to register default execution environments from private registries, the Custom Resource needs to know about the pull credentials. Those credentials should be stored as a secret and either specified as `ee_pull_credentials_secret` at the CR spec level, or simply be present on the namespace under the name `<resourcename>-ee-pull-credentials` . Instance initialization will register a `Container registry` type credential on the deployed instance and assign it to the registered default execution environments.
@@ -1076,8 +1143,14 @@ Example configuration of `extra_settings` parameter
- setting: AUTH_LDAP_BIND_DN
value: "cn=admin,dc=example,dc=com"
- setting: LOG_AGGREGATOR_LEVEL
value: "'DEBUG'"
```
Note for some settings, such as `LOG_AGGREGATOR_LEVEL`, the value may need double quotes.
#### No Log
Configure no_log for tasks with no_log
@@ -1145,6 +1218,74 @@ Example configuration of environment variables
eks.amazonaws.com/role-arn: arn:aws:iam::<ACCOUNT_ID>:role/<IAM_ROLE_NAME>
```
#### Labeling operator managed objects
In certain situations labeling of Kubernetes objects managed by the operator
might be desired (e.g. for owner identification purposes). For that
`additional_labels` parameter could be used
| Name | Description | Default |
| --------------------------- | ---------------------------------------------------------------------------------------- | ------- |
| additional_labels | Additional labels defined on the resource, which should be propagated to child resources | [] |
Example configuration where only `my/team` and `my/service` labels will be
propagated to child objects (`Deployment`, `Secret`s, `ServiceAccount`, etc):
```yaml
apiVersion: awx.ansible.com/v1beta1
kind: AWX
metadata:
name: awx-demo
labels:
my/team: "foo"
my/service: "bar"
my/do-not-inherit: "yes"
spec:
additional_labels:
- my/team
- my/service
...
```
#### Pods termination grace period
During deployment restarts or new rollouts, when old ReplicaSet Pods are being
terminated, the corresponding jobs which are managed (executed or controlled)
by old AWX Pods may end up in `Error` state as there is no mechanism to
transfer them to the newly spawned AWX Pods. To work around the problem one
could set `termination_grace_period_seconds` in AWX spec, which does the
following:
* It sets the corresponding
[`terminationGracePeriodSeconds`](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-termination)
Pod spec of the AWX Deployment to the value provided
> The grace period is the duration in seconds after the processes running in
> the pod are sent a termination signal and the time when the processes are
> forcibly halted with a kill signal
* It adds a
[`PreStop`](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#hook-handler-execution)
hook script, which will keep AWX Pods in terminating state until it finished,
up to `terminationGracePeriodSeconds`.
> This grace period applies to the total time it takes for both the PreStop
> hook to execute and for the Container to stop normally
While the hook script just waits until the corresponding AWX Pod (instance)
no longer has any managed jobs, in which case it finishes with success and
hands over the overall Pod termination process to normal AWX processes.
One may want to set this value to the maximum duration they accept to wait for
the affected Jobs to finish. Keeping in mind that such finishing jobs may
increase Pods termination time in such situations as `kubectl rollout restart`,
AWX upgrade by the operator, or Kubernetes [API-initiated
evictions](https://kubernetes.io/docs/concepts/scheduling-eviction/api-eviction/).
| Name | Description | Default |
| -------------------------------- | --------------------------------------------------------------- | ------- |
| termination_grace_period_seconds | Optional duration in seconds pods needs to terminate gracefully | not set |
### Uninstall ###
@@ -1167,7 +1308,14 @@ Apply the awx-operator.yml for that release to upgrade the operator, and in turn
#### Backup
The first part of any upgrade should be a backup. Note, there are secrets in the pod which work in conjunction with the database. Having just a database backup without the required secrets will not be sufficient for recovering from an issue when upgrading to a new version. See the [backup role documentation](https://github.com/ansible/awx-operator/tree/devel/roles/backup) for information on how to backup your database and secrets. In the event you need to recover the backup see the [restore role documentation](https://github.com/ansible/awx-operator/tree/devel/roles/restore).
The first part of any upgrade should be a backup. Note, there are secrets in the pod which work in conjunction with the database. Having just a database backup without the required secrets will not be sufficient for recovering from an issue when upgrading to a new version. See the [backup role documentation](https://github.com/ansible/awx-operator/tree/devel/roles/backup) for information on how to backup your database and secrets.
In the event you need to recover the backup see the [restore role documentation](https://github.com/ansible/awx-operator/tree/devel/roles/restore). *Before Restoring from a backup*, be sure to:
* delete the old existing AWX CR
* delete the persistent volume claim (PVC) for the database from the old deployment, which has a name like `postgres-13-<deployment-name>-postgres-13-0`
**Note**: Do not delete the namespace/project, as that will delete the backup and the backup's PVC as well.
#### PostgreSQL Upgrade Considerations
@@ -1208,6 +1356,25 @@ Then install the new AWX Operator by following the instructions in [Basic Instal
Once the new AWX Operator is up and running, your AWX deployment will also be upgraded.
### Disable IPV6
Starting with AWX Operator release 0.24.0,[IPV6 was enabled in ngnix configuration](https://github.com/ansible/awx-operator/pull/950) which causes
upgrades and installs to fail in environments where IPv6 is not allowed. Starting in 1.1.1 release, you can set the `ipv6_disabled` flag on the AWX
spec. If you need to use an AWX operator version between 0.24.0 and 1.1.1 in an IPv6 disabled environment, it is suggested to enabled ipv6 on worker
nodes.
In order to disable ipv6 on ngnix configuration (awx-web container), add following to the AWX spec.
The following variables are customizable
| Name | Description | Default |
| ------------- | ---------------------- | ------- |
| ipv6_disabled | Flag to disable ipv6 | false |
```yaml
spec:
ipv6_disabled: true
```
### Adding Execution Nodes
Starting with AWX Operator v0.30.0 and AWX v21.7.0, standalone execution nodes can be added to your deployments.
See [AWX execution nodes docs](https://github.com/ansible/awx/blob/devel/docs/execution_nodes.md) for information about this feature.

View File

@@ -37,6 +37,13 @@
register: asset_upload
changed_when: asset_upload.json.state == "uploaded"
- name: Configure git config
shell: |
git config user.name {{ gh_user }}
git config user.email {{ gh_user }}@users.noreply.github.com
args:
chdir: "{{ playbook_dir }}/../gh-pages"
- name: Publish helm index
command: |
make helm-index
@@ -45,3 +52,11 @@
CR_TOKEN: "{{ gh_token }}"
args:
chdir: "{{ playbook_dir }}/../"
- name: Stage and Push commit to gh-pages branch
shell: |
git add index.yaml
git commit -m "Updated index.yaml latest release"
git push
args:
chdir: "{{ playbook_dir }}/../gh-pages"

View File

@@ -26,6 +26,7 @@
image_version: "{{ image_version | default(omit) }}"
development_mode: "{{ development_mode | default(omit) | bool }}"
image_pull_policy: "{{ image_pull_policy | default(omit) }}"
nodeport_port: "{{ nodeport_port | default(omit) }}"
# ee_images:
# - name: test-ee
# image: quay.io/<user>/awx-ee

View File

@@ -78,6 +78,9 @@ spec:
clean_backup_on_delete:
description: Flag to indicate if backup should be deleted on PVC if AWXBackup object is deleted
type: boolean
pg_dump_suffix:
description: Additional parameters for the pg_dump command
type: string
postgres_label_selector:
description: Label selector used to identify postgres pod for backing up data
type: string
@@ -91,6 +94,11 @@ spec:
description: Configure no_log for no_log tasks
type: boolean
default: true
additional_labels:
description: Additional labels defined on the resource, which should be propagated to child resources
type: array
items:
type: string
set_self_labels:
description: Maintain some of the recommended `app.kubernetes.io/*` labels on the resource (self)
type: boolean

View File

@@ -50,6 +50,9 @@ spec:
description: Name of the restored deployment. This should be different from the original deployment name
if the original deployment still exists.
type: string
cluster_name:
description: Cluster name
type: string
backup_name:
description: AWXBackup object name
type: string
@@ -93,6 +96,11 @@ spec:
description: Configure no_log for no_log tasks
type: boolean
default: true
additional_labels:
description: Additional labels defined on the resource, which should be propagated to child resources
type: array
items:
type: string
set_self_labels:
description: Maintain some of the recommended `app.kubernetes.io/*` labels on the resource (self)
type: boolean

View File

@@ -153,13 +153,382 @@ spec:
nodeport_port:
description: Port to use for the nodeport
type: integer
default: 30080
node_selector:
description: nodeSelector for the pods
type: string
topology_spread_constraints:
description: topology rule(s) for the pods
type: string
affinity:
description: If specified, the pod's scheduling constraints
properties:
nodeAffinity:
properties:
preferredDuringSchedulingIgnoredDuringExecution:
items:
properties:
preference:
properties:
matchExpressions:
items:
properties:
key:
type: string
operator:
type: string
values:
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchFields:
items:
properties:
key:
type: string
operator:
type: string
values:
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
type: object
x-kubernetes-map-type: atomic
weight:
format: int32
type: integer
required:
- preference
- weight
type: object
type: array
requiredDuringSchedulingIgnoredDuringExecution:
properties:
nodeSelectorTerms:
items:
properties:
matchExpressions:
items:
properties:
key:
type: string
operator:
type: string
values:
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchFields:
items:
properties:
key:
type: string
operator:
type: string
values:
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
type: object
x-kubernetes-map-type: atomic
type: array
required:
- nodeSelectorTerms
type: object
x-kubernetes-map-type: atomic
type: object
podAffinity:
properties:
preferredDuringSchedulingIgnoredDuringExecution:
items:
properties:
podAffinityTerm:
properties:
labelSelector:
properties:
matchExpressions:
items:
properties:
key:
type: string
operator:
type: string
values:
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
type: object
type: object
x-kubernetes-map-type: atomic
namespaceSelector:
properties:
matchExpressions:
items:
properties:
key:
type: string
operator:
type: string
values:
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
type: object
type: object
x-kubernetes-map-type: atomic
namespaces:
items:
type: string
type: array
topologyKey:
type: string
required:
- topologyKey
type: object
weight:
format: int32
type: integer
required:
- podAffinityTerm
- weight
type: object
type: array
requiredDuringSchedulingIgnoredDuringExecution:
items:
properties:
labelSelector:
properties:
matchExpressions:
items:
properties:
key:
type: string
operator:
type: string
values:
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
type: object
type: object
x-kubernetes-map-type: atomic
namespaceSelector:
properties:
matchExpressions:
items:
properties:
key:
type: string
operator:
type: string
values:
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
type: object
type: object
x-kubernetes-map-type: atomic
namespaces:
items:
type: string
type: array
topologyKey:
type: string
required:
- topologyKey
type: object
type: array
type: object
podAntiAffinity:
properties:
preferredDuringSchedulingIgnoredDuringExecution:
items:
properties:
podAffinityTerm:
properties:
labelSelector:
properties:
matchExpressions:
items:
properties:
key:
type: string
operator:
type: string
values:
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
type: object
type: object
x-kubernetes-map-type: atomic
namespaceSelector:
properties:
matchExpressions:
items:
properties:
key:
type: string
operator:
type: string
values:
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
type: object
type: object
x-kubernetes-map-type: atomic
namespaces:
items:
type: string
type: array
topologyKey:
type: string
required:
- topologyKey
type: object
weight:
format: int32
type: integer
required:
- podAffinityTerm
- weight
type: object
type: array
requiredDuringSchedulingIgnoredDuringExecution:
items:
properties:
labelSelector:
properties:
matchExpressions:
items:
properties:
key:
type: string
operator:
type: string
values:
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
type: object
type: object
x-kubernetes-map-type: atomic
namespaceSelector:
properties:
matchExpressions:
items:
properties:
key:
type: string
operator:
type: string
values:
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
type: object
type: object
x-kubernetes-map-type: atomic
namespaces:
items:
type: string
type: array
topologyKey:
type: string
required:
- topologyKey
type: object
type: array
type: object
type: object
termination_grace_period_seconds:
description: Optional duration in seconds pods needs to terminate gracefully
type: integer
format: int32
service_labels:
description: Additional labels to apply to the service
type: string
@@ -393,6 +762,9 @@ spec:
init_container_extra_volume_mounts:
description: Specify volume mounts to be added to the init container
type: string
init_projects_container_image:
description: Registry path to the init projects container to use
type: string
postgres_image:
description: Registry path to the PostgreSQL container to use
type: string
@@ -524,6 +896,15 @@ spec:
description: Maintain some of the recommended `app.kubernetes.io/*` labels on the resource (self)
type: boolean
default: true
additional_labels:
description: Additional labels defined on the resource, which should be propagated to child resources
type: array
items:
type: string
ipv6_disabled:
description: Disable web container's nginx ipv6 listener
type: boolean
default: false
type: object
status:
properties:

View File

@@ -21,7 +21,7 @@ spec:
template:
metadata:
annotations:
kubectl.kubernetes.io/default-container: manager
kubectl.kubernetes.io/default-container: awx-manager
labels:
control-plane: controller-manager
spec:

View File

@@ -151,7 +151,7 @@ spec:
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:text
version: v1beta1
- description: Deploy a new instance of AWX
- description: Deploy a new instance of AWX. A standardized way to define, operate and scale automation with Ansible.
displayName: AWX
kind: AWX
name: awxs.awx.ansible.com
@@ -607,6 +607,11 @@ spec:
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:advanced
- urn:alm:descriptor:com.tectonic.ui:hidden
- displayName: Affinity
path: affinity
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:advanced
- urn:alm:descriptor:com.tectonic.ui:hidden
- displayName: Control Plane Priority Class
path: control_plane_priority_class
x-descriptors:
@@ -617,6 +622,11 @@ spec:
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:advanced
- urn:alm:descriptor:com.tectonic.ui:hidden
- displayName: Termination Grace Period Seconds
path: termination_grace_period_seconds
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:advanced
- urn:alm:descriptor:com.tectonic.ui:hidden
- displayName: Service Labels
path: service_labels
x-descriptors:
@@ -682,6 +692,12 @@ spec:
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:advanced
- urn:alm:descriptor:com.tectonic.ui:hidden
- description: Init Projects Container image to use
displayName: Init Projects Container Image
path: init_projects_container_image
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:advanced
- urn:alm:descriptor:com.tectonic.ui:hidden
- description: Secret where can be found the trusted Certificate Authority Bundle
path: bundle_cacert_secret
x-descriptors:
@@ -702,6 +718,17 @@ spec:
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:advanced
- urn:alm:descriptor:com.tectonic.ui:booleanSwitch
- displayName: Additional labels defined on the resource, which should be
propagated to child resources
path: additional_labels
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:advanced
- urn:alm:descriptor:com.tectonic.ui:hidden
- displayName: Disable IPv6 listener?
path: ipv6_disabled
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:advanced
- urn:alm:descriptor:com.tectonic.ui:booleanSwitch
statusDescriptors:
- description: Route to access the instance deployed
displayName: URL
@@ -777,6 +804,7 @@ spec:
- email: awx-project@googlegroups.com
name: AWX Team
maturity: alpha
MinKubeVersion: 1.22.15
provider:
name: Ansible
url: github.com/ansible/awx-operator

View File

@@ -20,7 +20,6 @@ rules:
- watch
- apiGroups:
- ""
- "rbac.authorization.k8s.io"
resources:
- pods
- services
@@ -31,6 +30,17 @@ rules:
- events
- configmaps
- secrets
verbs:
- get
- list
- create
- delete
- patch
- update
- watch
- apiGroups:
- "rbac.authorization.k8s.io"
resources:
- roles
- rolebindings
verbs:
@@ -43,12 +53,22 @@ rules:
- watch
- apiGroups:
- apps
- networking.k8s.io
resources:
- deployments
- daemonsets
- replicasets
- statefulsets
verbs:
- get
- list
- create
- delete
- patch
- update
- watch
- apiGroups:
- networking.k8s.io
resources:
- ingresses
verbs:
- get

View File

@@ -4,7 +4,7 @@
entrypoint:
- scorecard-test
- basic-check-spec
image: quay.io/operator-framework/scorecard-test:v1.23.0
image: quay.io/operator-framework/scorecard-test:v1.26.0
labels:
suite: basic
test: basic-check-spec-test

View File

@@ -4,7 +4,7 @@
entrypoint:
- scorecard-test
- olm-bundle-validation
image: quay.io/operator-framework/scorecard-test:v1.23.0
image: quay.io/operator-framework/scorecard-test:v1.26.0
labels:
suite: olm
test: olm-bundle-validation-test
@@ -14,7 +14,7 @@
entrypoint:
- scorecard-test
- olm-crds-have-validation
image: quay.io/operator-framework/scorecard-test:v1.23.0
image: quay.io/operator-framework/scorecard-test:v1.26.0
labels:
suite: olm
test: olm-crds-have-validation-test
@@ -24,7 +24,7 @@
entrypoint:
- scorecard-test
- olm-crds-have-resources
image: quay.io/operator-framework/scorecard-test:v1.23.0
image: quay.io/operator-framework/scorecard-test:v1.26.0
labels:
suite: olm
test: olm-crds-have-resources-test
@@ -34,7 +34,7 @@
entrypoint:
- scorecard-test
- olm-spec-descriptors
image: quay.io/operator-framework/scorecard-test:v1.23.0
image: quay.io/operator-framework/scorecard-test:v1.26.0
labels:
suite: olm
test: olm-spec-descriptors-test
@@ -44,7 +44,7 @@
entrypoint:
- scorecard-test
- olm-status-descriptors
image: quay.io/operator-framework/scorecard-test:v1.23.0
image: quay.io/operator-framework/scorecard-test:v1.26.0
labels:
suite: olm
test: olm-status-descriptors-test

View File

@@ -35,6 +35,19 @@ ansible_operator_meta:
namespace: awx
service_type: nodeport
```
The vars file will replace the awx resource so any value that you wish to over ride using the awx resource, put in the vars file. For example, if you wish to use your own image, version and pull policy, you can specify it like below:
```yaml
# vars.yml
---
ansible_operator_meta:
name: awx
namespace: awx
service_type: nodeport
image: $DEV_DOCKER_TAG_BASE/awx_kube_devel
image_pull_policy: Always
image_version: $COMPOSE_TAG
```
Run the installer:

View File

@@ -34,7 +34,7 @@ metadata:
namespace: <target namespace>
stringData:
host: <external ip or url resolvable by the cluster>
port: <external port, this usually defaults to 5432>
port: "<external port, this usually defaults to 5432>" # quotes are required
database: <desired database name>
username: <username to connect as>
password: <password to connect with>

View File

@@ -28,7 +28,7 @@
register: awx_pod
when: not awx_version
- name: Exract tags from images
- name: Extract tags from images
set_fact:
image_tags: |
{{ awx_pod.resources[0].spec.containers |
@@ -83,3 +83,61 @@
result: '{{ ansible_failed_result }}'
fail:
msg: '{{ failed_task }}'
- block:
- name: Look up details for this deployment
k8s_info:
namespace: "{{ namespace }}"
api_version: "awx.ansible.com/v1beta1"
kind: AWX
name: example-awx
register: this_awx
- name: Get pod details
k8s_info:
namespace: '{{ namespace }}'
kind: Pod
label_selectors:
- app.kubernetes.io/name = example-awx
register: awx_pod
- name: Extract additional_labels from AWX spec
set_fact:
awx_additional_labels: >-
{{ this_awx.resources[0].metadata.labels
| dict2items | selectattr('key', 'in', this_awx.resources[0].spec.additional_labels)
| list
}}
- name: Extract additional_labels from AWX Pod
set_fact:
pod_additional_labels: >-
{{ awx_pod.resources[0].metadata.labels
| dict2items | selectattr('key', 'in', this_awx.resources[0].spec.additional_labels)
| list
}}
- name: AWX Pod contains additional_labels
ansible.builtin.assert:
that:
- pod_additional_labels == awx_additional_labels
- name: Extract Pod labels which shouldn't have been propagated to it from AWX
set_fact:
pod_extra_labels: >-
{{ awx_pod.resources[0].metadata.labels
| dict2items | selectattr('key', 'in', ["my/do-not-inherit"])
| list
}}
- name: AWX Pod doesn't contain AWX labels not in additional_labels
ansible.builtin.assert:
that:
- pod_extra_labels == []
rescue:
- name: Re-emit failure
vars:
failed_task:
result: '{{ ansible_failed_result }}'
fail:
msg: '{{ failed_task }}'

View File

@@ -3,6 +3,10 @@ apiVersion: awx.ansible.com/v1beta1
kind: AWX
metadata:
name: example-awx
labels:
my/team: "foo"
my/service: "bar"
my/do-not-inherit: "yes"
spec:
{% if awx_image %}
image: {{ awx_image }}
@@ -26,6 +30,10 @@ spec:
requests:
cpu: 50m
memory: 16M
no_log: false
postgres_resource_requirements: {}
postgres_init_container_resource_requirements: {}
redis_resource_requirements: {}
additional_labels:
- my/team
- my/service

View File

@@ -93,6 +93,12 @@ backup_resource_requirements:
memory: "32Mi"
```
To customize the pg_dump command that will be executed on a backup use the `pg_dump_suffix` variable. This variable will append your provided pg_dump parameters to the end of the 'standard' command. For example to exclude the data from 'main_jobevent' and 'main_job' to decrease the size of the backup use:
```
pg_dump_suffix: "--exclude-table-data 'main_jobevent*' --exclude-table-data 'main_job'"
```
Testing
----------------

View File

@@ -31,6 +31,9 @@ backup_resource_requirements:
# Allow additional parameters to be added to the pg_dump backup command
pg_dump_suffix: ''
# Labels defined on the resource, which should be propagated to child resources
additional_labels: []
# Maintain some of the recommended `app.kubernetes.io/*` labels on the resource (self)
set_self_labels: true
...

View File

@@ -24,7 +24,8 @@ galaxy_info:
- backup
- automation
dependencies: []
dependencies:
- role: common
collections:
- kubernetes.core

View File

@@ -25,10 +25,11 @@
set_fact:
awx_spec:
spec: "{{ _awx }}"
previous_deployment_name: "{{ this_awx['resources'][0]['metadata']['name'] }}"
- name: Write awx object to pvc
k8s_exec:
k8s_cp:
namespace: "{{ backup_pvc_namespace }}"
pod: "{{ ansible_operator_meta.name }}-db-management"
command: >-
bash -c 'echo "$0" > {{ backup_dir }}/awx_object' {{ awx_spec | to_yaml | quote }}
remote_path: "{{ backup_dir }}/awx_object"
content: "{{ awx_spec | to_yaml }}"

View File

@@ -10,12 +10,7 @@
metadata:
name: "{{ ansible_operator_meta.name }}"
namespace: "{{ ansible_operator_meta.namespace }}"
labels:
app.kubernetes.io/name: "{{ ansible_operator_meta.name }}"
app.kubernetes.io/part-of: "{{ ansible_operator_meta.name }}"
app.kubernetes.io/managed-by: "{{ deployment_type }}-operator"
app.kubernetes.io/component: "{{ deployment_type }}"
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
labels: '{{ lookup("template", "../common/templates/labels/common.yaml.j2") | from_yaml }}'
when: set_self_labels | bool
- name: Look up details for this backup object
@@ -26,23 +21,33 @@
namespace: "{{ ansible_operator_meta.namespace }}"
register: this_backup
- name: Build `additional_labels_items` labels from `additional_labels`
set_fact:
additional_labels_items: >-
{{ this_backup['resources'][0]['metadata']['labels']
| dict2items | selectattr('key', 'in', additional_labels)
}}
when:
- additional_labels | length
- this_backup['resources'][0]['metadata']['labels']
- block:
- include_tasks: init.yml
- include_tasks: init.yml
- include_tasks: postgres.yml
- include_tasks: postgres.yml
- include_tasks: awx-cro.yml
- include_tasks: awx-cro.yml
- include_tasks: secrets.yml
- include_tasks: secrets.yml
- name: Set flag signifying this backup was successful
set_fact:
backup_complete: true
- name: Set flag signifying this backup was successful
set_fact:
backup_complete: true
- include_tasks: cleanup.yml
- include_tasks: cleanup.yml
when:
- this_backup['resources'][0]['status']['backupDirectory'] is not defined
- this_backup['resources'][0]['status']['backupDirectory'] is not defined
- name: Update status variables
include_tasks: update_status.yml

View File

@@ -0,0 +1,24 @@
---
- name: Get secret
k8s_info:
version: v1
kind: Secret
namespace: '{{ ansible_operator_meta.namespace }}'
name: "{{ item }}"
register: _secret
no_log: "{{ no_log }}"
- name: Backup secret if exists
block:
- name: Set secret key
set_fact:
_data: "{{ _secret['resources'][0]['data'] }}"
_type: "{{ _secret['resources'][0]['type'] }}"
no_log: "{{ no_log }}"
- name: Create and Add secret names and data to dictionary
set_fact:
secret_dict: "{{ secret_dict | default({}) | combine({item: { 'name': item, 'data': _data, 'type': _type }}) }}"
no_log: "{{ no_log }}"
when: _secret | length

View File

@@ -50,7 +50,7 @@
- name: Determine the timestamp for the backup once for all nodes
set_fact:
now: '{{ lookup("pipe", "date +%F-%T") }}'
now: '{{ lookup("pipe", "date +%F-%H%M%S") }}'
- name: Set backup directory name
set_fact:
@@ -75,7 +75,7 @@
namespace: "{{ backup_pvc_namespace }}"
pod: "{{ ansible_operator_meta.name }}-db-management"
command: >-
bash -c "chmod 0600 {{ backup_dir }}/tower.db && chown postgres:root {{ backup_dir }}/tower.db"
bash -c "chmod 660 {{ backup_dir }}/tower.db && chown :root {{ backup_dir }}/tower.db"
- name: Set full resolvable host name for postgres pod
set_fact:

View File

@@ -1,11 +1,5 @@
---
- name: Create Temporary secrets file
tempfile:
state: file
suffix: .json
register: tmp_secrets
- name: Dump (generated) secret names from statuses and data into file
include_tasks: dump_generated_secret.yml
with_items:
@@ -23,6 +17,12 @@
- bundle_cacert_secret
- ee_pull_credentials_secret
- name: Dump receptor secret names and data into file
include_tasks: dump_receptor_secrets.yml
loop:
- '{{ deployment_name }}-receptor-ca'
- '{{ deployment_name }}-receptor-work-signing'
# image_pull_secret is deprecated in favor of image_pull_secrets
- name: Dump image_pull_secret into file
include_tasks: dump_secret.yml
@@ -42,9 +42,9 @@
no_log: "{{ no_log }}"
- name: Write postgres configuration to pvc
k8s_exec:
k8s_cp:
namespace: "{{ backup_pvc_namespace }}"
pod: "{{ ansible_operator_meta.name }}-db-management"
command: >-
bash -c "echo '{{ secrets | to_yaml }}' > {{ backup_dir }}/secrets.yml"
remote_path: "{{ backup_dir }}/secrets.yml"
content: "{{ secrets | to_yaml }}"
no_log: "{{ no_log }}"

View File

@@ -6,11 +6,7 @@ metadata:
namespace: {{ backup_pvc_namespace }}
ownerReferences: null
labels:
app.kubernetes.io/name: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/managed-by: '{{ deployment_type }}-operator'
app.kubernetes.io/component: '{{ deployment_type }}'
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
spec:
accessModes:
- ReadWriteOnce

View File

@@ -5,11 +5,7 @@ metadata:
name: {{ ansible_operator_meta.name }}-db-management
namespace: {{ backup_pvc_namespace }}
labels:
app.kubernetes.io/name: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/managed-by: '{{ deployment_type }}-operator'
app.kubernetes.io/component: '{{ deployment_type }}'
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
spec:
containers:
- name: {{ ansible_operator_meta.name }}-db-management

View File

@@ -0,0 +1,3 @@
{% for item in additional_labels_items | default([]) %}
{{ item.key }}: '{{ item.value }}'
{% endfor %}

View File

@@ -4,3 +4,4 @@ app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/managed-by: '{{ deployment_type }}-operator'
app.kubernetes.io/component: '{{ deployment_type }}'
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
{{ lookup("template", "../common/templates/labels/additional_labels.yaml.j2") }}

View File

@@ -40,7 +40,9 @@ loadbalancer_protocol: 'http'
loadbalancer_port: '80'
service_annotations: ''
nodeport_port: '30080'
# Port to be used for NodePort configuration, default is to auto-assign a port between 30000-32768
#nodeport_port: '30080'
# The TLS termination mechanism to use to access
# the services. Supported mechanism are: edge, passthrough
#
@@ -81,6 +83,8 @@ node_selector: ''
# app.kubernetes.io/name: "<resourcename>"
topology_spread_constraints: ''
affinity: {}
# Add node tolerations for the AWX pods. Specify as literal block. E.g.:
# tolerations: |
# - key: "dedicated"
@@ -164,6 +168,8 @@ _control_plane_ee_image: quay.io/ansible/awx-ee:latest
_init_container_image: "{{ _control_plane_ee_image.split(':')[0] }}"
_init_container_image_version: "{{ _control_plane_ee_image.split(':')[1] }}"
_init_projects_container_image: quay.io/centos/centos:stream9
create_preload_data: true
replicas: "1"
@@ -299,5 +305,11 @@ no_log: true
#
auto_upgrade: true
# Labels defined on the resource, which should be propagated to child resources
additional_labels: []
# Maintain some of the recommended `app.kubernetes.io/*` labels on the resource (self)
set_self_labels: true
# Disable web container's nginx ipv6 listener
ipv6_disabled: false

View File

@@ -0,0 +1,66 @@
# file, which when exists, indicates that `master` script has successfully
# completed pre-stop script execution
marker_file="${PRE_STOP_MARKER_FILE:-/var/lib/pre-stop/.termination_marker}"
# file which the running `master` script continuously updates (mtime) to
# indicate it's still running. this file is then read by `watcher`s to
# understand if they still have to wait for `termination_marker`
heartbeat_file="${PRE_STOP_HEARTBEAT_FILE:-/var/lib/pre-stop/.heartbeat}"
# file which:
# * `watcher`s create when they bail out because they didn't see the
# `heartbeat_file` to be updated within `$heartbeat_failed_threshold`;
# * `master` creates when its handler command fails;
# when scripts see such file, they also give up
bailout_file="${PRE_STOP_BAILOUT_FILE:-/var/lib/pre-stop/.bailout}"
heartbeat_threshold="${PRE_STOP_HEARTBEAT_THRESHOLD:-60}"
# where the scripts' stdout/stderr are streamed
stdout="${PRE_STOP_STDOUT:-/proc/1/fd/1}"
stderr="${PRE_STOP_STDERR:-/proc/1/fd/2}"
# command the `master` script executes, which when successfully finishes,
# causes the script to create the `marker_file`
handler="${PRE_STOP_HANDLER:-bash -c \"PYTHONUNBUFFERED=x awx-manage disable_instance --wait --retry=inf\"}"
log_prefix="${PRE_STOP_LOG_PREFIX:-preStop.exec}"
[[ -n ${PRE_STOP_LOG_ROLE} ]] && log_prefix="${log_prefix}] [$PRE_STOP_LOG_ROLE"
# interval at which `watcher`s check for `marker_file` presence
recheck_sleep="${PRE_STOP_RECHECK_SLEEP:-1}"
# interval at which `watcher`s report into $stdout that they are still watching
report_every="${PRE_STOP_REPORT_EVERY:-30}"
function log {
printf "[%s] $1\n" "$log_prefix" "${@:2}"
}
function parameters_string {
for param in "$@"; do
printf "%s=\"%s\"\n" "$param" "${!param}"
done | paste -s -d ' '
}
function check_bailout {
if [[ -f $bailout_file ]]; then
log "\"%s\" file has been detected, accepting bail out signal and failing the hook script" \
"$bailout_file"
exit 1
fi
}
function check_heartbeat {
if [[ -f $heartbeat_file ]]; then
delta=$(( $(date +%s) - $(stat -c %Y "$heartbeat_file") ))
else
delta=$(( $(date +%s) - $1 ))
fi
if [[ $delta -gt $heartbeat_threshold ]]; then
log "The heartbeat file hasn't been updated since %ss, which is above the threshold of %ds, assuming the master is not operating and failing the hook script" \
$delta
$heartbeat_threshold
touch "$bailout_file"
exit 1
fi
}

View File

@@ -0,0 +1,50 @@
#/usr/bin/env bash
PRE_STOP_LOG_ROLE="${PRE_STOP_LOG_ROLE:-master}"
source $(dirname "$0")/termination-env
{
log "The hook has started: %s" \
"$(parameters_string \
"marker_file" \
"heartbeat_file" \
"bailout_file" \
"handler" \
)"
touch "$heartbeat_file"
set -o pipefail
eval "$handler" 2>&1 | while IFS= read -r line; do
# we check the files here and break early, but overall script termination
# happens later - as we need to distinguish between files detection and
# command failure, while bash doesn't offer a simple way to do this here
# inside the loop (`exit` does not terminate the script)
[[ -f $bailout_file ]] && break
[[ -f $marker_file ]] && break
log "[handler] %s" "$line"
touch "$heartbeat_file"
done
ec=$?
set +o pipefail
# process various cases in specific order
check_bailout
if [[ -f $marker_file ]]; then
log "Done! The marker file has been detected, assuming some other instance of the script has run to completion"
exit 0
elif [[ $ec -ne 0 ]]; then
log "The handler has failed with \"%d\" exit code, failing the hook script too" \
$ec
# signal others to bail out
touch "$bailout_file"
exit $ec
else
log "Done! Generating the marker file allowing to proceed to termination"
touch "$marker_file"
fi
} > "$stdout" 2> "$stderr"

View File

@@ -0,0 +1,33 @@
#/usr/bin/env bash
PRE_STOP_LOG_ROLE="${PRE_STOP_LOG_ROLE:-waiter}"
source $(dirname "$0")/termination-env
{
log "The hook has started: %s" \
"$(parameters_string \
"marker_file" \
"heartbeat_file" \
"bailout_file" \
"recheck_sleep" \
"report_every" \
)"
n=0
checks_started=$(date +%s)
while ! [[ -f $marker_file ]]; do
check_bailout
check_heartbeat $checks_started
if [[ $(($n % $report_every)) -eq 0 ]]; then
log "Waiting for the marker file to be accessible..."
fi
n=$(($n + 1))
sleep $recheck_sleep
done
log "The marker file found, exiting to proceed to termination"
} > "$stdout" 2> "$stderr"

View File

@@ -23,6 +23,8 @@
- '{{ _secret_key }}'
- '{{ _postgres_configuration }}'
- '{{ _broadcast_websocket_secret }}'
- '{{ ansible_operator_meta.name }}-receptor-ca'
- '{{ ansible_operator_meta.name }}-receptor-work-signing'
no_log: "{{ no_log }}"
when: not garbage_collect_secrets | bool

View File

@@ -18,10 +18,21 @@
namespace: "{{ ansible_operator_meta.namespace }}"
pod: "{{ tower_pod_name }}"
container: "{{ ansible_operator_meta.name }}-task"
command: >-
bash -c "echo \"from django.contrib.auth.models import User;
User.objects.create_superuser('{{ admin_user }}', '{{ admin_email }}', '{{ admin_password }}')\"
| awx-manage shell"
command: awx-manage createsuperuser --username={{ admin_user | quote }} --email={{ admin_email | quote }} --noinput
register: result
changed_when: "'That username is already taken' not in result.stderr"
failed_when: "'That username is already taken' not in result.stderr and 'Superuser created successfully' not in result.stdout"
no_log: "{{ no_log }}"
when: users_result.return_code > 0
- name: Update Django super user password
k8s_exec:
namespace: "{{ ansible_operator_meta.namespace }}"
pod: "{{ tower_pod_name }}"
container: "{{ ansible_operator_meta.name }}-task"
command: awx-manage update_password --username='{{ admin_user }}' --password='{{ admin_password }}'
register: result
changed_when: "'Password updated' in result.stdout"
no_log: "{{ no_log }}"
when: users_result.return_code > 0

View File

@@ -10,26 +10,46 @@
metadata:
name: '{{ ansible_operator_meta.name }}'
namespace: '{{ ansible_operator_meta.namespace }}'
labels: '{{ lookup("template", "labels/common.yaml.j2") | from_yaml }}'
labels: '{{ lookup("template", "../common/templates/labels/common.yaml.j2") | from_yaml }}'
when: set_self_labels | bool
- name: Build `additional_labels_items` labels from `additional_labels`
block:
- name: Look up details for this deployment
k8s_info:
api_version: "{{ api_version }}"
kind: "{{ kind }}"
name: "{{ ansible_operator_meta.name }}"
namespace: "{{ ansible_operator_meta.namespace }}"
register: this_awx
- name: Select resource labels which are in `additional_labels`
set_fact:
additional_labels_items: >-
{{ this_awx['resources'][0]['metadata']['labels']
| dict2items | selectattr('key', 'in', additional_labels)
| list
}}
when: this_awx['resources'][0]['metadata']['labels']
when: additional_labels | length
- name: Include secret key configuration tasks
include_tasks: secret_key_configuration.yml
- name: Load LDAP CAcert certificate
include_tasks: load_ldap_cacert_secret.yml
when:
- ldap_cacert_secret != ''
- ldap_cacert_secret != ''
- name: Load ldap bind password
include_tasks: load_ldap_password_secret.yml
when:
- ldap_password_secret != ''
- ldap_password_secret != ''
- name: Load bundle certificate authority certificate
include_tasks: load_bundle_cacert_secret.yml
when:
- bundle_cacert_secret != ''
- bundle_cacert_secret != ''
- name: Include admin password configuration tasks
include_tasks: admin_password_configuration.yml
@@ -46,8 +66,8 @@
- name: Load Route TLS certificate
include_tasks: load_route_tls_secret.yml
when:
- ingress_type | lower == 'route'
- route_tls_secret != ''
- ingress_type | lower == 'route'
- route_tls_secret != ''
- name: Include resources configuration tasks
include_tasks: resources_configuration.yml
@@ -71,8 +91,8 @@
bash -c "awx-manage migrate --noinput"
register: migrate_result
when:
- database_check is defined
- (database_check.stdout|trim) != '0'
- database_check is defined
- (database_check.stdout|trim) != '0'
- name: Initialize Django
include_tasks: initialize_django.yml

View File

@@ -13,9 +13,17 @@
- status.phase=Running
register: tower_pod
- name: Set the resource pod as a variable.
set_fact:
tower_pod: >-
{{ tower_pod['resources']
| rejectattr('metadata.deletionTimestamp', 'defined')
| sort(attribute='metadata.creationTimestamp')
| first | default({}) }}
- name: Set the resource pod name as a variable.
set_fact:
tower_pod_name: "{{ tower_pod['resources'][0]['metadata']['name'] | default('') }}"
tower_pod_name: "{{ tower_pod['metadata']['name'] | default('') }}"
- name: Set user provided control plane ee image
set_fact:
@@ -32,13 +40,13 @@
kind: Secret
namespace: '{{ ansible_operator_meta.namespace }}'
name: '{{ ansible_operator_meta.name }}-receptor-ca'
register: _receptor_ca
register: receptor_ca
no_log: "{{ no_log }}"
- name: Migrate Receptor CA Secret
when:
- _receptor_ca['resources'] | default([]) | length
- _receptor_ca['resources'][0]['type'] != "kubernetes.io/tls"
- receptor_ca['resources'] | default([]) | length
- receptor_ca['resources'][0]['type'] != "kubernetes.io/tls"
block:
- name: Delete old Receptor CA Secret
k8s:
@@ -53,7 +61,7 @@
register: _receptor_ca_key_file
- name: Copy Receptor CA key from old secret to tempfile
copy:
content: "{{ _receptor_ca['resources'][0]['data']['receptor-ca.key'] | b64decode }}"
content: "{{ receptor_ca['resources'][0]['data']['receptor-ca.key'] | b64decode }}"
dest: "{{ _receptor_ca_key_file.path }}"
no_log: "{{ no_log }}"
- name: Create tempfile for receptor-ca.crt
@@ -63,7 +71,7 @@
register: _receptor_ca_crt_file
- name: Copy Receptor CA cert from old secret to tempfile
copy:
content: "{{ _receptor_ca['resources'][0]['data']['receptor-ca.crt'] | b64decode }}"
content: "{{ receptor_ca['resources'][0]['data']['receptor-ca.crt'] | b64decode }}"
dest: "{{ _receptor_ca_crt_file.path }}"
no_log: "{{ no_log }}"
- name: Create New Receptor CA secret
@@ -71,6 +79,17 @@
apply: true
definition: "{{ lookup('template', 'secrets/receptor_ca_secret.yaml.j2') }}"
no_log: "{{ no_log }}"
- name: Read New Receptor CA Secret
k8s_info:
kind: Secret
namespace: '{{ ansible_operator_meta.namespace }}'
name: '{{ ansible_operator_meta.name }}-receptor-ca'
register: _receptor_ca
no_log: "{{ no_log }}"
- name: Set receptor_ca variable
set_fact:
receptor_ca: '{{ _receptor_ca }}'
no_log: "{{ no_log }}"
- name: Remove tempfiles
file:
path: "{{ item }}"
@@ -106,6 +125,17 @@
apply: true
definition: "{{ lookup('template', 'secrets/receptor_ca_secret.yaml.j2') }}"
no_log: "{{ no_log }}"
- name: Read Receptor CA secret
k8s_info:
kind: Secret
namespace: '{{ ansible_operator_meta.namespace }}'
name: '{{ ansible_operator_meta.name }}-receptor-ca'
register: _receptor_ca
no_log: "{{ no_log }}"
- name: Set receptor_ca variable
set_fact:
receptor_ca: '{{ _receptor_ca }}'
no_log: "{{ no_log }}"
- name: Remove tempfiles
file:
path: "{{ item }}"
@@ -113,14 +143,14 @@
loop:
- "{{ _receptor_ca_key_file.path }}"
- "{{ _receptor_ca_crt_file.path }}"
when: not _receptor_ca['resources'] | default([]) | length
when: not receptor_ca['resources'] | default([]) | length
- name: Check for Receptor work signing Secret
k8s_info:
kind: Secret
namespace: '{{ ansible_operator_meta.namespace }}'
name: '{{ ansible_operator_meta.name }}-receptor-work-signing'
register: _receptor_work_signing
register: receptor_work_signing
no_log: "{{ no_log }}"
- name: Generate Receptor work signing RSA key pair
@@ -151,6 +181,17 @@
apply: true
definition: "{{ lookup('template', 'secrets/receptor_work_signing_secret.yaml.j2') }}"
no_log: "{{ no_log }}"
- name: Read Receptor work signing Secret
k8s_info:
kind: Secret
namespace: '{{ ansible_operator_meta.namespace }}'
name: '{{ ansible_operator_meta.name }}-receptor-work-signing'
register: _receptor_work_signing
no_log: "{{ no_log }}"
- name: Set receptor_work_signing variable
set_fact:
receptor_work_signing: '{{ _receptor_work_signing }}'
no_log: "{{ no_log }}"
- name: Remove tempfiles
file:
path: "{{ item }}"
@@ -158,16 +199,16 @@
loop:
- "{{ _receptor_work_signing_private_key_file.path }}"
- "{{ _receptor_work_signing_public_key_file.path }}"
when: not _receptor_work_signing['resources'] | default([]) | length
when: not receptor_work_signing['resources'] | default([]) | length
- name: Apply Resources
k8s:
apply: yes
definition: "{{ lookup('template', item + '.yaml.j2') }}"
wait: yes
register: tower_resources_result
loop:
- 'configmaps/config'
- 'configmaps/pre_stop_scripts'
- 'secrets/app_credentials'
- 'rbac/service_account'
- 'storage/persistent'
@@ -210,21 +251,10 @@
apply: yes
definition: "{{ lookup('template', 'deployments/deployment.yaml.j2') }}"
wait: yes
wait_timeout: "{{ 120 * replicas or 120 }}"
register: this_deployment_result
- block:
- name: Delete pod to reload a resource configuration
k8s:
api_version: v1
state: absent
kind: Pod
namespace: '{{ ansible_operator_meta.namespace }}'
name: '{{ tower_pod_name }}'
wait: yes
when:
- tower_resources_result.changed
- tower_pod_name | length
- name: Get the new resource pod information after updating resource.
k8s_info:
kind: Pod
@@ -236,17 +266,20 @@
field_selectors:
- status.phase=Running
register: _new_pod
until:
- _new_pod['resources'] | length
- _new_pod['resources'][0]['metadata']['name'] != tower_pod_name
delay: 5
retries: 60
- name: Update new resource pod as a variable.
set_fact:
tower_pod: >-
{{ _new_pod['resources']
| rejectattr('metadata.deletionTimestamp', 'defined')
| sort(attribute='metadata.creationTimestamp')
| last | default({}) }}
- name: Update new resource pod name as a variable.
set_fact:
tower_pod_name: '{{ _new_pod["resources"][0]["metadata"]["name"] }}'
tower_pod_name: '{{ tower_pod["metadata"]["name"] | default("")}}'
when:
- tower_resources_result.changed or this_deployment_result.changed
- this_deployment_result.changed
- name: Verify the resource pod name is populated.
assert:

View File

@@ -40,10 +40,10 @@
- name: Set secret key secret
set_fact:
__secret_key_secret: '{{ _generated_secret_key["resources"] | default([]) | length | ternary(_generated_secret_key, _secret_key_secret) }}'
secret_key: '{{ _generated_secret_key["resources"] | default([]) | length | ternary(_generated_secret_key, _secret_key_secret) }}'
no_log: "{{ no_log }}"
- name: Store secret key secret name
set_fact:
secret_key_secret_name: "{{ __secret_key_secret['resources'][0]['metadata']['name'] }}"
secret_key_secret_name: "{{ secret_key['resources'][0]['metadata']['name'] }}"
no_log: "{{ no_log }}"

View File

@@ -17,3 +17,20 @@
{{ _custom_init_container_image |
default(lookup('env', 'RELATED_IMAGE_AWX_INIT_CONTAINER')) |
default(_default_init_container_image, true) }}
- name: Set default awx init projects container image
set_fact:
_default_init_projects_container_image: "{{ _init_projects_container_image }}"
- name: Set user provided awx init projects image
set_fact:
_custom_init_projects_container_image: "{{ init_projects_container_image }}"
when:
- init_projects_container_image | default([]) | length
- name: Set Init projects image URL
set_fact:
_init_projects_container_image: >-
{{ _custom_init_projects_container_image |
default(lookup('env', 'RELATED_IMAGE_AWX_INIT_PROJECTS_CONTAINER')) |
default(_default_init_projects_container_image, true) }}

View File

@@ -93,7 +93,7 @@
command: |
bash -c """
set -e -o pipefail
PGPASSWORD={{ awx_postgres_pass }} {{ pgdump }} | PGPASSWORD={{ awx_postgres_pass }} {{ pg_restore }}
PGPASSWORD='{{ awx_postgres_pass }}' {{ pgdump }} | PGPASSWORD='{{ awx_postgres_pass }}' {{ pg_restore }}
echo 'Successful'
"""
no_log: "{{ no_log }}"

View File

@@ -6,11 +6,7 @@ metadata:
name: '{{ ansible_operator_meta.name }}-{{ deployment_type }}-configmap'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
app.kubernetes.io/name: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/managed-by: '{{ deployment_type }}-operator'
app.kubernetes.io/component: '{{ deployment_type }}'
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
data:
environment: |
AWX_SKIP_MIGRATIONS=true
@@ -132,7 +128,9 @@ data:
{% if route_tls_termination_mechanism | lower == 'passthrough' %}
server {
listen 8052 default_server;
{% if not ipv6_disabled %}
listen [::]:8052 default_server;
{% endif %}
server_name _;
# Redirect all HTTP links to the matching HTTPS page
@@ -143,7 +141,9 @@ data:
server {
{% if route_tls_termination_mechanism | lower == 'passthrough' %}
listen 8053 ssl;
{% if not ipv6_disabled %}
listen [::]:8053 ssl;
{% endif %}
ssl_certificate /etc/nginx/pki/web.crt;
ssl_certificate_key /etc/nginx/pki/web.key;
@@ -154,7 +154,9 @@ data:
ssl_prefer_server_ciphers on;
{% else %}
listen 8052 default_server;
{% if not ipv6_disabled %}
listen [::]:8052 default_server;
{% endif %}
{% endif %}
# If you have a domain name, this is where to add it
@@ -212,9 +214,7 @@ data:
uwsgi_read_timeout 120s;
uwsgi_pass uwsgi;
include /etc/nginx/uwsgi_params;
{%- if extra_nginx_include is defined %}
include {{ extra_nginx_include }};
{%- endif %}
include /etc/nginx/conf.d/*.conf;
proxy_set_header X-Forwarded-Port 443;
uwsgi_param HTTP_X_FORWARDED_PORT 443;

View File

@@ -0,0 +1,16 @@
{% if termination_grace_period_seconds is defined %}
apiVersion: v1
kind: ConfigMap
metadata:
name: '{{ ansible_operator_meta.name }}-{{ deployment_type }}-pre-stop-scripts'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
data:
termination-master: |
{{ lookup("file", "files/pre-stop/termination-master") | indent(width=4) | trim }}
termination-waiter: |
{{ lookup("file", "files/pre-stop/termination-waiter") | indent(width=4) | trim }}
termination-env: |
{{ lookup("file", "files/pre-stop/termination-env") | indent(width=4) | trim }}
{% endif %}

View File

@@ -6,8 +6,8 @@ metadata:
name: '{{ ansible_operator_meta.name }}'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
{{ lookup("template", "labels/common.yaml.j2") | indent(width=4) | trim }}
{{ lookup("template", "labels/version.yaml.j2") | indent(width=4) | trim }}
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
{{ lookup("template", "../common/templates/labels/version.yaml.j2") | indent(width=4) | trim }}
spec:
replicas: {{ replicas }}
selector:
@@ -18,10 +18,28 @@ spec:
template:
metadata:
labels:
{{ lookup("template", "labels/common.yaml.j2") | indent(width=8) | trim }}
{{ lookup("template", "labels/version.yaml.j2") | indent(width=8) | trim }}
{% if annotations %}
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=8) | trim }}
{{ lookup("template", "../common/templates/labels/version.yaml.j2") | indent(width=8) | trim }}
annotations:
{% for template in [
"configmaps/config",
"configmaps/pre_stop_scripts",
"secrets/app_credentials",
"storage/persistent",
] %}
checksum-{{ template | replace('/', '-') }}: "{{ lookup('template', template + '.yaml.j2') | sha1 }}"
{% endfor %}
{% for secret in [
"bundle_cacert",
"route_tls",
"ldap_cacert",
"secret_key",
"receptor_ca",
"receptor_work_signing",
] %}
checksum-secret-{{ secret }}: "{{ lookup('ansible.builtin.vars', secret, default='')["resources"][0]["data"] | default('') | sha1 }}"
{% endfor %}
{% if annotations %}
{{ annotations | indent(width=8) }}
{% endif %}
spec:
@@ -54,10 +72,6 @@ spec:
mkdir -p /etc/pki/ca-trust/extracted/{java,pem,openssl,edk2}
update-ca-trust
{% endif %}
{% if projects_persistence|bool and is_k8s|bool %}
chmod 775 /var/lib/awx/projects
chgrp 1000 /var/lib/awx/projects
{% endif %}
{% if init_container_extra_commands %}
{{ init_container_extra_commands | indent(width=14) }}
{% endif %}
@@ -85,12 +99,27 @@ spec:
subPath: bundle-ca.crt
readOnly: true
{% endif %}
{% if projects_persistence|bool and is_k8s|bool %}
- name: "{{ ansible_operator_meta.name }}-projects"
mountPath: "/var/lib/awx/projects"
{% endif %}
{% if init_container_extra_volume_mounts -%}
{{ init_container_extra_volume_mounts | indent(width=12, first=True) }}
{% endif %}
{% if projects_persistence|bool and is_k8s|bool %}
- name: init-projects
image: '{{ _init_projects_container_image }}'
imagePullPolicy: '{{ image_pull_policy }}'
command:
- /bin/sh
- -c
- |
chmod 775 /var/lib/awx/projects
chgrp 1000 /var/lib/awx/projects
env:
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
volumeMounts:
- name: "{{ ansible_operator_meta.name }}-projects"
mountPath: "/var/lib/awx/projects"
{% endif %}
containers:
- image: '{{ _redis_image }}'
@@ -111,6 +140,23 @@ spec:
mountPath: "/var/run/redis"
- name: "{{ ansible_operator_meta.name }}-redis-data"
mountPath: "/data"
{% if termination_grace_period_seconds is defined %}
- name: pre-stop-data
mountPath: /var/lib/pre-stop
- name: pre-stop-scripts
mountPath: /var/lib/pre-stop/scripts
lifecycle:
preStop:
exec:
command:
- bash
- -c
# redis image doesn't support writing to `/proc/1/fd/*`
- >
PRE_STOP_STDOUT=/dev/stdout
PRE_STOP_STDERR=/dev/stderr
/var/lib/pre-stop/scripts/termination-waiter
{% endif %}
resources: {{ redis_resource_requirements }}
- image: '{{ _image }}'
name: '{{ ansible_operator_meta.name }}-web'
@@ -280,6 +326,18 @@ spec:
{% endif %}
{% if task_extra_volume_mounts -%}
{{ task_extra_volume_mounts | indent(width=12, first=True) }}
{% endif %}
{% if termination_grace_period_seconds is defined %}
- name: pre-stop-data
mountPath: /var/lib/pre-stop
- name: pre-stop-scripts
mountPath: /var/lib/pre-stop/scripts
lifecycle:
preStop:
exec:
command:
- bash
- /var/lib/pre-stop/scripts/termination-master
{% endif %}
env:
- name: SUPERVISOR_WEB_CONFIG_PATH
@@ -349,6 +407,18 @@ spec:
mountPath: "/var/lib/awx/projects"
{% if ee_extra_volume_mounts -%}
{{ ee_extra_volume_mounts | indent(width=12, first=True) }}
{% endif %}
{% if termination_grace_period_seconds is defined %}
- name: pre-stop-data
mountPath: /var/lib/pre-stop
- name: pre-stop-scripts
mountPath: /var/lib/pre-stop/scripts
lifecycle:
preStop:
exec:
command:
- bash
- /var/lib/pre-stop/scripts/termination-waiter
{% endif %}
env:
{% if development_mode | bool %}
@@ -368,6 +438,10 @@ spec:
topologySpreadConstraints:
{{ topology_spread_constraints | indent(width=8) }}
{% endif %}
{% if affinity | length %}
affinity:
{{ affinity | to_nice_yaml | indent(width=8) }}
{% endif %}
{% if tolerations %}
tolerations:
{{ tolerations | indent(width=8) }}
@@ -380,6 +454,9 @@ spec:
{% if security_context_settings|length %}
{{ security_context_settings | to_nice_yaml | indent(8) }}
{% endif %}
{% endif %}
{% if termination_grace_period_seconds is defined %}
terminationGracePeriodSeconds: {{ termination_grace_period_seconds }}
{% endif %}
volumes:
{% if bundle_ca_crt %}
@@ -409,6 +486,14 @@ spec:
items:
- key: ldap-ca.crt
path: 'ldap-ca.crt'
{% endif %}
{% if termination_grace_period_seconds is defined %}
- name: pre-stop-data
emptyDir: {}
- name: pre-stop-scripts
configMap:
name: '{{ ansible_operator_meta.name }}-{{ deployment_type }}-pre-stop-scripts'
defaultMode: 0775
{% endif %}
- name: "{{ ansible_operator_meta.name }}-application-credentials"
secret:

View File

@@ -8,11 +8,7 @@ metadata:
name: '{{ ansible_operator_meta.name }}-ingress'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
app.kubernetes.io/name: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/managed-by: '{{ deployment_type }}-operator'
app.kubernetes.io/component: '{{ deployment_type }}'
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
{% if ingress_annotations %}
annotations:
{{ ingress_annotations | indent(width=4) }}
@@ -52,11 +48,7 @@ metadata:
name: '{{ ansible_operator_meta.name }}'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
app.kubernetes.io/name: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/managed-by: '{{ deployment_type }}-operator'
app.kubernetes.io/component: '{{ deployment_type }}'
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
spec:
{% if route_host != '' %}
host: {{ route_host }}

View File

@@ -5,11 +5,7 @@ metadata:
name: '{{ ansible_operator_meta.name }}-service'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
app.kubernetes.io/name: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/managed-by: '{{ deployment_type }}-operator'
app.kubernetes.io/component: '{{ deployment_type }}'
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
{{ service_labels | indent(width=4) }}
{% if service_annotations %}
annotations:
@@ -23,7 +19,9 @@ spec:
protocol: TCP
targetPort: 8052
name: http
{% if nodeport_port is defined %}
nodePort: {{ nodeport_port }}
{% endif %}
{% elif service_type | lower != 'loadbalancer' and loadbalancer_protocol | lower != 'https' %}
- port: 80
protocol: TCP

View File

@@ -5,11 +5,7 @@ metadata:
name: '{{ ansible_operator_meta.name }}'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
app.kubernetes.io/name: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/managed-by: '{{ deployment_type }}-operator'
app.kubernetes.io/component: '{{ deployment_type }}'
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
{% if service_account_annotations %}
annotations:
{{ service_account_annotations | indent(width=4) }}
@@ -20,6 +16,8 @@ kind: Role
metadata:
name: '{{ ansible_operator_meta.name }}'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods"]
@@ -40,6 +38,8 @@ apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: '{{ ansible_operator_meta.name }}'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
subjects:
- kind: ServiceAccount
name: '{{ ansible_operator_meta.name }}'

View File

@@ -5,10 +5,6 @@ metadata:
name: '{{ ansible_operator_meta.name }}-admin-password'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
app.kubernetes.io/name: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/managed-by: '{{ deployment_type }}-operator'
app.kubernetes.io/component: '{{ deployment_type }}'
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
stringData:
password: '{{ lookup('password', '/dev/null length=32 chars=ascii_letters,digits') }}'

View File

@@ -6,11 +6,7 @@ metadata:
name: '{{ ansible_operator_meta.name }}-app-credentials'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
app.kubernetes.io/name: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/managed-by: '{{ deployment_type }}-operator'
app.kubernetes.io/component: '{{ deployment_type }}'
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
data:
credentials.py: "{{ lookup('template', 'settings/credentials.py.j2') | b64encode }}"
ldap.py: "{{ lookup('template', 'settings/ldap.py.j2') | b64encode }}"

View File

@@ -5,10 +5,6 @@ metadata:
name: '{{ ansible_operator_meta.name }}-broadcast-websocket'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
app.kubernetes.io/name: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/managed-by: '{{ deployment_type }}-operator'
app.kubernetes.io/component: '{{ deployment_type }}'
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
stringData:
secret: '{{ lookup('password', '/dev/null length=32 chars=ascii_letters,digits') }}'

View File

@@ -6,11 +6,7 @@ metadata:
name: '{{ ansible_operator_meta.name }}-postgres-configuration'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
app.kubernetes.io/name: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/managed-by: '{{ deployment_type }}-operator'
app.kubernetes.io/component: '{{ deployment_type }}'
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
stringData:
password: '{{ lookup('password', '/dev/null length=32 chars=ascii_letters,digits') }}'
username: '{{ database_username }}'

View File

@@ -6,11 +6,7 @@ metadata:
name: '{{ ansible_operator_meta.name }}-postgres-configuration'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
app.kubernetes.io/name: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/managed-by: '{{ deployment_type }}-operator'
app.kubernetes.io/component: '{{ deployment_type }}'
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
stringData:
password: '{{ awx_postgres_pass }}'
username: '{{ awx_postgres_user }}'

View File

@@ -5,11 +5,7 @@ metadata:
name: '{{ ansible_operator_meta.name }}-receptor-ca'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
app.kubernetes.io/name: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/managed-by: '{{ deployment_type }}-operator'
app.kubernetes.io/component: '{{ deployment_type }}'
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
type: kubernetes.io/tls
data:
tls.crt: '{{ lookup('file', '{{ _receptor_ca_crt_file.path }}') | b64encode }}'

View File

@@ -5,11 +5,7 @@ metadata:
name: '{{ ansible_operator_meta.name }}-receptor-work-signing'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
app.kubernetes.io/name: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/managed-by: '{{ deployment_type }}-operator'
app.kubernetes.io/component: '{{ deployment_type }}'
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
data:
work-private-key.pem: '{{ lookup('file', '{{ _receptor_work_signing_private_key_file.path }}') | b64encode }}'
work-public-key.pem: '{{ lookup('file', '{{ _receptor_work_signing_public_key_file.path }}') | b64encode }}'

View File

@@ -5,10 +5,6 @@ metadata:
name: '{{ ansible_operator_meta.name }}-secret-key'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
app.kubernetes.io/name: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/managed-by: '{{ deployment_type }}-operator'
app.kubernetes.io/component: '{{ deployment_type }}'
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
stringData:
secret_key: '{{ lookup('password', '/dev/null length=32 chars=ascii_letters,digits') }}'

View File

@@ -6,6 +6,7 @@ metadata:
name: '{{ ansible_operator_meta.name }}-postgres-{{ supported_pg_version }}'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
{{ lookup("template", "../common/templates/labels/additional_labels.yaml.j2") | indent(width=4) | trim }}
app.kubernetes.io/name: 'postgres-{{ supported_pg_version }}'
app.kubernetes.io/instance: 'postgres-{{ supported_pg_version }}-{{ ansible_operator_meta.name }}'
app.kubernetes.io/component: 'database'
@@ -27,6 +28,7 @@ spec:
template:
metadata:
labels:
{{ lookup("template", "../common/templates/labels/additional_labels.yaml.j2") | indent(width=8) | trim }}
app.kubernetes.io/name: 'postgres-{{ supported_pg_version }}'
app.kubernetes.io/instance: 'postgres-{{ supported_pg_version }}-{{ ansible_operator_meta.name }}'
app.kubernetes.io/component: 'database'
@@ -127,6 +129,7 @@ metadata:
name: '{{ ansible_operator_meta.name }}-postgres-{{ supported_pg_version }}'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
{{ lookup("template", "../common/templates/labels/additional_labels.yaml.j2") | indent(width=4) | trim }}
app.kubernetes.io/name: 'postgres-{{ supported_pg_version }}'
app.kubernetes.io/instance: 'postgres-{{ supported_pg_version }}-{{ ansible_operator_meta.name }}'
app.kubernetes.io/component: 'database'

View File

@@ -5,11 +5,7 @@ metadata:
name: '{{ ansible_operator_meta.name }}-projects-claim'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
app.kubernetes.io/name: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/managed-by: '{{ deployment_type }}-operator'
app.kubernetes.io/component: '{{ deployment_type }}'
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
spec:
accessModes:
- {{ projects_storage_access_mode }}

View File

@@ -17,6 +17,12 @@ This role assumes you are authenticated with an Openshift or Kubernetes cluster:
- AWX is deployed to via the operator
- An AWX backup is available on a PVC in your cluster (see the backup [README.md](../backup/README.md))
*Before Restoring from a backup*, be sure to:
- delete the old existing AWX CR
- delete the persistent volume claim (PVC) for the database from the old deployment, which has a name like `postgres-13-<deployment-name>-postgres-13-0`
**Note**: Do not delete the namespace/project, as that will delete the backup and the backup's PVC as well.
Usage
----------------

View File

@@ -11,6 +11,9 @@ backup_pvc_namespace: '{{ ansible_operator_meta.namespace }}'
# Required: backup name, found on the awxbackup object
backup_dir: ''
# Default cluster name
cluster_name: 'cluster.local'
# Set no_log settings on certain tasks
no_log: true
@@ -23,6 +26,9 @@ restore_resource_requirements:
cpu: "25m"
memory: "32Mi"
# Labels defined on the resource, which should be propagated to child resources
additional_labels: []
# Maintain some of the recommended `app.kubernetes.io/*` labels on the resource (self)
set_self_labels: true
...

View File

@@ -24,7 +24,8 @@ galaxy_info:
- restore
- automation
dependencies: []
dependencies:
- role: common
collections:
- kubernetes.core

View File

@@ -1,27 +1,5 @@
---
- name: Get AWX object definition from pvc
k8s_exec:
namespace: "{{ backup_pvc_namespace }}"
pod: "{{ ansible_operator_meta.name }}-db-management"
command: >-
bash -c "cat '{{ backup_dir }}/awx_object'"
register: awx_object
- name: Create temp file for spec dict
tempfile:
state: file
register: tmp_spec
- name: Write spec vars to temp file
copy:
content: "{{ awx_object.stdout }}"
dest: "{{ tmp_spec.path }}"
mode: '0644'
- name: Include spec vars to save them as a dict
include_vars: "{{ tmp_spec.path }}"
- name: Deploy AWX
k8s:
state: "{{ state | default('present') }}"

View File

@@ -0,0 +1,25 @@
---
- name: Import awx_object variables
block:
- name: Get AWX object definition from pvc
k8s_exec:
namespace: "{{ backup_pvc_namespace }}"
pod: "{{ ansible_operator_meta.name }}-db-management"
command: >-
bash -c "cat '{{ backup_dir }}/awx_object'"
register: awx_object
- name: Create temp file for spec dict
tempfile:
state: file
register: tmp_spec
- name: Write spec vars to temp file
copy:
content: "{{ awx_object.stdout }}"
dest: "{{ tmp_spec.path }}"
mode: '0644'
- name: Include spec vars to save them as a dict
include_vars: "{{ tmp_spec.path }}"

View File

@@ -10,12 +10,7 @@
metadata:
name: '{{ ansible_operator_meta.name }}'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
app.kubernetes.io/name: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/managed-by: '{{ deployment_type }}-operator'
app.kubernetes.io/component: '{{ deployment_type }}'
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
labels: '{{ lookup("template", "../common/templates/labels/common.yaml.j2") | from_yaml }}'
when: set_self_labels | bool
- name: Look up details for this restore object
@@ -26,23 +21,35 @@
namespace: "{{ ansible_operator_meta.namespace }}"
register: this_restore
- name: Build `additional_labels_items` labels from `additional_labels`
set_fact:
additional_labels_items: >-
{{ this_restore['resources'][0]['metadata']['labels']
| dict2items | selectattr('key', 'in', additional_labels)
}}
when:
- additional_labels | length
- this_restore['resources'][0]['metadata']['labels']
- block:
- include_tasks: init.yml
- include_tasks: init.yml
- include_tasks: secrets.yml
- include_tasks: import_vars.yml
- include_tasks: deploy_awx.yml
- include_tasks: secrets.yml
- include_tasks: postgres.yml
- include_tasks: deploy_awx.yml
- name: Set flag signifying this restore was successful
set_fact:
tower_restore_complete: True
- include_tasks: postgres.yml
- include_tasks: cleanup.yml
- name: Set flag signifying this restore was successful
set_fact:
tower_restore_complete: True
- include_tasks: cleanup.yml
when:
- this_restore['resources'][0]['status']['restoreComplete'] is not defined
- this_restore['resources'][0]['status']['restoreComplete'] is not defined
- name: Update status variables
include_tasks: update_status.yml

View File

@@ -66,9 +66,8 @@
- name: Set full resolvable host name for postgres pod
set_fact:
resolvable_db_host: "{{ awx_postgres_host }}.{{ ansible_operator_meta.namespace }}.svc.cluster.local"
resolvable_db_host: '{{ (awx_postgres_type == "managed") | ternary(awx_postgres_host + "." + ansible_operator_meta.namespace + ".svc." + cluster_name, awx_postgres_host) }}' # yamllint disable-line rule:line-length
no_log: "{{ no_log }}"
when: awx_postgres_type == 'managed'
- name: Set pg_restore command
set_fact:
@@ -76,7 +75,6 @@
pg_restore --clean --if-exists
-U {{ awx_postgres_user }}
-h {{ resolvable_db_host }}
-U {{ awx_postgres_user }}
-d {{ awx_postgres_database }}
-p {{ awx_postgres_port }}
no_log: "{{ no_log }}"

View File

@@ -54,6 +54,37 @@
no_log: "{{ no_log }}"
when: secrets['postgresConfigurationSecret']['data']['type'] | b64decode == 'managed'
- name: Set new receptor secret names
set_fact:
previous_receptor_ca_name: "{{ previous_deployment_name }}-receptor-ca"
previous_receptor_tls_name: "{{ previous_deployment_name }}-receptor-work-signing"
no_log: "{{ no_log }}"
- name: Set new name for receptor secrets using deployment_name
block:
- name: Set new receptor secret names
set_fact:
receptor_ca_name: "{{ deployment_name }}-receptor-ca"
receptor_work_signing_name: "{{ deployment_name }}-receptor-work-signing"
no_log: "{{ no_log }}"
- name: Set tmp dict for receptor secrets
set_fact:
_ca_secret: "{{ secrets[previous_receptor_ca_name] }}"
_work_signing_secret: "{{ secrets[previous_receptor_tls_name] }}"
no_log: "{{ no_log }}"
- name: Change receptor secret names in tmp dict
set_fact:
_ca_secret_name: "{{ _ca_secret | combine({ 'name': receptor_ca_name }) }}"
_work_signing_secret_name: "{{ _work_signing_secret | combine({ 'name': receptor_work_signing_name}) }}"
no_log: "{{ no_log }}"
- name: Create a new dict of receptor secrets with updated names
set_fact:
secrets: "{{ secrets | combine({previous_receptor_ca_name: _ca_secret_name, previous_receptor_tls_name: _work_signing_secret_name}) }}"
no_log: "{{ no_log }}"
- name: Apply secret
k8s:
state: present

View File

@@ -5,11 +5,7 @@ metadata:
name: {{ ansible_operator_meta.name }}-db-management
namespace: {{ backup_pvc_namespace }}
labels:
app.kubernetes.io/name: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/managed-by: '{{ deployment_type }}-operator'
app.kubernetes.io/component: '{{ deployment_type }}'
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
spec:
containers:
- name: {{ ansible_operator_meta.name }}-db-management

View File

@@ -6,11 +6,7 @@ metadata:
name: '{{ secrets[secret]['name'] }}'
namespace: '{{ ansible_operator_meta.namespace }}'
labels:
app.kubernetes.io/name: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}'
app.kubernetes.io/managed-by: '{{ deployment_type }}-operator'
app.kubernetes.io/component: '{{ deployment_type }}'
app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}'
{{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }}
type: '{{ secrets[secret]['type'] }}'
stringData:
{% for key, value in secrets[secret]['data'].items() %}