Issue #1: Get a minikube configuration working for local Molecule testing.

This commit is contained in:
Jeff Geerling
2019-11-06 16:24:11 -06:00
parent 94ce7b49a2
commit 2c527990fa
11 changed files with 254 additions and 140 deletions

View File

@@ -2,4 +2,17 @@
[![Build Status](https://travis-ci.com/geerlingguy/tower-operator.svg?branch=master)](https://travis-ci.com/geerlingguy/tower-operator)
A Tower operator for Kubernetes built with Operator SDK.
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.
## Testing
This Operator includes a molecule-based test framework, which can be executed standalone in Docker (e.g. in CI or in a single Docker container anywhere), or inside any kind of Kubernetes cluster (e.g. Minikube).
### Testing in Docker (standalone)
1. `molecule converge -s test-local`
### Testing in Minikube
1. `minikube start`
1. `molecule converge -s test-minikube`

View File

@@ -4,4 +4,4 @@ export WATCH_NAMESPACE=${TEST_NAMESPACE}
trap "kill $!" SIGINT SIGTERM EXIT
cd ${HOME}/project
exec molecule test -s test-cluster
exec molecule test -s test-minikube

View File

@@ -41,8 +41,8 @@ spec:
fieldPath: metadata.name
- name: OPERATOR_NAME
value: tower-operator
- name: ANSIBLE_VERBOSITY
value: '3'
# - name: ANSIBLE_VERBOSITY
# value: '3'
volumes:
- name: runner
emptyDir: {}

View File

@@ -8,7 +8,7 @@
tasks:
- name: Get tower Pod data
k8s_facts:
k8s_info:
kind: Pod
namespace: example-tower
label_selectors:
@@ -18,10 +18,3 @@
- name: Verify there is one tower pod
assert:
that: '{{ (tower_pods.resources | length) == 1 }}'
- name: Verify tower functionality
hosts: k8s
vars: []
tasks: []

View File

@@ -1,35 +1,28 @@
---
- name: Prepare
hosts: k8s
gather_facts: no
- name: Prepare operator resources
hosts: localhost
connection: local
vars:
kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}"
ansible_python_interpreter: '{{ ansible_playbook_python }}'
deploy_dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/deploy"
tasks:
- name: delete the kubeconfig if present
file:
path: '{{ kubeconfig }}'
state: absent
delegate_to: localhost
- name: Create Custom Resource Definition
k8s:
definition: "{{ lookup('file', '/'.join([deploy_dir, 'crds/tower_v1alpha1_tower_crd.yaml'])) }}"
- name: Fetch the kubeconfig
fetch:
dest: '{{ kubeconfig }}'
flat: yes
src: /root/.kube/config
- name: Ensure specified namespace is present
k8s:
api_version: v1
kind: Namespace
name: '{{ operator_namespace }}'
- name: Change the kubeconfig port to the proper value
replace:
regexp: 8443
replace: "{{ lookup('env', 'KIND_PORT') }}"
path: '{{ kubeconfig }}'
delegate_to: localhost
- name: Wait for the Kubernetes API to become available (this could take a minute)
uri:
url: "http://localhost:10080/kubernetes-ready"
status_code: 200
validate_certs: no
register: result
until: (result.status|default(-1)) == 200
retries: 60
delay: 5
- name: Create RBAC resources
k8s:
definition: "{{ lookup('template', '/'.join([deploy_dir, item])) }}"
namespace: '{{ operator_namespace }}'
with_items:
- role.yaml
- role_binding.yaml
- service_account.yaml

View File

@@ -1,33 +0,0 @@
---
- name: Converge
hosts: localhost
connection: local
vars:
ansible_python_interpreter: '{{ ansible_playbook_python }}'
deploy_dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/deploy"
image_name: tower.ansible.com/tower-operator:testing
custom_resource: "{{ lookup('file', '/'.join([deploy_dir, 'crds/tower_v1alpha1_tower_cr.yaml'])) | from_yaml }}"
tasks:
- name: Create the tower.ansible.com/v1alpha1.Tower
k8s:
namespace: '{{ namespace }}'
definition: "{{ lookup('file', '/'.join([deploy_dir, 'crds/tower_v1alpha1_tower_cr.yaml'])) }}"
- name: Get the newly created Custom Resource
debug:
msg: "{{ lookup('k8s', group='tower.ansible.com', api_version='v1alpha1', kind='Tower', namespace=namespace, resource_name=custom_resource.metadata.name) }}"
- name: Wait 60s for reconciliation to run
k8s_facts:
api_version: 'v1alpha1'
kind: 'Tower'
namespace: '{{ namespace }}'
name: '{{ custom_resource.metadata.name }}'
register: reconcile_cr
until:
- "'Successful' in (reconcile_cr | json_query('resources[].status.conditions[].reason'))"
delay: 6
retries: 10
- import_playbook: '{{ playbook_dir }}/../default/asserts.yml'

View File

@@ -1,5 +1,5 @@
---
- name: Build Operator in Kubernetes docker container
- name: Build Operator in Kind container
hosts: k8s
vars:
@@ -29,7 +29,9 @@
custom_resource: "{{ lookup('file', '/'.join([deploy_dir, 'crds/tower_v1alpha1_tower_cr.yaml'])) | from_yaml }}"
tasks:
- block:
- name: Delete the Operator Deployment
k8s:
state: absent
@@ -39,7 +41,7 @@
when: hostvars[groups.k8s.0].build_cmd.changed
- name: Wait 30s for Operator Deployment to terminate
k8s_facts:
k8s_info:
api_version: '{{ definition.apiVersion }}'
kind: '{{ definition.kind }}'
namespace: '{{ operator_namespace }}'
@@ -71,7 +73,7 @@
definition: '{{ custom_resource }}'
- name: Wait 5m for reconciliation to run
k8s_facts:
k8s_info:
api_version: '{{ custom_resource.apiVersion }}'
kind: '{{ custom_resource.kind }}'
namespace: '{{ custom_resource.metadata.namespace }}'
@@ -81,46 +83,48 @@
- "'Successful' in (cr | json_query('resources[].status.conditions[].reason'))"
delay: 6
retries: 50
rescue:
- name: debug cr
ignore_errors: yes
failed_when: false
debug:
var: debug_cr
vars:
debug_cr: '{{ lookup("k8s",
kind=custom_resource.kind,
api_version=custom_resource.apiVersion,
namespace=custom_resource.metadata.namespace,
resource_name=custom_resource.metadata.name
)}}'
- name: debug tower deployment
ignore_errors: yes
failed_when: false
debug:
var: deploy
vars:
deploy: '{{ lookup("k8s",
kind="Deployment",
api_version="apps/v1",
namespace=custom_resource.metadata.namespace,
label_selector="app=tower"
)}}'
- name: debug cr
ignore_errors: yes
failed_when: false
debug:
var: debug_cr
vars:
debug_cr: '{{ lookup("k8s",
kind=custom_resource.kind,
api_version=custom_resource.apiVersion,
namespace=custom_resource.metadata.namespace,
resource_name=custom_resource.metadata.name
)}}'
- name: get operator logs
ignore_errors: yes
failed_when: false
command: kubectl logs deployment/{{ definition.metadata.name }} -n {{ operator_namespace }} -c operator
environment:
KUBECONFIG: '{{ lookup("env", "KUBECONFIG") }}'
vars:
definition: "{{ lookup('template', '/'.join([deploy_dir, 'operator.yaml'])) | from_yaml }}"
register: log
- name: debug tower deployment
ignore_errors: yes
failed_when: false
debug:
var: deploy
vars:
deploy: '{{ lookup("k8s",
kind="Deployment",
api_version="apps/v1",
namespace=custom_resource.metadata.namespace,
label_selector="app=tower"
)}}'
- debug: var=log.stdout_lines
- name: get operator logs
ignore_errors: yes
failed_when: false
command: kubectl logs deployment/{{ definition.metadata.name }} -n {{ operator_namespace }} -c operator
environment:
KUBECONFIG: '{{ lookup("env", "KUBECONFIG") }}'
vars:
definition: "{{ lookup('template', '/'.join([deploy_dir, 'operator.yaml'])) | from_yaml }}"
register: log
- fail:
msg: "Failed on action: converge"
- debug: var=log.stdout_lines
- fail:
msg: "Failed on action: converge"
- import_playbook: '{{ playbook_dir }}/../default/asserts.yml'

