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,45 @@
# -*- 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 URL maps."""
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 URLMaps(base.Group):
"""List, create, and delete URL maps."""
URLMaps.category = base.NETWORKING_CATEGORY
URLMaps.detailed_help = {
'DESCRIPTION': """
List, create, and delete URL maps for external Application Load Balancer,
internal Application Load Balancer, and Traffic Director.
For more information about URL maps, see the
[URL maps documentation](https://cloud.google.com/load-balancing/docs/url-map-concepts).
See also: [URL maps API](https://cloud.google.com/compute/docs/reference/rest/v1/urlMaps)
and
[regional URL maps API](https://cloud.google.com/compute/docs/reference/rest/v1/regionUrlMaps).
""",
}

View File

@@ -0,0 +1,179 @@
# -*- 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 host rule to a URL map."""
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 arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import scope as compute_scope
from googlecloudsdk.command_lib.compute.url_maps import flags
from googlecloudsdk.command_lib.compute.url_maps import url_maps_utils
def _DetailedHelp():
# pylint:disable=line-too-long
return {
'brief':
'Add a rule to a URL map to map hosts to a path matcher.',
'DESCRIPTION': """
*{command}* is used to add a mapping of hosts to a path
matcher in a URL map. The mapping will match the host
component of HTTP requests to path matchers which in turn map
the request to a backend service. Before adding a host rule,
at least one path matcher must exist in the URL map to take
care of the path component of the requests.
`gcloud compute url-maps add-path-matcher` or
`gcloud compute url-maps edit` can be used to add path matchers.
""",
'EXAMPLES': """
To create a host rule mapping the ```*-foo.example.com``` and
```example.com``` hosts to the ```www``` path matcher, run:
$ {command} MY-URL-MAP --hosts='*-foo.example.com,example.com' --path-matcher-name=www
"""
}
# pylint:enable=line-too-long
def _Args(parser):
"""Add command line flags to the parser."""
parser.add_argument(
'--description',
help='An optional, textual description for the host rule.')
parser.add_argument(
'--hosts',
type=arg_parsers.ArgList(min_length=1),
metavar='HOST',
required=True,
help="""\
The set of hosts to match requests against. Each host must be
a fully qualified domain name (FQDN) with the exception that
the host can begin with a ``*'' or ``*-''. ``*'' acts as a
glob and will match any string of atoms to the left where an
atom is separated by dots (``.'') or dashes (``-'').
""")
parser.add_argument(
'--path-matcher-name',
required=True,
help="""\
The name of the path matcher to use if a request matches this
host rule. The path matcher must already exist in the URL map
(see `gcloud compute url-maps add-path-matcher`).
""")
def _GetGetRequest(client, url_map_ref):
"""Returns the request for the existing URL map resource."""
return (client.apitools_client.urlMaps, 'Get',
client.messages.ComputeUrlMapsGetRequest(
urlMap=url_map_ref.Name(), project=url_map_ref.project))
def _GetSetRequest(client, url_map_ref, replacement):
return (client.apitools_client.urlMaps, 'Update',
client.messages.ComputeUrlMapsUpdateRequest(
urlMap=url_map_ref.Name(),
urlMapResource=replacement,
project=url_map_ref.project))
def _Modify(client, args, existing):
"""Returns a modified URL map message."""
replacement = encoding.CopyProtoMessage(existing)
new_host_rule = client.messages.HostRule(
description=args.description,
hosts=sorted(args.hosts),
pathMatcher=args.path_matcher_name)
replacement.hostRules.append(new_host_rule)
return replacement
def _GetRegionalGetRequest(client, url_map_ref):
"""Returns the request to get an existing regional URL map resource."""
return (client.apitools_client.regionUrlMaps, 'Get',
client.messages.ComputeRegionUrlMapsGetRequest(
urlMap=url_map_ref.Name(),
project=url_map_ref.project,
region=url_map_ref.region))
def _GetRegionalSetRequest(client, url_map_ref, replacement):
"""Returns the request to update an existing regional URL map resource."""
return (client.apitools_client.regionUrlMaps, 'Update',
client.messages.ComputeRegionUrlMapsUpdateRequest(
urlMap=url_map_ref.Name(),
urlMapResource=replacement,
project=url_map_ref.project,
region=url_map_ref.region))
def _Run(
args,
holder,
url_map_arg,
):
"""Issues requests necessary to add host rule to the Url Map."""
client = holder.client
url_map_ref = url_map_arg.ResolveAsResource(
args, holder.resources, default_scope=compute_scope.ScopeEnum.GLOBAL)
if url_maps_utils.IsRegionalUrlMapRef(url_map_ref):
get_request = _GetRegionalGetRequest(client, url_map_ref)
else:
get_request = _GetGetRequest(client, url_map_ref)
old_url_map = client.MakeRequests([get_request])[0]
modified_url_map = _Modify(client, args, old_url_map)
if url_maps_utils.IsRegionalUrlMapRef(url_map_ref):
set_request = _GetRegionalSetRequest(client, url_map_ref, modified_url_map)
else:
set_request = _GetSetRequest(client, url_map_ref, modified_url_map)
return client.MakeRequests([set_request])
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA,
base.ReleaseTrack.GA)
class AddHostRule(base.UpdateCommand):
"""Add a rule to a URL map to map hosts to a path matcher."""
detailed_help = _DetailedHelp()
URL_MAP_ARG = None
@classmethod
def Args(cls, parser):
cls.URL_MAP_ARG = flags.UrlMapArgument()
cls.URL_MAP_ARG.AddArgument(parser)
_Args(parser)
def Run(self, args):
"""Issues requests necessary to add host rule to the Url Map."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
return _Run(args, holder, self.URL_MAP_ARG)

View File

@@ -0,0 +1,506 @@
# -*- 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 path matcher to a URL map."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import collections
from apitools.base.py import encoding
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import arg_parsers
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_buckets import (
flags as backend_bucket_flags,
)
from googlecloudsdk.command_lib.compute.backend_services import (
flags as backend_service_flags,
)
from googlecloudsdk.command_lib.compute.url_maps import flags
from googlecloudsdk.command_lib.compute.url_maps import url_maps_utils
from googlecloudsdk.core import properties
import six
def _DetailedHelp():
# pylint:disable=line-too-long
return {
'brief': 'Add a path matcher to a URL map.',
'DESCRIPTION': """
*{command}* is used to add a path matcher to a URL map. A path
matcher maps HTTP request paths to backend services or backend
buckets. Each path matcher must be referenced by at least one
host rule. This command can create a new host rule through the
`--new-hosts` flag or it can reconfigure an existing host rule
to point to the newly added path matcher using `--existing-host`.
In the latter case, if a path matcher is orphaned as a result
of the operation, this command will fail unless
`--delete-orphaned-path-matcher` is provided. Path matcher
constraints can be found
[here](https://cloud.google.com/load-balancing/docs/url-map-concepts#pm-constraints).
""",
'EXAMPLES': """
To create a rule for mapping the path ```/search/*``` to the
hypothetical ```search-service```, ```/static/*``` to the
```static-bucket``` backend bucket and ```/images/*``` to the
```images-service``` under the hosts ```example.com``` and
```*.example.com```, run:
$ {command} MY-URL-MAP --path-matcher-name=MY-MATCHER --default-service=MY-DEFAULT-SERVICE --backend-service-path-rules='/search/*=search_service,/images/*=images-service' --backend-bucket-path-rules='/static/*=static-bucket' --new-hosts=example.com '*.example.com'
Note that a default service or default backend bucket must be
provided to handle paths for which there is no mapping.
""",
}
# pylint:enable=line-too-long
def _Args(parser):
"""Common arguments to add-path-matcher commands for each release track."""
parser.add_argument(
'--description',
help='An optional, textual description for the path matcher.',
)
parser.add_argument(
'--path-matcher-name',
required=True,
help='The name to assign to the path matcher.',
)
parser.add_argument(
'--path-rules',
type=arg_parsers.ArgDict(min_length=1),
default={},
metavar='PATH=SERVICE',
help='Rules for mapping request paths to services.',
)
host_rule = parser.add_mutually_exclusive_group()
host_rule.add_argument(
'--new-hosts',
type=arg_parsers.ArgList(min_length=1),
metavar='NEW_HOST',
help=(
'If specified, a new host rule with the given hosts is created '
'and the path matcher is tied to the new host rule.'
),
)
host_rule.add_argument(
'--existing-host',
help="""\
An existing host rule to tie the new path matcher to. Although
host rules can contain more than one host, only a single host
is needed to uniquely identify the host rule.
""",
)
parser.add_argument(
'--delete-orphaned-path-matcher',
action='store_true',
default=False,
help=(
'If provided and a path matcher is orphaned as a result of this '
'command, the command removes the orphaned path matcher instead '
'of failing.'
),
)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
'--default-service',
help=(
'A backend service that will be used for requests that the path '
'matcher cannot match. Exactly one of --default-service or '
'--default-backend-bucket is required.'
),
)
group.add_argument(
'--default-backend-bucket',
help=(
'A backend bucket that will be used for requests that the path '
'matcher cannot match. Exactly one of --default-service or '
'--default-backend-bucket is required.'
),
)
parser.add_argument(
'--backend-service-path-rules',
type=arg_parsers.ArgDict(min_length=1),
default={},
metavar='PATH=SERVICE',
help='Rules for mapping request paths to services.',
)
parser.add_argument(
'--backend-bucket-path-rules',
type=arg_parsers.ArgDict(min_length=1),
default={},
metavar='PATH=BUCKET',
help='Rules for mapping request paths to backend buckets.',
)
def _GetGetRequest(client, url_map_ref):
"""Returns the request for the existing URL map resource."""
return (
client.apitools_client.urlMaps,
'Get',
client.messages.ComputeUrlMapsGetRequest(
urlMap=url_map_ref.Name(), project=url_map_ref.project
),
)
def _GetSetRequest(client, url_map_ref, replacement):
return (
client.apitools_client.urlMaps,
'Update',
client.messages.ComputeUrlMapsUpdateRequest(
urlMap=url_map_ref.Name(),
urlMapResource=replacement,
project=url_map_ref.project,
),
)
def _ModifyBase(client, args, existing):
"""Modifications to the URL map that are shared between release tracks.
Args:
client: The compute client.
args: the argparse arguments that this command was invoked with.
existing: the existing URL map message.
Returns:
A modified URL map message.
"""
replacement = encoding.CopyProtoMessage(existing)
if not args.new_hosts and not args.existing_host:
new_hosts = ['*']
else:
new_hosts = args.new_hosts
# If --new-hosts is given, we check to make sure none of those
# hosts already exist and once the check succeeds, we create the
# new host rule.
if new_hosts:
new_hosts = set(new_hosts)
for host_rule in existing.hostRules:
for host in host_rule.hosts:
if host in new_hosts:
raise compute_exceptions.ArgumentError(
'Cannot create a new host rule with host [{0}] because the '
'host is already part of a host rule that references the path '
'matcher [{1}].'.format(host, host_rule.pathMatcher)
)
replacement.hostRules.append(
client.messages.HostRule(
hosts=sorted(new_hosts), pathMatcher=args.path_matcher_name
)
)
# If --existing-host is given, we check to make sure that the
# corresponding host rule will not render a patch matcher
# orphan. If the check succeeds, we change the path matcher of the
# host rule. If the check fails, we remove the path matcher if
# --delete-orphaned-path-matcher is given otherwise we fail.
else:
target_host_rule = None
for host_rule in existing.hostRules:
for host in host_rule.hosts:
if host == args.existing_host:
target_host_rule = host_rule
break
if target_host_rule:
break
if not target_host_rule:
raise compute_exceptions.ArgumentError(
'No host rule with host [{0}] exists. Check your spelling or '
'use [--new-hosts] to create a new host rule.'.format(
args.existing_host
)
)
path_matcher_orphaned = True
for host_rule in replacement.hostRules:
if host_rule == target_host_rule:
host_rule.pathMatcher = args.path_matcher_name
continue
if host_rule.pathMatcher == target_host_rule.pathMatcher:
path_matcher_orphaned = False
break
if path_matcher_orphaned:
# A path matcher will be orphaned, so now we determine whether
# we should delete the path matcher or report an error.
if args.delete_orphaned_path_matcher:
replacement.pathMatchers = [
path_matcher
for path_matcher in existing.pathMatchers
if path_matcher.name != target_host_rule.pathMatcher
]
else:
raise compute_exceptions.ArgumentError(
'This operation will orphan the path matcher [{0}]. To '
'delete the orphan path matcher, rerun this command with '
'[--delete-orphaned-path-matcher] or use [gcloud compute '
'url-maps edit] to modify the URL map by hand.'.format(
host_rule.pathMatcher
)
)
return replacement
def _Modify(
client,
resources,
args,
url_map,
url_map_ref,
backend_service_arg,
backend_bucket_arg,
supports_regional_backend_bucket=False,
):
"""Returns a modified URL map message."""
replacement = _ModifyBase(client, args, url_map)
# Creates PathRule objects from --path-rules, --backend-service-path-rules,
# and --backend-bucket-path-rules.
service_map = collections.defaultdict(set)
bucket_map = collections.defaultdict(set)
for path, service in six.iteritems(args.path_rules):
service_map[service].add(path)
for path, service in six.iteritems(args.backend_service_path_rules):
service_map[service].add(path)
for path, bucket in six.iteritems(args.backend_bucket_path_rules):
bucket_map[bucket].add(path)
path_rules = []
for service, paths in sorted(six.iteritems(service_map)):
path_rules.append(
client.messages.PathRule(
paths=sorted(paths),
service=resources.Parse(
service,
params=_GetBackendParamsForUrlMap(url_map, url_map_ref),
collection=_GetBackendServiceCollectionForUrlMap(url_map),
).SelfLink(),
)
)
for bucket, paths in sorted(six.iteritems(bucket_map)):
if supports_regional_backend_bucket:
path_rules.append(
client.messages.PathRule(
paths=sorted(paths),
service=resources.Parse(
bucket,
params=_GetBackendParamsForUrlMap(url_map, url_map_ref),
collection=_GetBackendBucketCollectionForUrlMap(url_map),
).SelfLink(),
)
)
else:
path_rules.append(
client.messages.PathRule(
paths=sorted(paths),
service=resources.Parse(
bucket,
params={'project': properties.VALUES.core.project.GetOrFail},
collection='compute.backendBuckets',
).SelfLink(),
)
)
if args.default_service:
default_backend_uri = url_maps_utils.ResolveUrlMapDefaultService(
args, backend_service_arg, url_map_ref, resources
).SelfLink()
else:
if supports_regional_backend_bucket:
default_backend_uri = url_maps_utils.ResolveUrlMapDefaultBackendBucket(
args, backend_bucket_arg, url_map_ref, resources
).SelfLink()
else:
default_backend_uri = backend_bucket_arg.ResolveAsResource(
args, resources
).SelfLink()
new_path_matcher = client.messages.PathMatcher(
defaultService=default_backend_uri,
description=args.description,
name=args.path_matcher_name,
pathRules=path_rules,
)
replacement.pathMatchers.append(new_path_matcher)
return replacement
def _GetRegionalGetRequest(client, url_map_ref):
"""Returns the request to get an existing regional URL map resource."""
return (
client.apitools_client.regionUrlMaps,
'Get',
client.messages.ComputeRegionUrlMapsGetRequest(
urlMap=url_map_ref.Name(),
project=url_map_ref.project,
region=url_map_ref.region,
),
)
def _GetRegionalSetRequest(client, url_map_ref, replacement):
"""Returns the request to update an existing regional URL map resource."""
return (
client.apitools_client.regionUrlMaps,
'Update',
client.messages.ComputeRegionUrlMapsUpdateRequest(
urlMap=url_map_ref.Name(),
urlMapResource=replacement,
project=url_map_ref.project,
region=url_map_ref.region,
),
)
def _GetBackendParamsForUrlMap(url_map, url_map_ref):
params = {'project': properties.VALUES.core.project.GetOrFail}
if hasattr(url_map, 'region') and url_map.region:
params['region'] = url_map_ref.region
return params
def _GetBackendServiceCollectionForUrlMap(url_map):
if hasattr(url_map, 'region') and url_map.region:
return 'compute.regionBackendServices'
else:
return 'compute.backendServices'
def _GetBackendBucketCollectionForUrlMap(url_map):
if hasattr(url_map, 'region') and url_map.region:
return 'compute.regionBackendBuckets'
else:
return 'compute.backendBuckets'
def _Run(
args,
holder,
url_map_arg,
backend_servie_arg,
backend_bucket_arg,
supports_regional_backend_bucket=False,
):
"""Issues requests necessary to add path matcher to the Url Map."""
client = holder.client
url_map_ref = url_map_arg.ResolveAsResource(
args, holder.resources, default_scope=compute_scope.ScopeEnum.GLOBAL
)
if url_maps_utils.IsRegionalUrlMapRef(url_map_ref):
get_request = _GetRegionalGetRequest(client, url_map_ref)
else:
get_request = _GetGetRequest(client, url_map_ref)
url_map = client.MakeRequests([get_request])[0]
modified_url_map = _Modify(
client,
holder.resources,
args,
url_map,
url_map_ref,
backend_servie_arg,
backend_bucket_arg,
supports_regional_backend_bucket,
)
if url_maps_utils.IsRegionalUrlMapRef(url_map_ref):
set_request = _GetRegionalSetRequest(client, url_map_ref, modified_url_map)
else:
set_request = _GetSetRequest(client, url_map_ref, modified_url_map)
return client.MakeRequests([set_request])
@base.ReleaseTracks(base.ReleaseTrack.GA)
@base.UniverseCompatible
class AddPathMatcher(base.UpdateCommand):
"""Add a path matcher to a URL map."""
detailed_help = _DetailedHelp()
BACKEND_SERVICE_ARG = None
BACKEND_BUCKET_ARG = None
URL_MAP_ARG = None
_supports_regional_backend_bucket = False
@classmethod
def Args(cls, parser):
if cls._supports_regional_backend_bucket:
cls.BACKEND_BUCKET_ARG = (
backend_bucket_flags.RegionSupportingBackendBucketArgumentForUrlMap(
required=False
)
)
else:
cls.BACKEND_BUCKET_ARG = (
backend_bucket_flags.BackendBucketArgumentForUrlMap(required=False)
)
cls.BACKEND_SERVICE_ARG = (
backend_service_flags.BackendServiceArgumentForUrlMap()
)
cls.URL_MAP_ARG = flags.UrlMapArgument()
cls.URL_MAP_ARG.AddArgument(parser)
_Args(parser)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
return _Run(
args,
holder,
self.URL_MAP_ARG,
self.BACKEND_SERVICE_ARG,
self.BACKEND_BUCKET_ARG,
self._supports_regional_backend_bucket,
)
@base.ReleaseTracks(base.ReleaseTrack.BETA)
@base.UniverseCompatible
class AddPathMatcherBeta(AddPathMatcher):
"""Add a path matcher to a URL map."""
_supports_regional_backend_bucket = True
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
@base.UniverseCompatible
class AddPathMatcherAlpha(AddPathMatcher):
"""Add a path matcher to a URL map."""
_supports_regional_backend_bucket = True

View File

@@ -0,0 +1,33 @@
# -*- 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 group for managing instance configurations."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class Config(base.Group):
"""Manage URL map configurations."""
Config.detailed_help = {
'DESCRIPTION': """
Manage URL map configurations.
""",
}

View File

@@ -0,0 +1,51 @@
# -*- 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.
"""Export Url maps command."""
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 import scope as compute_scope
from googlecloudsdk.command_lib.compute.url_maps import flags
from googlecloudsdk.command_lib.util.declarative import python_command_util as declarative_python_util
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class Export(base.Command):
"""Export the configuration for a URL map."""
detailed_help = declarative_python_util.BuildHelpText(singular='URL map')
URL_MAP_ARG = None
@classmethod
def Args(cls, parser):
cls.URL_MAP_ARG = flags.UrlMapArgument(required=False)
declarative_python_util.RegisterArgs(
parser, cls.URL_MAP_ARG.AddArgument, operation_type='export')
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
resource_ref = str(
self.URL_MAP_ARG.ResolveAsResource(
args,
holder.resources,
default_scope=compute_scope.ScopeEnum.GLOBAL,
scope_lister=compute_flags.GetDefaultScopeLister(holder.client)))
return declarative_python_util.RunExport(
args=args, collection='compute.urlMaps', resource_ref=resource_ref)

View File

@@ -0,0 +1,219 @@
# -*- 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 URL maps."""
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 import scope as compute_scope
from googlecloudsdk.command_lib.compute.backend_buckets import flags as backend_bucket_flags
from googlecloudsdk.command_lib.compute.backend_services import flags as backend_service_flags
from googlecloudsdk.command_lib.compute.url_maps import flags
from googlecloudsdk.command_lib.compute.url_maps import url_maps_utils
def _DetailedHelp():
return {
'brief': 'Create a URL map.',
'DESCRIPTION': """
*{command}* is used to create URL maps which map HTTP and
HTTPS request URLs to backend services and backend buckets.
Mappings are done using a longest-match strategy.
There are two components to a mapping: a host rule and a path
matcher. A host rule maps one or more hosts to a path
matcher. A path matcher maps request paths to backend
services or backend buckets. For example, a host rule can map
the hosts ``*.google.com'' and ``google.com'' to a path
matcher called ``www''. The ``www'' path matcher in turn can
map the path ``/search/*'' to the search backend service, the
path ``/static/*'' to the static backend bucket and everything
else to a default backend service or default backend bucket.
Host rules and patch matchers can be added to the URL map
after the map is created by using `gcloud compute url-maps edit`
or by using `gcloud compute url-maps add-path-matcher`
and `gcloud compute url-maps add-host-rule`.
""",
'EXAMPLES': """
To create a global URL map with a default service, run:
$ {command} URL_MAP_NAME --default-service=BACKEND_SERVICE_NAME
To create a regional URL map with a default service, run:
$ {command} URL_MAP_NAME --default-service=BACKEND_SERVICE_NAME --region=REGION_NAME
To create a global URL map with a default backend bucket, run:
$ {command} URL_MAP_NAME --default-backend-bucket=BACKEND_BUCKET_NAME
""",
}
def _Args(parser):
"""Common arguments to create commands for each release track."""
parser.add_argument(
'--description', help='An optional, textual description for the URL map.'
)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
'--default-service',
help=(
'A backend service that will be used for requests for which this '
'URL map has no mappings. Exactly one of --default-service or '
'--default-backend-bucket is required.'
),
)
group.add_argument(
'--default-backend-bucket',
help=(
'A backend bucket that will be used for requests for which this '
'URL map has no mappings. Exactly one of --default-service or '
'--default-backend-bucket is required.'
),
)
parser.display_info.AddCacheUpdater(flags.UrlMapsCompleter)
def _MakeGlobalRequest(args, url_map_ref, default_backend_uri, client):
request = client.messages.ComputeUrlMapsInsertRequest(
project=url_map_ref.project,
urlMap=client.messages.UrlMap(
defaultService=default_backend_uri,
description=args.description,
name=url_map_ref.Name(),
),
)
return client.MakeRequests(
[(client.apitools_client.urlMaps, 'Insert', request)]
)
def _MakeRegionalRequest(args, url_map_ref, default_backend_uri, client):
request = client.messages.ComputeRegionUrlMapsInsertRequest(
project=url_map_ref.project,
urlMap=client.messages.UrlMap(
defaultService=default_backend_uri,
description=args.description,
name=url_map_ref.Name(),
),
region=url_map_ref.region,
)
return client.MakeRequests(
[(client.apitools_client.regionUrlMaps, 'Insert', request)]
)
def _Run(
args,
holder,
backend_bucket_arg,
backend_service_arg,
url_map_arg,
supports_regional_backend_bucket=False,
):
"""Issues requests necessary to create a Url Map."""
client = holder.client
url_map_ref = url_map_arg.ResolveAsResource(
args,
holder.resources,
default_scope=compute_scope.ScopeEnum.GLOBAL,
scope_lister=compute_flags.GetDefaultScopeLister(client),
)
if args.default_service:
default_backend_uri = url_maps_utils.ResolveUrlMapDefaultService(
args, backend_service_arg, url_map_ref, holder.resources
).SelfLink()
else:
if supports_regional_backend_bucket:
default_backend_uri = url_maps_utils.ResolveUrlMapDefaultBackendBucket(
args, backend_bucket_arg, url_map_ref, holder.resources
).SelfLink()
else:
default_backend_uri = backend_bucket_arg.ResolveAsResource(
args, holder.resources
).SelfLink()
if url_maps_utils.IsGlobalUrlMapRef(url_map_ref):
return _MakeGlobalRequest(args, url_map_ref, default_backend_uri, client)
elif url_maps_utils.IsRegionalUrlMapRef(url_map_ref):
return _MakeRegionalRequest(args, url_map_ref, default_backend_uri, client)
@base.ReleaseTracks(base.ReleaseTrack.GA)
@base.UniverseCompatible
class Create(base.CreateCommand):
"""Create a URL map."""
_supports_regional_backend_bucket = False
detailed_help = _DetailedHelp()
BACKEND_BUCKET_ARG = None
BACKEND_SERVICE_ARG = None
URL_MAP_ARG = None
@classmethod
def Args(cls, parser):
parser.display_info.AddFormat(flags.DEFAULT_LIST_FORMAT)
if cls._supports_regional_backend_bucket:
cls.BACKEND_BUCKET_ARG = (
backend_bucket_flags.RegionSupportingBackendBucketArgumentForUrlMap(
required=False
)
)
else:
cls.BACKEND_BUCKET_ARG = (
backend_bucket_flags.BackendBucketArgumentForUrlMap(required=False)
)
cls.BACKEND_SERVICE_ARG = (
backend_service_flags.BackendServiceArgumentForUrlMap(required=False)
)
cls.URL_MAP_ARG = flags.UrlMapArgument()
cls.URL_MAP_ARG.AddArgument(parser, operation_type='create')
_Args(parser)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
return _Run(
args,
holder,
self.BACKEND_BUCKET_ARG,
self.BACKEND_SERVICE_ARG,
self.URL_MAP_ARG,
self._supports_regional_backend_bucket,
)
@base.ReleaseTracks(base.ReleaseTrack.BETA)
@base.UniverseCompatible
class CreateBeta(Create):
"""Create a URL map."""
_supports_regional_backend_bucket = True
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
@base.UniverseCompatible
class CreateAlpha(Create):
"""Create a URL map."""
_supports_regional_backend_bucket = True

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 deleting URL maps."""
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.calliope import base
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute import scope as compute_scope
from googlecloudsdk.command_lib.compute.url_maps import flags
from googlecloudsdk.command_lib.compute.url_maps import url_maps_utils
def _DetailedHelp():
return {
'brief':
'Delete URL maps.',
'DESCRIPTION':
"""\
*{command}* deletes one or more URL maps.
""",
}
def _Run(args, holder, url_map_arg):
"""Issues requests necessary to delete URL maps."""
client = holder.client
url_map_refs = url_map_arg.ResolveAsResource(
args,
holder.resources,
default_scope=compute_scope.ScopeEnum.GLOBAL,
scope_lister=compute_flags.GetDefaultScopeLister(client))
utils.PromptForDeletion(url_map_refs)
requests = []
for url_map_ref in url_map_refs:
if url_maps_utils.IsRegionalUrlMapRef(url_map_ref):
requests.append((client.apitools_client.regionUrlMaps, 'Delete',
client.messages.ComputeRegionUrlMapsDeleteRequest(
**url_map_ref.AsDict())))
else:
requests.append(
(client.apitools_client.urlMaps, 'Delete',
client.messages.ComputeUrlMapsDeleteRequest(**url_map_ref.AsDict())))
return client.MakeRequests(requests)
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA,
base.ReleaseTrack.GA)
class Delete(base.DeleteCommand):
"""Delete URL maps."""
detailed_help = _DetailedHelp()
URL_MAP_ARG = None
@classmethod
def Args(cls, parser):
cls.URL_MAP_ARG = flags.UrlMapArgument(plural=True)
cls.URL_MAP_ARG.AddArgument(parser, operation_type='delete')
parser.display_info.AddCacheUpdater(flags.UrlMapsCompleter)
def Run(self, args):
"""Issues requests necessary to delete URL maps."""
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
return _Run(args, holder, self.URL_MAP_ARG)

View File

@@ -0,0 +1,77 @@
# -*- 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 url maps."""
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 import scope as compute_scope
from googlecloudsdk.command_lib.compute.url_maps import flags
from googlecloudsdk.command_lib.compute.url_maps import url_maps_utils
def _DetailedHelp():
return {
'brief':
'Describe a URL map.',
'DESCRIPTION':
"""\
*{command}* displays all data associated with a URL map in a
project.
""",
}
def _Run(args, holder, url_map_arg):
"""Issues requests necessary to describe URL maps."""
client = holder.client
url_map_ref = url_map_arg.ResolveAsResource(
args,
holder.resources,
default_scope=compute_scope.ScopeEnum.GLOBAL,
scope_lister=compute_flags.GetDefaultScopeLister(client))
if url_maps_utils.IsRegionalUrlMapRef(url_map_ref):
service = client.apitools_client.regionUrlMaps
request = client.messages.ComputeRegionUrlMapsGetRequest(
**url_map_ref.AsDict())
else:
service = client.apitools_client.urlMaps
request = client.messages.ComputeUrlMapsGetRequest(**url_map_ref.AsDict())
return client.MakeRequests([(service, 'Get', request)])[0]
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA,
base.ReleaseTrack.GA)
class Describe(base.DescribeCommand):
"""Describe a URL map."""
detailed_help = _DetailedHelp()
URL_MAP_ARG = None
@classmethod
def Args(cls, parser):
cls.URL_MAP_ARG = flags.UrlMapArgument()
cls.URL_MAP_ARG.AddArgument(parser, operation_type='describe')
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
return _Run(args, holder, self.URL_MAP_ARG)

