text: #  Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
  #  SPDX-License-Identifier: Apache-2.0
  
  import json
  import logging
  import os
  import time
  import traceback
  import urllib
  from datetime import datetime
  
  import click
  from betterboto import client as betterboto_client
  
  from servicecatalog_puppet import (
      aws,
      config,
      constants,
      manifest_utils,
      manifest_utils_for_launches,
  )
  from servicecatalog_puppet.workflow import runner
  from servicecatalog_puppet.workflow.assertions import assertions_section_task
  from servicecatalog_puppet.workflow.codebuild_runs import code_build_run_section_task
  from servicecatalog_puppet.workflow.lambda_invocations import (
      lambda_invocation_section_task,
  )
  from servicecatalog_puppet.workflow.apps import app_section_task
  from servicecatalog_puppet.workflow.launch import launch_section_task
  from servicecatalog_puppet.workflow.launch.reset_provisioned_product_owner_task import (
      ResetProvisionedProductOwnerTask,
  )
  from servicecatalog_puppet.workflow.spoke_local_portfolios import (
      spoke_local_portfolio_section_task,
  )
  from servicecatalog_puppet.workflow.stack import stack_section_task
  from servicecatalog_puppet.workflow.workspaces import workspace_section_task
  
  logger = logging.getLogger(constants.PUPPET_LOGGER_NAME)
  
  
  def reset_provisioned_product_owner(f):
      puppet_account_id = config.get_puppet_account_id()
      current_account_id = puppet_account_id
      manifest = manifest_utils.load(f, puppet_account_id)
  
      os.environ["SCT_CACHE_INVALIDATOR"] = str(datetime.now())
  
      task_defs = manifest_utils_for_launches.generate_launch_tasks(
          manifest, puppet_account_id, False, False
      )
  
      tasks_to_run = []
      for task in task_defs:
          task_status = task.get("status")
          if task_status == constants.PROVISIONED:
              tasks_to_run.append(
                  ResetProvisionedProductOwnerTask(
                      launch_name=task.get("launch_name"),
                      account_id=task.get("account_id"),
                      region=task.get("region"),
                  )
              )
  
      runner.run_tasks(
          puppet_account_id,
          current_account_id,
          tasks_to_run,
          10,
          execution_mode="hub",
          on_complete_url=None,
      )
  
  
  def cli(info, info_line_numbers):
      boto_level = os.environ.get("BOTO_LOG_LEVEL", logging.CRITICAL)
  
      logging.getLogger("boto").setLevel(boto_level)
      logging.getLogger("boto3").setLevel(boto_level)
      logging.getLogger("botocore").setLevel(boto_level)
      logging.getLogger("urllib3").setLevel(boto_level)
  
      if info:
          logging.basicConfig(
              format="%(levelname)s %(threadName)s %(message)s", level=logging.INFO
          )
      if info_line_numbers:
          logging.basicConfig(
              format="%(asctime)s %(levelname)s %(threadName)s [%(filename)s:%(lineno)d] %(message)s",
              datefmt="%Y-%m-%d:%H:%M:%S",
              level=logging.INFO,
          )
  
      if info or info_line_numbers:
          logging.getLogger(constants.PUPPET_LOGGER_NAME).setLevel(logging.INFO)
  
      if os.environ.get("PUPPET_LOG_LEVEL"):
          logging.getLogger(constants.PUPPET_LOGGER_NAME).setLevel(
              os.environ.get("PUPPET_LOG_LEVEL")
          )
  
  
  def wait_for_code_build_in(iam_role_arns):
      cross_accounts = []
      index = 0
      for role in iam_role_arns:
          cross_accounts.append((role, "waiting-for-code-build-{}".format(index)))
          index += 1
  
      with betterboto_client.CrossMultipleAccountsClientContextManager(
          "codebuild", cross_accounts
      ) as codebuild:
          while True:
              try:
                  result = codebuild.list_projects()
                  logger.info(f"Was able to list projects: {result}")
                  break
              except Exception as e:
                  logger.error("type error: " + str(e))
                  logger.error(traceback.format_exc())
  
  
  def wait_for_cloudformation_in(iam_role_arns):
      cross_accounts = []
      index = 0
      for role in iam_role_arns:
          cross_accounts.append((role, "waiting-for-cloudformation-{}".format(index)))
          index += 1
  
      with betterboto_client.CrossMultipleAccountsClientContextManager(
          "cloudformation", cross_accounts
      ) as cloudformation:
          while True:
              try:
                  result = cloudformation.list_stacks()
                  logger.info(f"Was able to list stacks: {result}")
                  break
              except Exception as e:
                  logger.error("type error: " + str(e))
                  logger.error(traceback.format_exc())
  
  
  def is_a_parameter_override_execution() -> bool:
      codepipeline_execution_id = os.getenv("EXECUTION_ID")
      with betterboto_client.ClientContextManager("codepipeline") as codepipeline:
          paginator = codepipeline.get_paginator("list_pipeline_executions")
          pages = paginator.paginate(
              pipelineName=constants.PIPELINE_NAME, PaginationConfig={"PageSize": 100,},
          )
          for page in pages:
              for pipeline_execution_summary in page.get(
                  "pipelineExecutionSummaries", []
              ):
                  if codepipeline_execution_id == pipeline_execution_summary.get(
                      "pipelineExecutionId"
                  ):
                      trigger_detail = pipeline_execution_summary.get("trigger").get(
                          "triggerDetail"
                      )
                      return trigger_detail == "ParameterisedSource"
      return False
  
  
  def wait_for_parameterised_run_to_complete(on_complete_url: str) -> bool:
      with betterboto_client.ClientContextManager("s3") as s3:
          paginator = s3.get_paginator("list_object_versions")
          pages = paginator.paginate(
              Bucket=f"sc-puppet-parameterised-runs-{config.get_puppet_account_id()}",
          )
          for page in pages:
              for version in page.get("Versions", []):
                  if version.get("Key") == "parameters.zip" and version.get("IsLatest"):
                      parameters_file_version_id = version.get("VersionId")
                      while True:
                          time.sleep(5)
                          with betterboto_client.ClientContextManager(
                              "codepipeline"
                          ) as codepipeline:
                              click.echo(
                                  f"looking for execution for {parameters_file_version_id}"
                              )
                              paginator = codepipeline.get_paginator(
                                  "list_pipeline_executions"
                              )
                              pages = paginator.paginate(
                                  pipelineName=constants.PIPELINE_NAME,
                                  PaginationConfig={"PageSize": 100,},
                              )
                              for page in pages:
                                  for pipeline_execution_summary in page.get(
                                      "pipelineExecutionSummaries", []
                                  ):
                                      if (
                                          pipeline_execution_summary.get("trigger").get(
                                              "triggerDetail"
                                          )
                                          == "ParameterisedSource"
                                      ):
                                          for s in pipeline_execution_summary.get(
                                              "sourceRevisions", []
                                          ):
                                              if (
                                                  s.get("actionName")
                                                  == "ParameterisedSource"
                                                  and s.get("revisionId")
                                                  == parameters_file_version_id
                                              ):
                                                  pipeline_execution_id = pipeline_execution_summary.get(
                                                      "pipelineExecutionId"
                                                  )
                                                  click.echo(
                                                      f"Found execution id {pipeline_execution_id}"
                                                  )
                                                  while True:
                                                      time.sleep(10)
                                                      pipelineExecution = codepipeline.get_pipeline_execution(
                                                          pipelineName=constants.PIPELINE_NAME,
                                                          pipelineExecutionId=pipeline_execution_id,
                                                      ).get(
                                                          "pipelineExecution"
                                                      )
                                                      status = pipelineExecution.get(
                                                          "status"
                                                      )
                                                      click.echo(
                                                          f"Current status (A): {status}"
                                                      )
                                                      if status in [
                                                          "Cancelled",
                                                          "Stopped",
                                                          "Succeeded",
                                                          "Superseded",
                                                          "Failed",
                                                      ]:
                                                          succeeded = status in [
                                                              "Succeeded"
                                                          ]
                                                          if on_complete_url:
                                                              logger.info(
                                                                  f"About to post results"
                                                              )
                                                              if succeeded:
                                                                  result = dict(
                                                                      Status="SUCCESS",
                                                                      Reason=f"All tasks run with success: {pipeline_execution_id}",
                                                                      UniqueId=pipeline_execution_id.replace(
                                                                          ":", ""
                                                                      ).replace(
                                                                          "-", ""
                                                                      ),
                                                                      Data=f"{pipeline_execution_id}",
                                                                  )
                                                              else:
                                                                  result = dict(
                                                                      Status="FAILURE",
                                                                      Reason=f"All tasks did not run with success: {pipeline_execution_id}",
                                                                      UniqueId=pipeline_execution_id.replace(
                                                                          ":", ""
                                                                      ).replace(
                                                                          "-", ""
                                                                      ),
                                                                      Data=f"{pipeline_execution_id}",
                                                                  )
                                                              req = urllib.request.Request(
                                                                  url=on_complete_url,
                                                                  data=json.dumps(
                                                                      result
                                                                  ).encode(),
                                                                  method="PUT",
                                                              )
                                                              with urllib.request.urlopen(
                                                                  req
                                                              ) as f:
                                                                  pass
                                                              logger.info(f.status)
                                                              logger.info(f.reason)
  
                                                          return succeeded
  
  
  def generate_tasks(
      f, puppet_account_id, executor_account_id, execution_mode, is_dry_run
  ):
      tasks = [
          launch_section_task.LaunchSectionTask(
              manifest_file_path=f.name, puppet_account_id=puppet_account_id,
          ),
          stack_section_task.StackSectionTask(
              manifest_file_path=f.name, puppet_account_id=puppet_account_id,
          ),
          app_section_task.AppSectionTask(
              manifest_file_path=f.name, puppet_account_id=puppet_account_id,
          ),
          workspace_section_task.WorkspaceSectionTask(
              manifest_file_path=f.name, puppet_account_id=puppet_account_id,
          ),
          assertions_section_task.AssertionsSectionTask(
              manifest_file_path=f.name, puppet_account_id=puppet_account_id,
          ),
      ]
      if not is_dry_run:
          tasks += [
              lambda_invocation_section_task.LambdaInvocationsSectionTask(
                  manifest_file_path=f.name, puppet_account_id=puppet_account_id,
              ),
              code_build_run_section_task.CodeBuildRunsSectionTask(
                  manifest_file_path=f.name, puppet_account_id=puppet_account_id,
              ),
          ]
          if execution_mode != constants.EXECUTION_MODE_SPOKE:
              tasks.append(
                  spoke_local_portfolio_section_task.SpokeLocalPortfolioSectionTask(
                      manifest_file_path=f.name, puppet_account_id=puppet_account_id,
                  )
              )
  
      return tasks
  
  
  def run(what, tail):
      pipelines = {"puppet": constants.PIPELINE_NAME}
      pipeline_name = pipelines.get(what)
      pipeline_execution_id = aws.run_pipeline(pipeline_name, tail)
      click.echo(
          f"https://{os.environ.get('AWS_DEFAULT_REGION')}.console.aws.amazon.com/codesuite/codepipeline/pipelines/{pipeline_name}/executions/{pipeline_execution_id}/timeline"
      )
  
  
  def uninstall(puppet_account_id):
      with betterboto_client.ClientContextManager(
          "cloudformation", region_name=config.get_home_region(puppet_account_id)
      ) as cloudformation:
          cloudformation.ensure_deleted(StackName=constants.BOOTSTRAP_STACK_NAME)
  