Merge pull request #14004 from mbruzek/enable-ssl-squash

Auto commit by PR queue bot
pull/6/head
k8s-merge-robot 2015-09-17 16:00:09 -07:00
commit 48275aa944
19 changed files with 345 additions and 175 deletions

View File

@ -1,9 +1,33 @@
options:
version:
type: string
default: "v0.15.0"
default: "v1.0.0"
description: |
The kubernetes release to use in this charm. The binary files are
compiled from the source identified by this tag in github. Using the
value of "source" will use the master kubernetes branch when compiling
the binaries.
username:
type: string
default: "admin"
description: |
The initial user for the kubernetes basic authentication file.
password:
type: string
default: ""
description: |
The password for the kubernetes basic authentication. If this value is
empty, a password will be generated at random for the username.
apiserver-cert:
type: string
default: ""
description: |
The ssl certificate to use for tls communication to the Kubernetes api
server. If this value is empty a self signed certificate and key will
be generated.
apiserver-key:
type: string
default: ""
description: |
The private key to use for tls communication to the Kubernetes api
server. If this value is empty a key and certificate will be generated.

View File

@ -8,13 +8,12 @@ limit nofile 20000 20000
kill timeout 30 # wait 30s between SIGTERM and SIGKILL.
exec /usr/local/bin/apiserver \
--address=%(api_bind_address)s \
--basic-auth-file=/srv/kubernetes/basic-auth.csv \
--bind-address=%(api_private_address)s \
--etcd-servers=%(etcd_servers)s \
--insecure-bind-address=%(api_private_address)s \
--logtostderr=true \
--service-cluster-ip-range=10.244.240.0/20
--secure-port=6443 \
--service-cluster-ip-range=10.244.240.0/20 \
--tls-cert-file=/srv/kubernetes/apiserver.crt \
--tls-private-key-file=/srv/kubernetes/apiserver.key

View File

@ -10,11 +10,4 @@ kill timeout 30 # wait 30s between SIGTERM and SIGKILL.
exec /usr/local/bin/controller-manager \
--address=%(bind_address)s \
--logtostderr=true \
--master=%(api_server_address)s
--master=%(api_http_uri)s

View File

