feat: Add new gcloud commands, API clients, and third-party libraries across various services.

This commit is contained in:
2026-01-01 20:26:35 +01:00
parent 5e23cbece0
commit a19e592eb7
25221 changed files with 8324611 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
# -*- 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.
"""Commands for reading and manipulating backend services."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
@base.UniverseCompatible
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA)
class BackendServices(base.Group):
"""List, create, and delete backend services."""
BackendServices.category = base.LOAD_BALANCING_CATEGORY
BackendServices.detailed_help = {
'DESCRIPTION': """
List, create, and delete backend services.
For more information about backend services, see the
[backend services documentation](https://cloud.google.com/load-balancing/docs/backend-service).
See also: [Global backend services API](https://cloud.google.com/compute/docs/reference/rest/v1/backendServices)
and [Regional backend services API](https://cloud.google.com/compute/docs/reference/rest/v1/regionBackendServices).
""",
}

View File

@@ -0,0 +1,350 @@
# -*- 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 adding a backend to a backend service."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import encoding
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import exceptions
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.backend_services import backend_flags
from googlecloudsdk.command_lib.compute.backend_services import backend_services_utils
from googlecloudsdk.command_lib.compute.backend_services import flags
@base.UniverseCompatible
@base.ReleaseTracks(base.ReleaseTrack.GA)
class AddBackend(base.UpdateCommand):
"""Add a backend to a backend service.
*{command}* adds a backend to a Google Cloud load balancer or Traffic
Director. Depending on the load balancing scheme of the backend service,
backends can be instance groups (managed or unmanaged), zonal network endpoint
groups (zonal NEGs), serverless NEGs, or an internet NEG. For more
information, see the [backend services
overview](https://cloud.google.com/load-balancing/docs/backend-service).
For most load balancers, you can define how Google Cloud measures capacity by
selecting a balancing mode. For more information, see [traffic
distribution](https://cloud.google.com/load-balancing/docs/backend-service#traffic_distribution).
To modify a backend, use the `gcloud compute backend-services update-backend`
or `gcloud compute backend-services edit` command.
"""
support_global_neg = True
support_region_neg = True
support_failover = True
support_in_flight_balancing = False
@classmethod
def Args(cls, parser):
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.AddArgument(parser)
backend_flags.AddDescription(parser)
flags.AddInstanceGroupAndNetworkEndpointGroupArgs(
parser,
'add to',
support_global_neg=cls.support_global_neg,
support_region_neg=cls.support_region_neg)
backend_flags.AddBalancingMode(
parser,
support_global_neg=cls.support_global_neg,
support_region_neg=cls.support_region_neg,
release_track=cls.ReleaseTrack(),
)
backend_flags.AddCapacityLimits(
parser,
support_global_neg=cls.support_global_neg,
support_region_neg=cls.support_region_neg,
release_track=cls.ReleaseTrack(),
)
backend_flags.AddCapacityScalar(
parser,
support_global_neg=cls.support_global_neg,
support_region_neg=cls.support_region_neg,
)
backend_flags.AddPreference(parser)
if cls.support_failover:
backend_flags.AddFailover(parser, default=None)
if cls.support_in_flight_balancing:
backend_flags.AddTrafficDuration(parser)
backend_flags.AddCustomMetrics(parser)
def _GetGetRequest(self, client, backend_service_ref):
if backend_service_ref.Collection() == 'compute.regionBackendServices':
return (client.apitools_client.regionBackendServices,
'Get',
client.messages.ComputeRegionBackendServicesGetRequest(
backendService=backend_service_ref.Name(),
region=backend_service_ref.region,
project=backend_service_ref.project))
return (client.apitools_client.backendServices,
'Get',
client.messages.ComputeBackendServicesGetRequest(
backendService=backend_service_ref.Name(),
project=backend_service_ref.project))
def _GetSetRequest(self, client, backend_service_ref, replacement):
if backend_service_ref.Collection() == 'compute.regionBackendServices':
return (client.apitools_client.regionBackendServices,
'Update',
client.messages.ComputeRegionBackendServicesUpdateRequest(
backendService=backend_service_ref.Name(),
backendServiceResource=replacement,
region=backend_service_ref.region,
project=backend_service_ref.project))
return (client.apitools_client.backendServices,
'Update',
client.messages.ComputeBackendServicesUpdateRequest(
backendService=backend_service_ref.Name(),
backendServiceResource=replacement,
project=backend_service_ref.project))
def _GetGroupRef(self, args, resources, client):
if args.instance_group:
return flags.MULTISCOPE_INSTANCE_GROUP_ARG.ResolveAsResource(
args,
resources,
scope_lister=compute_flags.GetDefaultScopeLister(client))
if args.network_endpoint_group:
return flags.GetNetworkEndpointGroupArg(
support_global_neg=self.support_global_neg,
support_region_neg=self.support_region_neg).ResolveAsResource(
args,
resources,
scope_lister=compute_flags.GetDefaultScopeLister(client))
def _CreateBackendMessage(
self,
messages,
group_uri,
balancing_mode,
preference,
traffic_duration,
args,
):
"""Create a backend message.
Args:
messages: The available API proto messages.
group_uri: String. The backend instance group uri.
balancing_mode: Backend.BalancingModeValueValuesEnum. The backend load
balancing mode.
preference: Backend.PreferenceValueValuesEnum. The backend preference
traffic_duration: Backend.TrafficDurationValueValuesEnum. The traffic
duration for the backend.
args: argparse Namespace. The arguments given to the add-backend command.
Returns:
A new Backend message with its fields set according to the given
arguments.
"""
backend_services_utils.ValidateBalancingModeArgs(
messages, args, self.ReleaseTrack()
)
if preference is not None:
backend = messages.Backend(
balancingMode=balancing_mode,
preference=preference,
capacityScaler=args.capacity_scaler,
description=args.description,
group=group_uri,
maxRate=args.max_rate,
maxRatePerInstance=args.max_rate_per_instance,
maxRatePerEndpoint=args.max_rate_per_endpoint,
maxUtilization=args.max_utilization,
maxConnections=args.max_connections,
maxConnectionsPerInstance=args.max_connections_per_instance,
maxConnectionsPerEndpoint=args.max_connections_per_endpoint,
failover=args.failover,
)
if (
self.ReleaseTrack() == base.ReleaseTrack.ALPHA
or self.ReleaseTrack() == base.ReleaseTrack.BETA
):
backend.maxInFlightRequests = args.max_in_flight_requests
backend.maxInFlightRequestsPerInstance = (
args.max_in_flight_requests_per_instance
)
backend.maxInFlightRequestsPerEndpoint = (
args.max_in_flight_requests_per_endpoint
)
backend.trafficDuration = traffic_duration
return backend
else:
backend = messages.Backend(
balancingMode=balancing_mode,
capacityScaler=args.capacity_scaler,
description=args.description,
group=group_uri,
maxRate=args.max_rate,
maxRatePerInstance=args.max_rate_per_instance,
maxRatePerEndpoint=args.max_rate_per_endpoint,
maxUtilization=args.max_utilization,
maxConnections=args.max_connections,
maxConnectionsPerInstance=args.max_connections_per_instance,
maxConnectionsPerEndpoint=args.max_connections_per_endpoint,
failover=args.failover,
)
if (
self.ReleaseTrack() == base.ReleaseTrack.ALPHA
or self.ReleaseTrack() == base.ReleaseTrack.BETA
):
backend.maxInFlightRequests = args.max_in_flight_requests
backend.maxInFlightRequestsPerInstance = (
args.max_in_flight_requests_per_instance
)
backend.maxInFlightRequestsPerEndpoint = (
args.max_in_flight_requests_per_endpoint
)
backend.trafficDuration = traffic_duration
return backend
def _Modify(self, client, resources, backend_service_ref, args, existing):
replacement = encoding.CopyProtoMessage(existing)
group_ref = self._GetGroupRef(args, resources, client)
group_uri = group_ref.SelfLink()
scope = ''
for backend in existing.backends:
if group_uri == backend.group:
if (
group_ref.Collection() == 'compute.instanceGroups'
or group_ref.Collection() == 'compute.networkEndpointGroups'
):
scope = 'zone [' + getattr(group_ref, 'zone') + ']'
elif (
group_ref.Collection() == 'compute.regionInstanceGroups'
or group_ref.Collection() == 'compute.regionNetworkEndpointGroups'
):
scope = 'region [' + getattr(group_ref, 'region') + ']'
elif group_ref.Collection() == 'compute.globalNetworkEndpointGroups':
scope = 'global'
raise exceptions.ArgumentError(
'Backend [{}] in {} already exists in backend service [{}].'.format(
group_ref.Name(), scope, backend_service_ref.Name()
)
)
if args.balancing_mode:
balancing_mode = client.messages.Backend.BalancingModeValueValuesEnum(
args.balancing_mode)
else:
balancing_mode = None
preference = None
if args.preference:
preference = client.messages.Backend.PreferenceValueValuesEnum(
args.preference)
traffic_duration = None
if (
self.ReleaseTrack() == base.ReleaseTrack.ALPHA
or self.ReleaseTrack() == base.ReleaseTrack.BETA
) and args.traffic_duration:
traffic_duration = client.messages.Backend.TrafficDurationValueValuesEnum(
args.traffic_duration
)
backend = self._CreateBackendMessage(
client.messages,
group_uri,
balancing_mode,
preference,
traffic_duration,
args,
)
if args.custom_metrics:
backend.customMetrics = args.custom_metrics
if args.custom_metrics_file:
backend.customMetrics = args.custom_metrics_file
replacement.backends.append(backend)
return replacement
def Run(self, args):
"""Issues requests necessary to add backend to the Backend Service."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
backend_service_ref = (
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client)))
get_request = self._GetGetRequest(client, backend_service_ref)
objects = client.MakeRequests([get_request])
new_object = self._Modify(client, holder.resources, backend_service_ref,
args, objects[0])
return client.MakeRequests(
[self._GetSetRequest(client, backend_service_ref, new_object)])
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class AddBackendBeta(AddBackend):
"""Add a backend to a backend service.
*{command}* adds a backend to a Google Cloud load balancer or Traffic
Director. Depending on the load balancing scheme of the backend service,
backends can be instance groups (managed or unmanaged), zonal network endpoint
groups (zonal NEGs), serverless NEGs, or an internet NEG. For more
information, see the [backend services
overview](https://cloud.google.com/load-balancing/docs/backend-service).
For most load balancers, you can define how Google Cloud measures capacity by
selecting a balancing mode. For more information, see [traffic
distribution](https://cloud.google.com/load-balancing/docs/backend-service#traffic_distribution).
To modify a backend, use the `gcloud compute backend-services update-backend`
or `gcloud compute backend-services edit` command.
"""
# Allow --preference flag to be set when updating the backend.
support_in_flight_balancing = True
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class AddBackendAlpha(AddBackend):
"""Add a backend to a backend service.
*{command}* adds a backend to a Google Cloud load balancer or Traffic
Director. Depending on the load balancing scheme of the backend service,
backends can be instance groups (managed or unmanaged), zonal network endpoint
groups (zonal NEGs), serverless NEGs, or an internet NEG. For more
information, see the [backend services
overview](https://cloud.google.com/load-balancing/docs/backend-service).
For most load balancers, you can define how Google Cloud measures capacity by
selecting a balancing mode. For more information, see [traffic
distribution](https://cloud.google.com/load-balancing/docs/backend-service#traffic_distribution).
To modify a backend, use the `gcloud compute backend-services update-backend`
or `gcloud compute backend-services edit` command.
"""
# Allow --preference flag to be set when updating the backend.
support_in_flight_balancing = True

View File

@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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 to set IAM policy for a resource."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute.backend_services import client
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.backend_services import flags
from googlecloudsdk.command_lib.iam import iam_util
class AddIamPolicyBinding(base.Command):
"""Add an IAM policy binding to a Compute Engine backend service."""
@staticmethod
def Args(parser):
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.AddArgument(parser)
iam_util.AddArgsForAddIamPolicyBinding(parser)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
backend_service_ref = (
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(holder.client)))
backend_service = client.BackendService(
backend_service_ref, compute_client=holder.client)
return backend_service.AddIamPolicyBinding(args.member, args.role)
AddIamPolicyBinding.detailed_help = {
'brief':
'Add an IAM policy binding to a Compute Engine backend service.',
'DESCRIPTION':
"""\
Add an IAM policy binding to a Compute Engine backend service. """,
'EXAMPLES':
"""\
To add an IAM policy binding for the role of
'compute.loadBalancerServiceUser' for the user 'test-user@gmail.com' with
backend service 'my-backend-service' and region 'REGION', run:
$ {command} my-backend-service --region=REGION \
--member='user:test-user@gmail.com' \
--role='roles/compute.loadBalancerServiceUser'
$ {command} my-backend-service --global \
--member='user:test-user@gmail.com' \
--role='roles/compute.loadBalancerServiceUser'
See https://cloud.google.com/iam/docs/managing-policies for details of
policy role and member types.
"""
}

View File

@@ -0,0 +1,85 @@
# -*- 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.
"""Command for adding service bindings to a backend service."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import encoding
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute.backend_services import client
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute import reference_utils
from googlecloudsdk.command_lib.compute.backend_services import flags
_DETAILED_HELP = {
'EXAMPLES':
"""\
To add a service binding to a backend service, run:
$ {command} NAME \
--service-bindings=SERVICE_BINDING1 --global
""",
}
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA,
base.ReleaseTrack.GA)
class AddServiceBindings(base.UpdateCommand):
"""Add service bindings to a backend service."""
detailed_help = _DETAILED_HELP
@classmethod
def Args(cls, parser):
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.AddArgument(parser)
flags.AddServiceBindings(
parser,
required=True,
help_text='List of service binding names to be added to the backend service.'
)
def _Modify(self, backend_service_ref, args, existing):
location = (
backend_service_ref.region if backend_service_ref.Collection()
== 'compute.regionBackendServices' else 'global')
replacement = encoding.CopyProtoMessage(existing)
old_bindings = replacement.serviceBindings or []
new_bindings = [
reference_utils.BuildServiceBindingUrl(backend_service_ref.project,
location, binding_name)
for binding_name in args.service_bindings
]
new_bindings = reference_utils.FilterReferences(new_bindings, old_bindings)
replacement.serviceBindings = sorted(
list(set(old_bindings) | set(new_bindings)))
return replacement
def Run(self, args):
"""Adds service bindings to the Backend Service."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
backend_service_ref = (
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(holder.client)))
backend_service = client.BackendService(
backend_service_ref, compute_client=holder.client)
new_object = self._Modify(backend_service_ref, args, backend_service.Get())
return backend_service.Set(new_object)

View File

@@ -0,0 +1,89 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 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 to add a Cloud CDN Signed URL key to a backend service."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute.operations import poller
from googlecloudsdk.api_lib.util import waiter
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute import signed_url_flags
from googlecloudsdk.command_lib.compute.backend_services import flags
from googlecloudsdk.core.util import files
class AddSignedUrlKey(base.UpdateCommand):
"""Add Cloud CDN Signed URL key to a backend service.
*{command}* is used to add a new Cloud CDN Signed URL key to a backend
service.
Cloud CDN Signed URLs give you a way to serve responses from the
globally distributed CDN cache, even if the request needs to be
authorized.
Signed URLs are a mechanism to temporarily give a client access to a
private resource without requiring additional authorization. To achieve
this, the full request URL that should be allowed is hashed
and cryptographically signed. By using the signed URL you give it, that
one request will be considered authorized to receive the requested
content.
Generally, a signed URL can be used by anyone who has it. However, it
is usually only intended to be used by the client that was directly
given the URL. To mitigate this, they expire at a time chosen by the
issuer. To minimize the risk of a signed URL being shared, it is recommended
that the signed URL be set to expire as soon as possible.
A 128-bit secret key is used for signing the URLs.
"""
@staticmethod
def Args(parser):
"""Set up arguments for this command."""
flags.GLOBAL_BACKEND_SERVICE_ARG.AddArgument(parser)
signed_url_flags.AddCdnSignedUrlKeyName(parser, required=True)
signed_url_flags.AddCdnSignedUrlKeyFile(parser, required=True)
def Run(self, args):
"""Issues the request to add Signed URL key to the backend bucket."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
api_client = holder.client.apitools_client
messages = holder.client.messages
service = api_client.backendServices
backend_service_ref = flags.GLOBAL_BACKEND_SERVICE_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(holder.client))
key_value = files.ReadFileContents(args.key_file).rstrip()
request = messages.ComputeBackendServicesAddSignedUrlKeyRequest(
project=backend_service_ref.project,
backendService=backend_service_ref.Name(),
signedUrlKey=messages.SignedUrlKey(
keyName=args.key_name, keyValue=key_value))
operation = service.AddSignedUrlKey(request)
operation_ref = holder.resources.Parse(
operation.selfLink, collection='compute.globalOperations')
operation_poller = poller.Poller(service)
return waiter.WaitFor(operation_poller, operation_ref,
'Adding Cloud CDN Signed URL key to [{0}]'.format(
backend_service_ref.Name()))

View File

@@ -0,0 +1,597 @@
# -*- 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 creating backend services.
There are separate alpha, beta, and GA command classes in this file.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.compute import cdn_flags_utils as cdn_flags
from googlecloudsdk.command_lib.compute import exceptions as compute_exceptions
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute import reference_utils
from googlecloudsdk.command_lib.compute import resource_manager_tags_utils
from googlecloudsdk.command_lib.compute import signed_url_flags
from googlecloudsdk.command_lib.compute.backend_services import backend_services_utils
from googlecloudsdk.command_lib.compute.backend_services import flags
from googlecloudsdk.core import log
def _ResolvePortName(args):
"""Determine port name if one was not specified."""
if args.port_name:
return args.port_name
def _LogAndReturn(port_name):
log.status.Print('Backend-services\' port_name automatically resolved to '
'{} based on the service protocol.'.format(port_name))
return port_name
if args.protocol == 'HTTPS':
return _LogAndReturn('https')
if args.protocol == 'HTTP2':
return _LogAndReturn('http2')
if args.protocol == 'SSL':
return _LogAndReturn('ssl')
if args.protocol == 'TCP':
return _LogAndReturn('tcp')
return 'http'
def _ResolveProtocol(messages, args, default='HTTP'):
valid_options = messages.BackendService.ProtocolValueValuesEnum.names()
if args.protocol and args.protocol not in valid_options:
raise ValueError('{} is not a supported option. See the help text of '
'--protocol for supported options.'.format(args.protocol))
return messages.BackendService.ProtocolValueValuesEnum(args.protocol or
default)
def AddIapFlag(parser):
# TODO(b/34479878): It would be nice if the auto-generated help text were
# a bit better so we didn't need to be quite so verbose here.
flags.AddIap(
parser,
help="""\
Configure Identity Aware Proxy (IAP) for external HTTP(S) load balancing.
You can configure IAP to be `enabled` or `disabled` (default). If enabled,
you can provide values for `oauth2-client-id` and `oauth2-client-secret`.
For example, `--iap=enabled,oauth2-client-id=foo,oauth2-client-secret=bar`
turns IAP on, and `--iap=disabled` turns it off. For more information, see
https://cloud.google.com/iap/.
""")
class CreateHelper(object):
"""Helper class to create a backend service."""
HEALTH_CHECK_ARG = None
HTTP_HEALTH_CHECK_ARG = None
HTTPS_HEALTH_CHECK_ARG = None
@classmethod
def Args(
cls,
parser,
support_subsetting_subset_size,
support_ip_port_dynamic_forwarding,
support_zonal_affinity,
support_allow_multinetwork,
support_identity,
):
"""Add flags to create a backend service to the parser."""
parser.display_info.AddFormat(flags.DEFAULT_LIST_FORMAT)
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.AddArgument(
parser, operation_type='create')
flags.AddDescription(parser)
cls.HEALTH_CHECK_ARG = flags.HealthCheckArgument()
cls.HEALTH_CHECK_ARG.AddArgument(parser, cust_metavar='HEALTH_CHECK')
cls.HTTP_HEALTH_CHECK_ARG = flags.HttpHealthCheckArgument()
cls.HTTP_HEALTH_CHECK_ARG.AddArgument(
parser, cust_metavar='HTTP_HEALTH_CHECK')
cls.HTTPS_HEALTH_CHECK_ARG = flags.HttpsHealthCheckArgument()
cls.HTTPS_HEALTH_CHECK_ARG.AddArgument(
parser, cust_metavar='HTTPS_HEALTH_CHECK')
flags.AddServiceLoadBalancingPolicy(parser)
flags.AddBackendServiceTlsSettings(
parser, support_identity=support_identity
)
flags.AddServiceBindings(parser)
flags.AddTimeout(parser)
flags.AddPortName(parser)
flags.AddProtocol(parser, default=None)
flags.AddEnableCdn(parser)
flags.AddSessionAffinity(parser, support_stateful_affinity=True)
flags.AddAffinityCookie(parser, support_stateful_affinity=True)
flags.AddConnectionDrainingTimeout(parser)
flags.AddLoadBalancingScheme(parser)
flags.AddCustomRequestHeaders(parser, remove_all_flag=False)
flags.AddCacheKeyIncludeProtocol(parser, default=True)
flags.AddCacheKeyIncludeHost(parser, default=True)
flags.AddCacheKeyIncludeQueryString(parser, default=True)
flags.AddCacheKeyQueryStringList(parser)
flags.AddCacheKeyExtendedCachingArgs(parser)
AddIapFlag(parser)
parser.display_info.AddCacheUpdater(flags.BackendServicesCompleter)
signed_url_flags.AddSignedUrlCacheMaxAge(parser, required=False)
flags.AddSubsettingPolicy(parser)
if support_subsetting_subset_size:
flags.AddSubsettingSubsetSize(parser)
flags.AddConnectionDrainOnFailover(parser, default=None)
flags.AddDropTrafficIfUnhealthy(parser, default=None)
flags.AddFailoverRatio(parser)
flags.AddEnableLogging(parser)
flags.AddLoggingSampleRate(parser)
flags.AddLoggingOptional(parser)
flags.AddLoggingOptionalFields(parser)
flags.AddNetwork(parser)
flags.AddLocalityLbPolicy(parser)
cdn_flags.AddCdnPolicyArgs(parser, 'backend service')
flags.AddConnectionTrackingPolicy(parser)
flags.AddCompressionMode(parser)
flags.AddIpAddressSelectionPolicy(parser)
flags.AddBackendServiceCustomMetrics(parser)
if support_ip_port_dynamic_forwarding:
flags.AddIpPortDynamicForwarding(parser)
if support_zonal_affinity:
flags.AddZonalAffinity(parser)
if support_allow_multinetwork:
flags.AddAllowMultinetwork(parser)
flags.AddResourceManagerTags(parser)
def __init__(
self,
support_subsetting_subset_size,
release_track,
support_ip_port_dynamic_forwarding,
support_zonal_affinity,
support_allow_multinetwork,
support_identity,
):
self._support_subsetting_subset_size = support_subsetting_subset_size
self._support_ip_port_dynamic_forwarding = (
support_ip_port_dynamic_forwarding
)
self._support_zonal_affinity = support_zonal_affinity
self._support_allow_multinetwork = support_allow_multinetwork
self._release_track = release_track
self._support_identity = support_identity
def _CreateGlobalRequests(self, holder, args, backend_services_ref):
"""Returns a global backend service create request."""
if args.load_balancing_scheme == 'INTERNAL':
raise exceptions.RequiredArgumentException(
'--region', 'Must specify --region for internal load balancer.')
if backend_services_utils.HasFailoverPolicyArgs(args):
raise exceptions.InvalidArgumentException(
'--global',
'failover policy parameters are only for regional passthrough '
'Network Load Balancers.')
backend_service = self._CreateBackendService(holder, args,
backend_services_ref)
client = holder.client
if args.connection_draining_timeout is not None:
backend_service.connectionDraining = (
client.messages.ConnectionDraining(
drainingTimeoutSec=args.connection_draining_timeout))
if args.enable_cdn is not None:
backend_service.enableCDN = args.enable_cdn
backend_services_utils.ApplyCdnPolicyArgs(
client,
args,
backend_service,
is_update=False,
apply_signed_url_cache_max_age=True)
if args.service_lb_policy is not None:
backend_service.serviceLbPolicy = reference_utils.BuildServiceLbPolicyUrl(
project_name=backend_services_ref.project,
location='global',
policy_name=args.service_lb_policy,
release_track=self._release_track,
)
if args.tls_settings is not None or (
self._support_identity and hasattr(args, 'identity') and args.identity
):
backend_services_utils.ApplyTlsSettingsArgs(
client,
args,
backend_service,
backend_services_ref.project,
'global',
self._release_track,
)
if args.service_bindings is not None:
backend_service.serviceBindings = [
reference_utils.BuildServiceBindingUrl(backend_services_ref.project,
'global', binding_name)
for binding_name in args.service_bindings
]
if args.compression_mode is not None:
backend_service.compressionMode = (
client.messages.BackendService.CompressionModeValueValuesEnum(
args.compression_mode))
backend_services_utils.ApplySubsettingArgs(
client, args, backend_service, self._support_subsetting_subset_size
)
if args.session_affinity is not None:
backend_service.sessionAffinity = (
client.messages.BackendService.SessionAffinityValueValuesEnum(
args.session_affinity))
backend_services_utils.ApplyAffinityCookieArgs(
client, args, backend_service
)
if args.custom_request_header is not None:
backend_service.customRequestHeaders = args.custom_request_header
if args.custom_response_header is not None:
backend_service.customResponseHeaders = args.custom_response_header
if (backend_service.cdnPolicy is not None and
backend_service.cdnPolicy.cacheMode and args.enable_cdn is not False): # pylint: disable=g-bool-id-comparison
backend_service.enableCDN = True
if args.locality_lb_policy is not None:
backend_service.localityLbPolicy = (
client.messages.BackendService.LocalityLbPolicyValueValuesEnum(
args.locality_lb_policy))
if args.resource_manager_tags is not None:
backend_service.params = self._CreateBackendServiceParams(
client.messages, args.resource_manager_tags)
self._ApplyIapArgs(client.messages, args.iap, backend_service)
if args.load_balancing_scheme != 'EXTERNAL':
backend_service.loadBalancingScheme = (
client.messages.BackendService.LoadBalancingSchemeValueValuesEnum(
args.load_balancing_scheme))
backend_services_utils.ApplyLogConfigArgs(
client.messages,
args,
backend_service,
)
backend_services_utils.ApplyIpAddressSelectionPolicyArgs(
client, args, backend_service
)
backend_services_utils.ApplyCustomMetrics(args, backend_service)
if self._support_ip_port_dynamic_forwarding:
backend_services_utils.IpPortDynamicForwarding(
client, args, backend_service
)
if self._support_allow_multinetwork:
backend_service.allowMultinetwork = args.allow_multinetwork
request = client.messages.ComputeBackendServicesInsertRequest(
backendService=backend_service, project=backend_services_ref.project
)
return [(client.apitools_client.backendServices, 'Insert', request)]
def _CreateRegionalRequests(self, holder, args, backend_services_ref):
"""Returns a regional backend service create request."""
if (
not args.cache_key_include_host
or not args.cache_key_include_protocol
or not args.cache_key_include_query_string
or args.cache_key_query_string_blacklist is not None
or args.cache_key_query_string_whitelist is not None
):
raise compute_exceptions.ArgumentError(
'Custom cache key flags cannot be used for regional requests.'
)
if args.IsSpecified('network') and args.load_balancing_scheme != 'INTERNAL':
raise exceptions.InvalidArgumentException(
'--network', 'can only specify network for INTERNAL backend service.'
)
backend_service = self._CreateRegionBackendService(
holder, args, backend_services_ref
)
client = holder.client
if args.connection_draining_timeout is not None:
backend_service.connectionDraining = client.messages.ConnectionDraining(
drainingTimeoutSec=args.connection_draining_timeout
)
if args.custom_request_header is not None:
backend_service.customRequestHeaders = args.custom_request_header
if args.custom_response_header is not None:
backend_service.customResponseHeaders = args.custom_response_header
backend_services_utils.ApplyFailoverPolicyArgs(
client.messages, args, backend_service
)
if args.service_lb_policy is not None:
raise compute_exceptions.ArgumentError(
'--service-lb-policy flag cannot be used for regional backend'
' service.'
)
if args.tls_settings is not None or (
self._support_identity and hasattr(args, 'identity') and args.identity
):
backend_services_utils.ApplyTlsSettingsArgs(
client,
args,
backend_service,
backend_services_ref.project,
backend_services_ref.region,
self._release_track,
)
if args.service_bindings is not None:
backend_service.serviceBindings = [
reference_utils.BuildServiceBindingUrl(
backend_services_ref.project,
backend_services_ref.region,
binding_name,
)
for binding_name in args.service_bindings
]
backend_services_utils.ApplySubsettingArgs(
client, args, backend_service, self._support_subsetting_subset_size
)
backend_services_utils.ApplyConnectionTrackingPolicyArgs(
client, args, backend_service)
self._ApplyIapArgs(client.messages, args.iap, backend_service)
if args.session_affinity is not None:
backend_service.sessionAffinity = (
client.messages.BackendService.SessionAffinityValueValuesEnum(
args.session_affinity))
if args.port_name is not None:
backend_service.portName = args.port_name
if args.IsSpecified('network'):
backend_service.network = flags.NETWORK_ARG.ResolveAsResource(
args, holder.resources).SelfLink()
if args.locality_lb_policy is not None:
backend_service.localityLbPolicy = (
client.messages.BackendService.LocalityLbPolicyValueValuesEnum(
args.locality_lb_policy))
if args.resource_manager_tags is not None:
backend_service.params = self._CreateBackendServiceParams(
client.messages, args.resource_manager_tag)
backend_services_utils.ApplyAffinityCookieArgs(
client, args, backend_service
)
backend_services_utils.ApplyLogConfigArgs(
client.messages,
args,
backend_service,
)
backend_services_utils.ApplyIpAddressSelectionPolicyArgs(
client, args, backend_service
)
backend_services_utils.ApplyCustomMetrics(args, backend_service)
if self._support_ip_port_dynamic_forwarding:
backend_services_utils.IpPortDynamicForwarding(
client, args, backend_service
)
if self._support_zonal_affinity:
backend_services_utils.ZonalAffinity(client, args, backend_service)
if self._support_allow_multinetwork:
backend_service.allowMultinetwork = args.allow_multinetwork
request = client.messages.ComputeRegionBackendServicesInsertRequest(
backendService=backend_service,
region=backend_services_ref.region,
project=backend_services_ref.project)
return [(client.apitools_client.regionBackendServices, 'Insert', request)]
def _CreateBackendService(self, holder, args, backend_services_ref):
health_checks = flags.GetHealthCheckUris(args, self, holder.resources)
enable_cdn = True if args.enable_cdn else None
return holder.client.messages.BackendService(
description=args.description,
name=backend_services_ref.Name(),
healthChecks=health_checks,
portName=_ResolvePortName(args),
protocol=_ResolveProtocol(holder.client.messages, args),
timeoutSec=args.timeout,
enableCDN=enable_cdn)
def _CreateRegionBackendService(self, holder, args, backend_services_ref):
"""Creates a regional backend service."""
health_checks = flags.GetHealthCheckUris(args, self, holder.resources)
messages = holder.client.messages
return messages.BackendService(
description=args.description,
name=backend_services_ref.Name(),
healthChecks=health_checks,
loadBalancingScheme=(
messages.BackendService.LoadBalancingSchemeValueValuesEnum(
args.load_balancing_scheme)),
protocol=_ResolveProtocol(messages, args, default='TCP'),
timeoutSec=args.timeout)
def _ApplyIapArgs(self, messages, iap_arg, backend_service):
if iap_arg is not None:
backend_service.iap = backend_services_utils.GetIAP(iap_arg, messages)
if backend_service.iap.enabled:
log.warning(backend_services_utils.IapBestPracticesNotice())
if (backend_service.iap.enabled and backend_service.protocol
is not messages.BackendService.ProtocolValueValuesEnum.HTTPS):
log.warning(backend_services_utils.IapHttpWarning())
def Run(self, args, holder):
"""Issues request necessary to create Backend Service."""
client = holder.client
ref = flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client))
if ref.Collection() == 'compute.backendServices':
requests = self._CreateGlobalRequests(holder, args, ref)
elif ref.Collection() == 'compute.regionBackendServices':
requests = self._CreateRegionalRequests(holder, args, ref)
return client.MakeRequests(requests)
def _CreateBackendServiceParams(self, messages, resource_manager_tags):
resource_manager_tags_map = (
resource_manager_tags_utils.GetResourceManagerTags(
resource_manager_tags
)
)
params = messages.BackendServiceParams
additional_properties = [
params.ResourceManagerTagsValue.AdditionalProperty(key=key, value=value)
for key, value in sorted(resource_manager_tags_map.items())
]
return params(
resourceManagerTags=params.ResourceManagerTagsValue(
additionalProperties=additional_properties
)
)
@base.UniverseCompatible
@base.ReleaseTracks(base.ReleaseTrack.GA)
class CreateGA(base.CreateCommand):
"""Create a backend service.
*{command}* creates a backend service for a Google Cloud load balancer or
Traffic Director. A backend service defines how to distribute traffic to
backends. Depending on the load balancing scheme of the backend service,
backends can be instance groups (managed or unmanaged), zonal network endpoint
groups (zonal NEGs), serverless NEGs, or an internet NEG. For more
information, see the [backend services
overview](https://cloud.google.com/load-balancing/docs/backend-service).
After you create a backend service, you add backends by using `gcloud
compute backend-services add-backend` or `gcloud compute backend-services
edit`.
"""
_support_subsetting_subset_size = False
_support_ip_port_dynamic_forwarding = False
_support_zonal_affinity = False
_support_allow_multinetwork = False
_support_identity = False
@classmethod
def Args(cls, parser):
CreateHelper.Args(
parser,
support_subsetting_subset_size=cls._support_subsetting_subset_size,
support_ip_port_dynamic_forwarding=cls._support_ip_port_dynamic_forwarding,
support_zonal_affinity=cls._support_zonal_affinity,
support_allow_multinetwork=cls._support_allow_multinetwork,
support_identity=cls._support_identity,
)
def Run(self, args):
"""Issues request necessary to create Backend Service."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
return CreateHelper(
support_subsetting_subset_size=self._support_subsetting_subset_size,
support_ip_port_dynamic_forwarding=self._support_ip_port_dynamic_forwarding,
support_zonal_affinity=self._support_zonal_affinity,
support_allow_multinetwork=self._support_allow_multinetwork,
release_track=self.ReleaseTrack(),
support_identity=self._support_identity,
).Run(args, holder)
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class CreateBeta(CreateGA):
"""Create a backend service.
*{command}* creates a backend service. A backend service defines how Cloud
Load Balancing distributes traffic. The backend service configuration contains
a set of values, such as the protocol used to connect to backends, various
distribution and session settings, health checks, and timeouts. These settings
provide fine-grained control over how your load balancer behaves. Most of the
settings have default values that allow for easy configuration if you need to
get started quickly.
After you create a backend service, you add backends by using `gcloud
compute backend-services add-backend`.
For more information about the available settings, see
https://cloud.google.com/load-balancing/docs/backend-service.
"""
_support_subsetting_subset_size = True
_support_ip_port_dynamic_forwarding = True
_support_zonal_affinity = True
_support_allow_multinetwork = False
_support_identity = True
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class CreateAlpha(CreateBeta):
"""Create a backend service.
*{command}* creates a backend service. A backend service defines how Cloud
Load Balancing distributes traffic. The backend service configuration contains
a set of values, such as the protocol used to connect to backends, various
distribution and session settings, health checks, and timeouts. These settings
provide fine-grained control over how your load balancer behaves. Most of the
settings have default values that allow for easy configuration if you need to
get started quickly.
After you create a backend service, you add backends by using `gcloud
compute backend-services add-backend`.
For more information about the available settings, see
https://cloud.google.com/load-balancing/docs/backend-service.
"""
_support_subsetting_subset_size = True
_support_ip_port_dynamic_forwarding = True
_support_zonal_affinity = True
_support_allow_multinetwork = True
_support_identity = True

View File

@@ -0,0 +1,62 @@
# -*- 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 deleting backend services."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute import utils
from googlecloudsdk.api_lib.compute.backend_services import client
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute.backend_services import backend_services_utils
from googlecloudsdk.command_lib.compute.backend_services import flags
class Delete(base.DeleteCommand):
"""Delete backend services.
*{command}* deletes one or more backend services.
"""
_BACKEND_SERVICE_ARG = flags.GLOBAL_REGIONAL_MULTI_BACKEND_SERVICE_ARG
@classmethod
def Args(cls, parser):
cls._BACKEND_SERVICE_ARG.AddArgument(parser, operation_type='delete')
parser.display_info.AddCacheUpdater(None)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
refs = self._BACKEND_SERVICE_ARG.ResolveAsResource(
args,
holder.resources,
default_scope=backend_services_utils.GetDefaultScope())
utils.PromptForDeletion(refs)
requests = []
for ref in refs:
backend_service = client.BackendService(
ref, compute_client=holder.client)
requests.extend(backend_service.Delete(only_generate_request=True))
errors = []
resources = holder.client.MakeRequests(requests, errors)
if errors:
utils.RaiseToolException(errors)
return resources

View File

@@ -0,0 +1,98 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 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 to delete a Cloud CDN Signed URL key from a backend service."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute.operations import poller
from googlecloudsdk.api_lib.util import waiter
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute import signed_url_flags
from googlecloudsdk.command_lib.compute.backend_services import flags
class DeleteSignedUrlKey(base.UpdateCommand):
"""Delete Cloud CDN Signed URL key from a backend service.
*{command}* is used to delete an existing Cloud CDN Signed URL key from a
backend service.
Cloud CDN Signed URLs give you a way to serve responses from the
globally distributed CDN cache, even if the request needs to be
authorized.
Signed URLs are a mechanism to temporarily give a client access to a
private resource without requiring additional authorization. To achieve
this, the full request URL that should be allowed is hashed
and cryptographically signed. By using the signed URL you give it, that
one request will be considered authorized to receive the requested
content.
Generally, a signed URL can be used by anyone who has it. However, it
is usually only intended to be used by the client that was directly
given the URL. To mitigate this, they expire at a time chosen by the
issuer. To minimize the risk of a signed URL being shared, it is recommended
that the signed URL be set to expire as soon as possible.
A 128-bit secret key is used for signing the URLs.
"""
@staticmethod
def Args(parser):
"""Set up arguments for this command."""
flags.GLOBAL_BACKEND_SERVICE_ARG.AddArgument(parser)
signed_url_flags.AddCdnSignedUrlKeyName(parser, required=True)
def Run(self, args):
"""Issues the request to delete Signed URL key from the backend bucket."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
api_client = holder.client.apitools_client
messages = holder.client.messages
service = api_client.backendServices
backend_service_ref = flags.GLOBAL_BACKEND_SERVICE_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(holder.client))
request = messages.ComputeBackendServicesDeleteSignedUrlKeyRequest(
project=backend_service_ref.project,
backendService=backend_service_ref.Name(),
keyName=args.key_name)
operation = service.DeleteSignedUrlKey(request)
operation_ref = holder.resources.Parse(
operation.selfLink, collection='compute.globalOperations')
operation_poller = poller.Poller(service)
return waiter.WaitFor(operation_poller, operation_ref,
'Deleting Cloud CDN Signed URL key from [{0}]'.format(
backend_service_ref.Name()))
def CreateRequests(self, args):
"""Creates and returns a BackendServices.DeleteSignedUrlKey request."""
backend_service_ref = flags.GLOBAL_BACKEND_SERVICE_ARG.ResolveAsResource(
args,
self.resources,
scope_lister=compute_flags.GetDefaultScopeLister(self.compute_client))
request = self.messages.ComputeBackendServicesDeleteSignedUrlKeyRequest(
backendService=backend_service_ref.Name(),
keyName=args.key_name,
project=self.project)
return [request]

View File

@@ -0,0 +1,64 @@
# -*- 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 describing backend services."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.backend_services import backend_services_utils
from googlecloudsdk.command_lib.compute.backend_services import flags
class Describe(base.DescribeCommand):
"""Describe a backend service."""
@staticmethod
def Args(parser):
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.AddArgument(
parser, operation_type='describe')
def Run(self, args):
"""Issues request necessary to describe the backend service."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
(backend_services_utils.
IsDefaultRegionalBackendServicePropertyNoneWarnOtherwise())
backend_service_ref = (
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client)))
if backend_service_ref.Collection() == 'compute.backendServices':
service = client.apitools_client.backendServices
request = client.messages.ComputeBackendServicesGetRequest(
**backend_service_ref.AsDict())
elif backend_service_ref.Collection() == 'compute.regionBackendServices':
service = client.apitools_client.regionBackendServices
request = client.messages.ComputeRegionBackendServicesGetRequest(
**backend_service_ref.AsDict())
return client.MakeRequests([(service, 'Get', request)])[0]
Describe.detailed_help = base_classes.GetMultiScopeDescriberHelp(
'backend service', [base_classes.ScopeType.regional_scope,
base_classes.ScopeType.global_scope])

