Downloadable Kubernetes configuration file

Kubernetes can use OpenStack application credentials for authentication.
Generate a kubeconfig file and make it available to download along with the
openrc and clouds.yaml files.

blueprint kubernetes-config-gen

Change-Id: I298370e7abf4f0d480bd5199060f24bab6d6daaa
This commit is contained in:
Claudio Pisa 2019-05-29 12:04:16 +00:00 committed by Ivan Kolodyazhny
parent a7a04da657
commit f381f4dd3c
9 changed files with 149 additions and 7 deletions

View File

@ -7,7 +7,7 @@ Settings Reference
Introduction Introduction
============ ============
Horizon's settings broadly fall into three categories: Horizon's settings broadly fall into four categories:
* `General Settings`_: this includes visual settings like the modal backdrop * `General Settings`_: this includes visual settings like the modal backdrop
style, bug url and theme configuration, as well as settings that affect every style, bug url and theme configuration, as well as settings that affect every
@ -21,6 +21,8 @@ Horizon's settings broadly fall into three categories:
should read the `Django settings documentation should read the `Django settings documentation
<https://docs.djangoproject.com/en/dev/topics/settings/>`_ to see the other <https://docs.djangoproject.com/en/dev/topics/settings/>`_ to see the other
options available to you. options available to you.
* `Other Settings`_: settings which do not fall into any of the above
categories.
To modify your settings, you have two options: To modify your settings, you have two options:
@ -2521,3 +2523,51 @@ Default: Absolute paths for `horizon/locale`, `openstack_auth/locale` and
Django uses relative paths by default so it causes localization issues Django uses relative paths by default so it causes localization issues
depending on your runtime settings. To avoid this we recommend to use absolute depending on your runtime settings. To avoid this we recommend to use absolute
paths for directories with locales. paths for directories with locales.
Other Settings
==============
KUBECONFIG_ENABLED
------------------
.. versionadded:: TBD
Default: ``False``
Kubernetes clusters can use Keystone as an external identity provider.
Horizon can generate a ``kubeconfig`` file from the application credentials
control panel which can be used for authenticating with a Kubernetes cluster.
This setting enables this behavior.
.. seealso::
`KUBECONFIG_KUBERNETES_URL`_ and `KUBECONFIG_CERTIFICATE_AUTHORITY_DATA`_
to provide parameters for the ``kubeconfig`` file.
KUBECONFIG_KUBERNETES_URL
-------------------------
.. versionadded:: TBD
Default: ``""``
A Kubernetes API endpoint URL to be included in the generated ``kubeconfig``
file.
.. seealso::
`KUBECONFIG_ENABLED`_ to enable the ``kubeconfig`` file generation.
KUBECONFIG_CERTIFICATE_AUTHORITY_DATA
-------------------------------------
.. versionadded:: TBD
Default: ``""``
Kubernetes API endpoint certificate authority data to be included in the
generated ``kubeconfig`` file.
.. seealso::
`KUBECONFIG_ENABLED`_ to enable the ``kubeconfig`` file generation.

View File

@ -15,6 +15,8 @@
import datetime import datetime
import logging import logging
from django.conf import settings
from django.forms import widgets
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.decorators.debug import sensitive_variables from django.views.decorators.debug import sensitive_variables
@ -49,6 +51,10 @@ class CreateApplicationCredentialForm(forms.SelfHandlingForm):
required=False) required=False)
unrestricted = forms.BooleanField(label=_("Unrestricted (dangerous)"), unrestricted = forms.BooleanField(label=_("Unrestricted (dangerous)"),
required=False) required=False)
kubernetes_namespace = forms.CharField(max_length=255,
label=_("Kubernetes Namespace"),
initial="default",
required=False)
def __init__(self, request, *args, **kwargs): def __init__(self, request, *args, **kwargs):
self.next_view = kwargs.pop('next_view', None) self.next_view = kwargs.pop('next_view', None)
@ -58,6 +64,8 @@ class CreateApplicationCredentialForm(forms.SelfHandlingForm):
role_names = [role['name'] for role in role_list] role_names = [role['name'] for role in role_list]
role_choices = ((name, name) for name in role_names) role_choices = ((name, name) for name in role_names)
self.fields['roles'].choices = role_choices self.fields['roles'].choices = role_choices
if not settings.KUBECONFIG_ENABLED:
self.fields['kubernetes_namespace'].widget = widgets.HiddenInput()
# We have to protect the entire "data" dict because it contains the # We have to protect the entire "data" dict because it contains the
# secret string. # secret string.
@ -98,6 +106,8 @@ class CreateApplicationCredentialForm(forms.SelfHandlingForm):
) )
self.request.session['application_credential'] = \ self.request.session['application_credential'] = \
new_app_cred.to_dict() new_app_cred.to_dict()
(self.request.session['application_credential']
['kubernetes_namespace']) = data['kubernetes_namespace']
request.method = 'GET' request.method = 'GET'
return self.next_view.as_view()(request) return self.next_view.as_view()(request)
except exceptions.Conflict: except exceptions.Conflict:

