Merge pull request #4 from matburt/awxify

Moving towards AWX as the default
This commit is contained in:
Matthew Jones
2020-08-18 16:55:38 -04:00
committed by GitHub
41 changed files with 428 additions and 335 deletions

View File

@@ -2,8 +2,17 @@
services: docker
language: python
before_install:
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
- sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
- sudo apt-get update
- sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
env:
- DOCKER_API_VERSION=1.38
install:
- pip3 install docker molecule yamllint ansible-lint openshift jmespath
- pip3 install docker molecule==3.0.6 yamllint ansible-lint openshift jmespath
script:
- molecule test -s test-local

View File

@@ -1,8 +1,8 @@
# Ansible Tower/AWX Operator
An [Ansible Tower](https://www.ansible.com/products/tower) operator for Kubernetes built with [Operator SDK](https://github.com/operator-framework/operator-sdk) and Ansible.
An [Ansible AWX](https://github.com/ansible/awx) operator for Kubernetes built with [Operator SDK](https://github.com/operator-framework/operator-sdk) and Ansible.
Also configurable to run the open source [AWX](https://github.com/ansible/awx) instead of Tower (helpful for certain use cases where a license requirement is not warranted, like CI environments).
Also configurable to be able to run [Tower](https://ansible.com/products/tower)
## Purpose
@@ -11,7 +11,7 @@ There are already official OpenShift/Kubernetes installers available for both AW
- [AWX on Kubernetes](https://github.com/ansible/awx/blob/devel/INSTALL.md#kubernetes)
- [Ansible Tower on Kubernetes](https://docs.ansible.com/ansible-tower/latest/html/administration/openshift_configuration.html)
This operator is meant to provide a more Kubernetes-native installation method for Ansible Tower or AWX via a Tower Custom Resource Definition (CRD).
This operator is meant to provide a more Kubernetes-native installation method for Ansible Tower or AWX via an AWX Custom Resource Definition (CRD).
Note that the operator is not supported by Red Hat, and is in alpha status. Long-term, this operator will become the supported method of installing on Kubernetes and Openshift, and will be listed on OperatorHub.io. For now, use it at your own risk!
@@ -19,59 +19,42 @@ Note that the operator is not supported by Red Hat, and is in alpha status. Long
This Kubernetes Operator is meant to be deployed in your Kubernetes cluster(s) and can manage one or more Tower or AWX instances in any namespace.
First you need to deploy Tower Operator into your cluster:
First you need to deploy AWX Operator into your cluster:
kubectl apply -f https://raw.githubusercontent.com/ansible/tower-operator/devel/deploy/tower-operator.yaml
kubectl apply -f https://raw.githubusercontent.com/ansible/awx-operator/devel/deploy/awx-operator.yaml
Then you can create instances of Tower, for example:
Then you can create instances of AWX, for example:
1. Make sure the namespace you're deploying into already exists (e.g. `kubectl create namespace ansible-tower`).
1. Create a file named `my-tower.yml` with the following contents:
1. Make sure the namespace you're deploying into already exists (e.g. `kubectl create namespace ansible-awx`).
2. Create a file named `my-awx.yml` with the following contents:
```
---
apiVersion: tower.ansible.com/v1beta1
kind: Tower
apiVersion: awx.ansible.com/v1beta1
kind: AWX
metadata:
name: tower
namespace: ansible-tower
name: awx
namespace: ansible-awx
spec:
deployment_type: tower
tower_secret_key: aabbcc
deployment_type: awx
tower_admin_user: test
tower_admin_email: test@example.com
tower_admin_password: changeme
tower_broadcast_websocket_secret: changeme
```
1. Use `kubectl` to create the mcrouter instance in your cluster:
3. Use `kubectl` to create the mcrouter instance in your cluster:
```
kubectl apply -f my-tower.yml
kubectl apply -f my-awx.yml
```
After a few minutes, your new Tower instance will be accessible at `http://tower.mycompany.com/` (assuming your cluster has an Ingress controller configured). Log in using the `tower_admin_` credentials configured in the `spec`, and supply a valid license to begin using Tower.
After a few minutes, your new AWX instance will be accessible at `http://awx.mycompany.com/` (assuming your cluster has an Ingress controller configured). Log in using the `tower_admin_` credentials configured in the `spec`.
### Red Hat Registry Authentication
To deploy Ansible Tower, images are pulled from the Red Hat Registry. Your Kubernetes or OpenShift cluster will have to have [Authentication Enabled for the Red Hat Registry](https://access.redhat.com/documentation/en-us/openshift_container_platform/3.11/html/configuring_clusters/install-config-configuring-red-hat-registry) for this to work, otherwise the Tower image will not be pulled.
If you deploy Ansible AWX, images are available from public registries, so no authentication is required.
### Deploy AWX instead of Tower
If you would like to deploy AWX (the open source upstream of Tower) into your cluster instead of Tower, override the default variables in the Tower `spec` for the `tower_task_image` and `tower_web_image`, so the AWX container images are used instead, and set the `deployment_type` to ``awx`:
---
spec:
...
deployment_type: awx
tower_task_image: ansible/awx_task:11.2.0
tower_web_image: ansible/awx_web:11.2.0
### Ingress Types
Depending on the cluster that you're running on, you may wish to use an `Ingress` to access your tower or you may wish to use a `Route` to access your tower. To toggle between these two options, you can add the following to your Tower custom resource:
Depending on the cluster that you're running on, you may wish to use an `Ingress` to access your tower or you may wish to use a `Route` to access your awx. To toggle between these two options, you can add the following to your Tower custom resource:
---
spec:
@@ -84,7 +67,7 @@ OR
spec:
...
tower_ingress_type: Ingress
tower_hostname: tower.mycompany.com
tower_hostname: awx.mycompany.com
By default, no ingress/route is deployed as the default is set to `none`.
@@ -97,9 +80,9 @@ Depending on the type of tasks that you'll be running, you may find that you nee
...
tower_task_privileged: true
If you are attempting to do this on an OpenShift cluster, you will need to grant the `tower` ServiceAccount the `privileged` SCC, which can be done with:
If you are attempting to do this on an OpenShift cluster, you will need to grant the `awx` ServiceAccount the `privileged` SCC, which can be done with:
oc adm policy add-scc-to-user privileged -z tower
oc adm policy add-scc-to-user privileged -z awx
Again, this is the most relaxed SCC that is provided by OpenShift, so be sure to familiarize yourself with the security concerns that accompany this action.
@@ -152,8 +135,8 @@ Once the operator is deployed, you can visit the Tower UI in your browser by fol
There are a few moving parts to this project:
1. The Docker image which powers Tower Operator.
2. The `tower-operator.yaml` Kubernetes manifest file which initially deploys the Operator into a cluster.
1. The Docker image which powers AWX Operator.
2. The `awx-operator.yaml` Kubernetes manifest file which initially deploys the Operator into a cluster.
Each of these must be appropriately built in preparation for a new tag:
@@ -161,17 +144,17 @@ Each of these must be appropriately built in preparation for a new tag:
Run the following command inside this directory:
operator-sdk build ansible/tower-operator:0.4.0
operator-sdk build ansible/awx-operator:0.4.0
Then push the generated image to Docker Hub:
docker push ansible/tower-operator:0.4.0
docker push ansible/awx-operator:0.4.0
#### Build a new version of the `tower-operator.yaml` file
#### Build a new version of the `awx-operator.yaml` file
Update the tower-operator version in two places:
Update the awx-operator version in two places:
1. `deploy/tower-operator.yaml`: in the `ansible` and `operator` container definitions in the `tower-operator` Deployment.
1. `deploy/awx-operator.yaml`: in the `ansible` and `operator` container definitions in the `awx-operator` Deployment.
2. `build/chain-operator-files.yml`: the `operator_image` variable.
Once the versions are updated, run the playbook in the `build/` directory:
@@ -182,8 +165,8 @@ After it is built, test it on a local cluster:
minikube start --memory 6g --cpus 4
minikube addons enable ingress
kubectl apply -f deploy/tower-operator.yaml
kubectl create namespace example-tower
kubectl apply -f deploy/awx-operator.yaml
kubectl create namespace example-awx
kubectl apply -f deploy/crds/tower_v1beta1_tower_cr_awx.yaml
<test everything>
minikube delete

View File

@@ -6,19 +6,19 @@
gather_facts: false
vars:
operator_image: ansible/tower-operator:0.4.0
operator_image: ansible/awx-operator:0.4.0
pull_policy: Always
operator_file_path: "../deploy/tower-operator.yaml"
operator_file_path: "../deploy/awx-operator.yaml"
operator_template: "../deploy/operator.yaml"
tasks:
- name: Clear out current contents of tower-operator.yml
- name: Clear out current contents of awx-operator.yml
copy:
dest: "{{ operator_file_path }}"
content: ''
force: true
- name: Concatenate operator files into tower-operator.yml
- name: Concatenate operator files into awx-operator.yml
blockinfile:
path: "{{ operator_file_path }}"
block: "{{ item }}"
@@ -33,12 +33,12 @@
- "../deploy/service_account.yaml"
- "../deploy/operator.yaml"
- name: Remove space at beginning of tower-operator.yml
- name: Remove space at beginning of awx-operator.yml
shell: >
echo "$(tail -n +2 {{ operator_file_path }})" > {{ operator_file_path }}
changed_when: true
- name: Template the tower-operator.yaml file into tower-operator.yml
- name: Template the awx-operator.yaml file into awx-operator.yml
template:
src: "{{ operator_file_path }}"
dest: "{{ operator_file_path }}"

View File

@@ -3,7 +3,7 @@ FROM ${BASEIMAGE}
USER 0
RUN yum install -y python-devel gcc libffi-devel
RUN pip install molecule==2.20.1
RUN pip install molecule==3.0.6 jmespath
ARG NAMESPACEDMAN
ADD $NAMESPACEDMAN /namespaced.yaml

View File

@@ -3,7 +3,7 @@ apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: tower-operator
name: awx-operator
rules:
- apiGroups:
- route.openshift.io
@@ -45,7 +45,7 @@ rules:
- apiGroups:
- apps
resourceNames:
- tower-operator
- awx-operator
resources:
- deployments/finalizers
verbs:
@@ -64,7 +64,7 @@ rules:
verbs:
- get
- apiGroups:
- tower.ansible.com
- awx.ansible.com
resources:
- '*'
verbs:
@@ -74,53 +74,54 @@ rules:
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: tower-operator
name: awx-operator
subjects:
- kind: ServiceAccount
name: tower-operator
name: awx-operator
namespace: default
roleRef:
kind: ClusterRole
name: tower-operator
name: awx-operator
apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: tower-operator
name: awx-operator
namespace: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tower-operator
name: awx-operator
namespace: default
spec:
replicas: 1
selector:
matchLabels:
name: tower-operator
name: awx-operator
template:
metadata:
labels:
name: tower-operator
name: awx-operator
spec:
serviceAccountName: tower-operator
serviceAccountName: awx-operator
containers:
- name: ansible
command:
- /usr/local/bin/ao-logs
- /tmp/ansible-operator/runner
- stdout
image: "ansible/tower-operator:0.4.0"
image: "ansible/awx-operator:0.4.0"
imagePullPolicy: "Always"
volumeMounts:
- mountPath: /tmp/ansible-operator/runner
name: runner
readOnly: true
- name: operator
image: "ansible/tower-operator:0.4.0"
image: "ansible/awx-operator:0.4.0"
imagePullPolicy: "Always"
volumeMounts:
- mountPath: /tmp/ansible-operator/runner
@@ -134,7 +135,7 @@ spec:
fieldRef:
fieldPath: metadata.name
- name: OPERATOR_NAME
value: tower-operator
value: awx-operator
volumes:
- name: runner
emptyDir: {}
@@ -143,14 +144,14 @@ spec:
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: towers.tower.ansible.com
name: awxs.awx.ansible.com
spec:
group: tower.ansible.com
group: awx.ansible.com
names:
kind: Tower
listKind: TowerList
plural: towers
singular: tower
kind: AWX
listKind: AWXList
plural: awxs
singular: awx
scope: Namespaced
subresources:
status: {}
@@ -161,7 +162,7 @@ spec:
storage: true
validation:
openAPIV3Schema:
description: Schema validation for the Tower CRD
description: Schema validation for the AWX CRD
type: object
properties:
spec:
@@ -170,5 +171,27 @@ spec:
deployment_type:
type: string
pattern: "^(tower|awx)(-)?.*$"
external_database:
type: bool
description: |
If true you must supply a secret containing the location and credentials for
connecting to the external database by a user who has permission to create
and apply a schema.
The secret should have the name: <custom resource name>-postgres-configuration and
should look like:
apiVersion: v1
kind: Secret
metadata:
name: <crname>-postgres-configuration
namespace: <target namespace>
stringData:
address: <external ip or url resolvable by the cluster>
port: <external port, this usually defaults to 5432>
database: <desired database name>
username: <username to connect as>
password: <password to connect with>
type: Opaque
required:
- deployment_type

View File

@@ -1,24 +1,23 @@
---
apiVersion: tower.ansible.com/v1beta1
kind: Tower
apiVersion: awx.ansible.com/v1beta1
kind: AWX
metadata:
name: example-tower
namespace: example-tower
name: example-awx
namespace: example-awx
spec:
deployment_type: awx
tower_ingress_type: none
tower_task_privileged: false
tower_hostname: example-tower.test
tower_secret_key: aabbcc
tower_hostname: example-awx.test
tower_broadcast_websocket_secret: changeme
tower_admin_user: test
tower_admin_email: test@example.com
tower_admin_password: changeme
tower_task_image: ansible/awx_task:11.2.0
tower_web_image: ansible/awx_web:11.2.0
tower_task_image: ansible/awx:14.0.0
tower_web_image: ansible/awx:14.0.0
tower_task_mem_request: 1Gi
tower_task_cpu_request: 500m
@@ -32,7 +31,5 @@ spec:
tower_redis_image: redis:latest
tower_postgres_pass: awxpass
tower_postgres_image: postgres:10
tower_postgres_storage_request: 8Gi
tower_postgres_storage_class: ''

View File

@@ -0,0 +1,55 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: awxs.awx.ansible.com
spec:
group: awx.ansible.com
names:
kind: AWX
listKind: AWXList
plural: awxs
singular: awx
scope: Namespaced
subresources:
status: {}
version: v1beta1
versions:
- name: v1beta1
served: true
storage: true
validation:
openAPIV3Schema:
description: Schema validation for the AWX CRD
type: object
properties:
spec:
type: object
properties:
deployment_type:
type: string
pattern: "^(tower|awx)(-)?.*$"
external_database:
type: bool
description: |
If true you must supply a secret containing the location and credentials for
connecting to the external database by a user who has permission to create
and apply a schema.
The secret should have the name: <custom resource name>-postgres-configuration and
should look like:
apiVersion: v1
kind: Secret
metadata:
name: <crname>-postgres-configuration
namespace: <target namespace>
stringData:
address: <external ip or url resolvable by the cluster>
port: <external port, this usually defaults to 5432>
database: <desired database name>
username: <username to connect as>
password: <password to connect with>
type: Opaque
required:
- deployment_type

View File

@@ -1,24 +1,23 @@
---
apiVersion: tower.ansible.com/v1beta1
kind: Tower
apiVersion: awx.ansible.com/v1beta1
kind: AWX
metadata:
name: example-tower
namespace: example-tower
name: example-awx
namespace: example-awx
spec:
deployment_type: awx
tower_ingress_type: ingress
tower_task_privileged: false
tower_hostname: example-tower.test
tower_secret_key: aabbcc
tower_hostname: example-awx.test
tower_broadcast_websocket_secret: changeme
tower_admin_user: test
tower_admin_email: test@example.com
tower_admin_password: changeme
tower_task_image: ansible/awx_task:11.2.0
tower_web_image: ansible/awx_web:11.2.0
tower_task_image: ansible/awx:14.0.0
tower_web_image: ansible/awx:14.0.0
tower_task_mem_request: 128M
tower_task_cpu_request: 500m

View File

@@ -1,38 +0,0 @@
---
apiVersion: tower.ansible.com/v1beta1
kind: Tower
metadata:
name: example-tower
namespace: example-tower
spec:
deployment_type: tower
tower_ingress_type: none
tower_task_privileged: false
tower_hostname: example-tower.test
tower_secret_key: aabbcc
tower_broadcast_websocket_secret: changeme
tower_admin_user: test
tower_admin_email: test@example.com
tower_admin_password: changeme
tower_task_image: registry.redhat.io/ansible-tower-37/ansible-tower-rhel7:3.7.0
tower_web_image: registry.redhat.io/ansible-tower-37/ansible-tower-rhel7:3.7.0
tower_task_mem_request: 1Gi
tower_task_cpu_request: 500m
tower_web_mem_request: 2Gi
tower_web_cpu_request: 1000m
tower_create_preload_data: true
tower_memcached_image: memcached:alpine
tower_redis_image: redis:latest
tower_postgres_pass: awxpass
tower_postgres_image: postgres:10
tower_postgres_storage_request: 8Gi
tower_postgres_storage_class: ''

View File

@@ -1,33 +0,0 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: towers.tower.ansible.com
spec:
group: tower.ansible.com
names:
kind: Tower
listKind: TowerList
plural: towers
singular: tower
scope: Namespaced
subresources:
status: {}
version: v1beta1
versions:
- name: v1beta1
served: true
storage: true
validation:
openAPIV3Schema:
description: Schema validation for the Tower CRD
type: object
properties:
spec:
type: object
properties:
deployment_type:
type: string
pattern: "^(tower|awx)(-)?.*$"
required:
- deployment_type

View File

@@ -2,18 +2,18 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: tower-operator
name: awx-operator
spec:
replicas: 1
selector:
matchLabels:
name: tower-operator
name: awx-operator
template:
metadata:
labels:
name: tower-operator
name: awx-operator
spec:
serviceAccountName: tower-operator
serviceAccountName: awx-operator
containers:
- name: ansible
command:
@@ -41,7 +41,7 @@ spec:
fieldRef:
fieldPath: metadata.name
- name: OPERATOR_NAME
value: tower-operator
value: awx-operator
volumes:
- name: runner
emptyDir: {}

View File

@@ -3,7 +3,7 @@ apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: tower-operator
name: awx-operator
rules:
- apiGroups:
- route.openshift.io
@@ -45,7 +45,7 @@ rules:
- apiGroups:
- apps
resourceNames:
- tower-operator
- awx-operator
resources:
- deployments/finalizers
verbs:
@@ -64,7 +64,7 @@ rules:
verbs:
- get
- apiGroups:
- tower.ansible.com
- awx.ansible.com
resources:
- '*'
verbs:

View File

@@ -2,12 +2,12 @@
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: tower-operator
name: awx-operator
subjects:
- kind: ServiceAccount
name: tower-operator
name: awx-operator
namespace: default
roleRef:
kind: ClusterRole
name: tower-operator
name: awx-operator
apiGroup: rbac.authorization.k8s.io

View File

@@ -2,5 +2,4 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: tower-operator
namespace: default
name: awx-operator

View File

@@ -2,4 +2,4 @@
- hosts: localhost
gather_facts: no
roles:
- tower
- awx

View File

@@ -7,14 +7,14 @@
ansible_python_interpreter: '{{ ansible_playbook_python }}'
tasks:
- name: Get tower Pod data
- name: Get AWX Pod data
k8s_info:
kind: Pod
namespace: example-tower
namespace: example-awx
label_selectors:
- app=tower
- app=awx
register: tower_pods
- name: Verify there is one tower pod
- name: Verify there is one AWX pod
assert:
that: '{{ (tower_pods.resources | length) == 1 }}'

View File

@@ -5,6 +5,6 @@
vars:
ansible_python_interpreter: '{{ ansible_playbook_python }}'
roles:
- tower
- awx
- import_playbook: '{{ playbook_dir }}/asserts.yml'

View File

@@ -10,7 +10,7 @@
tasks:
- name: Create Custom Resource Definition
k8s:
definition: "{{ lookup('file', '/'.join([deploy_dir, 'crds/tower_v1beta1_tower_crd.yaml'])) }}"
definition: "{{ lookup('file', '/'.join([deploy_dir, 'crds/awx_v1beta1_crd.yaml'])) }}"
- name: Ensure specified namespace is present
k8s:

View File

@@ -3,7 +3,7 @@
hosts: k8s
vars:
image_name: tower.ansible.com/tower-operator:testing
image_name: awx.ansible.com/awx-operator:testing
tasks:
# using command so we don't need to install any dependencies
@@ -25,8 +25,8 @@
ansible_python_interpreter: '{{ ansible_playbook_python }}'
deploy_dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/deploy"
pull_policy: Never
operator_image: tower.ansible.com/tower-operator:testing
custom_resource: "{{ lookup('file', '/'.join([deploy_dir, 'crds/tower_v1beta1_tower_cr_molecule.yaml'])) | from_yaml }}"
operator_image: awx.ansible.com/awx-operator:testing
custom_resource: "{{ lookup('file', '/'.join([deploy_dir, 'crds/awx_v1beta1_molecule.yaml'])) | from_yaml }}"
tasks:
@@ -59,14 +59,14 @@
namespace: '{{ operator_namespace }}'
definition: "{{ lookup('template', '/'.join([deploy_dir, 'operator.yaml'])) }}"
- name: Ensure the Tower custom_resource namespace exists
- name: Ensure the AWX custom_resource namespace exists
k8s:
state: present
name: '{{ custom_resource.metadata.namespace }}'
kind: Namespace
api_version: v1
- name: Create the Tower Custom Resource
- name: Create the AWX Custom Resource
k8s:
state: present
namespace: '{{ custom_resource.metadata.namespace }}'
@@ -99,7 +99,7 @@
resource_name=custom_resource.metadata.name
)}}'
- name: debug tower deployment
- name: debug awx deployment
ignore_errors: yes
failed_when: false
debug:
@@ -109,7 +109,7 @@
kind="Deployment",
api_version="apps/v1",
namespace=custom_resource.metadata.namespace,
label_selector="app=tower"
label_selector="app=awx"
)}}'
- name: get operator logs

View File

@@ -22,6 +22,7 @@
regexp: 8443
replace: "{{ lookup('env', 'KIND_PORT') }}"
path: '{{ kubeconfig }}'
mode: 0644
delegate_to: localhost
- name: Wait for the Kubernetes API to become available (this could take a minute)

View File

@@ -7,7 +7,7 @@
connection: local
vars:
image_name: tower.ansible.com/tower-operator:testing
image_name: awx.ansible.com/awx-operator:testing
tasks:
# Use raw Docker commands inside Minikube to avoid extra Python dependencies.
@@ -33,9 +33,9 @@
ansible_python_interpreter: '{{ ansible_playbook_python }}'
deploy_dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/deploy"
pull_policy: Never
operator_image: tower.ansible.com/tower-operator:testing
operator_image: awx.ansible.com/awx-operator:testing
# Change this to _awx to test AWX, _tower to test Tower.
custom_resource: "{{ lookup('file', '/'.join([deploy_dir, 'crds/tower_v1beta1_tower_cr_awx.yaml'])) | from_yaml }}"
custom_resource: "{{ lookup('file', '/'.join([deploy_dir, 'crds/awx_v1beta1_molecule.yaml'])) | from_yaml }}"
tasks:
- block:
@@ -66,14 +66,14 @@
namespace: '{{ operator_namespace }}'
definition: "{{ lookup('template', '/'.join([deploy_dir, 'operator.yaml'])) }}"
- name: Ensure the Tower custom_resource namespace exists
- name: Ensure the AWX custom_resource namespace exists
k8s:
state: present
name: '{{ custom_resource.metadata.namespace }}'
kind: Namespace
api_version: v1
- name: Create the Tower Custom Resource
- name: Create the AWX Custom Resource
k8s:
state: present
namespace: '{{ custom_resource.metadata.namespace }}'
@@ -106,7 +106,7 @@
resource_name=custom_resource.metadata.name
)}}'
- name: debug tower deployment
- name: debug awx deployment
ignore_errors: yes
failed_when: false
debug:
@@ -116,7 +116,7 @@
kind="Deployment",
api_version="apps/v1",
namespace=custom_resource.metadata.namespace,
label_selector="app=tower"
label_selector="app=awx"
)}}'
- name: get operator logs

View File

@@ -2,8 +2,7 @@
tower_task_privileged: false
tower_ingress_type: none
tower_hostname: example-tower.test
tower_secret_key: aabbcc
tower_hostname: example-awx.test
tower_admin_user: test
tower_admin_email: test@example.com
@@ -12,12 +11,12 @@ tower_admin_password: changeme
tower_broadcast_websocket_secret: changeme
# Use these image versions for Ansible Tower.
tower_task_image: registry.redhat.io/ansible-tower-37/ansible-tower-rhel7:3.7.0
tower_web_image: registry.redhat.io/ansible-tower-37/ansible-tower-rhel7:3.7.0
# tower_task_image: registry.redhat.io/ansible-tower-37/ansible-tower-rhel7:3.7.0
# tower_web_image: registry.redhat.io/ansible-tower-37/ansible-tower-rhel7:3.7.0
# Use these image versions for Ansible AWX.
# tower_task_image: ansible/awx_task:11.2.0
# tower_web_image: ansible/awx_web:11.2.0
tower_task_image: ansible/awx:14.0.0
tower_web_image: ansible/awx:14.0.0
tower_create_preload_data: true

View File

@@ -1,7 +1,7 @@
---
galaxy_info:
author: Ansible
description: Tower role for Tower Operator for Kubernetes.
description: AWX role for AWX Operator for Kubernetes.
company: Red Hat, Inc.
license: MIT

View File

@@ -1,5 +1,5 @@
---
- name: Check if there are any Tower super users defined.
- name: Check if there are any AWX super users defined.
shell: >-
kubectl exec -n {{ meta.namespace }} -c {{ meta.name }}-task {{ tower_pod_name }} -- bash -c
"echo 'from django.contrib.auth.models import User;
@@ -10,7 +10,7 @@
register: users_result
changed_when: users_result.rc > 0
- name: Create Tower super user via Django if it doesn't exist.
- name: Create AWX super user via Django if it doesn't exist.
shell: >-
kubectl exec -n {{ meta.namespace }} -c {{ meta.name }}-task {{ tower_pod_name }} -- bash -c
"echo \"from django.contrib.auth.models import User;

110
roles/awx/tasks/main.yml Normal file
View File

@@ -0,0 +1,110 @@
---
- name: Include deployment type vars
include_vars: "{{ deployment_type }}.yml"
- name: Check for existing secret key
k8s_info:
kind: Secret
namespace: '{{ meta.namespace }}'
name: '{{ meta.name }}-secret-key'
register: secret_key_resources
- name: Check for existing postgres configuration
k8s_info:
kind: Secret
namespace: '{{ meta.namespace }}'
name: '{{ meta.name }}-postgres-configuration'
register: postgres_config_resources
- name: Create Database configuration if it doesn't already exist
k8s:
apply: yes
definition: "{{ lookup('template', 'tower_postgres_secret.yaml.j2') }}"
register: k8s_postgres_config_result
when: postgres_config_resources['resources'] | length < 1 and not external_database | default(False) | bool
- name: Create Database if External Database not selected
k8s:
apply: yes
definition: "{{ lookup('template', 'tower_postgres.yaml.j2') }}"
register: k8s_postgres_result
when: not external_database | default(False) | bool
- name: Read Database Configuration
k8s_info:
kind: Secret
namespace: '{{ meta.namespace }}'
name: '{{ meta.name }}-postgres-configuration'
register: postgres_configuration
- name: Store Database Configuration
set_fact:
awx_postgres_user: "{{ postgres_configuration['resources'][0]['data']['username'] | b64decode }}"
awx_postgres_pass: "{{ postgres_configuration['resources'][0]['data']['password'] | b64decode }}"
awx_postgres_database: "{{ postgres_configuration['resources'][0]['data']['database'] | b64decode }}"
awx_postgres_port: "{{ postgres_configuration['resources'][0]['data']['port'] | b64decode }}"
awx_postgres_host: "{{ postgres_configuration['resources'][0]['data']['host'] | b64decode }}"
- name: Deploy Tower Secret Key if needed
k8s:
apply: yes
definition: "{{ lookup('template', 'tower_secret.yaml.j2') }}"
register: k8s_tower_secret_result
when: secret_key_resources['resources'] | length < 1
- name: Ensure configured AWX resources exist in the cluster.
k8s:
apply: yes
definition: "{{ lookup('template', item) | from_yaml_all | list }}"
register: k8s_defs_result
with_items:
- tower_config.yaml.j2
- launch_awx.yaml.j2
- supervisor.yaml.j2
- name: Apply Tower Deployment Configuration
k8s:
apply: yes
definition: "{{ lookup('template', 'tower.yaml.j2') }}"
register: tower_deployment_result
- name: Get the AWX pod information.
k8s_info:
kind: Pod
namespace: '{{ meta.namespace }}'
label_selectors:
- app=awx
register: tower_pods
until: "tower_pods['resources'][0]['status']['phase'] == 'Running'"
delay: 5
retries: 60
- name: Set the awx pod name as a variable.
set_fact:
tower_pod_name: "{{ tower_pods['resources'][0]['metadata']['name'] }}"
- name: Verify tower_pod_name is populated.
assert:
that: tower_pod_name != ''
fail_msg: "Could not find the tower pod's name."
- name: Check if database is populated (auth_user table exists).
shell: >-
kubectl exec -n {{ meta.namespace }} -c {{ meta.name }}-task {{ tower_pod_name }} -- bash -c
"echo 'from django.db import connection;
tbl = \"auth_user\" in connection.introspection.table_names();
exit(0 if tbl else 1)'
| awx-manage shell"
ignore_errors: true
changed_when: false
register: database_check
when: k8s_defs_result is not changed
- name: Migrate the database if the K8s resources were updated. # noqa 305
shell: >-
kubectl exec -n {{ meta.namespace }} -c {{ meta.name }}-task {{ tower_pod_name }} -- bash -c
"awx-manage migrate --noinput"
register: migrate_result
when: (k8s_defs_result is changed) or (database_check is defined and database_check.rc != 0)
- include_tasks: initialize.yml

View File

@@ -2,11 +2,11 @@ DATABASES = {
'default': {
'ATOMIC_REQUESTS': True,
'ENGINE': 'awx.main.db.profiled_pg',
'NAME': "awx",
'USER': "awx",
'PASSWORD': "{{ tower_postgres_pass }}",
'HOST': '{{ meta.name }}-postgres',
'PORT': "{{ tower_postgres_port }}",
'NAME': "{{ awx_postgres_database }}",
'USER': "{{ awx_postgres_user }}",
'PASSWORD': "{{ awx_postgres_pass }}",
'HOST': '{{ awx_postgres_host }}',
'PORT': "{{ awx_postgres_port }}",
'OPTIONS': { 'sslmode': '{{ pg_sslmode|default("prefer") }}',
'sslrootcert': '{{ ca_trust_bundle }}',
},

View File

@@ -0,0 +1,5 @@
DATABASE_USER={{ awx_postgres_user }}
DATABASE_NAME={{ awx_postgres_database }}
DATABASE_HOST={{ awx_postgres_host }}
DATABASE_PORT={{ awx_postgres_port }}
DATABASE_PASSWORD={{ awx_postgres_pass }}

View File

@@ -5,7 +5,7 @@ metadata:
name: {{ meta.name }}-launch-awx
namespace: {{ meta.namespace }}
labels:
app: tower
app: awx
data:
launch-awx-task: |
#!/usr/bin/env bash

View File

@@ -5,12 +5,14 @@ metadata:
name: {{ meta.name }}-supervisor-config
namespace: {{ meta.namespace }}
labels:
app: tower
app: awx
data:
supervisor-web-config: |
[supervisord]
nodaemon = True
umask = 022
logfile = /dev/stdout
logfile_maxbytes = 0
[program:nginx]
command = nginx -g "daemon off;"
@@ -96,6 +98,8 @@ data:
[supervisord]
nodaemon = True
umask = 022
logfile = /dev/stdout
logfile_maxbytes = 0
[program:dispatcher]
command = awx-manage run_dispatcher

View File

@@ -1,16 +1,15 @@
# Tower Secret.
# AWX Secret Configurations
---
apiVersion: v1
kind: Secret
metadata:
name: '{{ meta.name }}-secrets'
namespace: '{{ meta.namespace }}'
data:
secret_key: '{{ tower_secret_key | b64encode }}'
credentials_py: "{{ lookup('template', 'credentials.py.j2') | b64encode }}"
environment_sh: "{{ lookup('template', 'environment.sh.j2') | b64encode }}"
apiVersion: v1
kind: Secret
metadata:
name: '{{ meta.name }}-secrets'
namespace: '{{ meta.namespace }}'
data:
credentials_py: "{{ lookup('template', 'credentials.py.j2') | b64encode }}"
environment_sh: "{{ lookup('template', 'environment.sh.j2') | b64encode }}"
# Tower Deployment.
# AWX Deployment.
---
apiVersion: apps/v1
kind: Deployment
@@ -18,16 +17,16 @@ metadata:
name: '{{ meta.name }}'
namespace: '{{ meta.namespace }}'
labels:
app: tower
app: awx
spec:
replicas: {{ tower_replicas }}
selector:
matchLabels:
app: tower
app: awx
template:
metadata:
labels:
app: tower
app: awx
spec:
containers:
- name: memcached
@@ -167,25 +166,25 @@ spec:
path: 'environment.sh'
- name: {{ meta.name }}-secret-key
secret:
secretName: '{{ meta.name }}-secrets'
secretName: '{{ meta.name }}-secret-key'
items:
- key: secret_key
path: SECRET_KEY
- name: {{ meta.name }}-settings
configMap:
name: '{{ meta.name }}-tower-configmap'
name: '{{ meta.name }}-awx-configmap'
items:
- key: settings
path: settings.py
- name: {{ meta.name }}-nginx-conf
configMap:
name: '{{ meta.name }}-tower-configmap'
name: '{{ meta.name }}-awx-configmap'
items:
- key: nginx_conf
path: nginx.conf
- name: {{ meta.name }}-redis-config
configMap:
name: {{ meta.name }}-tower-configmap
name: {{ meta.name }}-awx-configmap
items:
- key: redis_conf
path: redis.conf
@@ -226,7 +225,7 @@ spec:
- name: rsyslog-dir
emptyDir: {}
# Tower Service.
# AWX Service.
---
apiVersion: v1
kind: Service
@@ -234,7 +233,7 @@ metadata:
name: '{{ meta.name }}-service'
namespace: '{{ meta.namespace }}'
labels:
app: tower
app: awx
spec:
ports:
- port: 80
@@ -242,9 +241,9 @@ spec:
targetPort: 8052
name: http
selector:
app: tower
app: awx
# Tower Ingress.
# AWX Ingress.
{% if 'ingress' == tower_ingress_type|lower %}
---
apiVersion: extensions/v1beta1

View File

@@ -1,12 +1,12 @@
# Tower Web ConfigMap.
# AWX Web ConfigMap.
---
apiVersion: v1
kind: ConfigMap
metadata:
name: '{{ meta.name }}-tower-configmap'
name: '{{ meta.name }}-awx-configmap'
namespace: '{{ meta.namespace }}'
labels:
app: tower
app: awx
data:
environment: |
DATABASE_USER=awx

View File

@@ -1,13 +1,3 @@
# Postgres Secret.
---
apiVersion: v1
kind: Secret
metadata:
name: '{{ meta.name }}-postgres-pass'
namespace: '{{ meta.namespace }}'
data:
password: '{{ tower_postgres_pass | b64encode }}'
# Postgres StatefulSet.
---
apiVersion: v1
@@ -16,11 +6,11 @@ metadata:
name: '{{ meta.name }}-postgres'
namespace: '{{ meta.namespace }}'
labels:
app: tower-postgres
app: awx-postgres
spec:
selector:
matchLabels:
app: tower-postgres
app: awx-postgres
serviceName: '{{ meta.name }}'
replicas: 1
updateStrategy:
@@ -28,25 +18,31 @@ spec:
template:
metadata:
labels:
app: tower-postgres
app: awx-postgres
spec:
containers:
- image: '{{ tower_postgres_image }}'
name: postgres
env:
- name: POSTGRES_DB
value: awx
valueFrom:
secretKeyRef:
name: '{{ meta.name }}-postgres-configuration'
key: database
- name: POSTGRES_USER
value: awx
valueFrom:
secretKeyRef:
name: '{{ meta.name }}-postgres-configuration'
key: username
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: '{{ meta.name }}-postgres-pass'
name: '{{ meta.name }}-postgres-configuration'
key: password
- name: PGDATA
value: '{{ tower_postgres_data_path }}'
ports:
- containerPort: 3306
- containerPort: 5432
name: postgres
volumeMounts:
- name: postgres
@@ -73,11 +69,11 @@ metadata:
name: '{{ meta.name }}-postgres'
namespace: '{{ meta.namespace }}'
labels:
app: tower-postgres
app: awx-postgres
spec:
ports:
- port: 5432
clusterIP: None
selector:
app: tower-postgres
app: awx-postgres

View File

@@ -0,0 +1,13 @@
# Postgres Secret.
---
apiVersion: v1
kind: Secret
metadata:
name: '{{ meta.name }}-postgres-configuration'
namespace: '{{ meta.namespace }}'
stringData:
password: '{{ lookup('password', 'p' + meta.name + 'pg length=32 chars=ascii_letters,digits') }}'
username: 'awx'
database: 'awx'
port: '5432'
host: {{ meta.name }}-postgres

View File

@@ -0,0 +1,8 @@
---
apiVersion: v1
kind: Secret
metadata:
name: '{{ meta.name }}-secret-key'
namespace: '{{ meta.namespace }}'
stringData:
secret_key: '{{ lookup('password', 'ts' + meta.name + 'pg length=32 chars=ascii_letters,digits') }}'

View File

@@ -0,0 +1,36 @@
---
- name: Check for existing secret key
k8s_info:
kind: Secret
namespace: '{{ meta.namespace }}'
name: '{{ meta.name }}-secret-key'
register: secret_key_resources
- name: Check for existing postgres configuration
k8s_info:
kind: Secret
namespace: '{{ meta.namespace }}'
name: '{{ meta.name }}-postgres-configuration'
register: postgres_config_resources
- name: Remove ownerReferences from PG configuration if it exists
k8s:
definition:
apiVersion: v1
kind: Secret
metadata:
name: '{{ meta.name }}-postgres-configuration'
namespace: '{{ meta.namespace }}'
ownerReferences: null
when: postgres_config_resources['resources'] | length > 0
- name: Remove ownerReferences from Tower Secret if it exists
k8s:
definition:
apiVersion: v1
kind: Secret
metadata:
name: '{{ meta.name }}-secret-key'
namespace: '{{ meta.namespace }}'
ownerReferences: null
when: secret_key_resources['resources'] | length > 0

View File

@@ -1,70 +0,0 @@
---
- name: Include deployment type vars
include_vars: "{{ deployment_type }}.yml"
- name: Ensure configured Tower resources exist in the cluster.
k8s:
apply: yes
definition: "{{ lookup('template', item) | from_yaml_all | list }}"
register: k8s_defs_result
with_items:
- tower_postgres.yaml.j2
- tower_config.yaml.j2
- launch_awx.yaml.j2
- supervisor.yaml.j2
- tower.yaml.j2
- name: Get the Tower pod information.
k8s_info:
kind: Pod
namespace: '{{ meta.namespace }}'
label_selectors:
- app=tower
register: tower_pods
until: "tower_pods['resources'][0]['status']['phase'] == 'Running'"
delay: 5
retries: 60
- name: Set the tower pod name as a variable.
set_fact:
tower_pod_name: "{{ tower_pods['resources'][0]['metadata']['name'] }}"
- name: Verify tower_pod_name is populated.
assert:
that: tower_pod_name != ''
fail_msg: "Could not find the tower pod's name."
- name: Check if database is populated (auth_user table exists).
shell: >-
kubectl exec -n {{ meta.namespace }} -c {{ meta.name }}-task {{ tower_pod_name }} -- bash -c
"echo 'from django.db import connection;
tbl = \"auth_user\" in connection.introspection.table_names();
exit(0 if tbl else 1)'
| awx-manage shell"
ignore_errors: true
changed_when: false
register: database_check
when: k8s_defs_result is not changed
- name: Migrate the database if the K8s resources were updated. # noqa 305
shell: >-
kubectl exec -n {{ meta.namespace }} -c {{ meta.name }}-task {{ tower_pod_name }} -- bash -c
"awx-manage migrate --noinput"
register: migrate_result
when: (k8s_defs_result is changed) or (database_check is defined and database_check.rc != 0)
- include_tasks: initialize.yml
- name: Scale the tower deployment to 0 replicas after migration.
k8s:
definition: "{{ lookup('template', 'tower.yaml.j2') | from_yaml_all | list }}"
vars:
tower_replicas: "0"
when: migrate_result and migrate_result.changed
- name: Scale the tower deployment back to 1 replica after migration.
k8s:
definition: "{{ lookup('template', 'tower.yaml.j2') | from_yaml_all | list }}"
vars:
tower_replicas: "1"
when: migrate_result and migrate_result.changed

View File

@@ -1,5 +0,0 @@
DATABASE_USER=awx
DATABASE_NAME=awx
DATABASE_HOST={{ meta.name }}-postgres
DATABASE_PORT={{ tower_postgres_port }}
DATABASE_PASSWORD={{ tower_postgres_pass }}

View File

@@ -1,5 +1,8 @@
---
- version: v1beta1
group: tower.ansible.com
kind: Tower
group: awx.ansible.com
kind: AWX
playbook: /opt/ansible/main.yml
finalizer:
name: finalizer.awx.ansible.com
role: awx_remove