@ -1,5 +1,9 @@
# This file configures ngnix to serve Kubernetes binaries using http.
# The charms find the location path from the api relation to the charm.
server {
listen %(api_bind_address)s:80;
listen 80 default_server;
root %(alias)s;
location %(web_uri)s {
alias %(alias)s;
}

View File

@ -1,39 +1,27 @@
# HTTP/HTTPS server
#
# Proxy HTTPS from the public address to the kube-apiserver running at 6443.
server {
listen 80;
server_name localhost;
listen 443;
server_name localhost;
root html;
index index.html index.htm;
root html;
index index.html index.htm;
# ssl on;
# ssl_certificate /usr/share/nginx/server.cert;
# ssl_certificate_key /usr/share/nginx/server.key;
ssl on;
ssl_certificate /srv/kubernetes/apiserver.crt;
ssl_certificate_key /srv/kubernetes/apiserver.key;
ssl_session_timeout 5m;
# ssl_session_timeout 5m;
# ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
# ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS;
# ssl_prefer_server_ciphers on;
# don't use SSLv3 because of POODLE
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS;
ssl_prefer_server_ciphers on;
location / {
# auth_basic "Restricted";
# auth_basic_user_file /usr/share/nginx/htpasswd;
# Proxy settings
# disable buffering so that watch works
proxy_buffering off;
proxy_pass %(api_server_address)s;
proxy_connect_timeout 159s;
proxy_send_timeout 600s;
proxy_read_timeout 600s;
# Disable retry
proxy_next_upstream off;
# Support web sockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / {
proxy_buffering off;
proxy_pass %(api_https_uri)s;
proxy_connect_timeout 159s;
proxy_send_timeout 600s;
proxy_read_timeout 600s;
proxy_redirect off;
}
}

View File

@ -10,11 +10,4 @@ kill timeout 30 # wait 30s between SIGTERM and SIGKILL.
exec /usr/local/bin/scheduler \
--address=%(bind_address)s \
--logtostderr=true \
--master=%(api_server_address)s
--master=%(api_http_uri)s

View File

@ -23,8 +23,9 @@ import socket
import subprocess
import sys
from charmhelpers.core import hookenv, host
from charmhelpers.contrib import ssl
from kubernetes_installer import KubernetesInstaller
from path import path
from path import Path
hooks = hookenv.Hooks()
@ -55,10 +56,14 @@ def config_changed():
create kubernetes binary files.
"""
hookenv.log('Starting config-changed')
charm_dir = path(hookenv.charm_dir())
charm_dir = Path(hookenv.charm_dir())
config = hookenv.config()
# Get the version of kubernetes to install.
version = config['version']
username = config['username']
password = config['password']
certificate = config['apiserver-cert']
key = config['apiserver-key']
if version == 'master':
# The 'master' branch of kuberentes is used when master is configured.
@ -70,32 +75,59 @@ def config_changed():
# Create a branch to a tag to get the release version.
branch = 'tags/{0}'.format(version)
# Get the package architecture, rather than arch from the kernel (uname -m).
cert_file = '/srv/kubernetes/apiserver.crt'
key_file = '/srv/kubernetes/apiserver.key'
# When the cert or key changes we need to restart the apiserver.
if config.changed('apiserver-cert') or config.changed('apiserver-key'):
hookenv.log('Certificate or key has changed.')
if not certificate or not key:
generate_cert(key=key_file, cert=cert_file)
else:
hookenv.log('Writing new certificate and key to server.')
with open(key_file, 'w') as file:
file.write(key)
with open(cert_file, 'w') as file:
file.write(certificate)
# Restart apiserver as the certificate or key has changed.
if host.service_running('apiserver'):
host.service_restart('apiserver')
# Reload nginx because it proxies https to apiserver.
if host.service_running('nginx'):
host.service_reload('nginx')
if config.changed('username') or config.changed('password'):
hookenv.log('Username or password changed, creating authentication.')
basic_auth(config['username'], config['username'], config['password'])
if host.service_running('apiserver'):
host.service_restart('apiserver')
# Get package architecture, rather than arch from the kernel (uname -m).
arch = subprocess.check_output(['dpkg', '--print-architecture']).strip()
if not branch:
output_path = charm_dir / 'files/output'
installer = KubernetesInstaller(arch, version, output_path)
kube_installer = KubernetesInstaller(arch, version, output_path)
else:
# Build the kuberentes binaries from source on the units.
kubernetes_dir = path('/opt/kubernetes')
kubernetes_dir = Path('/opt/kubernetes')
# Construct the path to the binaries using the arch.
output_path = kubernetes_dir / '_output/local/bin/linux' / arch
installer = KubernetesInstaller(arch, version, output_path)
kube_installer = KubernetesInstaller(arch, version, output_path)
if not kubernetes_dir.exists():
print('The source directory {0} does not exist'.format(kubernetes_dir))
print('Was the kubernetes code cloned during install?')
message = 'The kubernetes source directory {0} does not exist. ' \
'Was the kubernetes repository cloned during the install?'
print(message.format(kubernetes_dir))
exit(1)
# Change to the kubernetes directory (git repository).
with kubernetes_dir:
# Create a command to get the current branch.
git_branch = 'git branch | grep "\*" | cut -d" " -f2'
current_branch = subprocess.check_output(git_branch, shell=True).strip()
current_branch = subprocess.check_output(git_branch, shell=True)
current_branch = current_branch.strip()
print('Current branch: ', current_branch)
# Create the path to a file to indicate if the build was broken.
broken_build = charm_dir / '.broken_build'
@ -104,12 +136,12 @@ def config_changed():
print('Last build failed: ', last_build_failed)
# Rebuild if current version is different or last build failed.
if current_branch != version or last_build_failed:
installer.build(branch)
kube_installer.build(branch)
if not output_path.isdir():
broken_build.touch()
# Create the symoblic links to the right directories.
installer.install()
kube_installer.install()
relation_changed()
@ -123,10 +155,10 @@ def relation_changed():
# Check required keys
for k in ('etcd_servers',):
if not template_data.get(k):
print "Missing data for", k, template_data
print 'Missing data for', k, template_data
return
print "Running with\n", template_data
print 'Running with\n', template_data
# Render and restart as needed
for n in ('apiserver', 'controller-manager', 'scheduler'):
@ -157,7 +189,7 @@ def network_relation_changed():
def notify_minions():
print("Notify minions.")
print('Notify minions.')
config = hookenv.config()
for r in hookenv.relation_ids('minions-api'):
hookenv.relation_set(
@ -165,7 +197,49 @@ def notify_minions():
hostname=hookenv.unit_private_ip(),
port=8080,
version=config['version'])
print("Notified minions of version " + config['version'])
print('Notified minions of version ' + config['version'])
def basic_auth(name, id, pwd=None, file='/srv/kubernetes/basic-auth.csv'):
"""
Create a basic authentication file for kubernetes. The file is a csv file
with 3 columns: password, user name, user id. From the Kubernetes docs:
The basic auth credentials last indefinitely, and the password cannot be
changed without restarting apiserver.
"""
if not pwd:
import random
import string
alphanumeric = string.ascii_letters + string.digits
pwd = ''.join(random.choice(alphanumeric) for _ in range(16))
lines = []
auth_file = Path(file)
if auth_file.isfile():
lines = auth_file.lines()
for line in lines:
target = ',{0},{1}'.format(name, id)
if target in line:
lines.remove(line)
auth_line = '{0},{1},{2}'.format(pwd, name, id)
lines.append(auth_line)
auth_file.write_lines(lines)
def generate_cert(common_name=None,
key='/srv/kubernetes/apiserver.key',
cert='/srv/kubernetes/apiserver.crt'):
"""
Create the certificate and key for the Kubernetes tls enablement.
"""
hookenv.log('Generating new self signed certificate and key', 'INFO')
if not common_name:
common_name = hookenv.unit_get('public-address')
if os.path.isfile(key) or os.path.isfile(cert):
hookenv.log('Overwriting the existing certificate or key', 'WARNING')
hookenv.log('Generating certificate for {0}'.format(common_name), 'INFO')
# Generate the self signed certificate with the public address as CN.
# https://pythonhosted.org/charmhelpers/api/charmhelpers.contrib.ssl.html
ssl.generate_selfsigned(key, cert, cn=common_name)
def get_template_data():
@ -173,18 +247,21 @@ def get_template_data():
config = hookenv.config()
version = config['version']
template_data = {}
template_data['etcd_servers'] = ",".join([
"http://%s:%s" % (s[0], s[1]) for s in sorted(
template_data['etcd_servers'] = ','.join([
'http://%s:%s' % (s[0], s[1]) for s in sorted(
get_rel_hosts('etcd', rels, ('hostname', 'port')))])
template_data['minions'] = ",".join(get_rel_hosts('minions-api', rels))
template_data['minions'] = ','.join(get_rel_hosts('minions-api', rels))
private_ip = hookenv.unit_private_ip()
public_ip = hookenv.unit_public_ip()
template_data['api_public_address'] = _bind_addr(public_ip)
template_data['api_private_address'] = _bind_addr(private_ip)
template_data['bind_address'] = '127.0.0.1'
template_data['api_http_uri'] = 'http://%s:%s' % (private_ip, 8080)
template_data['api_https_uri'] = 'https://%s:%s' % (private_ip, 6443)
template_data['api_bind_address'] = _bind_addr(hookenv.unit_private_ip())
template_data['bind_address'] = "127.0.0.1"
template_data['api_server_address'] = "http://%s:%s" % (
hookenv.unit_private_ip(), 8080)
arch = subprocess.check_output(['dpkg', '--print-architecture']).strip()
template_data['web_uri'] = "/kubernetes/%s/local/bin/linux/%s/" % (version,
template_data['web_uri'] = '/kubernetes/%s/local/bin/linux/%s/' % (version,
arch)
if version == 'local':
template_data['alias'] = hookenv.charm_dir() + '/files/output/'
@ -201,7 +278,7 @@ def _bind_addr(addr):
try:
return socket.gethostbyname(addr)
except socket.error:
raise ValueError("Could not resolve private address")
raise ValueError('Could not resolve address %s' % addr)
def _encode(d):
@ -223,7 +300,7 @@ def get_rel_hosts(rel_name, rels, keys=('private-address',)):
return hosts
def render_file(name, data, src_suffix="upstart.tmpl", tgt_path=None):
def render_file(name, data, src_suffix='upstart.tmpl', tgt_path=None):
tmpl_path = os.path.join(
os.environ.get('CHARM_DIR'), 'files', '%s.%s' % (name, src_suffix))
@ -244,5 +321,6 @@ def render_file(name, data, src_suffix="upstart.tmpl", tgt_path=None):
fh.write(rendered)
return True
if __name__ == '__main__':
hooks.execute(sys.argv)

View File

@ -18,10 +18,10 @@ import setup
setup.pre_install()
import subprocess
from charmhelpers.core import hookenv
from charmhelpers import fetch
from charmhelpers.core import hookenv
from charmhelpers.fetch import archiveurl
from path import path
from path import Path
def install():
@ -30,21 +30,28 @@ def install():
download_go()
hookenv.log('Adding kubernetes and go to the path')
address = hookenv.unit_private_ip()
strings = [
'export GOROOT=/usr/local/go\n',
'export PATH=$PATH:$GOROOT/bin\n',
'export KUBE_MASTER_IP=0.0.0.0\n',
'export KUBERNETES_MASTER=http://$KUBE_MASTER_IP\n',
'export KUBERNETES_MASTER=http://{0}:8080\n'.format(address),
]
update_rc_files(strings)
hookenv.log('Downloading kubernetes code')
clone_repository()
# Create the directory to store the keys and auth files.
srv = Path('/srv/kubernetes')
if not srv.isdir():
srv.makedirs_p()
hookenv.open_port(8080)
hookenv.open_port(6443)
hookenv.open_port(443)
hookenv.log('Install complete')
def download_go():
"""
Kubernetes charm strives to support upstream. Part of this is installing a
@ -59,12 +66,12 @@ def download_go():
def clone_repository():
"""
Clone the upstream repository into /opt/kubernetes for deployment compilation
of kubernetes. Subsequently used during upgrades.
Clone the upstream repository into /opt/kubernetes for deployment
compilation of kubernetes. Subsequently used during upgrades.
"""
repository = 'https://github.com/kubernetes/kubernetes.git'
kubernetes_directory = path('/opt/kubernetes')
kubernetes_directory = Path('/opt/kubernetes')
# Since we can not clone twice, check for the directory and remove it.
if kubernetes_directory.isdir():
kubernetes_directory.rmtree_p()
@ -75,7 +82,6 @@ def clone_repository():
print(output)
def install_packages():
"""
Install required packages to build the k8s source, and syndicate between
@ -83,17 +89,21 @@ def install_packages():
"""
hookenv.log('Installing Debian packages')
# Create the list of packages to install.
apt_packages = ['build-essential', 'git', 'make', 'nginx', 'python-pip']
apt_packages = ['apache2-utils',
'build-essential',
'git',
'make',
'nginx',
'python-pip', ]
fetch.apt_install(fetch.filter_installed_packages(apt_packages))
def update_rc_files(strings):
"""
Preseed the bash environment for ubuntu and root with K8's env vars to
make interfacing with the api easier. (see: kubectrl docs)
"""
rc_files = [path('/home/ubuntu/.bashrc'), path('/root/.bashrc')]
rc_files = [Path('/home/ubuntu/.bashrc'), Path('/root/.bashrc')]
for rc_file in rc_files:
lines = rc_file.lines()
for string in strings:
@ -102,6 +112,5 @@ def update_rc_files(strings):
rc_file.write_lines(lines)
if __name__ == "__main__":
install()

View File

@ -17,7 +17,7 @@
import os
import shlex
import subprocess
from path import path
from path import Path
def run(command, shell=False):
@ -46,7 +46,7 @@ class KubernetesInstaller():
'kubelet': 'kubelet'}
self.arch = arch
self.version = version
self.output_dir = path(output_dir)
self.output_dir = Path(output_dir)
def build(self, branch):
""" Build kubernetes from a github repository using the Makefile. """
@ -88,7 +88,7 @@ class KubernetesInstaller():
print(make_what)
rc = subprocess.call(shlex.split(make_what), env=go_env)
def install(self, install_dir=path('/usr/local/bin')):
def install(self, install_dir=Path('/usr/local/bin')):
""" Install kubernetes binary files from the output directory. """
if not install_dir.isdir():

