コード例 #1
0
ファイル: add_sandbox.py プロジェクト: piojo/aquilon
    def render(self, session, logger, dbuser, sandbox, start, get, comments, **arguments):
        if not dbuser:
            raise AuthorizationException("Cannot create a sandbox without an " "authenticated connection.")

        sandbox = self.force_my_sandbox(session, logger, dbuser, sandbox)

        # See `git check-ref-format --help` for naming restrictions.
        # We want to layer a few extra restrictions on top of that...
        validate_template_name("--sandbox", sandbox)

        Branch.get_unique(session, sandbox, preclude=True)

        # Check that the user has cleared up a directory of the same
        # name; if this is not the case the branch may be created (in git)
        # and added to the database - however CommandGet will fail roleing
        # back the database leaving the branch created in git
        templatesdir = self.config.get("broker", "templatesdir")
        sandboxdir = os.path.join(templatesdir, dbuser.name, sandbox)
        if os.path.exists(sandboxdir):
            raise ArgumentError("Sandbox directory %s already exists; " "cannot create branch." % sandboxdir)

        if not start:
            start = self.config.get("broker", "default_domain_start")
        dbstart = Branch.get_unique(session, start, compel=True)

        kingdir = self.config.get("broker", "kingdir")
        base_commit = run_git(["show-ref", "--hash", "refs/heads/" + dbstart.name], logger=logger, path=kingdir)

        compiler = self.config.get("panc", "pan_compiler")
        dbsandbox = Sandbox(name=sandbox, owner=dbuser, compiler=compiler, base_commit=base_commit, comments=comments)
        session.add(dbsandbox)
        session.flush()

        # Currently this will fail if the branch already exists...
        # That seems like the right behavior.  It's an internal
        # consistency issue that would need to be addressed explicitly.
        run_git(["branch", sandbox, dbstart.name], logger=logger, path=kingdir)

        # If we arrive there the above "git branch" command has succeeded;
        # therefore we should comit the changes to the database.  If this is
        # not done, and CommandGet fails (see dir check above), then the
        # git branch will be created but the database changes roled back.
        session.commit()

        if get == False:
            # The client knows to interpret an empty response as no action.
            return []

        return CommandGet.render(self, session=session, logger=logger, dbuser=dbuser, sandbox=sandbox)
コード例 #2
0
ファイル: publish.py プロジェクト: ned21/aquilon
    def render(self, session, logger, branch, sandbox, bundle, sync, rebase,
               **arguments):
        # Most of the logic here is duplicated in deploy
        if branch:
            sandbox = branch
        dbsandbox = Sandbox.get_unique(session, sandbox, compel=True)

        (handle, filename) = mkstemp()
        contents = b64decode(bundle)
        write_file(filename, contents, logger=logger)

        if sync and not dbsandbox.is_sync_valid and dbsandbox.trackers:
            # FIXME: Maybe raise an ArgumentError and request that the
            # command run with --nosync?  Maybe provide a --validate flag?
            # For now, we just auto-flip anyway (below) making the point moot.
            pass
        if not dbsandbox.is_sync_valid:
            dbsandbox.is_sync_valid = True

        if rebase and dbsandbox.trackers:
            raise ArgumentError(
                "{0} has trackers, rebasing is not allowed.".format(dbsandbox))

        kingdir = self.config.get("broker", "kingdir")
        rundir = self.config.get("broker", "rundir")

        tempdir = mkdtemp(prefix="publish_",
                          suffix="_%s" % dbsandbox.name,
                          dir=rundir)
        try:
            run_git([
                "clone", "--shared", "--branch", dbsandbox.name, kingdir,
                dbsandbox.name
            ],
                    path=tempdir,
                    logger=logger)
            temprepo = os.path.join(tempdir, dbsandbox.name)
            run_git(["bundle", "verify", filename],
                    path=temprepo,
                    logger=logger)
            ref = "HEAD:%s" % (dbsandbox.name)
            command = ["pull", filename, ref]
            if rebase:
                command.append("--force")
            run_git(command,
                    path=temprepo,
                    logger=logger,
                    loglevel=CLIENT_INFO)
            # FIXME: Run tests before pushing back to template-king
            if rebase:
                target_ref = "+" + dbsandbox.name
            else:
                target_ref = dbsandbox.name
            run_git(["push", "origin", target_ref],
                    path=temprepo,
                    logger=logger)
        except ProcessException, e:
            raise ArgumentError("\n%s%s" % (e.out, e.err))