View File

@@ -1,28 +1,37 @@
---
- import_playbook: ../default/prepare.yml
- name: Prepare operator resources
hosts: localhost
connection: local
- name: Prepare kubernetes environment
hosts: k8s
gather_facts: no
vars:
ansible_python_interpreter: '{{ ansible_playbook_python }}'
deploy_dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/deploy"
kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}"
tasks:
- name: Create Custom Resource Definition
k8s:
definition: "{{ lookup('file', '/'.join([deploy_dir, 'crds/tower_v1alpha1_tower_crd.yaml'])) }}"
- name: delete the kubeconfig if present
file:
path: '{{ kubeconfig }}'
state: absent
delegate_to: localhost
- name: Ensure specified namespace is present
k8s:
api_version: v1
kind: Namespace
name: '{{ operator_namespace }}'
- name: Fetch the kubeconfig
fetch:
dest: '{{ kubeconfig }}'
flat: yes
src: /root/.kube/config
- name: Create RBAC resources
k8s:
definition: "{{ lookup('template', '/'.join([deploy_dir, item])) }}"
namespace: '{{ operator_namespace }}'
with_items:
- role.yaml
- role_binding.yaml
- service_account.yaml
- name: Change the kubeconfig port to the proper value
replace:
regexp: 8443
replace: "{{ lookup('env', 'KIND_PORT') }}"
path: '{{ kubeconfig }}'
delegate_to: localhost
- name: Wait for the Kubernetes API to become available (this could take a minute)
uri:
url: "http://localhost:10080/kubernetes-ready"
status_code: 200
validate_certs: no
register: result
until: (result.status|default(-1)) == 200
retries: 60
delay: 5
- import_playbook: ../default/prepare.yml