View File

@@ -0,0 +1,464 @@
# -*- 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 URL maps."""
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
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.url_maps import flags
from googlecloudsdk.command_lib.compute.url_maps import url_maps_utils
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
def _DetailedHelp():
return {
'brief': 'Modify URL maps',
'DESCRIPTION': """\
*{command}* can be used to modify a URL map. The URL map
resource is fetched from the server and presented in a text
editor. After the file is saved and closed, this command will
update the resource. Only fields that can be modified are
displayed in the editor.
The editor used to modify the resource is chosen by inspecting
the ``EDITOR'' environment variable.
""",
}
def _ProcessEditedResource(
holder,
url_map_ref,
file_contents,
original_object,
original_record,
modifiable_record,
args,
enable_regional_backend_buckets=False,
):
"""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
)
reference_normalizer = property_selector.PropertySelector(
transformations=_GetReferenceNormalizers(
holder.resources, enable_regional_backend_buckets
)
)
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.UrlMap
)
# 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(
[_GetSetRequest(holder.client, url_map_ref, new_object)]
)
def _EditResource(
args,
client,
holder,
original_object,
url_map_ref,
track,
enable_regional_backend_buckets=False,
):
"""Allows user to edit the URL Map."""
original_record = encoding.MessageToDict(original_object)
# Selects only the fields that can be modified.
field_selector = property_selector.PropertySelector(
properties=[
'defaultService',
'description',
'hostRules',
'pathMatchers',
'tests',
'defaultCustomErrorResponsePolicy',
]
)
modifiable_record = field_selector.Apply(original_record)
buf = _BuildFileContents(
args, client, modifiable_record, original_record, track
)
file_contents = buf.getvalue()
while True:
try:
file_contents = edit.OnlineEdit(file_contents)
except edit.NoSaveException:
raise compute_exceptions.AbortedError('Edit aborted by user.')
try:
enable_regional_backend_buckets = track in ['alpha']
resource_list = _ProcessEditedResource(
holder,
url_map_ref,
file_contents,
original_object,
original_record,
modifiable_record,
args,
enable_regional_backend_buckets,
)
break
except (
ValueError,
yaml.YAMLParseError,
messages.ValidationError,
exceptions.ToolException,
) as e:
message = getattr(e, 'message', six.text_type(e))
if isinstance(e, 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 compute_exceptions.AbortedError('Edit aborted by user.')
return resource_list
def _BuildFileContents(args, client, modifiable_record, original_record, track):
"""Builds the initial editable file."""
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(_GetExampleResource(client, track)),
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
def _GetExampleResource(client, track):
"""Gets an example URL Map."""
backend_service_uri_prefix = (
'https://compute.googleapis.com/compute/%(track)s/projects/'
'my-project/global/backendServices/'
% {'track': track}
)
backend_bucket_uri_prefix = (
'https://compute.googleapis.com/compute/%(track)s/projects/'
'my-project/global/backendBuckets/'
% {'track': track}
)
return client.messages.UrlMap(
name='site-map',
defaultService=backend_service_uri_prefix + 'default-service',
defaultCustomErrorResponsePolicy=client.messages.CustomErrorResponsePolicy(
errorService=backend_service_uri_prefix + 'error-service',
errorResponseRules=[
client.messages.CustomErrorResponsePolicyCustomErrorResponseRule(
matchResponseCodes=['4xx'],
path='/errors/4xx/not-found.html',
overrideResponseCode=404,
),
],
),
hostRules=[
client.messages.HostRule(
hosts=['*.google.com', 'google.com'], pathMatcher='www'
),
client.messages.HostRule(
hosts=['*.youtube.com', 'youtube.com', '*-youtube.com'],
pathMatcher='youtube',
),
],
pathMatchers=[
client.messages.PathMatcher(
name='www',
defaultService=backend_service_uri_prefix + 'www-default',
pathRules=[
client.messages.PathRule(
paths=['/search', '/search/*'],
service=backend_service_uri_prefix + 'search',
),
client.messages.PathRule(
paths=['/search/ads', '/search/ads/*'],
service=backend_service_uri_prefix + 'ads',
),
client.messages.PathRule(
paths=['/images/*'],
service=backend_bucket_uri_prefix + 'images',
),
],
),
client.messages.PathMatcher(
name='youtube',
defaultService=backend_service_uri_prefix + 'youtube-default',
pathRules=[
client.messages.PathRule(
paths=['/search', '/search/*'],
service=backend_service_uri_prefix + 'youtube-search',
),
client.messages.PathRule(
paths=['/watch', '/view', '/preview'],
service=backend_service_uri_prefix + 'youtube-watch',
),
],
),
],
tests=[
client.messages.UrlMapTest(
host='www.google.com',
path='/search/ads/inline?q=flowers',
service=backend_service_uri_prefix + 'ads',
),
client.messages.UrlMapTest(
host='youtube.com',
path='/watch/this',
service=backend_service_uri_prefix + 'youtube-default',
),
client.messages.UrlMapTest(
host='youtube.com',
path='/images/logo.png',
service=backend_bucket_uri_prefix + 'images',
),
],
)
def _GetReferenceNormalizers(
resource_registry, enable_regional_backend_buckets=False
):
"""Gets normalizers that translate short names to URIs."""
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
if enable_regional_backend_buckets:
allowed_collections = [
'compute.backendServices',
'compute.backendBuckets',
'compute.regionBackendServices',
'compute.regionBackendBuckets',
]
else:
allowed_collections = [
'compute.backendServices',
'compute.backendBuckets',
'compute.regionBackendServices',
]
return [
(
'defaultService',
MakeReferenceNormalizer('defaultService', allowed_collections),
),
(
'pathMatchers[].defaultService',
MakeReferenceNormalizer('defaultService', allowed_collections),
),
(
'pathMatchers[].pathRules[].service',
MakeReferenceNormalizer('service', allowed_collections),
),
(
'CustomErrorResponsePolicy.errorService',
MakeReferenceNormalizer('errorService', allowed_collections),
),
(
'tests[].service',
MakeReferenceNormalizer('service', allowed_collections),
),
]
def _GetGetRequest(client, url_map_ref):
"""Gets the request for getting the URL Map."""
if url_maps_utils.IsRegionalUrlMapRef(url_map_ref):
return (
client.apitools_client.regionUrlMaps,
'Get',
client.messages.ComputeRegionUrlMapsGetRequest(
urlMap=url_map_ref.Name(),
project=url_map_ref.project,
region=url_map_ref.region,
),
)
return (
client.apitools_client.urlMaps,
'Get',
client.messages.ComputeUrlMapsGetRequest(**url_map_ref.AsDict()),
)
def _GetSetRequest(client, url_map_ref, replacement):
"""Gets the request for setting the URL Map."""
if url_maps_utils.IsRegionalUrlMapRef(url_map_ref):
return (
client.apitools_client.regionUrlMaps,
'Update',
client.messages.ComputeRegionUrlMapsUpdateRequest(
urlMap=url_map_ref.Name(),
urlMapResource=replacement,
project=url_map_ref.project,
region=url_map_ref.region,
),
)
return (
client.apitools_client.urlMaps,
'Update',
client.messages.ComputeUrlMapsUpdateRequest(
urlMapResource=replacement, **url_map_ref.AsDict()
),
)
def _Run(args, holder, track, url_map_arg):
"""Issues requests necessary to edit URL maps."""
client = holder.client
url_map_ref = url_map_arg.ResolveAsResource(
args, holder.resources, default_scope=compute_scope.ScopeEnum.GLOBAL
)
get_request = _GetGetRequest(client, url_map_ref)
objects = client.MakeRequests([get_request])
resource_list = _EditResource(
args, client, holder, objects[0], url_map_ref, track
)
for resource in resource_list:
yield resource
class InvalidResourceError(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
@base.ReleaseTracks(base.ReleaseTrack.GA)
@base.UniverseCompatible
class Edit(base.Command):
"""Modify URL maps."""
detailed_help = _DetailedHelp()
DEFAULT_FORMAT = 'yaml'
URL_MAP_ARG = None
TRACK = 'v1'
@classmethod
def Args(cls, parser):
cls.URL_MAP_ARG = flags.UrlMapArgument()
cls.URL_MAP_ARG.AddArgument(parser)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
return _Run(args, holder, self.TRACK, self.URL_MAP_ARG)
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class EditBeta(Edit):
TRACK = 'beta'
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class EditAlpha(EditBeta):
TRACK = 'alpha'

View File

@@ -0,0 +1,107 @@
# -*- 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 Url maps 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 import scope as compute_scope
from googlecloudsdk.command_lib.compute.url_maps import flags
from googlecloudsdk.command_lib.compute.url_maps import url_maps_utils
from googlecloudsdk.command_lib.export import util as export_util
from googlecloudsdk.core.util import files
def _DetailedHelp():
return {
'brief':
'Export a URL map.',
'DESCRIPTION':
"""\
Exports a URL map's configuration to a file.
This configuration can be imported at a later time.
""",
'EXAMPLES':
"""\
A URL map can be exported by running:
$ {command} NAME --destination=<path-to-file>
"""
}
def _GetApiVersion(release_track):
"""Returns the API version based on the release track."""
if release_track == base.ReleaseTrack.ALPHA:
return 'alpha'
elif release_track == base.ReleaseTrack.BETA:
return 'beta'
return 'v1'
def _GetSchemaPath(release_track, for_help=False):
"""Returns the resource schema path."""
return export_util.GetSchemaPath(
'compute', _GetApiVersion(release_track), 'UrlMap', for_help=for_help)
def _Run(args, holder, url_map_arg, release_track):
"""Issues requests necessary to export URL maps."""
client = holder.client
url_map_ref = url_map_arg.ResolveAsResource(
args,
holder.resources,
default_scope=compute_scope.ScopeEnum.GLOBAL,
scope_lister=compute_flags.GetDefaultScopeLister(client))
url_map = url_maps_utils.SendGetRequest(client, url_map_ref)
if args.destination:
with files.FileWriter(args.destination) as stream:
export_util.Export(
message=url_map,
stream=stream,
schema_path=_GetSchemaPath(release_track))
else:
export_util.Export(
message=url_map,
stream=sys.stdout,
schema_path=_GetSchemaPath(release_track))
@base.ReleaseTracks(base.ReleaseTrack.GA, base.ReleaseTrack.BETA,
base.ReleaseTrack.ALPHA)
class Export(base.Command):
"""Export a URL map."""
detailed_help = _DetailedHelp()
URL_MAP_ARG = None
@classmethod
def Args(cls, parser):
cls.URL_MAP_ARG = flags.UrlMapArgument()
cls.URL_MAP_ARG.AddArgument(parser, operation_type='export')
export_util.AddExportFlags(
parser, _GetSchemaPath(cls.ReleaseTrack(), for_help=True))
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
return _Run(args, holder, self.URL_MAP_ARG, self.ReleaseTrack())

View File

@@ -0,0 +1,430 @@
# -*- 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 URL maps 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 as compute_exceptions
from googlecloudsdk.command_lib.compute import flags as compute_flags
from googlecloudsdk.command_lib.compute import scope as compute_scope
from googlecloudsdk.command_lib.compute.url_maps import flags
from googlecloudsdk.command_lib.compute.url_maps import url_maps_utils
from googlecloudsdk.command_lib.export import util as export_util
from googlecloudsdk.core import log
from googlecloudsdk.core import yaml_validator
from googlecloudsdk.core.console import console_io
def _DetailedHelp():
return {
'brief':
'Import a URL map.',
'DESCRIPTION':
"""\
Imports a URL map's configuration from a file.
""",
'EXAMPLES':
"""\
A URL map can be imported by running:
$ {command} NAME --source=<path-to-file>
"""
}
def _GetApiVersion(release_track):
"""Returns the API version based on the release track."""
if release_track == base.ReleaseTrack.ALPHA:
return 'alpha'
elif release_track == base.ReleaseTrack.BETA:
return 'beta'
return 'v1'
def _GetSchemaPath(release_track, for_help=False):
"""Returns the resource schema path."""
return export_util.GetSchemaPath(
'compute', _GetApiVersion(release_track), 'UrlMap', for_help=for_help)
def _SendPatchRequest(client, resources, url_map_ref, replacement):
"""Sends a URL map patch request and waits for the operation to finish.
Args:
client: The API client.
resources: The resource parser.
url_map_ref: The URL map reference.
replacement: The URL map to patch with.
Returns:
The operation result.
"""
if url_map_ref.Collection() == 'compute.regionUrlMaps':
service = client.apitools_client.regionUrlMaps
operation = client.apitools_client.regionUrlMaps.Patch(
client.messages.ComputeRegionUrlMapsPatchRequest(
project=url_map_ref.project,
region=url_map_ref.region,
urlMap=url_map_ref.Name(),
urlMapResource=replacement))
else:
service = client.apitools_client.urlMaps
operation = client.apitools_client.urlMaps.Patch(
client.messages.ComputeUrlMapsPatchRequest(
project=url_map_ref.project,
urlMap=url_map_ref.Name(),
urlMapResource=replacement))
return url_maps_utils.WaitForOperation(resources, service, operation,
url_map_ref, 'Updating URL map')
def _SendInsertRequest(client, resources, url_map_ref, url_map):
"""Sends a URL map insert request and waits for the operation to finish.
Args:
client: The API client.
resources: The resource parser.
url_map_ref: The URL map reference.
url_map: The URL map to insert.
Returns:
The operation result.
"""
if url_map_ref.Collection() == 'compute.regionUrlMaps':
service = client.apitools_client.regionUrlMaps
operation = client.apitools_client.regionUrlMaps.Insert(
client.messages.ComputeRegionUrlMapsInsertRequest(
project=url_map_ref.project,
region=url_map_ref.region,
urlMap=url_map))
else:
service = client.apitools_client.urlMaps
operation = client.apitools_client.urlMaps.Insert(
client.messages.ComputeUrlMapsInsertRequest(
project=url_map_ref.project, urlMap=url_map))
return url_maps_utils.WaitForOperation(resources, service, operation,
url_map_ref, 'Creating URL map')
def _GetClearedFieldsForDuration(duration, field_prefix):
"""Gets a list of fields cleared by the user for Duration."""
cleared_fields = []
if hasattr(duration, 'seconds'):
cleared_fields.append(field_prefix + 'seconds')
if hasattr(duration, 'nanos'):
cleared_fields.append(field_prefix + 'nanos')
return cleared_fields
def _GetClearedFieldsForUrlRewrite(url_rewrite, field_prefix):
"""Gets a list of fields cleared by the user for UrlRewrite."""
cleared_fields = []
if not url_rewrite.pathPrefixRewrite:
cleared_fields.append(field_prefix + 'pathPrefixRewrite')
if not url_rewrite.hostRewrite:
cleared_fields.append(field_prefix + 'hostRewrite')
return cleared_fields
def _GetClearedFieldsForRetryPolicy(retry_policy, field_prefix):
"""Gets a list of fields cleared by the user for RetryPolicy."""
cleared_fields = []
if not retry_policy.retryConditions:
cleared_fields.append(field_prefix + 'retryConditions')
if hasattr(retry_policy, 'numRetries'):
cleared_fields.append(field_prefix + 'numRetries')
if not retry_policy.perTryTimeout:
cleared_fields.append(field_prefix + 'perTryTimeout')
else:
cleared_fields = cleared_fields + _GetClearedFieldsForDuration(
retry_policy.perTryTimeout, field_prefix + 'perTryTimeout.')
return cleared_fields
def _GetClearedFieldsForRequestMirrorPolicy(mirror_policy, field_prefix):
"""Gets a list of fields cleared by the user for RequestMirrorPolicy."""
cleared_fields = []
if not mirror_policy.backendService:
cleared_fields.append(field_prefix + 'backendService')
return cleared_fields
def _GetClearedFieldsForCorsPolicy(cors_policy, field_prefix):
"""Gets a list of fields cleared by the user for CorsPolicy."""
cleared_fields = []
if not cors_policy.allowOrigins:
cleared_fields.append(field_prefix + 'allowOrigins')
if not cors_policy.allowOriginRegexes:
cleared_fields.append(field_prefix + 'allowOriginRegexes')
if not cors_policy.allowMethods:
cleared_fields.append(field_prefix + 'allowMethods')
if not cors_policy.allowHeaders:
cleared_fields.append(field_prefix + 'allowHeaders')
if not cors_policy.exposeHeaders:
cleared_fields.append(field_prefix + 'exposeHeaders')
if not cors_policy.maxAge:
cleared_fields.append(field_prefix + 'maxAge')
if not cors_policy.allowCredentials:
cleared_fields.append(field_prefix + 'allowCredentials')
if not cors_policy.disabled:
cleared_fields.append(field_prefix + 'disabled')
return cleared_fields
def _GetClearedFieldsForFaultDelay(fault_delay, field_prefix):
"""Gets a list of fields cleared by the user for HttpFaultDelay."""
cleared_fields = []
if not fault_delay.fixedDelay:
cleared_fields.append(field_prefix + 'fixedDelay')
else:
cleared_fields = cleared_fields + _GetClearedFieldsForDuration(
fault_delay.fixedDelay, field_prefix + 'fixedDelay.')
if not fault_delay.percentage:
cleared_fields.append(field_prefix + 'percentage')
return cleared_fields
def _GetClearedFieldsForFaultAbort(fault_abort, field_prefix):
"""Gets a list of fields cleared by the user for HttpFaultAbort."""
cleared_fields = []
if not fault_abort.httpStatus:
cleared_fields.append(field_prefix + 'httpStatus')
if not fault_abort.percentage:
cleared_fields.append(field_prefix + 'percentage')
return cleared_fields
def _GetClearedFieldsForFaultInjectionPolicy(fault_injection_policy,
field_prefix):
"""Gets a list of fields cleared by the user for FaultInjectionPolicy."""
cleared_fields = []
if not fault_injection_policy.delay:
cleared_fields.append(field_prefix + 'delay')
else:
cleared_fields = cleared_fields + _GetClearedFieldsForFaultDelay(
fault_injection_policy.delay, field_prefix + 'delay.')
if not fault_injection_policy.abort:
cleared_fields.append(field_prefix + 'abort')
else:
cleared_fields = cleared_fields + _GetClearedFieldsForFaultAbort(
fault_injection_policy.abort, field_prefix + 'abort.')
return cleared_fields
def _GetClearedFieldsForRoutAction(route_action, field_prefix):
"""Gets a list of fields cleared by the user for HttpRouteAction."""
cleared_fields = []
if not route_action.weightedBackendServices:
cleared_fields.append(field_prefix + 'weightedBackendServices')
if not route_action.urlRewrite:
cleared_fields.append(field_prefix + 'urlRewrite')
else:
cleared_fields = cleared_fields + _GetClearedFieldsForUrlRewrite(
route_action.urlRewrite, field_prefix + 'urlRewrite.')
if not route_action.timeout:
cleared_fields.append(field_prefix + 'timeout')
else:
cleared_fields = cleared_fields + _GetClearedFieldsForDuration(
route_action.timeout, field_prefix + 'timeout.')
if not route_action.retryPolicy:
cleared_fields.append(field_prefix + 'retryPolicy')
else:
cleared_fields = cleared_fields + _GetClearedFieldsForRetryPolicy(
route_action.retryPolicy, field_prefix + 'retryPolicy.')
if not route_action.requestMirrorPolicy:
cleared_fields.append(field_prefix + 'requestMirrorPolicy')
else:
cleared_fields = cleared_fields + _GetClearedFieldsForRequestMirrorPolicy(
route_action.requestMirrorPolicy, field_prefix + 'requestMirrorPolicy.')
if not route_action.corsPolicy:
cleared_fields.append(field_prefix + 'corsPolicy')
else:
cleared_fields = cleared_fields + _GetClearedFieldsForCorsPolicy(
route_action.corsPolicy, field_prefix + 'corsPolicy.')
if not route_action.faultInjectionPolicy:
cleared_fields.append(field_prefix + 'faultInjectionPolicy')
else:
cleared_fields = cleared_fields + _GetClearedFieldsForFaultInjectionPolicy(
route_action.faultInjectionPolicy,
field_prefix + 'faultInjectionPolicy.')
return cleared_fields
def _GetClearedFieldsForCustomErrorResponsePolicy(
custom_error_response_policy, field_prefix
):
"""Gets a list of fields cleared by the user for CustomErrorResponsePolicy."""
cleared_fields = []
if not custom_error_response_policy.errorResponseRules:
cleared_fields.append(field_prefix + 'errorResponseRules')
if not custom_error_response_policy.errorService:
cleared_fields.append(field_prefix + 'errorService')
return cleared_fields
def _GetClearedFieldsForUrlRedirect(url_redirect, field_prefix):
"""Gets a list of fields cleared by the user for UrlRedirect."""
cleared_fields = []
if not url_redirect.hostRedirect:
cleared_fields.append(field_prefix + 'hostRedirect')
if not url_redirect.pathRedirect:
cleared_fields.append(field_prefix + 'pathRedirect')
if not url_redirect.prefixRedirect:
cleared_fields.append(field_prefix + 'prefixRedirect')
if not url_redirect.redirectResponseCode:
cleared_fields.append(field_prefix + 'redirectResponseCode')
if not url_redirect.httpsRedirect:
cleared_fields.append(field_prefix + 'httpsRedirect')
if not url_redirect.stripQuery:
cleared_fields.append(field_prefix + 'stripQuery')
return cleared_fields
def _GetClearedFieldsForHeaderAction(header_action, field_prefix):
"""Gets a list of fields cleared by the user for HeaderAction."""
cleared_fields = []
if not header_action.requestHeadersToRemove:
cleared_fields.append(field_prefix + 'requestHeadersToRemove')
if not header_action.requestHeadersToAdd:
cleared_fields.append(field_prefix + 'requestHeadersToAdd')
if not header_action.responseHeadersToRemove:
cleared_fields.append(field_prefix + 'responseHeadersToRemove')
if not header_action.responseHeadersToAdd:
cleared_fields.append(field_prefix + 'responseHeadersToAdd')
return cleared_fields
def _Run(args, holder, url_map_arg, release_track):
"""Issues requests necessary to import URL maps."""
client = holder.client
resources = holder.resources
url_map_ref = url_map_arg.ResolveAsResource(
args,
resources,
default_scope=compute_scope.ScopeEnum.GLOBAL,
scope_lister=compute_flags.GetDefaultScopeLister(client))
data = console_io.ReadFromFileOrStdin(args.source or '-', binary=False)
try:
url_map = export_util.Import(
message_type=client.messages.UrlMap,
stream=data,
schema_path=_GetSchemaPath(release_track))
except yaml_validator.ValidationError as e:
raise compute_exceptions.ValidationError(str(e))
if url_map.name != url_map_ref.Name():
# Replace warning and raise error after 10/01/2021
log.warning('The name of the Url Map must match the value of the ' +
'\'name\' attribute in the YAML file. Future versions of ' +
'gcloud will fail with an error.')
# Get existing URL map.
try:
url_map_old = url_maps_utils.SendGetRequest(client, url_map_ref)
except apitools_exceptions.HttpError as error:
if error.status_code != 404:
raise error
# Url Map does not exist, create a new one.
return _SendInsertRequest(client, resources, url_map_ref, url_map)
# No change, do not send requests to server.
if url_map_old == url_map:
return
console_io.PromptContinue(
message=('Url Map [{0}] will be overwritten.').format(url_map_ref.Name()),
cancel_on_no=True)
# Populate id and fingerprint fields when YAML files don't contain them.
if not url_map.id:
url_map.id = url_map_old.id
if url_map.fingerprint:
# Replace warning and raise error after 10/01/2021
log.warning('An up-to-date fingerprint must be provided to ' +
'update the Url Map. Future versions of gcloud will fail ' +
'with an error \'412 conditionNotMet\'')
url_map.fingerprint = url_map_old.fingerprint
# Unspecified fields are assumed to be cleared.
# TODO(b/182287403) Replace with proto reflection and update scenario tests.
cleared_fields = []
if not url_map.description:
cleared_fields.append('description')
if not url_map.hostRules:
cleared_fields.append('hostRules')
if not url_map.pathMatchers:
cleared_fields.append('pathMatchers')
if not url_map.tests:
cleared_fields.append('tests')
if not url_map.defaultService:
cleared_fields.append('defaultService')
if not url_map.defaultCustomErrorResponsePolicy:
cleared_fields.append('defaultCustomErrorResponsePolicy')
else:
cleared_fields = (
cleared_fields
+ _GetClearedFieldsForCustomErrorResponsePolicy(
url_map.defaultCustomErrorResponsePolicy,
'defaultCustomErrorResponsePolicy.',
)
)
if not url_map.defaultRouteAction:
cleared_fields.append('defaultRouteAction')
else:
cleared_fields = cleared_fields + _GetClearedFieldsForRoutAction(
url_map.defaultRouteAction, 'defaultRouteAction.')
if not url_map.defaultUrlRedirect:
cleared_fields.append('defaultUrlRedirect')
else:
cleared_fields = cleared_fields + _GetClearedFieldsForUrlRedirect(
url_map.defaultUrlRedirect, 'defaultUrlRedirect.')
if not url_map.headerAction:
cleared_fields.append('headerAction')
else:
cleared_fields = cleared_fields + _GetClearedFieldsForHeaderAction(
url_map.headerAction, 'headerAction.')
with client.apitools_client.IncludeFields(cleared_fields):
return _SendPatchRequest(client, resources, url_map_ref, url_map)
@base.ReleaseTracks(
base.ReleaseTrack.GA, base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA
)
@base.UniverseCompatible
class Import(base.UpdateCommand):
"""Import a URL map."""
detailed_help = _DetailedHelp()
URL_MAP_ARG = None
@classmethod
def Args(cls, parser):
cls.URL_MAP_ARG = flags.UrlMapArgument()
cls.URL_MAP_ARG.AddArgument(parser, operation_type='import')
export_util.AddImportFlags(
parser, _GetSchemaPath(cls.ReleaseTrack(), for_help=True))
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
return _Run(args, holder, self.URL_MAP_ARG, self.ReleaseTrack())

View File

@@ -0,0 +1,182 @@
# -*- coding: utf-8 -*- #
# Copyright 2015 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 cache invalidation."""
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 batch_helper
from googlecloudsdk.api_lib.compute import utils
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.url_maps import flags
from googlecloudsdk.command_lib.compute.url_maps import url_maps_utils
from googlecloudsdk.core import log
def _DetailedHelp():
return {
'brief':
'Invalidate specified objects for a URL map in Cloud CDN caches.',
'DESCRIPTION':
"""\
*{command}* requests that Cloud CDN stop using cached content for
resources at a particular URL path or set of URL paths.
*{command}* may succeed even if no content is cached for some or all
URLs with the given path.
""",
}
def _Args(parser):
"""Add invalidate-cdn-cache arguments to the parser."""
parser.add_argument(
'--path',
required=False,
help="""\
A path specifying which objects to invalidate. PATH must start with
``/'' and the only place a ``*'' is allowed is at the end following a
``/''. It will be matched against URL paths, which do not include
scheme, host, or any text after the first ``?'' or ``#'' (and those
characters are not allowed here). For example, for the URL
``https://example.com/whatever/x.html?a=b'', the path is
``/whatever/x.html''.
If PATH ends with ``*'', the preceding string is a prefix, and all URLs
whose paths begin with it will be invalidated. If PATH doesn't end with
``*'', then only URLs with exactly that path will be invalidated.
Examples:
- ``'', ``*'', anything that doesn't start with ``/'': error
- ``/'': just the root URL
- ``/*'': everything
- ``/x/y'': ``/x/y'' only (and not ``/x/y/'')
- ``/x/y/'': ``/x/y/'' only (and not ``/x/y'')
- ``/x/y/*'': ``/x/y/'' and everything under it
""")
parser.add_argument(
'--host',
required=False,
default=None,
help="""\
If set, this invalidation will apply only to requests to the
specified host.
""")
parser.add_argument(
'--tags',
required=False,
default=None,
help="""\
A single tag or a comma-delimited list of tags. When multiple tags are
specified, the invalidation applies them using boolean OR logic.
Example:
- ``--tags=abcd,user123''
""")
base.ASYNC_FLAG.AddToParser(parser)
def _CreateRequests(holder, args, url_map_arg):
"""Returns a list of requests necessary for cache invalidations."""
url_map_ref = url_map_arg.ResolveAsResource(
args, holder.resources, default_scope=compute_scope.ScopeEnum.GLOBAL)
if url_maps_utils.IsRegionalUrlMapRef(url_map_ref):
raise compute_exceptions.ArgumentError(
'Invalid flag [--region]:'
' Regional URL maps do not support Cloud CDN caching.'
)
cache_invalidation_rule = holder.client.messages.CacheInvalidationRule(
path=args.path)
if args.host is not None:
cache_invalidation_rule.host = args.host
if hasattr(args, 'tags') and args.tags is not None:
cache_invalidation_rule.cacheTags.extend(
[tag.strip() for tag in args.tags.split(',')])
messages = holder.client.messages
request = messages.ComputeUrlMapsInvalidateCacheRequest(
project=url_map_ref.project,
urlMap=url_map_ref.Name(),
cacheInvalidationRule=cache_invalidation_rule)
collection = holder.client.apitools_client.urlMaps
return [(collection, 'InvalidateCache', request)]
def _Run(args, holder, url_map_arg):
"""Issues requests necessary to invalidate a URL map cdn cache."""
client = holder.client
requests = _CreateRequests(holder, args, url_map_arg)
if args.async_:
resources, errors = batch_helper.MakeRequests(
requests=requests,
http=client.apitools_client.http,
batch_url=client.batch_url)
if not errors:
for invalidation_operation in resources:
log.status.write('Invalidation pending for [{0}]\n'.format(
invalidation_operation.targetLink))
log.status.write('Monitor its progress at [{0}]\n'.format(
invalidation_operation.selfLink))
else:
utils.RaiseToolException(errors)
else:
# We want to run through the generator that MakeRequests returns in order
# to actually make the requests.
resources = client.MakeRequests(requests)
return resources
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class InvalidateCdnCache(base.SilentCommand):
"""Invalidate specified objects for a URL map in Cloud CDN caches."""
detailed_help = _DetailedHelp()
URL_MAP_ARG = None
@classmethod
def Args(cls, parser):
cls.URL_MAP_ARG = flags.GlobalUrlMapArgument()
cls.URL_MAP_ARG.AddArgument(parser, cust_metavar='URLMAP')
_Args(parser)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
return _Run(args, holder, self.URL_MAP_ARG)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class InvalidateCdnCacheBeta(InvalidateCdnCache):
pass
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class InvalidateCdnCacheAlpha(InvalidateCdnCacheBeta):
pass

