mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 13:50:01 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			250 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			250 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/env python
 | |
| 
 | |
| # Copyright 2015 The Kubernetes Authors.
 | |
| #
 | |
| # 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.
 | |
| 
 | |
| import os
 | |
| import socket
 | |
| import subprocess
 | |
| 
 | |
| from charms import layer
 | |
| from charms.reactive import when, when_any, when_not
 | |
| from charms.reactive import set_state, remove_state
 | |
| from charms.reactive import hook
 | |
| from charmhelpers.core import hookenv
 | |
| from charmhelpers.core import host
 | |
| from charmhelpers.contrib.charmsupport import nrpe
 | |
| from charms.reactive.helpers import data_changed
 | |
| 
 | |
| from charms.layer import nginx
 | |
| from charms.layer import tls_client
 | |
| 
 | |
| from subprocess import Popen
 | |
| from subprocess import PIPE
 | |
| from subprocess import STDOUT
 | |
| from subprocess import CalledProcessError
 | |
| 
 | |
| 
 | |
| apilb_nginx = """/var/log/nginx.*.log {
 | |
|     daily
 | |
|     missingok
 | |
|     rotate 14
 | |
|     compress
 | |
|     delaycompress
 | |
|     notifempty
 | |
|     create 0640 www-data adm
 | |
|     sharedscripts
 | |
|     prerotate
 | |
|         if [ -d /etc/logrotate.d/httpd-prerotate ]; then \\
 | |
|             run-parts /etc/logrotate.d/httpd-prerotate; \\
 | |
|         fi \\
 | |
|     endscript
 | |
|     postrotate
 | |
|         invoke-rc.d nginx rotate >/dev/null 2>&1
 | |
|     endscript
 | |
| }"""
 | |
| 
 | |
| 
 | |
| def get_ingress_address(relation):
 | |
|     try:
 | |
|         network_info = hookenv.network_get(relation.relation_name)
 | |
|     except NotImplementedError:
 | |
|         network_info = []
 | |
| 
 | |
|     if network_info and 'ingress-addresses' in network_info:
 | |
|         # just grab the first one for now, maybe be more robust here?
 | |
|         return network_info['ingress-addresses'][0]
 | |
|     else:
 | |
|         # if they don't have ingress-addresses they are running a juju that
 | |
|         # doesn't support spaces, so just return the private address
 | |
|         return hookenv.unit_get('private-address')
 | |
| 
 | |
| 
 | |
| @when('certificates.available', 'website.available')
 | |
| def request_server_certificates(tls, website):
 | |
|     '''Send the data that is required to create a server certificate for
 | |
|     this server.'''
 | |
|     # Use the public ip of this unit as the Common Name for the certificate.
 | |
|     common_name = hookenv.unit_public_ip()
 | |
|     # Create SANs that the tls layer will add to the server cert.
 | |
|     sans = [
 | |
|         hookenv.unit_public_ip(),
 | |
|         get_ingress_address(website),
 | |
|         socket.gethostname(),
 | |
|     ]
 | |
|     # maybe they have extra names they want as SANs
 | |
|     extra_sans = hookenv.config('extra_sans')
 | |
|     if extra_sans and not extra_sans == "":
 | |
|         sans.extend(extra_sans.split())
 | |
|     # Create a path safe name by removing path characters from the unit name.
 | |
|     certificate_name = hookenv.local_unit().replace('/', '_')
 | |
|     # Request a server cert with this information.
 | |
|     tls.request_server_cert(common_name, sans, certificate_name)
 | |
| 
 | |
| 
 | |
| @when('config.changed.extra_sans', 'certificates.available',
 | |
|       'website.available')
 | |
| def update_certificate(tls, website):
 | |
|     # Using the config.changed.extra_sans flag to catch changes.
 | |
|     # IP changes will take ~5 minutes or so to propagate, but
 | |
|     # it will update.
 | |
|     request_server_certificates(tls, website)
 | |
| 
 | |
| 
 | |
| @when('certificates.server.cert.available',
 | |
|       'nginx.available', 'tls_client.server.certificate.written')
 | |
| def kick_nginx(tls):
 | |
|     # we are just going to sighup it, but still want to avoid kicking it
 | |
|     # without need
 | |
|     if data_changed('cert', tls.get_server_cert()):
 | |
|         # certificate changed, so sighup nginx
 | |
|         hookenv.log("Certificate information changed, sending SIGHUP to nginx")
 | |
|         host.service_restart('nginx')
 | |
|     tls_client.reset_certificate_write_flag('server')
 | |
| 
 | |
| 
 | |
| @when('config.changed.port')
 | |
| def close_old_port():
 | |
|     config = hookenv.config()
 | |
|     old_port = config.previous('port')
 | |
|     if not old_port:
 | |
|         return
 | |
|     try:
 | |
|         hookenv.close_port(old_port)
 | |
|     except CalledProcessError:
 | |
|         hookenv.log('Port %d already closed, skipping.' % old_port)
 | |
| 
 | |
| 
 | |
| def maybe_write_apilb_logrotate_config():
 | |
|     filename = '/etc/logrotate.d/apilb_nginx'
 | |
|     if not os.path.exists(filename):
 | |
|         # Set log rotation for apilb log file
 | |
|         with open(filename, 'w+') as fp:
 | |
|             fp.write(apilb_nginx)
 | |
| 
 | |
| 
 | |
