content: #  Copyright (c) ZenML GmbH 2021. All Rights Reserved.
  #
  #  Licensed under the Apache License, Version 2.0 (the "License");
  #  you may not use this file except in compliance with the License.
  #  You may obtain a copy of the License at:
  #
  #       https://www.apache.org/licenses/LICENSE-2.0
  #
  #  Unless required by applicable law or agreed to in writing, software
  #  distributed under the License is distributed on an "AS IS" BASIS,
  #  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
  #  or implied. See the License for the specific language governing
  #  permissions and limitations under the License.
  """Stack CLI."""
  
  import getpass
  import re
  import time
  import webbrowser
  from typing import (
      TYPE_CHECKING,
      Any,
      Dict,
      List,
      Optional,
      Set,
      Union,
  )
  from uuid import UUID
  
  import click
  from rich.console import Console
  from rich.markdown import Markdown
  from rich.prompt import Confirm
  from rich.style import Style
  from rich.syntax import Syntax
  
  import zenml
  from zenml.analytics.enums import AnalyticsEvent
  from zenml.analytics.utils import track_handler
  from zenml.cli import utils as cli_utils
  from zenml.cli.cli import TagGroup, cli
  from zenml.cli.text_utils import OldSchoolMarkdownHeading
  from zenml.cli.utils import (
      OutputFormat,
      _component_display_name,
      is_sorted_or_filtered,
      list_options,
      print_model_url,
  )
  from zenml.client import Client
  from zenml.console import console
  from zenml.enums import (
      CliCategories,
      StackComponentType,
      StackDeploymentProvider,
  )
  from zenml.exceptions import (
      IllegalOperationError,
  )
  from zenml.logger import get_logger
  from zenml.models import (
      ComponentInfo,
      ServiceConnectorInfo,
      ServiceConnectorResourcesInfo,
      StackFilter,
      StackRequest,
  )
  from zenml.models.v2.core.service_connector import (
      ServiceConnectorRequest,
      ServiceConnectorResponse,
  )
  from zenml.service_connectors.service_connector_utils import (
      get_resources_options_from_resource_model_for_full_stack,
  )
  from zenml.utils import requirements_utils
  from zenml.utils.dashboard_utils import get_component_url, get_stack_url
  from zenml.utils.time_utils import utc_now_tz_aware
  from zenml.utils.yaml_utils import read_yaml, write_yaml
  
  if TYPE_CHECKING:
      from zenml.models import StackResponse
  
  logger = get_logger(__name__)
  
  
  @cli.group(
      cls=TagGroup,
      tag=CliCategories.MANAGEMENT_TOOLS,
  )
  def stack() -> None:
      """Manage stacks."""
  
  
  @stack.command(
      "register",
      context_settings=dict(ignore_unknown_options=True),
      help="Register a stack with components.",
  )
  @click.argument("stack_name", type=str, required=True)
  @click.option(
      "-a",
      "--artifact-store",
      "artifact_store",
      help="Name of the artifact store for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-o",
      "--orchestrator",
      "orchestrator",
      help="Name of the orchestrator for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-c",
      "--container_registry",
      "container_registry",
      help="Name of the container registry for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-r",
      "--model_registry",
      "model_registry",
      help="Name of the model registry for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-s",
      "--step_operator",
      "step_operator",
      help="Name of the step operator for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-f",
      "--feature_store",
      "feature_store",
      help="Name of the feature store for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-d",
      "--model_deployer",
      "model_deployer",
      help="Name of the model deployer for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-e",
      "--experiment_tracker",
      "experiment_tracker",
      help="Name of the experiment tracker for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-al",
      "--alerter",
      "alerter",
      help="Name of the alerter for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-an",
      "--annotator",
      "annotator",
      help="Name of the annotator for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-dv",
      "--data_validator",
      "data_validator",
      help="Name of the data validator for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-i",
      "--image_builder",
      "image_builder",
      help="Name of the image builder for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-D",
      "--deployer",
      "deployer",
      help="Name of the deployer for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-l",
      "--log_store",
      "log_store",
      help="Name of the log store for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "--set",
      "set_stack",
      is_flag=True,
      help="Immediately set this stack as active.",
      type=click.BOOL,
  )
  @click.option(
      "-p",
      "--provider",
      help="Name of the cloud provider for this stack.",
      type=click.Choice(["aws", "azure", "gcp"]),
      required=False,
  )
  @click.option(
      "-sc",
      "--connector",
      help="Name of the service connector for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "--secret",
      "secrets",
      help="Secret to attach to the stack.",
      type=str,
      required=False,
      multiple=True,
  )
  @click.option(
      "--env",
      "environment_variables",
      help="Environment variables to set when running on this stack. Must be of "
      "the format 'KEY=VALUE'.",
      type=str,
      required=False,
      multiple=True,
  )
  def register_stack(
      stack_name: str,
      artifact_store: Optional[str] = None,
      orchestrator: Optional[str] = None,
      container_registry: Optional[str] = None,
      model_registry: Optional[str] = None,
      step_operator: Optional[str] = None,
      feature_store: Optional[str] = None,
      model_deployer: Optional[str] = None,
      experiment_tracker: Optional[str] = None,
      alerter: Optional[str] = None,
      annotator: Optional[str] = None,
      data_validator: Optional[str] = None,
      image_builder: Optional[str] = None,
      deployer: Optional[str] = None,
      log_store: Optional[str] = None,
      set_stack: bool = False,
      provider: Optional[str] = None,
      connector: Optional[str] = None,
      secrets: List[str] = [],
      environment_variables: List[str] = [],
  ) -> None:
      """Register a stack.
  
      Args:
          stack_name: Unique name of the stack
          artifact_store: Name of the artifact store for this stack.
          orchestrator: Name of the orchestrator for this stack.
          container_registry: Name of the container registry for this stack.
          model_registry: Name of the model registry for this stack.
          step_operator: Name of the step operator for this stack.
          feature_store: Name of the feature store for this stack.
          model_deployer: Name of the model deployer for this stack.
          experiment_tracker: Name of the experiment tracker for this stack.
          alerter: Name of the alerter for this stack.
          annotator: Name of the annotator for this stack.
          data_validator: Name of the data validator for this stack.
          image_builder: Name of the new image builder for this stack.
          deployer: Name of the deployer for this stack.
          log_store: Name of the log store for this stack.
          set_stack: Immediately set this stack as active.
          provider: Name of the cloud provider for this stack.
          connector: Name of the service connector for this stack.
          secrets: List of secrets to attach to the stack.
          environment_variables: List of environment variables to set when
              running on this stack. Must be of the format "KEY=VALUE".
      """
      if (provider is None and connector is None) and (
          artifact_store is None or orchestrator is None
      ):
          cli_utils.error(
              "The only way to register a stack without specifying an "
              "orchestrator and an artifact store is by using either a provider"
              "(-p/--provider) or an existing service connector "
              "(-sc/--connector). Please specify the artifact store and "
              "the orchestrator or the service connector or cloud type settings."
          )
  
      client = Client()
  
      if provider is not None or connector is not None:
          if client.zen_store.is_local_store():
              cli_utils.error(
                  "You are registering a stack using a service connector, but "
                  "this feature cannot be used with a local ZenML deployment. "
                  "ZenML needs to be accessible from the cloud provider to allow "
                  "the stack and its components to be registered automatically. "
                  "Please deploy ZenML in a remote environment as described in "
                  "the documentation: https://docs.zenml.io/deploying-zenml/deploying-zenml "
                  "or use a managed ZenML Pro server instance for quick access "
                  "to this feature and more: https://www.zenml.io/pro"
              )
  
      try:
          client.get_stack(
              name_id_or_prefix=stack_name,
              allow_name_prefix_match=False,
          )
          cli_utils.error(
              f"A stack with name `{stack_name}` already exists, "
              "please use a different name."
          )
      except KeyError:
          pass
  
      environment: Dict[str, str] = {}
      for environment_variable in environment_variables:
          key, value = environment_variable.split("=", 1)
          environment[key] = value
  
      labels: Dict[str, str] = {}
      components: Dict[StackComponentType, List[Union[UUID, ComponentInfo]]] = {}
  
      # Cloud Flow
      created_objects: Set[str] = set()
      service_connector: Optional[Union[UUID, ServiceConnectorInfo]] = None
      if provider is not None and connector is None:
          service_connector_response = None
          use_auto_configure = False
          try:
              service_connector_response, _ = client.create_service_connector(
                  name=stack_name,
                  connector_type=provider,
                  register=False,
                  auto_configure=True,
                  verify=False,
              )
          except NotImplementedError:
              cli_utils.warning(
                  f"The {provider.upper()} service connector libraries are not "
                  "installed properly. Please run `zenml integration install "
                  f"{provider}` and try again to enable auto-discovery of the "
                  "connection configuration."
              )
          except Exception:
              pass
  
          if service_connector_response:
              use_auto_configure = Confirm.ask(
                  f"[bold]{provider.upper()} cloud service connector[/bold] "
                  "has detected connection credentials in your environment.\n"
                  "Would you like to use these credentials or create a new "
                  "configuration by providing connection details?",
                  default=True,
                  show_choices=True,
                  show_default=True,
              )
  
          connector_selected: Optional[int] = None
          if not use_auto_configure:
              service_connector_response = None
              existing_connectors = client.list_service_connectors(
                  connector_type=provider, size=100
              )
              if existing_connectors.total:
                  connector_selected = cli_utils.multi_choice_prompt(
                      object_type=f"{provider.upper()} service connectors",
                      choices=[
                          [connector.name]
                          for connector in existing_connectors.items
                      ],
                      headers=["Name"],
                      prompt_text=f"We found these {provider.upper()} service "
                      "connectors. Do you want to create a new one or use one "
                      "of the existing ones?",
                      default_choice="0",
                      allow_zero_be_a_new_object=True,
                  )
          if use_auto_configure or connector_selected is None:
              service_connector = _get_service_connector_info(
                  cloud_provider=provider,
                  connector_details=service_connector_response,
              )
              created_objects.add("service_connector")
          else:
              selected_connector = existing_connectors.items[connector_selected]
              service_connector = selected_connector.id
              connector = selected_connector.name
              if isinstance(selected_connector.connector_type, str):
                  provider = selected_connector.connector_type
              else:
                  provider = selected_connector.connector_type.connector_type
      elif connector is not None:
          service_connector_response = client.get_service_connector(connector)
          service_connector = service_connector_response.id
          if provider:
              if service_connector_response.type != provider:
                  cli_utils.warning(
                      f"The service connector `{connector}` is not of type `{provider}`."
                  )
          else:
              provider = service_connector_response.type
  
      if service_connector:
          labels["zenml:wizard"] = "true"
          if provider:
              labels["zenml:provider"] = provider
          resources_info = None
          # explore the service connector
          with console.status(
              "Exploring resources available to the service connector...\n"
          ):
              resources_info = (
                  get_resources_options_from_resource_model_for_full_stack(
                      connector_details=service_connector
                  )
              )
          if resources_info is None:
              cli_utils.error(
                  f"Failed to fetch service connector resources information for {service_connector}..."
              )
  
          # create components
          needed_components = (
              (StackComponentType.ARTIFACT_STORE, artifact_store),
              (StackComponentType.ORCHESTRATOR, orchestrator),
              (StackComponentType.CONTAINER_REGISTRY, container_registry),
          )
          for component_type, preset_name in needed_components:
              component_info: Optional[Union[UUID, ComponentInfo]] = None
              if preset_name is not None:
                  component_response = client.get_stack_component(
                      component_type, preset_name
                  )
                  component_info = component_response.id
                  component_name = component_response.name
              else:
                  if isinstance(service_connector, UUID):
                      # find existing components under same connector
                      if (
                          component_type
                          in resources_info.components_resources_info
                      ):
                          existing_components = [
                              existing_response
                              for res_info in resources_info.components_resources_info[
                                  component_type
                              ]
                              for existing_response in res_info.connected_through_service_connector
                          ]
  
                          # if some existing components are found - prompt user what to do
                          component_selected: Optional[int] = None
                          component_selected = cli_utils.multi_choice_prompt(
                              object_type=component_type.value.replace("_", " "),
                              choices=[
                                  [
                                      component.flavor_name,
                                      component.name,
                                      component.configuration or "",
                                      component.connector_resource_id,
                                  ]
                                  for component in existing_components
                              ],
                              headers=[
                                  "Type",
                                  "Name",
                                  "Configuration",
                                  "Connected as",
                              ],
                              prompt_text=f"We found these {component_type.value.replace('_', ' ')} "
                              "connected using the current service connector. Do you "
                              "want to create a new one or use existing one?",
                              default_choice="0",
                              allow_zero_be_a_new_object=True,
                          )
                  else:
                      component_selected = None
  
                  if component_selected is None:
                      component_info = _get_stack_component_info(
                          component_type=component_type.value,
                          cloud_provider=provider
                          or resources_info.connector_type,
                          resources_info=resources_info,
                          service_connector_index=0,
                      )
                      component_name = stack_name
                      created_objects.add(component_type.value)
                  else:
                      selected_component = existing_components[
                          component_selected
                      ]
                      component_info = selected_component.id
                      component_name = selected_component.name
  
              components[component_type] = [component_info]
              if component_type == StackComponentType.ARTIFACT_STORE:
                  artifact_store = component_name
              if component_type == StackComponentType.ORCHESTRATOR:
                  orchestrator = component_name
              if component_type == StackComponentType.CONTAINER_REGISTRY:
                  container_registry = component_name
  
      # normal flow once all components are defined
      with console.status(f"Registering stack '{stack_name}'...\n"):
          for component_type_, component_name_ in [
              (StackComponentType.ARTIFACT_STORE, artifact_store),
              (StackComponentType.ORCHESTRATOR, orchestrator),
              (StackComponentType.ALERTER, alerter),
              (StackComponentType.ANNOTATOR, annotator),
              (StackComponentType.DATA_VALIDATOR, data_validator),
              (StackComponentType.FEATURE_STORE, feature_store),
              (StackComponentType.IMAGE_BUILDER, image_builder),
              (StackComponentType.LOG_STORE, log_store),
              (StackComponentType.MODEL_DEPLOYER, model_deployer),
              (StackComponentType.MODEL_REGISTRY, model_registry),
              (StackComponentType.STEP_OPERATOR, step_operator),
              (StackComponentType.EXPERIMENT_TRACKER, experiment_tracker),
              (StackComponentType.CONTAINER_REGISTRY, container_registry),
              (StackComponentType.DEPLOYER, deployer),
          ]:
              if component_name_ and component_type_ not in components:
                  components[component_type_] = [
                      client.get_stack_component(
                          component_type_, component_name_
                      ).id
                  ]
  
          try:
              created_stack = client.zen_store.create_stack(
                  stack=StackRequest(
                      name=stack_name,
                      components=components,
                      service_connectors=[service_connector]
                      if service_connector
                      else [],
                      labels=labels,
                      secrets=secrets,
                      environment=environment,
                  )
              )
          except (KeyError, IllegalOperationError) as err:
              cli_utils.exception(err)
  
          cli_utils.declare(
              f"Stack '{created_stack.name}' successfully registered!"
          )
          cli_utils.print_stack_configuration(
              stack=created_stack,
              active=created_stack.id == client.active_stack_model.id,
          )
  
      if set_stack:
          client.activate_stack(created_stack.id)
  
          scope = "repository" if client.uses_local_configuration else "global"
          cli_utils.declare(
              f"Active {scope} stack set to:'{created_stack.name}'"
          )
  
      delete_commands = []
      if "service_connector" in created_objects:
          created_objects.remove("service_connector")
          connectors = set()
          for each in created_objects:
              if comps_ := created_stack.components[StackComponentType(each)]:
                  if conn_ := comps_[0].connector:
                      connectors.add(conn_.name)
          for connector in connectors:
              delete_commands.append(
                  "zenml service-connector delete " + connector
              )
      for each in created_objects:
          if comps_ := created_stack.components[StackComponentType(each)]:
              delete_commands.append(
                  f"zenml {each.replace('_', '-')} delete {comps_[0].name}"
              )
      delete_commands.append("zenml stack delete -y " + created_stack.name)
  
      Console().print(
          "To delete the objects created by this command run, please run in a sequence:\n"
      )
      Console().print(Syntax("\n".join(delete_commands[::-1]), "bash"))
  
      print_model_url(get_stack_url(created_stack))
  
  
  @stack.command(
      "update",
      context_settings=dict(ignore_unknown_options=True),
      help="Update a stack with new components.",
  )
  @click.argument("stack_name_or_id", type=str, required=False)
  @click.option(
      "-a",
      "--artifact-store",
      "artifact_store",
      help="Name of the new artifact store for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-o",
      "--orchestrator",
      "orchestrator",
      help="Name of the new orchestrator for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-c",
      "--container_registry",
      "container_registry",
      help="Name of the new container registry for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-r",
      "--model_registry",
      "model_registry",
      help="Name of the model registry for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-s",
      "--step_operator",
      "step_operator",
      help="Name of the new step operator for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-f",
      "--feature_store",
      "feature_store",
      help="Name of the new feature store for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-d",
      "--model_deployer",
      "model_deployer",
      help="Name of the new model deployer for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-e",
      "--experiment_tracker",
      "experiment_tracker",
      help="Name of the new experiment tracker for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-al",
      "--alerter",
      "alerter",
      help="Name of the new alerter for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-an",
      "--annotator",
      "annotator",
      help="Name of the new annotator for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-dv",
      "--data_validator",
      "data_validator",
      help="Name of the data validator for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-i",
      "--image_builder",
      "image_builder",
      help="Name of the image builder for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-D",
      "--deployer",
      "deployer",
      help="Name of the deployer for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "-l",
      "--log_store",
      "log_store",
      help="Name of the log store for this stack.",
      type=str,
      required=False,
  )
  @click.option(
      "--secret",
      "secrets",
      help="Secrets to attach to the stack.",
      type=str,
      required=False,
      multiple=True,
  )
  @click.option(
      "--remove-secret",
      "remove_secrets",
      help="Secrets to remove from the stack.",
      type=str,
      required=False,
      multiple=True,
  )
  @click.option(
      "--env",
      "environment_variables",
      help="Environment variables to set when running on this stack. Must be of "
      "the format 'KEY=VALUE'. To remove an environment variable from the "
      "stack, use an empty value, e.g. 'KEY='",
      type=str,
      required=False,
      multiple=True,
  )
  def update_stack(
      stack_name_or_id: Optional[str] = None,
      artifact_store: Optional[str] = None,
      orchestrator: Optional[str] = None,
      container_registry: Optional[str] = None,
      step_operator: Optional[str] = None,
      feature_store: Optional[str] = None,
      model_deployer: Optional[str] = None,
      experiment_tracker: Optional[str] = None,
      alerter: Optional[str] = None,
      annotator: Optional[str] = None,
      data_validator: Optional[str] = None,
      image_builder: Optional[str] = None,
      model_registry: Optional[str] = None,
      deployer: Optional[str] = None,
      log_store: Optional[str] = None,
      secrets: List[str] = [],
      remove_secrets: List[str] = [],
      environment_variables: List[str] = [],
  ) -> None:
      """Update a stack.
  
      Args:
          stack_name_or_id: Name or id of the stack to update.
          artifact_store: Name of the new artifact store for this stack.
          orchestrator: Name of the new orchestrator for this stack.
          container_registry: Name of the new container registry for this stack.
          step_operator: Name of the new step operator for this stack.
          feature_store: Name of the new feature store for this stack.
          model_deployer: Name of the new model deployer for this stack.
          experiment_tracker: Name of the new experiment tracker for this
              stack.
          alerter: Name of the new alerter for this stack.
          annotator: Name of the new annotator for this stack.
          data_validator: Name of the new data validator for this stack.
          image_builder: Name of the new image builder for this stack.
          model_registry: Name of the new model registry for this stack.
          deployer: Name of the new deployer for this stack.
          log_store: Name of the log store for this stack.
          secrets: Secrets to attach to the stack.
          remove_secrets: Secrets to remove from the stack.
          environment_variables: Environment variables to set when running on this
              stack. Must be of the format "KEY=VALUE".
      """
      client = Client()
  
      environment: Dict[str, Any] = {}
      for environment_variable in environment_variables:
          key, value = environment_variable.split("=", 1)
          # Fallback to None if the value is empty so the existing environment
          # variable is removed
          environment[key] = value or None
  
      with console.status("Updating stack...\n"):
          updates: Dict[StackComponentType, List[Union[str, UUID]]] = dict()
          if artifact_store:
              updates[StackComponentType.ARTIFACT_STORE] = [artifact_store]
          if alerter:
              updates[StackComponentType.ALERTER] = [alerter]
          if annotator:
              updates[StackComponentType.ANNOTATOR] = [annotator]
          if container_registry:
              updates[StackComponentType.CONTAINER_REGISTRY] = [
                  container_registry
              ]
          if data_validator:
              updates[StackComponentType.DATA_VALIDATOR] = [data_validator]
          if experiment_tracker:
              updates[StackComponentType.EXPERIMENT_TRACKER] = [
                  experiment_tracker
              ]
          if feature_store:
              updates[StackComponentType.FEATURE_STORE] = [feature_store]
          if model_registry:
              updates[StackComponentType.MODEL_REGISTRY] = [model_registry]
          if image_builder:
              updates[StackComponentType.IMAGE_BUILDER] = [image_builder]
          if model_deployer:
              updates[StackComponentType.MODEL_DEPLOYER] = [model_deployer]
          if orchestrator:
              updates[StackComponentType.ORCHESTRATOR] = [orchestrator]
          if step_operator:
              updates[StackComponentType.STEP_OPERATOR] = [step_operator]
          if deployer:
              updates[StackComponentType.DEPLOYER] = [deployer]
          if log_store:
              updates[StackComponentType.LOG_STORE] = [log_store]
  
          try:
              updated_stack = client.update_stack(
                  name_id_or_prefix=stack_name_or_id,
                  component_updates=updates,
                  add_secrets=secrets,
                  remove_secrets=remove_secrets,
                  environment=environment,
              )
  
          except (KeyError, IllegalOperationError) as err:
              cli_utils.exception(err)
  
          cli_utils.declare(
              f"Stack `{updated_stack.name}` successfully updated!"
          )
      print_model_url(get_stack_url(updated_stack))
  
  
  @stack.command(
      "remove-component",
      context_settings=dict(ignore_unknown_options=True),
      help="Remove stack components from a stack.",
  )
  @click.argument("stack_name_or_id", type=str, required=False)
  @click.option(
      "-c",
      "--container_registry",
      "container_registry_flag",
      help="Include this to remove the container registry from this stack.",
      is_flag=True,
      required=False,
  )
  @click.option(
      "-s",
      "--step_operator",
      "step_operator_flag",
      help="Include this to remove the step operator from this stack.",
      is_flag=True,
      required=False,
  )
  @click.option(
      "-r",
      "--model_registry",
      "model_registry_flag",
      help="Include this to remove the model registry from this stack.",
      is_flag=True,
      required=False,
  )
  @click.option(
      "-f",
      "--feature_store",
      "feature_store_flag",
      help="Include this to remove the feature store from this stack.",
      is_flag=True,
      required=False,
  )
  @click.option(
      "-d",
      "--model_deployer",
      "model_deployer_flag",
      help="Include this to remove the model deployer from this stack.",
      is_flag=True,
      required=False,
  )
  @click.option(
      "-e",
      "--experiment_tracker",
      "experiment_tracker_flag",
      help="Include this to remove the experiment tracker from this stack.",
      is_flag=True,
      required=False,
  )
  @click.option(
      "-al",
      "--alerter",
      "alerter_flag",
      help="Include this to remove the alerter from this stack.",
      is_flag=True,
      required=False,
  )
  @click.option(
      "-an",
      "--annotator",
      "annotator_flag",
      help="Include this to remove the annotator from this stack.",
      is_flag=True,
      required=False,
  )
  @click.option(
      "-dv",
      "--data_validator",
      "data_validator_flag",
      help="Include this to remove the data validator from this stack.",
      is_flag=True,
      required=False,
  )
  @click.option(
      "-i",
      "--image_builder",
      "image_builder_flag",
      help="Include this to remove the image builder from this stack.",
      is_flag=True,
      required=False,
  )
  @click.option(
      "-D",
      "--deployer",
      "deployer_flag",
      help="Include this to remove the deployer from this stack.",
      is_flag=True,
      required=False,
  )
  @click.option(
      "-l",
      "--log_store",
      "log_store_flag",
      help="Include this to remove the log store from this stack.",
      is_flag=True,
      required=False,
  )
  def remove_stack_component(
      stack_name_or_id: Optional[str] = None,
      container_registry_flag: Optional[bool] = False,
      step_operator_flag: Optional[bool] = False,
      feature_store_flag: Optional[bool] = False,
      model_deployer_flag: Optional[bool] = False,
      experiment_tracker_flag: Optional[bool] = False,
      alerter_flag: Optional[bool] = False,
      annotator_flag: Optional[bool] = False,
      data_validator_flag: Optional[bool] = False,
      image_builder_flag: Optional[bool] = False,
      model_registry_flag: Optional[str] = None,
      deployer_flag: Optional[bool] = False,
      log_store_flag: Optional[bool] = False,
  ) -> None:
      """Remove stack components from a stack.
  
      Args:
          stack_name_or_id: Name of the stack to remove components from.
          container_registry_flag: To remove the container registry from this
              stack.
          step_operator_flag: To remove the step operator from this stack.
          feature_store_flag: To remove the feature store from this stack.
          model_deployer_flag: To remove the model deployer from this stack.
          experiment_tracker_flag: To remove the experiment tracker from this
              stack.
          alerter_flag: To remove the alerter from this stack.
          annotator_flag: To remove the annotator from this stack.
  