Merge pull request #7629 from ryanpetrello/k8s-creds

add the ability to specify K8S/OCP credentials on a Job Template

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
softwarefactory-project-zuul[bot] 2020-07-17 17:07:46 +00:00 committed by GitHub
commit 765595fd11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 117 additions and 25 deletions

View File

@ -4100,7 +4100,8 @@ class JobLaunchSerializer(BaseSerializer):
errors.setdefault('credentials', []).append(_(
'Cannot assign multiple {} credentials.'
).format(cred.unique_hash(display=True)))
if cred.credential_type.kind not in ('ssh', 'vault', 'cloud', 'net'):
if cred.credential_type.kind not in ('ssh', 'vault', 'cloud',
'net', 'kubernetes'):
errors.setdefault('credentials', []).append(_(
'Cannot assign a Credential of kind `{}`'
).format(cred.credential_type.kind))

View File

@ -2657,7 +2657,7 @@ class JobTemplateCredentialsList(SubListCreateAttachDetachAPIView):
return {"error": _("Cannot assign multiple {credential_type} credentials.").format(
credential_type=sub.unique_hash(display=True))}
kind = sub.credential_type.kind
if kind not in ('ssh', 'vault', 'cloud', 'net'):
if kind not in ('ssh', 'vault', 'cloud', 'net', 'kubernetes'):
return {'error': _('Cannot assign a Credential of kind `{}`.').format(kind)}
return super(JobTemplateCredentialsList, self).is_valid_relation(parent, sub, created)

View File

@ -101,3 +101,17 @@ def openstack(cred, env, private_data_dir):
f.close()
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
env['OS_CLIENT_CONFIG_FILE'] = path
def kubernetes_bearer_token(cred, env, private_data_dir):
env['K8S_AUTH_HOST'] = cred.get_input('host', default='')
env['K8S_AUTH_API_KEY'] = cred.get_input('bearer_token', default='')
if cred.get_input('verify_ssl') and 'ssl_ca_cert' in cred.inputs:
env['K8S_AUTH_VERIFY_SSL'] = 'True'
handle, path = tempfile.mkstemp(dir=private_data_dir)
with os.fdopen(handle, 'w') as f:
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
f.write(cred.get_input('ssl_ca_cert'))
env['K8S_AUTH_SSL_CA_CERT'] = path
else:
env['K8S_AUTH_VERIFY_SSL'] = 'False'

View File