| @when('nginx.available', 'apiserver.available',
 | |
|       'certificates.server.cert.available')
 | |
| def install_load_balancer(apiserver, tls):
 | |
|     ''' Create the default vhost template for load balancing '''
 | |
|     # Get the tls paths from the layer data.
 | |
|     layer_options = layer.options('tls-client')
 | |
|     server_cert_path = layer_options.get('server_certificate_path')
 | |
|     cert_exists = server_cert_path and os.path.isfile(server_cert_path)
 | |
|     server_key_path = layer_options.get('server_key_path')
 | |
|     key_exists = server_key_path and os.path.isfile(server_key_path)
 | |
|     # Do both the key and certificate exist?
 | |
|     if cert_exists and key_exists:
 | |
|         # At this point the cert and key exist, and they are owned by root.
 | |
|         chown = ['chown', 'www-data:www-data', server_cert_path]
 | |
| 
 | |
|         # Change the owner to www-data so the nginx process can read the cert.
 | |
|         subprocess.call(chown)
 | |
|         chown = ['chown', 'www-data:www-data', server_key_path]
 | |
| 
 | |
|         # Change the owner to www-data so the nginx process can read the key.
 | |
|         subprocess.call(chown)
 | |
| 
 | |
|         port = hookenv.config('port')
 | |
|         hookenv.open_port(port)
 | |
|         services = apiserver.services()
 | |
|         nginx.configure_site(
 | |
|                 'apilb',
 | |
|                 'apilb.conf',
 | |
|                 server_name='_',
 | |
|                 services=services,
 | |
|                 port=port,
 | |
|                 server_certificate=server_cert_path,
 | |
|                 server_key=server_key_path,
 | |
|                 proxy_read_timeout=hookenv.config('proxy_read_timeout')
 | |
|         )
 | |
| 
 | |
|         maybe_write_apilb_logrotate_config()
 | |
|         hookenv.status_set('active', 'Loadbalancer ready.')
 | |
| 
 | |
| 
 | |
| @hook('upgrade-charm')
 | |
| def upgrade_charm():
 | |
|     maybe_write_apilb_logrotate_config()
 | |
| 
 | |
| 
 | |
| @when('nginx.available')
 | |
| def set_nginx_version():
 | |
|     ''' Surface the currently deployed version of nginx to Juju '''
 | |
|     cmd = 'nginx -v'
 | |
|     p = Popen(cmd, shell=True,
 | |
|               stdin=PIPE,
 | |
|               stdout=PIPE,
 | |
|               stderr=STDOUT,
 | |
|               close_fds=True)
 | |
|     raw = p.stdout.read()
 | |
|     # The version comes back as:
 | |
|     # nginx version: nginx/1.10.0 (Ubuntu)
 | |
|     version = raw.split(b'/')[-1].split(b' ')[0]
 | |
|     hookenv.application_version_set(version.rstrip())
 | |
| 
 | |
| 
 | |
| @when('website.available')
 | |
| def provide_application_details(website):
 | |
|     ''' re-use the nginx layer website relation to relay the hostname/port
 | |
|     to any consuming kubernetes-workers, or other units that require the
 | |
|     kubernetes API '''
 | |
|     website.configure(port=hookenv.config('port'))
 | |
| 
 | |
| 
 | |
| @when('loadbalancer.available')
 | |
| def provide_loadbalancing(loadbalancer):
 | |
|     '''Send the public address and port to the public-address interface, so
 | |
|     the subordinates can get the public address of this loadbalancer.'''
 | |
|     loadbalancer.set_address_port(hookenv.unit_get('public-address'),
 | |
|                                   hookenv.config('port'))
 | |
| 
 | |
| 
 | |
| @when('nrpe-external-master.available')
 | |
| @when_not('nrpe-external-master.initial-config')
 | |
| def initial_nrpe_config(nagios=None):
 | |
|     set_state('nrpe-external-master.initial-config')
 | |
|     update_nrpe_config(nagios)
 | |
| 
 | |
| 
 | |
| @when('nginx.available')
 | |
| @when('nrpe-external-master.available')
 | |
| @when_any('config.changed.nagios_context',
 | |
|           'config.changed.nagios_servicegroups')
 | |
| def update_nrpe_config(unused=None):
 | |
|     services = ('nginx',)
 | |
| 
 | |
|     hostname = nrpe.get_nagios_hostname()
 | |
|     current_unit = nrpe.get_nagios_unit_name()
 | |
|     nrpe_setup = nrpe.NRPE(hostname=hostname)
 | |
|     nrpe.add_init_service_checks(nrpe_setup, services, current_unit)
 | |
|     nrpe_setup.write()
 | |
| 
 | |
| 
 | |
| @when_not('nrpe-external-master.available')
 | |
| @when('nrpe-external-master.initial-config')
 | |
| def remove_nrpe_config(nagios=None):
 | |
|     remove_state('nrpe-external-master.initial-config')
 | |
| 
 | |
|     # List of systemd services for which the checks will be removed
 | |
|     services = ('nginx',)
 | |
| 
 | |
|     # The current nrpe-external-master interface doesn't handle a lot of logic,
 | |
|     # use the charm-helpers code for now.
 | |
|     hostname = nrpe.get_nagios_hostname()
 | |
|     nrpe_setup = nrpe.NRPE(hostname=hostname)
 | |
| 
 | |
|     for service in services:
 | |
|         nrpe_setup.remove_check(shortname=service)
 |