Merge pull request #6844 from ryanpetrello/fix-6561

add help text + i18n handling for built-in Tower credential types
This commit is contained in:
Ryan Petrello
2017-07-05 13:25:33 -04:00
committed by GitHub
4 changed files with 70 additions and 22 deletions

View File

@@ -1924,6 +1924,17 @@ class CredentialTypeSerializer(BaseSerializer):
) )
return res return res
def to_representation(self, data):
value = super(CredentialTypeSerializer, self).to_representation(data)
# translate labels and help_text for credential fields "managed by Tower"
if value.get('managed_by_tower'):
for field in value.get('inputs', {}).get('fields', []):
field['label'] = _(field['label'])
if 'help_text' in field:
field['help_text'] = _(field['help_text'])
return value
# TODO: remove when API v1 is removed # TODO: remove when API v1 is removed
@six.add_metaclass(BaseSerializerMetaclass) @six.add_metaclass(BaseSerializerMetaclass)

View File

@@ -25,8 +25,8 @@ class Migration(migrations.Migration):
('name', models.CharField(max_length=512)), ('name', models.CharField(max_length=512)),
('kind', models.CharField(max_length=32, choices=[(b'ssh', 'SSH'), (b'vault', 'Vault'), (b'net', 'Network'), (b'scm', 'Source Control'), (b'cloud', 'Cloud'), (b'insights', 'Insights')])), ('kind', models.CharField(max_length=32, choices=[(b'ssh', 'SSH'), (b'vault', 'Vault'), (b'net', 'Network'), (b'scm', 'Source Control'), (b'cloud', 'Cloud'), (b'insights', 'Insights')])),
('managed_by_tower', models.BooleanField(default=False, editable=False)), ('managed_by_tower', models.BooleanField(default=False, editable=False)),
('inputs', awx.main.fields.CredentialTypeInputField(default={}, blank=True)), ('inputs', awx.main.fields.CredentialTypeInputField(default={}, blank=True, help_text='Enter inputs using either JSON or YAML syntax. Use the radio button to toggle between the two. Refer to the Ansible Tower documentation for example syntax.')),
('injectors', awx.main.fields.CredentialTypeInjectorField(default={}, blank=True)), ('injectors', awx.main.fields.CredentialTypeInjectorField(default={}, blank=True, help_text='Enter injectors using either JSON or YAML syntax. Use the radio button to toggle between the two. Refer to the Ansible Tower documentation for example syntax.')),
('created_by', models.ForeignKey(related_name="{u'class': 'credentialtype', u'app_label': 'main'}(class)s_created+", on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)), ('created_by', models.ForeignKey(related_name="{u'class': 'credentialtype', u'app_label': 'main'}(class)s_created+", on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)),
('modified_by', models.ForeignKey(related_name="{u'class': 'credentialtype', u'app_label': 'main'}(class)s_modified+", on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)), ('modified_by', models.ForeignKey(related_name="{u'class': 'credentialtype', u'app_label': 'main'}(class)s_modified+", on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)),
('tags', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', blank=True, help_text='A comma-separated list of tags.', verbose_name='Tags')), ('tags', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', blank=True, help_text='A comma-separated list of tags.', verbose_name='Tags')),

View File

