コード例 #1
0
ファイル: builder.py プロジェクト: hfeeki/fabcloudkit
    def _copy_from(self, copy_spec):
        from_role_name = copy_spec.get('role', None)
        if not from_role_name:
            return None

        # get the last known good build from the source machine.
        # note: we could alternatively get this from an instance tag.
        inst, role = ctx().get_host_in_role(from_role_name)
        with settings(host_string=inst.public_dns_name, user=role.user):
            message('Getting last good build-name from: "{0}"'.format(from_role_name))
            src_build_name = BuildInfo().get_last_good()

        # copy it from the source machine. note that all machines must have been provisioned
        # properly to allow the current machine access to the source machine.
        tarball = self._tarball_name(src_build_name)
        path = ctx().build_path(tarball)
        copy_file_from(role.user, inst.private_dns_name, path, path)

        with cd(ctx().builds_root()):
            # untar it.
            command = 'tar -x --file={tarball}'.format(**locals())
            result = run(command)
            if result.failed:
                raise HaltError('Failed to untar: "{0}"'.format(path))

            # delete the tar.
            if copy_spec.get('delete_tar', True):
                run('rm {tarball}'.format(**locals()))

        # update the build information.
        BuildInfo().set_last_good(src_build_name)
        succeed_msg('Successfully copied build: "{0}"'.format(src_build_name))
        return src_build_name
コード例 #2
0
ファイル: builder.py プロジェクト: hfeeki/fabcloudkit
    def _get_repos(self, repo_names):
        if repo_names == '__all__':
            return ctx().repos()

        if isinstance(repo_names, basestring):
            repo_names = [repo_names]
        return [ctx().get_repo(name) for name in repo_names]
コード例 #3
0
ファイル: provisioner.py プロジェクト: hfeeki/fabcloudkit
    def _create_dirs(self):
        # repos directory.
        result = sudo('mkdir -p -m 0777 {0}'.format(ctx().repos_root()))
        if result.failed:
            HaltError('Unable to create root directory for repo storage.')

        # builds directory.
        result = sudo('mkdir -p -m 0777 {0}'.format(ctx().builds_root()))
        if result.failed:
            HaltError('Unable to create root directory for builds.')
コード例 #4
0
ファイル: gunicorn.py プロジェクト: waxkinetic/fabcloudkit
    def start(self, spec, build_name, prog_name):
        """Starts a gunicorn server running.

        Writes a supervisor configuration for the program, starts the program, and
        optionally verifies the service is running by making an HTTP request.

        :param spec:
            a dictionary containing the gunicorn specification. may contain the keys:

            'script':
                optional; the gunicorn command (e.g., 'gunicorn' or 'gunicorn_django')
                default: 'gunicorn'

            'options':
                optional; gunicorn "long-name" options. these are the same as the option names
                preceded with double-dash, i.e., use "name" and not "n". "bind" cannot be set
                because it's set automatically. "name" and "workers" have intelligent defaults,
                but can be overridden.

            'app_module':
                required; the python module:variable to run.

            'http_test_path':
                optional; if specified, a HTTP HEAD request is made to the gunicorn server
                using this path. if a 200, 300 or 400 response is received, the server is
                considered to be running, otherwise it is considered not running and an
                exception will be raised.

        :param build_name:
            name of the build directory/virtualenv containing gunicorn.

        :param prog_name:
            name of the gunicorn program.

        :return:
            the port number on which the server is running.
        """
        # create the command and write a new supervisor config for this build.
        # (this creates a [program:<prog_name>] config section for supervisor).
        cmd, port = self._get_cmd(spec, build_name, prog_name)
        log_root = path.join(ctx().build_path(build_name), 'logs')
        self._supervisor.write_config(prog_name, cmd, ctx().builds_root(), log_root)

        # start it and wait until supervisor thinks its up and running, then test the
        # service by sending it the specified HTTP request.
        self._supervisor.start(prog_name)
        http_path = spec.get('http_test_path', None)
        if not (self._supervisor.wait_until_running(prog_name) and self._http_test(http_path, port)):
            # cleanup and fail.
            self._supervisor.stop_and_remove(prog_name)
            raise HaltError('Failed to start local server at: "{0}"'.format(self._get_bind(port)))

        message('Successfully started local server.')
        return port, self._get_bind(port)
