1# -*- coding: utf-8 -*- #
2# Copyright 2015 Google LLC. All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#    http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15"""Command for updating health checks."""
16
17from __future__ import absolute_import
18from __future__ import division
19from __future__ import unicode_literals
20
21from googlecloudsdk.api_lib.compute import base_classes
22from googlecloudsdk.api_lib.compute import health_checks_utils
23from googlecloudsdk.calliope import base
24from googlecloudsdk.calliope import exceptions
25from googlecloudsdk.command_lib.compute import scope as compute_scope
26from googlecloudsdk.command_lib.compute.health_checks import flags
27from googlecloudsdk.core import exceptions as core_exceptions
28from googlecloudsdk.core import log
29
30
31def _DetailedHelp():
32  return {
33      'brief':
34          'Update a HTTP2 health check.',
35      'DESCRIPTION':
36          """\
37      *{command}* is used to update an existing HTTP2 health check. Only
38      arguments passed in will be updated on the health check. Other
39      attributes will remain unaffected.
40      """,
41      'EXAMPLES':
42          """\
43          To update health check interval to 10s, run:
44
45            $ {command} my-health-check-name --check-interval=10s
46      """,
47  }
48
49
50def _Args(parser, include_l7_internal_load_balancing, include_log_config):
51  health_check_arg = flags.HealthCheckArgument(
52      'HTTP2',
53      include_l7_internal_load_balancing=include_l7_internal_load_balancing)
54  health_check_arg.AddArgument(parser, operation_type='update')
55  health_checks_utils.AddHttpRelatedUpdateArgs(parser)
56  health_checks_utils.AddHttpRelatedResponseArg(parser)
57  health_checks_utils.AddProtocolAgnosticUpdateArgs(parser, 'HTTP2')
58  if include_log_config:
59    health_checks_utils.AddHealthCheckLoggingRelatedArgs(parser)
60
61
62def _GetGetRequest(client, health_check_ref):
63  """Returns a request for fetching the existing health check."""
64  return (client.apitools_client.healthChecks, 'Get',
65          client.messages.ComputeHealthChecksGetRequest(
66              healthCheck=health_check_ref.Name(),
67              project=health_check_ref.project))
68
69
70def _GetSetRequest(client, health_check_ref, replacement):
71  """Returns a request for updating the health check."""
72  return (client.apitools_client.healthChecks, 'Update',
73          client.messages.ComputeHealthChecksUpdateRequest(
74              healthCheck=health_check_ref.Name(),
75              healthCheckResource=replacement,
76              project=health_check_ref.project))
77
78
79def _GetRegionalGetRequest(client, health_check_ref):
80  """Returns a request for fetching the existing health check."""
81  return (client.apitools_client.regionHealthChecks, 'Get',
82          client.messages.ComputeRegionHealthChecksGetRequest(
83              healthCheck=health_check_ref.Name(),
84              project=health_check_ref.project,
85              region=health_check_ref.region))
86
87
88def _GetRegionalSetRequest(client, health_check_ref, replacement):
89  """Returns a request for updating the health check."""
90  return (client.apitools_client.regionHealthChecks, 'Update',
91          client.messages.ComputeRegionHealthChecksUpdateRequest(
92              healthCheck=health_check_ref.Name(),
93              healthCheckResource=replacement,
94              project=health_check_ref.project,
95              region=health_check_ref.region))
96
97
98def _Modify(client, args, existing_check, include_log_config):
99  """Returns a modified HealthCheck message."""
100  # We do not support using 'update http2' with a health check of a
101  # different protocol.
102  if (existing_check.type !=
103      client.messages.HealthCheck.TypeValueValuesEnum.HTTP2):
104    raise core_exceptions.Error(
105        'update http2 subcommand applied to health check with protocol ' +
106        existing_check.type.name)
107
108  # Description, PortName, Response and Host are the only attributes that can
109  # be cleared by passing in an empty string (but we don't want to set it to
110  # an empty string).
111  if args.description:
112    description = args.description
113  elif args.description is None:
114    description = existing_check.description
115  else:
116    description = None
117
118  if args.host:
119    host = args.host
120  elif args.host is None:
121    host = existing_check.http2HealthCheck.host
122  else:
123    host = None
124
125  if args.response:
126    response = args.response
127  elif args.response is None:
128    response = existing_check.http2HealthCheck.response
129  else:
130    response = None
131
132  port, port_name, port_specification = health_checks_utils.\
133      HandlePortRelatedFlagsForUpdate(args, existing_check.http2HealthCheck)
134
135  proxy_header = existing_check.http2HealthCheck.proxyHeader
136  if args.proxy_header is not None:
137    proxy_header = (
138        client.messages.HTTP2HealthCheck.ProxyHeaderValueValuesEnum(
139            args.proxy_header))
140  new_health_check = client.messages.HealthCheck(
141      name=existing_check.name,
142      description=description,
143      type=client.messages.HealthCheck.TypeValueValuesEnum.HTTP2,
144      http2HealthCheck=client.messages.HTTP2HealthCheck(
145          host=host,
146          port=port,
147          portName=port_name,
148          portSpecification=port_specification,
149          requestPath=(args.request_path or
150                       existing_check.http2HealthCheck.requestPath),
151          proxyHeader=proxy_header,
152          response=response),
153      checkIntervalSec=(args.check_interval or existing_check.checkIntervalSec),
154      timeoutSec=args.timeout or existing_check.timeoutSec,
155      healthyThreshold=(args.healthy_threshold or
156                        existing_check.healthyThreshold),
157      unhealthyThreshold=(args.unhealthy_threshold or
158                          existing_check.unhealthyThreshold),
159  )
160
161  if include_log_config:
162    new_health_check.logConfig = health_checks_utils.ModifyLogConfig(
163        client, args, existing_check.logConfig)
164  return new_health_check
165
166
167def _ValidateArgs(args, include_log_config):
168  """Validates given args and raises exception if any args are invalid."""
169  health_checks_utils.CheckProtocolAgnosticArgs(args)
170
171  args_unset = not (args.port or args.request_path or args.check_interval or
172                    args.timeout or args.healthy_threshold or
173                    args.unhealthy_threshold or args.proxy_header or
174                    args.use_serving_port)
175
176  if include_log_config:
177    args_unset = (args.enable_logging is None and args_unset)
178
179  if (args.description is None and args.host is None and
180      args.response is None and args.port_name is None and args_unset):
181    raise exceptions.ToolException('At least one property must be modified.')
182
183
184def _Run(args, holder, include_l7_internal_load_balancing, include_log_config):
185  """Issues requests necessary to update the HTTP2 Health Checks."""
186  client = holder.client
187
188  _ValidateArgs(args, include_log_config)
189
190  health_check_arg = flags.HealthCheckArgument(
191      'HTTP2',
192      include_l7_internal_load_balancing=include_l7_internal_load_balancing)
193  health_check_ref = health_check_arg.ResolveAsResource(
194      args, holder.resources, default_scope=compute_scope.ScopeEnum.GLOBAL)
195  if health_checks_utils.IsRegionalHealthCheckRef(health_check_ref):
196    get_request = _GetRegionalGetRequest(client, health_check_ref)
197  else:
198    get_request = _GetGetRequest(client, health_check_ref)
199
200  objects = client.MakeRequests([get_request])
201
202  new_object = _Modify(client, args, objects[0], include_log_config)
203
204  # If existing object is equal to the proposed object or if
205  # _Modify() returns None, then there is no work to be done, so we
206  # print the resource and return.
207  if objects[0] == new_object:
208    log.status.Print('No change requested; skipping update for [{0}].'.format(
209        objects[0].name))
210    return objects
211
212  if health_checks_utils.IsRegionalHealthCheckRef(health_check_ref):
213    set_request = _GetRegionalSetRequest(client, health_check_ref, new_object)
214  else:
215    set_request = _GetSetRequest(client, health_check_ref, new_object)
216
217  return client.MakeRequests([set_request])
218
219
220@base.ReleaseTracks(base.ReleaseTrack.GA)
221class Update(base.UpdateCommand):
222  """Update a HTTP2 health check."""
223
224  _include_l7_internal_load_balancing = True
225  _include_log_config = True
226  detailed_help = _DetailedHelp()
227
228  @classmethod
229  def Args(cls, parser):
230    _Args(parser, cls._include_l7_internal_load_balancing,
231          cls._include_log_config)
232
233  def Run(self, args):
234    holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
235    return _Run(args, holder, self._include_l7_internal_load_balancing,
236                self._include_log_config)
237
238
239@base.ReleaseTracks(base.ReleaseTrack.BETA)
240class UpdateBeta(Update):
241
242  pass
243
244
245@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
246class UpdateAlpha(UpdateBeta):
247
248  pass
249