View File

@@ -10,7 +10,7 @@ lint:
name: yamllint
enabled: False
platforms:
- name: test-cluster
- name: test-minikube
groups:
- k8s
provisioner:
@@ -18,14 +18,14 @@ provisioner:
inventory:
group_vars:
all:
namespace: ${TEST_NAMESPACE:-default}
operator_namespace: ${TEST_NAMESPACE:-default}
lint:
name: ansible-lint
enabled: False
env:
ANSIBLE_ROLES_PATH: ${MOLECULE_PROJECT_DIRECTORY}/roles
scenario:
name: test-cluster
name: test-minikube
test_sequence:
- lint
- destroy

View File

@@ -0,0 +1,133 @@
---
- name: Build Operator in Minikube
hosts: localhost
connection: local
vars:
image_name: tower.ansible.com/tower-operator:testing
tasks:
# Use raw Docker commands inside Minikube to avoid extra Python dependencies.
- name: Get existing image hash
shell: |
eval $(minikube docker-env)
docker images -q {{ image_name }}
register: prev_hash
changed_when: false
- name: Build Operator Image
shell: |
eval $(minikube docker-env)
docker build -f ../../build/Dockerfile -t {{ image_name }} ../..
register: build_cmd
changed_when: not prev_hash.stdout or (prev_hash.stdout and prev_hash.stdout not in ''.join(build_cmd.stdout_lines[-2:]))
- name: Converge
hosts: localhost
connection: local
vars:
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_v1alpha1_tower_cr.yaml'])) | from_yaml }}"
tasks:
- block:
- name: Delete the Operator Deployment
k8s:
state: absent
namespace: '{{ operator_namespace }}'
definition: "{{ lookup('template', '/'.join([deploy_dir, 'operator.yaml'])) }}"
register: delete_deployment
when: build_cmd.changed
- name: Wait 30s for Operator Deployment to terminate
k8s_info:
api_version: '{{ definition.apiVersion }}'
kind: '{{ definition.kind }}'
namespace: '{{ operator_namespace }}'
name: '{{ definition.metadata.name }}'
vars:
definition: "{{ lookup('template', '/'.join([deploy_dir, 'operator.yaml'])) | from_yaml }}"
register: deployment
until: not deployment.resources
delay: 3
retries: 10
when: delete_deployment.changed
- name: Create the Operator Deployment
k8s:
namespace: '{{ operator_namespace }}'
definition: "{{ lookup('template', '/'.join([deploy_dir, 'operator.yaml'])) }}"
- name: Ensure the Tower custom_resource namespace exists
k8s:
state: present
name: '{{ custom_resource.metadata.namespace }}'
kind: Namespace
api_version: v1
- name: Create the tower.ansible.com/v1alpha1.Tower
k8s:
state: present
namespace: '{{ custom_resource.metadata.namespace }}'
definition: '{{ custom_resource }}'
- name: Wait 5m for reconciliation to run
k8s_info:
api_version: '{{ custom_resource.apiVersion }}'
kind: '{{ custom_resource.kind }}'
namespace: '{{ custom_resource.metadata.namespace }}'
name: '{{ custom_resource.metadata.name }}'
register: cr
until:
- "'Successful' in (cr | json_query('resources[].status.conditions[].reason'))"
delay: 6
retries: 50
rescue:
- name: debug cr
ignore_errors: yes
failed_when: false
debug:
var: debug_cr
vars:
debug_cr: '{{ lookup("k8s",
kind=custom_resource.kind,
api_version=custom_resource.apiVersion,
namespace=custom_resource.metadata.namespace,
resource_name=custom_resource.metadata.name
)}}'
- name: debug tower deployment
ignore_errors: yes
failed_when: false
debug:
var: deploy
vars:
deploy: '{{ lookup("k8s",
kind="Deployment",
api_version="apps/v1",
namespace=custom_resource.metadata.namespace,
label_selector="app=tower"
)}}'
- name: get operator logs
ignore_errors: yes
failed_when: false
command: kubectl logs deployment/{{ definition.metadata.name }} -n {{ operator_namespace }} -c operator
environment:
KUBECONFIG: '{{ lookup("env", "KUBECONFIG") }}'
vars:
definition: "{{ lookup('template', '/'.join([deploy_dir, 'operator.yaml'])) | from_yaml }}"
register: log
- debug: var=log.stdout_lines
- fail:
msg: "Failed on action: converge"
- import_playbook: '{{ playbook_dir }}/../default/asserts.yml'

View File

@@ -0,0 +1,2 @@
---
- import_playbook: ../default/prepare.yml