コード例 #5
0
    def _increment_name(self, ref_repo_name):
        # some projects have more than one repo. in this case one is designated as the "reference".
        # the reference repo gives it's most recent commit ID that's used in the new build name.
        # if no reference is given, just use the first (hopefully, the only) repo in the Context.
        if ref_repo_name:
            ref_repo = ctx().get_repo(ref_repo_name)
        else:
            ref_repo = ctx().repos()[0]

        name = BuildInfo.next(ref_repo.dir)
        succeed_msg('Created new build name: "{0}"'.format(name))
        return name
コード例 #6
0
    def _tarball(self, build_name):
        tarball = self._tarball_name(build_name)
        dir_to_tar = ctx().build_path(build_name)

        with cd(ctx().builds_root()):
            options = '--create --gzip --format=ustar --owner=0 --group=0'
            command = 'tar {options} --file={tarball} {build_name}'.format(**locals())
            result = run(command)

        if result.failed:
            raise HaltError('Failed to create tarball for: "{0}"'.format(dir_to_tar))
        succeed_msg('Created build tarball: "{0}"'.format(tarball))
        return self
コード例 #7
0
ファイル: provisioner.py プロジェクト: hfeeki/fabcloudkit
    def _execute_git_spec(self, git_spec):
        if git_spec.get('install_key_file', False):
            tool.git.install_key_file(ctx().get_key('git').local_file)

        clone_spec = git_spec.get('clone', [])
        if clone_spec == '__all__':
            repos = ctx().repos()
        else:
            if isinstance(clone_spec, basestring):
                clone_spec = [clone_spec]
            repos = [ctx().get_repo(name) for name in clone_spec]

        for repo in repos:
            tool.git.clone(repo.url, ctx().name, ctx().repos_root(), repo.dir)
コード例 #8
0
ファイル: gunicorn.py プロジェクト: waxkinetic/fabcloudkit
    def _get_cmd(self, spec, build_name, prog_name):
        """Builds the gunicorn command.

        See documentation of start() for parameter descriptions.

        :return:
            the gunicorn command as a string.
        """
        cmd = spec.get('script', 'gunicorn')
        app = spec.get('app_module', None)
        cmd_path = path.join(ctx().build_path(build_name), 'bin')

        # allow overrides of some gunicorn options.
        port = unused_port()
        options = dict(spec.get('options', {}))
        options['bind'] = self._get_bind(port)

        if 'workers' not in options:
            options['workers'] = (2 * cpu_count()) + 1
        if 'name' not in options:
            options['name'] = prog_name

        debug = options.pop('debug', False)
        options = ' '.join(['--{0} {1}'.format(k,v) for k,v in options.iteritems()])
        if debug:
            options += ' --debug'
        return '{cmd_path}/{cmd} {options} {app}'.format(**locals()), port
コード例 #9
0
ファイル: git.py プロジェクト: hfeeki/fabcloudkit
def clone_all():
    """
    Clones all repos defined in the current context.

    :return: None
    """
    for repo in ctx().repos():
        clone(repo['url'], repo_name=repo['dir'])
コード例 #10
0
ファイル: git.py プロジェクト: hfeeki/fabcloudkit
def pull_all():
    """
    Performs a "pull" for all repos defined in the current context.

    :return: None
    """
    for repo in ctx().repos():
        pull(repo['dir'])
コード例 #11
0
    def copy_from(self, role_name, post_build=None, delete_tar=True):
        """Copies an existing build from an instance in the specified role.

        Instead of building itself, a build is copied from another instance to the current
        instance.

        :param role_name: the role of the instance to copy the build tarball from.
        :param post_build: list of post-build commands to execute.
        :param delete_tar: True to delete the tarball, False otherwise.
        :return: the name of the copied build.
        """
        # get the last known good build from the source machine.
        # note: we could alternatively get this from an instance tag.
        message('Copying build from instance in role: "{0}"'.format(role_name))
        inst, role = ctx().get_host_in_role(role_name)
        with settings(host_string=inst.public_dns_name, user=role.user):
            message('Getting last good build-name from: "{0}"'.format(role_name))
            src_build_name = BuildInfo().get_last_good()

        # copy it from the source machine. note that all machines must have been provisioned
        # properly to allow the current machine access to the source machine.
        tarball = self._tarball_name(src_build_name)
        path = ctx().build_path(tarball)
        copy_file_from(role.user, inst.private_dns_name, path, path)

        with cd(ctx().builds_root()):
            # untar it.
            command = 'tar -x --file={tarball}'.format(**locals())
            result = run(command)
            if result.failed:
                raise HaltError('Failed to untar: "{0}"'.format(path))

            # delete the tar.
            if delete_tar:
                run('rm {tarball}'.format(**locals()))

        # update the build information.
        BuildInfo().set_last_good(src_build_name)

        # execute any post-build commands.
        if post_build:
            self._execute_post_build(post_build, src_build_name)

        succeed_msg('Successfully copied build: "{0}"'.format(src_build_name))
        return src_build_name
