Skip to content

Commit

Permalink
Fixing e2e charm to work with token auth (kubernetes#129)
Browse files Browse the repository at this point in the history
* Fixing e2e charm to work with token auth

* Add support for e2e requesting elevated privs

Refactors e2e to use token auth, and to request the proper auth level
with that token.
  • Loading branch information
Charles Butler authored and ktsakalozos committed May 23, 2017
1 parent dc8ea60 commit 83ef377
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 20 deletions.
1 change: 1 addition & 0 deletions cluster/juju/layers/kubernetes-e2e/layer.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ includes:
- layer:basic
- layer:tls-client
- layer:snap
- interface:kube-control
- interface:http
options:
tls-client:
Expand Down
2 changes: 2 additions & 0 deletions cluster/juju/layers/kubernetes-e2e/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ series:
requires:
kubernetes-master:
interface: http
kube-control:
interface: kube-control
resources:
kubectl:
type: file
Expand Down
75 changes: 59 additions & 16 deletions cluster/juju/layers/kubernetes-e2e/reactive/kubernetes_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@

from subprocess import check_call
from subprocess import check_output
from socket import gethostname

import random


@hook('upgrade-charm')
Expand Down Expand Up @@ -78,29 +81,36 @@ def install_snaps():
set_state('kubernetes-e2e.installed')


@when('tls_client.ca.saved', 'tls_client.client.certificate.saved',
'tls_client.client.key.saved', 'kubernetes-master.available',
'kubernetes-e2e.installed')
@when('kube-control.connected')
def request_kubelet_and_proxy_credentials(kube_control):
""" Request kubelet node authorization with a well formed kubelet user.
This also implies that we are requesting kube-proxy auth. """

nodeuser = 'e2e-'.format(gethostname())
kube_control.set_auth_request(nodeuser, group='system:masters')


@when('tls_client.ca_installed', 'kubernetes-master.available',
'kubernetes-e2e.installed', 'kube-control.auth.available')
@when_not('kubeconfig.ready')
def prepare_kubeconfig_certificates(master):
def prepare_kubeconfig_certificates(master, kube_control):
''' Prepare the data to feed to create the kubeconfig file. '''

layer_options = layer.options('tls-client')
# Get all the paths to the tls information required for kubeconfig.
ca = layer_options.get('ca_certificate_path')
key = layer_options.get('client_key_path')
cert = layer_options.get('client_certificate_path')
creds = kube_control.get_auth_credentials()

servers = get_kube_api_servers(master)
server = random.choice(get_kube_api_servers(master))

# pedantry
kubeconfig_path = '/home/ubuntu/.kube/config'

# Create kubernetes configuration in the default location for ubuntu.
create_kubeconfig('/root/.kube/config', servers[0], ca, key, cert,
user='root')
create_kubeconfig(kubeconfig_path, servers[0], ca, key, cert,
user='ubuntu')
create_kubeconfig('/root/.kube/config', server, ca=ca,
token=creds['kubelet_token'], user='root')
create_kubeconfig(kubeconfig_path, server, ca=ca,
token=creds['kubelet_token'], user='root')
# Set permissions on the ubuntu users kubeconfig to ensure a consistent UX
cmd = ['chown', 'ubuntu:ubuntu', kubeconfig_path]
check_call(cmd)
Expand All @@ -124,19 +134,52 @@ def set_app_version():
hookenv.application_version_set(version_from.rstrip())


def create_kubeconfig(kubeconfig, server, ca, key, certificate, user='ubuntu',
context='juju-context', cluster='juju-cluster'):
@when_not('kube-control.connected')
def missing_kube_control():
"""Inform the operator they need to add the kube-control relation.
If deploying via bundle this won't happen, but if operator is upgrading a
a charm in a deployment that pre-dates the kube-control relation, it'll be
missing.
"""
hookenv.status_set(
'blocked',
'Relate {}:kube-control kubernetes-master:kube-control'.format(
hookenv.service_name()))


def create_kubeconfig(kubeconfig, server, ca, key=None, certificate=None,
user='ubuntu', context='juju-context',
cluster='juju-cluster', password=None, token=None):
'''Create a configuration for Kubernetes based on path using the supplied
arguments for values of the Kubernetes server, CA, key, certificate, user
context and cluster.'''
if not key and not certificate and not password and not token:
raise ValueError('Missing authentication mechanism.')

# token and password are mutually exclusive. Error early if both are
# present. The developer has requested an impossible situation.
# see: kubectl config set-credentials --help
if token and password:
raise ValueError('Token and Password are mutually exclusive.')
# Create the config file with the address of the master server.
cmd = 'kubectl config --kubeconfig={0} set-cluster {1} ' \
'--server={2} --certificate-authority={3} --embed-certs=true'
check_call(split(cmd.format(kubeconfig, cluster, server, ca)))
# Create the credentials using the client flags.
cmd = 'kubectl config --kubeconfig={0} set-credentials {1} ' \
'--client-key={2} --client-certificate={3} --embed-certs=true'
check_call(split(cmd.format(kubeconfig, user, key, certificate)))
cmd = 'kubectl config --kubeconfig={0} ' \
'set-credentials {1} '.format(kubeconfig, user)

if key and certificate:
cmd = '{0} --client-key={1} --client-certificate={2} '\
'--embed-certs=true'.format(cmd, key, certificate)
if password:
cmd = "{0} --username={1} --password={1}".format(cmd, user, password)
# This is mutually exclusive from password. They will not work together.
if token:
cmd = "{0} --token={1}".format(cmd, token)
check_call(split(cmd))
# Create a default context with the cluster.
cmd = 'kubectl config --kubeconfig={0} set-context {1} ' \
'--cluster={2} --user={3}'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,11 +352,11 @@ def send_cluster_dns_detail(kube_control):
def create_service_configs(kube_control):
"""Create the users for kubelet and kube-proxy """
# generate the username/pass for the requesting unit
userid, user = kube_control.auth_user()
kubelet_token = get_token(user)
userid, usermap = kube_control.auth_user()
kubelet_token = get_token(usermap['user'])
if not kubelet_token:
kubelet_token = token_generator(64, user)
setup_tokens(kubelet_token, user, userid, "system:nodes")
kubelet_token = token_generator(64, usermap['user'])
setup_tokens(kubelet_token, usermap['user'], userid, usermap['group'])

proxy_token = get_token('kube-proxy')
if not proxy_token:
Expand Down

0 comments on commit 83ef377

Please sign in to comment.