Ejemplo n.º 1
0
def switch_to_temp_branch(repo: BlueprintRepo, defined_branch_in_file: str):
    stashed_flag = False
    created_remote_flag = False
    created_local_temp_branch = False
    random_suffix = "".join(
        random.choice(string.ascii_lowercase) for i in range(10))
    uncommitted_branch_name = UNCOMMITTED_BRANCH_NAME + defined_branch_in_file + "-" + random_suffix
    stashed_items_before = count_stashed_items(repo)
    try:
        if repo.is_dirty() or repo.untracked_files:
            create_gitkeep_in_branch()
            stash_local_changes(repo)
            stashed_flag = True
        created_local_temp_branch = create_local_temp_branch(
            repo, uncommitted_branch_name)
        if stashed_flag:
            preserve_uncommitted_code(repo)
            commit_to_local_temp_branch(repo)
        create_remote_branch(repo, uncommitted_branch_name)
        created_remote_flag = True
    except Exception as e:
        logger.debug(f"An issue while creating temp branch: {str(e)}")
        if not stashed_flag and (count_stashed_items(repo) >
                                 stashed_items_before):
            revert_from_uncommitted_code(repo)
        if created_local_temp_branch:
            revert_from_local_temp_branch(repo, defined_branch_in_file,
                                          stashed_flag)
            delete_temp_local_branch(repo, defined_branch_in_file)
        if created_remote_flag:
            delete_temp_remote_branch(repo, defined_branch_in_file)
        raise

    return uncommitted_branch_name
Ejemplo n.º 2
0
def create_temp_branch_and_stash_if_needed(repo: BlueprintRepo,
                                           working_branch: str) -> str:
    temp_working_branch = ""
    # Checking if:
    # 1) User has specified not use local (specified a branch) (This func is only called if not specified)
    # 2) User is in an actual git dir (working_branch)
    # 3) There is even a need to create a temp branch for out-of-sync reasons:
    #   either repo.is_dirty() (changes have not been committed locally)
    #   or not repo.is_current_branch_synced() (changes committed locally but not pushed to remote)

    if working_branch and not repo.is_current_state_synced_with_remote():
        try:
            temp_working_branch = switch_to_temp_branch(repo, working_branch)
            BaseCommand.info(
                "Using your local blueprint changes (including uncommitted changes and/or untracked files)"
            )
            logger.debug(
                f"Using temp branch: {temp_working_branch} "
                f"(This shall include any uncommitted changes and/or untracked files)"
            )
        except Exception as e:
            logger.error(
                f"Was not able push your latest changes to temp branch for validation. Reason: {str(e)}"
            )
    return temp_working_branch
Ejemplo n.º 3
0
def is_tf_blueprint(blueprint_name: str, repo: BlueprintRepo) -> bool:
    tf_sandbox_flag = False
    yaml_obj = repo.get_blueprint_yaml(blueprint_name)
    if "services" in yaml_obj.keys():
        if len(yaml_obj["services"]):
            tf_sandbox_flag = True
    return tf_sandbox_flag
Ejemplo n.º 4
0
def is_k8s_blueprint(blueprint_name: str, repo: BlueprintRepo) -> bool:
    k8s_sandbox_flag = False
    yaml_obj = repo.get_blueprint_yaml(blueprint_name)
    for cloud in yaml_obj["clouds"]:
        if "/" in cloud:
            k8s_sandbox_flag = True
    return k8s_sandbox_flag
Ejemplo n.º 5
0
def debug_output_about_repo_examination(repo: BlueprintRepo,
                                        blueprint_name: str):
    if not repo.repo_has_blueprint(blueprint_name):
        logger.debug(
            f"Current repo does not contain a definition for the blueprint '{blueprint_name}'."
        )
    if repo.is_dirty():
        logger.debug("You have uncommitted changes")
    if repo.untracked_files:
        logger.debug(
            "Untracked files detected - only staged or committed files will be used when testing local changes"
        )
    if not repo.current_branch_exists_on_remote():
        logger.debug("Your current local branch doesn't exist on remote")
        # raise BadBlueprintRepo("Your current local branch doesn't exist on remote")
    if not repo.is_current_branch_synced():
        logger.debug("Your local branch is not synced with remote")
