kubernetes/cluster/juju/charms/trusty/kubernetes/hooks/hooks.py
Matt Bruzek 7a387a87a1 Changes necessary to enable ssl for the Juju charms
Squashed commit of the following:

commit dedaccffdc1d797b5f23e0004280c2fcc0ecffa9
Merge: 24c3585 f03a267
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Tue Sep 15 17:07:42 2015 -0500

    Merge branch 'master' of github.com:kubernetes/kubernetes into enable-ssl

commit 24c358566cc0963fb86dc057db15739f031ba6f6
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Tue Sep 15 16:44:58 2015 -0500

    Fixing problems with verify-boilerplate.

commit a64443352c64498255ac98fc0da1a7d8d5934485
Merge: f152794 ee3f662
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Tue Sep 15 15:43:08 2015 -0500

    Merge branch 'enable-ssl' of github.com:mbruzek/kubernetes into enable-ssl

    Conflicts:
    	cluster/juju/util.sh

commit f152794502c017ae7d3cff0351d8bf44b7311883
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 14:12:21 2015 -0500

    Fixes for problems found in testing.

commit 94effa4827d5f30c60621e9133c4526c187e40b4
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 12:34:37 2015 -0500

    Making updates for changes in master branch.

commit a81795b44e57d54b8b4ae486ca6ea8164ac8fc6b
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 11:39:36 2015 -0500

    pep8 fix

commit 53a862caea02c4b35f8cd19b1549fda29e040f12
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 11:37:31 2015 -0500

    Adding diagnostic log messages and reloading nginx.

commit 96411a924fb05e2e58534cce94d9a1e51d7db9af
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 11:19:31 2015 -0500

    Making the check user logic cleaner.

commit a0243b34cdda2f865e559bd4812c5a78ab6f6f05
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 11:18:04 2015 -0500

    Open port 6443 for ssl enablement.

commit e8423614763aa6d650089c735c3dc1893bf73993
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 11:16:54 2015 -0500

    Generating certificates and adding config options.

commit 6570a818e252f2cb156a577094ba987dec834fe1
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 11:14:56 2015 -0500

    Removing the http configuration adding https config.

commit e624bd79f8840b71b141a111bca7c6091b677575
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 11:13:04 2015 -0500

    Changed the distribution nginx config slightly.

commit c497911170268ee75bed53afeb5fa32ff44c82af
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 10:57:17 2015 -0500

    Adding the crt and key to the apiserver flags.

commit 6c1e76c5de31eb4d0f800065ce4bc96a82801846
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 10:56:15 2015 -0500

    Adding the cert and key configuration parameters.

commit 55da910d473efa0be0af5efccf2336612525986e
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 10:49:46 2015 -0500

    Adding a requirements file to install path.py with pip -r

commit 27a39686af89e134268be50ce5e4fc36cffdf2b3
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Fri Aug 21 16:34:56 2015 -0500

    Making the install hook idempotent.

commit ee3f66287ba045592f932c3a41aeb8e0167a9235
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 14:12:21 2015 -0500

    Fixes for problems found in testing.

commit 3dfdbb0e21d79da66617f7a29e6dd8d977528057
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 12:34:37 2015 -0500

    Making updates for changes in master branch.

commit df9c297fe27c63713fc0d7dbd461b2d47133614c
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 11:39:36 2015 -0500

    pep8 fix

commit 645b25d9cc84555ca7af5c75e3f0b1300aaa9f48
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 11:37:31 2015 -0500

    Adding diagnostic log messages and reloading nginx.

commit 57654320bd73dc4dd52d6d56021d40a97c6ed893
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 11:37:02 2015 -0500

    Removing xtrace.

commit a0e8cd98353e7cd411786bc8836fe99a77cde3ba
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 11:19:31 2015 -0500

    Making the check user logic cleaner.

commit 6c6eb7ff02d6dbf66d3175b715e957b00a861525
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 11:18:04 2015 -0500

    Open port 6443 for ssl enablement.