View File

@@ -0,0 +1,305 @@
# -*- 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 modifying backend services."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import io
from apitools.base.protorpclite import messages
from apitools.base.py import encoding
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute import property_selector
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions as calliope_exceptions
from googlecloudsdk.command_lib.compute import exceptions
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.backend_services import backend_services_utils
from googlecloudsdk.command_lib.compute.backend_services import flags
from googlecloudsdk.core import resources
from googlecloudsdk.core import yaml
from googlecloudsdk.core.console import console_io
from googlecloudsdk.core.util import edit
import six
class InvalidResourceError(calliope_exceptions.ToolException):
# Normally we'd want to subclass core.exceptions.Error, but base_classes.Edit
# abuses ToolException to classify errors when displaying messages to users,
# and we should continue to fit in that framework for now.
pass
class Edit(base.Command):
"""Modify a backend service.
*{command}* modifies a backend service of a Google Cloud load balancer or
Traffic Director. The backend service resource is fetched from the server
and presented in a text editor that displays the configurable fields.
The specific editor is defined by the ``EDITOR'' environment variable.
The name of each backend corresponds to the name of an instance group,
zonal NEG, serverless NEG, or internet NEG.
To add, remove, or swap backends, use the `gcloud compute backend-services
remove-backend` and `gcloud compute backend-services add-backend` commands.
"""
DEFAULT_FORMAT = 'yaml'
_BACKEND_SERVICE_ARG = flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG
@classmethod
def Args(cls, parser):
cls._BACKEND_SERVICE_ARG.AddArgument(parser)
def _ProcessEditedResource(self, holder, backend_service_ref, file_contents,
original_object, original_record,
modifiable_record, args):
"""Returns an updated resource that was edited by the user."""
# It's very important that we replace the characters of comment
# lines with spaces instead of removing the comment lines
# entirely. JSON and YAML deserialization give error messages
# containing line, column, and the character offset of where the
# error occurred. If the deserialization fails; we want to make
# sure those numbers map back to what the user actually had in
# front of him or her otherwise the errors will not be very
# useful.
non_comment_lines = '\n'.join(
' ' * len(line) if line.startswith('#') else line
for line in file_contents.splitlines())
modified_record = base_classes.DeserializeValue(
non_comment_lines, args.format or Edit.DEFAULT_FORMAT)
# Normalizes all of the fields that refer to other
# resource. (i.e., translates short names to URIs)
reference_normalizer = property_selector.PropertySelector(
transformations=self.GetReferenceNormalizers(holder.resources))
modified_record = reference_normalizer.Apply(modified_record)
if modifiable_record == modified_record:
new_object = None
else:
modified_record['name'] = original_record['name']
fingerprint = original_record.get('fingerprint')
if fingerprint:
modified_record['fingerprint'] = fingerprint
new_object = encoding.DictToMessage(modified_record,
holder.client.messages.BackendService)
# If existing object is equal to the proposed object or if
# there is no new object, then there is no work to be done, so we
# return the original object.
if not new_object or original_object == new_object:
return [original_object]
return holder.client.MakeRequests(
[self.GetSetRequest(holder.client, backend_service_ref, new_object)])
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
backend_service_ref = self._BACKEND_SERVICE_ARG.ResolveAsResource(
args,
holder.resources,
default_scope=backend_services_utils.GetDefaultScope(),
scope_lister=compute_flags.GetDefaultScopeLister(client))
get_request = self.GetGetRequest(client, backend_service_ref)
objects = client.MakeRequests([get_request])
original_object = objects[0]
original_record = encoding.MessageToDict(original_object)
# Selects only the fields that can be modified.
field_selector = property_selector.PropertySelector(properties=[
'backends',
'customRequestHeaders',
'customResponseHeaders',
'description',
'enableCDN',
'healthChecks',
'iap.enabled',
'iap.oauth2ClientId',
'iap.oauth2ClientSecret',
'port',
'portName',
'protocol',
'timeoutSec',
])
modifiable_record = field_selector.Apply(original_record)
file_contents = self.BuildFileContents(args, client, original_record,
modifiable_record)
resource_list = self.EditResource(args, backend_service_ref, file_contents,
holder, modifiable_record,
original_object, original_record)
for resource in resource_list:
yield resource
def BuildFileContents(self, args, client, original_record, modifiable_record):
buf = io.StringIO()
for line in base_classes.HELP.splitlines():
buf.write('#')
if line:
buf.write(' ')
buf.write(line)
buf.write('\n')
buf.write('\n')
buf.write(base_classes.SerializeDict(modifiable_record,
args.format or Edit.DEFAULT_FORMAT))
buf.write('\n')
example = base_classes.SerializeDict(
encoding.MessageToDict(self.GetExampleResource(client)),
args.format or Edit.DEFAULT_FORMAT)
base_classes.WriteResourceInCommentBlock(example, 'Example resource:', buf)
buf.write('#\n')
original = base_classes.SerializeDict(original_record,
args.format or Edit.DEFAULT_FORMAT)
base_classes.WriteResourceInCommentBlock(original, 'Original resource:',
buf)
return buf.getvalue()
def EditResource(self, args, backend_service_ref, file_contents, holder,
modifiable_record, original_object, original_record):
while True:
try:
file_contents = edit.OnlineEdit(file_contents)
except edit.NoSaveException:
raise exceptions.AbortedError('Edit aborted by user.')
try:
resource_list = self._ProcessEditedResource(holder, backend_service_ref,
file_contents,
original_object,
original_record,
modifiable_record, args)
break
except (ValueError, yaml.YAMLParseError,
messages.ValidationError,
calliope_exceptions.ToolException) as e:
message = getattr(e, 'message', six.text_type(e))
if isinstance(e, calliope_exceptions.ToolException):
problem_type = 'applying'
else:
problem_type = 'parsing'
message = ('There was a problem {0} your changes: {1}'
.format(problem_type, message))
if not console_io.PromptContinue(
message=message,
prompt_string='Would you like to edit the resource again?'):
raise exceptions.AbortedError('Edit aborted by user.')
return resource_list
def GetExampleResource(self, client):
uri_prefix = ('https://compute.googleapis.com/compute/v1/projects/'
'my-project/')
instance_groups_uri_prefix = (
'https://compute.googleapis.com/compute/v1/projects/'
'my-project/zones/')
return client.messages.BackendService(
backends=[
client.messages.Backend(
balancingMode=(
client.messages.Backend.BalancingModeValueValuesEnum.RATE),
group=(instance_groups_uri_prefix +
'us-central1-a/instanceGroups/group-1'),
maxRate=100),
client.messages.Backend(
balancingMode=(
client.messages.Backend.BalancingModeValueValuesEnum.RATE),
group=(instance_groups_uri_prefix +
'europe-west1-a/instanceGroups/group-2'),
maxRate=150),
],
customRequestHeaders=['X-Forwarded-Port:443'],
customResponseHeaders=['X-Client-Geo-Location:US,Mountain View'],
description='My backend service',
healthChecks=[
uri_prefix + 'global/httpHealthChecks/my-health-check-1',
uri_prefix + 'global/httpHealthChecks/my-health-check-2'
],
name='backend-service',
port=80,
portName='http',
protocol=client.messages.BackendService.ProtocolValueValuesEnum.HTTP,
selfLink=uri_prefix + 'global/backendServices/backend-service',
timeoutSec=30,
)
def GetReferenceNormalizers(self, resource_registry):
def MakeReferenceNormalizer(field_name, allowed_collections):
"""Returns a function to normalize resource references."""
def NormalizeReference(reference):
"""Returns normalized URI for field_name."""
try:
value_ref = resource_registry.Parse(reference)
except resources.UnknownCollectionException:
raise InvalidResourceError(
'[{field_name}] must be referenced using URIs.'.format(
field_name=field_name))
if value_ref.Collection() not in allowed_collections:
raise InvalidResourceError(
'Invalid [{field_name}] reference: [{value}].'. format(
field_name=field_name, value=reference))
return value_ref.SelfLink()
return NormalizeReference
# Ensure group is a uri or full collection path representing an instance
# group. Full uris/paths are required because if the user gives us less, we
# don't want to be in the business of guessing health checks.
return [
('healthChecks[]',
MakeReferenceNormalizer(
'healthChecks',
('compute.httpHealthChecks', 'compute.httpsHealthChecks',
'compute.healthChecks', 'compute.regionHealthChecks'))),
('backends[].group',
MakeReferenceNormalizer(
'group',
('compute.instanceGroups', 'compute.regionInstanceGroups'))),
]
def GetGetRequest(self, client, backend_service_ref):
if backend_service_ref.Collection() == 'compute.regionBackendServices':
return (client.apitools_client.regionBackendServices, 'Get',
client.messages.ComputeRegionBackendServicesGetRequest(
**backend_service_ref.AsDict()))
return (client.apitools_client.backendServices, 'Get',
client.messages.ComputeBackendServicesGetRequest(
**backend_service_ref.AsDict()))
def GetSetRequest(self, client, backend_service_ref, replacement):
if backend_service_ref.Collection() == 'compute.regionBackendServices':
return (client.apitools_client.regionBackendServices, 'Update',
client.messages.ComputeRegionBackendServicesUpdateRequest(
backendServiceResource=replacement,
**backend_service_ref.AsDict()))
return (client.apitools_client.backendServices, 'Update',
client.messages.ComputeBackendServicesUpdateRequest(
backendServiceResource=replacement,
**backend_service_ref.AsDict()))