@ -220,7 +220,7 @@ def test_create_valid_kind(kind, get, post, admin):
@pytest.mark.django_db
@pytest.mark.parametrize('kind', ['ssh', 'vault', 'scm', 'insights'])
@pytest.mark.parametrize('kind', ['ssh', 'vault', 'scm', 'insights', 'kubernetes'])
def test_create_invalid_kind(kind, get, post, admin):
response = post(reverse('api:credential_type_list'), {
'kind': kind,

View File

@ -483,25 +483,26 @@ def test_job_launch_pass_with_prompted_vault_password(machine_credential, vault_
@pytest.mark.django_db
def test_job_launch_JT_with_credentials(machine_credential, credential, net_credential, deploy_jobtemplate):
def test_job_launch_JT_with_credentials(machine_credential, credential, net_credential, kube_credential, deploy_jobtemplate):
deploy_jobtemplate.ask_credential_on_launch = True
deploy_jobtemplate.save()
kv = dict(credentials=[credential.pk, net_credential.pk, machine_credential.pk])
kv = dict(credentials=[credential.pk, net_credential.pk, machine_credential.pk, kube_credential.pk])
serializer = JobLaunchSerializer(data=kv, context={'template': deploy_jobtemplate})
validated = serializer.is_valid()
assert validated, serializer.errors
kv['credentials'] = [credential, net_credential, machine_credential] # convert to internal value
kv['credentials'] = [credential, net_credential, machine_credential, kube_credential] # convert to internal value
prompted_fields, ignored_fields, errors = deploy_jobtemplate._accept_or_ignore_job_kwargs(
_exclude_errors=['required', 'prompts'], **kv)
job_obj = deploy_jobtemplate.create_unified_job(**prompted_fields)
creds = job_obj.credentials.all()
assert len(creds) == 3
assert len(creds) == 4
assert credential in creds
assert net_credential in creds
assert machine_credential in creds
assert kube_credential in creds
@pytest.mark.django_db

View File

@ -1037,6 +1037,43 @@ class TestJobCredentials(TestJobExecution):
assert '--vault-id dev@prompt' in ' '.join(args)
assert '--vault-id prod@prompt' in ' '.join(args)
@pytest.mark.parametrize("verify", (True, False))
def test_k8s_credential(self, job, private_data_dir, verify):
k8s = CredentialType.defaults['kubernetes_bearer_token']()
inputs = {
'host': 'https://example.org/',
'bearer_token': 'token123',
}
if verify:
inputs['verify_ssl'] = True
inputs['ssl_ca_cert'] = 'CERTDATA'
credential = Credential(
pk=1,
credential_type=k8s,
inputs = inputs,
)
credential.inputs['bearer_token'] = encrypt_field(credential, 'bearer_token')
job.credentials.add(credential)
env = {}
safe_env = {}
credential.credential_type.inject_credential(
credential, env, safe_env, [], private_data_dir
)
assert env['K8S_AUTH_HOST'] == 'https://example.org/'
assert env['K8S_AUTH_API_KEY'] == 'token123'
if verify:
assert env['K8S_AUTH_VERIFY_SSL'] == 'True'
cert = open(env['K8S_AUTH_SSL_CA_CERT'], 'r').read()
assert cert == 'CERTDATA'
else:
assert env['K8S_AUTH_VERIFY_SSL'] == 'False'
assert 'K8S_AUTH_SSL_CA_CERT' not in env
assert safe_env['K8S_AUTH_API_KEY'] == tasks.HIDDEN_PASSWORD
def test_aws_cloud_credential(self, job, private_data_dir):
aws = CredentialType.defaults['aws']()
credential = Credential(

View File

@ -67,6 +67,10 @@
&--external:before {
content: '\f14c'
}
&--kubernetes:before, &--kubernetes_bearer_token:before {
content: '\f0c2';
}
}
.TagComponent-button {

View File

@ -111,7 +111,7 @@ function multiCredentialModalController(GetBasePath, qs, MultiCredentialService)
scope.credentialTypes.forEach((credentialType => {
if(credentialType.kind
.match(/^(machine|cloud|net|ssh|vault)$/)) {
.match(/^(machine|cloud|net|ssh|vault|kubernetes)$/)) {
scope.displayedCredentialTypes.push(credentialType);
}
}));

View File

@ -29,6 +29,7 @@
<i class="fa fa-code-fork MultiCredential-tagIcon" ng-switch-when="scm"></i>
<i class="fa fa-key MultiCredential-tagIcon" ng-switch-when="ssh"></i>
<i class="fa fa-archive MultiCredential-tagIcon" ng-switch-when="vault"></i>
<i class="fa fa-cloud MultiCredential-tagIcon" ng-switch-when="kubernetes"></i>
</div>
<div class="MultiCredential-tag MultiCredential-tag--deletable">
<span ng-if="!tag.info" class="MultiCredential-name--label ng-binding">

View File

@ -26,6 +26,7 @@
<i class="fa fa-code-fork MultiCredential-tagIcon" ng-switch-when="scm"></i>
<i class="fa fa-key MultiCredential-tagIcon" ng-switch-when="ssh"></i>
<i class="fa fa-archive MultiCredential-tagIcon" ng-switch-when="vault"></i>
<i class="fa fa-cloud MultiCredential-tagIcon" ng-switch-when="kubernetes"></i>
</div>
<div class="MultiCredential-iconContainer" ng-switch="tag.kind" ng-if="!fieldIsDisabled">
<i class="fa fa-cloud MultiCredential-tagIcon" ng-switch-when="cloud"></i>
@ -34,6 +35,7 @@
<i class="fa fa-code-fork MultiCredential-tagIcon" ng-switch-when="scm"></i>
<i class="fa fa-key MultiCredential-tagIcon" ng-switch-when="ssh"></i>
<i class="fa fa-archive MultiCredential-tagIcon" ng-switch-when="vault"></i>
<i class="fa fa-cloud MultiCredential-tagIcon" ng-switch-when="kubernetes"></i>
</div>
<div class="MultiCredential-tag"
ng-class="{'MultiCredential-tag--deletable': !fieldIsDisabled, 'MultiCredential-tag--disabled': fieldIsDisabled}">

View File

@ -55,7 +55,7 @@ export default [ 'ProcessErrors', 'CredentialTypeModel', 'TemplatesStrings', '$f
vm.promptDataClone.prompts.credentials.credentialTypeOptions = [];
response.data.results.forEach((credentialTypeRow => {
vm.promptDataClone.prompts.credentials.credentialTypes[credentialTypeRow.id] = credentialTypeRow.kind;
if(credentialTypeRow.kind.match(/^(cloud|net|ssh|vault)$/)) {
if(credentialTypeRow.kind.match(/^(cloud|net|ssh|vault|kubernetes)$/)) {
if(credentialTypeRow.kind === 'ssh') {
vm.promptDataClone.prompts.credentials.credentialKind = credentialTypeRow.id.toString();
}

View File

@ -7,7 +7,7 @@ class CredentialTypes extends Base {
}
async loadAllTypes(
acceptableKinds = ['machine', 'cloud', 'net', 'ssh', 'vault']
acceptableKinds = ['machine', 'cloud', 'net', 'ssh', 'vault', 'kubernetes']
) {
const pageSize = 200;
// The number of credential types a user can have is unlimited. In practice, it is unlikely for

View File

@ -0,0 +1,23 @@
The MIT License (MIT)
Copyright (c) 2007-2017 Anthon van der Neut/Ruamel BVBA
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -32,8 +32,8 @@ django-oauth-toolkit==1.1.3 # via -r /awx_devel/requirements/requirements.in
django-pglocks==1.0.4 # via -r /awx_devel/requirements/requirements.in
django-polymorphic==2.1.2 # via -r /awx_devel/requirements/requirements.in
django-qsstats-magic==1.1.0 # via -r /awx_devel/requirements/requirements.in
django-redis==4.5.0
django-radius==1.3.3 # via -r /awx_devel/requirements/requirements.in
django-redis==4.5.0 # via -r /awx_devel/requirements/requirements.in
django-solo==1.1.3 # via -r /awx_devel/requirements/requirements.in
django-split-settings==1.0.0 # via -r /awx_devel/requirements/requirements.in
django-taggit==1.2.0 # via -r /awx_devel/requirements/requirements.in
@ -100,7 +100,7 @@ python3-openid==3.1.0 # via social-auth-core
python3-saml==1.9.0 # via -r /awx_devel/requirements/requirements.in
pytz==2019.3 # via django, irc, tempora, twilio
pyyaml==5.3.1 # via -r /awx_devel/requirements/requirements.in, ansible-runner, djangorestframework-yaml, kubernetes
redis==3.4.1 # via -r /awx_devel/requirements/requirements.in
redis==3.4.1 # via -r /awx_devel/requirements/requirements.in, django-redis
requests-oauthlib==1.3.0 # via kubernetes, msrest, social-auth-core
requests==2.23.0 # via -r /awx_devel/requirements/requirements.in, adal, azure-keyvault, django-oauth-toolkit, kubernetes, msrest, requests-oauthlib, slackclient, social-auth-core, twilio
rsa==4.0 # via google-auth

View File

@ -62,5 +62,7 @@ requests
requests-credssp==1.0.2 # For windows authentication awx/issues/1144
# OpenStack
openstacksdk==0.37.0
# Openshift/k8s
openshift>=0.11.0 # minimum version to pull in new pyyaml for CVE-2017-18342
pip==19.3.1 # see upgrade blockers
setuptools==41.6.0 # see upgrade blockers
setuptools==41.6.0 # see upgrade blockers

View File

@ -26,7 +26,7 @@ azure-mgmt-loganalytics==0.2.0 # via -r /awx_devel/requirements/requirements_an
azure-mgmt-marketplaceordering==0.1.0 # via -r /awx_devel/requirements/requirements_ansible.in
azure-mgmt-monitor==0.5.2 # via -r /awx_devel/requirements/requirements_ansible.in
azure-mgmt-network==2.3.0 # via -r /awx_devel/requirements/requirements_ansible.in
azure-mgmt-nspkg==2.0.0 # via -r /awx_devel/requirements/requirements_ansible.in, azure-mgmt-authorization, azure-mgmt-automation, azure-mgmt-batch, azure-mgmt-cdn, azure-mgmt-compute, azure-mgmt-containerinstance, azure-mgmt-containerregistry, azure-mgmt-containerservice, azure-mgmt-cosmosdb, azure-mgmt-devtestlabs, azure-mgmt-dns, azure-mgmt-hdinsight, azure-mgmt-iothub, azure-mgmt-keyvault, azure-mgmt-loganalytics, azure-mgmt-marketplaceordering, azure-mgmt-monitor, azure-mgmt-network, azure-mgmt-rdbms, azure-mgmt-redis, azure-mgmt-resource, azure-mgmt-servicebus, azure-mgmt-sql, azure-mgmt-storage, azure-mgmt-trafficmanager, azure-mgmt-web
azure-mgmt-nspkg==2.0.0; python_version < "3" # via -r /awx_devel/requirements/requirements_ansible.in, azure-mgmt-authorization, azure-mgmt-automation, azure-mgmt-batch, azure-mgmt-cdn, azure-mgmt-compute, azure-mgmt-containerinstance, azure-mgmt-containerregistry, azure-mgmt-containerservice, azure-mgmt-cosmosdb, azure-mgmt-devtestlabs, azure-mgmt-dns, azure-mgmt-hdinsight, azure-mgmt-iothub, azure-mgmt-keyvault, azure-mgmt-loganalytics, azure-mgmt-marketplaceordering, azure-mgmt-monitor, azure-mgmt-network, azure-mgmt-rdbms, azure-mgmt-redis, azure-mgmt-resource, azure-mgmt-servicebus, azure-mgmt-sql, azure-mgmt-storage, azure-mgmt-trafficmanager, azure-mgmt-web
azure-mgmt-rdbms==1.4.1 # via -r /awx_devel/requirements/requirements_ansible.in
azure-mgmt-redis==5.0.0 # via -r /awx_devel/requirements/requirements_ansible.in
azure-mgmt-resource==2.1.0 # via -r /awx_devel/requirements/requirements_ansible.in
@ -43,7 +43,7 @@ boto3==1.9.223 # via -r /awx_devel/requirements/requirements_ansible.
boto==2.47.0 # via -r /awx_devel/requirements/requirements_ansible.in
botocore==1.12.253 # via boto3, s3transfer
cachetools==3.1.1 # via google-auth
certifi==2019.11.28 # via msrest, requests
certifi==2019.11.28 # via kubernetes, msrest, requests
cffi==1.13.2 # via bcrypt, cryptography, pynacl
chardet==3.0.4 # via requests
colorama==0.4.3 # via azure-cli-core, knack
@ -53,18 +53,19 @@ docutils==0.15.2 # via botocore
dogpile.cache==0.9.0 # via openstacksdk
enum34==1.1.6; python_version < "3" # via cryptography, knack, msrest, ovirt-engine-sdk-python
futures==3.3.0; python_version < "3" # via openstacksdk, s3transfer
google-auth==1.6.2 # via -r /awx_devel/requirements/requirements_ansible.in
google-auth==1.6.2 # via -r /awx_devel/requirements/requirements_ansible.in, kubernetes
humanfriendly==4.18 # via azure-cli-core
idna==2.8 # via requests
ipaddress==1.0.23; python_version < "3" # via cryptography, openstacksdk
ipaddress==1.0.23; python_version < "3" # via cryptography, kubernetes, openstacksdk
iso8601==0.1.12 # via keystoneauth1, openstacksdk
isodate==0.6.0 # via msrest
jinja2==2.10.1 # via -r /awx_devel/requirements/requirements_ansible.in
jinja2==2.10.1 # via -r /awx_devel/requirements/requirements_ansible.in, openshift
jmespath==0.9.4 # via azure-cli-core, boto3, botocore, knack, openstacksdk
jsonpatch==1.24 # via openstacksdk
jsonpointer==2.0 # via jsonpatch
keystoneauth1==3.18.0 # via openstacksdk
knack==0.3.3 # via azure-cli-core
kubernetes==11.0.0 # via openshift
lxml==4.4.2 # via ncclient
markupsafe==1.1.1 # via jinja2
monotonic==1.5; python_version < "3" # via humanfriendly
@ -76,6 +77,7 @@ netaddr==0.7.19 # via -r /awx_devel/requirements/requirements_ansible.
netifaces==0.10.9 # via openstacksdk
ntlm-auth==1.4.0 # via requests-credssp, requests-ntlm
oauthlib==3.1.0 # via requests-oauthlib
openshift==0.11.2 # via -r /awx_devel/requirements/requirements_ansible.in
openstacksdk==0.37.0 # via -r /awx_devel/requirements/requirements_ansible.in
os-service-types==1.7.0 # via keystoneauth1, openstacksdk
ovirt-engine-sdk-python==4.3.0 # via -r /awx_devel/requirements/requirements_ansible.in
@ -93,27 +95,32 @@ pykerberos==1.2.1 # via requests-kerberos
pynacl==1.3.0 # via paramiko
pyopenssl==19.1.0 # via azure-cli-core, requests-credssp
pyparsing==2.4.5 # via packaging
python-dateutil==2.8.1 # via adal, azure-storage, botocore
python-dateutil==2.8.1 # via adal, azure-storage, botocore, kubernetes
python-string-utils==0.6.0; python_version < "3" # via openshift
pyvmomi==6.7.3 # via -r /awx_devel/requirements/requirements_ansible.in
pywinrm[kerberos]==0.3.0 # via -r /awx_devel/requirements/requirements_ansible.in
pyyaml==5.2 # via azure-cli-core, knack, openstacksdk
pyyaml==5.2 # via azure-cli-core, knack, kubernetes, openstacksdk
requests-credssp==1.0.2 # via -r /awx_devel/requirements/requirements_ansible.in
requests-kerberos==0.12.0 # via pywinrm
requests-ntlm==1.1.0 # via pywinrm
requests-oauthlib==1.3.0 # via msrest
requests==2.22.0 # via -r /awx_devel/requirements/requirements_ansible.in, adal, apache-libcloud, azure-cli-core, azure-keyvault, azure-storage, keystoneauth1, msrest, pyvmomi, pywinrm, requests-credssp, requests-kerberos, requests-ntlm, requests-oauthlib
requests-oauthlib==1.3.0 # via kubernetes, msrest
requests==2.22.0 # via -r /awx_devel/requirements/requirements_ansible.in, adal, apache-libcloud, azure-cli-core, azure-keyvault, azure-storage, keystoneauth1, kubernetes, msrest, pyvmomi, pywinrm, requests-credssp, requests-kerberos, requests-ntlm, requests-oauthlib
requestsexceptions==1.4.0 # via openstacksdk
rsa==4.0 # via google-auth
ruamel.ordereddict==0.4.14; python_version < "3" # via ruamel.yaml
ruamel.yaml.clib==0.2.0 # via ruamel.yaml
ruamel.yaml==0.16.10 # via openshift
s3transfer==0.2.1 # via boto3
selectors2==2.0.1 # via ncclient
six==1.13.0 # via azure-cli-core, bcrypt, cryptography, google-auth, isodate, keystoneauth1, knack, munch, ncclient, openstacksdk, ovirt-engine-sdk-python, packaging, pynacl, pyopenssl, python-dateutil, pyvmomi, pywinrm, requests-credssp, stevedore
six==1.13.0 # via azure-cli-core, bcrypt, cryptography, google-auth, isodate, keystoneauth1, knack, kubernetes, munch, ncclient, openshift, openstacksdk, ovirt-engine-sdk-python, packaging, pynacl, pyopenssl, python-dateutil, pyvmomi, pywinrm, requests-credssp, stevedore, websocket-client
stevedore==1.31.0 # via keystoneauth1
tabulate==0.8.2 # via azure-cli-core, knack
typing==3.7.4.1; python_version < "3" # via msrest
urllib3==1.25.7 # via botocore, requests
urllib3==1.25.7 # via botocore, kubernetes, requests
websocket-client==0.57.0 # via kubernetes
wheel==0.33.6 # via azure-cli-core (overriden, see upgrade blockers)
xmltodict==0.12.0 # via pywinrm
# The following packages are considered to be unsafe in a requirements file:
pip==19.3.1 # via -r /awx_devel/requirements/requirements_ansible.in, azure-cli-core
setuptools==41.6.0 # via -r /awx_devel/requirements/requirements_ansible.in, ncclient
setuptools==41.6.0 # via -r /awx_devel/requirements/requirements_ansible.in, kubernetes, ncclient