diff --git a/awx/api/serializers.py b/awx/api/serializers.py
index 8246ad8f1d..43d495637d 100644
--- a/awx/api/serializers.py
+++ b/awx/api/serializers.py
@@ -4776,8 +4776,7 @@ class InstanceGroupSerializer(BaseSerializer):
)
is_container_group = serializers.BooleanField(
help_text=_('Indicates whether instances in this group are containerized.'
- 'Containerized groups have a designated Openshift or Kubernetes cluster.'),
- read_only=True
+ 'Containerized groups have a designated Openshift or Kubernetes cluster.')
)
# NOTE: help_text is duplicated from field definitions, no obvious way of
# both defining field details here and also getting the field's help_text
@@ -4853,6 +4852,15 @@ class InstanceGroupSerializer(BaseSerializer):
raise serializers.ValidationError(_('Only Kubernetes credentials can be associated with an Instance Group'))
return value
+ def validate(self, attrs):
+ attrs = super(InstanceGroupSerializer, self).validate(attrs)
+
+ if attrs.get('credential') and not attrs.get('is_container_group'):
+ raise serializers.ValidationError({'is_container_group': _(
+ 'is_container_group must be True when associating a credential to an Instance Group')})
+
+ return attrs
+
def get_capacity_dict(self):
# Store capacity values (globally computed) in the context
if 'capacity_map' not in self.context:
diff --git a/awx/main/management/commands/register_queue.py b/awx/main/management/commands/register_queue.py
index edd8068b89..15891a771f 100644
--- a/awx/main/management/commands/register_queue.py
+++ b/awx/main/management/commands/register_queue.py
@@ -17,13 +17,14 @@ class InstanceNotFound(Exception):
class RegisterQueue:
- def __init__(self, queuename, controller, instance_percent, inst_min, hostname_list):
+ def __init__(self, queuename, controller, instance_percent, inst_min, hostname_list, is_container_group=None):
self.instance_not_found_err = None
self.queuename = queuename
self.controller = controller
self.instance_percent = instance_percent
self.instance_min = inst_min
self.hostname_list = hostname_list
+ self.is_container_group = is_container_group
def get_create_update_instance_group(self):
created = False
@@ -36,6 +37,10 @@ class RegisterQueue:
ig.policy_instance_minimum = self.instance_min
changed = True
+ if self.is_container_group:
+ ig.is_container_group = self.is_container_group
+ changed = True
+
if changed:
ig.save()
diff --git a/awx/main/managers.py b/awx/main/managers.py
index 1af57a9423..0d36515628 100644
--- a/awx/main/managers.py
+++ b/awx/main/managers.py
@@ -144,7 +144,8 @@ class InstanceManager(models.Manager):
from awx.main.management.commands.register_queue import RegisterQueue
pod_ip = os.environ.get('MY_POD_IP')
registered = self.register(ip_address=pod_ip)
- RegisterQueue('tower', None, 100, 0, []).register()
+ is_container_group = settings.IS_K8S
+ RegisterQueue('tower', None, 100, 0, [], is_container_group).register()
return registered
else:
return (False, self.me())
diff --git a/awx/main/migrations/0132_instancegroup_is_container_group.py b/awx/main/migrations/0132_instancegroup_is_container_group.py
new file mode 100644
index 0000000000..faffdc1af5
--- /dev/null
+++ b/awx/main/migrations/0132_instancegroup_is_container_group.py
@@ -0,0 +1,27 @@
+# Generated by Django 2.2.16 on 2021-03-13 14:53
+
+from django.db import migrations, models
+
+
+def migrate_existing_container_groups(apps, schema_editor):
+ InstanceGroup = apps.get_model('main', 'InstanceGroup')
+
+ for group in InstanceGroup.objects.filter(credential__isnull=False).iterator():
+ group.is_container_group = True
+ group.save(update_fields=['is_container_group'])
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('main', '0131_undo_org_polymorphic_ee'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='instancegroup',
+ name='is_container_group',
+ field=models.BooleanField(default=False),
+ ),
+ migrations.RunPython(migrate_existing_container_groups, migrations.RunPython.noop),
+ ]
diff --git a/awx/main/models/ha.py b/awx/main/models/ha.py
index 94d4b8d462..eda3100b17 100644
--- a/awx/main/models/ha.py
+++ b/awx/main/models/ha.py
@@ -199,6 +199,9 @@ class InstanceGroup(HasPolicyEditsMixin, BaseModel, RelatedJobsMixin):
null=True,
on_delete=models.CASCADE
)
+ is_container_group = models.BooleanField(
+ default=False
+ )
credential = models.ForeignKey(
'Credential',
related_name='%(class)ss',
@@ -253,13 +256,6 @@ class InstanceGroup(HasPolicyEditsMixin, BaseModel, RelatedJobsMixin):
def is_isolated(self):
return bool(self.controller)
- @property
- def is_container_group(self):
- if settings.IS_K8S:
- return True
-
- return bool(self.credential and self.credential.kubernetes)
-
'''
RelatedJobsMixin
'''
diff --git a/awx/main/tests/functional/api/test_instance_group.py b/awx/main/tests/functional/api/test_instance_group.py
index 61c1054912..22ecf1a2f6 100644
--- a/awx/main/tests/functional/api/test_instance_group.py
+++ b/awx/main/tests/functional/api/test_instance_group.py
@@ -49,6 +49,7 @@ def isolated_instance_group(instance_group, instance):
def containerized_instance_group(instance_group, kube_credential):
ig = InstanceGroup(name="container")
ig.credential = kube_credential
+ ig.is_container_group = True
ig.save()
return ig
@@ -287,6 +288,7 @@ def test_containerized_group_default_fields(instance_group, kube_credential):
assert ig.policy_instance_minimum == 5
assert ig.policy_instance_percentage == 5
ig.credential = kube_credential
+ ig.is_container_group = True
ig.save()
assert ig.policy_instance_list == []
assert ig.policy_instance_minimum == 0
diff --git a/awx/main/tests/functional/task_management/test_container_groups.py b/awx/main/tests/functional/task_management/test_container_groups.py
index 84dcaf12d7..e739ff879b 100644
--- a/awx/main/tests/functional/task_management/test_container_groups.py
+++ b/awx/main/tests/functional/task_management/test_container_groups.py
@@ -13,6 +13,7 @@ from awx.main.utils import (
@pytest.fixture
def containerized_job(default_instance_group, kube_credential, job_template_factory):
default_instance_group.credential = kube_credential
+ default_instance_group.is_container_group = True
default_instance_group.save()
objects = job_template_factory('jt', organization='org1', project='proj',
inventory='inv', credential='cred',
diff --git a/awx/ui_next/src/screens/InstanceGroup/ContainerGroupAdd/ContainerGroupAdd.jsx b/awx/ui_next/src/screens/InstanceGroup/ContainerGroupAdd/ContainerGroupAdd.jsx
index 6dffae83d3..7549a9d1cc 100644
--- a/awx/ui_next/src/screens/InstanceGroup/ContainerGroupAdd/ContainerGroupAdd.jsx
+++ b/awx/ui_next/src/screens/InstanceGroup/ContainerGroupAdd/ContainerGroupAdd.jsx
@@ -33,6 +33,7 @@ function ContainerGroupAdd() {
pod_spec_override: values.override
? getPodSpecValue(values.pod_spec_override)
: null,
+ is_container_group: true,
});
history.push(`/instance_groups/container_group/${response.id}/details`);
} catch (error) {
diff --git a/awx/ui_next/src/screens/InstanceGroup/ContainerGroupAdd/ContainerGroupAdd.test.jsx b/awx/ui_next/src/screens/InstanceGroup/ContainerGroupAdd/ContainerGroupAdd.test.jsx
index b8bbb301ab..3b47a19d29 100644
--- a/awx/ui_next/src/screens/InstanceGroup/ContainerGroupAdd/ContainerGroupAdd.test.jsx
+++ b/awx/ui_next/src/screens/InstanceGroup/ContainerGroupAdd/ContainerGroupAdd.test.jsx
@@ -26,6 +26,7 @@ const initialPodSpec = {
},
],
},
+ is_container_group: true,
},
};
@@ -80,6 +81,7 @@ describe('', () => {
expect(InstanceGroupsAPI.create).toHaveBeenCalledWith({
...instanceGroupCreateData,
credential: 71,
+ is_container_group: true,
});
expect(wrapper.find('FormSubmitError').length).toBe(0);
expect(history.location.pathname).toBe(
diff --git a/awx/ui_next/src/screens/InstanceGroup/ContainerGroupEdit/ContainerGroupEdit.jsx b/awx/ui_next/src/screens/InstanceGroup/ContainerGroupEdit/ContainerGroupEdit.jsx
index 9f4454c0a8..bd56246c18 100644
--- a/awx/ui_next/src/screens/InstanceGroup/ContainerGroupEdit/ContainerGroupEdit.jsx
+++ b/awx/ui_next/src/screens/InstanceGroup/ContainerGroupEdit/ContainerGroupEdit.jsx
@@ -39,6 +39,7 @@ function ContainerGroupEdit({ instanceGroup }) {
name: values.name,
credential: values.credential ? values.credential.id : null,
pod_spec_override: values.override ? values.pod_spec_override : null,
+ is_container_group: true,
});
history.push(detailsIUrl);
} catch (error) {
diff --git a/awx/ui_next/src/screens/InstanceGroup/ContainerGroupEdit/ContainerGroupEdit.test.jsx b/awx/ui_next/src/screens/InstanceGroup/ContainerGroupEdit/ContainerGroupEdit.test.jsx
index 860c6363c5..6d05e43472 100644
--- a/awx/ui_next/src/screens/InstanceGroup/ContainerGroupEdit/ContainerGroupEdit.test.jsx
+++ b/awx/ui_next/src/screens/InstanceGroup/ContainerGroupEdit/ContainerGroupEdit.test.jsx
@@ -147,6 +147,7 @@ describe('', () => {
...updatedInstanceGroup,
credential: 12,
pod_spec_override: null,
+ is_container_group: true,
});
expect(history.location.pathname).toEqual(
'/instance_groups/container_group/123/details'
diff --git a/awx_collection/plugins/modules/tower_instance_group.py b/awx_collection/plugins/modules/tower_instance_group.py
index f32b60aebf..d80e2e6ed1 100644
--- a/awx_collection/plugins/modules/tower_instance_group.py
+++ b/awx_collection/plugins/modules/tower_instance_group.py
@@ -37,6 +37,11 @@ options:
- Credential to authenticate with Kubernetes or OpenShift. Must be of type "Kubernetes/OpenShift API Bearer Token".
required: False
type: str
+ is_container_group:
+ description:
+ - Signifies that this InstanceGroup should act as a ContainerGroup. If no credential is specified, the underlying Pod's ServiceAccount will be used.
+ required: False
+ type: bool
policy_instance_percentage:
description:
- Minimum percentage of all instances that will be automatically assigned to this group when new instances come online.
@@ -85,6 +90,7 @@ def main():
name=dict(required=True),
new_name=dict(),
credential=dict(),
+ is_container_group=dict(type='bool', default=False),
policy_instance_percentage=dict(type='int', default='0'),
policy_instance_minimum=dict(type='int', default='0'),
policy_instance_list=dict(type='list'),
@@ -100,6 +106,7 @@ def main():
name = module.params.get('name')
new_name = module.params.get("new_name")
credential = module.params.get('credential')
+ is_container_group = module.params.get('is_container_group')
policy_instance_percentage = module.params.get('policy_instance_percentage')
policy_instance_minimum = module.params.get('policy_instance_minimum')
policy_instance_list = module.params.get('policy_instance_list')
@@ -129,6 +136,8 @@ def main():
new_fields['name'] = new_name if new_name else (module.get_item_name(existing_item) if existing_item else name)
if credential is not None:
new_fields['credential'] = credential_id
+ if is_container_group is not None:
+ new_fields['is_container_group'] = is_container_group
if policy_instance_percentage is not None:
new_fields['policy_instance_percentage'] = policy_instance_percentage
if policy_instance_minimum is not None:
diff --git a/awx_collection/test/awx/test_instance_group.py b/awx_collection/test/awx/test_instance_group.py
index 248d6f2d91..2516ce20ba 100644
--- a/awx_collection/test/awx/test_instance_group.py
+++ b/awx_collection/test/awx/test_instance_group.py
@@ -50,6 +50,7 @@ def test_container_group_create(run_module, admin_user, kube_credential):
result = run_module('tower_instance_group', {
'name': 'foo-c-group',
'credential': kube_credential.id,
+ 'is_container_group': True,
'state': 'present'
}, admin_user)
assert not result.get('failed', False), result['msg']
@@ -61,6 +62,7 @@ def test_container_group_create(run_module, admin_user, kube_credential):
result = run_module('tower_instance_group', {
'name': 'foo-c-group',
'credential': kube_credential.id,
+ 'is_container_group': True,
'pod_spec_override': pod_spec,
'state': 'present'
}, admin_user)
diff --git a/tools/ansible/roles/dockerfile/files/launch_awx_task.sh b/tools/ansible/roles/dockerfile/files/launch_awx_task.sh
index 8b9774a477..4df5aefeb7 100755
--- a/tools/ansible/roles/dockerfile/files/launch_awx_task.sh
+++ b/tools/ansible/roles/dockerfile/files/launch_awx_task.sh
@@ -13,29 +13,4 @@ if [ -n "${AWX_KUBE_DEVEL}" ]; then
export SDB_NOTIFY_HOST=$MY_POD_IP
fi
-source /etc/tower/conf.d/environment.sh
-
-ANSIBLE_REMOTE_TEMP=/tmp ANSIBLE_LOCAL_TEMP=/tmp ansible -i "127.0.0.1," -c local -v -m wait_for -a "host=$DATABASE_HOST port=$DATABASE_PORT" all
-ANSIBLE_REMOTE_TEMP=/tmp ANSIBLE_LOCAL_TEMP=/tmp ansible -i "127.0.0.1," -c local -v -m postgresql_db --become-user $DATABASE_USER -a "name=$DATABASE_NAME owner=$DATABASE_USER login_user=$DATABASE_USER login_host=$DATABASE_HOST login_password=$DATABASE_PASSWORD port=$DATABASE_PORT" all
-
-if [ -z "$AWX_SKIP_MIGRATIONS" ]; then
- echo "Running migrations..."
- awx-manage migrate --noinput
-fi
-
-if [ -z "$AWX_SKIP_PROVISION_INSTANCE" ]; then
- awx-manage provision_instance --hostname=$(hostname)
-fi
-
-if [ -z "$AWX_SKIP_REGISTERQUEUE" ]; then
- awx-manage register_queue --queuename=tower --instance_percent=100
-fi
-
-if [ ! -z "$AWX_ADMIN_USER" ]&&[ ! -z "$AWX_ADMIN_PASSWORD" ]; then
- echo "from django.contrib.auth.models import User; User.objects.create_superuser('$AWX_ADMIN_USER', 'root@localhost', '$AWX_ADMIN_PASSWORD')" | awx-manage shell
-fi
-echo 'from django.conf import settings; x = settings.AWX_TASK_ENV; x["HOME"] = "/var/lib/awx"; settings.AWX_TASK_ENV = x' | awx-manage shell
-
-unset $(cut -d = -f -1 /etc/tower/conf.d/environment.sh)
-
supervisord -c /etc/supervisord_task.conf