View File

@@ -0,0 +1,65 @@
# -*- 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 URL maps."""
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.url_maps import flags
def _DetailedHelp():
return base_classes.GetMultiScopeListerHelp(
'URL maps',
scopes=[
base_classes.ScopeType.global_scope,
base_classes.ScopeType.regional_scope
])
def _Run(args, holder):
"""Issues requests necessary to list URL maps."""
client = holder.client
request_data = lister.ParseMultiScopeFlags(args, holder.resources)
list_implementation = lister.MultiScopeLister(
client,
regional_service=client.apitools_client.regionUrlMaps,
global_service=client.apitools_client.urlMaps,
aggregation_service=client.apitools_client.urlMaps)
return lister.Invoke(request_data, list_implementation)
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.GA,
base.ReleaseTrack.ALPHA)
class List(base.ListCommand):
"""List URL maps."""
detailed_help = _DetailedHelp()
@classmethod
def Args(cls, parser):
parser.display_info.AddFormat(flags.DEFAULT_LIST_FORMAT)
lister.AddMultiScopeListerFlags(parser, regional=True, global_=True)
parser.display_info.AddCacheUpdater(flags.UrlMapsCompleter)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
return _Run(args, holder)

View File

@@ -0,0 +1,129 @@
# -*- coding: utf-8 -*- #
# Copyright 2016 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 Cloud CDN cache invalidations."""
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 constants
from googlecloudsdk.calliope import arg_parsers
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.url_maps import flags
from googlecloudsdk.command_lib.compute.url_maps import url_maps_utils
from googlecloudsdk.core import properties
from googlecloudsdk.core.resource import resource_projector
def _DetailedHelp():
return {
'brief':
'List Cloud CDN cache invalidations for a URL map.',
'DESCRIPTION':
"""\
List Cloud CDN cache invalidations for a URL map. A cache invalidation
instructs Cloud CDN to stop using cached content. You can list
invalidations to check which have completed.
""",
}
def _GetUrlMapGetRequest(url_map_ref, client):
return (client.apitools_client.urlMaps, 'Get',
client.messages.ComputeUrlMapsGetRequest(
project=properties.VALUES.core.project.GetOrFail(),
urlMap=url_map_ref.Name()))
def _Run(args, holder, url_map_arg):
"""Issues requests necessary to list URL map cdn cache invalidations."""
client = holder.client
url_map_ref = url_map_arg.ResolveAsResource(
args, holder.resources, default_scope=compute_scope.ScopeEnum.GLOBAL)
if url_maps_utils.IsRegionalUrlMapRef(url_map_ref):
raise compute_exceptions.ArgumentError(
'Invalid flag [--region]:'
' Regional URL maps do not support Cloud CDN caching.'
)
get_request = _GetUrlMapGetRequest(url_map_ref, client)
objects = client.MakeRequests([get_request])
urlmap_id = objects[0].id
filter_expr = ('(operationType eq invalidateCache) (targetId eq '
'{urlmap_id})').format(urlmap_id=urlmap_id)
max_results = args.limit or constants.MAX_RESULTS_PER_PAGE
project = properties.VALUES.core.project.GetOrFail()
requests = [
(client.apitools_client.globalOperations, 'AggregatedList',
client.apitools_client.globalOperations.GetRequestType('AggregatedList')(
filter=filter_expr,
maxResults=max_results,
orderBy='creationTimestamp desc',
project=project))
]
return resource_projector.MakeSerializable(
client.MakeRequests(requests=requests))
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class ListCacheInvalidations(base.ListCommand):
"""List Cloud CDN cache invalidations for a URL map."""
detailed_help = _DetailedHelp()
@staticmethod
def _Flags(parser):
parser.add_argument(
'--limit',
type=arg_parsers.BoundedInt(1, 1000, unlimited=False),
help=(
'The maximum number of invalidations to list. This has an upper'
' limit of 1000. For more results, use Cloud Logging.'
),
)
@classmethod
def Args(cls, parser):
cls.URL_MAP_ARG = flags.GlobalUrlMapArgument()
cls.URL_MAP_ARG.AddArgument(parser, operation_type='describe')
parser.display_info.AddFormat("""\
table(
description,
operation_http_status():label=HTTP_STATUS,
status,
insertTime:label=TIMESTAMP
)""")
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
return _Run(args, holder, self.URL_MAP_ARG)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class ListCacheInvalidationsBeta(ListCacheInvalidations):
pass
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class ListCacheInvalidationsAlpha(ListCacheInvalidationsBeta):
pass