コード例 #3
0
ファイル: domain.py プロジェクト: stdweird/aquilon
 def sandbox_has_latest(self, config, sandboxdir):
     domainsdir = config.get("broker", "domainsdir")
     prod_domain = config.get("broker", "default_domain_start")
     proddir = os.path.join(domainsdir, prod_domain)
     try:
         prod_commit = run_git(["rev-list", "-n", "1", "HEAD"], path=proddir, logger=self.logger).strip()
     except ProcessException:
         prod_commit = ""
     if not prod_commit:
         raise InternalError("Error finding top commit for %s" % prod_domain)
     filterre = re.compile("^" + prod_commit + "$")
     try:
         found_latest = run_git(["rev-list", "HEAD"], path=sandboxdir, logger=self.logger, filterre=filterre)
     except ProcessException:
         self.logger.warn("Failed to run git command in sandbox %s." % sandboxdir)
         found_latest = ""
     return bool(found_latest)
コード例 #4
0
    def render(self, session, logger, dbuser, sandbox, start, get, comments,
               **arguments):
        if not dbuser:
            raise AuthorizationException("Cannot create a sandbox without an "
                                         "authenticated connection.")

        sandbox = self.force_my_sandbox(session, logger, dbuser, sandbox)

        # See `git check-ref-format --help` for naming restrictions.
        # We want to layer a few extra restrictions on top of that...
        valid = re.compile('^[a-zA-Z0-9_.-]+$')
        if (not valid.match(sandbox)):
            raise ArgumentError("sandbox name '%s' is not valid" % sandbox)

        Branch.get_unique(session, sandbox, preclude=True)

        if not start:
            start = self.config.get("broker", "default_domain_start")
        dbstart = Branch.get_unique(session, start, compel=True)

        kingdir = self.config.get("broker", "kingdir")
        base_commit = run_git(["show-ref", "--hash", "refs/heads/" +
                               dbstart.name], logger=logger, path=kingdir)

        compiler = self.config.get("panc", "pan_compiler")
        dbsandbox = Sandbox(name=sandbox, owner=dbuser, compiler=compiler,
                            base_commit=base_commit, comments=comments)
        session.add(dbsandbox)
        session.flush()

        # Currently this will fail if the branch already exists...
        # That seems like the right behavior.  It's an internal
        # consistency issue that would need to be addressed explicitly.
        run_git(["branch", sandbox, dbstart.name], logger=logger, path=kingdir)

        if get == False:
            # The client knows to interpret an empty response as no action.
            return []

        return CommandGet.render(self, session=session, logger=logger,
                                 dbuser=dbuser, sandbox=sandbox)
コード例 #5
0
ファイル: branch.py プロジェクト: piojo/aquilon
def remove_branch(config, logger, dbbranch):
    session = object_session(dbbranch)
    deps = get_branch_dependencies(dbbranch)
    if deps:
        raise ArgumentError("\n".join(deps))

    session.delete(dbbranch)

    domain = TemplateDomain(dbbranch, logger=logger)
    # Can this fail?  Is recovery needed?
    with CompileKey(domain=dbbranch.name, logger=logger):
        for dir in domain.directories():
            remove_dir(dir, logger=logger)

    kingdir = config.get("broker", "kingdir")
    try:
        run_git(["branch", "-D", dbbranch.name],
                path=kingdir, logger=logger)
    except ProcessException, e:
        logger.warning("Error removing branch %s from template-king, "
                       "proceeding anyway: %s", dbbranch.name, e)
コード例 #6
0
ファイル: branch.py プロジェクト: ned21/aquilon
def remove_branch(config, logger, dbbranch):
    session = object_session(dbbranch)
    deps = get_branch_dependencies(dbbranch)
    if deps:
        raise ArgumentError("\n".join(deps))

    session.delete(dbbranch)

    domain = TemplateDomain(dbbranch, logger=logger)
    # Can this fail?  Is recovery needed?
    with CompileKey(domain=dbbranch.name, logger=logger):
        for dir in domain.directories():
            remove_dir(dir, logger=logger)

    kingdir = config.get("broker", "kingdir")
    try:
        run_git(["branch", "-D", dbbranch.name], path=kingdir, logger=logger)
    except ProcessException, e:
        logger.warning(
            "Error removing branch %s from template-king, "
            "proceeding anyway: %s", dbbranch.name, e)
コード例 #7
0
ファイル: domain.py プロジェクト: ned21/aquilon
 def sandbox_has_latest(self, config, sandboxdir):
     domainsdir = config.get('broker', 'domainsdir')
     prod_domain = config.get('broker', 'default_domain_start')
     proddir = os.path.join(domainsdir, prod_domain)
     try:
         prod_commit = run_git(['rev-list', '-n', '1', 'HEAD'],
                               path=proddir, logger=self.logger).strip()
     except ProcessException:
         prod_commit = ''
     if not prod_commit:
         raise InternalError("Error finding top commit for %s" %
                             prod_domain)
     filterre = re.compile('^' + prod_commit + '$')
     try:
         found_latest = run_git(['rev-list', 'HEAD'], path=sandboxdir,
                                logger=self.logger, filterre=filterre)
     except ProcessException:
         self.logger.warn("Failed to run git command in sandbox %s." %
                          sandboxdir)
         found_latest = ''
     return bool(found_latest)