View File

@@ -0,0 +1,101 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""Export backend service command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import sys
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.backend_services import backend_services_utils
from googlecloudsdk.command_lib.compute.backend_services import flags
from googlecloudsdk.command_lib.export import util as export_util
from googlecloudsdk.core.util import files
DETAILED_HELP = {
'DESCRIPTION':
"""\
Exports a backend service's configuration to a file.
This configuration can be imported at a later time.
""",
'EXAMPLES':
"""\
A backend service can be exported by running:
$ {command} NAME --destination=<path-to-file> --global
"""
}
@base.ReleaseTracks(base.ReleaseTrack.GA, base.ReleaseTrack.BETA,
base.ReleaseTrack.ALPHA)
class Export(base.Command):
"""Export a backend service.
Exports a backend service's configuration to a file.
This configuration can be imported at a later time.
"""
detailed_help = DETAILED_HELP
@classmethod
def GetApiVersion(cls):
"""Returns the API version based on the release track."""
if cls.ReleaseTrack() == base.ReleaseTrack.ALPHA:
return 'alpha'
elif cls.ReleaseTrack() == base.ReleaseTrack.BETA:
return 'beta'
return 'v1'
@classmethod
def GetSchemaPath(cls, for_help=False):
"""Returns the resource schema path."""
return export_util.GetSchemaPath(
'compute', cls.GetApiVersion(), 'BackendService', for_help=for_help)
@classmethod
def Args(cls, parser):
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.AddArgument(
parser, operation_type='export')
export_util.AddExportFlags(parser, cls.GetSchemaPath(for_help=True))
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
(backend_services_utils.
IsDefaultRegionalBackendServicePropertyNoneWarnOtherwise())
backend_service_ref = (
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client)))
backend_service = backend_services_utils.SendGetRequest(
client, backend_service_ref)
if args.destination:
with files.FileWriter(args.destination) as stream:
export_util.Export(message=backend_service,
stream=stream,
schema_path=self.GetSchemaPath())
else:
export_util.Export(message=backend_service,
stream=sys.stdout,
schema_path=self.GetSchemaPath())