View File

@@ -0,0 +1,161 @@
# -*- 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 host rule from a URL map."""
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 as compute_exceptions
from googlecloudsdk.command_lib.compute import scope as compute_scope
from googlecloudsdk.command_lib.compute.url_maps import flags
from googlecloudsdk.command_lib.compute.url_maps import url_maps_utils
def _DetailedHelp():
return {
'brief':
'Remove a host rule from a URL map.',
'DESCRIPTION': """
*{command}* is used to remove a host rule from a URL map. When
a host rule is removed, its path matcher is only removed if
it is not referenced by any other host rules and
`--delete-orphaned-path-matcher` is provided.
""",
'EXAMPLES': """
To remove a host rule that contains the host `example.com`
from the URL map named `MY-URL-MAP`, you can use this
command:
$ {command} MY-URL-MAP --host=example.com
"""
}
def _GetGetRequest(client, url_map_ref):
"""Returns the request for the existing URL map resource."""
if url_maps_utils.IsGlobalUrlMapRef(url_map_ref):
return (client.apitools_client.urlMaps, 'Get',
client.messages.ComputeUrlMapsGetRequest(
urlMap=url_map_ref.Name(), project=url_map_ref.project))
else:
return (client.apitools_client.regionUrlMaps, 'Get',
client.messages.ComputeRegionUrlMapsGetRequest(
urlMap=url_map_ref.Name(),
project=url_map_ref.project,
region=url_map_ref.region))
def _GetSetRequest(client, url_map_ref, replacement):
if url_maps_utils.IsGlobalUrlMapRef(url_map_ref):
return (client.apitools_client.urlMaps, 'Update',
client.messages.ComputeUrlMapsUpdateRequest(
urlMap=url_map_ref.Name(),
urlMapResource=replacement,
project=url_map_ref.project))
else:
return (client.apitools_client.regionUrlMaps, 'Update',
client.messages.ComputeRegionUrlMapsUpdateRequest(
urlMap=url_map_ref.Name(),
urlMapResource=replacement,
project=url_map_ref.project,
region=url_map_ref.region))
def _Modify(args, existing):
"""Returns a modified URL map message."""
replacement = encoding.CopyProtoMessage(existing)
path_matcher_to_remove = None
new_host_rules = []
for host_rule in existing.hostRules:
if args.host in host_rule.hosts:
path_matcher_to_remove = host_rule.pathMatcher
else:
new_host_rules.append(host_rule)
if not path_matcher_to_remove:
raise compute_exceptions.ArgumentError(
'No host rule contains the host [{0}].'.format(args.host))
replacement.hostRules = new_host_rules
path_matcher_is_used_by_other_rules = False
for host_rule in replacement.hostRules:
if host_rule.pathMatcher == path_matcher_to_remove:
path_matcher_is_used_by_other_rules = True
break
if not path_matcher_is_used_by_other_rules:
if args.delete_orphaned_path_matcher:
replacement.pathMatchers = [
path_matcher for path_matcher in existing.pathMatchers
if path_matcher.name != path_matcher_to_remove
]
else:
raise compute_exceptions.ArgumentError(
'This operation will orphan the path matcher [{0}]. To '
'delete the orphan path matcher, rerun this command with '
'[--delete-orphaned-path-matcher] or use [gcloud compute '
'url-maps edit] to modify the URL map by hand.'.format(
host_rule.pathMatcher))
return replacement
def _Run(args, holder, url_map_arg):
"""Issues requests necessary to remove host rule on URL maps."""
client = holder.client
url_map_ref = url_map_arg.ResolveAsResource(
args, holder.resources, default_scope=compute_scope.ScopeEnum.GLOBAL)
get_request = _GetGetRequest(client, url_map_ref)
objects = client.MakeRequests([get_request])
new_object = _Modify(args, objects[0])
return client.MakeRequests([_GetSetRequest(client, url_map_ref, new_object)])
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA,
base.ReleaseTrack.GA)
class RemoveHostRule(base.UpdateCommand):
"""Remove a host rule from a URL map."""
detailed_help = _DetailedHelp()
URL_MAP_ARG = None
@classmethod
def Args(cls, parser):
cls.URL_MAP_ARG = flags.UrlMapArgument()
cls.URL_MAP_ARG.AddArgument(parser)
parser.add_argument(
'--host',
required=True,
help='One of the hosts in the host rule to remove.')
parser.add_argument(
'--delete-orphaned-path-matcher',
action='store_true',
default=False,
help=('If provided and a path matcher is orphaned as a result of this '
'command, the command removes the orphaned path matcher instead '
'of failing.'))
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
return _Run(args, holder, self.URL_MAP_ARG)