コード例 #12
0
ファイル: builder.py プロジェクト: hfeeki/fabcloudkit
 def _execute_post_build(self, post_spec, build_name):
     message('Running post-build commands:')
     with prefix(virtualenv.activate_prefix(ctx().build_path(build_name))):
         for desc in post_spec:
             f = sudo if desc.get('sudo', False) else run
             result = f(desc['command'])
             if result.failed and not desc.get('ignore_fail', False):
                 raise HaltError('Post-build command failed: "{0}"'.format(desc['command']))
     message('Completed post-build commands.')
コード例 #13
0
ファイル: provisioner.py プロジェクト: hfeeki/fabcloudkit
    def _execute_access_spec(self, spec):
        access = spec.get('roles', [])
        if isinstance(access, basestring):
            access = [access]

        if access:
            public_key = get_public_key()
            for role_name in access:
                # check if access is allowed.
                target_role = ctx().get_role(role_name)
                if not target_role.allows_access_to(self._role.name):
                    raise RuntimeError('Role "{0}" does not allow access to role "{1}"'
                                       .format(target_role.name, self._role.name))

                # it is; put this host's public key in the target host's authorized_keys file.
                inst, role = ctx().get_host_in_role(role_name)
                with settings(host_string=inst.public_dns_name, user=role.user):
                    authorize_key(public_key)
コード例 #14
0
ファイル: git.py プロジェクト: hfeeki/fabcloudkit
def clone(url, name=None, parent_dir=None, repo_name=''):
    start_msg('----- Cloning git repo: "{url}"'.format(**locals()))
    if not name:
        name = ctx().name
        message('Using context name: "{0}"'.format(name))
    if not parent_dir:
        parent_dir = ctx().repos_root()
        message('Using parent directory: "{0}"'.format(parent_dir))

    # make sure the parent directory exists.
    result = sudo('mkdir -p -m 0777 {0}'.format(parent_dir))
    if result.failed:
        raise HaltError('Unable to create repo parent directory: {0}'.format(parent_dir))

    with cd(parent_dir):
        result = run('git clone {url} {repo_name}'.format(**locals()))
        if result.return_code != 0:
            raise HaltError('Failed to clone repo: "{url}"'.format(**locals()))
    succeed_msg('Clone successful.')
コード例 #15
0
ファイル: builder.py プロジェクト: hfeeki/fabcloudkit
    def _increment_name(self, plan):
        # some projects have more than one repo. in this case one is designated as the "reference".
        # the reference repo gives it's most recent commit ID that's used in the new build name.
        ref_name = plan.get('reference_repo', None)
        if ref_name:
            ref_repo = ctx().get_repo(ref_name)
        else:
            ref_repo = self._get_repos(plan.get('repos', []))[0]

        name = BuildInfo.next(ref_repo.dir)
        succeed_msg('Created new build name: "{0}"'.format(name))
        return name
コード例 #16
0
    def _get_nginx_static(self, options, build_name):
        static_spec = options.get('static', None)
        if not static_spec:
            return ''

        dir = site_packages_dir(ctx().build_path(build_name))
        static = ''.join([
        "location {0} {{\n"
        "    alias {1};\n"
        "}}\n".format(d['url'], path.join(dir, d['local'])) for d in static_spec
        ])
        return static
コード例 #17
0
ファイル: keys.py プロジェクト: waxkinetic/fabcloudkit
    def install(self, roles):
        if isinstance(roles, basestring):
            roles = [roles]

        if not roles:
            raise HaltError('No roles specified for request_access.')

        public_key = self._key_pair.get_public_key()
        for role_name in roles:
            # check if access is allowed.
            target_role = ctx().get_role(role_name)
            if not target_role.allows_access_to(env.role_name):
                raise RuntimeError('Role "{0}" does not allow access to role "{1}"'
                                   .format(target_role.name, env.role_name))

            # it is; put this host's public key in the target host's authorized_keys file.
            inst, role = ctx().get_host_in_role(role_name)
            with role.and_instance(inst):
                self._authorize_key(public_key)
        succeed_msg('Access granted to instances in role(s): {0}'.format(roles))
        return self