Ejemplo n.º 6
0
def figure_out_branches(user_defined_branch: str, blueprint_name: str):
    temp_working_branch = ""
    repo = None
    stashed_flag = False
    success = True
    if user_defined_branch:
        working_branch = user_defined_branch
    else:
        # Try to detect branch from current git-enabled folder
        logger.debug("Branch hasn't been specified. Trying to identify branch from current working directory")
        try:
            repo = BlueprintRepo(os.getcwd())
            examine_blueprint_working_branch(repo, blueprint_name=blueprint_name)
            working_branch = get_blueprint_working_branch(repo)
            BaseCommand.fyi_info(f"Automatically detected current working branch: {working_branch}")

        except Exception as e:
            working_branch = None
            logger.error(f"Branch could not be identified/used from the working directory; reason: {e}.")
            success = False

        # Checking if:
        # 1) User has specified not use local (specified a branch)
        # 2) User is in an actual git dir (working_branch)
        # 3) There is even a need to create a temp branch for out-of-sync reasons:
        #   either repo.is_dirty() (changes have not been committed locally)
        #   or not repo.is_current_branch_synced() (changes committed locally but not pushed to remote)
        if not user_defined_branch and working_branch and not repo.is_current_state_synced_with_remote():
            try:
                temp_working_branch, stashed_flag = switch_to_temp_branch(repo, working_branch)
                BaseCommand.info(
                    "Using your local blueprint changes (including uncommitted changes and/or untracked files)"
                )
                logger.debug(
                    f"Using temp branch: {temp_working_branch} "
                    f"(This shall include any uncommitted changes and/or untracked files)"
                )
            except Exception as e:
                logger.error(f"Was not able push your latest changes to temp branch for validation. Reason: {str(e)}")
                success = False

    return repo, working_branch, temp_working_branch, stashed_flag, success
Ejemplo n.º 7
0
def examine_blueprint_working_branch(repo: BlueprintRepo, blueprint_name: str) -> None:
    if repo.is_repo_detached():
        raise BadBlueprintRepo("Repo's HEAD is in detached state")

    if not repo.repo_has_blueprint(blueprint_name):
        logger.debug(f"Current repo does not contain a definition for the blueprint '{blueprint_name}'.")

    if repo.is_dirty():
        logger.debug("You have uncommitted changes")

    if repo.untracked_files:
        logger.debug(
            "Untracked files detected - only staged or committed files will be used when testing local changes"
        )

    if not repo.current_branch_exists_on_remote():
        logger.debug("Your current local branch doesn't exist on remote")
        # raise BadBlueprintRepo("Your current local branch doesn't exist on remote")

    if not repo.is_current_branch_synced():
        logger.debug("Your local branch is not synced with remote")
    return
Ejemplo n.º 8
0
def get_and_check_folder_based_repo(blueprint_name: str) -> BlueprintRepo:
    # Try to detect branch from current git-enabled folder
    logger.debug(
        "Branch hasn't been specified. Trying to identify branch from current working directory"
    )
    try:
        repo = BlueprintRepo(os.getcwd())
        check_repo_for_errors(repo)
        debug_output_about_repo_examination(repo, blueprint_name)
    except Exception as e:
        logger.error(
            f"Branch could not be identified/used from the working directory; reason: {e}."
        )
        raise
    return repo
Ejemplo n.º 9
0
 def setUp(self):
     self.test_dir = tempfile.mkdtemp()
     Repo.clone_from(self.git_repo_url, self.test_dir)
     self.bp_repo = BlueprintRepo(self.test_dir)
