Merge lp:~mbruzek/charms/trusty/kubernetes-master/trunk into lp:~kubernetes/charms/trusty/kubernetes-master/trunk

Proposed by Matt Bruzek
Status: Merged
Merged at revision: 14
Proposed branch: lp:~mbruzek/charms/trusty/kubernetes-master/trunk
Merge into: lp:~kubernetes/charms/trusty/kubernetes-master/trunk
Diff against target: 979 lines (+383/-164)
16 files modified
Makefile (+2/-1)
README.md (+42/-17)
config.yaml (+24/-0)
copyright (+1/-1)
files/apiserver.upstart.tmpl (+8/-9)
files/controller-manager.upstart.tmpl (+1/-8)
files/distribution.conf.tmpl (+5/-1)
files/nginx.conf.tmpl (+25/-37)
files/scheduler.upstart.tmpl (+1/-8)
hooks/__init__.py (+16/-0)
hooks/hooks.py (+118/-26)
hooks/install.py (+35/-10)
hooks/kubernetes_installer.py (+17/-3)
hooks/setup.py (+14/-0)
unit_tests/kubernetes_installer_test.py (+25/-13)
unit_tests/test_install.py (+49/-30)
To merge this branch: bzr merge lp:~mbruzek/charms/trusty/kubernetes-master/trunk
Reviewer Review Type Date Requested Status
Charles Butler (community) Approve
Review via email: mp+273994@code.launchpad.net

Description of the change

Synched this charm with the content on github. Then ran the lint and CI tests against the code.

Had to clean up quite a few failures to get here, but this charm passes all tests in CI and is synchronized with the content in github.

http://reports.vapour.ws/charm-test-details/charm-bundle-test-parent-1122

To post a comment you must log in.
19. By Matt Bruzek

Changing the flake8 rules to avoid conflict with verify-boilerplate k8s tool.

Revision history for this message
Charles Butler (lazypower) wrote :

+1 lGTM