View File

@ -40,4 +40,12 @@
actions, check "unrestricted". actions, check "unrestricted".
{% endblocktrans %} {% endblocktrans %}
</p> </p>
<p>
{% if kubeconfig_enabled %}
{% blocktrans trimmed %}
You can optionally provide a Kubernetes Namespace. It will be included in the
kubeconfig file which can be downloaded from the next screen.
{% endblocktrans %}
{% endif %}
</p>
{% endblock %} {% endblock %}

View File

@ -31,5 +31,11 @@
<span class="fa fa-download"></span> <span class="fa fa-download"></span>
{{ download_clouds_yaml_label }} {{ download_clouds_yaml_label }}
</a> </a>
{% if download_kubeconfig_url %}
<a href="{{ download_kubeconfig_url }}" class="btn btn-default">
<span class="fa fa-download"></span>
{{ download_kubeconfig_label }}
</a>
{% endif %}
<a onClick="location.href='{{cancel_url}}'" href="{{ cancel_url }}" class="btn btn-default">{{ cancel_label }}</a> <a onClick="location.href='{{cancel_url}}'" href="{{ cancel_url }}" class="btn btn-default">{{ cancel_label }}</a>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,26 @@
apiVersion: v1
kind: Config
clusters:
- name: kubernetes
cluster:
server: {{ kubernetes_url }}
certificate-authority-data: {{ kubernetes_certificate_authority_data }}
contexts:
- name: kubernetes
context:
cluster: kubernetes
user: {{ user }}
namespace: {{ kubernetes_namespace }}
current-context: kubernetes
users:
- name: {{ user }}
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
command: bin/kubectl-keystone-auth
args:
- "--keystone-url={{ auth_url }}
- "--domain-name=none"
- "--user-name={{ user }}"
- "--application-credential-id={{ application_credential_id }}"
- "--application-credential-secret={{ application_credential_secret }}"

View File

@ -28,6 +28,8 @@ urlpatterns = [
views.CreateSuccessfulView.as_view(), name='success'), views.CreateSuccessfulView.as_view(), name='success'),
url(r'^download_openrc/$', url(r'^download_openrc/$',
views.download_rc_file, name='download_openrc'), views.download_rc_file, name='download_openrc'),
url(r'^download_kubeconfig/$',
views.download_kubeconfig_file, name='download_kubeconfig'),
url(r'^download_clouds_yaml/$', url(r'^download_clouds_yaml/$',
views.download_clouds_yaml_file, name='download_clouds_yaml'), views.download_clouds_yaml_file, name='download_clouds_yaml'),
] ]

View File