コード例 #8
0
ファイル: publish.py プロジェクト: piojo/aquilon
    def render(self, session, logger, branch, sandbox, bundle, sync, rebase,
               **arguments):
        # Most of the logic here is duplicated in deploy
        if branch:
            sandbox = branch
        dbsandbox = Sandbox.get_unique(session, sandbox, compel=True)

        (handle, filename) = mkstemp()
        contents = b64decode(bundle)
        write_file(filename, contents, logger=logger)

        if sync and not dbsandbox.is_sync_valid and dbsandbox.trackers:
            # FIXME: Maybe raise an ArgumentError and request that the
            # command run with --nosync?  Maybe provide a --validate flag?
            # For now, we just auto-flip anyway (below) making the point moot.
            pass
        if not dbsandbox.is_sync_valid:
            dbsandbox.is_sync_valid = True

        if rebase and dbsandbox.trackers:
            raise ArgumentError("{0} has trackers, rebasing is not allowed."
                                .format(dbsandbox))

        kingdir = self.config.get("broker", "kingdir")
        rundir = self.config.get("broker", "rundir")

        tempdir = mkdtemp(prefix="publish_", suffix="_%s" % dbsandbox.name,
                          dir=rundir)
        try:
            run_git(["clone", "--shared", "--branch", dbsandbox.name,
                     kingdir, dbsandbox.name],
                    path=tempdir, logger=logger)
            temprepo = os.path.join(tempdir, dbsandbox.name)
            run_git(["bundle", "verify", filename],
                    path=temprepo, logger=logger)
            ref = "HEAD:%s" % (dbsandbox.name)
            command = ["pull", filename, ref]
            if rebase:
                command.append("--force")
            run_git(command, path=temprepo, logger=logger, loglevel=CLIENT_INFO)
            # FIXME: Run tests before pushing back to template-king
            if rebase:
                target_ref = "+" + dbsandbox.name
            else:
                target_ref = dbsandbox.name
            run_git(["push", "origin", target_ref],
                    path=temprepo, logger=logger)
        except ProcessException, e:
            raise ArgumentError("\n%s%s" % (e.out, e.err))
コード例 #9
0
ファイル: rollback.py プロジェクト: ned21/aquilon
    def render(self, session, logger, domain, ref, lastsync, **arguments):
        dbdomain = Domain.get_unique(session, domain, compel=True)
        if not dbdomain.tracked_branch:
            # Could check dbdomain.trackers and rollback all of them...
            raise ArgumentError("rollback requires a tracking domain")

        if lastsync:
            if not dbdomain.rollback_commit:
                raise ArgumentError("domain %s does not have a rollback "
                                    "commit saved, please specify one "
                                    "explicitly." % dbdomain.name)
            ref = dbdomain.rollback_commit

        if not ref:
            raise ArgumentError("Commit reference to rollback to required.")

        kingdir = self.config.get("broker", "kingdir")
        domaindir = os.path.join(self.config.get("broker", "domainsdir"),
                                 dbdomain.name)
        out = run_git(["branch", "--contains", ref],
                      logger=logger,
                      path=kingdir)
        if not re.search(r'\b%s\b' % dbdomain.tracked_branch.name, out):
            # There's no real technical reason why this needs to be
            # true.  It just seems like a good sanity check.
            raise ArgumentError("Cannot roll back to commit: "
                                "branch %s does not contain %s" %
                                (dbdomain.tracked_branch.name, ref))

        dbdomain.tracked_branch.is_sync_valid = False
        session.add(dbdomain.tracked_branch)
        dbdomain.rollback_commit = None
        session.add(dbdomain)

        key = CompileKey(domain=dbdomain.name, logger=logger)
        try:
            lock_queue.acquire(key)
            run_git(["push", ".", "+%s:%s" % (ref, dbdomain.name)],
                    path=kingdir,
                    logger=logger)
            # Duplicated this logic from aquilon.worker.processes.sync_domain()
            run_git(["fetch"], path=domaindir, logger=logger)
            run_git(["reset", "--hard",
                     "origin/%s" % dbdomain.name],
                    path=domaindir,
                    logger=logger)
        except ProcessException, e:
            raise ArgumentError(
                "Problem encountered updating templates for "
                "domain %s: %s", dbdomain.name, e)
