def write_config(self, name, server_names, proxy_pass, static_locations='', log_root=None, listen=80): """ Writes an Nginx server configuration file. This function writes a specific style of configuration, that seems to be somewhat common, where Nginx is used as a reverse-proxy for a locally-running (e.g., WSGI) server. :param name: identifies the server name; used to name the configuration file. :param server_names: :param proxy_pass: identifies the local proxy to which Nginx will pass requests. """ start_msg('----- Writing Nginx server configuration for "{0}":'.format(name)) # be sure the log directory exists. if log_root is None: log_root = path.join(cfg().deploy_root, name, 'logs') result = sudo('mkdir -p {0}'.format(log_root)) if result.failed: raise HaltError('Unable to create log directory: "{0}"'.format(log_root)) # generate and write the configuration file. server_config = _NGINX_SERVER_CONF.format(**locals()) dest = path.join(cfg().nginx_include_conf, '{name}.conf'.format(**locals())) message('Writing to file: "{0}"'.format(dest)) put_string(server_config, dest, use_sudo=True) succeed_msg('Wrote conf file for "{0}".'.format(name)) return self
def install(self, **kwargs): """ Installs and configures supervisor on the remote machine. Supervisor is installed via easy_install, a supervisor.conf file is created with an entry to include additional conf files in the directory: cfg().supervisord_include_conf. An init.d script is written, and if the program "chkconfig" exists, supervisor is added. :return: None """ start_msg('----- Install "supervisord" via "easy_install".') result = sudo("easy_install supervisor") if result.return_code != 0: HaltError('Failed to install "supervisord".') result = run("which supervisord") if result.return_code != 0: raise HaltError('Confusion: just installed "supervisord" but its not there?') message("Install successful; setting configuration.") # create the root supervisord.conf with an [include] entry that allows loading additional # from the folder: /etc/supervisor/conf.d/*.conf # we use this location for site-specific config (e.g., a site's [program] section). # start with the default conf file; the grep strips comment lines. result = run('echo_supervisord_conf | grep "^;.*$" -v') if result.failed: raise HaltError("Unable to retrieve default supervisord configuration.") # build the new configuration by just appending the include definition, # then write it to: /etc/supervisord.conf files = path.join(cfg().supervisord_include_conf, "*.conf") new_conf = "{result}\n\n[include]\nfiles = {files}\n".format(**locals()) put_string(new_conf, "/etc/supervisord.conf", use_sudo=True) # make sure the directory exists. result = sudo("mkdir -p {0}".format(cfg().supervisord_include_conf)) if result.failed: raise HaltError('Unable to create include dir: "{0}"'.format(cfg().supervisord_include_conf)) # finally write an init-script to /etc/init.d so supervisord gets run at startup. # TODO: write system-dependent script using "uname -s": OSX=Darwin, Amazon Linux AMI=Linux, ?? put_string(_INIT_SCRIPT_LINUX, "/etc/init.d/supervisor", use_sudo=True, mode=00755) # the Amazon Linux AMI uses chkconfig; the init.d script won't do the job by itself. # set supervisord so it can be managed by chkconfig; and turn on boot startup. # ubuntu (and Debian?) use UpStart or update-rc.d, so check them out. result = run("which chkconfig") if result.succeeded: message("System has chkconfig; configuring.") result = sudo("chkconfig --add supervisor") if result.failed: raise HaltError('"chkconfig --add supervisor" failed.') result = sudo("chkconfig supervisor on") if result.failed: raise HaltError('"chkconfig supervisor on" failed.') succeed_msg('"supervisord" is installed ({0}).'.format(result)) return self
def write_config(self, name, cmd, dir=None, log_root=None, env=None): """ Writes a supervisor [program] entry to a "conf" file. The conf file is named "<name>.conf", and is located in the directory identified by: cfg().supervisord_include_conf Calling this function is typically followed soon after by a call to reload_config(). :param name: specifies the program name. :param cmd: specifies the command to start the program. :param dir: specifies the directory to chdir to before executing command. default: no chdir. :param log_root: specifies the location for supervisor log file. default is 'logs' in the deployment root directory. :param env: specifies the child process environment. default: None. """ start_msg('----- Writing supervisor conf file for "{0}":'.format(name)) if dir is None: dir = "" if env is None: env = "" if not log_root: log_root = path.join(cfg().deploy_root, "logs") # first be sure the log directory exists. if not supervisor will fail to load the config. result = sudo("mkdir -p {0}".format(log_root)) if result.failed: raise HaltError('Unable to create log directory: "{0}"'.format(log_root)) # now write the entry. entry = ( "[program:{name}]\n" "command={cmd}\n" "directory={dir}\n" "user=nobody\n" "autostart=true\n" "autorestart=true\n" "stdout_logfile={log_root}/{name}.log\n" "redirect_stderr=True\n" "environment={env}\n".format(**locals()) ) dest = path.join(cfg().supervisord_include_conf, "{name}.conf".format(**locals())) message('Writing to file: "{0}"'.format(dest)) put_string(entry, dest, use_sudo=True) succeed_msg('Wrote conf file for "{0}".'.format(name)) return self
def check(self, **kwargs): """Determines if a key-pair has been generated for this host. :return: True if a key-pair exists, False otherwise. """ result = run('test -f {0}'.format(cfg().machine_key_file())) return result.succeeded
def install(self, **kwargs): # install Nginx using the package manager. self._simple.install() start_msg('----- Configuring "Nginx":') # verify that there's an init-script. result = run('test -f /etc/init.d/nginx') if result.failed: raise HaltError('Uh oh. Package manager did not install an Nginx init-script.') # write nginx.conf file. dest = path.join(cfg().nginx_conf, 'nginx.conf') message('Writing "nginx.conf"') put_string(_NGINX_CONF, dest, use_sudo=True) # the Amazon Linux AMI uses chkconfig; the init.d script won't do the job by itself. # set Nginx so it can be managed by chkconfig; and turn on boot startup. result = run('which chkconfig') if result.succeeded: message('System has chkconfig; configuring.') result = sudo('chkconfig --add nginx') if result.failed: raise HaltError('"chkconfig --add nginx" failed.') result = sudo('chkconfig nginx on') if result.failed: raise HaltError('"chkconfig nginx on" failed.') succeed_msg('Successfully installed and configured "Nginx".') return self
def build_instance(self, inst): # this seems to mitigate random SSH connection issues. disconnect_all() builder = Builder(self) with settings(host_string=inst.public_dns_name, user=self.user): build_name = builder.execute() inst.add_tag(cfg().fck_last_good_build, build_name)
def build_instance(self, inst): # this seems to mitigate random SSH connection issues. disconnect_all() builder = Builder(self) with self.and_instance(inst): build_name = builder.execute() inst.add_tag(cfg().fck_last_good_build, build_name)
def activate_instance(self, inst): # this seems to mitigate random SSH connection issues. disconnect_all() activator = Activator(self) with settings(host_string=inst.public_dns_name, user=self.user): build_name, port = activator.execute() if build_name is not None: inst.add_tag(cfg().fck_active_build, '{build_name} ({port})'.format(**locals()))
def generate_key_pair(): """ Generates a private/public key-pair for the host, stored in the ~/.ssh/ directory. :return: None """ if not has_key_pair(): result = run('ssh-keygen -b 1024 -t rsa -N "" -f {0}'.format(cfg().machine_key_file())) if result.failed: raise HaltError('Unable to generate key pair.')
def install(self, **kwargs): """Generates a private/public key-pair for the host, stored in the ~/.ssh/ directory. :return: self """ result = run('ssh-keygen -b 1024 -t rsa -N "" -f {0}'.format(cfg().machine_key_file())) if result.failed: raise HaltError('Unable to generate and install key pair.') succeed_msg('Key pair generated.') return self
def delete_server_config(name): start_msg('----- Deleting server configuration for "{0}":'.format(name)) # delete the file, but ignore any errors. config_name = '{name}.conf'.format(**locals()) result = sudo('rm -f {0}'.format(path.join(cfg().nginx_include_conf, config_name))) if result.failed: failed_msg('Ignoring failed attempt to delete configuration "{0}"'.format(config_name)) else: succeed_msg('Successfully deleted configuration "{0}".'.format(config_name))
def install_internal(name): start_msg('----- Running installation for: "{0}":'.format(name)) info = cfg().tool_info(name) if not info: raise HaltError('No tool information available for: "{0}"'.format(name)) cmd = info["yum"] if has_yum() else info["apt"] result = sudo(cmd, warn_only=True) if result.failed: raise HaltError('Failed to install: "{0}".'.format(name)) succeed_msg('Installed "{0}" successfully.'.format(name))
def delete_program_config(name): """ Deletes a program entry previously written by write_program_config(). :param name: specifies the program name used with write_program_config(). :return: None """ start_msg('----- Removing supervisor program entry for "{0}":'.format(name)) dest = path.join(cfg().supervisord_include_conf, '{name}.conf'.format(**locals())) result = sudo('rm -f {dest}'.format(**locals())) if result.failed: raise HaltError('Unable to remove entry.') succeed_msg('Removed successfully.')
def get_public_key(self): """Returns a string containing the public key portion of the host's key-pair. The key_pair() should have already been installed. :return: string containing the host's public key (empty if no key-pair has been generated). """ stream = StringIO() if self.check(): result = get('{0}.pub'.format(cfg().machine_key_file()), stream) if result.failed: raise HaltError('Unable to retrieve public key file.') return stream.getvalue()
def check_internal(name): start_msg('----- Checking for tool "{0}":'.format(name)) # if info for this tool isn't available, act as though the tool isn't present. info = cfg().tool_info(name) if not info: message('No information available for tool "{0}"'.format(name)) return False # if there's no "check" command, act as though the tool isn't present. cmd = info.get("check", None) if not cmd: message('No check command for tool "{0}"; assuming not installed'.format(name)) return False # otherwise run the check command. result = run(cmd, warn_only=True) if result.failed: failed_msg('Tool "{0}" is not installed.'.format(name)) return False succeed_msg('Tool "{0}" is installed.'.format(name)) return result.succeeded
def create(cls, name): info = cfg().tool_info(name) if not info: raise HaltError('There is no tool-definition for "{0}"'.format(name)) return SimpleTool(name, info)
def repos_root(self): return posixpath.join(cfg().deploy_root, self.name, cfg().repos_dir)
def builds_root(self): return posixpath.join(cfg().deploy_root, self.name, cfg().builds_dir)
def all_hosts_in_role(self, role_name): hosts = [] for inst in self._instances.itervalues(): if role_name == inst.tags.get(cfg().fck_role, None): hosts.append(inst) return hosts, self.get_role(role_name)
def get_host_in_role(self, role_name): for inst in self._instances.itervalues(): instance_role_name = inst.tags.get(cfg().fck_role, None) if instance_role_name == role_name: return inst, self.get_role(role_name) raise RuntimeError('No instance in role "{0}" is available.'.format(role_name))
def copy_file_from(from_user, from_host, from_path, to_path): result = run( 'scp -o StrictHostKeyChecking=no -i {key} {from_user}@{from_host}:{from_path} {to_path}' .format(key=cfg().machine_key_file(), **locals())) if result.failed: raise HaltError('Unable to copy from {0}:{1}'.format(from_host, from_path))
def deactivate_instance(self, inst): activator = Activator(self) with settings(host_string=inst.public_dns_name, user=self.user): activator.deactivate() inst.remove_tag(cfg().fck_active_build)
def init_instance(self, inst): inst.add_tag(cfg().fck_role, self.name) ctx().add_instance(inst) return inst