@ -86,6 +86,11 @@ class CreateView(forms.ModalFormView):
kwargs['next_view'] = CreateSuccessfulView kwargs['next_view'] = CreateSuccessfulView
return kwargs return kwargs
def get_context_data(self, **kwargs):
context = super(CreateView, self).get_context_data(**kwargs)
context['kubeconfig_enabled'] = settings.KUBECONFIG_ENABLED
return context
class CreateSuccessfulView(forms.ModalFormView): class CreateSuccessfulView(forms.ModalFormView):
template_name = 'identity/application_credentials/success.html' template_name = 'identity/application_credentials/success.html'
@ -97,15 +102,20 @@ class CreateSuccessfulView(forms.ModalFormView):
cancel_label = _("Close") cancel_label = _("Close")
download_openrc_label = _("Download openrc file") download_openrc_label = _("Download openrc file")
download_clouds_yaml_label = _("Download clouds.yaml") download_clouds_yaml_label = _("Download clouds.yaml")
download_kubeconfig_label = _("Download kubeconfig file")
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(CreateSuccessfulView, self).get_context_data(**kwargs) context = super(CreateSuccessfulView, self).get_context_data(**kwargs)
context['download_openrc_label'] = self.download_openrc_label context['download_openrc_label'] = self.download_openrc_label
context['download_clouds_yaml_label'] = self.download_clouds_yaml_label context['download_clouds_yaml_label'] = self.download_clouds_yaml_label
context['download_kubeconfig_label'] = self.download_kubeconfig_label
context['download_openrc_url'] = reverse( context['download_openrc_url'] = reverse(
'horizon:identity:application_credentials:download_openrc') 'horizon:identity:application_credentials:download_openrc')
context['download_clouds_yaml_url'] = reverse( context['download_clouds_yaml_url'] = reverse(
'horizon:identity:application_credentials:download_clouds_yaml') 'horizon:identity:application_credentials:download_clouds_yaml')
if settings.KUBECONFIG_ENABLED:
context['download_kubeconfig_url'] = reverse(
'horizon:identity:application_credentials:download_kubeconfig')
return context return context
def get_initial(self): def get_initial(self):
@ -125,12 +135,18 @@ def _get_context(request):
interface = 'public' interface = 'public'
region = getattr(request.user, 'services_region', '') region = getattr(request.user, 'services_region', '')
app_cred = request.session['application_credential'] app_cred = request.session['application_credential']
context = dict(auth_url=auth_url, context = {
interface=interface, 'auth_url': auth_url,
region=region, 'interface': interface,
application_credential_id=app_cred['id'], 'region': region,
application_credential_name=app_cred['name'], 'user': request.user,
application_credential_secret=app_cred['secret']) 'application_credential_id': app_cred['id'],
'application_credential_name': app_cred['name'],
'application_credential_secret': app_cred['secret'],
'kubernetes_namespace': app_cred['kubernetes_namespace'],
'kubernetes_url': settings.KUBECONFIG_KUBERNETES_URL,
'kubernetes_certificate_authority_data':
settings.KUBECONFIG_CERTIFICATE_AUTHORITY_DATA}
return context return context
@ -166,6 +182,14 @@ def download_clouds_yaml_file(request):
return _render_attachment(filename, template, context, request) return _render_attachment(filename, template, context, request)
def download_kubeconfig_file(request):
context = _get_context(request)
template = 'identity/application_credentials/kubeconfig.template'
filename = 'app-cred-%s-kubeconfig' % context['application_credential_name']
response = _render_attachment(filename, template, context, request)
return response
class DetailView(views.HorizonTemplateView): class DetailView(views.HorizonTemplateView):
template_name = 'identity/application_credentials/detail.html' template_name = 'identity/application_credentials/detail.html'
page_title = "{{ application_credential.name }}" page_title = "{{ application_credential.name }}"

View File

@ -374,3 +374,11 @@ REST_API_REQUIRED_SETTINGS = [
# and are not encrypted on the browser. This is an experimental API and # and are not encrypted on the browser. This is an experimental API and
# may be deprecated in the future without notice. # may be deprecated in the future without notice.
REST_API_ADDITIONAL_SETTINGS = [] REST_API_ADDITIONAL_SETTINGS = []
# Kubernetes clusters can use Keystone as an external identity provider.
# Horizon can generate a 'kubeconfig' file from the application credentials
# control panel which can be used for authenticating with a Kubernetes cluster.
# These settings control the kubeconfig parameters.
KUBECONFIG_ENABLED = False
KUBECONFIG_KUBERNETES_URL = ""
KUBECONFIG_CERTIFICATE_AUTHORITY_DATA = ""

View File

@ -0,0 +1,8 @@
---
features:
- |
[`blueprint kubernetes-config-gen <https://blueprints.launchpad.net/horizon/+spec/kubernetes-config-gen>`_]
Horizon now supports the optional automatic generation of a Kubernetes
configuration file (kubeconfig) based on application credentials. Adds
a new download button for this purpose in the application credentials
creation dialog.