Ejemplo n.º 10
0
class TestBlueprintRepo(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        super(TestBlueprintRepo, cls).setUpClass()
        cls.git_repo_url = "https://github.com/QualiSystemsLab/colony-demo-space.git"

    def setUp(self):
        self.test_dir = tempfile.mkdtemp()
        Repo.clone_from(self.git_repo_url, self.test_dir)
        self.bp_repo = BlueprintRepo(self.test_dir)

    def test_blueprint_repo_has_blueprints(self):
        bp_list = self.bp_repo.blueprints
        self.assertTrue(len(bp_list))

    def test_has_non_existing_blueprint(self):
        fake_bp = "MyTestBp"
        self.assertFalse(self.bp_repo.repo_has_blueprint(fake_bp))

    def test_has_blueprint(self):
        bp_name = "promotions-manager-all-aws"
        self.assertTrue(self.bp_repo.repo_has_blueprint(bp_name))

    def test_create_repo_from_non_git_dir(self):
        with tempfile.TemporaryDirectory() as wrong_dir:
            self.assertRaises(BadBlueprintRepo, BlueprintRepo, wrong_dir)

    def test_raise_on_bare_repo(self):
        with tempfile.TemporaryDirectory() as wrong_dir:
            Repo.init(wrong_dir, bare=True)
            self.assertRaises(BadBlueprintRepo, BlueprintRepo, wrong_dir)

    def test_raise_on_repo_without_blueprints_dir(self):
        with tempfile.TemporaryDirectory() as temp_dir:
            Repo.init(temp_dir)
            self.assertRaises(BadBlueprintRepo, BlueprintRepo, temp_dir)

    def test_raise_on_repo_without_remotes(self):
        with tempfile.TemporaryDirectory() as temp_dir:
            Repo.init(temp_dir)
            os.mkdir(f"{temp_dir}/blueprints")
            self.assertRaises(BadBlueprintRepo, BlueprintRepo, temp_dir)

    def test_has_remote_branch(self):
        self.assertTrue(self.bp_repo.current_branch_exists_on_remote())

    def test_no_branch_on_remote(self):
        local_branch = "my_super_branch"
        new_branch = self.bp_repo.create_head(local_branch)
        assert self.bp_repo.active_branch != new_branch
        new_branch.checkout()
        self.assertFalse(self.bp_repo.current_branch_exists_on_remote())

    def test_is_synced(self):
        self.assertTrue(self.bp_repo.is_current_branch_synced())

    def test_repo_not_synced(self):
        index = self.bp_repo.index

        new_file_path = os.path.join(self.test_dir, "new-file-name")
        open(new_file_path, "w").close()
        index.add([new_file_path])

        author = Actor("An author", "*****@*****.**")
        committer = Actor("A committer", "*****@*****.**")

        index.commit("my commit message", author=author, committer=committer)
        self.assertFalse(self.bp_repo.is_current_branch_synced())

    def tearDown(self):
        # Close the file, the directory will be removed after the test
        shutil.rmtree(self.test_dir)
Ejemplo n.º 11
0
    def do_start(self):
        blueprint_name = self.args["<blueprint_name>"]
        branch = self.args.get("--branch")
        commit = self.args.get("--commit")
        name = self.args["--name"]
        timeout = self.args["--wait"]

        if timeout is not None:
            try:
                timeout = int(timeout)
            except ValueError:
                raise DocoptExit("Timeout must be a number")

            if timeout < 0:
                raise DocoptExit("Timeout must be positive")

        try:
            duration = int(self.args["--duration"] or 120)
            if duration <= 0:
                raise DocoptExit("Duration must be positive")

        except ValueError:
            raise DocoptExit("Duration must be a number")

        if commit and branch is None:
            raise DocoptExit("Since commit is specified, branch is required")

        inputs = parse_comma_separated_string(self.args["--inputs"])
        artifacts = parse_comma_separated_string(self.args["--artifacts"])

        repo, working_branch, temp_working_branch, stashed_flag, success = figure_out_branches(
            branch, blueprint_name)

        if not success:
            return self.error("Unable to start Sandbox")

        # TODO(ddovbii): This obtaining default values magic must be refactored
        logger.debug(
            "Trying to obtain default values for artifacts and inputs from local git blueprint repo"
        )
        try:
            repo = BlueprintRepo(os.getcwd())
            if not repo.is_current_branch_synced():
                logger.debug(
                    "Skipping obtaining values since local branch is not synced with remote"
                )
            else:
                for art_name, art_path in repo.get_blueprint_artifacts(
                        blueprint_name).items():
                    if art_name not in artifacts and art_path is not None:
                        logger.debug(
                            f"Artifact `{art_name}` has been set with default path `{art_path}`"
                        )
                        artifacts[art_name] = art_path

                for input_name, input_value in repo.get_blueprint_default_inputs(
                        blueprint_name).items():
                    if input_name not in inputs and input_value is not None:
                        logger.debug(
                            f"Parameter `{input_name}` has been set with default value `{input_value}`"
                        )
                        inputs[input_name] = input_value

        except Exception as e:
            logger.debug(f"Unable to obtain default values. Details: {e}")

        branch_to_be_used = temp_working_branch or working_branch

        if name is None:
            suffix = datetime.datetime.now().strftime("%b%d-%H:%M:%S")
            branch_name_or_type = ""
            if working_branch:
                branch_name_or_type = working_branch + "-"
            if temp_working_branch:
                branch_name_or_type = "localchanges-"
            name = f"{blueprint_name}-{branch_name_or_type}{suffix}"

        try:
            sandbox_id = self.manager.start(name, blueprint_name, duration,
                                            branch_to_be_used, commit,
                                            artifacts, inputs)
            BaseCommand.action_announcement("Starting sandbox")
            BaseCommand.important_value("Id: ", sandbox_id)
            BaseCommand.url(
                prefix_message="URL: ",
                message=self.manager.get_sandbox_ui_link(sandbox_id))

        except Exception as e:
            logger.exception(e, exc_info=False)
            sandbox_id = None
            if temp_working_branch.startswith(UNCOMMITTED_BRANCH_NAME):
                revert_from_temp_branch(repo, working_branch, stashed_flag)
                delete_temp_branch(repo, temp_working_branch)
            return self.die()

        # todo: I think the below can be simplified and refactored
        if timeout is None:
            revert_wait_and_delete_temp_branch(self.manager, blueprint_name,
                                               repo, sandbox_id, stashed_flag,
                                               temp_working_branch,
                                               working_branch)
            return self.success("The Sandbox was created")

        else:
            start_time = datetime.datetime.now()

            logger.debug(f"Waiting for the Sandbox {sandbox_id} to start...")
            # Waiting loop
            while (datetime.datetime.now() -
                   start_time).seconds < timeout * 60:
                sandbox = self.manager.get(sandbox_id)
                status = getattr(sandbox, "sandbox_status")
                if status == "Active":
                    revert_and_delete_temp_branch(repo, working_branch,
                                                  temp_working_branch,
                                                  stashed_flag)
                    return self.success(sandbox_id)

                elif status == "Launching":
                    progress = getattr(sandbox, "launching_progress")
                    for check_points, properties in progress.items():
                        logger.debug(f"{check_points}: {properties['status']}")
                    time.sleep(30)

                else:
                    revert_and_delete_temp_branch(repo, working_branch,
                                                  temp_working_branch,
                                                  stashed_flag)
                    return self.die(
                        f"The Sandbox {sandbox_id} has started. Current state is: {status}"
                    )

            # timeout exceeded
            logger.error(
                f"Sandbox {sandbox_id} was not active after the provided timeout of {timeout} minutes"
            )
            revert_and_delete_temp_branch(repo, working_branch,
                                          temp_working_branch, stashed_flag)
            return self.die()
Ejemplo n.º 12
0
def check_repo_for_errors(repo: BlueprintRepo) -> None:
    if repo.is_repo_detached():
        logger.error("Repo's HEAD is in detached state")
        raise BadBlueprintRepo("Repo's HEAD is in detached state")
Ejemplo n.º 13
0
def delete_temp_local_branch(repo: BlueprintRepo, temp_branch: str) -> None:
    logger.debug(f"[GIT] Deleting local branch {temp_branch}")
    repo.delete_head("-D", temp_branch)