mirror of
https://github.com/ansible/awx.git
synced 2026-03-13 23:17:32 -02:30
Chop out the dev environment isolated node
This commit is contained in:
7
Makefile
7
Makefile
@@ -174,12 +174,7 @@ init:
|
|||||||
. $(VENV_BASE)/awx/bin/activate; \
|
. $(VENV_BASE)/awx/bin/activate; \
|
||||||
fi; \
|
fi; \
|
||||||
$(MANAGEMENT_COMMAND) provision_instance --hostname=$(COMPOSE_HOST); \
|
$(MANAGEMENT_COMMAND) provision_instance --hostname=$(COMPOSE_HOST); \
|
||||||
$(MANAGEMENT_COMMAND) register_queue --queuename=tower --instance_percent=100;\
|
$(MANAGEMENT_COMMAND) register_queue --queuename=tower --instance_percent=100;
|
||||||
if [ "$(AWX_GROUP_QUEUES)" == "tower,thepentagon" ]; then \
|
|
||||||
$(MANAGEMENT_COMMAND) provision_instance --hostname=isolated; \
|
|
||||||
$(MANAGEMENT_COMMAND) register_queue --queuename='thepentagon' --hostnames=isolated --controller=tower; \
|
|
||||||
$(MANAGEMENT_COMMAND) generate_isolated_key > /awx_devel/awx/main/isolated/authorized_keys; \
|
|
||||||
fi;
|
|
||||||
|
|
||||||
# Refresh development environment after pulling new code.
|
# Refresh development environment after pulling new code.
|
||||||
refresh: clean requirements_dev version_file develop migrate
|
refresh: clean requirements_dev version_file develop migrate
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ import pytest
|
|||||||
from unittest import mock
|
from unittest import mock
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from django.utils.timezone import now, timedelta
|
from awx.main.tasks import RunProjectUpdate, RunInventoryUpdate
|
||||||
|
from awx.main.models import ProjectUpdate, InventoryUpdate, InventorySource
|
||||||
from awx.main.tasks import RunProjectUpdate, RunInventoryUpdate, awx_isolated_heartbeat, isolated_manager
|
|
||||||
from awx.main.models import ProjectUpdate, InventoryUpdate, InventorySource, Instance, InstanceGroup
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@@ -70,61 +68,3 @@ class TestDependentInventoryUpdate:
|
|||||||
# Verify that it bails after 1st update, detecting a cancel
|
# Verify that it bails after 1st update, detecting a cancel
|
||||||
assert is2.inventory_updates.count() == 0
|
assert is2.inventory_updates.count() == 0
|
||||||
iu_run_mock.assert_called_once()
|
iu_run_mock.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
class MockSettings:
|
|
||||||
AWX_ISOLATED_PERIODIC_CHECK = 60
|
|
||||||
CLUSTER_HOST_ID = 'tower_1'
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
|
||||||
class TestIsolatedManagementTask:
|
|
||||||
@pytest.fixture
|
|
||||||
def control_group(self):
|
|
||||||
return InstanceGroup.objects.create(name='alpha')
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def control_instance(self, control_group):
|
|
||||||
return control_group.instances.create(hostname='tower_1')
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def needs_updating(self, control_group):
|
|
||||||
ig = InstanceGroup.objects.create(name='thepentagon', controller=control_group)
|
|
||||||
inst = ig.instances.create(hostname='isolated', capacity=103)
|
|
||||||
inst.last_isolated_check = now() - timedelta(seconds=MockSettings.AWX_ISOLATED_PERIODIC_CHECK)
|
|
||||||
inst.save()
|
|
||||||
return ig
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def just_updated(self, control_group):
|
|
||||||
ig = InstanceGroup.objects.create(name='thepentagon', controller=control_group)
|
|
||||||
inst = ig.instances.create(hostname='isolated', capacity=103)
|
|
||||||
inst.last_isolated_check = now()
|
|
||||||
inst.save()
|
|
||||||
return inst
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def old_version(self, control_group):
|
|
||||||
ig = InstanceGroup.objects.create(name='thepentagon', controller=control_group)
|
|
||||||
inst = ig.instances.create(hostname='isolated-old', capacity=103)
|
|
||||||
inst.save()
|
|
||||||
return inst
|
|
||||||
|
|
||||||
def test_takes_action(self, control_instance, needs_updating):
|
|
||||||
original_isolated_instance = needs_updating.instances.all().first()
|
|
||||||
with mock.patch('awx.main.tasks.settings', MockSettings()):
|
|
||||||
with mock.patch.object(isolated_manager.IsolatedManager, 'health_check') as check_mock:
|
|
||||||
awx_isolated_heartbeat()
|
|
||||||
iso_instance = Instance.objects.get(hostname='isolated')
|
|
||||||
call_args, _ = check_mock.call_args
|
|
||||||
assert call_args[0][0] == iso_instance
|
|
||||||
assert iso_instance.last_isolated_check > original_isolated_instance.last_isolated_check
|
|
||||||
assert iso_instance.modified == original_isolated_instance.modified
|
|
||||||
|
|
||||||
def test_does_not_take_action(self, control_instance, just_updated):
|
|
||||||
with mock.patch('awx.main.tasks.settings', MockSettings()):
|
|
||||||
with mock.patch.object(isolated_manager.IsolatedManager, 'health_check') as check_mock:
|
|
||||||
awx_isolated_heartbeat()
|
|
||||||
iso_instance = Instance.objects.get(hostname='isolated')
|
|
||||||
check_mock.assert_not_called()
|
|
||||||
assert iso_instance.capacity == 103
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
---
|
|
||||||
version: '2'
|
|
||||||
services:
|
|
||||||
# Primary Tower Development Container link
|
|
||||||
awx:
|
|
||||||
environment:
|
|
||||||
AWX_GROUP_QUEUES: tower,thepentagon
|
|
||||||
links:
|
|
||||||
- isolated
|
|
||||||
# Isolated Rampart Container
|
|
||||||
isolated:
|
|
||||||
image: ${DEV_DOCKER_TAG_BASE}/awx_isolated:${TAG}
|
|
||||||
container_name: tools_isolated_1
|
|
||||||
hostname: isolated
|
|
||||||
volumes:
|
|
||||||
- "../awx/main/isolated:/awx_devel"
|
|
||||||
privileged: true
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
ARG TAG=latest
|
|
||||||
FROM ansible/awx_devel:${TAG}
|
|
||||||
|
|
||||||
RUN dnf install -y gcc python36-devel openssh-server
|
|
||||||
RUN python3 -m ensurepip && pip3 install "virtualenv < 20" ansible-runner
|
|
||||||
RUN dnf remove -y gcc python36-devel && rm -rf /var/cache/dnf
|
|
||||||
|
|
||||||
RUN rm -f /etc/ssh/ssh_host_ecdsa_key /etc/ssh/ssh_host_rsa_key
|
|
||||||
RUN ssh-keygen -q -N "" -t dsa -f /etc/ssh/ssh_host_ecdsa_key
|
|
||||||
RUN ssh-keygen -q -N "" -t rsa -f /etc/ssh/ssh_host_rsa_key
|
|
||||||
RUN sed -i "s/#UsePrivilegeSeparation.*/UsePrivilegeSeparation no/g" /etc/ssh/sshd_config
|
|
||||||
RUN sed -i "s/UsePAM.*/UsePAM yes/g" /etc/ssh/sshd_config
|
|
||||||
RUN sed -i "s/#StrictModes.*/StrictModes no/g" /etc/ssh/sshd_config
|
|
||||||
RUN mkdir -p /root/.ssh
|
|
||||||
RUN ln -s /awx_devel/authorized_keys /root/.ssh/authorized_keys
|
|
||||||
|
|
||||||
ENTRYPOINT ["tini", "--"]
|
|
||||||
CMD ["/usr/sbin/sshd", "-D"]
|
|
||||||
|
|
||||||
EXPOSE 22
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
## Instructions on using an isolated node
|
|
||||||
|
|
||||||
The building of the isolated node is done in the `make docker-compose-build`
|
|
||||||
target. Its image uses a different tag from the tools_awx container.
|
|
||||||
|
|
||||||
Given that the images are built, you can run the combined docker compose target. This uses
|
|
||||||
the base `docker-compose.yml` with modifications found in `docker-isolated-override.yml`.
|
|
||||||
You will still need to give COMPOSE_TAG with whatever your intended
|
|
||||||
base branch is. For example:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
make docker-isolated COMPOSE_TAG=devel
|
|
||||||
```
|
|
||||||
|
|
||||||
This will automatically exchange the keys in order for the `tools_awx_1`
|
|
||||||
container to access the `tools_isolated_1` container over ssh.
|
|
||||||
After that, it will bring up all the containers like the normal docker-compose
|
|
||||||
workflow.
|
|
||||||
|
|
||||||
### Running a job on the Isolated Node
|
|
||||||
|
|
||||||
Create a job template that runs normally. Add the id of the instance
|
|
||||||
group named `thepentagon` to the JT's instance groups. To do this, POST
|
|
||||||
the id (probably id=2) to `/api/v2/job_templates/N/instance_groups/`.
|
|
||||||
After that, run the job template.
|
|
||||||
|
|
||||||
The models are automatically created when running the Makefile target,
|
|
||||||
and they are structured as follows:
|
|
||||||
|
|
||||||
+-------+ +-------------+
|
|
||||||
| tower |<----+ thepentagon |
|
|
||||||
+-------+ +-------------+
|
|
||||||
^ ^
|
|
||||||
| |
|
|
||||||
| |
|
|
||||||
+---+---+ +-----+----+
|
|
||||||
| tower | | isolated |
|
|
||||||
+-------+ +----------+
|
|
||||||
|
|
||||||
The `controller` for the group "thepentagon" and all hosts therein is
|
|
||||||
determined by a ForeignKey within the instance group.
|
|
||||||
|
|
||||||
### Run a playbook
|
|
||||||
|
|
||||||
In order to run an isolated job, associate the instance group `thepentagon` with
|
|
||||||
a job template, inventory, or organization, then run a job that derives from
|
|
||||||
that resource. You should be able to confirm success by inspecting the
|
|
||||||
`instance_group` of the job.
|
|
||||||
|
|
||||||
#### Advanced Manual Testing
|
|
||||||
|
|
||||||
If you want to run a job manually inside of the isolated container with this
|
|
||||||
tooling, you need a private data directory. Normal isolated job runs will
|
|
||||||
clean up their private data directory, but you can temporarily disable this
|
|
||||||
by disabling some parts of the cleanup_isolated.yml playbook.
|
|
||||||
|
|
||||||
Example location of a private data directory:
|
|
||||||
|
|
||||||
`/tmp/awx_29_OM6Mnx/`
|
|
||||||
|
|
||||||
The following command would run the playbook corresponding to that job.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ansible-runner start /tmp/awx_29_OM6Mnx/ -p some_playbook.yml
|
|
||||||
```
|
|
||||||
|
|
||||||
Other ansible-runner commands include `start`, `is-alive`, and `stop`.
|
|
||||||
Reference in New Issue
Block a user