View File

@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
def pre_install():
"""
Do any setup required before the install hook.

View File

@ -15,7 +15,6 @@
# limitations under the License.
from mock import patch
from path import path
from path import Path
import pytest
import subprocess
@ -38,7 +37,7 @@ def test_run():
assert output
assert 'kubernetes_installer.py' in output
invalid_directory = path('/not/a/real/directory')
invalid_directory = Path('/not/a/real/directory')
assert not invalid_directory.exists()
invalid_command = 'ls {0}'.format(invalid_directory)
with pytest.raises(subprocess.CalledProcessError) as error:
@ -67,13 +66,13 @@ class TestKubernetesInstaller():
assert 'kubelet' in ki.aliases
assert ki.arch == 'i386'
assert ki.version == '3.0.1'
assert ki.output_dir == path('/tmp/does_not_exist')
assert ki.output_dir == Path('/tmp/does_not_exist')
@patch('kubernetes_installer.run')
@patch('kubernetes_installer.subprocess.call')
def test_build(self, cmock, rmock):
""" Test the build method with master and non-master branches. """
directory = path('/tmp/kubernetes_installer_test/build')
directory = Path('/tmp/kubernetes_installer_test/build')
ki = self.makeone('amd64', 'v99.00.11', directory)
assert not directory.exists(), 'The %s directory exists!' % directory
# Call the build method with "master" branch.
@ -94,7 +93,7 @@ class TestKubernetesInstaller():
def test_install(self):
""" Test the install method that it creates the correct links. """
directory = path('/tmp/kubernetes_installer_test/install')
directory = Path('/tmp/kubernetes_installer_test/install')
ki = self.makeone('ppc64le', '1.2.3', directory)
assert not directory.exists(), 'The %s directory exits!' % directory
directory.makedirs_p()

View File

@ -17,10 +17,7 @@
"""
The main hook file that is called by Juju.
"""
import json
import httplib
import os
import time
import socket
import subprocess
import sys
@ -28,7 +25,7 @@ import urlparse
from charmhelpers.core import hookenv, host
from kubernetes_installer import KubernetesInstaller
from path import path
from path import Path
from lib.registrator import Registrator
@ -43,10 +40,10 @@ def api_relation_changed():
from the kubernetes-master charm and installs it locally on this machine.
"""
hookenv.log('Starting api-relation-changed')
charm_dir = path(hookenv.charm_dir())
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')
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)
@ -228,6 +225,7 @@ def register_machine(apiserver, retry=False):
# for now this is OK
pass
def setup_kubernetes_group():
output = subprocess.check_output(['groups', 'kubernetes'])

