Skip to content

Commit

Permalink
convey OpenStack verify_ssl defaults in the CredentialType schema
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanpetrello committed Feb 20, 2019
1 parent 9f04fbe commit b1a3386
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 5 deletions.
9 changes: 9 additions & 0 deletions awx/main/fields.py
Expand Up @@ -671,6 +671,7 @@ def schema(self, model_instance):
'multiline': {'type': 'boolean'},
'secret': {'type': 'boolean'},
'ask_at_runtime': {'type': 'boolean'},
'default': {},
},
'additionalProperties': False,
'required': ['id', 'label'],
Expand Down Expand Up @@ -714,6 +715,14 @@ def validate(self, value, model_instance):
# If no type is specified, default to string
field['type'] = 'string'

if 'default' in field:
default = field['default']
_type = {'string': str, 'boolean': bool}[field['type']]
if type(default) != _type:
raise django_exceptions.ValidationError(
_('{} is not a {}').format(default, field['type'])
)

for key in ('choices', 'multiline', 'format', 'secret',):
if key in field and field['type'] != 'string':
raise django_exceptions.ValidationError(
Expand Down
9 changes: 8 additions & 1 deletion awx/main/models/credential/__init__.py
Expand Up @@ -445,13 +445,19 @@ def get_input(self, field_name, **kwargs):
try:
return decrypt_field(self, field_name)
except AttributeError:
for field in self.credential_type.inputs.get('fields', []):
if field['id'] == field_name and 'default' in field:
return field['default']
if 'default' in kwargs:
return kwargs['default']
raise AttributeError
if field_name in self.inputs:
return self.inputs[field_name]
if 'default' in kwargs:
return kwargs['default']
for field in self.credential_type.inputs.get('fields', []):
if field['id'] == field_name and 'default' in field:
return field['default']
raise AttributeError(field_name)

def has_input(self, field_name):
Expand Down Expand Up @@ -973,7 +979,8 @@ def create(self):
}, {
'id': 'verify_ssl',
'label': ugettext_noop('Verify SSL'),
'type': 'boolean'
'type': 'boolean',
'default': True,
}],
'required': ['username', 'password', 'host', 'project']
}
Expand Down
34 changes: 34 additions & 0 deletions awx/main/tests/functional/api/test_credential.py
Expand Up @@ -1353,6 +1353,40 @@ def test_openstack_create_ok(post, organization, admin, version, params):
assert response.status_code == 201


@pytest.mark.django_db
@pytest.mark.parametrize('verify_ssl, expected', [
[None, True],
[True, True],
[False, False],
])
def test_openstack_verify_ssl(get, post, organization, admin, verify_ssl, expected):
openstack = CredentialType.defaults['openstack']()
openstack.save()
inputs = {
'username': 'some_user',
'password': 'some_password',
'project': 'some_project',
'host': 'some_host',
}
if verify_ssl is not None:
inputs['verify_ssl'] = verify_ssl
params = {
'credential_type': openstack.id,
'inputs': inputs,
'name': 'Best credential ever',
'organization': organization.id
}
response = post(
reverse('api:credential_list', kwargs={'version': 'v2'}),
params,
admin
)
assert response.status_code == 201

cred = Credential.objects.get(pk=response.data['id'])
assert cred.get_input('verify_ssl') == expected


@pytest.mark.django_db
@pytest.mark.parametrize('version, params', [
['v1', {}],
Expand Down
68 changes: 68 additions & 0 deletions awx/main/tests/functional/api/test_credential_type.py
Expand Up @@ -271,6 +271,74 @@ def test_create_with_required_inputs(get, post, admin):
assert required == ['api_token']


@pytest.mark.django_db
@pytest.mark.parametrize('default, status_code', [
['some default string', 201],
[None, 400],
[True, 400],
[False, 400],
])
@pytest.mark.parametrize('secret', [True, False])
def test_create_with_default_string(get, post, admin, default, status_code, secret):
response = post(reverse('api:credential_type_list'), {
'kind': 'cloud',
'name': 'MyCloud',
'inputs': {
'fields': [{
'id': 'api_token',
'label': 'API Token',
'type': 'string',
'secret': secret,
'default': default,
}],
'required': ['api_token'],
},
'injectors': {}
}, admin)
assert response.status_code == status_code
if status_code == 201:
cred = Credential(
credential_type=CredentialType.objects.get(pk=response.data['id']),
name='My Custom Cred'
)
assert cred.get_input('api_token') == default
elif status_code == 400:
assert "{} is not a string".format(default) in json.dumps(response.data)


@pytest.mark.django_db
@pytest.mark.parametrize('default, status_code', [
['some default string', 400],
[None, 400],
[True, 201],
[False, 201],
])
def test_create_with_default_bool(get, post, admin, default, status_code):
response = post(reverse('api:credential_type_list'), {
'kind': 'cloud',
'name': 'MyCloud',
'inputs': {
'fields': [{
'id': 'api_token',
'label': 'API Token',
'type': 'boolean',
'default': default,
}],
'required': ['api_token'],
},
'injectors': {}
}, admin)
assert response.status_code == status_code
if status_code == 201:
cred = Credential(
credential_type=CredentialType.objects.get(pk=response.data['id']),
name='My Custom Cred'
)
assert cred.get_input('api_token') == default
elif status_code == 400:
assert "{} is not a boolean".format(default) in json.dumps(response.data)


@pytest.mark.django_db
@pytest.mark.parametrize('inputs', [
True,
Expand Down
7 changes: 4 additions & 3 deletions awx/main/tests/unit/test_tasks.py
Expand Up @@ -106,7 +106,7 @@ def test_safe_env_returns_new_copy():


@pytest.mark.parametrize("source,expected", [
(False, False), (True, True)
(None, True), (False, False), (True, True)
])
def test_openstack_client_config_generation(mocker, source, expected):
update = tasks.RunInventoryUpdate()
Expand All @@ -116,9 +116,10 @@ def test_openstack_client_config_generation(mocker, source, expected):
'username': 'demo',
'password': 'secrete',
'project': 'demo-project',
'domain': 'my-demo-domain',
'verify_ssl': source,
'domain': 'my-demo-domain'
}
if source is not None:
inputs['verify_ssl'] = source
credential = Credential(pk=1, credential_type=credential_type, inputs=inputs)

cred_method = mocker.Mock(return_value=credential)
Expand Down
4 changes: 3 additions & 1 deletion awx/ui/client/lib/components/input/base.controller.js
Expand Up @@ -20,7 +20,7 @@ function BaseInputController (strings) {
scope.state._displayPromptOnLaunch = true;
}

if (scope.state._value) {
if (typeof scope.state._value !== 'undefined') {
scope.state._edit = true;
scope.state._preEditValue = scope.state._value;

Expand All @@ -37,6 +37,8 @@ function BaseInputController (strings) {
scope.state._isBeingReplaced = false;
scope.state._activeModel = '_displayValue';
}
} else if (typeof scope.state.default !== 'undefined') {
scope.state._value = scope.state.default;
}

form.register(type, scope);
Expand Down
6 changes: 6 additions & 0 deletions docs/custom_credential_types.md
Expand Up @@ -136,6 +136,12 @@ ordered fields for that type:
"multiline": false # if true, the field should be rendered
# as multi-line for input entry
# (only applicable to `type=string`)
"default": "default value" # optional, can be used to provide a
# default value if the field is left empty
# when creating a credential of this type
# credential forms will use this value
# as a prefill when making credentials of
# this type
},{
# field 2...
},{
Expand Down

0 comments on commit b1a3386

Please sign in to comment.