View File

@@ -0,0 +1,156 @@
# -*- 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 path matcher from a URL map."""
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 as compute_exceptions
from googlecloudsdk.command_lib.compute import scope as compute_scope
from googlecloudsdk.command_lib.compute.url_maps import flags
from googlecloudsdk.command_lib.compute.url_maps import url_maps_utils
def _DetailedHelp():
return {
'brief':
'Remove a path matcher from a URL map.',
'DESCRIPTION': """
*{command}* is used to remove a path matcher from a URL
map. When a path matcher is removed, all host rules that
refer to the path matcher are also removed.
""",
'EXAMPLES': """
To remove the path matcher named ``MY-MATCHER'' from the URL map named
``MY-URL-MAP'', you can use this command:
$ {command} MY-URL-MAP --path-matcher-name=MY-MATCHER
""",
}
def _GetGetRequest(client, url_map_ref):
"""Returns the request for the existing URL map resource."""
return (client.apitools_client.urlMaps, 'Get',
client.messages.ComputeUrlMapsGetRequest(
urlMap=url_map_ref.Name(), project=url_map_ref.project))
def _GetSetRequest(client, url_map_ref, replacement):
return (client.apitools_client.urlMaps, 'Update',
client.messages.ComputeUrlMapsUpdateRequest(
urlMap=url_map_ref.Name(),
urlMapResource=replacement,
project=url_map_ref.project))
def _Modify(args, existing):
"""Returns a modified URL map message."""
replacement = encoding.CopyProtoMessage(existing)
# Removes the path matcher.
new_path_matchers = []
path_matcher_found = False
for path_matcher in existing.pathMatchers:
if path_matcher.name == args.path_matcher_name:
path_matcher_found = True
else:
new_path_matchers.append(path_matcher)
if not path_matcher_found:
raise compute_exceptions.ArgumentError(
'No path matcher with the name [{0}] was found.'.format(
args.path_matcher_name))
replacement.pathMatchers = new_path_matchers
# Removes all host rules that refer to the path matcher.
new_host_rules = []
for host_rule in existing.hostRules:
if host_rule.pathMatcher != args.path_matcher_name:
new_host_rules.append(host_rule)
replacement.hostRules = new_host_rules
return replacement
def _GetRegionalGetRequest(client, url_map_ref):
"""Returns the request to get an existing regional URL map resource."""
return (client.apitools_client.regionUrlMaps, 'Get',
client.messages.ComputeRegionUrlMapsGetRequest(
urlMap=url_map_ref.Name(),
project=url_map_ref.project,
region=url_map_ref.region))
def _GetRegionalSetRequest(client, url_map_ref, replacement):
"""Returns the request to update an existing regional URL map resource."""
return (client.apitools_client.regionUrlMaps, 'Update',
client.messages.ComputeRegionUrlMapsUpdateRequest(
urlMap=url_map_ref.Name(),
urlMapResource=replacement,
project=url_map_ref.project,
region=url_map_ref.region))
def _Run(args, holder, url_map_arg):
"""Issues requests necessary to remove path matcher on URL maps."""
client = holder.client
url_map_ref = url_map_arg.ResolveAsResource(
args, holder.resources, default_scope=compute_scope.ScopeEnum.GLOBAL)
if url_maps_utils.IsRegionalUrlMapRef(url_map_ref):
get_request = _GetRegionalGetRequest(client, url_map_ref)
else:
get_request = _GetGetRequest(client, url_map_ref)
url_map = client.MakeRequests([get_request])[0]
modified_url_map = _Modify(args, url_map)
if url_maps_utils.IsRegionalUrlMapRef(url_map_ref):
set_request = _GetRegionalSetRequest(client, url_map_ref, modified_url_map)
else:
set_request = _GetSetRequest(client, url_map_ref, modified_url_map)
return client.MakeRequests([set_request])
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA,
base.ReleaseTrack.GA)
class RemovePathMatcher(base.UpdateCommand):
"""Remove a path matcher from a URL map."""
detailed_help = _DetailedHelp()
URL_MAP_ARG = None
@classmethod
def Args(cls, parser):
cls.URL_MAP_ARG = flags.UrlMapArgument()
cls.URL_MAP_ARG.AddArgument(parser)
parser.add_argument(
'--path-matcher-name',
required=True,
help='The name of the path matcher to remove.')
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
return _Run(args, holder, self.URL_MAP_ARG)