View File

@@ -0,0 +1,194 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google Inc. 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 getting effective security policies of backend services."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute import firewalls_utils
from googlecloudsdk.api_lib.compute import lister
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.backend_services import flags
from googlecloudsdk.core import log
@base.UniverseCompatible
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA)
class GetEffectiveSecurityPolicies(base.DescribeCommand, base.ListCommand):
"""Get the effective security policies of a Compute Engine backend service.
*{command}* is used to get the effective security policies applied to the
backend service.
## EXAMPLES
To get the effective security policies for a backend service, run:
$ {command} example-backend-service
gets the effective security policies applied on the backend service
'example-backend-service'.
"""
@staticmethod
def Args(parser):
flags.GLOBAL_BACKEND_SERVICE_ARG.AddArgument(
parser, operation_type='get effective security policies'
)
parser.display_info.AddFormat(
firewalls_utils.EFFECTIVE_SECURITY_POLICY_LIST_FORMAT
)
lister.AddBaseListerArgs(parser)
def _GetSecurityPolicyName(self, sp_ref):
sp_ref_list = sp_ref.split('/')
return sp_ref_list[-1]
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
backend_service_ref = flags.GLOBAL_BACKEND_SERVICE_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
get_request = client.messages.ComputeBackendServicesGetRequest(
**backend_service_ref.AsDict()
)
get_response = client.MakeRequests([
(client.apitools_client.backendServices, 'Get', get_request),
])[0]
has_edge_sp = False
has_sp = False
get_effective_sp_request = client.messages.ComputeBackendServicesGetEffectiveSecurityPoliciesRequest(
**backend_service_ref.AsDict()
)
requests = [(
client.apitools_client.backendServices,
'GetEffectiveSecurityPolicies',
get_effective_sp_request,
)]
if (
hasattr(get_response, 'edgeSecurityPolicy')
and get_response.edgeSecurityPolicy
):
get_edge_sp_request = client.messages.ComputeSecurityPoliciesGetRequest(
project=backend_service_ref.project,
securityPolicy=self._GetSecurityPolicyName(
get_response.edgeSecurityPolicy
),
)
requests.append((
client.apitools_client.securityPolicies,
'Get',
get_edge_sp_request,
))
has_edge_sp = True
if hasattr(get_response, 'securityPolicy') and get_response.securityPolicy:
get_sp_request = client.messages.ComputeSecurityPoliciesGetRequest(
project=backend_service_ref.project,
securityPolicy=self._GetSecurityPolicyName(
get_response.securityPolicy
),
)
requests.append((
client.apitools_client.securityPolicies,
'Get',
get_sp_request,
))
has_sp = True
responses = client.MakeRequests(requests)
get_effective_sp_response = responses[0]
org_policies = []
edge_policy = None
backend_policy = None
all_policies = []
if hasattr(get_effective_sp_response, 'securityPolicies'):
org_policies.extend(get_effective_sp_response.securityPolicies)
if has_edge_sp:
edge_policy = responses[1]
if has_sp:
backend_policy = responses[2]
elif has_sp:
backend_policy = responses[1]
all_policies.extend(org_policies)
if edge_policy:
all_policies.append(edge_policy)
if backend_policy:
all_policies.append(backend_policy)
if args.IsSpecified('format') and args.format == 'json':
return (
client.messages.BackendServicesGetEffectiveSecurityPoliciesResponse(
securityPolicies=all_policies
)
)
result = []
for sp in org_policies:
result.extend(
firewalls_utils.ConvertOrgSecurityPolicyRulesToEffectiveSpRules(sp)
)
if edge_policy:
result.extend(
firewalls_utils.ConvertSecurityPolicyRulesToEffectiveSpRules(
edge_policy
)
)
if backend_policy:
result.extend(
firewalls_utils.ConvertSecurityPolicyRulesToEffectiveSpRules(
backend_policy
)
)
return result
def Epilog(self, resources_were_displayed):
del resources_were_displayed
log.status.Print('\n' + firewalls_utils.LIST_NOTICE_SECURITY_POLICY)
GetEffectiveSecurityPolicies.detailed_help = {
'EXAMPLES': """\
To get the effective security policies of backend_service with name
example-backend_service, run:
$ {command} example-backend_service
To show all fields of the security policy, please show in JSON format with
option --format=json
To list more the fields of the effective security policy rules in table
format, run:
$ {command} example-backend_service --format="table(
type,
security_policy_name,
priority,
description,
action,
preview,
expression,
src_ip_ranges.list():label=SRC_RANGES)"
""",
}

View File

@@ -0,0 +1,84 @@
# -*- 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 getting health status of backend(s) in a backend service."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute.backend_services import client
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.backend_services import backend_services_utils
from googlecloudsdk.command_lib.compute.backend_services import flags
class GetHealth(base.ListCommand):
"""Gets health status."""
_BACKEND_SERVICE_ARG = flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG
@classmethod
def Args(cls, parser):
cls._BACKEND_SERVICE_ARG.AddArgument(parser)
def GetReference(self, holder, args):
"""Override. Don't assume a default scope."""
return self._BACKEND_SERVICE_ARG.ResolveAsResource(
args,
holder.resources,
default_scope=backend_services_utils.GetDefaultScope(),
scope_lister=compute_flags.GetDefaultScopeLister(holder.client))
def Run(self, args):
"""Returns a list of backendServiceGroupHealth objects."""
if args.uri:
args.uri = False
args.format = 'value[delimiter="\n"](status.healthStatus[].instance)'
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
ref = self.GetReference(holder, args)
backend_service = client.BackendService(
ref, compute_client=holder.client)
return backend_service.GetHealth()
GetHealth.detailed_help = {
'brief': 'Get backend health statuses from a backend service.',
'DESCRIPTION': """
*{command}* is used to request the current health status of
instances in a backend service. Every group in the service
is checked and the health status of each configured instance
is printed.
If a group contains names of instances that don't exist or
instances that haven't yet been pushed to the load-balancing
system, they will not show up. Those that are listed as
``HEALTHY'' are able to receive load-balanced traffic. Those that
are marked as ``UNHEALTHY'' are either failing the configured
health-check or not responding to it.
Since the health checks are performed continuously and in
a distributed manner, the state returned by this command is
the most recent result of a vote of several redundant health
checks. Backend services that do not have a valid global
forwarding rule referencing it will not be health checked and
so will have no health status.
"""
}

View File

@@ -0,0 +1,70 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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 to get IAM policy for a resource."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute.backend_services import client
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.backend_services import flags
class GetIamPolicy(base.ListCommand):
"""Get the IAM policy for a Compute Engine backend service."""
@staticmethod
def Args(parser):
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.AddArgument(parser)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
backend_service_ref = (
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(holder.client)))
backend_service = client.BackendService(
backend_service_ref, compute_client=holder.client)
return backend_service.GetIamPolicy()
GetIamPolicy.detailed_help = {
'brief':
'Get the IAM policy for a Compute Engine backend service.',
'DESCRIPTION':
"""\
*{command}* displays the IAM policy associated with a
Compute Engine backend service in a project. If formatted as JSON,
the output can be edited and used as a policy file for
set-iam-policy. The output includes an "etag" field
identifying the version emitted and allowing detection of
concurrent policy updates; see
$ {parent} set-iam-policy for additional details. """,
'EXAMPLES':
"""\
To print the IAM policy for a given backend service, run:
$ {command} my-backend-service --region=REGION
$ {command} my-backend-service --global
"""
}

View File