コード例 #10
0
ファイル: rollback.py プロジェクト: jrha/aquilon
    def render(self, session, logger, domain, ref, lastsync, **arguments):
        dbdomain = Domain.get_unique(session, domain, compel=True)
        if not dbdomain.tracked_branch:
            # Could check dbdomain.trackers and rollback all of them...
            raise ArgumentError("rollback requires a tracking domain")

        if lastsync:
            if not dbdomain.rollback_commit:
                raise ArgumentError("domain %s does not have a rollback "
                                    "commit saved, please specify one "
                                    "explicitly." % dbdomain.name)
            ref = dbdomain.rollback_commit

        if not ref:
            raise ArgumentError("Commit reference to rollback to required.")

        kingdir = self.config.get("broker", "kingdir")
        domaindir = os.path.join(self.config.get("broker", "domainsdir"),
                                 dbdomain.name)
        out = run_git(["branch", "--contains", ref],
                      logger=logger, path=kingdir)
        if not re.search(r'\b%s\b' % dbdomain.tracked_branch.name, out):
            # There's no real technical reason why this needs to be
            # true.  It just seems like a good sanity check.
            raise ArgumentError("Cannot roll back to commit: "
                                "branch %s does not contain %s" %
                                (dbdomain.tracked_branch.name, ref))

        dbdomain.tracked_branch.is_sync_valid = False
        session.add(dbdomain.tracked_branch)
        dbdomain.rollback_commit = None
        session.add(dbdomain)

        key = CompileKey(domain=dbdomain.name, logger=logger)
        try:
            lock_queue.acquire(key)
            run_git(["push", ".", "+%s:%s" % (ref, dbdomain.name)],
                    path=kingdir, logger=logger)
            # Duplicated this logic from aquilon.worker.processes.sync_domain()
            run_git(["fetch"], path=domaindir, logger=logger)
            run_git(["reset", "--hard", "origin/%s" % dbdomain.name],
                    path=domaindir, logger=logger)
        except ProcessException, e:
            raise ArgumentError("Problem encountered updating templates for "
                                "domain %s: %s", dbdomain.name, e)
コード例 #11
0
def main():
    print "Calculating sandbox base commits. This may take around 10 minutes."

    logging.basicConfig(level=logging.WARNING)
    kingdir = config.get("broker", "kingdir")

    domains = session.query(Domain).all()

    # Define preference order when multiple domains have the same commits.
    # This is just cosmetics, but makes it easier to verify the output.
    for idx, domain in enumerate(("prod", "qa", "secure-aquilon-prod",
                                  "secure-aquilon-qa")):
        dbdom = Domain.get_unique(session, domain, compel=True)
        domains.remove(dbdom)
        domains.insert(idx, dbdom)

    base_commits = {}
    q = session.query(Sandbox)
    q = q.order_by('name')

    # The base_commit column does not exist yet...
    q = q.options(defer("base_commit"))

    for sandbox in q:
        base_domain = None
        base_commit = None
        min_ahead = None

        commits = run_git(["rev-list", "refs/heads/" + sandbox.name], path=kingdir).split("\n")

        for domain in domains:
            merge_base = run_git(["merge-base", "refs/heads/" + sandbox.name,
                                  "refs/heads/" + domain.name],
                                 path=kingdir).strip()
            # Number of commits since branching from the given domain
            ahead = commits.index(merge_base)

            if base_domain is None or ahead < min_ahead:
                base_domain = domain
                base_commit = merge_base
                min_ahead = ahead

            if min_ahead == 0:
                break

        print "{0: <40}: {1.name} (ahead {2})".format(sandbox, base_domain,
                                                      min_ahead)

        base_commits[sandbox.name] = base_commit

    session.expunge_all()

    try:
        if session.bind.dialect.name == 'oracle':
            query = text("""
        ALTER TABLE sandbox ADD base_commit VARCHAR2(40 CHAR)
""")
        elif session.bind.dialect.name == 'postgresql':
            query = text("""
        ALTER TABLE sandbox ADD base_commit CHARACTER VARYING (40)
""")
        print "\nExecuting: %s" % query
        session.execute(query)
        session.commit()
    except DatabaseError:
        # Allow the script to be re-run by not failing if the column already
        # exists. If the column does not exist, then trying to update it will
        # fail anyway.
        print """
WARNING: Adding the sandbox.base_commit column has failed. If you're running
this script for the second time, then that's likely OK, otherwise you should
verify and correct the schema manually.
"""
        session.rollback()

    for sandbox in q:
        sandbox.base_commit = base_commits[sandbox.name]
    session.commit()

    try:
        if session.bind.dialect.name == 'oracle':
            query = text("""
        ALTER TABLE sandbox MODIFY (base_commit VARCHAR2(40 CHAR)
            CONSTRAINT sandbox_base_commit_nn NOT NULL)
""")
        elif session.bind.dialect.name == 'postgresql':
            query = text("""
        ALTER TABLE sandbox ALTER COLUMN base_commit SET NOT NULL
""")
        print "\nExecuting: %s" % query
        session.execute(query)
        session.commit()
    except DatabaseError:
        print """
WARNING: Enabling the NOT NULL constraint for sandbox.base_commit column has
failed. If you're running this script for the second time, then that's likely
OK, otherwise you should verify and correct the schema manually.
"""
        session.rollback()