View File

@ -13,20 +13,21 @@ apt-get install -q -y \
python-pip \
wget
pip install path.py
pip install -r $CHARM_DIR/python_requirements.txt
# Create the necessary kubernetes group.
groupadd kubernetes
useradd -d /var/lib/kubernetes \
-g kubernetes \
-s /sbin/nologin \
--system \
kubernetes
groupadd --force kubernetes
if grep -q "^kubernetes:" /etc/passwd; then
echo "The kubernetes user already exists!"
else
# Create the user when kubernetes does not exist.
useradd -d /var/lib/kubernetes \
-g kubernetes \
-s /sbin/nologin \
--system \
kubernetes
fi
install -d -m 0744 -o kubernetes -g kubernetes /var/lib/kubernetes
install -d -m 0744 -o kubernetes -g kubernetes /etc/kubernetes/manifests
# wait for the world, depends on where we installed it from distro
#sudo service docker.io stop
# or upstream archive
#sudo service docker stop

View File

@ -15,7 +15,7 @@
# limitations under the License.
import subprocess
from path import path
from path import Path
class KubernetesInstaller():
@ -49,7 +49,7 @@ class KubernetesInstaller():
print(output)
destination.chmod(0o755)
def install(self, install_dir=path('/usr/local/bin')):
def install(self, install_dir=Path('/usr/local/bin')):
""" Create links to the binary files to the install directory. """
if not install_dir.isdir():