Thanks for the hard work Matt.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile'
2--- Makefile 2015-09-24 19:50:43 +0000
3+++ Makefile 2015-10-09 16:18:58 +0000
4@@ -6,7 +6,7 @@
5 .venv/bin/pip install -q -r requirements.txt
6
7 lint: virtualenv
8- @.venv/bin/flake8 hooks --exclude=charmhelpers
9+ @.venv/bin/flake8 hooks --exclude=charmhelpers --ignore=W391
10 @.venv/bin/charm proof
11
12 test: virtualenv
13@@ -27,3 +27,4 @@
14 clean:
15 rm -rf .venv
16 find -name *.pyc -delete
17+ rm -rf unit_tests/.cache
18
19=== modified file 'README.md'
20--- README.md 2015-04-10 21:42:42 +0000
21+++ README.md 2015-10-09 16:18:58 +0000
22@@ -1,6 +1,6 @@
23 # Kubernetes Master Charm
24
25-[Kubernetes](https://github.com/googlecloudplatform/kubernetes) is an open
26+[Kubernetes](https://github.com/kubernetes/kubernetes) is an open
27 source system for managing containerized applications across multiple hosts.
28 Kubernetes uses [Docker](http://www.docker.io/) to package, instantiate and run
29 containerized applications.
30@@ -56,22 +56,44 @@
31
32 #### Deploying the recommended configuration
33
34-A bundle can be used to deploy Kubernetes onto any cloud it can be
35-orchestrated directly in the Juju Graphical User Interface, when using
36-`juju quickstart`:
37-
38- juju quickstart https://raw.githubusercontent.com/whitmo/bundle-kubernetes/master/bundles.yaml
39-
40-
41-For more information on the recommended bundle deployment, see the
42-[Kubernetes bundle documentation](https://github.com/whitmo/bundle-kubernetes)
43+Use the 'juju quickstart' command to deploy a Kubernetes cluster to any cloud
44+supported by Juju.
45+
46+The charm store version of the Kubernetes bundle can be deployed as follows:
47+
48+ juju quickstart u/kubernetes/kubernetes-cluster
49+
50+> Note: The charm store bundle may be locked to a specific Kubernetes release.
51+
52+Alternately you could deploy a Kubernetes bundle straight from github or a file:
53+
54+ juju quickstart https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/juju/bundles/local.yaml
55+
56+The command above does few things for you:
57+
58+- Starts a curses based gui for managing your cloud or MAAS credentials
59+- Looks for a bootstrapped deployment environment, and bootstraps if
60+ required. This will launch a bootstrap node in your chosen
61+ deployment environment (machine 0).
62+- Deploys the Juju GUI to your environment onto the bootstrap node.
63+- Provisions 4 machines, and deploys the Kubernetes services on top of
64+ them (Kubernetes-master, two Kubernetes minions using flannel, and etcd).
65+- Orchestrates the relations among the services, and exits.
66+
67+Now you should have a running Kubernetes. Run `juju status
68+--format=oneline` to see the address of your kubernetes-master unit.
69+
70+For further reading on [Juju Quickstart](https://pypi.python.org/pypi/juju-quickstart)
71+
72+Go to the [Getting started with Juju guide](https://github.com/kubernetes/kubernetes/blob/master/docs/getting-started-guides/juju.md)
73+for more information about deploying a development Kubernetes cluster.
74
75
76 #### Post Deployment
77
78 To interact with the kubernetes environment, either build or
79-[download](https://github.com/GoogleCloudPlatform/kubernetes/releases) the
80-[kubectl](https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/kubectl.md)
81+[download](https://github.com/kubernetes/kubernetes/releases) the
82+[kubectl](https://github.com/kubernetes/kubernetes/blob/master/docs/user-guide/kubectl/kubectl.md)
83 binary (available in the releases binary tarball) and point it to the master with :
84
85
86@@ -90,12 +112,15 @@
87 command.
88
89 Congratulations you know have deployed a Kubernetes environment! Use the
90-[kubectl](https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/kubectl.md)
91+[kubectl](https://github.com/kubernetes/kubernetes/blob/master/docs/user-guide/kubectl/kubectl.md)
92 to interact with the environment.
93
94 # Kubernetes information
95
96-- [Kubernetes github project](https://github.com/GoogleCloudPlatform/kubernetes)
97-- [Kubernetes issue tracker](https://github.com/GoogleCloudPlatform/kubernetes/issues)
98-- [Kubernetes Documenation](https://github.com/GoogleCloudPlatform/kubernetes/tree/master/docs)
99-- [Kubernetes releases](https://github.com/GoogleCloudPlatform/kubernetes/releases)
100+- [Kubernetes github project](https://github.com/kubernetes/kubernetes)
101+- [Kubernetes issue tracker](https://github.com/kubernetes/kubernetes/issues)
102+- [Kubernetes Documenation](https://github.com/kubernetes/kubernetes/tree/master/docs)
103+- [Kubernetes releases](https://github.com/kubernetes/kubernetes/releases)
104+
105+
106+[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/cluster/juju/charms/trusty/kubernetes-master/README.md?pixel)]()
107
108=== modified file 'config.yaml'
109--- config.yaml 2015-07-17 20:01:20 +0000
110+++ config.yaml 2015-10-09 16:18:58 +0000
111@@ -7,3 +7,27 @@
112 compiled from the source identified by this tag in github. Using the
113 value of "source" will use the master kubernetes branch when compiling
114 the binaries.
115+ username:
116+ type: string
117+ default: "admin"
118+ description: |
119+ The initial user for the kubernetes basic authentication file.
120+ password:
121+ type: string
122+ default: ""
123+ description: |
124+ The password for the kubernetes basic authentication. If this value is
125+ empty, a password will be generated at random for the username.
126+ apiserver-cert:
127+ type: string
128+ default: ""
129+ description: |
130+ The ssl certificate to use for tls communication to the Kubernetes api
131+ server. If this value is empty a self signed certificate and key will
132+ be generated.
133+ apiserver-key:
134+ type: string
135+ default: ""
136+ description: |
137+ The private key to use for tls communication to the Kubernetes api
138+ server. If this value is empty a key and certificate will be generated.
139
140=== modified file 'copyright'
141--- copyright 2015-07-17 20:01:20 +0000
142+++ copyright 2015-10-09 16:18:58 +0000
143@@ -1,4 +1,4 @@
144-Copyright 2015 Canonical Ltd.
145+Copyright 2015 Google Inc. All rights reserved.
146
147 Licensed under the Apache License, Version 2.0 (the "License");
148 you may not use this file except in compliance with the License.
149
150=== modified file 'files/apiserver.upstart.tmpl'
151--- files/apiserver.upstart.tmpl 2015-07-17 20:01:20 +0000
152+++ files/apiserver.upstart.tmpl 2015-10-09 16:18:58 +0000
153@@ -8,13 +8,12 @@
154 kill timeout 30 # wait 30s between SIGTERM and SIGKILL.
155
156 exec /usr/local/bin/apiserver \
157- --address=%(api_bind_address)s \
158- --etcd_servers=%(etcd_servers)s \
159+ --basic-auth-file=/srv/kubernetes/basic-auth.csv \
160+ --bind-address=%(api_private_address)s \
161+ --etcd-servers=%(etcd_servers)s \
162+ --insecure-bind-address=%(api_private_address)s \
163 --logtostderr=true \
164- --service-cluster-ip-range=10.244.240.0/20
165-
166-
167-
168-
169-
170-
171+ --secure-port=6443 \
172+ --service-cluster-ip-range=10.244.240.0/20 \
173+ --tls-cert-file=/srv/kubernetes/apiserver.crt \
174+ --tls-private-key-file=/srv/kubernetes/apiserver.key
175
176=== modified file 'files/controller-manager.upstart.tmpl'
177--- files/controller-manager.upstart.tmpl 2015-04-10 21:12:25 +0000
178+++ files/controller-manager.upstart.tmpl 2015-10-09 16:18:58 +0000
179@@ -10,11 +10,4 @@
180 exec /usr/local/bin/controller-manager \
181 --address=%(bind_address)s \
182 --logtostderr=true \
183- --master=%(api_server_address)s
184-
185-
186-
187-
188-
189-
190-
191+ --master=%(api_http_uri)s
192
193=== modified file 'files/distribution.conf.tmpl'
194--- files/distribution.conf.tmpl 2015-07-17 20:01:20 +0000
195+++ files/distribution.conf.tmpl 2015-10-09 16:18:58 +0000
196@@ -1,5 +1,9 @@
197+# This file configures ngnix to serve Kubernetes binaries using http.
198+# The charms find the location path from the api relation to the charm.
199 server {
200- listen %(api_bind_address)s:80;
201+ listen 80 default_server;
202+ root %(alias)s;
203+
204 location %(web_uri)s {
205 alias %(alias)s;
206 }
207
208=== modified file 'files/nginx.conf.tmpl'
209--- files/nginx.conf.tmpl 2015-01-27 17:59:40 +0000
210+++ files/nginx.conf.tmpl 2015-10-09 16:18:58 +0000
211@@ -1,39 +1,27 @@
212-# HTTP/HTTPS server
213-#
214+# Proxy HTTPS from the public address to the kube-apiserver running at 6443.
215 server {
216- listen 80;
217- server_name localhost;
218-
219- root html;
220- index index.html index.htm;
221-
222-# ssl on;
223-# ssl_certificate /usr/share/nginx/server.cert;
224-# ssl_certificate_key /usr/share/nginx/server.key;
225-
226-# ssl_session_timeout 5m;
227-# ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
228-# 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;
229-# ssl_prefer_server_ciphers on;
230-
231- location / {
232-# auth_basic "Restricted";
233-# auth_basic_user_file /usr/share/nginx/htpasswd;
234-
235- # Proxy settings
236- # disable buffering so that watch works
237- proxy_buffering off;
238- proxy_pass %(api_server_address)s;
239- proxy_connect_timeout 159s;
240- proxy_send_timeout 600s;
241- proxy_read_timeout 600s;
242-
243- # Disable retry
244- proxy_next_upstream off;
245-
246- # Support web sockets
247- proxy_http_version 1.1;
248- proxy_set_header Upgrade $http_upgrade;
249- proxy_set_header Connection "upgrade";
250- }
251+ listen 443;
252+ server_name localhost;
253+
254+ root html;
255+ index index.html index.htm;
256+
257+ ssl on;
258+ ssl_certificate /srv/kubernetes/apiserver.crt;
259+ ssl_certificate_key /srv/kubernetes/apiserver.key;
260+ ssl_session_timeout 5m;
261+
262+ # don't use SSLv3 because of POODLE
263+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
264+ 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;
265+ ssl_prefer_server_ciphers on;
266+
267+ location / {
268+ proxy_buffering off;
269+ proxy_pass %(api_https_uri)s;
270+ proxy_connect_timeout 159s;
271+ proxy_send_timeout 600s;
272+ proxy_read_timeout 600s;
273+ proxy_redirect off;
274+ }
275 }
276
277=== modified file 'files/scheduler.upstart.tmpl'
278--- files/scheduler.upstart.tmpl 2015-04-10 21:12:25 +0000
279+++ files/scheduler.upstart.tmpl 2015-10-09 16:18:58 +0000
280@@ -10,11 +10,4 @@
281 exec /usr/local/bin/scheduler \
282 --address=%(bind_address)s \
283 --logtostderr=true \
284- --master=%(api_server_address)s
285-
286-
287-
288-
289-
290-
291-
292+ --master=%(api_http_uri)s
293
294=== modified file 'hooks/__init__.py'
295--- hooks/__init__.py 2015-09-24 19:50:43 +0000
296+++ hooks/__init__.py 2015-10-09 16:18:58 +0000
297@@ -0,0 +1,16 @@
298+#!/usr/bin/env python
299+
300+# Copyright 2015 The Kubernetes Authors All rights reserved.
301+#
302+# Licensed under the Apache License, Version 2.0 (the "License");
303+# you may not use this file except in compliance with the License.
304+# You may obtain a copy of the License at
305+#
306+# http://www.apache.org/licenses/LICENSE-2.0
307+#
308+# Unless required by applicable law or agreed to in writing, software
309+# distributed under the License is distributed on an "AS IS" BASIS,
310+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
311+# See the License for the specific language governing permissions and
312+# limitations under the License.
313+
314
315=== modified file 'hooks/hooks.py'
316--- hooks/hooks.py 2015-09-24 19:50:43 +0000
317+++ hooks/hooks.py 2015-10-09 16:18:58 +0000
318@@ -1,5 +1,19 @@
319 #!/usr/bin/env python
320
321+# Copyright 2015 The Kubernetes Authors All rights reserved.
322+#
323+# Licensed under the Apache License, Version 2.0 (the "License");
324+# you may not use this file except in compliance with the License.
325+# You may obtain a copy of the License at
326+#
327+# http://www.apache.org/licenses/LICENSE-2.0
328+#
329+# Unless required by applicable law or agreed to in writing, software
330+# distributed under the License is distributed on an "AS IS" BASIS,
331+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
332+# See the License for the specific language governing permissions and
333+# limitations under the License.
334+
335 """
336 The main hook file is called by Juju.
337 """
338@@ -9,8 +23,9 @@
339 import subprocess
340 import sys
341 from charmhelpers.core import hookenv, host
342+from charmhelpers.contrib import ssl
343 from kubernetes_installer import KubernetesInstaller
344-from path import path
345+from path import Path
346
347 hooks = hookenv.Hooks()
348
349@@ -41,10 +56,14 @@
350 create kubernetes binary files.
351 """
352 hookenv.log('Starting config-changed')
353- charm_dir = path(hookenv.charm_dir())
354+ charm_dir = Path(hookenv.charm_dir())
355 config = hookenv.config()
356 # Get the version of kubernetes to install.
357 version = config['version']
358+ username = config['username']
359+ password = config['password']
360+ certificate = config['apiserver-cert']
361+ key = config['apiserver-key']
362
363 if version == 'master':
364 # The 'master' branch of kuberentes is used when master is configured.
365@@ -56,32 +75,59 @@
366 # Create a branch to a tag to get the release version.
367 branch = 'tags/{0}'.format(version)
368
369- # Get the package architecture
370+ cert_file = '/srv/kubernetes/apiserver.crt'
371+ key_file = '/srv/kubernetes/apiserver.key'
372+ # When the cert or key changes we need to restart the apiserver.
373+ if config.changed('apiserver-cert') or config.changed('apiserver-key'):
374+ hookenv.log('Certificate or key has changed.')
375+ if not certificate or not key:
376+ generate_cert(key=key_file, cert=cert_file)
377+ else:
378+ hookenv.log('Writing new certificate and key to server.')
379+ with open(key_file, 'w') as file:
380+ file.write(key)
381+ with open(cert_file, 'w') as file:
382+ file.write(certificate)
383+ # Restart apiserver as the certificate or key has changed.
384+ if host.service_running('apiserver'):
385+ host.service_restart('apiserver')
386+ # Reload nginx because it proxies https to apiserver.
387+ if host.service_running('nginx'):
388+ host.service_reload('nginx')
389+
390+ if config.changed('username') or config.changed('password'):
391+ hookenv.log('Username or password changed, creating authentication.')
392+ basic_auth(username, username, password)
393+ if host.service_running('apiserver'):
394+ host.service_restart('apiserver')
395+
396+ # Get package architecture, rather than arch from the kernel (uname -m).
397 arch = subprocess.check_output(['dpkg', '--print-architecture']).strip()
398
399 if not branch:
400 output_path = charm_dir / 'files/output'
401- installer = KubernetesInstaller(arch, version, output_path)
402+ kube_installer = KubernetesInstaller(arch, version, output_path)
403 else:
404
405 # Build the kuberentes binaries from source on the units.
406- kubernetes_dir = path('/opt/kubernetes')
407+ kubernetes_dir = Path('/opt/kubernetes')
408
409 # Construct the path to the binaries using the arch.
410 output_path = kubernetes_dir / '_output/local/bin/linux' / arch
411- installer = KubernetesInstaller(arch, version, output_path)
412+ kube_installer = KubernetesInstaller(arch, version, output_path)
413
414 if not kubernetes_dir.exists():
415- print('The source dir {0} does not exist'.format(kubernetes_dir))
416- print('Was the kubernetes code cloned during install?')
417+ message = 'The kubernetes source directory {0} does not exist. ' \
418+ 'Was the kubernetes repository cloned during the install?'
419+ print(message.format(kubernetes_dir))
420 exit(1)
421
422 # Change to the kubernetes directory (git repository).
423 with kubernetes_dir:
424-
425 # Create a command to get the current branch.
426 git_branch = 'git branch | grep "\*" | cut -d" " -f2'
427- current_branch = subprocess.check_output(git_branch, shell=True).strip() # noqa
428+ current_branch = subprocess.check_output(git_branch, shell=True)
429+ current_branch = current_branch.strip()
430 print('Current branch: ', current_branch)
431 # Create the path to a file to indicate if the build was broken.
432 broken_build = charm_dir / '.broken_build'
433@@ -90,12 +136,12 @@
434 print('Last build failed: ', last_build_failed)
435 # Rebuild if current version is different or last build failed.
436 if current_branch != version or last_build_failed:
437- installer.build(branch)
438+ kube_installer.build(branch)
439 if not output_path.isdir():
440 broken_build.touch()
441
442 # Create the symoblic links to the right directories.
443- installer.install()
444+ kube_installer.install()
445
446 relation_changed()
447
448@@ -109,10 +155,10 @@
449 # Check required keys
450 for k in ('etcd_servers',):
451 if not template_data.get(k):
452- print "Missing data for", k, template_data
453+ print 'Missing data for', k, template_data
454 return
455
456- print "Running with\n", template_data
457+ print 'Running with\n', template_data
458
459 # Render and restart as needed
460 for n in ('apiserver', 'controller-manager', 'scheduler'):
461@@ -143,7 +189,7 @@
462
463
464 def notify_minions():
465- print("Notify minions.")
466+ print('Notify minions.')
467 config = hookenv.config()
468 for r in hookenv.relation_ids('minions-api'):
469 hookenv.relation_set(
470@@ -151,7 +197,49 @@
471 hostname=hookenv.unit_private_ip(),
472 port=8080,
473 version=config['version'])
474- print("Notified minions of version " + config['version'])
475+ print('Notified minions of version ' + config['version'])
476+
477+
478+def basic_auth(name, id, pwd=None, file='/srv/kubernetes/basic-auth.csv'):
479+ """
480+ Create a basic authentication file for kubernetes. The file is a csv file
481+ with 3 columns: password, user name, user id. From the Kubernetes docs:
482+ The basic auth credentials last indefinitely, and the password cannot be
483+ changed without restarting apiserver.
484+ """
485+ if not pwd:
486+ import random
487+ import string
488+ alphanumeric = string.ascii_letters + string.digits
489+ pwd = ''.join(random.choice(alphanumeric) for _ in range(16))
490+ lines = []
491+ auth_file = Path(file)
492+ if auth_file.isfile():
493+ lines = auth_file.lines()
494+ for line in lines:
495+ target = ',{0},{1}'.format(name, id)
496+ if target in line:
497+ lines.remove(line)
498+ auth_line = '{0},{1},{2}'.format(pwd, name, id)
499+ lines.append(auth_line)
500+ auth_file.write_lines(lines)
501+
502+
503+def generate_cert(common_name=None,
504+ key='/srv/kubernetes/apiserver.key',
505+ cert='/srv/kubernetes/apiserver.crt'):
506+ """
507+ Create the certificate and key for the Kubernetes tls enablement.
508+ """
509+ hookenv.log('Generating new self signed certificate and key', 'INFO')
510+ if not common_name:
511+ common_name = hookenv.unit_get('public-address')
512+ if os.path.isfile(key) or os.path.isfile(cert):
513+ hookenv.log('Overwriting the existing certificate or key', 'WARNING')
514+ hookenv.log('Generating certificate for {0}'.format(common_name), 'INFO')
515+ # Generate the self signed certificate with the public address as CN.
516+ # https://pythonhosted.org/charmhelpers/api/charmhelpers.contrib.ssl.html
517+ ssl.generate_selfsigned(key, cert, cn=common_name)
518
519
520 def get_template_data():
521@@ -159,18 +247,21 @@
522 config = hookenv.config()
523 version = config['version']
524 template_data = {}
525- template_data['etcd_servers'] = ",".join([
526- "http://%s:%s" % (s[0], s[1]) for s in sorted(
527+ template_data['etcd_servers'] = ','.join([
528+ 'http://%s:%s' % (s[0], s[1]) for s in sorted(
529 get_rel_hosts('etcd', rels, ('hostname', 'port')))])
530- template_data['minions'] = ",".join(get_rel_hosts('minions-api', rels))
531+ template_data['minions'] = ','.join(get_rel_hosts('minions-api', rels))
532+ private_ip = hookenv.unit_private_ip()
533+ public_ip = hookenv.unit_public_ip()
534+ template_data['api_public_address'] = _bind_addr(public_ip)
535+ template_data['api_private_address'] = _bind_addr(private_ip)
536+ template_data['bind_address'] = '127.0.0.1'
537+ template_data['api_http_uri'] = 'http://%s:%s' % (private_ip, 8080)
538+ template_data['api_https_uri'] = 'https://%s:%s' % (private_ip, 6443)
539
540- template_data['api_bind_address'] = _bind_addr(hookenv.unit_private_ip())
541- template_data['bind_address'] = "127.0.0.1"
542- template_data['api_server_address'] = "http://%s:%s" % (
543- hookenv.unit_private_ip(), 8080)
544 arch = subprocess.check_output(['dpkg', '--print-architecture']).strip()
545
546- template_data['web_uri'] = "/kubernetes/%s/local/bin/linux/%s/" % (version,
547+ template_data['web_uri'] = '/kubernetes/%s/local/bin/linux/%s/' % (version,
548 arch)
549 if version == 'local':
550 template_data['alias'] = hookenv.charm_dir() + '/files/output/'
551@@ -187,7 +278,7 @@
552 try:
553 return socket.gethostbyname(addr)
554 except socket.error:
555- raise ValueError("Could not resolve private address")
556+ raise ValueError('Could not resolve address %s' % addr)
557
558
559 def _encode(d):
560@@ -209,7 +300,7 @@
561 return hosts
562
563
564-def render_file(name, data, src_suffix="upstart.tmpl", tgt_path=None):
565+def render_file(name, data, src_suffix='upstart.tmpl', tgt_path=None):
566 tmpl_path = os.path.join(
567 os.environ.get('CHARM_DIR'), 'files', '%s.%s' % (name, src_suffix))
568
569@@ -230,5 +321,6 @@
570 fh.write(rendered)
571 return True
572
573+
574 if __name__ == '__main__':
575 hooks.execute(sys.argv)
576
577=== modified file 'hooks/install.py'
578--- hooks/install.py 2015-09-28 15:23:30 +0000
579+++ hooks/install.py 2015-10-09 16:18:58 +0000
580@@ -1,13 +1,27 @@
581 #!/usr/bin/env python
582
583+# Copyright 2015 The Kubernetes Authors All rights reserved.
584+#
585+# Licensed under the Apache License, Version 2.0 (the "License");
586+# you may not use this file except in compliance with the License.
587+# You may obtain a copy of the License at
588+#
589+# http://www.apache.org/licenses/LICENSE-2.0
590+#
591+# Unless required by applicable law or agreed to in writing, software
592+# distributed under the License is distributed on an "AS IS" BASIS,
593+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
594+# See the License for the specific language governing permissions and
595+# limitations under the License.
596+
597 import setup
598 setup.pre_install()
599 import subprocess
600
601+from charmhelpers import fetch
602 from charmhelpers.core import hookenv
603-from charmhelpers import fetch
604 from charmhelpers.fetch import archiveurl
605-from path import path
606+from path import Path
607
608
609 def install():
610@@ -16,18 +30,24 @@
611 download_go()
612
613 hookenv.log('Adding kubernetes and go to the path')
614-
615+ address = hookenv.unit_private_ip()
616 strings = [
617 'export GOROOT=/usr/local/go\n',
618 'export PATH=$PATH:$GOROOT/bin\n',
619- 'export KUBE_MASTER_IP=0.0.0.0\n',
620- 'export KUBERNETES_MASTER=http://$KUBE_MASTER_IP\n',
621- ]
622+ 'export KUBERNETES_MASTER=http://{0}:8080\n'.format(address),
623+ ]
624 update_rc_files(strings)
625 hookenv.log('Downloading kubernetes code')
626 clone_repository()
627
628+ # Create the directory to store the keys and auth files.
629+ srv = Path('/srv/kubernetes')
630+ if not srv.isdir():
631+ srv.makedirs_p()
632+
633 hookenv.open_port(8080)
634+ hookenv.open_port(6443)
635+ hookenv.open_port(443)
636
637 hookenv.log('Install complete')
638
639@@ -51,7 +71,7 @@
640 """
641
642 repository = 'https://github.com/kubernetes/kubernetes.git'
643- kubernetes_directory = path('/opt/kubernetes')
644+ kubernetes_directory = Path('/opt/kubernetes')
645 # Since we can not clone twice, check for the directory and remove it.
646 if kubernetes_directory.isdir():
647 kubernetes_directory.rmtree_p()
648@@ -69,8 +89,13 @@
649 """
650 hookenv.log('Installing Debian packages')
651 # Create the list of packages to install.
652- apt_packages = ['build-essential', 'git', 'make', 'nginx', 'python-pip',
653- 'docker.io']
654+ apt_packages = ['apache2-utils',
655+ 'build-essential',
656+ 'docker.io',
657+ 'git',
658+ 'make',
659+ 'nginx',
660+ 'python-pip', ]
661 fetch.apt_install(fetch.filter_installed_packages(apt_packages))
662
663
664@@ -80,7 +105,7 @@
665 make interfacing with the api easier. (see: kubectrl docs)
666 """
667 if not rc_files:
668- rc_files = [path('/home/ubuntu/.bashrc'), path('/root/.bashrc')]
669+ rc_files = [Path('/home/ubuntu/.bashrc'), Path('/root/.bashrc')]
670
671 for rc_file in rc_files:
672 lines = rc_file.lines()
673
674=== modified file 'hooks/kubernetes_installer.py'
675--- hooks/kubernetes_installer.py 2015-07-17 20:01:20 +0000
676+++ hooks/kubernetes_installer.py 2015-10-09 16:18:58 +0000
677@@ -1,9 +1,23 @@
678 #!/usr/bin/env python
679
680+# Copyright 2015 The Kubernetes Authors All rights reserved.
681+#
682+# Licensed under the Apache License, Version 2.0 (the "License");
683+# you may not use this file except in compliance with the License.
684+# You may obtain a copy of the License at
685+#
686+# http://www.apache.org/licenses/LICENSE-2.0
687+#
688+# Unless required by applicable law or agreed to in writing, software
689+# distributed under the License is distributed on an "AS IS" BASIS,
690+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
691+# See the License for the specific language governing permissions and
692+# limitations under the License.
693+
694 import os
695 import shlex
696 import subprocess
697-from path import path
698+from path import Path
699
700
701 def run(command, shell=False):
702@@ -32,7 +46,7 @@
703 'kubelet': 'kubelet'}
704 self.arch = arch
705 self.version = version
706- self.output_dir = path(output_dir)
707+ self.output_dir = Path(output_dir)
708
709 def build(self, branch):
710 """ Build kubernetes from a github repository using the Makefile. """
711@@ -74,7 +88,7 @@
712 print(make_what)
713 rc = subprocess.call(shlex.split(make_what), env=go_env)
714
715- def install(self, install_dir=path('/usr/local/bin')):
716+ def install(self, install_dir=Path('/usr/local/bin')):
717 """ Install kubernetes binary files from the output directory. """
718
719 if not install_dir.isdir():
720
721=== modified file 'hooks/setup.py'
722--- hooks/setup.py 2015-09-24 19:50:43 +0000
723+++ hooks/setup.py 2015-10-09 16:18:58 +0000
724@@ -1,5 +1,19 @@
725 #!/usr/bin/env python
726
727+# Copyright 2015 The Kubernetes Authors All rights reserved.
728+#
729+# Licensed under the Apache License, Version 2.0 (the "License");
730+# you may not use this file except in compliance with the License.
731+# You may obtain a copy of the License at
732+#
733+# http://www.apache.org/licenses/LICENSE-2.0
734+#
735+# Unless required by applicable law or agreed to in writing, software
736+# distributed under the License is distributed on an "AS IS" BASIS,
737+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
738+# See the License for the specific language governing permissions and
739+# limitations under the License.
740+
741
742 def pre_install():
743 """
744
745=== modified file 'unit_tests/kubernetes_installer_test.py'
746--- unit_tests/kubernetes_installer_test.py 2015-09-24 19:50:43 +0000
747+++ unit_tests/kubernetes_installer_test.py 2015-10-09 16:18:58 +0000
748@@ -1,8 +1,21 @@
749 #!/usr/bin/env python
750
751+# Copyright 2015 The Kubernetes Authors All rights reserved.
752+#
753+# Licensed under the Apache License, Version 2.0 (the "License");
754+# you may not use this file except in compliance with the License.
755+# You may obtain a copy of the License at
756+#
757+# http://www.apache.org/licenses/LICENSE-2.0
758+#
759+# Unless required by applicable law or agreed to in writing, software
760+# distributed under the License is distributed on an "AS IS" BASIS,
761+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
762+# See the License for the specific language governing permissions and
763+# limitations under the License.
764+
765 from mock import patch
766 from mock import ANY
767-from path import path
768 from path import Path
769 import pytest
770 import subprocess
771@@ -25,7 +38,7 @@
772 assert output
773 assert 'kubernetes_installer.py' in output
774
775- invalid_directory = path('/not/a/real/directory')
776+ invalid_directory = Path('/not/a/real/directory')
777 assert not invalid_directory.exists()
778 invalid_command = 'ls {0}'.format(invalid_directory)
779 with pytest.raises(subprocess.CalledProcessError) as error:
780@@ -54,24 +67,24 @@
781 assert 'kubelet' in ki.aliases
782 assert ki.arch == 'i386'
783 assert ki.version == '3.0.1'
784- assert ki.output_dir == path('/tmp/does_not_exist')
785+ assert ki.output_dir == Path('/tmp/does_not_exist')
786
787 @patch('kubernetes_installer.run')
788 @patch('kubernetes_installer.subprocess.call')
789 def test_build(self, cmock, rmock):
790 """ Test the build method with master and non-master branches. """
791- directory = path('/tmp/kubernetes_installer_test/build')
792+ directory = Path('/tmp/kubernetes_installer_test/build')
793 ki = self.makeone('amd64', 'v99.00.11', directory)
794 assert not directory.exists(), 'The %s directory exists!' % directory
795 # Call the build method with "master" branch.
796 ki.build("master")
797 # TODO: run is called many times but mock only remembers last one.
798 rmock.assert_called_with('git reset --hard origin/master')
799-
800- # TODO: call is complex and hard to verify with mock, fix that.
801- # this isn't oding what we think it should b edoing, magic mock
802- # makes this tricky.
803- # list['foo', 'baz'], env = ANY
804+
805+ # TODO: call is complex and hard to verify with mock, fix that.
806+ # this is not doing what we think it should be doing, magic mock
807+ # makes this tricky.
808+ # list['foo', 'baz'], env = ANY
809 make_args = ['make', 'all', 'WHAT=cmd/kube-apiserver cmd/kubectl cmd/kube-controller-manager plugin/cmd/kube-scheduler cmd/kubelet cmd/kube-proxy'] # noqa
810 cmock.assert_called_once_with(make_args, env=ANY)
811
812@@ -79,7 +92,7 @@
813 @patch('kubernetes_installer.subprocess.call')
814 def test_schenanigans(self, cmock, rmock):
815 """ Test the build method with master and non-master branches. """
816- directory = path('/tmp/kubernetes_installer_test/build')
817+ directory = Path('/tmp/kubernetes_installer_test/build')
818 ki = self.makeone('amd64', 'v99.00.11', directory)
819 assert not directory.exists(), 'The %s directory exists!' % directory
820
821@@ -88,14 +101,13 @@
822 # TODO: run is called many times, but mock only remembers last one.
823 rmock.assert_called_with('git checkout -b v99.00.11 branch')
824 # TODO: call is complex and hard to verify with mock, fix that.
825- assert cmock.called
826-
827+ assert cmock.called
828
829 directory.rmtree_p()
830
831 def test_install(self):
832 """ Test the install method that it creates the correct links. """
833- directory = path('/tmp/kubernetes_installer_test/install')
834+ directory = Path('/tmp/kubernetes_installer_test/install')
835 ki = self.makeone('ppc64le', '1.2.3', directory)
836 assert not directory.exists(), 'The %s directory exits!' % directory
837 directory.makedirs_p()
838
839=== modified file 'unit_tests/test_install.py'
840--- unit_tests/test_install.py 2015-09-28 15:23:30 +0000
841+++ unit_tests/test_install.py 2015-10-09 16:18:58 +0000
842@@ -1,5 +1,19 @@
843 #!/usr/bin/env python
844
845+# Copyright 2015 The Kubernetes Authors All rights reserved.
846+#
847+# Licensed under the Apache License, Version 2.0 (the "License");
848+# you may not use this file except in compliance with the License.
849+# You may obtain a copy of the License at
850+#
851+# http://www.apache.org/licenses/LICENSE-2.0
852+#
853+# Unless required by applicable law or agreed to in writing, software
854+# distributed under the License is distributed on an "AS IS" BASIS,
855+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
856+# See the License for the specific language governing permissions and
857+# limitations under the License.
858+
859 from mock import patch, Mock, MagicMock
860 from path import Path
861 import pytest
862@@ -12,26 +26,27 @@
863 # Import the modules from the hook
864 import install
865
866+
867 class TestInstallHook():
868
869- @patch('install.path')
870+ @patch('install.Path')
871 def test_update_rc_files(self, pmock):
872 """
873 Test happy path on updating env files. Assuming everything
874 exists and is in place.
875 """
876- pmock.return_value.lines.return_value = ['line1', 'line2']
877+ pmock.return_value.lines.return_value = ['line1', 'line2']
878 install.update_rc_files(['test1', 'test2'])
879 pmock.return_value.write_lines.assert_called_with(['line1', 'line2',
880 'test1', 'test2'])
881
882- def test_update_rc_files_with_nonexistant_path(self):
883+ def test_update_rc_files_with_nonexistent_path(self):
884 """
885 Test an unhappy path if the bashrc/users do not exist.
886 """
887- p = [Path('/home/deadbeefdoesnotexist/.bashrc')]
888+ p = [Path('/home/deadbeefdoesnotexist/.bashrc')]
889 with pytest.raises(OSError) as exinfo:
890- install.update_rc_files(['test1','test2'], rc_files=p)
891+ install.update_rc_files(['test1', 'test2'], rc_files=p)
892
893 @patch('install.fetch')
894 @patch('install.hookenv')
895@@ -40,8 +55,13 @@
896 Verify we are calling the known essentials to build and syndicate
897 kubes.
898 """
899- pkgs = ['build-essential', 'git',
900- 'make', 'nginx', 'python-pip', 'docker.io']
901+ pkgs = ['apache2-utils',
902+ 'build-essential',
903+ 'docker.io',
904+ 'git',
905+ 'make',
906+ 'nginx',
907+ 'python-pip',]
908 install.install_packages()
909 hemock.log.assert_called_with('Installing Debian packages')
910 ftmock.filter_installed_packages.assert_called_with(pkgs)
911@@ -49,22 +69,22 @@
912 @patch('install.archiveurl.ArchiveUrlFetchHandler')
913 def test_go_download(self, aumock):
914 """
915- Test that we are actually handing off to charm-helpers to
916- download a specific archive of Go. This is non-configurable so
917- its reasonably safe to assume we're going to always do this,
918- and when it changes we shall curse the brittleness of this test.
919+ Test that we are actually handing off to charm-helpers to
920+ download a specific archive of Go. This is non-configurable so
921+ its reasonably safe to assume we're going to always do this,
922+ and when it changes we shall curse the brittleness of this test.
923 """
924 ins_mock = aumock.return_value.install
925 install.download_go()
926- url = 'https://storage.googleapis.com/golang/go1.4.2.linux-amd64.tar.gz'
927- sha1='5020af94b52b65cc9b6f11d50a67e4bae07b0aff'
928+ url = 'https://storage.googleapis.com/golang/go1.4.2.linux-amd64.tar.gz' # noqa
929+ sha1 = '5020af94b52b65cc9b6f11d50a67e4bae07b0aff'
930 ins_mock.assert_called_with(url, '/usr/local', sha1, 'sha1')
931
932 @patch('install.subprocess')
933 def test_clone_repository(self, spmock):
934 """
935- We're not using a unit-tested git library - so ensure our subprocess
936- call is consistent. If we change this, we want to know we've broken it.
937+ We're not using a unit-tested git library - so ensure our subprocess
938+ call is consistent. If we change this, we want to know we've broken it.
939 """
940 install.clone_repository()
941 repo = 'https://github.com/kubernetes/kubernetes.git'
942@@ -75,23 +95,22 @@
943 @patch('install.download_go')
944 @patch('install.clone_repository')
945 @patch('install.update_rc_files')
946+ @patch('install.Path')
947 @patch('install.hookenv')
948- def test_install_main(self, hemock, urmock, crmock, dgmock, ipmock):
949+ def test_install_main(self, hemock, pmock, urmock, crmock, dgmock, ipmock):
950 """
951 Ensure the driver/main method is calling all the supporting methods.
952 """
953- strings = [
954- 'export GOROOT=/usr/local/go\n',
955- 'export PATH=$PATH:$GOROOT/bin\n',
956- 'export KUBE_MASTER_IP=0.0.0.0\n',
957- 'export KUBERNETES_MASTER=http://$KUBE_MASTER_IP\n',
958- ]
959-
960 install.install()
961- # magic mock overwrites method signatures, this isn't doing what
962- # we think it should be doing
963- # crmock.assert_called_once()
964- # dgmock.assert_called_once()
965- # crmock.assert_called_once()
966- urmock.assert_called_with(strings)
967- hemock.open_port.assert_called_with(8080)
968+
969+ assert(ipmock.called)
970+ assert(dgmock.called)
971+ assert(crmock.called)
972+ assert(urmock.called)
973+
974+ assert(pmock.called)
975+ pmock.assert_called_with('/srv/kubernetes')
976+
977+ hemock.open_port.assert_any_call(443)
978+ hemock.open_port.assert_any_call(8080)
979+ hemock.open_port.assert_any_call(6443)

Subscribers

People subscribed via source and target branches