# -*- coding: utf-8 -*- # # Copyright 2014 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. """Command for deprecating images.""" from __future__ import absolute_import from __future__ import division from __future__ import unicode_literals import datetime from googlecloudsdk.api_lib.compute import base_classes from googlecloudsdk.calliope import arg_parsers from googlecloudsdk.calliope import base from googlecloudsdk.command_lib.compute.images import flags def _ResolveTime(absolute, relative_sec, current_time): """Get the RFC 3339 time string for a provided absolute or relative time.""" if absolute: # TODO(b/36057353): It's unfortunate that datetime.datetime cannot # parse from RFC 3339, but it can output to it. It would be # super cool if we could verify the validity of the user's # input here and fail fast if an invalid date/time is given. # For now, I assume that the user's input is valid. return absolute elif relative_sec: return ( current_time + datetime.timedelta(seconds=relative_sec) ).replace(microsecond=0).isoformat() else: return None class DeprecateImages(base.SilentCommand): """Manage deprecation status of Compute Engine images. *{command}* is used to deprecate images. """ @staticmethod def Args(parser): DeprecateImages.DISK_IMAGE_ARG = flags.MakeDiskImageArg() DeprecateImages.DISK_IMAGE_ARG.AddArgument(parser) flags.REPLACEMENT_DISK_IMAGE_ARG.AddArgument(parser) deprecation_statuses = { 'ACTIVE': 'The image is currently supported.', 'DELETED': ( 'New uses result in an error. Setting this state will not ' 'automatically delete the image. You must still make a request to ' 'delete the image to remove it from the image list.'), 'DEPRECATED': ( 'Operations which create a new *DEPRECATED* resource return ' 'successfully, but with a warning indicating that the image is ' 'deprecated and recommending its replacement.'), 'OBSOLETE': 'New uses result in an error.', } parser.add_argument( '--state', choices=deprecation_statuses, default='ACTIVE', type=lambda x: x.upper(), required=True, help='The deprecation state to set on the image.') deprecate_group = parser.add_mutually_exclusive_group() deprecate_group.add_argument( '--deprecate-on', help="""\ Specifies a date when the image should be marked as DEPRECATED. Note: This is only informational and the image will not be deprecated unless you manually deprecate it. This flag is mutually exclusive with *--deprecate-in*. The date and time specified must be valid RFC 3339 full-date or date-time. For times in UTC, this looks like ``YYYY-MM-DDTHH:MM:SSZ''. For example: 2020-01-02T00:00:00Z for midnight on January 2, 2020 in UTC. """) deprecate_group.add_argument( '--deprecate-in', type=arg_parsers.Duration(), help="""\ Specifies a time duration in which the image should be marked as ``DEPRECATED''. Note: This is only informational and the image will not be deprecated unless you manually deprecate it. This flag is mutually exclusive with *--deprecate-on*. For example, specifying ``30d'' sets the planned ``DEPRECATED'' date to 30 days from the current system time, but does not deprecate the image. You must manually deprecate the image in 30 days. See $ gcloud topic datetimes for information on duration formats. """) delete_group = parser.add_mutually_exclusive_group() delete_group.add_argument( '--delete-on', help="""\ Specifies a date when the image should be marked as ``DELETED''. Note: This is only informational and the image will not be deleted unless you manually delete it. This flag is mutually exclusive with *--delete-in*. The date and time specified must be valid RFC 3339 full-date or date-time. For times in UTC, this looks like ``YYYY-MM-DDTHH:MM:SSZ''. For example: 2020-01-02T00:00:00Z for midnight on January 2, 2020 in UTC. """) delete_group.add_argument( '--delete-in', type=arg_parsers.Duration(), help="""\ Specifies a time duration in which the image should be marked as ``DELETED''. Note: This is only informational and the image will not be deleted unless you manually delete it. For example, specifying ``30d'' sets the planned ``DELETED'' time to 30 days from the current system time, but does not delete the image. You must manually delete the image in 30 days. See $ gcloud topic datetimes for information on duration formats. This flag is mutually exclusive with *--delete-on*. """) obsolete_group = parser.add_mutually_exclusive_group() obsolete_group.add_argument( '--obsolete-on', help="""\ Specifies a date when the image should be marked as ``OBSOLETE''. Note: This is only informational and the image will not be obsoleted unless you manually obsolete it. This flag is mutually exclusive with *--obsolete-in*. The date and time specified must be valid RFC 3339 full-date or date-time. For times in UTC, this looks like ``YYYY-MM-DDTHH:MM:SSZ''. For example: 2020-01-02T00:00:00Z for midnight on January 2, 2020 in UTC. """) obsolete_group.add_argument( '--obsolete-in', type=arg_parsers.Duration(), help="""\ Specifies a time duration in which the image should be marked as ``OBSOLETE''. Note: This is only informational and the image will not be obsoleted unless you manually obsolete it. This flag is mutually exclusive with *--obsolete-on*. For example, specifying ``30d'' sets the planned ``OBSOLETE'' time to 30 days from the current system time, but does not obsolete the image. You must manually obsolete the image in 30 days. See $ gcloud topic datetimes for information on duration formats. """) def Run(self, args): """Invokes requests necessary for deprecating images.""" # TODO(b/13695932): Note that currently there is a bug in the backend # whereby any request other than a completely empty request or a request # with state set to something other than ACTIVE will fail. # GCloud will be able to be made more permissive w.r.t. the checks # below when the API changes. holder = base_classes.ComputeApiHolder(self.ReleaseTrack()) client = holder.client # Determine the date and time to deprecate for each flag set. current_time = datetime.datetime.now() delete_time = _ResolveTime(args.delete_on, args.delete_in, current_time) obsolete_time = _ResolveTime( args.obsolete_on, args.obsolete_in, current_time) deprecate_time = _ResolveTime( args.deprecate_on, args.deprecate_in, current_time) state = client.messages.DeprecationStatus.StateValueValuesEnum(args.state) replacement_ref = flags.REPLACEMENT_DISK_IMAGE_ARG.ResolveAsResource( args, holder.resources) if replacement_ref: replacement_uri = replacement_ref.SelfLink() else: replacement_uri = None image_ref = DeprecateImages.DISK_IMAGE_ARG.ResolveAsResource( args, holder.resources) request = client.messages.ComputeImagesDeprecateRequest( deprecationStatus=client.messages.DeprecationStatus( state=state, deleted=delete_time, obsolete=obsolete_time, deprecated=deprecate_time, replacement=replacement_uri), image=image_ref.Name(), project=image_ref.project) return client.MakeRequests([(client.apitools_client.images, 'Deprecate', request)]) DeprecateImages.detailed_help = { 'EXAMPLES': """ To deprecate an image called 'IMAGE' immediately, mark it as obsolete in one day, and mark it as deleted in two days, use: $ {command} IMAGE --state=DEPRECATED --obsolete-in=1d --delete-in=2d To un-deprecate an image called 'IMAGE' and clear times for deprecated, obsoleted, and deleted, use: $ {command} IMAGE --state=ACTIVE """, }