@@ -0,0 +1,277 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""Import backend service command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import exceptions
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.backend_services import backend_services_utils
from googlecloudsdk.command_lib.compute.backend_services import flags
from googlecloudsdk.command_lib.export import util as export_util
from googlecloudsdk.core import yaml_validator
from googlecloudsdk.core.console import console_io
DETAILED_HELP = {
'DESCRIPTION':
"""\
Imports a backend service's configuration from a file.
""",
'EXAMPLES':
"""\
A backend service can be imported by running:
$ {command} NAME --source=<path-to-file> --global
"""
}
@base.ReleaseTracks(base.ReleaseTrack.GA)
@base.UniverseCompatible
class ImportGA(base.UpdateCommand):
"""Import a backend service.
If the specified backend service already exists, it will be overwritten.
Otherwise, a new backend service will be created.
To edit a backend service you can export the backend service to a file,
edit its configuration, and then import the new configuration.
"""
detailed_help = DETAILED_HELP
_support_negative_cache = False
@classmethod
def GetApiVersion(cls):
"""Returns the API version based on the release track."""
if cls.ReleaseTrack() == base.ReleaseTrack.ALPHA:
return 'alpha'
elif cls.ReleaseTrack() == base.ReleaseTrack.BETA:
return 'beta'
return 'v1'
@classmethod
def GetSchemaPath(cls, for_help=False):
"""Returns the resource schema path."""
return export_util.GetSchemaPath(
'compute', cls.GetApiVersion(), 'BackendService', for_help=for_help)
@classmethod
def Args(cls, parser):
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.AddArgument(
parser, operation_type='import')
export_util.AddImportFlags(parser, cls.GetSchemaPath(for_help=True))
def SendPatchRequest(self, client, resources, backend_service_ref,
replacement):
"""Sends a Backend Services patch request and waits for the operation to finish.
Args:
client: The API client.
resources: The resource parser.
backend_service_ref: The backend service reference.
replacement: The backend service to patch with.
Returns:
The operation result.
"""
if backend_service_ref.Collection() == 'compute.regionBackendServices':
service = client.apitools_client.regionBackendServices
operation = client.apitools_client.regionBackendServices.Patch(
client.messages.ComputeRegionBackendServicesPatchRequest(
project=backend_service_ref.project,
region=backend_service_ref.region,
backendService=backend_service_ref.Name(),
backendServiceResource=replacement))
else:
service = client.apitools_client.backendServices
operation = client.apitools_client.backendServices.Patch(
client.messages.ComputeBackendServicesPatchRequest(
project=backend_service_ref.project,
backendService=backend_service_ref.Name(),
backendServiceResource=replacement))
return backend_services_utils.WaitForOperation(resources, service,
operation,
backend_service_ref,
'Updating backend service')
def SendInsertRequest(self, client, resources, backend_service_ref,
backend_service):
"""Sends a Backend Services insert request and waits for the operation to finish.
Args:
client: The API client.
resources: The resource parser.
backend_service_ref: The backend service reference.
backend_service: The backend service to insert.
Returns:
The operation result.
"""
if backend_service_ref.Collection() == 'compute.regionBackendServices':
service = client.apitools_client.regionBackendServices
operation = client.apitools_client.regionBackendServices.Insert(
client.messages.ComputeRegionBackendServicesInsertRequest(
project=backend_service_ref.project,
region=backend_service_ref.region,
backendService=backend_service))
else:
service = client.apitools_client.backendServices
operation = client.apitools_client.backendServices.Insert(
client.messages.ComputeBackendServicesInsertRequest(
project=backend_service_ref.project,
backendService=backend_service))
return backend_services_utils.WaitForOperation(resources, service,
operation,
backend_service_ref,
'Creating backend service')
def GetClearedFieldList(self, backend_service):
"""Retrieves a list of fields to clear for the backend service being inserted.
Args:
backend_service: The backend service being inserted.
Returns:
The the list of fields to clear for a GA resource.
"""
# Unspecified fields are assumed to be cleared.
cleared_fields = []
# TODO(b/321258406) This entire section ought to use a library which
# walks the schema and compares it to the resource being imported
# and the utility must be tested to verify that deeply nested structures
# are creating field masks appropriately.
if not backend_service.securitySettings:
cleared_fields.append('securitySettings')
if not backend_service.localityLbPolicy:
cleared_fields.append('localityLbPolicy')
if not backend_service.localityLbPolicies:
cleared_fields.append('localityLbPolicies')
if not backend_service.circuitBreakers:
cleared_fields.append('circuitBreakers')
if not backend_service.consistentHash:
cleared_fields.append('consistentHash')
if not backend_service.outlierDetection:
cleared_fields.append('outlierDetection')
if not backend_service.customRequestHeaders:
cleared_fields.append('customRequestHeaders')
if not backend_service.customResponseHeaders:
cleared_fields.append('customResponseHeaders')
if backend_service.cdnPolicy:
cdn_policy = backend_service.cdnPolicy
if cdn_policy.defaultTtl is None:
cleared_fields.append('cdnPolicy.defaultTtl')
if cdn_policy.clientTtl is None:
cleared_fields.append('cdnPolicy.clientTtl')
if cdn_policy.maxTtl is None:
cleared_fields.append('cdnPolicy.maxTtl')
if not cdn_policy.negativeCachingPolicy:
cleared_fields.append('cdnPolicy.negativeCachingPolicy')
if not cdn_policy.bypassCacheOnRequestHeaders:
cleared_fields.append('cdnPolicy.bypassCacheOnRequestHeaders')
if cdn_policy.serveWhileStale is None:
cleared_fields.append('cdnPolicy.serveWhileStale')
if cdn_policy.requestCoalescing is None:
cleared_fields.append('cdnPolicy.requestCoalescing')
else:
cleared_fields.append('cdnPolicy')
if not backend_service.haPolicy:
cleared_fields.append('haPolicy')
else:
ha_policy = backend_service.haPolicy
if not ha_policy.fastIPMove:
cleared_fields.append('haPolicy.fastIPMove')
if not ha_policy.leader:
cleared_fields.append('haPolicy.leader')
else:
leader = ha_policy.leader
if not leader.backendGroup:
cleared_fields.append('haPolicy.leader.backendGroup')
if leader.networkEndpoint and not leader.networkEndpoint.instance:
cleared_fields.append('haPolicy.leader.networkEndpoint.instance')
elif not leader.networkEndpoint:
cleared_fields.append('haPolicy.leader.networkEndpoint')
return cleared_fields
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
resources = holder.resources
backend_service_ref = (
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.ResolveAsResource(
args,
resources,
scope_lister=compute_flags.GetDefaultScopeLister(client)))
data = console_io.ReadFromFileOrStdin(args.source or '-', binary=False)
try:
backend_service = export_util.Import(
message_type=client.messages.BackendService,
stream=data,
schema_path=self.GetSchemaPath())
except yaml_validator.ValidationError as e:
raise exceptions.ValidationError(str(e))
# Get existing backend service.
try:
backend_service_old = backend_services_utils.SendGetRequest(
client, backend_service_ref)
except apitools_exceptions.HttpError as error:
if error.status_code != 404:
raise error
# Backend service does not exist, create a new one.
return self.SendInsertRequest(client, resources, backend_service_ref,
backend_service)
# No change, do not send requests to server.
if backend_service_old == backend_service:
return
console_io.PromptContinue(
message=('Backend Service [{0}] will be overwritten.').format(
backend_service_ref.Name()),
cancel_on_no=True)
# populate id and fingerprint fields. These two fields are manually
# removed from the schema files.
backend_service.id = backend_service_old.id
backend_service.fingerprint = backend_service_old.fingerprint
# Unspecified fields are assumed to be cleared.
cleared_fields = self.GetClearedFieldList(backend_service)
with client.apitools_client.IncludeFields(cleared_fields):
return self.SendPatchRequest(client, resources, backend_service_ref,
backend_service)
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA)
class ImportAlphaBeta(ImportGA):
"""Import a backend service.
If the specified backend service already exists, it will be overwritten.
Otherwise, a new backend service will be created.
To edit a backend service you can export the backend service to a file,
edit its configuration, and then import the new configuration.
"""

View File

@@ -0,0 +1,61 @@
# -*- 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 listing backend services."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute import lister
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute.backend_services import flags
@base.ReleaseTracks(base.ReleaseTrack.GA)
class List(base.ListCommand):
"""List backend services."""
@staticmethod
def Args(parser):
parser.display_info.AddFormat(flags.DEFAULT_LIST_FORMAT)
lister.AddMultiScopeListerFlags(parser, regional=True, global_=True)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
request_data = lister.ParseMultiScopeFlags(args, holder.resources)
list_implementation = lister.MultiScopeLister(
client,
regional_service=client.apitools_client.regionBackendServices,
global_service=client.apitools_client.backendServices,
aggregation_service=client.apitools_client.backendServices)
return lister.Invoke(request_data, list_implementation)
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA)
class ListBeta(List):
@staticmethod
def Args(parser):
parser.display_info.AddFormat(flags.DEFAULT_BETA_LIST_FORMAT)
lister.AddMultiScopeListerFlags(parser, regional=True, global_=True)
List.detailed_help = base_classes.GetGlobalRegionalListerHelp(
'backend services')

View File

@@ -0,0 +1,98 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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 listing backend services."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import list_pager
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import exceptions as compute_exceptions
from googlecloudsdk.command_lib.compute import scope as compute_scope
from googlecloudsdk.command_lib.compute.backend_services import flags
from googlecloudsdk.core import properties
_DETAILED_HELP = {
"DESCRIPTION": """\
*{command}* retrieves the list of backend service resources in the
specified project for which you have compute.backendService.get
and compute.backendService.use permissions. This command is useful
when you're creating load balancers in a Shared VPC environment
and you want to use [cross-project service
referencing](https://cloud.google.com/load-balancing/docs/https#cross-project).
You can use this command to find out which backend
services in other projects are available to you for referencing.
""",
"EXAMPLES": """\
To list all global backend services in a project, run:
$ {command} --global
To list all backend services in a region, run:
$ {command} --region=REGION
""",
}
@base.UniverseCompatible
class ListUsable(base.ListCommand):
"""List usable backend services."""
detailed_help = _DETAILED_HELP
@staticmethod
def Args(parser):
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_NOT_REQUIRED_ARG.AddArgument(parser)
parser.display_info.AddFormat(flags.DEFAULT_BETA_LIST_FORMAT)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
messages = holder.client.messages
resource_scope, scope_value = (
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_NOT_REQUIRED_ARG.scopes.SpecifiedByArgs(
args
)
)
if not resource_scope:
raise compute_exceptions.ArgumentError(
"Either --global or --region must be specified."
)
if resource_scope.scope_enum == compute_scope.ScopeEnum.GLOBAL:
request = messages.ComputeBackendServicesListUsableRequest(
project=properties.VALUES.core.project.Get(required=True)
)
apitools = client.apitools_client.backendServices
elif resource_scope.scope_enum == compute_scope.ScopeEnum.REGION:
request = messages.ComputeRegionBackendServicesListUsableRequest(
region=scope_value,
project=properties.VALUES.core.project.Get(required=True),
)
apitools = client.apitools_client.regionBackendServices
return list_pager.YieldFromList(
apitools,
request,
method="ListUsable",
batch_size_attribute="maxResults",
batch_size=500,
field="items",
)

View File

@@ -0,0 +1,148 @@
# -*- 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 removing a backend from a backend service."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import encoding
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import exceptions
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.backend_services import flags
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA,
base.ReleaseTrack.GA)
class RemoveBackend(base.UpdateCommand):
"""Remove a backend from a backend service.
*{command}* is used to remove a backend from a backend
service.
Before removing a backend, it is a good idea to "drain" the
backend first. A backend can be drained by setting its
capacity scaler to zero through 'gcloud compute
backend-services edit'.
"""
support_global_neg = True
support_region_neg = True
@classmethod
def Args(cls, parser):
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.AddArgument(parser)
flags.AddInstanceGroupAndNetworkEndpointGroupArgs(
parser,
'remove from',
support_global_neg=cls.support_global_neg,
support_region_neg=cls.support_region_neg)
def GetGetRequest(self, client, backend_service_ref):
if backend_service_ref.Collection() == 'compute.regionBackendServices':
return (client.apitools_client.regionBackendServices,
'Get',
client.messages.ComputeRegionBackendServicesGetRequest(
backendService=backend_service_ref.Name(),
region=backend_service_ref.region,
project=backend_service_ref.project))
return (client.apitools_client.backendServices,
'Get',
client.messages.ComputeBackendServicesGetRequest(
backendService=backend_service_ref.Name(),
project=backend_service_ref.project))
def GetSetRequest(self, client, backend_service_ref, replacement):
if backend_service_ref.Collection() == 'compute.regionBackendServices':
return (client.apitools_client.regionBackendServices,
'Update',
client.messages.ComputeRegionBackendServicesUpdateRequest(
backendService=backend_service_ref.Name(),
backendServiceResource=replacement,
region=backend_service_ref.region,
project=backend_service_ref.project))
return (client.apitools_client.backendServices,
'Update',
client.messages.ComputeBackendServicesUpdateRequest(
backendService=backend_service_ref.Name(),
backendServiceResource=replacement,
project=backend_service_ref.project))
def _GetGroupRef(self, args, resources, client):
if args.instance_group:
return flags.MULTISCOPE_INSTANCE_GROUP_ARG.ResolveAsResource(
args,
resources,
scope_lister=compute_flags.GetDefaultScopeLister(client))
if args.network_endpoint_group:
return flags.GetNetworkEndpointGroupArg(
support_global_neg=self.support_global_neg,
support_region_neg=self.support_region_neg).ResolveAsResource(
args,
resources,
scope_lister=compute_flags.GetDefaultScopeLister(client))
def Modify(self, client, resources, backend_service_ref, args, existing):
replacement = encoding.CopyProtoMessage(existing)
group_ref = self._GetGroupRef(args, resources, client)
group_uri = group_ref.RelativeName()
backend_idx = None
for i, backend in enumerate(existing.backends):
if group_uri == resources.ParseURL(backend.group).RelativeName():
backend_idx = i
if backend_idx is None:
scope_value = getattr(group_ref, 'region', None)
if scope_value is None:
scope_value = getattr(group_ref, 'zone', None)
scope = 'zone'
else:
scope = 'region'
raise exceptions.ArgumentError(
'Backend [{0}] in {1} [{2}] is not a backend of backend service '
'[{3}].'.format(group_ref.Name(),
scope,
scope_value,
backend_service_ref.Name()))
else:
replacement.backends.pop(backend_idx)
return replacement
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
backend_service_ref = (
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client)))
get_request = self.GetGetRequest(client, backend_service_ref)
objects = client.MakeRequests([get_request])
new_object = self.Modify(client, holder.resources, backend_service_ref,
args, objects[0])
return client.MakeRequests(
[self.GetSetRequest(client, backend_service_ref, new_object)])

View File

