mirror of
https://github.com/ansible/awx.git
synced 2026-02-20 04:30:05 -03:30
Merge branch 'devel' into non-root-docker-tests
This commit is contained in:
@@ -29,3 +29,11 @@ STANDARD_INVENTORY_UPDATE_ENV = {
|
|||||||
CAN_CANCEL = ('new', 'pending', 'waiting', 'running')
|
CAN_CANCEL = ('new', 'pending', 'waiting', 'running')
|
||||||
ACTIVE_STATES = CAN_CANCEL
|
ACTIVE_STATES = CAN_CANCEL
|
||||||
CENSOR_VALUE = '************'
|
CENSOR_VALUE = '************'
|
||||||
|
ENV_BLACKLIST = frozenset((
|
||||||
|
'VIRTUAL_ENV', 'PATH', 'PYTHONPATH', 'PROOT_TMP_DIR', 'JOB_ID',
|
||||||
|
'INVENTORY_ID', 'INVENTORY_SOURCE_ID', 'INVENTORY_UPDATE_ID',
|
||||||
|
'AD_HOC_COMMAND_ID', 'REST_API_URL', 'REST_API_TOKEN', 'MAX_EVENT_RES',
|
||||||
|
'CALLBACK_QUEUE', 'CALLBACK_CONNECTION', 'CACHE',
|
||||||
|
'JOB_CALLBACK_DEBUG', 'INVENTORY_HOSTVARS', 'FACT_QUEUE',
|
||||||
|
'AWX_HOST', 'PROJECT_REVISION'
|
||||||
|
))
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ from awx.main.utils.filters import SmartFilter
|
|||||||
from awx.main.utils.encryption import encrypt_value, decrypt_value, get_encryption_key
|
from awx.main.utils.encryption import encrypt_value, decrypt_value, get_encryption_key
|
||||||
from awx.main.validators import validate_ssh_private_key
|
from awx.main.validators import validate_ssh_private_key
|
||||||
from awx.main.models.rbac import batch_role_ancestor_rebuilding, Role
|
from awx.main.models.rbac import batch_role_ancestor_rebuilding, Role
|
||||||
from awx.main.constants import CHOICES_PRIVILEGE_ESCALATION_METHODS
|
from awx.main.constants import CHOICES_PRIVILEGE_ESCALATION_METHODS, ENV_BLACKLIST
|
||||||
from awx.main import utils
|
from awx.main import utils
|
||||||
|
|
||||||
|
|
||||||
@@ -767,7 +767,12 @@ class CredentialTypeInjectorField(JSONSchemaField):
|
|||||||
# of underscores, digits, and alphabetics from the portable
|
# of underscores, digits, and alphabetics from the portable
|
||||||
# character set. The first character of a name is not
|
# character set. The first character of a name is not
|
||||||
# a digit.
|
# a digit.
|
||||||
'^[a-zA-Z_]+[a-zA-Z0-9_]*$': {'type': 'string'},
|
'^[a-zA-Z_]+[a-zA-Z0-9_]*$': {
|
||||||
|
'type': 'string',
|
||||||
|
# The environment variable _value_ can be any ascii,
|
||||||
|
# but pexpect will choke on any unicode
|
||||||
|
'pattern': '^[\x00-\x7F]*$'
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'additionalProperties': False,
|
'additionalProperties': False,
|
||||||
},
|
},
|
||||||
@@ -783,6 +788,19 @@ class CredentialTypeInjectorField(JSONSchemaField):
|
|||||||
'additionalProperties': False
|
'additionalProperties': False
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def validate_env_var_allowed(self, env_var):
|
||||||
|
if env_var.startswith('ANSIBLE_'):
|
||||||
|
raise django_exceptions.ValidationError(
|
||||||
|
_('Environment variable {} may affect Ansible configuration so its '
|
||||||
|
'use is not allowed in credentials.').format(env_var),
|
||||||
|
code='invalid', params={'value': env_var},
|
||||||
|
)
|
||||||
|
if env_var in ENV_BLACKLIST:
|
||||||
|
raise django_exceptions.ValidationError(
|
||||||
|
_('Environment variable {} is blacklisted from use in credentials.').format(env_var),
|
||||||
|
code='invalid', params={'value': env_var},
|
||||||
|
)
|
||||||
|
|
||||||
def validate(self, value, model_instance):
|
def validate(self, value, model_instance):
|
||||||
super(CredentialTypeInjectorField, self).validate(
|
super(CredentialTypeInjectorField, self).validate(
|
||||||
value, model_instance
|
value, model_instance
|
||||||
@@ -834,6 +852,9 @@ class CredentialTypeInjectorField(JSONSchemaField):
|
|||||||
setattr(valid_namespace['tower'].filename, template_name, 'EXAMPLE_FILENAME')
|
setattr(valid_namespace['tower'].filename, template_name, 'EXAMPLE_FILENAME')
|
||||||
|
|
||||||
for type_, injector in value.items():
|
for type_, injector in value.items():
|
||||||
|
if type_ == 'env':
|
||||||
|
for key in injector.keys():
|
||||||
|
self.validate_env_var_allowed(key)
|
||||||
for key, tmpl in injector.items():
|
for key, tmpl in injector.items():
|
||||||
try:
|
try:
|
||||||
Environment(
|
Environment(
|
||||||
|
|||||||
@@ -439,15 +439,6 @@ class CredentialType(CommonModelNameNotUnique):
|
|||||||
|
|
||||||
defaults = OrderedDict()
|
defaults = OrderedDict()
|
||||||
|
|
||||||
ENV_BLACKLIST = set((
|
|
||||||
'VIRTUAL_ENV', 'PATH', 'PYTHONPATH', 'PROOT_TMP_DIR', 'JOB_ID',
|
|
||||||
'INVENTORY_ID', 'INVENTORY_SOURCE_ID', 'INVENTORY_UPDATE_ID',
|
|
||||||
'AD_HOC_COMMAND_ID', 'REST_API_URL', 'REST_API_TOKEN', 'MAX_EVENT_RES',
|
|
||||||
'CALLBACK_QUEUE', 'CALLBACK_CONNECTION', 'CACHE',
|
|
||||||
'JOB_CALLBACK_DEBUG', 'INVENTORY_HOSTVARS', 'FACT_QUEUE',
|
|
||||||
'AWX_HOST', 'PROJECT_REVISION'
|
|
||||||
))
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
ordering = ('kind', 'name')
|
ordering = ('kind', 'name')
|
||||||
@@ -648,8 +639,14 @@ class CredentialType(CommonModelNameNotUnique):
|
|||||||
file_label = file_label.split('.')[1]
|
file_label = file_label.split('.')[1]
|
||||||
setattr(tower_namespace.filename, file_label, path)
|
setattr(tower_namespace.filename, file_label, path)
|
||||||
|
|
||||||
|
injector_field = self._meta.get_field('injectors')
|
||||||
for env_var, tmpl in self.injectors.get('env', {}).items():
|
for env_var, tmpl in self.injectors.get('env', {}).items():
|
||||||
if env_var.startswith('ANSIBLE_') or env_var in self.ENV_BLACKLIST:
|
try:
|
||||||
|
injector_field.validate_env_var_allowed(env_var)
|
||||||
|
except ValidationError as e:
|
||||||
|
logger.error(six.text_type(
|
||||||
|
'Ignoring prohibited env var {}, reason: {}'
|
||||||
|
).format(env_var, e))
|
||||||
continue
|
continue
|
||||||
env[env_var] = Template(tmpl).render(**namespace)
|
env[env_var] = Template(tmpl).render(**namespace)
|
||||||
safe_env[env_var] = Template(tmpl).render(**safe_namespace)
|
safe_env[env_var] = Template(tmpl).render(**safe_namespace)
|
||||||
|
|||||||
@@ -359,18 +359,17 @@ def test_create_with_valid_injectors(get, post, admin):
|
|||||||
},
|
},
|
||||||
'injectors': {
|
'injectors': {
|
||||||
'env': {
|
'env': {
|
||||||
'ANSIBLE_MY_CLOUD_TOKEN': '{{api_token}}'
|
'AWX_MY_CLOUD_TOKEN': '{{api_token}}'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, admin)
|
}, admin, expect=201)
|
||||||
assert response.status_code == 201
|
|
||||||
|
|
||||||
response = get(reverse('api:credential_type_list'), admin)
|
response = get(reverse('api:credential_type_list'), admin)
|
||||||
assert response.data['count'] == 1
|
assert response.data['count'] == 1
|
||||||
injectors = response.data['results'][0]['injectors']
|
injectors = response.data['results'][0]['injectors']
|
||||||
assert len(injectors) == 1
|
assert len(injectors) == 1
|
||||||
assert injectors['env'] == {
|
assert injectors['env'] == {
|
||||||
'ANSIBLE_MY_CLOUD_TOKEN': '{{api_token}}'
|
'AWX_MY_CLOUD_TOKEN': '{{api_token}}'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -388,7 +387,7 @@ def test_create_with_undefined_template_variable_xfail(post, admin):
|
|||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
'injectors': {
|
'injectors': {
|
||||||
'env': {'ANSIBLE_MY_CLOUD_TOKEN': '{{api_tolkien}}'}
|
'env': {'AWX_MY_CLOUD_TOKEN': '{{api_tolkien}}'}
|
||||||
}
|
}
|
||||||
}, admin)
|
}, admin)
|
||||||
assert response.status_code == 400
|
assert response.status_code == 400
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
import pytest
|
import pytest
|
||||||
|
import six
|
||||||
|
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from rest_framework.serializers import ValidationError as DRFValidationError
|
from rest_framework.serializers import ValidationError as DRFValidationError
|
||||||
@@ -123,6 +125,9 @@ def test_cred_type_input_schema_validity(input_, valid):
|
|||||||
({'env': {'AWX_SECRET_99': '{{awx_secret}}'}}, True),
|
({'env': {'AWX_SECRET_99': '{{awx_secret}}'}}, True),
|
||||||
({'env': {'99': '{{awx_secret}}'}}, False),
|
({'env': {'99': '{{awx_secret}}'}}, False),
|
||||||
({'env': {'AWX_SECRET=': '{{awx_secret}}'}}, False),
|
({'env': {'AWX_SECRET=': '{{awx_secret}}'}}, False),
|
||||||
|
({'env': {'ANSIBLE_SETTING': '{{awx_secret}}'}}, False),
|
||||||
|
({'env': {'DRAGON': u'🐉'}}, False),
|
||||||
|
({'env': {u'🐉': 'DRAGON'}}, False),
|
||||||
({'extra_vars': 123}, False),
|
({'extra_vars': 123}, False),
|
||||||
({'extra_vars': {}}, True),
|
({'extra_vars': {}}, True),
|
||||||
({'extra_vars': {'hostname': '{{host}}'}}, True),
|
({'extra_vars': {'hostname': '{{host}}'}}, True),
|
||||||
@@ -147,7 +152,8 @@ def test_cred_type_injectors_schema(injectors, valid):
|
|||||||
)
|
)
|
||||||
field = CredentialType._meta.get_field('injectors')
|
field = CredentialType._meta.get_field('injectors')
|
||||||
if valid is False:
|
if valid is False:
|
||||||
with pytest.raises(ValidationError):
|
with pytest.raises(ValidationError, message=six.text_type(
|
||||||
|
"Injector was supposed to throw a validation error, data: {}").format(injectors)):
|
||||||
field.clean(injectors, type_)
|
field.clean(injectors, type_)
|
||||||
else:
|
else:
|
||||||
field.clean(injectors, type_)
|
field.clean(injectors, type_)
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
---
|
---
|
||||||
dockerhub_version: "{{ lookup('file', playbook_dir + '/../VERSION') }}"
|
|
||||||
|
|
||||||
admin_user: 'admin'
|
admin_user: 'admin'
|
||||||
admin_email: 'root@localhost'
|
admin_email: 'root@localhost'
|
||||||
admin_password: 'password'
|
admin_password: ''
|
||||||
|
|
||||||
rabbitmq_user: 'awx'
|
rabbitmq_user: 'awx'
|
||||||
rabbitmq_password: 'password'
|
rabbitmq_password: ''
|
||||||
rabbitmq_erlang_cookie: 'cookiemonster'
|
rabbitmq_erlang_cookie: ''
|
||||||
|
|
||||||
kubernetes_base_path: "{{ local_base_config_path|default('/tmp') }}/{{ kubernetes_deployment_name }}-config"
|
kubernetes_base_path: "{{ local_base_config_path|default('/tmp') }}/{{ kubernetes_deployment_name }}-config"
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
register: result
|
register: result
|
||||||
until: result.stdout == "Running"
|
until: result.stdout == "Running"
|
||||||
retries: 60
|
retries: 60
|
||||||
|
delay: 10
|
||||||
|
|
||||||
- name: Create directory for backup
|
- name: Create directory for backup
|
||||||
file:
|
file:
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
kubectl_or_oc: "{{ openshift_oc_bin if openshift_oc_bin is defined else 'kubectl' }}"
|
kubectl_or_oc: "{{ openshift_oc_bin if openshift_oc_bin is defined else 'kubectl' }}"
|
||||||
|
|
||||||
- set_fact:
|
- set_fact:
|
||||||
deployment_object: "{{ 'dc' if openshift_host is defined else 'deployment' }}"
|
deployment_object: "sts"
|
||||||
|
|
||||||
- name: Record deployment size
|
- name: Record deployment size
|
||||||
shell: |
|
shell: |
|
||||||
@@ -156,6 +156,7 @@
|
|||||||
register: result
|
register: result
|
||||||
until: result.stdout == "Running"
|
until: result.stdout == "Running"
|
||||||
retries: 60
|
retries: 60
|
||||||
|
delay: 10
|
||||||
|
|
||||||
- name: Migrate database
|
- name: Migrate database
|
||||||
shell: |
|
shell: |
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
extra_opts: [--strip-components=1]
|
extra_opts: [--strip-components=1]
|
||||||
|
|
||||||
- set_fact:
|
- set_fact:
|
||||||
deployment_object: "{{ 'dc' if openshift_host is defined else 'deployment' }}"
|
deployment_object: "sts"
|
||||||
|
|
||||||
- name: Record deployment size
|
- name: Record deployment size
|
||||||
shell: |
|
shell: |
|
||||||
@@ -70,6 +70,7 @@
|
|||||||
register: result
|
register: result
|
||||||
until: result.stdout == "Running"
|
until: result.stdout == "Running"
|
||||||
retries: 60
|
retries: 60
|
||||||
|
delay: 10
|
||||||
|
|
||||||
- name: Temporarily grant createdb role
|
- name: Temporarily grant createdb role
|
||||||
shell: |
|
shell: |
|
||||||
@@ -79,7 +80,7 @@
|
|||||||
--host={{ pg_hostname | default('postgresql') }} \
|
--host={{ pg_hostname | default('postgresql') }} \
|
||||||
--port={{ pg_port | default('5432') }} \
|
--port={{ pg_port | default('5432') }} \
|
||||||
--username=postgres \
|
--username=postgres \
|
||||||
--dbname=template1 -c 'ALTER USER tower CREATEDB;'"
|
--dbname=template1 -c 'ALTER USER {{ pg_username }} CREATEDB;'"
|
||||||
no_log: true
|
no_log: true
|
||||||
when: pg_hostname is not defined or pg_hostname == ''
|
when: pg_hostname is not defined or pg_hostname == ''
|
||||||
|
|
||||||
@@ -102,7 +103,7 @@
|
|||||||
--host={{ pg_hostname | default('postgresql') }} \
|
--host={{ pg_hostname | default('postgresql') }} \
|
||||||
--port={{ pg_port | default('5432') }} \
|
--port={{ pg_port | default('5432') }} \
|
||||||
--username=postgres \
|
--username=postgres \
|
||||||
--dbname=template1 -c 'ALTER USER tower NOCREATEDB;'"
|
--dbname=template1 -c 'ALTER USER {{ pg_username }} NOCREATEDB;'"
|
||||||
no_log: true
|
no_log: true
|
||||||
when: pg_hostname is not defined or pg_hostname == ''
|
when: pg_hostname is not defined or pg_hostname == ''
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ metadata:
|
|||||||
namespace: {{ kubernetes_namespace }}
|
namespace: {{ kubernetes_namespace }}
|
||||||
name: rabbitmq
|
name: rabbitmq
|
||||||
labels:
|
labels:
|
||||||
app: rabbitmq
|
app: {{ kubernetes_deployment_name }}
|
||||||
type: LoadBalancer
|
type: LoadBalancer
|
||||||
spec:
|
spec:
|
||||||
type: NodePort
|
type: NodePort
|
||||||
@@ -26,7 +26,7 @@ spec:
|
|||||||
port: 5672
|
port: 5672
|
||||||
targetPort: 5672
|
targetPort: 5672
|
||||||
selector:
|
selector:
|
||||||
app: rabbitmq
|
app: {{ kubernetes_deployment_name }}
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
@@ -109,13 +109,8 @@ userNames:
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
---
|
---
|
||||||
{% if openshift_host is defined %}
|
apiVersion: apps/v1beta1
|
||||||
apiVersion: v1
|
kind: StatefulSet
|
||||||
kind: DeploymentConfig
|
|
||||||
{% else %}
|
|
||||||
apiVersion: extensions/v1beta1
|
|
||||||
kind: Deployment
|
|
||||||
{% endif %}
|
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ kubernetes_deployment_name }}
|
name: {{ kubernetes_deployment_name }}
|
||||||
namespace: {{ kubernetes_namespace }}
|
namespace: {{ kubernetes_namespace }}
|
||||||
@@ -126,31 +121,14 @@ spec:
|
|||||||
labels:
|
labels:
|
||||||
name: {{ kubernetes_deployment_name }}-web-deploy
|
name: {{ kubernetes_deployment_name }}-web-deploy
|
||||||
service: django
|
service: django
|
||||||
app: rabbitmq
|
app: {{ kubernetes_deployment_name }}
|
||||||
spec:
|
spec:
|
||||||
serviceAccountName: awx
|
serviceAccountName: awx
|
||||||
|
terminationGracePeriodSeconds: 10
|
||||||
containers:
|
containers:
|
||||||
- name: {{ kubernetes_deployment_name }}-web
|
- name: {{ kubernetes_deployment_name }}-web
|
||||||
image: "{{ kubernetes_web_image }}:{{ kubernetes_web_version }}"
|
image: "{{ kubernetes_web_image }}:{{ kubernetes_web_version }}"
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
env:
|
|
||||||
- name: DATABASE_USER
|
|
||||||
value: {{ pg_username }}
|
|
||||||
- name: DATABASE_NAME
|
|
||||||
value: {{ pg_database }}
|
|
||||||
- name: DATABASE_HOST
|
|
||||||
value: {{ pg_hostname|default('postgresql') }}
|
|
||||||
- name: DATABASE_PORT
|
|
||||||
value: "{{ pg_port|default('5432') }}"
|
|
||||||
- name: DATABASE_PASSWORD
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: "{{ kubernetes_deployment_name }}-secrets"
|
|
||||||
key: pg_password
|
|
||||||
- name: MEMCACHED_HOST
|
|
||||||
value: {{ memcached_hostname|default('localhost') }}
|
|
||||||
- name: RABBITMQ_HOST
|
|
||||||
value: {{ rabbitmq_hostname|default('localhost') }}
|
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8052
|
- containerPort: 8052
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
@@ -341,7 +319,7 @@ spec:
|
|||||||
port:
|
port:
|
||||||
targetPort: http
|
targetPort: http
|
||||||
tls:
|
tls:
|
||||||
insecureEdgeTerminationPolicy: Allow
|
insecureEdgeTerminationPolicy: Redirect
|
||||||
termination: edge
|
termination: edge
|
||||||
to:
|
to:
|
||||||
kind: Service
|
kind: Service
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: ansible-tower-management
|
- name: ansible-tower-management
|
||||||
image: {{ kubernetes_task_image }}
|
image: "{{ kubernetes_task_image }}:{{ kubernetes_task_version }}"
|
||||||
command: ["sleep", "infinity"]
|
command: ["sleep", "infinity"]
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: {{ kubernetes_deployment_name }}-application-config
|
- name: {{ kubernetes_deployment_name }}-application-config
|
||||||
|
|||||||
Reference in New Issue
Block a user