View File

@@ -0,0 +1,250 @@
# -*- 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 changing the default service of a URL map."""
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 scope as compute_scope
from googlecloudsdk.command_lib.compute.backend_buckets import flags as backend_bucket_flags
from googlecloudsdk.command_lib.compute.backend_services import flags as backend_service_flags
from googlecloudsdk.command_lib.compute.url_maps import flags
from googlecloudsdk.command_lib.compute.url_maps import url_maps_utils
from googlecloudsdk.core import log
def _DetailedHelp():
return {
'brief': 'Change the default service or default bucket of a URL map.',
'DESCRIPTION': """\
*{command}* is used to change the default service or default
bucket of a URL map. The default service or default bucket is
used for any requests for which there is no mapping in the
URL map.
""",
}
def _Args(parser):
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
'--default-service',
help=(
'A backend service that will be used for requests for which this '
'URL map has no mappings.'
),
)
group.add_argument(
'--default-backend-bucket',
help=(
'A backend bucket that will be used for requests for which this '
'URL map has no mappings.'
),
)
def _GetGetRequest(client, url_map_ref):
"""Returns the request for the existing URL map resource."""
return (
client.apitools_client.urlMaps,
'Get',
client.messages.ComputeUrlMapsGetRequest(
urlMap=url_map_ref.Name(), project=url_map_ref.project
),
)
def _GetSetRequest(client, url_map_ref, replacement):
return (
client.apitools_client.urlMaps,
'Update',
client.messages.ComputeUrlMapsUpdateRequest(
urlMap=url_map_ref.Name(),
urlMapResource=replacement,
project=url_map_ref.project,
),
)
def _Modify(
resources,
args,
url_map,
url_map_ref,
backend_bucket_arg,
backend_service_arg,
supports_regional_backend_bucket=False,
):
"""Returns a modified URL map message."""
replacement = encoding.CopyProtoMessage(url_map)
if args.default_service:
default_backend_uri = url_maps_utils.ResolveUrlMapDefaultService(
args, backend_service_arg, url_map_ref, resources
).SelfLink()
else:
if supports_regional_backend_bucket:
default_backend_uri = url_maps_utils.ResolveUrlMapDefaultBackendBucket(
args, backend_bucket_arg, url_map_ref, resources
).SelfLink()
else:
default_backend_uri = backend_bucket_arg.ResolveAsResource(
args, resources
).SelfLink()
replacement.defaultService = default_backend_uri
return replacement
def _GetRegionalGetRequest(client, url_map_ref):
"""Returns the request to get an existing regional URL map resource."""
return (
client.apitools_client.regionUrlMaps,
'Get',
client.messages.ComputeRegionUrlMapsGetRequest(
urlMap=url_map_ref.Name(),
project=url_map_ref.project,
region=url_map_ref.region,
),
)
def _GetRegionalSetRequest(client, url_map_ref, replacement):
"""Returns the request to update an existing regional URL map resource."""
return (
client.apitools_client.regionUrlMaps,
'Update',
client.messages.ComputeRegionUrlMapsUpdateRequest(
urlMap=url_map_ref.Name(),
urlMapResource=replacement,
project=url_map_ref.project,
region=url_map_ref.region,
),
)
def _Run(
args,
holder,
backend_bucket_arg,
backend_service_arg,
url_map_arg,
supports_regional_backend_bucket=False,
):
"""Issues requests necessary to set the default service of URL maps."""
client = holder.client
url_map_ref = url_map_arg.ResolveAsResource(
args, holder.resources, default_scope=compute_scope.ScopeEnum.GLOBAL
)
if url_maps_utils.IsRegionalUrlMapRef(url_map_ref):
get_request = _GetRegionalGetRequest(client, url_map_ref)
else:
get_request = _GetGetRequest(client, url_map_ref)
old_url_map = client.MakeRequests([get_request])
modified_url_map = _Modify(
holder.resources,
args,
old_url_map[0],
url_map_ref,
backend_bucket_arg,
backend_service_arg,
supports_regional_backend_bucket,
)
# 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 old_url_map[0] == modified_url_map:
log.status.Print(
'No change requested; skipping update for [{0}].'.format(
old_url_map[0].name
)
)
return old_url_map
if url_maps_utils.IsRegionalUrlMapRef(url_map_ref):
set_request = _GetRegionalSetRequest(client, url_map_ref, modified_url_map)
else:
set_request = _GetSetRequest(client, url_map_ref, modified_url_map)
return client.MakeRequests([set_request])
@base.ReleaseTracks(base.ReleaseTrack.GA)
@base.UniverseCompatible
class SetDefaultService(base.UpdateCommand):
"""Change the default service or default bucket of a URL map."""
_supports_regional_backend_bucket = False
detailed_help = _DetailedHelp()
BACKEND_BUCKET_ARG = None
BACKEND_SERVICE_ARG = None
URL_MAP_ARG = None
@classmethod
def Args(cls, parser):
if cls._supports_regional_backend_bucket:
cls.BACKEND_BUCKET_ARG = (
backend_bucket_flags.RegionSupportingBackendBucketArgumentForUrlMap(
required=False
)
)
else:
cls.BACKEND_BUCKET_ARG = (
backend_bucket_flags.BackendBucketArgumentForUrlMap(required=False)
)
cls.BACKEND_SERVICE_ARG = (
backend_service_flags.BackendServiceArgumentForUrlMap(required=False)
)
cls.URL_MAP_ARG = flags.UrlMapArgument()
cls.URL_MAP_ARG.AddArgument(parser)
_Args(parser)
def Run(self, args):
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
return _Run(
args,
holder,
self.BACKEND_BUCKET_ARG,
self.BACKEND_SERVICE_ARG,
self.URL_MAP_ARG,
self._supports_regional_backend_bucket,
)
@base.ReleaseTracks(base.ReleaseTrack.BETA)
@base.UniverseCompatible
class SetDefaultServiceBeta(SetDefaultService):
"""Change the default service or default bucket of a URL map."""
_supports_regional_backend_bucket = True
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
@base.UniverseCompatible
class SetDefaultServiceAlpha(SetDefaultService):
"""Change the default service or default bucket of a URL map."""
_supports_regional_backend_bucket = True