@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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 to set IAM policy for a resource."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute.backend_services import client
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.backend_services import flags
from googlecloudsdk.command_lib.iam import iam_util
class RemoveIamPolicyBinding(base.Command):
"""Remove an IAM policy binding from a Compute Engine backend service."""
@staticmethod
def Args(parser):
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.AddArgument(parser)
iam_util.AddArgsForRemoveIamPolicyBinding(parser)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
backend_service_ref = (
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(holder.client)))
backend_service = client.BackendService(
backend_service_ref, compute_client=holder.client)
return backend_service.RemoveIamPolicyBinding(args.member, args.role)
RemoveIamPolicyBinding.detailed_help = {
'brief':
'Remove an IAM policy binding from a Compute Engine backend service.',
'DESCRIPTION':
"""\
Remove an IAM policy binding from a Compute Engine backend service. """,
'EXAMPLES':
"""\
To remove an IAM policy binding for the role of
'roles/compute.loadBalancerServiceUser' for the user 'test-user@gmail.com'
with backend service 'my-backend-service' and region 'REGION', run:
$ {command} my-backend-service --region=REGION \
--member='user:test-user@gmail.com' \
--role='roles/compute.loadBalancerServiceUser'
$ {command} my-backend-service --global \
--member='user:test-user@gmail.com' \
--role='roles/compute.loadBalancerServiceUser'
See https://cloud.google.com/iam/docs/managing-policies for details of
policy role and member types.
"""
}

View File

@@ -0,0 +1,89 @@
# -*- 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.
"""Command for removing service bindings to a backend service."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import encoding
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute.backend_services import client
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute import reference_utils
from googlecloudsdk.command_lib.compute.backend_services import flags
_DETAILED_HELP = {
'EXAMPLES':
"""\
To remove a service binding from a backend service, run:
$ {command} NAME \
--service-bindings=SERVICE_BINDING1 --global
""",
}
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA,
base.ReleaseTrack.GA)
class RemoveServiceBindings(base.UpdateCommand):
"""Remove service bindings from a backend service."""
detailed_help = _DETAILED_HELP
@classmethod
def Args(cls, parser):
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.AddArgument(parser)
flags.AddServiceBindings(
parser,
required=True,
help_text='List of service binding names to be removed from the backend service.'
)
def _Modify(self, backend_service_ref, args, existing):
location = (
backend_service_ref.region if backend_service_ref.Collection()
== 'compute.regionBackendServices' else 'global')
replacement = encoding.CopyProtoMessage(existing)
old_bindings = replacement.serviceBindings or []
bindings_to_remove = [
reference_utils.BuildServiceBindingUrl(backend_service_ref.project,
location, binding_name)
for binding_name in args.service_bindings
]
replacement.serviceBindings = reference_utils.FilterReferences(
old_bindings, bindings_to_remove)
return replacement
def Run(self, args):
"""Remove service bindings from the Backend Service."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
backend_service_ref = (
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(holder.client)))
backend_service = client.BackendService(
backend_service_ref, compute_client=holder.client)
new_object = self._Modify(backend_service_ref, args, backend_service.Get())
cleared_fields = []
if not new_object.serviceBindings:
cleared_fields.append('serviceBindings')
with holder.client.apitools_client.IncludeFields(cleared_fields):
return backend_service.Set(new_object)

View File

@@ -0,0 +1,72 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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 to set IAM policy for a resource."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute.backend_services import client
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.backend_services import flags
from googlecloudsdk.command_lib.iam import iam_util
class SetIamPolicy(base.Command):
"""Set the IAM policy binding for a Compute Engine backend service."""
@staticmethod
def Args(parser):
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.AddArgument(parser)
iam_util.AddArgForPolicyFile(parser)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
backend_service_ref = (
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(holder.client)))
backend_service = client.BackendService(
backend_service_ref, compute_client=holder.client)
return backend_service.SetIamPolicy(
iam_util.ParsePolicyFile(args.policy_file,
holder.client.messages.Policy))
SetIamPolicy.detailed_help = {
'brief':
'Set the IAM policy binding for a Compute Engine backend service.',
'DESCRIPTION':
"""\
Sets the IAM policy for the given backend service as defined in a
JSON or YAML file. """,
'EXAMPLES':
"""\
The following command will read an IAM policy defined in a JSON file
'policy.json' and set it for the backend service `my-backend-service`:
$ {command} my-backend-service policy.json --region=REGION
$ {command} my-backend-service policy.json --global
See https://cloud.google.com/iam/docs/managing-policies for details of the
policy file format and contents.
"""
}

View File

@@ -0,0 +1,72 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 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 setting the security policy for a backend service."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute.backend_services import client
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute.backend_services import flags
from googlecloudsdk.command_lib.compute.security_policies import (
flags as security_policy_flags)
@base.Deprecate(
is_removed=False,
warning=('This command is deprecated and will not be promoted to beta. '
'Please use "gcloud beta backend-services update" instead.'))
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class SetSecurityPolicy(base.SilentCommand):
"""Set the security policy for a backend service."""
SECURITY_POLICY_ARG = None
@classmethod
def Args(cls, parser):
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.AddArgument(parser)
cls.SECURITY_POLICY_ARG = (
security_policy_flags.SecurityPolicyArgumentForTargetResource(
resource='backend service', required=True))
cls.SECURITY_POLICY_ARG.AddArgument(parser)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
ref = flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.ResolveAsResource(
args, holder.resources)
if not args.security_policy:
security_policy_ref = None
else:
security_policy_ref = self.SECURITY_POLICY_ARG.ResolveAsResource(
args, holder.resources).SelfLink()
backend_service = client.BackendService(ref, compute_client=holder.client)
return backend_service.SetSecurityPolicy(
security_policy=security_policy_ref)
SetSecurityPolicy.detailed_help = {
'brief':
'Set the security policy for a backend service',
'DESCRIPTION':
"""\
*{command}* is used to set the security policy for a backend service.
Setting an empty string will clear the existing security policy. """,
}

View File

