Beispiel #1
0
def configure_kubectl(config: str, env: Optional[str], local: Optional[bool],
                      var: Dict[str, str]) -> None:
    """
    Configure kubectl so you can connect to the cluster

    This command constructs a configuration with prepopulated server and certificate authority data values for the managed cluster.

    If you have the KUBECONFIG environment variable set, then the resulting configuration file is created at that location.
    Otherwise, by default, the resulting configuration file is created at the default kubeconfig path (.kube/config) in your home directory.
    """
    try:
        opta_acquire_lock()
        config = check_opta_file_exists(config)
        if local:
            config = local_setup(config, input_variables=var)
        layer = Layer.load_from_yaml(config,
                                     env,
                                     input_variables=var,
                                     strict_input_variables=False)
        amplitude_client.send_event(
            amplitude_client.CONFIGURE_KUBECTL_EVENT,
            event_properties={
                "org_name": layer.org_name,
                "layer_name": layer.name
            },
        )
        layer.verify_cloud_credentials()
        purge_opta_kube_config(layer)
        configure(layer)
        load_opta_kube_config_to_default(layer)
    finally:
        opta_release_lock()
Beispiel #2
0
def apply(
    config: str,
    env: Optional[str],
    refresh: bool,
    local: bool,
    image_tag: Optional[str],
    test: bool,
    auto_approve: bool,
    detailed_plan: bool,
    var: Dict[str, str],
) -> None:
    """Create or update infrastructure

    Apply changes to match the Opta configuration
    files in the current directory.

    Examples:

    opta apply --auto-approve

    opta apply --auto-approve --var variable1=value1

    opta apply -c my-config.yaml --image-tag=v1
    """
    try:
        opta_acquire_lock()
        config = check_opta_file_exists(config)
        _apply(
            config,
            env,
            refresh,
            local,
            image_tag,
            test,
            auto_approve,
            detailed_plan=detailed_plan,
            input_variables=var,
        )
    finally:
        opta_release_lock()
Beispiel #3
0
def force_unlock(
    config: str, env: Optional[str], local: Optional[bool], var: Dict[str, str],
) -> None:
    """Release a stuck lock on the current workspace

    Manually unlock the state for the defined configuration.

    This will not modify your infrastructure. This command removes the lock on the
    state for the current workspace.

    Examples:

    opta force-unlock -c my-config.yaml -e prod
    """
    try:
        opta_acquire_lock()
        tf_flags: List[str] = []
        config = check_opta_file_exists(config)
        if local:
            config = local_setup(config, input_variables=var)
        amplitude_client.send_event(amplitude_client.FORCE_UNLOCK_EVENT)
        layer = Layer.load_from_yaml(
            config, env, input_variables=var, strict_input_variables=False
        )
        layer.verify_cloud_credentials()
        modules = Terraform.get_existing_modules(layer)
        layer.modules = [x for x in layer.modules if x.name in modules]
        gen_all(layer)

        tf_lock_exists, _ = Terraform.tf_lock_details(layer)
        if tf_lock_exists:
            Terraform.init(layer=layer)
            click.confirm(
                "This will remove the lock on the remote state."
                "\nPlease make sure that no other instance of opta command is running on this file."
                "\nDo you still want to proceed?",
                abort=True,
            )
            tf_flags.append("-force")
            Terraform.force_unlock(layer, *tf_flags)

        if Terraform.download_state(layer):
            if layer.parent is not None or "k8scluster" in modules:
                set_kube_config(layer)
                kube_context = layer.get_cloud_client().get_kube_context_name()
                pending_upgrade_release_list = Helm.get_helm_list(
                    kube_context=kube_context, status="pending-upgrade"
                )
                click.confirm(
                    "Do you also wish to Rollback the Helm releases in Pending-Upgrade State?"
                    "\nPlease make sure that no other instance of opta command is running on this file."
                    "\nDo you still want to proceed?",
                    abort=True,
                )

                for release in pending_upgrade_release_list:
                    Helm.rollback_helm(
                        kube_context,
                        release["name"],
                        namespace=release["namespace"],
                        revision=release["revision"],
                    )
    finally:
        opta_release_lock()
