HEX
Server: Apache/2.4.65 (Ubuntu)
System: Linux ielts-store-v2 6.8.0-1036-gcp #38~22.04.1-Ubuntu SMP Thu Aug 14 01:19:18 UTC 2025 x86_64
User: root (0)
PHP: 7.2.34-54+ubuntu20.04.1+deb.sury.org+1
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,
Upload Files
File: //snap/google-cloud-cli/current/lib/googlecloudsdk/command_lib/deploy/promote_util.py
# -*- coding: utf-8 -*- #
# Copyright 2021 Google LLC. 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
#
#    http://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.
"""Utilities for the promote operation."""


from googlecloudsdk.api_lib.clouddeploy import rollout
from googlecloudsdk.command_lib.deploy import exceptions
from googlecloudsdk.command_lib.deploy import release_util
from googlecloudsdk.command_lib.deploy import rollout_util
from googlecloudsdk.command_lib.deploy import target_util
from googlecloudsdk.core import log
from googlecloudsdk.core import resources

_LAST_TARGET_IN_SEQUENCE = (
    'Release {} is already deployed to the last target '
    '({}) in the promotion sequence.\n- Release: {}\n- Target: {}\n'
)
# In progress List filter
IN_PROGRESS_FILTER_TEMPLATE = 'state="IN_PROGRESS"'


def Promote(
    release_ref,
    release_obj,
    to_target,
    is_create,
    rollout_id=None,
    annotations=None,
    labels=None,
    description=None,
    starting_phase_id=None,
    override_deploy_policies=None,
):
  """Creates a rollout for the given release in the destination target.

  If to_target is not specified and there is a rollout in progress, the promote
  will fail. Otherwise this computes the destination target based on the
  promotion sequence.

  Args:
    release_ref: protorpc.messages.Message, release resource object.
    release_obj: apitools.base.protorpclite.messages.Message, release message
      object.
    to_target: str, the target to promote the release to.
    is_create: bool, if creates a rollout during release creation.
    rollout_id: str, ID to assign to the generated rollout.
    annotations: dict[str,str], a dict of annotation (key,value) pairs that
      allow clients to store small amounts of arbitrary data in cloud deploy
      resources.
    labels: dict[str,str], a dict of label (key,value) pairs that can be used to
      select cloud deploy resources and to find collections of cloud deploy
      resources that satisfy certain conditions.
    description: str, rollout description.
    starting_phase_id: str, the starting phase for the rollout.
    override_deploy_policies: List of Deploy Policies to override.

  Raises:
    googlecloudsdk.command_lib.deploy.exceptions.RolloutIdExhausted
    googlecloudsdk.command_lib.deploy.exceptions.ReleaseInactiveError
  Returns:
    The rollout resource created.
  """
  dest_target = to_target
  if not dest_target:
    dest_target = GetToTargetID(release_obj, is_create)

  rollout_resource = rollout_util.CreateRollout(
      release_ref,
      dest_target,
      rollout_id,
      annotations,
      labels,
      description,
      starting_phase_id,
      override_deploy_policies,
  )

  # Check if it requires approval.
  target_obj = release_util.GetSnappedTarget(release_obj, dest_target)
  if target_obj and target_obj.requireApproval:
    log.status.Print('The rollout is pending approval.')

  return rollout_resource


def GetToTargetID(release_obj, is_create):
  """Get the to_target parameter for promote API.

  This checks the promotion sequence to get the next stage to promote the
  release to.

  Args:
    release_obj: apitools.base.protorpclite.messages.Message, release message.
    is_create: bool, if getting the target for release creation.

  Returns:
    the target ID.

  Raises:
    NoStagesError: if no pipeline stages exist in the release.
    ReleaseInactiveError: if this is not called during release creation and the
    specified release has no rollouts.
  """

  if not release_obj.deliveryPipelineSnapshot.serialPipeline.stages:
    raise exceptions.NoStagesError(release_obj.name)
  # Use release short name to avoid the issue by mixed use of
  # the project number and id.
  release_ref = resources.REGISTRY.ParseRelativeName(
      release_obj.name,
      collection='clouddeploy.projects.locations.deliveryPipelines.releases',
  )
  to_target = release_obj.deliveryPipelineSnapshot.serialPipeline.stages[
      0
  ].targetId
  # The order of pipeline stages represents the promotion sequence.
  # E.g. test->stage->prod. Here we start with the last stage.
  reversed_stages = list(
      reversed(release_obj.deliveryPipelineSnapshot.serialPipeline.stages)
  )
  release_dict = release_ref.AsDict()
  for i, stage in enumerate(reversed_stages):
    target_ref = target_util.TargetReference(
        stage.targetId, release_dict['projectsId'], release_dict['locationsId']
    )
    # Starting with the last target in the promotion sequence per above, find
    # the last successfully deployed rollout to that target.
    current_rollout = target_util.GetCurrentRollout(
        target_ref,
        resources.REGISTRY.Parse(
            None,
            collection='clouddeploy.projects.locations.deliveryPipelines',
            params={
                'projectsId': release_dict['projectsId'],
                'locationsId': release_dict['locationsId'],
                'deliveryPipelinesId': release_dict['deliveryPipelinesId'],
            },
        ),
    )

    if current_rollout:
      current_rollout_ref = resources.REGISTRY.Parse(
          current_rollout.name,
          collection='clouddeploy.projects.locations.deliveryPipelines.releases.rollouts',
      )
      # Promotes the release from the target that is farthest along in the
      # promotion sequence to its next stage in the promotion sequence.
      if current_rollout_ref.Parent().Name() == release_ref.Name():
        if i > 0:
          to_target = reversed_stages[i - 1].targetId
        else:
          log.status.Print(
              _LAST_TARGET_IN_SEQUENCE.format(
                  release_ref.Name(),
                  target_ref.Name(),
                  release_ref.RelativeName(),
                  target_ref.RelativeName(),
              )
          )
          to_target = target_ref.RelativeName()
          # Once a target to promote to is found break out of the loop
        break

  # This means the release is not deployed to any target,
  # to_target flag is required in this case.
  if (
      to_target
      == release_obj.deliveryPipelineSnapshot.serialPipeline.stages[0].targetId
      and not is_create
  ):
    raise exceptions.ReleaseInactiveError()

  return target_util.TargetId(to_target)


def CheckIfInProgressRollout(release_ref, release_obj, to_target_id):
  """Checks if there are any rollouts in progress for the given release.

  Args:
    release_ref: protorpc.messages.Message, release resource object.
    release_obj: apitools.base.protorpclite.messages.Message, release message
      object.
    to_target_id: string, target id (e.g. 'prod')

  Raises:
    googlecloudsdk.command_lib.deploy.exceptions.RolloutInProgressError
  """
  rollouts = list(
      rollout.RolloutClient().List(
          release_ref.RelativeName(), IN_PROGRESS_FILTER_TEMPLATE, limit=1
      )
  )

  if rollouts:
    raise exceptions.RolloutInProgressError(release_obj.name, to_target_id)