コード例 #12
0
def validate_branch_commits(dbsource, dbsource_author,
                            dbtarget, dbtarget_author, logger, config):
    domainsdir = config.get('broker', 'domainsdir')
    if isinstance(dbsource, Sandbox):
        authored_sandbox = AuthoredSandbox(dbsource, dbsource_author)
        source_path = authored_sandbox.path
    else:
        source_path = os.path.join(domainsdir, dbsource.name)

    if isinstance(dbtarget, Sandbox):
        authored_sandbox = AuthoredSandbox(dbtarget, dbtarget_author)
        target_path = authored_sandbox.path
    else:
        target_path = os.path.join(domainsdir, dbtarget.name)

    # check if dbsource has anything uncommitted
    git_status = run_git(["status", "--porcelain"],
                         path=source_path,
                         logger=logger)
    if git_status:
        raise ArgumentError("The source {0:l} contains uncommitted files."
                            .format(dbsource))

    # get latest source commit bit
    dbsource_commit = run_git(['rev-list', '--max-count=1', 'HEAD'],
                              path=source_path, logger=logger)
    dbsource_commit = dbsource_commit.rstrip()
    if not dbsource_commit:  # pragma: no cover
        raise ArgumentError("Unable to retrieve the git commit history from "
                            "source branch {0:l}.".format(dbsource))

    # make sure all commits in the source have been published.
    # we can check the latest commit bit from the source in template-king
    # any results returned will mean that all commits has been published
    kingdir = config.get("broker", "kingdir")
    try:
        found = run_git(['cat-file', '-t', dbsource_commit],
                        path=kingdir, logger=logger)
        found = found.strip()
    except ProcessException as pe:
        if pe.code != 128:
            raise
        else:
            found = None
    if found != 'commit':
        raise ArgumentError("The source {0:l} latest commit has not been "
                            "published to template-king yet.".format(dbsource))

    # check if target branch has the latest source commit
    try:
        filterre = re.compile('^' + dbsource_commit + '$')
        found = run_git(['rev-list', 'HEAD'], filterre=filterre,
                        path=target_path, logger=logger)
    except ProcessException as pe:
        if pe.code != 128:
            raise
        else:
            found = None
    if not found:
        raise ArgumentError("The target {0:l} does not contain the latest "
                            "commit from source {1:l}.".format(dbtarget,
                                                               dbsource))
コード例 #13
0
ファイル: deploy.py プロジェクト: jrha/aquilon
    def render(self, session, logger, source, target, sync, dryrun,
               comments, justification, user, requestid, **arguments):
        # Most of the logic here is duplicated in publish
        dbsource = Branch.get_unique(session, source, compel=True)

        # The target has to be a non-tracking domain
        dbtarget = Domain.get_unique(session, target, compel=True)

        if sync and isinstance(dbtarget.tracked_branch, Domain) \
           and dbtarget.tracked_branch.autosync and dbtarget.autosync:
            # The user probably meant to deploy to the tracked branch,
            # but only do so if all the relevant autosync flags are
            # positive.
            logger.warning("Deploying to tracked branch %s and then will "
                           "auto-sync %s" % (
                           dbtarget.tracked_branch.name, dbtarget.name))
            dbtarget = dbtarget.tracked_branch
        elif dbtarget.tracked_branch:
            raise ArgumentError("Cannot deploy to tracking domain %s.  "
                                "Did you mean domain %s?" %
                                (dbtarget.name, dbtarget.tracked_branch.name))

        if sync and not dbtarget.is_sync_valid and dbtarget.trackers:
            # FIXME: Maybe raise an ArgumentError and request that the
            # command run with --nosync?  Maybe provide a --validate flag?
            # For now, just auto-flip (below).
            pass
        if not dbtarget.is_sync_valid:
            dbtarget.is_sync_valid = True

        if dbtarget.requires_change_manager:
            if not justification:
                raise AuthorizationException(
                    "{0} is under change management control.  Please specify "
                    "--justification.".format(dbtarget))
            validate_justification(user, justification)

        if isinstance(dbsource, Sandbox):
            domainsdir = self.config.get('broker', 'domainsdir')
            targetdir = os.path.join(domainsdir, dbtarget.name)
            filterre = re.compile('^' + dbsource.base_commit + '$')
            found = run_git(['rev-list', 'HEAD'], path=targetdir,
                            logger=logger, filterre=filterre)
            if not found:
                raise ArgumentError("You're trying to deploy a sandbox to a "
                                    "domain that does not contain the commit "
                                    "where the sandbox was branched from.")

        kingdir = self.config.get("broker", "kingdir")
        rundir = self.config.get("broker", "rundir")

        tempdir = mkdtemp(prefix="deploy_", suffix="_%s" % dbsource.name,
                          dir=rundir)
        try:
            run_git(["clone", "--shared", "--branch", dbtarget.name,
                     kingdir, dbtarget.name],
                    path=tempdir, logger=logger)
            temprepo = os.path.join(tempdir, dbtarget.name)

            # We could try to use fmt-merge-msg but its usage is so obscure that
            # faking it is easier
            merge_msg = []
            merge_msg.append("Merge remote branch 'origin/%s' into %s" %
                             (dbsource.name, dbtarget.name))
            merge_msg.append("")
            merge_msg.append("User: %s" % user)
            merge_msg.append("Request ID: %s" % requestid)
            if justification:
                merge_msg.append("Justification: %s" % justification)
            if comments:
                merge_msg.append("Comments: %s" % comments)

            try:
                run_git(["merge", "--no-ff", "origin/%s" % dbsource.name,
                         "-m", "\n".join(merge_msg)],
                        path=temprepo, logger=logger, loglevel=CLIENT_INFO)
            except ProcessException, e:
                # No need to re-print e, output should have gone to client
                # immediately via the logger.
                raise ArgumentError("Failed to merge changes from %s into %s" %
                                    (dbsource.name, dbtarget.name))
            # FIXME: Run tests before pushing back to template-king.
            # Use a different try/except and a specific error message.

            if dryrun:
                session.rollback()
                return

            run_git(["push", "origin", dbtarget.name],
                    path=temprepo, logger=logger)
