def set_current(timestamp): """ Set an app directory to the currently live app by creating a symlink as specified in config """ app_path = path.join(install_parent, timestamp) log( "Linking live path '{live}' to app dir: {app_dir}".format( app_dir=app_path, live=live_link_path ) ) run(sh.rm, live_link_path, force=True) run(sh.ln, app_path, live_link_path, symbolic=True) site_to_enable = path.join(sites_available_dir, timestamp) site_links = sh.glob(path.join(sites_enabled_dir, '*')) # Delete existing site links run(sh.rm, site_links, f=True) # Add our link into sites-enabled run(sh.ln, site_to_enable, sites_enabled_path, s=True) # Restart apache restart()
def extract_app_files(url, timestamp): """ Extract the app zip file into an install directory (specified in config) """ install_path = path.join(install_parent, timestamp) # Unless install dir already exists, extract it if not (path.exists(install_path) and listdir(install_path)): tempfile_path = '/tmp/wsgi-app-package.tgz' create_dir(install_path) log( "Extracting '{url}' to '{dir}'".format( url=url, dir=install_path ) ) run(sh.rm, tempfile_path, f=True) urlretrieve(url, tempfile_path) # Extract files into install dir run( sh.tar, file=tempfile_path, directory=install_path, strip="1", z=True, x=True ) return install_path
def setup_http_server(): public_address = sh.unit_get('public-address').rstrip() log('setting up "http-server" with address "{0}"'.format(public_address)) sh.relation_set('hostname={0}'.format(public_address)) restart()
def run(sh_function, *args, **kwargs): """ Run command with logging """ output = sh_function(*args, **kwargs) if output: log(str(output))
def install_packages(packages): """ Install a list of packages if the list isn't empty and log that we've done so """ if packages: log("Installing apt packages: {0}".format(packages)) run(sh.apt_get.install, packages.split(), y=True)
def create_dir(dir_path): """ Create a directoy and all parents, if it doesn't already exists and log that we've done so """ if not path.exists(dir_path): log('Creating directory: {0}'.format(dir_path)) run(sh.mkdir, dir_path, p=True)
def wsgi_relation_broken(): """ When WSGI relation (e.g.: gunicorn) goes away """ log('Hook function: wsgi_relation_broken') config_data = ansible_config() close_port(config_data['listen_port'])
def wsgi_relation(): """ Setup relation for serving the WSGI file (e.g. gunicorn) Sets a whole bunch of relation settings including log file locations and environent variables """ log('Hook function: wsgi_relation') config_data = ansible_config() log_file_path = path.join( config_data['log_dir'], config_data['app_label'] + '-access.log' ) env_dictionary = parse_json_file(env_file_path) env_list = ["{0}={1}".format(k, v) for k, v in env_dictionary.items()] env_string = " ".join(env_list) wsgi_relation_settings = { 'project_name': config_data.get('app_label', ''), 'working_dir': path.join(config_data.get('code_dir', ''), 'current'), 'python_path': config_data.get('python_path', ''), 'wsgi_user': config_data.get('wsgi_user', ''), 'wsgi_group': config_data.get('wsgi_group', ''), 'port': config_data.get('listen_port', ''), 'wsgi_access_logfile': log_file_path, 'wsgi_wsgi_file': config_data.get('wsgi_application', ''), 'wsgi_extra': '--error-logfile=' + log_file_path, 'env_extra': env_string, 'timestamp': datetime.now().isoformat() } # Set these settings on any wsgi-file relations for relation_id in relation_ids('wsgi-file'): log( 'Setting wsgi-file relation settings: ' + str(wsgi_relation_settings) ) relation_set( relation_id=relation_id, **wsgi_relation_settings ) # Relation changed - re-run update target update_target() open_port(config_data['listen_port'])
def save_environment_variables_string(env_vars): log('setting environment variable: {0}'.format(env_vars)) export_string = 'export {0}\n'.format(env_vars) already_set = False with open(scriptrc_path) as scriptrc_read: already_set = export_string in scriptrc_read.read() # Save into scripts/scriptrc if not already_set: with open(scriptrc_path, 'a') as scriptrc: scriptrc.write(export_string)
def run_command(command=None): if command is None: return False log('Running Command "%s"' % command) try: return subprocess.check_output( command, shell=True, stderr=subprocess.STDOUT).decode('utf-8').replace('\n', '') except subprocess.CalledProcessError as e: log('Error running "%s" : %s' % (command, e.output)) return False
def unlink_database(variable_name): """ Remove "DATABASE_URL" environment variable """ log('Function: unlink_database') env_vars = parse_json_file(env_file_path) if 'DATABASE_URL' in env_vars: del env_vars['DATABASE_URL'] save_to_json_file(env_file_path, env_vars) # Reset wsgi relation settings wsgi_relation()
def unlink_webservice(): """ Remove "WEBSERVICE_URL" environment variable """ log('Function: unlink_database') env_vars = parse_json_file(env_file_path) if 'WEBSERVICE_URL' in env_vars: del env_vars['WEBSERVICE_URL'] save_to_json_file(env_file_path, env_vars) # Reset wsgi relation settings wsgi_relation()
def mongodb_relation(): """ Setup relation to a mongodb database (will replace any other database relations) """ log('Hook function: mongodb_relation') host = relation_get("hostname") if 'mongodb' in relations() and host: link_database( scheme='mongodb', database_host=host, port=relation_get("port"), variable_name='MONGO_URL' )
def copy_ssl_certificates(timestamp): """ Copy either the default self-signed certificate or the provided custom ones into /etc/ssl/certs/wsgi-app.* Return the locations of the created files """ certs_dir = '/etc/ssl/certs' keyfile_path = path.join( certs_dir, 'wsgi-app.{0}.key'.format(timestamp) ) certificate_path = path.join( certs_dir, 'wsgi-app.{0}.crt'.format(timestamp) ) custom_keyfile = config('ssl_keyfile') custom_certificate = config('ssl_certificate') create_dir(certs_dir) log('Saving certificate files') if custom_keyfile and custom_certificate: keyfile_content = b64decode(custom_keyfile) certificate_content = b64decode(custom_certificate) with open(keyfile_path, 'w') as keyfile: keyfile.write(keyfile_content) with open(certificate_path, 'w') as certificate: certificate.write(certificate_content) else: config_path = path.join(charm_dir, 'ssl/wsgi-app.conf') run( sh.openssl.req, "-new", "-nodes", "-x509", "-newkey", "rsa:2048", "-days", "365", "-keyout", keyfile_path, "-out", certificate_path, "-config", config_path ) return (keyfile_path, certificate_path)
def webservice_relation(): """ Create "WEBSERVICE_URL" environment variable from relation """ log('Function: webservice_relation') http_protocol = relation_get('http_protocol') or 'http' address = relation_get('private-address') hostname = relation_get('hostname') or address # If hostname is IP address or FQDN, use it # otherwise use private_address ip_regex = re.compile( ( r"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.)" r"{3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$" ) ) hostname_regex = re.compile( ( r"^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)+" r"([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$" ) ) if hostname_regex.match(hostname) or ip_regex.match(hostname): domain = hostname else: domain = address webservice_url = build_url( scheme=http_protocol, domain=domain, port=relation_get("port") ) update_property_in_json_file( env_file_path, 'WEBSERVICE_URL', webservice_url ) # Relation changed - re-run update target update_target() # Reset wsgi relation settings wsgi_relation()
def install(): """ - Install ansible - Create the cache directory The hook() helper decorating this install function ensures that after this function finishes, any tasks in the playbook tagged with install are executed. """ log('Hook function: install') # Recreate cache directory if path.isdir(cache_dir): rmtree(cache_dir) mkdir(cache_dir) update_env()
def bridge_setup(cni): status_set('maintenance', 'Setting up new interface') interface = get_config('gateway-physical-interface') if interface == 'none' or interface == None: op = run_command('ip route | grep default').split(' ') interface = op[4] store('old_interface', interface) store('new_interface', 'br%s' % (interface)) op = run_command('ovn-k8s-util nics-to-bridge %s' % (interface)) log('Bridge create output: %s' % (op)) op = run_command('dhclient -r br%s' % (interface)) op = run_command('dhclient br%s' % (interface)) status_set('maintenance', 'Waiting to initialise master') set_state('bridge.setup.done')
def pgsql_relation(): """ Setup relation to a postgresql database (will replace any other database relations) """ log('Hook function: pgsql_relation') host = relation_get("host") if 'pgsql' in relations() and host: link_database( scheme='postgresql', database_host=host, port=relation_get("port"), username=relation_get("user"), password=relation_get("password"), database_name=relation_get("database") )
def pip_dependencies(app_path): """ Install pip dependencies from requirements file and from the dependencies directory """ # Read paths from config requirements_path = path.join(app_path, config('pip_requirements_path')) dependencies_path = path.join(app_path, config('pip_cache_path')) if path.isfile(requirements_path): # Install from requirements file if possible log("Installing pip requirements from {0}".format(requirements_path)) # Install dependencies in dependencies directory run( sh.pip.install, r=requirements_path, find_links=dependencies_path, # Path to local package files no_index=config('pip_no_index') # Use PyPi? )
def install_gateway(cni): status_set('maintenance', 'Initialising gateway') run_command( 'sudo ovs-vsctl set Open_vSwitch . external_ids:k8s-api-server="0.0.0.0:8080"' ) run_command( 'git clone https://github.com/openvswitch/ovn-kubernetes /tmp/ovn-kubernetes' ) os.chdir('/tmp/ovn-kubernetes') run_command('sudo pip2 install .') old_interface = get_interface(old=True) new_interface = get_interface(old=False) op = run_command('ifconfig %s | grep "inet addr:"' % (new_interface)) br_ip = op.lstrip().split()[1].replace('addr:', '') gateway_ip = run_command('ip route | grep default').split(' ')[2] hostname = run_command('hostname') op = run_command('ovn-k8s-overlay gateway-init \ --cluster-ip-subnet="192.168.0.0/16" \ --bridge-interface %s \ --physical-ip %s/32 \ --node-name="%s-gateway" \ --default-gw %s' % (new_interface, br_ip, hostname, gateway_ip)) log('Gateway init output: %s' % (op)) op = run_command('ovn-k8s-gateway-helper --physical-bridge=%s \ --physical-interface=%s --pidfile --detach' % (new_interface, old_interface)) log('Gateway Helper start: %s' % (op)) status_set('active', 'Master subnet : 192.168.1.0/24') set_state('gateway.installed')
def update_target(): """ Run the "update-charm" make target within the project """ log('Hook function: update_target') config_data = ansible_config() required_configs = [ 'build_label', 'archive_filename', 'current_code_dir', 'update_make_target' ] # Check all required configs are set if ( items_are_not_empty(config_data, required_configs) and path.isdir(config_data['current_code_dir']) ): # Ensure make is installed apt_output = sh.apt_get.install('make') log('Installed make:') log(str(apt_output)) env_vars = parse_json_file(env_file_path) # Execute make target with all environment variables make_output = sh.make( config_data['update_make_target'], directory=path.join(config_data['current_code_dir']), _env=env_vars ) log('Make output:') log(str(make_output))
def can_connect(url): """ Check whether we can connect to a URL and log the result """ log("Checking connection to: {0}".format(url)) success = True try: urlopen(url, timeout=1) log("... can connect") except URLError: log("... can't connect") success = False return success
def initialize_vault(): log("Running initialize_vault now") set_state('vault.initialized')
def restart(): service_restart("apache2") log('Restarted apache')