@@ -0,0 +1,662 @@
# -*- 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.
"""Commands for updating backend services.
There are separate alpha, beta, and GA command classes in this file.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import encoding
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute.backend_services import (
client as backend_service_client)
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.compute import cdn_flags_utils as cdn_flags
from googlecloudsdk.command_lib.compute import exceptions as compute_exceptions
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute import reference_utils
from googlecloudsdk.command_lib.compute import signed_url_flags
from googlecloudsdk.command_lib.compute.backend_services import backend_services_utils
from googlecloudsdk.command_lib.compute.backend_services import flags
from googlecloudsdk.command_lib.compute.security_policies import (
flags as security_policy_flags)
from googlecloudsdk.core import log
def AddIapFlag(parser):
# TODO(b/34479878): It would be nice if the auto-generated help text were
# a bit better so we didn't need to be quite so verbose here.
flags.AddIap(
parser,
help="""\
Change the Identity Aware Proxy (IAP) service configuration for the
backend service. You can set IAP to 'enabled' or 'disabled', or modify
the OAuth2 client configuration (oauth2-client-id and
oauth2-client-secret) used by IAP. If any fields are unspecified, their
values will not be modified. For instance, if IAP is enabled,
'--iap=disabled' will disable IAP, and a subsequent '--iap=enabled' will
then enable it with the same OAuth2 client configuration as the first
time it was enabled. See
https://cloud.google.com/iap/ for more information about this feature.
""")
class UpdateHelper(object):
"""Helper class that updates a backend service."""
HEALTH_CHECK_ARG = None
HTTP_HEALTH_CHECK_ARG = None
HTTPS_HEALTH_CHECK_ARG = None
SECURITY_POLICY_ARG = None
EDGE_SECURITY_POLICY_ARG = None
@classmethod
def Args(
cls,
parser,
support_subsetting_subset_size,
support_ip_port_dynamic_forwarding,
support_zonal_affinity,
support_allow_multinetwork,
):
"""Add all arguments for updating a backend service."""
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.AddArgument(
parser, operation_type='update')
flags.AddDescription(parser)
cls.HEALTH_CHECK_ARG = flags.HealthCheckArgument()
cls.HEALTH_CHECK_ARG.AddArgument(parser, cust_metavar='HEALTH_CHECK')
cls.HTTP_HEALTH_CHECK_ARG = flags.HttpHealthCheckArgument()
cls.HTTP_HEALTH_CHECK_ARG.AddArgument(
parser, cust_metavar='HTTP_HEALTH_CHECK')
cls.HTTPS_HEALTH_CHECK_ARG = flags.HttpsHealthCheckArgument()
cls.HTTPS_HEALTH_CHECK_ARG.AddArgument(
parser, cust_metavar='HTTPS_HEALTH_CHECK')
flags.AddNoHealthChecks(parser)
cls.SECURITY_POLICY_ARG = (
security_policy_flags
.SecurityPolicyMultiScopeArgumentForTargetResource(
resource='backend service',
region_hidden=True,
scope_flags_usage=compute_flags.ScopeFlagsUsage
.USE_EXISTING_SCOPE_FLAGS,
short_help_text=(
'The security policy that will be set for this {0}.')))
cls.SECURITY_POLICY_ARG.AddArgument(parser)
cls.EDGE_SECURITY_POLICY_ARG = (
security_policy_flags.EdgeSecurityPolicyArgumentForTargetResource(
resource='backend service'))
cls.EDGE_SECURITY_POLICY_ARG.AddArgument(parser)
flags.AddTimeout(parser, default=None)
flags.AddPortName(parser)
flags.AddProtocol(parser, default=None)
flags.AddConnectionDrainingTimeout(parser)
flags.AddEnableCdn(parser)
flags.AddCacheKeyIncludeProtocol(parser, default=None)
flags.AddCacheKeyIncludeHost(parser, default=None)
flags.AddCacheKeyIncludeQueryString(parser, default=None)
flags.AddCacheKeyQueryStringList(parser)
flags.AddCacheKeyExtendedCachingArgs(parser)
flags.AddSessionAffinity(
parser,
support_stateful_affinity=True,
)
flags.AddAffinityCookie(parser, support_stateful_affinity=True)
signed_url_flags.AddSignedUrlCacheMaxAge(
parser, required=False, unspecified_help='')
flags.AddSubsettingPolicy(parser)
if support_subsetting_subset_size:
flags.AddSubsettingSubsetSize(parser)
flags.AddConnectionDrainOnFailover(parser, default=None)
flags.AddDropTrafficIfUnhealthy(parser, default=None)
flags.AddFailoverRatio(parser)
flags.AddEnableLogging(parser)
flags.AddLoggingSampleRate(parser)
flags.AddLoggingOptional(parser)
flags.AddLoggingOptionalFields(parser)
AddIapFlag(parser)
flags.AddCustomRequestHeaders(parser, remove_all_flag=True, default=None)
cdn_flags.AddCdnPolicyArgs(parser, 'backend service', update_command=True)
flags.AddConnectionTrackingPolicy(parser)
flags.AddCompressionMode(parser)
flags.AddServiceLoadBalancingPolicy(parser, required=False, is_update=True)
flags.AddServiceBindings(parser, required=False, is_update=True)
flags.AddLocalityLbPolicy(parser, is_update=True)
flags.AddIpAddressSelectionPolicy(parser)
flags.AddExternalMigration(parser)
flags.AddBackendServiceTlsSettings(parser, add_clear_argument=True)
flags.AddBackendServiceCustomMetrics(parser, add_clear_argument=True)
if support_ip_port_dynamic_forwarding:
flags.AddIpPortDynamicForwarding(parser)
if support_zonal_affinity:
flags.AddZonalAffinity(parser)
if support_allow_multinetwork:
flags.AddAllowMultinetwork(parser)
def __init__(
self,
support_subsetting_subset_size,
support_ip_port_dynamic_forwarding=False,
support_zonal_affinity=False,
support_allow_multinetwork=False,
release_track=None,
):
self._support_subsetting_subset_size = support_subsetting_subset_size
self._support_ip_port_dynamic_forwarding = (
support_ip_port_dynamic_forwarding
)
self._support_zonal_affinity = support_zonal_affinity
self._support_allow_multinetwork = support_allow_multinetwork
self._release_track = release_track
def Modify(self, client, resources, args, existing, backend_service_ref):
"""Modify Backend Service."""
replacement = encoding.CopyProtoMessage(existing)
cleared_fields = []
location = (
backend_service_ref.region if backend_service_ref.Collection()
== 'compute.regionBackendServices' else 'global')
if args.connection_draining_timeout is not None:
replacement.connectionDraining = client.messages.ConnectionDraining(
drainingTimeoutSec=args.connection_draining_timeout)
if args.no_custom_request_headers is not None:
replacement.customRequestHeaders = []
if args.custom_request_header is not None:
replacement.customRequestHeaders = args.custom_request_header
if not replacement.customRequestHeaders:
cleared_fields.append('customRequestHeaders')
if args.custom_response_header is not None:
replacement.customResponseHeaders = args.custom_response_header
if args.no_custom_response_headers:
replacement.customResponseHeaders = []
if not replacement.customResponseHeaders:
cleared_fields.append('customResponseHeaders')
if args.IsSpecified('description'):
replacement.description = args.description
health_checks = flags.GetHealthCheckUris(args, self, resources)
if health_checks:
replacement.healthChecks = health_checks
if args.IsSpecified('no_health_checks'):
replacement.healthChecks = []
cleared_fields.append('healthChecks')
if args.timeout:
replacement.timeoutSec = args.timeout
if args.port_name:
replacement.portName = args.port_name
if args.protocol:
replacement.protocol = (
client.messages.BackendService.ProtocolValueValuesEnum(args.protocol))
if args.enable_cdn is not None:
replacement.enableCDN = args.enable_cdn
elif not replacement.enableCDN and args.cache_mode:
# TODO(b/209812994): Replace implicit config change with
# warning that CDN is disabled and a prompt to enable it with
# --enable-cdn
log.warning(
'Setting a cache mode also enabled Cloud CDN, which was previously ' +
'disabled. If this was not intended, disable Cloud ' +
'CDN with `--no-enable-cdn`.')
replacement.enableCDN = True
if args.session_affinity is not None:
replacement.sessionAffinity = (
client.messages.BackendService.SessionAffinityValueValuesEnum(
args.session_affinity))
# strongSessionAffinityCookie only usable with STRONG_COOKIE_AFFINITY.
if args.session_affinity != 'STRONG_COOKIE_AFFINITY':
cleared_fields.append('strongSessionAffinityCookie')
backend_services_utils.ApplyAffinityCookieArgs(client, args, replacement)
if args.connection_draining_timeout is not None:
replacement.connectionDraining = client.messages.ConnectionDraining(
drainingTimeoutSec=args.connection_draining_timeout)
backend_services_utils.ApplySubsettingArgs(
client, args, replacement, self._support_subsetting_subset_size
)
if args.locality_lb_policy is not None:
replacement.localityLbPolicy = (
client.messages.BackendService.LocalityLbPolicyValueValuesEnum(
args.locality_lb_policy))
if args.no_locality_lb_policy is not None:
replacement.localityLbPolicy = None
cleared_fields.append('localityLbPolicy')
backend_services_utils.ApplyCdnPolicyArgs(
client,
args,
replacement,
is_update=True,
apply_signed_url_cache_max_age=True,
cleared_fields=cleared_fields)
backend_services_utils.ApplyConnectionTrackingPolicyArgs(
client, args, replacement)
if args.compression_mode is not None:
replacement.compressionMode = (
client.messages.BackendService.CompressionModeValueValuesEnum(
args.compression_mode))
self._ApplyIapArgs(client, args.iap, existing, replacement)
backend_services_utils.ApplyFailoverPolicyArgs(
client.messages, args, replacement
)
backend_services_utils.ApplyLogConfigArgs(
client.messages,
args,
replacement,
cleared_fields=cleared_fields,
)
if args.service_lb_policy is not None:
replacement.serviceLbPolicy = reference_utils.BuildServiceLbPolicyUrl(
project_name=backend_service_ref.project,
location=location,
policy_name=args.service_lb_policy,
release_track=self._release_track,
)
if args.no_service_lb_policy is not None:
replacement.serviceLbPolicy = None
cleared_fields.append('serviceLbPolicy')
if args.service_bindings is not None:
replacement.serviceBindings = [
reference_utils.BuildServiceBindingUrl(backend_service_ref.project,
location, binding_name)
for binding_name in args.service_bindings
]
if args.no_service_bindings is not None:
replacement.serviceBindings = []
cleared_fields.append('serviceBindings')
backend_services_utils.ApplyIpAddressSelectionPolicyArgs(
client, args, replacement
)
if args.external_managed_migration_state is not None:
replacement.externalManagedMigrationState = client.messages.BackendService.ExternalManagedMigrationStateValueValuesEnum(
args.external_managed_migration_state
)
if args.external_managed_migration_testing_percentage is not None:
replacement.externalManagedMigrationTestingPercentage = (
args.external_managed_migration_testing_percentage
)
if args.clear_external_managed_migration_state is not None:
replacement.externalManagedMigrationState = None
replacement.externalManagedMigrationTestingPercentage = None
cleared_fields.append('externalManagedMigrationState')
cleared_fields.append('externalManagedMigrationTestingPercentage')
if args.load_balancing_scheme is not None:
replacement.loadBalancingScheme = (
client.messages.BackendService.LoadBalancingSchemeValueValuesEnum(
args.load_balancing_scheme
)
)
if args.tls_settings is not None:
backend_services_utils.ApplyTlsSettingsArgs(
client,
args,
replacement,
backend_service_ref.project,
location,
self._release_track,
)
if args.no_tls_settings is not None:
replacement.tlsSettings = None
cleared_fields.append('tlsSettings')
if args.custom_metrics:
replacement.customMetrics = args.custom_metrics
if args.custom_metrics_file:
replacement.customMetrics = args.custom_metrics_file
if args.clear_custom_metrics:
replacement.customMetrics = []
cleared_fields.append('customMetrics')
if self._support_ip_port_dynamic_forwarding:
backend_services_utils.IpPortDynamicForwarding(client, args, replacement)
if self._support_zonal_affinity:
backend_services_utils.ZonalAffinity(client, args, replacement)
if self._support_allow_multinetwork and args.IsSpecified(
'allow_multinetwork'
):
replacement.allowMultinetwork = args.allow_multinetwork
return replacement, cleared_fields
def ValidateArgs(self, args):
"""Validate arguments."""
if not any([
args.IsSpecified('affinity_cookie_ttl'),
args.IsSpecified('connection_draining_timeout'),
args.IsSpecified('no_custom_request_headers'),
args.IsSpecified('custom_request_header'),
args.IsSpecified('description'),
args.IsSpecified('enable_cdn'),
args.IsSpecified('cache_key_include_protocol'),
args.IsSpecified('cache_key_include_host'),
args.IsSpecified('cache_key_include_query_string'),
args.IsSpecified('cache_key_query_string_whitelist'),
args.IsSpecified('cache_key_query_string_blacklist'),
args.IsSpecified('cache_key_include_http_header'),
args.IsSpecified('cache_key_include_named_cookie'),
args.IsSpecified('signed_url_cache_max_age'),
args.IsSpecified('http_health_checks'),
args.IsSpecified('iap'),
args.IsSpecified('port_name'),
args.IsSpecified('protocol'),
args.IsSpecified('security_policy'),
args.IsSpecified('edge_security_policy'),
args.IsSpecified('session_affinity'),
args.IsSpecified('timeout'),
args.IsSpecified('connection_drain_on_failover'),
args.IsSpecified('drop_traffic_if_unhealthy'),
args.IsSpecified('failover_ratio'),
args.IsSpecified('enable_logging'),
args.IsSpecified('logging_sample_rate'),
args.IsSpecified('logging_optional'),
args.IsSpecified('logging_optional_fields'),
args.IsSpecified('health_checks'),
args.IsSpecified('https_health_checks'),
args.IsSpecified('no_health_checks'),
args.IsSpecified('subsetting_policy'),
args.IsSpecified('subsetting_subset_size')
if self._support_subsetting_subset_size
else False,
args.IsSpecified('request_coalescing'),
args.IsSpecified('cache_mode'),
args.IsSpecified('client_ttl'),
args.IsSpecified('no_client_ttl'),
args.IsSpecified('default_ttl'),
args.IsSpecified('no_default_ttl'),
args.IsSpecified('max_ttl'),
args.IsSpecified('no_max_ttl'),
args.IsSpecified('negative_caching'),
args.IsSpecified('negative_caching_policy'),
args.IsSpecified('no_negative_caching_policies'),
args.IsSpecified('custom_response_header'),
args.IsSpecified('no_custom_response_headers'),
args.IsSpecified('serve_while_stale'),
args.IsSpecified('no_serve_while_stale'),
args.IsSpecified('bypass_cache_on_request_headers'),
args.IsSpecified('no_bypass_cache_on_request_headers'),
args.IsSpecified('connection_persistence_on_unhealthy_backends'),
args.IsSpecified('tracking_mode'),
args.IsSpecified('idle_timeout_sec'),
args.IsSpecified('enable_strong_affinity'),
args.IsSpecified('compression_mode'),
args.IsSpecified('service_lb_policy'),
args.IsSpecified('no_service_lb_policy'),
args.IsSpecified('service_bindings'),
args.IsSpecified('no_service_bindings'),
args.IsSpecified('locality_lb_policy'),
args.IsSpecified('no_locality_lb_policy'),
args.IsSpecified('ip_address_selection_policy'),
args.IsSpecified('external_managed_migration_state'),
args.IsSpecified('external_managed_migration_testing_percentage'),
args.IsSpecified('clear_external_managed_migration_state'),
args.IsSpecified('load_balancing_scheme'),
args.IsSpecified('tls_settings'),
args.IsSpecified('no_tls_settings'),
args.IsSpecified('custom_metrics'),
args.IsSpecified('custom_metrics_file'),
args.IsSpecified('clear_custom_metrics'),
args.IsSpecified('ip_port_dynamic_forwarding')
if self._support_ip_port_dynamic_forwarding
else False,
args.IsSpecified('zonal_affinity_spillover')
if self._support_zonal_affinity
else False,
args.IsSpecified('zonal_affinity_spillover_ratio')
if self._support_zonal_affinity
else False,
args.IsSpecified('allow_multinetwork')
if self._support_allow_multinetwork
else False,
]):
raise compute_exceptions.UpdatePropertyError(
'At least one property must be modified.')
def GetSetRequest(self, client, backend_service_ref, replacement):
"""Returns a backend service patch request."""
if (
backend_service_ref.Collection() == 'compute.backendServices'
and replacement.failoverPolicy
):
raise exceptions.InvalidArgumentException(
'--global',
'failover policy parameters are only for regional passthrough '
'Network Load Balancers.')
if backend_service_ref.Collection() == 'compute.regionBackendServices':
return (
client.apitools_client.regionBackendServices,
'Patch',
client.messages.ComputeRegionBackendServicesPatchRequest(
project=backend_service_ref.project,
region=backend_service_ref.region,
backendService=backend_service_ref.Name(),
backendServiceResource=replacement),
)
return (
client.apitools_client.backendServices,
'Patch',
client.messages.ComputeBackendServicesPatchRequest(
project=backend_service_ref.project,
backendService=backend_service_ref.Name(),
backendServiceResource=replacement),
)
def _GetSetSecurityPolicyRequest(self, client, backend_service_ref,
security_policy_ref):
backend_service = backend_service_client.BackendService(
backend_service_ref, compute_client=client)
return backend_service.MakeSetSecurityPolicyRequestTuple(
security_policy=security_policy_ref)
def _GetSetEdgeSecurityPolicyRequest(self, client, backend_service_ref,
security_policy_ref):
backend_service = backend_service_client.BackendService(
backend_service_ref, compute_client=client)
return backend_service.MakeSetEdgeSecurityPolicyRequestTuple(
security_policy=security_policy_ref)
def GetGetRequest(self, client, backend_service_ref):
"""Create Backend Services get request."""
if backend_service_ref.Collection() == 'compute.regionBackendServices':
return (
client.apitools_client.regionBackendServices,
'Get',
client.messages.ComputeRegionBackendServicesGetRequest(
project=backend_service_ref.project,
region=backend_service_ref.region,
backendService=backend_service_ref.Name()),
)
return (
client.apitools_client.backendServices,
'Get',
client.messages.ComputeBackendServicesGetRequest(
project=backend_service_ref.project,
backendService=backend_service_ref.Name()),
)
def _ApplyIapArgs(self, client, iap_arg, existing, replacement):
"""Applies IAP args."""
if iap_arg is not None:
existing_iap = existing.iap
replacement.iap = backend_services_utils.GetIAP(
iap_arg, client.messages, existing_iap_settings=existing_iap)
if replacement.iap.enabled and not (existing_iap and
existing_iap.enabled):
log.warning(backend_services_utils.IapBestPracticesNotice())
if (replacement.iap.enabled and replacement.protocol
is not client.messages.BackendService.ProtocolValueValuesEnum.HTTPS):
log.warning(backend_services_utils.IapHttpWarning())
def Run(self, args, holder):
"""Issues requests necessary to update the Backend Services."""
self.ValidateArgs(args)
client = holder.client
backend_service_ref = (
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client)))
get_request = self.GetGetRequest(client, backend_service_ref)
objects = client.MakeRequests([get_request])
new_object, cleared_fields = self.Modify(client, holder.resources, args,
objects[0], backend_service_ref)
# If existing object is equal to the proposed object or if
# Modify() returns None, then there is no work to be done, so we
# print the resource and return.
if objects[0] == new_object:
# Only skip update if security_policy and edge_security_policy are not
# set.
if (getattr(args, 'security_policy', None) is None and
getattr(args, 'edge_security_policy', None) is None):
log.status.Print(
'No change requested; skipping update for [{0}].'.format(
objects[0].name))
return objects
backend_service_result = []
else:
backend_service_request = self.GetSetRequest(client, backend_service_ref,
new_object)
# Cleared list fields need to be explicitly identified for Patch API.
with client.apitools_client.IncludeFields(cleared_fields):
backend_service_result = client.MakeRequests([backend_service_request])
# Empty string is a valid value.
if getattr(args, 'security_policy', None) is not None:
if getattr(args, 'security_policy', None):
security_policy_ref = self.SECURITY_POLICY_ARG.ResolveAsResource(
args, holder.resources).SelfLink()
# If security policy is an empty string we should clear the current policy
else:
security_policy_ref = None
security_policy_request = self._GetSetSecurityPolicyRequest(
client, backend_service_ref, security_policy_ref)
security_policy_result = client.MakeRequests([security_policy_request])
else:
security_policy_result = []
# Empty string is a valid value.
if getattr(args, 'edge_security_policy', None) is not None:
if getattr(args, 'edge_security_policy', None):
security_policy_ref = self.EDGE_SECURITY_POLICY_ARG.ResolveAsResource(
args, holder.resources).SelfLink()
# If security policy is an empty string we should clear the current policy
else:
security_policy_ref = None
edge_security_policy_request = self._GetSetEdgeSecurityPolicyRequest(
client, backend_service_ref, security_policy_ref)
edge_security_policy_result = client.MakeRequests(
[edge_security_policy_request])
else:
edge_security_policy_result = []
return (backend_service_result + security_policy_result +
edge_security_policy_result)
@base.UniverseCompatible
@base.ReleaseTracks(base.ReleaseTrack.GA)
class UpdateGA(base.UpdateCommand):
"""Update a backend service.
*{command}* is used to update backend services.
"""
_support_subsetting_subset_size = False
_support_ip_port_dynamic_forwarding = False
_support_zonal_affinity = False
_support_allow_multinetwork = False
@classmethod
def Args(cls, parser):
UpdateHelper.Args(
parser,
support_subsetting_subset_size=cls._support_subsetting_subset_size,
support_ip_port_dynamic_forwarding=cls._support_ip_port_dynamic_forwarding,
support_zonal_affinity=cls._support_zonal_affinity,
support_allow_multinetwork=cls._support_allow_multinetwork,
)
def Run(self, args):
"""Issues requests necessary to update the Backend Services."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
return UpdateHelper(
self._support_subsetting_subset_size,
support_ip_port_dynamic_forwarding=self._support_ip_port_dynamic_forwarding,
support_zonal_affinity=self._support_zonal_affinity,
support_allow_multinetwork=self._support_allow_multinetwork,
release_track=self.ReleaseTrack(),
).Run(args, holder)
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class UpdateBeta(UpdateGA):
"""Update a backend service.
*{command}* is used to update backend services.
"""
_support_subsetting_subset_size = True
_support_ip_port_dynamic_forwarding = True
_support_zonal_affinity = True
_support_allow_multinetwork = False
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class UpdateAlpha(UpdateBeta):
"""Update a backend service.
*{command}* is used to update backend services.
"""
_support_subsetting_subset_size = True
_support_ip_port_dynamic_forwarding = True
_support_zonal_affinity = True
_support_allow_multinetwork = True

View File