commit 29f18cc95ff96de1a48f72af2cff2e37420a43c7
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 11:16:54 2015 -0500

    Generating certificates and adding config options.

commit c9bdaa499552980153ff263a1ab9b4fa73a0536a
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 11:14:56 2015 -0500

    Removing the http configuration adding https config.

commit ec33e66a159a4b44207353b16741c7fad2e4ee0d
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 11:13:04 2015 -0500

    Changed the distribution nginx config slightly.

commit 96dc16879c0dd230569ceb928f9f7bf92ff8ab3f
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 10:57:17 2015 -0500

    Adding the crt and key to the apiserver flags.

commit 308799502c0a22f214408395f5efa4821d075374
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 10:56:15 2015 -0500

    Adding the cert and key configuration parameters.

commit 7f407a4356de8703ff2f237432084f35910d8abd
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Wed Sep 9 10:49:46 2015 -0500

    Adding a requirements file to install path.py with pip -r

commit f800ae1152076758d4db203fdbecf6d945c0e892
Author: Matt Bruzek <matthew.bruzek@canonical.com>
Date:   Fri Aug 21 16:34:56 2015 -0500

    Making the install hook idempotent.

Resolving verification problems.
2015-09-17 15:23:02 -05:00

240 lines
7.9 KiB
Python
Executable File