コード例 #18
0
ファイル: git.py プロジェクト: hfeeki/fabcloudkit
def pull(repo_name=None, repo_dir=None):
    if not repo_dir:
        if not repo_name:
            raise HaltError('Either "repo_name" or "repo_dir" must be specified.')
        repo_dir = ctx().repo_path(repo_name)

    start_msg('Executing git pull in repo: "{0}"'.format(repo_dir))
    with cd(repo_dir):
        result = run('git pull')
        if result.failed:
            raise HaltError('Error during "git pull" ({0})'.format(result))
    succeed_msg('Pull successful ({0}).'.format(result))
コード例 #19
0
ファイル: role.py プロジェクト: hfeeki/fabcloudkit
    def create_instance(self, image_id=None, key_name=None, instance_type=None, security_groups=None, **kwargs):
        # default to values specified in the role definition, but allow to be overridden.
        if image_id is None:
            image_id = self.aws.ami_id
        if key_name is None:
            key_name = self.aws.key_name
        if security_groups is None:
            security_groups = self.aws.security_groups
        if instance_type is None:
            instance_type = self.aws.instance_type

        # create the instance.
        conn = EC2Connection(ctx().aws_key, ctx().aws_secret)
        result = conn.run_instances(image_id, key_name=key_name,
            security_groups=security_groups, instance_type=instance_type, **kwargs)

        # wait until it's running.
        inst = result.instances[0]
        while inst.state != 'running':
            time.sleep(5)
            inst.update()
        return self.init_instance(inst)
コード例 #20
0
ファイル: git.py プロジェクト: hfeeki/fabcloudkit
def head_commit(repo_name=None, repo_dir=None):
    if not repo_dir:
        if not repo_name:
            raise HaltError('Either "repo_name" or "repo_dir" must be specified.')
        repo_dir = ctx().repo_path(repo_name)

    start_msg('Getting commit ID in git repo: "{0}":'.format(repo_dir))
    with cd(repo_dir):
        # pipe the result through cat, otherwise the result that comes back from run()
        # is garbled and requires extensive weird parsing to extract the commit ID.
        result = run('git log -1 --pretty=format:%h | cat')
        if result.failed:
            raise HaltError('Error during "git log" ({0})'.format(result))
    succeed_msg('Got head commit ID ({0}).'.format(result))
    return result
コード例 #21
0
ファイル: builder.py プロジェクト: hfeeki/fabcloudkit
    def _build(self, plan):
        # increment the build name and create a new virtualenv for the build.
        build_name = self._increment_name(plan)
        build_env_dir = ctx().build_path(build_name)
        virtualenv.ensure(build_env_dir, plan.get('interpreter', None))

        # run "setup.py install" in each repo.
        for repo in self._get_repos(plan.get('repos', [])):
            build_repo(build_env_dir, repo)

        # run tests.
        self._unittest(plan, build_name)

        # save the last known good build-name.
        BuildInfo.set_last_good(build_name)
        return build_name
コード例 #22
0
ファイル: activator.py プロジェクト: hfeeki/fabcloudkit
    def _start_gunicorn(self, spec, build_name):
        # create the command and write a new supervisor config for this build.
        # (this creates a [program:<build_name>] config section for supervisor).
        cmd, port = self._build_gunicorn_cmd(spec.get('gunicorn', {}), build_name)
        log_root = self._log_root(build_name)
        supervisord.write_program_config(build_name, cmd, ctx().builds_root(), log_root)

        # start it and wait until supervisor thinks its up and running, then test the
        # service by sending it the specified HTTP request.
        supervisord.start(build_name)
        if not supervisord.wait_until_running(build_name) or not self._http_test(spec, port):
            # cleanup and fail.
            supervisord.stop_and_remove(build_name)
            raise HaltError('Failed to start local server at: "{0}"'.format(self._gunicorn_bind(port)))

        message('Successfully started local server.')
        return port
コード例 #23
0
ファイル: activator.py プロジェクト: hfeeki/fabcloudkit
    def _build_gunicorn_cmd(self, spec, build_name):
        cmd = spec.get('script', 'gunicorn')
        app = spec.app_module
        cmd_path = path.join(ctx().build_path(build_name), 'bin')

        # allow overrides of some gunicorn options.
        port = unused_port()
        dct = dict(spec.get('options', {}))
        dct['bind'] = self._gunicorn_bind(port)

        if 'workers' not in dct:
            dct['workers'] = (2 * cpu_count()) + 1
        if 'name' not in dct:
            dct['name'] = build_name

        debug = dct.pop('debug', False)
        options = ' '.join(['--{0} {1}'.format(k,v) for k,v in dct.iteritems()])
        if debug:
            options += ' --debug'
        return '{cmd_path}/{cmd} {options} {app}'.format(**locals()), port