コード例 #14
0
def main():
    print "Calculating sandbox base commits. This may take around 10 minutes."

    logging.basicConfig(level=logging.WARNING)
    kingdir = config.get("broker", "kingdir")

    domains = session.query(Domain).all()

    # Define preference order when multiple domains have the same commits.
    # This is just cosmetics, but makes it easier to verify the output.
    for idx, domain in enumerate(
        ("prod", "qa", "secure-aquilon-prod", "secure-aquilon-qa")):
        dbdom = Domain.get_unique(session, domain, compel=True)
        domains.remove(dbdom)
        domains.insert(idx, dbdom)

    base_commits = {}
    q = session.query(Sandbox)
    q = q.order_by('name')

    # The base_commit column does not exist yet...
    q = q.options(defer("base_commit"))

    for sandbox in q:
        base_domain = None
        base_commit = None
        min_ahead = None

        commits = run_git(["rev-list", "refs/heads/" + sandbox.name],
                          path=kingdir).split("\n")

        for domain in domains:
            merge_base = run_git([
                "merge-base", "refs/heads/" + sandbox.name,
                "refs/heads/" + domain.name
            ],
                                 path=kingdir).strip()
            # Number of commits since branching from the given domain
            ahead = commits.index(merge_base)

            if base_domain is None or ahead < min_ahead:
                base_domain = domain
                base_commit = merge_base
                min_ahead = ahead

            if min_ahead == 0:
                break

        print "{0: <40}: {1.name} (ahead {2})".format(sandbox, base_domain,
                                                      min_ahead)

        base_commits[sandbox.name] = base_commit

    session.expunge_all()

    try:
        if session.bind.dialect.name == 'oracle':
            query = text("""
        ALTER TABLE sandbox ADD base_commit VARCHAR2(40 CHAR)
""")
        elif session.bind.dialect.name == 'postgresql':
            query = text("""
        ALTER TABLE sandbox ADD base_commit CHARACTER VARYING (40)
""")
        print "\nExecuting: %s" % query
        session.execute(query)
        session.commit()
    except DatabaseError:
        # Allow the script to be re-run by not failing if the column already
        # exists. If the column does not exist, then trying to update it will
        # fail anyway.
        print """
WARNING: Adding the sandbox.base_commit column has failed. If you're running
this script for the second time, then that's likely OK, otherwise you should
verify and correct the schema manually.
"""
        session.rollback()

    for sandbox in q:
        sandbox.base_commit = base_commits[sandbox.name]
    session.commit()

    try:
        if session.bind.dialect.name == 'oracle':
            query = text("""
        ALTER TABLE sandbox MODIFY (base_commit VARCHAR2(40 CHAR)
            CONSTRAINT sandbox_base_commit_nn NOT NULL)
""")
        elif session.bind.dialect.name == 'postgresql':
            query = text("""
        ALTER TABLE sandbox ALTER COLUMN base_commit SET NOT NULL
""")
        print "\nExecuting: %s" % query
        session.execute(query)
        session.commit()
    except DatabaseError:
        print """
WARNING: Enabling the NOT NULL constraint for sandbox.base_commit column has
failed. If you're running this script for the second time, then that's likely
OK, otherwise you should verify and correct the schema manually.
"""
        session.rollback()
