mirror of
https://github.com/ansible/awx.git
synced 2026-05-08 09:57:35 -02:30
migrate v1 credentials to the new v2 credentialtype + credential model
see: #5878
This commit is contained in:
57
awx/main/migrations/0039_v320_add_credentialtype_model.py
Normal file
57
awx/main/migrations/0039_v320_add_credentialtype_model.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
import taggit.managers
|
||||||
|
import awx.main.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('main', '0038_v320_data_migrations'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CredentialType',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
|
('created', models.DateTimeField(default=None, editable=False)),
|
||||||
|
('modified', models.DateTimeField(default=None, editable=False)),
|
||||||
|
('description', models.TextField(default=b'', blank=True)),
|
||||||
|
('name', models.CharField(max_length=512)),
|
||||||
|
('kind', models.CharField(max_length=32, choices=[(b'machine', 'Machine'), (b'net', 'Network'), (b'scm', 'Source Control'), (b'cloud', 'Cloud')])),
|
||||||
|
('managed_by_tower', models.BooleanField(default=False, editable=False)),
|
||||||
|
('inputs', awx.main.fields.CredentialTypeInputField(default={}, blank=True)),
|
||||||
|
('injectors', awx.main.fields.CredentialTypeInjectorField(default={}, blank=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)),
|
||||||
|
('tags', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', blank=True, help_text='A comma-separated list of tags.', verbose_name='Tags')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ('kind', 'name'),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='credential',
|
||||||
|
options={'ordering': ('name',)},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='credential',
|
||||||
|
name='inputs',
|
||||||
|
field=awx.main.fields.CredentialInputField(default={}, blank=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='credential',
|
||||||
|
name='credential_type',
|
||||||
|
field=models.ForeignKey(related_name='credentials', to='main.CredentialType', null=True),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='credential',
|
||||||
|
unique_together=set([('organization', 'name', 'credential_type')]),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.conf import settings
|
|
||||||
import taggit.managers
|
|
||||||
import awx.main.fields
|
|
||||||
from awx.main.migrations import _credentialtypes as credentialtypes
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('main', '0038_v320_data_migrations'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='CredentialType',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
|
||||||
('created', models.DateTimeField(default=None, editable=False)),
|
|
||||||
('modified', models.DateTimeField(default=None, editable=False)),
|
|
||||||
('description', models.TextField(default=b'', blank=True)),
|
|
||||||
('name', models.CharField(max_length=512)),
|
|
||||||
('kind', models.CharField(max_length=32, choices=[(b'machine', 'Machine'), (b'net', 'Network'), (b'scm', 'Source Control'), (b'cloud', 'Cloud')])),
|
|
||||||
('managed_by_tower', models.BooleanField(default=False, editable=False)),
|
|
||||||
('inputs', awx.main.fields.CredentialTypeInputField(default={}, blank=True)),
|
|
||||||
('injectors', awx.main.fields.CredentialTypeInjectorField(default={}, blank=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)),
|
|
||||||
('tags', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', blank=True, help_text='A comma-separated list of tags.', verbose_name='Tags')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'ordering': ('kind', 'name'),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='credential',
|
|
||||||
options={'ordering': ('name',)},
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='credential',
|
|
||||||
name='inputs',
|
|
||||||
field=awx.main.fields.CredentialInputField(default={}, blank=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='credential',
|
|
||||||
name='credential_type',
|
|
||||||
field=models.ForeignKey(related_name='credentials', to='main.CredentialType', null=True),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
migrations.AlterUniqueTogether(
|
|
||||||
name='credential',
|
|
||||||
unique_together=set([('organization', 'name', 'credential_type')]),
|
|
||||||
),
|
|
||||||
migrations.RunPython(credentialtypes.create_tower_managed_credential_types),
|
|
||||||
# MIGRATION TODO: For each credential, look at the columns below to
|
|
||||||
# determine the appropriate CredentialType (and assign it). Additionally,
|
|
||||||
# set `self.input` to the appropriate JSON blob
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='credential',
|
|
||||||
name='authorize',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='credential',
|
|
||||||
name='authorize_password',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='credential',
|
|
||||||
name='become_method',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='credential',
|
|
||||||
name='become_password',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='credential',
|
|
||||||
name='become_username',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='credential',
|
|
||||||
name='client',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='credential',
|
|
||||||
name='cloud',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='credential',
|
|
||||||
name='domain',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='credential',
|
|
||||||
name='host',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='credential',
|
|
||||||
name='kind',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='credential',
|
|
||||||
name='password',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='credential',
|
|
||||||
name='project',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='credential',
|
|
||||||
name='secret',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='credential',
|
|
||||||
name='security_token',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='credential',
|
|
||||||
name='ssh_key_data',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='credential',
|
|
||||||
name='ssh_key_unlock',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='credential',
|
|
||||||
name='subscription',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='credential',
|
|
||||||
name='tenant',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='credential',
|
|
||||||
name='username',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='credential',
|
|
||||||
name='vault_password',
|
|
||||||
),
|
|
||||||
migrations.AlterUniqueTogether(
|
|
||||||
name='credentialtype',
|
|
||||||
unique_together=set([('name', 'kind')]),
|
|
||||||
),
|
|
||||||
# MIGRATION TODO: Once credentials are migrated, alter the credential_type
|
|
||||||
# foreign key to be non-NULLable
|
|
||||||
]
|
|
||||||
16
awx/main/migrations/0040_v320_migrate_v1_credentials.py
Normal file
16
awx/main/migrations/0040_v320_migrate_v1_credentials.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
from awx.main.migrations import _credentialtypes as credentialtypes
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('main', '0039_v320_add_credentialtype_model'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(credentialtypes.migrate_to_v2_credentials),
|
||||||
|
]
|
||||||
98
awx/main/migrations/0041_v320_drop_v1_credential_fields.py
Normal file
98
awx/main/migrations/0041_v320_drop_v1_credential_fields.py
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('main', '0040_v320_migrate_v1_credentials'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='credential',
|
||||||
|
name='authorize',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='credential',
|
||||||
|
name='authorize_password',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='credential',
|
||||||
|
name='become_method',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='credential',
|
||||||
|
name='become_password',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='credential',
|
||||||
|
name='become_username',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='credential',
|
||||||
|
name='client',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='credential',
|
||||||
|
name='cloud',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='credential',
|
||||||
|
name='domain',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='credential',
|
||||||
|
name='host',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='credential',
|
||||||
|
name='kind',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='credential',
|
||||||
|
name='password',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='credential',
|
||||||
|
name='project',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='credential',
|
||||||
|
name='secret',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='credential',
|
||||||
|
name='security_token',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='credential',
|
||||||
|
name='ssh_key_data',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='credential',
|
||||||
|
name='ssh_key_unlock',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='credential',
|
||||||
|
name='subscription',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='credential',
|
||||||
|
name='tenant',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='credential',
|
||||||
|
name='username',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='credential',
|
||||||
|
name='vault_password',
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='credentialtype',
|
||||||
|
unique_together=set([('name', 'kind')]),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,5 +1,49 @@
|
|||||||
from awx.main.models import CredentialType
|
from django.db.models.signals import post_save
|
||||||
|
from awx.main.models import Credential, CredentialType
|
||||||
|
|
||||||
|
|
||||||
def create_tower_managed_credential_types(apps, schema_editor):
|
def migrate_to_v2_credentials(apps, schema_editor):
|
||||||
CredentialType.setup_tower_managed_defaults()
|
CredentialType.setup_tower_managed_defaults()
|
||||||
|
|
||||||
|
for cred in apps.get_model('main', 'Credential').objects.all():
|
||||||
|
data = {}
|
||||||
|
if getattr(cred, 'vault_password', None):
|
||||||
|
data['vault_password'] = cred.vault_password
|
||||||
|
credential_type = CredentialType.from_v1_kind(cred.kind, data)
|
||||||
|
defined_fields = credential_type.defined_fields
|
||||||
|
cred.credential_type = apps.get_model('main', 'CredentialType').objects.get(pk=credential_type.pk)
|
||||||
|
|
||||||
|
# temporarily disable implicit role signals; the class we're working on
|
||||||
|
# is the "pre-migration" credential model; our signals don't like that
|
||||||
|
# it differs from the "post-migration" credential model
|
||||||
|
for field in cred.__class__.__implicit_role_fields:
|
||||||
|
post_save.disconnect(field, cred.__class__, dispatch_uid='implicit-role-post-save')
|
||||||
|
|
||||||
|
for field in defined_fields:
|
||||||
|
if getattr(cred, field, None):
|
||||||
|
cred.inputs[field] = getattr(cred, field)
|
||||||
|
cred.save()
|
||||||
|
|
||||||
|
#
|
||||||
|
# If the credential contains a vault password, create a new
|
||||||
|
# *additional* credential with the proper CredentialType; this needs to
|
||||||
|
# perform a deep copy of the Credential that considers:
|
||||||
|
#
|
||||||
|
if cred.vault_password:
|
||||||
|
new_fields = {}
|
||||||
|
for field in CredentialType.from_v1_kind('ssh').defined_fields:
|
||||||
|
if getattr(cred, field, None):
|
||||||
|
new_fields[field] = getattr(cred, field)
|
||||||
|
|
||||||
|
if new_fields:
|
||||||
|
# We need to make an ssh credential, too
|
||||||
|
new_cred = Credential(credential_type=CredentialType.from_v1_kind('ssh'))
|
||||||
|
for field, value in new_fields.items():
|
||||||
|
new_cred.inputs[field] = value
|
||||||
|
|
||||||
|
# TODO: copy RBAC and Job Template assignments
|
||||||
|
new_cred.save()
|
||||||
|
|
||||||
|
# re-enable implicit role signals
|
||||||
|
for field in cred.__class__.__implicit_role_fields:
|
||||||
|
post_save.connect(field._post_save, cred.__class__, True, dispatch_uid='implicit-role-post-save')
|
||||||
|
|||||||
313
awx/main/tests/functional/test_credential_migration.py
Normal file
313
awx/main/tests/functional/test_credential_migration.py
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
import mock
|
||||||
|
import pytest
|
||||||
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
|
|
||||||
|
from awx.main.models import Credential, CredentialType
|
||||||
|
from awx.main.migrations._credentialtypes import migrate_to_v2_credentials
|
||||||
|
from awx.main.utils.common import decrypt_field
|
||||||
|
|
||||||
|
EXAMPLE_PRIVATE_KEY = '-----BEGIN PRIVATE KEY-----\nxyz==\n-----END PRIVATE KEY-----'
|
||||||
|
|
||||||
|
# TODO: remove this set of tests when API v1 is removed
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def migrate(credential, kind):
|
||||||
|
with mock.patch.object(Credential, 'kind', kind), \
|
||||||
|
mock.patch.object(Credential, 'objects', mock.Mock(
|
||||||
|
get=lambda **kw: credential,
|
||||||
|
all=lambda: [credential]
|
||||||
|
)):
|
||||||
|
class Apps(apps.__class__):
|
||||||
|
def get_model(self, app, model):
|
||||||
|
if model == 'Credential':
|
||||||
|
return Credential
|
||||||
|
return apps.get_model(app, model)
|
||||||
|
yield
|
||||||
|
migrate_to_v2_credentials(Apps(), None)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_ssh_migration():
|
||||||
|
cred = Credential(name='My Credential')
|
||||||
|
with migrate(cred, 'ssh'):
|
||||||
|
cred.__dict__.update({
|
||||||
|
'username': 'bob',
|
||||||
|
'password': 'secret',
|
||||||
|
'ssh_key_data': EXAMPLE_PRIVATE_KEY,
|
||||||
|
'ssh_key_unlock': 'keypass',
|
||||||
|
'become_method': 'sudo',
|
||||||
|
'become_username': 'superuser',
|
||||||
|
'become_password': 'superpassword',
|
||||||
|
})
|
||||||
|
|
||||||
|
assert cred.credential_type.name == 'SSH'
|
||||||
|
assert cred.inputs['username'] == 'bob'
|
||||||
|
assert cred.inputs['password'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'password') == 'secret'
|
||||||
|
assert cred.inputs['ssh_key_data'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'ssh_key_data') == EXAMPLE_PRIVATE_KEY
|
||||||
|
assert cred.inputs['ssh_key_unlock'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'ssh_key_unlock') == 'keypass'
|
||||||
|
assert cred.inputs['become_method'] == 'sudo'
|
||||||
|
assert cred.inputs['become_username'] == 'superuser'
|
||||||
|
assert cred.inputs['become_password'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'become_password') == 'superpassword'
|
||||||
|
assert Credential.objects.count() == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_scm_migration():
|
||||||
|
cred = Credential(name='My Credential')
|
||||||
|
with migrate(cred, 'scm'):
|
||||||
|
cred.__dict__.update({
|
||||||
|
'username': 'bob',
|
||||||
|
'password': 'secret',
|
||||||
|
'ssh_key_data': EXAMPLE_PRIVATE_KEY,
|
||||||
|
'ssh_key_unlock': 'keypass',
|
||||||
|
})
|
||||||
|
|
||||||
|
assert cred.credential_type.name == 'Source Control'
|
||||||
|
assert cred.inputs['username'] == 'bob'
|
||||||
|
assert cred.inputs['password'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'password') == 'secret'
|
||||||
|
assert cred.inputs['ssh_key_data'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'ssh_key_data') == EXAMPLE_PRIVATE_KEY
|
||||||
|
assert cred.inputs['ssh_key_unlock'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'ssh_key_unlock') == 'keypass'
|
||||||
|
assert Credential.objects.count() == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_vault_only_migration():
|
||||||
|
cred = Credential(name='My Credential')
|
||||||
|
with migrate(cred, 'ssh'):
|
||||||
|
cred.__dict__.update({
|
||||||
|
'vault_password': 'vault',
|
||||||
|
})
|
||||||
|
|
||||||
|
assert cred.credential_type.name == 'Vault'
|
||||||
|
assert cred.inputs['vault_password'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'vault_password') == 'vault'
|
||||||
|
assert Credential.objects.count() == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_vault_with_ssh_migration():
|
||||||
|
cred = Credential(name='My Credential')
|
||||||
|
with migrate(cred, 'ssh'):
|
||||||
|
cred.__dict__.update({
|
||||||
|
'vault_password': 'vault',
|
||||||
|
'username': 'bob',
|
||||||
|
'password': 'secret',
|
||||||
|
'ssh_key_data': EXAMPLE_PRIVATE_KEY,
|
||||||
|
'ssh_key_unlock': 'keypass',
|
||||||
|
'become_method': 'sudo',
|
||||||
|
'become_username': 'superuser',
|
||||||
|
'become_password': 'superpassword',
|
||||||
|
})
|
||||||
|
assert Credential.objects.count() == 2
|
||||||
|
|
||||||
|
assert Credential.objects.filter(credential_type__name='Vault').get() == cred
|
||||||
|
assert cred.inputs.keys() == ['vault_password']
|
||||||
|
assert cred.inputs['vault_password'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'vault_password') == 'vault'
|
||||||
|
|
||||||
|
ssh_cred = Credential.objects.filter(credential_type__name='SSH').get()
|
||||||
|
assert sorted(ssh_cred.inputs.keys()) == sorted(CredentialType.from_v1_kind('ssh').defined_fields)
|
||||||
|
assert ssh_cred.credential_type.name == 'SSH'
|
||||||
|
assert ssh_cred.inputs['username'] == 'bob'
|
||||||
|
assert ssh_cred.inputs['password'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(ssh_cred, 'password') == 'secret'
|
||||||
|
assert ssh_cred.inputs['ssh_key_data'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(ssh_cred, 'ssh_key_data') == EXAMPLE_PRIVATE_KEY
|
||||||
|
assert ssh_cred.inputs['ssh_key_unlock'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(ssh_cred, 'ssh_key_unlock') == 'keypass'
|
||||||
|
assert ssh_cred.inputs['become_method'] == 'sudo'
|
||||||
|
assert ssh_cred.inputs['become_username'] == 'superuser'
|
||||||
|
assert ssh_cred.inputs['become_password'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(ssh_cred, 'become_password') == 'superpassword'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_net_migration():
|
||||||
|
cred = Credential(name='My Credential')
|
||||||
|
with migrate(cred, 'net'):
|
||||||
|
cred.__dict__.update({
|
||||||
|
'username': 'bob',
|
||||||
|
'password': 'secret',
|
||||||
|
'ssh_key_data': EXAMPLE_PRIVATE_KEY,
|
||||||
|
'ssh_key_unlock': 'keypass',
|
||||||
|
'authorize_password': 'authorize-secret',
|
||||||
|
})
|
||||||
|
|
||||||
|
assert cred.credential_type.name == 'Network'
|
||||||
|
assert cred.inputs['username'] == 'bob'
|
||||||
|
assert cred.inputs['password'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'password') == 'secret'
|
||||||
|
assert cred.inputs['ssh_key_data'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'ssh_key_data') == EXAMPLE_PRIVATE_KEY
|
||||||
|
assert cred.inputs['ssh_key_unlock'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'ssh_key_unlock') == 'keypass'
|
||||||
|
assert cred.inputs['authorize_password'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'authorize_password') == 'authorize-secret'
|
||||||
|
assert Credential.objects.count() == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_aws_migration():
|
||||||
|
cred = Credential(name='My Credential')
|
||||||
|
with migrate(cred, 'aws'):
|
||||||
|
cred.__dict__.update({
|
||||||
|
'username': 'bob',
|
||||||
|
'password': 'secret',
|
||||||
|
'security_token': 'secret-token'
|
||||||
|
})
|
||||||
|
|
||||||
|
assert cred.credential_type.name == 'Amazon Web Services'
|
||||||
|
assert cred.inputs['username'] == 'bob'
|
||||||
|
assert cred.inputs['password'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'password') == 'secret'
|
||||||
|
assert cred.inputs['security_token'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'security_token') == 'secret-token'
|
||||||
|
assert Credential.objects.count() == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_openstack_migration():
|
||||||
|
cred = Credential(name='My Credential')
|
||||||
|
with migrate(cred, 'openstack'):
|
||||||
|
cred.__dict__.update({
|
||||||
|
'username': 'bob',
|
||||||
|
'password': 'secret',
|
||||||
|
'host': 'https://keystone.example.org/',
|
||||||
|
'project': 'TENANT_ID',
|
||||||
|
})
|
||||||
|
|
||||||
|
assert cred.credential_type.name == 'OpenStack'
|
||||||
|
assert cred.inputs['username'] == 'bob'
|
||||||
|
assert cred.inputs['password'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'password') == 'secret'
|
||||||
|
assert cred.inputs['host'] == 'https://keystone.example.org/'
|
||||||
|
assert cred.inputs['project'] == 'TENANT_ID'
|
||||||
|
assert Credential.objects.count() == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason="TODO: rackspace should be a custom type (we're removing official support)")
|
||||||
|
def test_rackspace():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_vmware_migration():
|
||||||
|
cred = Credential(name='My Credential')
|
||||||
|
with migrate(cred, 'vmware'):
|
||||||
|
cred.__dict__.update({
|
||||||
|
'username': 'bob',
|
||||||
|
'password': 'secret',
|
||||||
|
'host': 'https://example.org/',
|
||||||
|
})
|
||||||
|
|
||||||
|
assert cred.credential_type.name == 'VMware vCenter'
|
||||||
|
assert cred.inputs['username'] == 'bob'
|
||||||
|
assert cred.inputs['password'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'password') == 'secret'
|
||||||
|
assert cred.inputs['host'] == 'https://example.org/'
|
||||||
|
assert Credential.objects.count() == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_satellite6_migration():
|
||||||
|
cred = Credential(name='My Credential')
|
||||||
|
with migrate(cred, 'satellite6'):
|
||||||
|
cred.__dict__.update({
|
||||||
|
'username': 'bob',
|
||||||
|
'password': 'secret',
|
||||||
|
'host': 'https://example.org/',
|
||||||
|
})
|
||||||
|
|
||||||
|
assert cred.credential_type.name == 'Red Hat Satellite 6'
|
||||||
|
assert cred.inputs['username'] == 'bob'
|
||||||
|
assert cred.inputs['password'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'password') == 'secret'
|
||||||
|
assert cred.inputs['host'] == 'https://example.org/'
|
||||||
|
assert Credential.objects.count() == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_cloudforms_migration():
|
||||||
|
cred = Credential(name='My Credential')
|
||||||
|
with migrate(cred, 'cloudforms'):
|
||||||
|
cred.__dict__.update({
|
||||||
|
'username': 'bob',
|
||||||
|
'password': 'secret',
|
||||||
|
'host': 'https://example.org/',
|
||||||
|
})
|
||||||
|
|
||||||
|
assert cred.credential_type.name == 'Red Hat CloudForms'
|
||||||
|
assert cred.inputs['username'] == 'bob'
|
||||||
|
assert cred.inputs['password'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'password') == 'secret'
|
||||||
|
assert cred.inputs['host'] == 'https://example.org/'
|
||||||
|
assert Credential.objects.count() == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_gce_migration():
|
||||||
|
cred = Credential(name='My Credential')
|
||||||
|
with migrate(cred, 'gce'):
|
||||||
|
cred.__dict__.update({
|
||||||
|
'username': 'bob',
|
||||||
|
'project': 'PROJECT-123',
|
||||||
|
'ssh_key_data': EXAMPLE_PRIVATE_KEY
|
||||||
|
})
|
||||||
|
|
||||||
|
assert cred.credential_type.name == 'Google Compute Engine'
|
||||||
|
assert cred.inputs['username'] == 'bob'
|
||||||
|
assert cred.inputs['project'] == 'PROJECT-123'
|
||||||
|
assert cred.inputs['ssh_key_data'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'ssh_key_data') == EXAMPLE_PRIVATE_KEY
|
||||||
|
assert Credential.objects.count() == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_azure_classic_migration():
|
||||||
|
cred = Credential(name='My Credential')
|
||||||
|
with migrate(cred, 'azure'):
|
||||||
|
cred.__dict__.update({
|
||||||
|
'username': 'bob',
|
||||||
|
'ssh_key_data': EXAMPLE_PRIVATE_KEY
|
||||||
|
})
|
||||||
|
|
||||||
|
assert cred.credential_type.name == 'Microsoft Azure Classic (deprecated)'
|
||||||
|
assert cred.inputs['username'] == 'bob'
|
||||||
|
assert cred.inputs['ssh_key_data'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'ssh_key_data') == EXAMPLE_PRIVATE_KEY
|
||||||
|
assert Credential.objects.count() == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_azure_rm_migration():
|
||||||
|
cred = Credential(name='My Credential')
|
||||||
|
with migrate(cred, 'azure_rm'):
|
||||||
|
cred.__dict__.update({
|
||||||
|
'subscription': 'some-subscription',
|
||||||
|
'username': 'bob',
|
||||||
|
'password': 'some-password',
|
||||||
|
'client': 'some-client',
|
||||||
|
'secret': 'some-secret',
|
||||||
|
'tenant': 'some-tenant',
|
||||||
|
})
|
||||||
|
|
||||||
|
assert cred.credential_type.name == 'Microsoft Azure Resource Manager'
|
||||||
|
assert cred.inputs['subscription'] == 'some-subscription'
|
||||||
|
assert cred.inputs['username'] == 'bob'
|
||||||
|
assert cred.inputs['password'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'password') == 'some-password'
|
||||||
|
assert cred.inputs['client'] == 'some-client'
|
||||||
|
assert cred.inputs['secret'].startswith('$encrypted$')
|
||||||
|
assert decrypt_field(cred, 'secret') == 'some-secret'
|
||||||
|
assert cred.inputs['tenant'] == 'some-tenant'
|
||||||
|
assert Credential.objects.count() == 1
|
||||||
Reference in New Issue
Block a user