@@ -0,0 +1,500 @@
# -*- 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 updating a backend in a backend service."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import encoding
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import exceptions
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute.backend_services import backend_flags
from googlecloudsdk.command_lib.compute.backend_services import backend_services_utils
from googlecloudsdk.command_lib.compute.backend_services import flags
@base.UniverseCompatible
@base.ReleaseTracks(base.ReleaseTrack.GA)
class UpdateBackend(base.UpdateCommand):
"""Update an existing backend of a load balancer or Traffic Director.
*{command}* updates attributes of a backend that is already associated with a
backend service. Configurable attributes depend on the load balancing scheme
and the type of backend (instance group, zonal NEG, serverless NEG, or
internet NEG). For more information, see [traffic
distribution](https://cloud.google.com/load-balancing/docs/backend-service#traffic_distribution).
and the [Failover for Internal TCP/UDP Load Balancing
overview](https://cloud.google.com/load-balancing/docs/internal/failover-overview).
To add, remove, or swap backends, use the `gcloud compute backend-services
remove-backend` and `gcloud compute backend-services add-backend` commands.
"""
@staticmethod
def Args(parser):
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.AddArgument(parser)
backend_flags.AddDescription(parser)
flags.AddInstanceGroupAndNetworkEndpointGroupArgs(parser, 'update in')
backend_flags.AddBalancingMode(parser)
backend_flags.AddCapacityLimits(parser)
backend_flags.AddCapacityScalar(parser)
backend_flags.AddFailover(parser, default=None)
backend_flags.AddPreference(parser)
backend_flags.AddCustomMetrics(parser, add_clear_argument=True)
def _GetGetRequest(self, client, backend_service_ref):
if backend_service_ref.Collection() == 'compute.regionBackendServices':
return (client.apitools_client.regionBackendServices,
'Get',
client.messages.ComputeRegionBackendServicesGetRequest(
backendService=backend_service_ref.Name(),
region=backend_service_ref.region,
project=backend_service_ref.project))
return (client.apitools_client.backendServices,
'Get',
client.messages.ComputeBackendServicesGetRequest(
backendService=backend_service_ref.Name(),
project=backend_service_ref.project))
def _GetSetRequest(self, client, backend_service_ref, replacement):
if backend_service_ref.Collection() == 'compute.regionBackendServices':
return (client.apitools_client.regionBackendServices,
'Update',
client.messages.ComputeRegionBackendServicesUpdateRequest(
backendService=backend_service_ref.Name(),
backendServiceResource=replacement,
region=backend_service_ref.region,
project=backend_service_ref.project))
return (client.apitools_client.backendServices,
'Update',
client.messages.ComputeBackendServicesUpdateRequest(
backendService=backend_service_ref.Name(),
backendServiceResource=replacement,
project=backend_service_ref.project))
def _GetGroupRef(self, args, resources, client):
if args.instance_group:
return flags.MULTISCOPE_INSTANCE_GROUP_ARG.ResolveAsResource(
args,
resources,
scope_lister=compute_flags.GetDefaultScopeLister(client))
if args.network_endpoint_group:
return flags.GetNetworkEndpointGroupArg().ResolveAsResource(
args,
resources,
scope_lister=compute_flags.GetDefaultScopeLister(client))
def _Modify(self, client, resources, backend_service_ref, args, existing):
replacement = encoding.CopyProtoMessage(existing)
group_ref = self._GetGroupRef(args, resources, client)
backend_to_update = None
for backend in replacement.backends:
# At most one backend will match
if group_ref.RelativeName() == resources.ParseURL(
backend.group).RelativeName():
backend_to_update = backend
break
if not backend_to_update:
scope_type = None
scope_name = None
if hasattr(group_ref, 'zone'):
scope_type = 'zone'
scope_name = group_ref.zone
if hasattr(group_ref, 'region'):
scope_type = 'region'
scope_name = group_ref.region
raise exceptions.ArgumentError(
'No backend with name [{0}] in {1} [{2}] is part of the backend '
'service [{3}].'.format(group_ref.Name(), scope_type, scope_name,
backend_service_ref.Name()))
if args.description:
backend_to_update.description = args.description
elif args.description is not None:
backend_to_update.description = None
self._ModifyBalancingModeArgs(
client, args, backend_to_update, self.ReleaseTrack()
)
if backend_to_update is not None and args.failover is not None:
backend_to_update.failover = args.failover
if backend_to_update is not None and args.preference is not None:
backend_to_update.preference = (
client.messages.Backend.PreferenceValueValuesEnum(args.preference)
)
if (
(
self.ReleaseTrack() == base.ReleaseTrack.ALPHA
or self.ReleaseTrack() == base.ReleaseTrack.BETA
)
and backend_to_update is not None
and args.traffic_duration is not None
):
backend_to_update.trafficDuration = (
client.messages.Backend.TrafficDurationValueValuesEnum(
args.traffic_duration
)
)
if args.custom_metrics:
backend_to_update.customMetrics = args.custom_metrics
if args.custom_metrics_file:
backend_to_update.customMetrics = args.custom_metrics_file
if args.clear_custom_metrics:
backend_to_update.customMetrics = []
return replacement
def _ModifyBalancingModeArgs(
self, client, args, backend_to_update, release_track=None
):
"""Update balancing mode fields in backend_to_update according to args.
Args:
client: The compute client.
args: The arguments given to the update-backend command.
backend_to_update: The backend message to modify.
release_track: The release track of the command.
"""
_ModifyBalancingModeArgs(
client.messages, args, backend_to_update, release_track
)
def _ValidateArgs(self, args):
"""Validatest that at least one field to update is specified.
Args:
args: The arguments given to the update-backend command.
"""
if not any([
args.description is not None,
args.balancing_mode,
args.max_utilization is not None,
args.max_rate is not None,
args.max_rate_per_instance is not None,
args.max_rate_per_endpoint is not None,
args.max_connections is not None,
args.max_connections_per_instance is not None,
args.max_connections_per_endpoint is not None,
args.capacity_scaler is not None,
args.failover is not None,
args.preference is not None,
]):
raise exceptions.UpdatePropertyError(
'At least one property must be modified.')
def Run(self, args):
"""Issues requests necessary to update backend of the Backend Service."""
self._ValidateArgs(args)
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
backend_service_ref = (
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.ResolveAsResource(
args,
holder.resources,
scope_lister=compute_flags.GetDefaultScopeLister(client)))
get_request = self._GetGetRequest(client, backend_service_ref)
backend_service = client.MakeRequests([get_request])[0]
modified_backend_service = self._Modify(
client, holder.resources, backend_service_ref, args, backend_service)
return client.MakeRequests([
self._GetSetRequest(client, backend_service_ref,
modified_backend_service)
])
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class UpdateBackendBeta(UpdateBackend):
"""Update an existing backend in a backend service.
*{command}* updates a backend that is part of a backend
service. This is useful for changing the way a backend
behaves. Example changes that can be made include changing the
load balancing policy and draining a backend by setting
its capacity scaler to zero.
Backends are instance groups or network endpoint groups. One
of the `--network-endpoint-group` or `--instance-group` flags is required to
identify the backend that you are modifying. You cannot change
the instance group or network endpoint group associated with a backend, but
you can remove a backend and add a new one with `backend-services
remove-backend` and `backend-services add-backend`.
The `gcloud compute backend-services edit` command can also
update a backend if the use of a text editor is desired.
For more information about the available settings, see
https://cloud.google.com/load-balancing/docs/backend-service.
"""
@classmethod
def Args(cls, parser):
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.AddArgument(parser)
flags.AddInstanceGroupAndNetworkEndpointGroupArgs(parser, 'update in')
backend_flags.AddDescription(parser)
backend_flags.AddBalancingMode(parser, release_track=cls.ReleaseTrack())
backend_flags.AddCapacityLimits(parser, release_track=cls.ReleaseTrack())
backend_flags.AddCapacityScalar(parser)
backend_flags.AddFailover(parser, default=None)
backend_flags.AddPreference(parser)
backend_flags.AddCustomMetrics(parser, add_clear_argument=True)
backend_flags.AddTrafficDuration(parser)
def _ValidateArgs(self, args):
"""Overrides."""
if not any([
args.description is not None,
args.balancing_mode,
args.max_utilization is not None,
args.max_rate is not None,
args.max_rate_per_instance is not None,
args.max_rate_per_endpoint is not None,
args.max_connections is not None,
args.max_connections_per_instance is not None,
args.max_connections_per_endpoint is not None,
args.max_in_flight_requests is not None,
args.max_in_flight_requests_per_instance is not None,
args.max_in_flight_requests_per_endpoint is not None,
args.traffic_duration is not None,
args.capacity_scaler is not None,
args.failover is not None,
args.preference is not None,
]):
raise exceptions.UpdatePropertyError(
'At least one property must be modified.')
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class UpdateBackendAlpha(UpdateBackendBeta):
"""Update an existing backend in a backend service.
*{command}* updates a backend that is part of a backend
service. This is useful for changing the way a backend
behaves. Example changes that can be made include changing the
load balancing policy and draining a backend by setting
its capacity scaler to zero.
Backends are instance groups or network endpoint groups. One
of the `--network-endpoint-group` or `--instance-group` flags is required to
identify the backend that you are modifying. You cannot change
the instance group or network endpoint group associated with a backend, but
you can remove a backend and add a new one with `backend-services
remove-backend` and `backend-services add-backend`.
The `gcloud compute backend-services edit` command can also
update a backend if the use of a text editor is desired.
For more information about the available settings, see
https://cloud.google.com/load-balancing/docs/backend-service.
"""
@classmethod
def Args(cls, parser):
flags.GLOBAL_REGIONAL_BACKEND_SERVICE_ARG.AddArgument(parser)
flags.AddInstanceGroupAndNetworkEndpointGroupArgs(parser, 'update in')
backend_flags.AddDescription(parser)
backend_flags.AddBalancingMode(parser, release_track=cls.ReleaseTrack())
backend_flags.AddCapacityLimits(parser, release_track=cls.ReleaseTrack())
backend_flags.AddCapacityScalar(parser)
backend_flags.AddFailover(parser, default=None)
backend_flags.AddPreference(parser)
backend_flags.AddTrafficDuration(parser)
backend_flags.AddCustomMetrics(parser, add_clear_argument=True)
def _ValidateArgs(self, args):
"""Overrides."""
if not any([
args.description is not None,
args.balancing_mode,
args.max_utilization is not None,
args.max_rate is not None,
args.max_rate_per_instance is not None,
args.max_rate_per_endpoint is not None,
args.max_connections is not None,
args.max_connections_per_instance is not None,
args.max_connections_per_endpoint is not None,
args.max_in_flight_requests is not None,
args.max_in_flight_requests_per_instance is not None,
args.max_in_flight_requests_per_endpoint is not None,
args.traffic_duration is not None,
args.capacity_scaler is not None,
args.failover is not None,
args.preference is not None,
args.custom_metrics is not None,
args.custom_metrics_file is not None,
args.clear_custom_metrics is not None,
]):
raise exceptions.UpdatePropertyError(
'At least one property must be modified.')
def _ClearMutualExclusiveBackendCapacityThresholds(backend, release_track=None):
"""Initialize the backend's mutually exclusive capacity thresholds."""
backend.maxRate = None
backend.maxRatePerInstance = None
backend.maxConnections = None
backend.maxConnectionsPerInstance = None
backend.maxRatePerEndpoint = None
backend.maxConnectionsPerEndpoint = None
if (
release_track == base.ReleaseTrack.ALPHA
or release_track == base.ReleaseTrack.BETA
):
backend.maxInFlightRequests = None
backend.maxInFlightRequestsPerInstance = None
backend.maxInFlightRequestsPerEndpoint = None
def _ModifyBalancingModeArgs(
messages, args, backend_to_update, release_track=None
):
"""Update balancing mode fields in backend_to_update according to args.
Args:
messages: API messages class, determined by the release track.
args: The arguments given to the update-backend command.
backend_to_update: The backend message to modify.
release_track: The release track of the command.
"""
backend_services_utils.ValidateBalancingModeArgs(
messages, args, backend_to_update.balancingMode, release_track)
if args.balancing_mode:
backend_to_update.balancingMode = (
messages.Backend.BalancingModeValueValuesEnum(
args.balancing_mode))
# If the balancing mode is being changed to RATE (CONNECTION), we must
# clear the max utilization, max inflight requests and max connections
# (rate) fields. If the balancing mode is being chagned to IN_FLIGHT,
# we must clear the max rate and max connections fields otherwise the
# server will reject the request.
if (backend_to_update.balancingMode ==
messages.Backend.BalancingModeValueValuesEnum.RATE):
backend_to_update.maxUtilization = None
backend_to_update.maxConnections = None
backend_to_update.maxConnectionsPerInstance = None
backend_to_update.maxConnectionsPerEndpoint = None
if (
release_track == base.ReleaseTrack.ALPHA
or release_track == base.ReleaseTrack.BETA
):
backend_to_update.maxInFlightRequests = None
backend_to_update.maxInFlightRequestsPerInstance = None
backend_to_update.maxInFlightRequestsPerEndpoint = None
elif (backend_to_update.balancingMode ==
messages.Backend.BalancingModeValueValuesEnum.CONNECTION):
backend_to_update.maxUtilization = None
backend_to_update.maxRate = None
backend_to_update.maxRatePerInstance = None
backend_to_update.maxRatePerEndpoint = None
if (
release_track == base.ReleaseTrack.ALPHA
or release_track == base.ReleaseTrack.BETA
):
backend_to_update.maxInFlightRequests = None
backend_to_update.maxInFlightRequestsPerInstance = None
backend_to_update.maxInFlightRequestsPerEndpoint = None
elif (
(
release_track == base.ReleaseTrack.ALPHA
or release_track == base.ReleaseTrack.BETA
)
and backend_to_update.balancingMode
== messages.Backend.BalancingModeValueValuesEnum.IN_FLIGHT
):
backend_to_update.maxRate = None
backend_to_update.maxRatePerInstance = None
backend_to_update.maxRatePerEndpoint = None
backend_to_update.maxConnections = None
backend_to_update.maxConnectionsPerInstance = None
backend_to_update.maxConnectionsPerEndpoint = None
# Now, we set the parameters that control load balancing.
# ValidateBalancingModeArgs takes care that the control parameters
# are compatible with the balancing mode.
if args.max_utilization is not None:
backend_to_update.maxUtilization = args.max_utilization
# max_rate, max_rate_per_instance, max_connections and
# max_connections_per_instance, max_in_flight_requests
# and max_in_flight_requests_per_instance are mutually exclusive arguments.
if args.max_rate is not None:
_ClearMutualExclusiveBackendCapacityThresholds(backend_to_update)
backend_to_update.maxRate = args.max_rate
elif args.max_rate_per_instance is not None:
_ClearMutualExclusiveBackendCapacityThresholds(backend_to_update)
backend_to_update.maxRatePerInstance = args.max_rate_per_instance
elif args.max_connections is not None:
_ClearMutualExclusiveBackendCapacityThresholds(backend_to_update)
backend_to_update.maxConnections = args.max_connections
elif args.max_connections_per_instance is not None:
_ClearMutualExclusiveBackendCapacityThresholds(backend_to_update)
backend_to_update.maxConnectionsPerInstance = (
args.max_connections_per_instance)
elif args.max_rate_per_endpoint is not None:
_ClearMutualExclusiveBackendCapacityThresholds(backend_to_update)
backend_to_update.maxRatePerEndpoint = args.max_rate_per_endpoint
elif args.max_connections_per_endpoint is not None:
_ClearMutualExclusiveBackendCapacityThresholds(backend_to_update)
backend_to_update.maxConnectionsPerEndpoint = (
args.max_connections_per_endpoint)
elif (
release_track == base.ReleaseTrack.ALPHA
or release_track == base.ReleaseTrack.BETA
):
if args.max_in_flight_requests is not None:
_ClearMutualExclusiveBackendCapacityThresholds(
backend_to_update, release_track
)
backend_to_update.maxInFlightRequests = args.max_in_flight_requests
elif args.max_in_flight_requests_per_instance is not None:
_ClearMutualExclusiveBackendCapacityThresholds(
backend_to_update, release_track
)
backend_to_update.maxInFlightRequestsPerInstance = (
args.max_in_flight_requests_per_instance
)
elif args.max_in_flight_requests_per_endpoint is not None:
_ClearMutualExclusiveBackendCapacityThresholds(
backend_to_update, release_track
)
backend_to_update.maxInFlightRequestsPerEndpoint = (
args.max_in_flight_requests_per_endpoint
)
if args.capacity_scaler is not None:
backend_to_update.capacityScaler = args.capacity_scaler