コード例 #15
0
ファイル: add_domain.py プロジェクト: jrha/aquilon
    def render(self, session, logger, dbuser, domain, track, start,
               change_manager, comments, allow_manage, **arguments):
        if not dbuser:
            raise AuthorizationException("Cannot create a domain without "
                                         "an authenticated connection.")

        Branch.get_unique(session, domain, preclude=True)

        valid = re.compile('^[a-zA-Z0-9_.-]+$')
        if (not valid.match(domain)):
            raise ArgumentError("Domain name '%s' is not valid." % domain)

        # FIXME: Verify that track is a valid branch name?
        # Or just let the branch command fail?

        compiler = self.config.get("panc", "pan_compiler")
        dbtracked = None
        if track:
            dbtracked = Branch.get_unique(session, track, compel=True)
            if getattr(dbtracked, "tracked_branch", None):
                raise ArgumentError("Cannot nest tracking.  Try tracking "
                                    "{0:l} directly.".format(dbtracked.tracked_branch))
            start_point = dbtracked
            if change_manager:
                raise ArgumentError("Cannot enforce a change manager for "
                                    "tracking domains.")
        else:
            if not start:
                start = self.config.get("broker", "default_domain_start")
            start_point = Branch.get_unique(session, start, compel=True)

        dbdomain = Domain(name=domain, owner=dbuser, compiler=compiler,
                          tracked_branch=dbtracked,
                          requires_change_manager=bool(change_manager),
                          comments=comments)
        session.add(dbdomain)
        if allow_manage is not None:
            dbdomain.allow_manage = allow_manage
        session.flush()

        domainsdir = self.config.get("broker", "domainsdir")
        clonedir = os.path.join(domainsdir, dbdomain.name)
        if os.path.exists(clonedir):
            raise InternalError("Domain directory already exists")

        kingdir = self.config.get("broker", "kingdir")
        cmd = ["branch"]
        if track:
            cmd.append("--track")
        else:
            cmd.append("--no-track")
        cmd.append(dbdomain.name)
        cmd.append(start_point.name)
        run_git(cmd, path=kingdir, logger=logger)

        # If the branch command above fails the DB will roll back as normal.
        # If the command below fails we need to clean up from itself and above.
        try:
            run_git(["clone", "--branch", dbdomain.name,
                     kingdir, dbdomain.name],
                    path=domainsdir, logger=logger)
        except ProcessException, e:
            try:
                remove_dir(clonedir, logger=logger)
                run_git(["branch", "-D", dbdomain.name],
                        path=kingdir, logger=logger)
            except ProcessException, e2:
                logger.info("Exception while cleaning up: %s", e2)