コード例 #24
0
ファイル: build.py プロジェクト: waxkinetic/fabcloudkit
def build_repo(build_env_dir, repo):
    full_repo_dir = ctx().repo_path(repo.dir)
    dist_dir = path.join(full_repo_dir, 'dist')

    # with the build virtualenv activated, and within the repo directory.
    with prefix(VirtualEnvTool.activate_prefix(build_env_dir)), cd(full_repo_dir):
        start_msg('Running "python setup.py install" for repo "{0}"'.format(repo.dir))

        # first create a source distribution using setup.py in this repo.
        result = run('python setup.py sdist --formats=gztar')
        if result.failed:
            raise HaltError('"python setup.py sdist" failed in repo: "{0}"'.format(repo.dir))

        # now use pip to install. couple of things to note:
        # a) pip does a "flat" (not versioned) install, no eggs, and consistent package directory names.
        # b) we're still allowing pip to grab packages from pypi; this should be fixed in a later version
        #    where packages can (optionally) be picked up only from a local directory.
        result = run('pip install --quiet --find-links=file://{dist_dir} {repo.package_name}'.format(**locals()))
        if result.failed:
            raise HaltError('"pip install" failed in repo: "{0}"'.format(repo.dir))
        succeed_msg('Build successful.')
コード例 #25
0
ファイル: role.py プロジェクト: hfeeki/fabcloudkit
 def init_instance(self, inst):
     inst.add_tag(cfg().fck_role, self.name)
     ctx().add_instance(inst)
     return inst
コード例 #26
0
ファイル: build.py プロジェクト: waxkinetic/fabcloudkit
 def _file_path(self):
     return ctx().repo_path(self.BUILD_INFO_FILE)
コード例 #27
0
ファイル: build.py プロジェクト: waxkinetic/fabcloudkit
 def __init__(self, context_name=None):
     self._context_name = ctx().name if not context_name else context_name
     self._dct = None
コード例 #28
0
 def _log_root(self, build_name):
     return path.join(ctx().build_path(build_name), 'logs')
コード例 #29
0
    def build(self, repos, reference_repo=None, post_build=None, interpreter=None, tarball=False, unittest=None):
        """Performs a 'python' build.

        Performs a python build by running setup.py in each identified repo. If desired, repos can
        be refreshed first (e.g., via git pull).

        :param repos:
            specifies the list of repos in which to run setup.py.

        :param reference_repo:
            optional; the reference repo from which to retrieve the head commit id.
            this id used as a component of the build name. if not specified, the
            first repo in the context is used.

        :param post_build:
            a list of post-build commands. a list of dictionaries. each dict must
            contain the key "command" that specifies the command to execute. optionally,
            it may include a "sudo" value of [True|False], and an "ignore_fail" value
            of [True|False].

        :param interpreter:
            specifies the Python interpreter to use in the build's virtualenv. if
            not specified, the operating system default interpreter is used. note
            that the interpreter must already exist on the system.

        :param tarball:
            True to create a tarball of the build; this is required if any other
            instance will use "copy_from".

        :param unittest:
            TBD

        :return:
            the new build name
        """
        start_msg('Executing build for instance in role "{0}":'.format(env.role_name))

        # increment the build name and create a new virtualenv for the build.
        build_name = self._increment_name(reference_repo)
        build_env_dir = ctx().build_path(build_name)
        VirtualEnvTool().ensure(build_env_dir, interpreter)

        # run "setup.py install" in each repo.
        for repo_name in ([repos] if isinstance(repos, basestring) else repos):
            build_repo(build_env_dir, ctx().get_repo(repo_name))

        # run tests.
        self._unittest(unittest, build_name)

        # save the last known good build-name.
        BuildInfo.set_last_good(build_name)
        if tarball:
            self._tarball(build_name)

        # execute any post-build commands.
        if post_build:
            self._execute_post_build(post_build, build_name)

        # make the build_name available to the caller; it'll be set as an instance-tag.
        succeed_msg('Build completed successfully for role "{0}".'.format(env.role_name))
        env.role.set_env(build_result=build_name)
        return self