View File

@@ -0,0 +1,201 @@
# -*- coding: utf-8 -*- #
# Copyright 2020 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.
"""Validate URL maps command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.compute import exceptions as compute_exceptions
from googlecloudsdk.command_lib.export import util as export_util
from googlecloudsdk.core import properties
from googlecloudsdk.core import yaml_validator
from googlecloudsdk.core.console import console_io
def _DetailedHelp():
return {
'brief':
'Validate a URL map.',
'DESCRIPTION':
"""\
Runs static validation for the UrlMap.
In particular, the tests of the provided UrlMap will be run.
Calling this method does NOT create or update the UrlMap.
""",
'EXAMPLES':
"""\
A URL map can be validated by running:
$ {command} --source=<path-to-file>
"""
}
def _GetApiVersion(release_track):
"""Returns the API version based on the release track."""
if release_track == base.ReleaseTrack.ALPHA:
return 'alpha'
elif release_track == base.ReleaseTrack.BETA:
return 'beta'
return 'v1'
def _GetSchemaPath(release_track, for_help=False):
"""Returns the resource schema path."""
return export_util.GetSchemaPath(
'compute', _GetApiVersion(release_track), 'UrlMap', for_help=for_help)
def _AddSourceFlag(parser, schema_path=None):
help_text = """Path to a YAML file containing configuration export data.
The YAML file must not contain any output-only fields. Alternatively,
you may omit this flag to read from standard input. For a schema
describing the export/import format, see: {}.
""".format(schema_path)
parser.add_argument(
'--source', help=textwrap.dedent(help_text), required=False)
def _AddGlobalFlag(parser):
parser.add_argument(
'--global', action='store_true', help='If set, the URL map is global.')
def _AddRegionFlag(parser):
parser.add_argument('--region', help='Region of the URL map to validate.')
def _AddScopeFlags(parser):
scope = parser.add_mutually_exclusive_group()
_AddGlobalFlag(scope)
_AddRegionFlag(scope)
def _AddLoadBalancingSchemeFlag(parser):
"""Add --load-balancing-scheme flag."""
help_text = """\
Specifies the load balancer type this validation request is for. Use
`EXTERNAL_MANAGED` for global external Application Load Balancer. Use
`EXTERNAL` for classic Application Load Balancer.
Other load balancer types are not supported. For more information, refer to
[Choosing a load balancer](https://cloud.google.com/load-balancing/docs/choosing-load-balancer/).
If unspecified, the load balancing scheme will be inferred from the backend
service resources this URL map references. If that can not be inferred (for
example, this URL map only references backend buckets, or this URL map is
for rewrites and redirects only and doesn't reference any backends),
`EXTERNAL` will be used as the default type.
If specified, the scheme must not conflict with the load balancing
scheme of the backend service resources this URL map references.
"""
parser.add_argument(
'--load-balancing-scheme',
choices=['EXTERNAL', 'EXTERNAL_MANAGED'],
help=help_text,
required=False)
def _MakeGlobalRequest(client, project, url_map, load_balancing_scheme):
"""Construct (not send) and return the request for global UrlMap."""
if load_balancing_scheme is None:
return client.messages.ComputeUrlMapsValidateRequest(
project=project,
urlMap=url_map.name,
urlMapsValidateRequest=client.messages.UrlMapsValidateRequest(
resource=url_map))
else:
scheme_enum = client.messages.UrlMapsValidateRequest.LoadBalancingSchemesValueListEntryValuesEnum(
load_balancing_scheme)
return client.messages.ComputeUrlMapsValidateRequest(
project=project,
urlMap=url_map.name,
urlMapsValidateRequest=client.messages.UrlMapsValidateRequest(
resource=url_map, loadBalancingSchemes=[scheme_enum]))
def _MakeRegionalRequest(client, project, region, url_map):
return client.messages.ComputeRegionUrlMapsValidateRequest(
project=project,
region=region,
urlMap=url_map.name,
regionUrlMapsValidateRequest=client.messages.RegionUrlMapsValidateRequest(
resource=url_map))
def _SendGlobalRequest(client, project, url_map, load_balancing_scheme):
return client.apitools_client.urlMaps.Validate(
_MakeGlobalRequest(client, project, url_map, load_balancing_scheme))
def _SendRegionalRequest(client, project, region, url_map):
return client.apitools_client.regionUrlMaps.Validate(
_MakeRegionalRequest(client, project, region, url_map))
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA,
base.ReleaseTrack.GA)
class Validate(base.Command):
"""Validates URL map configs from your project."""
detailed_help = _DetailedHelp()
@classmethod
def Args(cls, parser):
_AddSourceFlag(parser, _GetSchemaPath(cls.ReleaseTrack(), for_help=True))
_AddScopeFlags(parser)
_AddLoadBalancingSchemeFlag(parser)
def Run(self, args):
"""Runs the command.
Args:
args: argparse.Namespace, An object that contains the values for the
arguments specified in the .Args() method.
Returns:
A response object returned by rpc call Validate.
"""
project = properties.VALUES.core.project.GetOrFail()
holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
client = holder.client
if args.region is not None and args.load_balancing_scheme:
raise exceptions.InvalidArgumentException(
'--load-balancing-scheme',
'Cannot specify load balancing scheme for regional URL maps.')
# Import UrlMap to be verified
data = console_io.ReadFromFileOrStdin(args.source or '-', binary=False)
try:
url_map = export_util.Import(
message_type=client.messages.UrlMap,
stream=data,
schema_path=_GetSchemaPath(self.ReleaseTrack()))
except yaml_validator.ValidationError as e:
raise compute_exceptions.ValidationError(str(e))
# Send UrlMap.validate request
if args.region is not None:
return _SendRegionalRequest(client, project, args.region, url_map)
return _SendGlobalRequest(client, project, url_map,
args.load_balancing_scheme)