@@ -99,12 +99,12 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='credential', model_name='credential',
name='credential_type', name='credential_type',
field=models.ForeignKey(related_name='credentials', to='main.CredentialType', null=False, help_text='Type for this credential. Credential Types define valid fields (e.g,. "username", "password") and their properties (e.g,. "username is required" or "password should be stored with encryption").') field=models.ForeignKey(related_name='credentials', to='main.CredentialType', null=False, help_text='Specify the type of credential you want to create. Refer to the Ansible Tower documentation for details on each type.')
), ),
migrations.AlterField( migrations.AlterField(
model_name='credential', model_name='credential',
name='inputs', name='inputs',
field=awx.main.fields.CredentialInputField(default={}, help_text='Data structure used to specify input values (e.g., {"username": "jane-doe", "password": "secret"}). Valid fields and their requirements vary depending on the fields defined on the chosen CredentialType.', blank=True), field=awx.main.fields.CredentialInputField(default={}, help_text='Enter inputs using either JSON or YAML syntax. Use the radio button to toggle between the two. Refer to the Ansible Tower documentation for example syntax.', blank=True),
), ),
migrations.RemoveField( migrations.RemoveField(
model_name='job', model_name='job',

View File

@@ -219,10 +219,8 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
'CredentialType', 'CredentialType',
related_name='credentials', related_name='credentials',
null=False, null=False,
help_text=_('Type for this credential. Credential Types define ' help_text=_('Specify the type of credential you want to create. Refer '
'valid fields (e.g,. "username", "password") and their ' 'to the Ansible Tower documentation for details on each type.')
'properties (e.g,. "username is required" or "password '
'should be stored with encryption").')
) )
organization = models.ForeignKey( organization = models.ForeignKey(
'Organization', 'Organization',
@@ -235,10 +233,9 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
inputs = CredentialInputField( inputs = CredentialInputField(
blank=True, blank=True,
default={}, default={},
help_text=_('Data structure used to specify input values (e.g., ' help_text=_('Enter inputs using either JSON or YAML syntax. Use the '
'{"username": "jane-doe", "password": "secret"}). Valid ' 'radio button to toggle between the two. Refer to the '
'fields and their requirements vary depending on the ' 'Ansible Tower documentation for example syntax.')
'fields defined on the chosen CredentialType.')
) )
admin_role = ImplicitRoleField( admin_role = ImplicitRoleField(
parent_role=[ parent_role=[
@@ -421,11 +418,17 @@ class CredentialType(CommonModelNameNotUnique):
) )
inputs = CredentialTypeInputField( inputs = CredentialTypeInputField(
blank=True, blank=True,
default={} default={},
help_text=_('Enter inputs using either JSON or YAML syntax. Use the '
'radio button to toggle between the two. Refer to the '
'Ansible Tower documentation for example syntax.')
) )
injectors = CredentialTypeInjectorField( injectors = CredentialTypeInjectorField(
blank=True, blank=True,
default={} default={},
help_text=_('Enter injectors using either JSON or YAML syntax. Use the '
'radio button to toggle between the two. Refer to the '
'Ansible Tower documentation for example syntax.')
) )
def get_absolute_url(self, request=None): def get_absolute_url(self, request=None):
@@ -621,7 +624,10 @@ def ssh(cls):
'id': 'become_method', 'id': 'become_method',
'label': 'Privilege Escalation Method', 'label': 'Privilege Escalation Method',
'choices': map(operator.itemgetter(0), 'choices': map(operator.itemgetter(0),
V1Credential.FIELDS['become_method'].choices) V1Credential.FIELDS['become_method'].choices),
'help_text': ('Specify a method for "become" operations. This is '
'equivalent to specifying the --become-method '
'Ansible parameter.')
}, { }, {
'id': 'become_username', 'id': 'become_username',
'label': 'Privilege Escalation Username', 'label': 'Privilege Escalation Username',
@@ -751,6 +757,10 @@ def aws(cls):
'label': 'STS Token', 'label': 'STS Token',
'type': 'string', 'type': 'string',
'secret': True, 'secret': True,
'help_text': ('Security Token Service (STS) is a web service '
'that enables you to request temporary, '
'limited-privilege credentials for AWS Identity '
'and Access Management (IAM) users.'),
}], }],
'required': ['username', 'password'] 'required': ['username', 'password']
} }
@@ -777,6 +787,8 @@ def openstack(cls):
'id': 'host', 'id': 'host',
'label': 'Host (Authentication URL)', 'label': 'Host (Authentication URL)',
'type': 'string', 'type': 'string',
'help_text': ('The host to authenticate with. For example, '
'https://openstack.business.com/v2.0/')
}, { }, {
'id': 'project', 'id': 'project',
'label': 'Project (Tenant Name)', 'label': 'Project (Tenant Name)',
@@ -784,7 +796,11 @@ def openstack(cls):
}, { }, {
'id': 'domain', 'id': 'domain',
'label': 'Domain Name', 'label': 'Domain Name',
'type': 'string' 'type': 'string',
'help_text': ('OpenStack domains define administrative boundaries. '
'It is only needed for Keystone v3 authentication '
'URLs. Refer to Ansible Tower documentation for '
'common scenarios.')
}], }],
'required': ['username', 'password', 'host', 'project'] 'required': ['username', 'password', 'host', 'project']
} }
@@ -802,6 +818,8 @@ def vmware(cls):
'id': 'host', 'id': 'host',
'label': 'VCenter Host', 'label': 'VCenter Host',
'type': 'string', 'type': 'string',
'help_text': ('Enter the hostname or IP address which corresponds '
'to your VMware vCenter.')
}, { }, {
'id': 'username', 'id': 'username',
'label': 'Username', 'label': 'Username',
@@ -828,6 +846,8 @@ def satellite6(cls):
'id': 'host', 'id': 'host',
'label': 'Satellite 6 URL', 'label': 'Satellite 6 URL',
'type': 'string', 'type': 'string',
'help_text': ('Enter the URL which corresponds to your Red Hat '
'Satellite 6 server. For example, https://satellite.example.org')
}, { }, {
'id': 'username', 'id': 'username',
'label': 'Username', 'label': 'Username',
@@ -853,6 +873,9 @@ def cloudforms(cls):
'id': 'host', 'id': 'host',
'label': 'CloudForms URL', 'label': 'CloudForms URL',
'type': 'string', 'type': 'string',
'help_text': ('Enter the URL for the virtual machine which '
'corresponds to your CloudForm instance. '
'For example, https://cloudforms.example.org')
}, { }, {
'id': 'username', 'id': 'username',
'label': 'Username', 'label': 'Username',
@@ -877,18 +900,25 @@ def gce(cls):
'fields': [{ 'fields': [{
'id': 'username', 'id': 'username',
'label': 'Service Account Email Address', 'label': 'Service Account Email Address',
'type': 'string' 'type': 'string',
'help_text': ('The email address assigned to the Google Compute '
'Engine service account.')
}, { }, {
'id': 'project', 'id': 'project',
'label': 'Project', 'label': 'Project',
'type': 'string' 'type': 'string',
'help_text': ('The Project ID is the GCE assigned identification. '
'It is constructed as two words followed by a three '
'digit number. Example: adjective-noun-000')
}, { }, {
'id': 'ssh_key_data', 'id': 'ssh_key_data',
'label': 'RSA Private Key', 'label': 'RSA Private Key',
'type': 'string', 'type': 'string',
'format': 'ssh_private_key', 'format': 'ssh_private_key',
'secret': True, 'secret': True,
'multiline': True 'multiline': True,
'help_text': ('Paste the contents of the PEM file associated '
'with the service account email.')
}] }]
} }
) )
@@ -904,14 +934,19 @@ def azure(cls):
'fields': [{ 'fields': [{
'id': 'username', 'id': 'username',
'label': 'Subscription ID', 'label': 'Subscription ID',
'type': 'string' 'type': 'string',
'help_text': ('Subscription ID is an Azure construct, which is '
'mapped to a username.')
}, { }, {
'id': 'ssh_key_data', 'id': 'ssh_key_data',
'label': 'Management Certificate', 'label': 'Management Certificate',
'type': 'string', 'type': 'string',
'format': 'ssh_private_key', 'format': 'ssh_private_key',
'secret': True, 'secret': True,
'multiline': True 'multiline': True,
'help_text': ('Paste the contents of the PEM file that corresponds '
'to the certificate you uploaded in the Microsoft '
'Azure console.')
}] }]
} }
) )
@@ -927,7 +962,9 @@ def azure_rm(cls):
'fields': [{ 'fields': [{
'id': 'subscription', 'id': 'subscription',
'label': 'Subscription ID', 'label': 'Subscription ID',
'type': 'string' 'type': 'string',
'help_text': ('Subscription ID is an Azure construct, which is '
'mapped to a username.')
}, { }, {
'id': 'username', 'id': 'username',
'label': 'Username', 'label': 'Username',