#!/usr/bin/env python
# Copyright 2015 The Kubernetes Authors All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
The main hook file that is called by Juju.
"""
import os
import socket
import subprocess
import sys
import urlparse
from charmhelpers.core import hookenv, host
from kubernetes_installer import KubernetesInstaller
from path import Path
from lib.registrator import Registrator
hooks = hookenv.Hooks()
@hooks.hook('api-relation-changed')
def api_relation_changed():
"""
On the relation to the api server, this function determines the appropriate
architecture and the configured version to copy the kubernetes binary files
from the kubernetes-master charm and installs it locally on this machine.
"""
hookenv.log('Starting api-relation-changed')
charm_dir = Path(hookenv.charm_dir())
# Get the package architecture, rather than the from the kernel (uname -m).
arch = subprocess.check_output(['dpkg', '--print-architecture']).strip()
kubernetes_bin_dir = Path('/opt/kubernetes/bin')
# Get the version of kubernetes to install.
version = subprocess.check_output(['relation-get', 'version']).strip()
print('Relation version: ', version)
if not version:
print('No version present in the relation.')
exit(0)
version_file = charm_dir / '.version'
if version_file.exists():
previous_version = version_file.text()
print('Previous version: ', previous_version)
if version == previous_version:
exit(0)
# Can not download binaries while the service is running, so stop it.
# TODO: Figure out a better way to handle upgraded kubernetes binaries.
for service in ('kubelet', 'proxy'):
if host.service_running(service):
host.service_stop(service)
command = ['relation-get', 'private-address']
# Get the kubernetes-master address.
server = subprocess.check_output(command).strip()
print('Kubernetes master private address: ', server)
installer = KubernetesInstaller(arch, version, server, kubernetes_bin_dir)
installer.download()
installer.install()
# Write the most recently installed version number to the file.
version_file.write_text(version)
relation_changed()
@hooks.hook('etcd-relation-changed',
'network-relation-changed')
def relation_changed():
"""Connect the parts and go :-)
"""
template_data = get_template_data()
# Check required keys
for k in ('etcd_servers', 'kubeapi_server'):
if not template_data.get(k):
print('Missing data for %s %s' % (k, template_data))
return
print('Running with\n%s' % template_data)
# Setup kubernetes supplemental group
setup_kubernetes_group()
# Register upstart managed services
for n in ('kubelet', 'proxy'):
if render_upstart(n, template_data) or not host.service_running(n):
print('Starting %s' % n)
host.service_restart(n)
# Register machine via api
print('Registering machine')
register_machine(template_data['kubeapi_server'])
# Save the marker (for restarts to detect prev install)
template_data.save()
def get_template_data():
rels = hookenv.relations()
template_data = hookenv.Config()
template_data.CONFIG_FILE_NAME = '.unit-state'
overlay_type = get_scoped_rel_attr('network', rels, 'overlay_type')
etcd_servers = get_rel_hosts('etcd', rels, ('hostname', 'port'))
api_servers = get_rel_hosts('api', rels, ('hostname', 'port'))
# kubernetes master isn't ha yet.
if api_servers:
api_info = api_servers.pop()
api_servers = 'http://%s:%s' % (api_info[0], api_info[1])
template_data['overlay_type'] = overlay_type
template_data['kubelet_bind_addr'] = _bind_addr(
hookenv.unit_private_ip())
template_data['proxy_bind_addr'] = _bind_addr(
hookenv.unit_get('public-address'))
template_data['kubeapi_server'] = api_servers
template_data['etcd_servers'] = ','.join([
'http://%s:%s' % (s[0], s[1]) for s in sorted(etcd_servers)])
template_data['identifier'] = os.environ['JUJU_UNIT_NAME'].replace(
'/', '-')
return _encode(template_data)
def _bind_addr(addr):
if addr.replace('.', '').isdigit():
return addr
try:
return socket.gethostbyname(addr)
except socket.error:
raise ValueError('Could not resolve private address')
def _encode(d):
for k, v in d.items():
if isinstance(v, unicode):
d[k] = v.encode('utf8')
return d
def get_scoped_rel_attr(rel_name, rels, attr):
private_ip = hookenv.unit_private_ip()
for r, data in rels.get(rel_name, {}).items():
for unit_id, unit_data in data.items():
if unit_data.get('private-address') != private_ip:
continue
if unit_data.get(attr):
return unit_data.get(attr)
def get_rel_hosts(rel_name, rels, keys=('private-address',)):
hosts = []
for r, data in rels.get(rel_name, {}).items():
for unit_id, unit_data in data.items():
if unit_id == hookenv.local_unit():
continue
values = [unit_data.get(k) for k in keys]
if not all(values):
continue
hosts.append(len(values) == 1 and values[0] or values)
return hosts
def render_upstart(name, data):
tmpl_path = os.path.join(
os.environ.get('CHARM_DIR'), 'files', '%s.upstart.tmpl' % name)
with open(tmpl_path) as fh:
tmpl = fh.read()
rendered = tmpl % data
tgt_path = '/etc/init/%s.conf' % name
if os.path.exists(tgt_path):
with open(tgt_path) as fh:
contents = fh.read()
if contents == rendered:
return False
with open(tgt_path, 'w') as fh:
fh.write(rendered)
return True
def register_machine(apiserver, retry=False):
parsed = urlparse.urlparse(apiserver)
# identity = hookenv.local_unit().replace('/', '-')
private_address = hookenv.unit_private_ip()
with open('/proc/meminfo') as fh:
info = fh.readline()
mem = info.strip().split(':')[1].strip().split()[0]
cpus = os.sysconf('SC_NPROCESSORS_ONLN')
# https://github.com/kubernetes/kubernetes/blob/master/docs/admin/node.md
registration_request = Registrator()
registration_request.data['kind'] = 'Node'
registration_request.data['id'] = private_address
registration_request.data['name'] = private_address
registration_request.data['metadata']['name'] = private_address
registration_request.data['spec']['capacity']['mem'] = mem + ' K'
registration_request.data['spec']['capacity']['cpu'] = cpus
registration_request.data['spec']['externalID'] = private_address
registration_request.data['status']['hostIP'] = private_address
response, result = registration_request.register(parsed.hostname,
parsed.port,
'/api/v1/nodes')
print(response)
try:
registration_request.command_succeeded(response, result)
except ValueError:
# This happens when we have already registered
# for now this is OK
pass
def setup_kubernetes_group():
output = subprocess.check_output(['groups', 'kubernetes'])
# TODO: check group exists
if 'docker' not in output:
subprocess.check_output(
['usermod', '-a', '-G', 'docker', 'kubernetes'])
if __name__ == '__main__':
hooks.execute(sys.argv)