View File

@ -0,0 +1 @@
path.py

View File

@ -0,0 +1,16 @@
#!/bin/bash
# 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.

View File

@ -0,0 +1,17 @@
#!/bin/bash
# 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.
NUM_MINIONS=${NUM_MINIONS:-2}

View File

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: kube-system

View File

@ -18,29 +18,68 @@
set -o errexit
set -o nounset
set -o pipefail
#set -o xtrace
UTIL_SCRIPT=$(readlink -m "${BASH_SOURCE}")
JUJU_PATH=$(dirname ${UTIL_SCRIPT})
KUBE_ROOT=$(readlink -m ${JUJU_PATH}/../../)
# Use the config file specified in $KUBE_CONFIG_FILE, or config-default.sh.
source "${JUJU_PATH}/${KUBE_CONFIG_FILE-config-default.sh}"
source ${JUJU_PATH}/prereqs/ubuntu-juju.sh
export JUJU_REPOSITORY=${JUJU_PATH}/charms
#KUBE_BUNDLE_URL='https://raw.githubusercontent.com/whitmo/bundle-kubernetes/master/bundles.yaml'
KUBE_BUNDLE_PATH=${JUJU_PATH}/bundles/local.yaml
function verify-prereqs() {
gather_installation_reqs
}
# Build the binaries on the local system and copy the binaries to the Juju charm.
function build-local() {
local targets=(
cmd/kube-proxy \
cmd/kube-apiserver \
cmd/kube-controller-manager \
cmd/kubelet \
plugin/cmd/kube-scheduler \
cmd/kubectl \
test/e2e/e2e.test \
)
# Make a clean environment to avoid compiler errors.
make clean
# Build the binaries locally that are used in the charms.
make all WHAT="cmd/kube-apiserver cmd/kubectl cmd/kube-controller-manager plugin/cmd/kube-scheduler cmd/kubelet cmd/kube-proxy"
OUTPUT_DIR=_output/local/bin/linux/amd64
make all WHAT="${targets[*]}"
local OUTPUT_DIR=_output/local/bin/linux/amd64
mkdir -p cluster/juju/charms/trusty/kubernetes-master/files/output
# Copy the binary output to the charm directory.
# Copy the binaries from the output directory to the charm directory.
cp -v $OUTPUT_DIR/* cluster/juju/charms/trusty/kubernetes-master/files/output
}
function detect-master() {
local kubestatus
# Capturing a newline, and my awk-fu was weak - pipe through tr -d
kubestatus=$(juju status --format=oneline kubernetes-master | grep kubernetes-master/0 | awk '{print $3}' | tr -d "\n")
export KUBE_MASTER_IP=${kubestatus}
export KUBE_SERVER=http://${KUBE_MASTER_IP}:8080
}
function detect-minions() {
# Run the Juju command that gets the minion private IP addresses.
local ipoutput
ipoutput=$(juju run --service kubernetes "unit-get private-address" --format=json)
# [
# {"MachineId":"2","Stdout":"192.168.122.188\n","UnitId":"kubernetes/0"},
# {"MachineId":"3","Stdout":"192.168.122.166\n","UnitId":"kubernetes/1"}
# ]
# Strip out the IP addresses
export KUBE_MINION_IP_ADDRESSES=($(${JUJU_PATH}/return-node-ips.py "${ipoutput}"))
# echo "Kubernetes minions: " ${KUBE_MINION_IP_ADDRESSES[@]} 1>&2
export NUM_MINIONS=${#KUBE_MINION_IP_ADDRESSES[@]}
}
function get-password() {
export KUBE_USER=admin
# Get the password from the basic-auth.csv file on kubernetes-master.
export KUBE_PASSWORD=$(juju run --unit kubernetes-master/0 "cat /srv/kubernetes/basic-auth.csv" | grep ${KUBE_USER} | cut -d, -f1)
}
function kube-up() {
build-local
if [[ -d "~/.juju/current-env" ]]; then
@ -51,61 +90,45 @@ function kube-up() {
# The juju-deployer command will deploy the bundle and can be run
# multiple times to continue deploying the parts that fail.
juju deployer -c ${KUBE_BUNDLE_PATH}
source "${KUBE_ROOT}/cluster/common.sh"
get-password
# Sleep due to juju bug http://pad.lv/1432759
sleep-status
detect-master
detect-minions
export KUBE_MASTER_IP="${KUBE_MASTER_IP}:8080"
local prefix=$RANDOM
export KUBE_CERT="/tmp/${prefix}-kubecfg.crt"
export KUBE_KEY="/tmp/${prefix}-kubecfg.key"
export CA_CERT="/tmp/${prefix}-kubecfg.ca"
export CONTEXT="juju"
# Copy the cert and key to this machine.
(
umask 077
juju scp kubernetes-master/0:/srv/kubernetes/apiserver.crt ${KUBE_CERT}
juju run --unit kubernetes-master/0 'chmod 644 /srv/kubernetes/apiserver.key'
juju scp kubernetes-master/0:/srv/kubernetes/apiserver.key ${KUBE_KEY}
juju run --unit kubernetes-master/0 'chmod 600 /srv/kubernetes/apiserver.key'
cp ${KUBE_CERT} ${CA_CERT}
create-kubeconfig
)
}
function kube-down() {
local force="${1-}"
# Remove the binary files from the charm directory.
rm -rf cluster/juju/charms/trusty/kubernetes-master/files/output/
local jujuenv
jujuenv=$(cat ~/.juju/current-environment)
juju destroy-environment $jujuenv
juju destroy-environment ${jujuenv} ${force} || true
}
function detect-master() {
local kubestatus
# Capturing a newline, and my awk-fu was weak - pipe through tr -d
kubestatus=$(juju status --format=oneline kubernetes-master | grep kubernetes-master/0 | awk '{print $3}' | tr -d "\n")
export KUBE_MASTER_IP=${kubestatus}
export KUBE_MASTER=${KUBE_MASTER_IP}
export KUBERNETES_MASTER=http://${KUBE_MASTER}:8080
echo "Kubernetes master: " ${KUBERNETES_MASTER}
}
function detect-minions() {
# Run the Juju command that gets the minion private IP addresses.
local ipoutput
ipoutput=$(juju run --service kubernetes "unit-get private-address" --format=json)
echo $ipoutput
# Strip out the IP addresses
#
# Example Output:
#- MachineId: "10"
# Stdout: |
# 10.197.55.232
# UnitId: kubernetes/0
# - MachineId: "11"
# Stdout: |
# 10.202.146.124
# UnitId: kubernetes/1
export KUBE_MINION_IP_ADDRESSES=($(${JUJU_PATH}/return-node-ips.py "${ipoutput}"))
echo "Kubernetes minions: " ${KUBE_MINION_IP_ADDRESSES[@]}
export NUM_MINIONS=${#KUBE_MINION_IP_ADDRESSES[@]}
export MINION_NAMES=$KUBE_MINION_IP_ADDRESSES
}
function setup-logging-firewall() {
echo "TODO: setup logging and firewall rules"
}
function teardown-logging-firewall() {
echo "TODO: teardown logging and firewall rules"
function prepare-e2e() {
echo "prepare-e2e() The Juju provider does not need any preperations for e2e." 1>&2
}
function sleep-status() {
@ -115,7 +138,7 @@ function sleep-status() {
i=0
maxtime=900
jujustatus=''
echo "Waiting up to 15 minutes to allow the cluster to come online... wait for it..."
echo "Waiting up to 15 minutes to allow the cluster to come online... wait for it..." 1>&2
jujustatus=$(juju status kubernetes-master --format=oneline)
if [[ $jujustatus == *"started"* ]];
@ -132,6 +155,28 @@ function sleep-status() {
# sleep because we cannot get the status back of where the minions are in the deploy phase
# thanks to a generic "started" state and our service not actually coming online until the
# minions have received the binary from the master distribution hub during relations
echo "Sleeping an additional minute to allow the cluster to settle"
echo "Sleeping an additional minute to allow the cluster to settle" 1>&2
sleep 60
}
# Execute prior to running tests to build a release if required for environment.
function test-build-release {
echo "test-build-release() " 1>&2
}
# Execute prior to running tests to initialize required structure. This is
# called from hack/e2e.go only when running -up (it is run after kube-up).
function test-setup {
echo "test-setup() " 1>&2
}
# Execute after running tests to perform any required clean-up. This is called
# from hack/e2e.go
function test-teardown() {
kube-down "-y"
}
# Verify the prerequisites are statisfied before running.
function verify-prereqs() {
gather_installation_reqs
}