Beispiel #4
0
def destroy(
    config: str,
    env: Optional[str],
    auto_approve: bool,
    detailed_plan: bool,
    local: Optional[bool],
    var: Dict[str, str],
) -> None:
    """Destroy all opta resources from the current config

    To destroy an environment, you have to first destroy all the services first.

    Examples:

    opta destroy -c my-service.yaml --auto-approve

    opta destroy -c my-env.yaml --auto-approve
    """
    try:
        opta_acquire_lock()
        pre_check()
        logger.warning(
            "You are destroying your cloud infra state. DO NOT, I REPEAT, DO NOT do this as "
            "an attempt to debug a weird/errored apply. What you have created is not some ephemeral object that can be "
            "tossed arbitrarily (perhaps some day) and destroying unnecessarily just to reapply typically makes it "
            "worse. If you're doing this cause you are really trying to destroy the environment entirely, then that's"
            "perfectly fine-- if not then please reach out to the opta team in the slack workspace "
            "(https://slack.opta.dev) and I promise that they'll be happy to help debug."
        )

        config = check_opta_file_exists(config)
        if local:
            config, _ = _handle_local_flag(config, False)
            _clean_tf_folder()
        layer = Layer.load_from_yaml(config, env, input_variables=var)
        event_properties: Dict = layer.get_event_properties()
        amplitude_client.send_event(
            amplitude_client.DESTROY_EVENT, event_properties=event_properties,
        )
        layer.verify_cloud_credentials()
        layer.validate_required_path_dependencies()
        if not Terraform.download_state(layer):
            logger.info(
                "The opta state could not be found. This may happen if destroy ran successfully before."
            )
            return

        tf_lock_exists, _ = Terraform.tf_lock_details(layer)
        if tf_lock_exists:
            raise UserErrors(USER_ERROR_TF_LOCK)

        # Any child layers should be destroyed first before the current layer.
        children_layers = _fetch_children_layers(layer)
        if children_layers:
            # TODO: ideally we can just automatically destroy them but it's
            # complicated...
            logger.error(
                "Found the following services that depend on this environment. Please run `opta destroy` on them first!\n"
                + "\n".join(children_layers)
            )
            raise UserErrors("Dependant services found!")

        tf_flags: List[str] = []
        if auto_approve:
            sleep_time = 5
            logger.info(
                f"{attr('bold')}Opta will now destroy the {attr('underlined')}{layer.name}{attr(0)}"
                f"{attr('bold')} layer.{attr(0)}\n"
                f"{attr('bold')}Sleeping for {attr('underlined')}{sleep_time} secs{attr(0)}"
                f"{attr('bold')}, press Ctrl+C to Abort.{attr(0)}"
            )
            time.sleep(sleep_time)
            tf_flags.append("-auto-approve")
        modules = Terraform.get_existing_modules(layer)
        layer.modules = [x for x in layer.modules if x.name in modules]
        gen_all(layer)
        Terraform.init(False, "-reconfigure", layer=layer)
        Terraform.refresh(layer)

        idx = len(layer.modules) - 1
        for module in reversed(layer.modules):
            try:
                module_address_prefix = f"-target=module.{module.name}"
                logger.info("Planning your changes (might take a minute)")
                Terraform.plan(
                    "-lock=false",
                    "-input=false",
                    "-destroy",
                    f"-out={TF_PLAN_PATH}",
                    layer=layer,
                    *list([module_address_prefix]),
                )
                PlanDisplayer.display(detailed_plan=detailed_plan)
                tf_flags = []
                if not auto_approve:
                    click.confirm(
                        "The above are the planned changes for your opta run. Do you approve?",
                        abort=True,
                    )
                else:
                    tf_flags.append("-auto-approve")
                Terraform.apply(layer, *tf_flags, TF_PLAN_PATH, no_init=True, quiet=False)
                layer.post_delete(idx)
                idx -= 1
            except Exception as e:
                raise e

        Terraform.delete_state_storage(layer)
    finally:
        opta_release_lock()
Beispiel #5
0
def deploy(
    image: str,
    config: str,
    env: Optional[str],
    tag: Optional[str],
    auto_approve: bool,
    detailed_plan: bool,
    local: Optional[bool],
    var: Dict[str, str],
) -> None:
    """Deploys an image to Kubernetes

    - Pushes the local image to private container registry (ECR, GCR, ACR), if configuration contains `image: AUTO`,
      else uses the image provided from a Repo.

    - Update the kubernetes deployment to use the new image.

    - Create new pods to use the new image - automatically done by kubernetes.

    Examples:

    opta deploy -c image-auto-configuration.yaml -i image:local --auto-approve

    opta deploy -c repo-provided-configuration.yaml -e prod

    opta deploy -c my-service.yaml -i my-image:latest --local

    Documentation: https://docs.opta.dev/features/custom_image/

    """

    try:
        opta_acquire_lock()
        pre_check()

        config = check_opta_file_exists(config)
        if local:
            config = local_setup(config,
                                 image_tag=tag,
                                 refresh_local_env=True,
                                 input_variables=var)
        if not is_service_config(config):
            raise UserErrors(
                fmt_msg("""
                Opta deploy can only run on service yaml files. This is an environment yaml file.
                ~See https://docs.opta.dev/getting-started/ for more details.
                ~
                ~(We think that this is an environment yaml file, because service yaml must
                ~specify the "environments" field).
                """))

        layer = Layer.load_from_yaml(config, env, input_variables=var)
        amplitude_client.send_event(
            amplitude_client.DEPLOY_EVENT,
            event_properties={
                "org_name": layer.org_name,
                "layer_name": layer.name
            },
        )
        is_auto = __check_layer_and_image(layer, image)
        layer.verify_cloud_credentials()
        layer.validate_required_path_dependencies()
        if Terraform.download_state(layer):
            tf_lock_exists, _ = Terraform.tf_lock_details(layer)
            if tf_lock_exists:
                raise UserErrors(USER_ERROR_TF_LOCK)

        try:
            outputs = Terraform.get_outputs(layer)
        except MissingState:
            outputs = {}

        image_digest, image_tag = (None, None)
        if is_auto:
            if "docker_repo_url" not in outputs or outputs[
                    "docker_repo_url"] == "":
                logger.info(
                    "Did not find docker repository in state, so applying once to create it before deployment"
                )
                _apply(
                    config=config,
                    env=env,
                    refresh=False,
                    image_tag=None,
                    test=False,
                    local=local,
                    auto_approve=auto_approve,
                    stdout_logs=False,
                    detailed_plan=detailed_plan,
                    input_variables=var,
                )
            if image is not None:
                image_digest, image_tag = push_image(
                    image=image,
                    config=config,
                    env=env,
                    tag=tag,
                    input_variables=var,
                )
        _apply(
            config=config,
            env=env,
            refresh=False,
            image_tag=None,
            test=False,
            local=local,
            auto_approve=auto_approve,
            image_digest=image_digest,
            detailed_plan=detailed_plan,
            input_variables=var,
        )
    finally:
        opta_release_lock()