コード例 #16
0
ファイル: deploy.py プロジェクト: ned21/aquilon
    def render(self, session, logger, source, target, sync, dryrun, comments,
               justification, user, requestid, **arguments):
        # Most of the logic here is duplicated in publish
        dbsource = Branch.get_unique(session, source, compel=True)

        # The target has to be a non-tracking domain
        dbtarget = Domain.get_unique(session, target, compel=True)

        if sync and isinstance(dbtarget.tracked_branch, Domain) \
           and dbtarget.tracked_branch.autosync and dbtarget.autosync:
            # The user probably meant to deploy to the tracked branch,
            # but only do so if all the relevant autosync flags are
            # positive.
            logger.warning("Deploying to tracked branch %s and then will "
                           "auto-sync %s" %
                           (dbtarget.tracked_branch.name, dbtarget.name))
            dbtarget = dbtarget.tracked_branch
        elif dbtarget.tracked_branch:
            raise ArgumentError("Cannot deploy to tracking domain %s.  "
                                "Did you mean domain %s?" %
                                (dbtarget.name, dbtarget.tracked_branch.name))

        if sync and not dbtarget.is_sync_valid and dbtarget.trackers:
            # FIXME: Maybe raise an ArgumentError and request that the
            # command run with --nosync?  Maybe provide a --validate flag?
            # For now, just auto-flip (below).
            pass
        if not dbtarget.is_sync_valid:
            dbtarget.is_sync_valid = True

        if dbtarget.requires_change_manager:
            if not justification:
                raise AuthorizationException(
                    "{0} is under change management control.  Please specify "
                    "--justification.".format(dbtarget))
            validate_justification(user, justification)

        if isinstance(dbsource, Sandbox):
            domainsdir = self.config.get('broker', 'domainsdir')
            targetdir = os.path.join(domainsdir, dbtarget.name)
            filterre = re.compile('^' + dbsource.base_commit + '$')
            found = run_git(['rev-list', 'HEAD'],
                            path=targetdir,
                            logger=logger,
                            filterre=filterre)
            if not found:
                raise ArgumentError("You're trying to deploy a sandbox to a "
                                    "domain that does not contain the commit "
                                    "where the sandbox was branched from.")

        kingdir = self.config.get("broker", "kingdir")
        rundir = self.config.get("broker", "rundir")

        tempdir = mkdtemp(prefix="deploy_",
                          suffix="_%s" % dbsource.name,
                          dir=rundir)
        try:
            run_git([
                "clone", "--shared", "--branch", dbtarget.name, kingdir,
                dbtarget.name
            ],
                    path=tempdir,
                    logger=logger)
            temprepo = os.path.join(tempdir, dbtarget.name)

            # We could try to use fmt-merge-msg but its usage is so obscure that
            # faking it is easier
            merge_msg = []
            merge_msg.append("Merge remote branch 'origin/%s' into %s" %
                             (dbsource.name, dbtarget.name))
            merge_msg.append("")
            merge_msg.append("User: %s" % user)
            merge_msg.append("Request ID: %s" % requestid)
            if justification:
                merge_msg.append("Justification: %s" % justification)
            if comments:
                merge_msg.append("Comments: %s" % comments)

            try:
                run_git([
                    "merge", "--no-ff",
                    "origin/%s" % dbsource.name, "-m", "\n".join(merge_msg)
                ],
                        path=temprepo,
                        logger=logger,
                        loglevel=CLIENT_INFO)
            except ProcessException, e:
                # No need to re-print e, output should have gone to client
                # immediately via the logger.
                raise ArgumentError("Failed to merge changes from %s into %s" %
                                    (dbsource.name, dbtarget.name))
            # FIXME: Run tests before pushing back to template-king.
            # Use a different try/except and a specific error message.

            if dryrun:
                session.rollback()
                return

            run_git(["push", "origin", dbtarget.name],
                    path=temprepo,
                    logger=logger)
コード例 #17
0
    def render(self, session, logger, dbuser, domain, track, start,
               change_manager, comments, allow_manage, **arguments):
        if not dbuser:
            raise AuthorizationException("Cannot create a domain without "
                                         "an authenticated connection.")

        Branch.get_unique(session, domain, preclude=True)

        valid = re.compile('^[a-zA-Z0-9_.-]+$')
        if (not valid.match(domain)):
            raise ArgumentError("Domain name '%s' is not valid." % domain)

        # FIXME: Verify that track is a valid branch name?
        # Or just let the branch command fail?

        compiler = self.config.get("panc", "pan_compiler")
        dbtracked = None
        if track:
            dbtracked = Branch.get_unique(session, track, compel=True)
            if getattr(dbtracked, "tracked_branch", None):
                raise ArgumentError("Cannot nest tracking.  Try tracking "
                                    "{0:l} directly.".format(
                                        dbtracked.tracked_branch))
            start_point = dbtracked
            if change_manager:
                raise ArgumentError("Cannot enforce a change manager for "
                                    "tracking domains.")
        else:
            if not start:
                start = self.config.get("broker", "default_domain_start")
            start_point = Branch.get_unique(session, start, compel=True)

        dbdomain = Domain(name=domain,
                          owner=dbuser,
                          compiler=compiler,
                          tracked_branch=dbtracked,
                          requires_change_manager=bool(change_manager),
                          comments=comments)
        session.add(dbdomain)
        if allow_manage is not None:
            dbdomain.allow_manage = allow_manage
        session.flush()

        domainsdir = self.config.get("broker", "domainsdir")
        clonedir = os.path.join(domainsdir, dbdomain.name)
        if os.path.exists(clonedir):
            raise InternalError("Domain directory already exists")

        kingdir = self.config.get("broker", "kingdir")
        cmd = ["branch"]
        if track:
            cmd.append("--track")
        else:
            cmd.append("--no-track")
        cmd.append(dbdomain.name)
        cmd.append(start_point.name)
        run_git(cmd, path=kingdir, logger=logger)

        # If the branch command above fails the DB will roll back as normal.
        # If the command below fails we need to clean up from itself and above.
        try:
            run_git(
                ["clone", "--branch", dbdomain.name, kingdir, dbdomain.name],
                path=domainsdir,
                logger=logger)
        except ProcessException, e:
            try:
                remove_dir(clonedir, logger=logger)
                run_git(["branch", "-D", dbdomain.name],
                        path=kingdir,
                        logger=logger)
            except ProcessException, e2:
                logger.info("Exception while cleaning up: %s", e2)