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,91 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Functions for creating a client to talk to the App Engine Admin API."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.util import apis as core_apis
from googlecloudsdk.core import properties
from googlecloudsdk.core import resources
class AppengineApiClientBase(object):
"""Base class for App Engine API client."""
def __init__(self, client):
self.client = client
self.project = properties.VALUES.core.project.Get(required=True)
@property
def messages(self):
return self.client.MESSAGES_MODULE
@classmethod
def ApiVersion(cls):
return 'v1'
@classmethod
def GetApiClient(cls, api_version=None):
"""Initializes an AppengineApiClient using the specified API version.
Uses the api_client_overrides/appengine property to determine which client
version to use if api_version is not set. Additionally uses the
api_endpoint_overrides/appengine property to determine the server endpoint
for the App Engine API.
Args:
api_version: The api version override.
Returns:
An AppengineApiClient used by gcloud to communicate with the App Engine
API.
Raises:
ValueError: If default_version does not correspond to a supported version
of the API.
"""
if api_version is None:
api_version = cls.ApiVersion()
return cls(core_apis.GetClientInstance('appengine', api_version))
def _FormatApp(self):
res = resources.REGISTRY.Parse(
self.project, params={}, collection='appengine.apps')
return res.RelativeName()
def _GetServiceRelativeName(self, service_name):
res = resources.REGISTRY.Parse(
service_name,
params={'appsId': self.project},
collection='appengine.apps.services')
return res.RelativeName()
def _FormatVersion(self, service_name, version_id):
res = resources.REGISTRY.Parse(
version_id,
params={'appsId': self.project,
'servicesId': service_name},
collection='appengine.apps.services.versions')
return res.RelativeName()
def _FormatOperation(self, op_id):
res = resources.REGISTRY.Parse(
op_id,
params={'appsId': self.project},
collection='appengine.apps.operations')
return res.RelativeName()

View File

@@ -0,0 +1,93 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Functions for creating a client to talk to the App Engine Admin API."""
from googlecloudsdk.api_lib.app import operations_util
from googlecloudsdk.api_lib.app.api import appengine_api_client_base as base
from googlecloudsdk.calliope import base as calliope_base
from googlecloudsdk.core import log
from googlecloudsdk.core import resources
DEFAULT_VERSION = 'v1beta'
# 'app update' is currently only exposed in beta.
UPDATE_VERSIONS_MAP = {
calliope_base.ReleaseTrack.GA: DEFAULT_VERSION,
calliope_base.ReleaseTrack.ALPHA: DEFAULT_VERSION,
calliope_base.ReleaseTrack.BETA: DEFAULT_VERSION
}
def GetApiClientForTrack(release_track):
return AppengineAppUpdateApiClient.GetApiClient(
UPDATE_VERSIONS_MAP[release_track])
class AppengineAppUpdateApiClient(base.AppengineApiClientBase):
"""Client used by gcloud to communicate with the App Engine API."""
def __init__(self, client):
base.AppengineApiClientBase.__init__(self, client)
self._registry = resources.REGISTRY.Clone()
# pylint: disable=protected-access
self._registry.RegisterApiByName('appengine', client._VERSION)
def PatchApplication(
self, split_health_checks=None, service_account=None, ssl_policy=None
):
"""Updates an application.
Args:
split_health_checks: Boolean, whether to enable split health checks by
default.
service_account: str, the app-level default service account to update for
this App Engine app.
ssl_policy: enum, the app-level SSL policy to update for this App Engine
app. Can be DEFAULT or MODERN.
Returns:
Long running operation.
"""
# Create a configuration update request.
update_mask = ''
if split_health_checks is not None:
update_mask += 'featureSettings.splitHealthChecks,'
if service_account is not None:
update_mask += 'serviceAccount,'
if ssl_policy is not None:
update_mask += 'sslPolicy,'
application_update = self.messages.Application()
application_update.featureSettings = self.messages.FeatureSettings(
splitHealthChecks=split_health_checks)
application_update.serviceAccount = service_account
application_update.sslPolicy = ssl_policy
update_request = self.messages.AppengineAppsPatchRequest(
name=self._FormatApp(),
application=application_update,
updateMask=update_mask)
operation = self.client.apps.Patch(update_request)
log.debug('Received operation: [{operation}] with mask [{mask}]'.format(
operation=operation.name,
mask=update_mask))
return operations_util.WaitForOperation(self.client.apps_operations,
operation)

View File

@@ -0,0 +1,177 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Functions for creating a client to talk to the App Engine Admin API."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.app import operations_util
from googlecloudsdk.api_lib.app.api import appengine_api_client_base as base
from googlecloudsdk.calliope import base as calliope_base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.core import resources
DOMAINS_VERSION_MAP = {
calliope_base.ReleaseTrack.GA: 'v1',
calliope_base.ReleaseTrack.ALPHA: 'v1alpha',
calliope_base.ReleaseTrack.BETA: 'v1beta'
}
def GetApiClientForTrack(release_track):
return AppengineDomainsApiClient.GetApiClient(
DOMAINS_VERSION_MAP[release_track])
class AppengineDomainsApiClient(base.AppengineApiClientBase):
"""Client used by gcloud to communicate with the App Engine API."""
def __init__(self, client):
base.AppengineApiClientBase.__init__(self, client)
self._registry = resources.REGISTRY.Clone()
# pylint: disable=protected-access
self._registry.RegisterApiByName('appengine', client._VERSION)
def DeleteDomainMapping(self, domain):
"""Deletes a domain mapping for the given application.
Args:
domain: str, the domain to delete.
"""
request = self.messages.AppengineAppsDomainMappingsDeleteRequest(
name=self._FormatDomainMapping(domain))
operation = self.client.apps_domainMappings.Delete(request)
operations_util.WaitForOperation(self.client.apps_operations, operation)
def GetDomainMapping(self, domain):
"""Gets a domain mapping for the given application.
Args:
domain: str, the domain to retrieve.
Returns:
The retrieved DomainMapping object.
"""
request = self.messages.AppengineAppsDomainMappingsGetRequest(
name=self._FormatDomainMapping(domain))
return self.client.apps_domainMappings.Get(request)
def ListDomainMappings(self):
"""Lists all domain mappings for the given application.
Returns:
A list of DomainMapping objects.
"""
request = self.messages.AppengineAppsDomainMappingsListRequest(
parent=self._FormatApp())
response = self.client.apps_domainMappings.List(request)
return response.domainMappings
def ListVerifiedDomains(self):
"""Lists all domains verified by the current user.
Returns:
A list of AuthorizedDomain objects.
"""
request = self.messages.AppengineAppsAuthorizedDomainsListRequest(
parent=self._FormatApp())
response = self.client.apps_authorizedDomains.List(request)
return response.domains
def CreateDomainMapping(self, domain, certificate_id, management_type):
"""Creates a domain mapping for the given application.
Args:
domain: str, the custom domain string.
certificate_id: str, a certificate id for the new domain.
management_type: SslSettings.SslManagementTypeValueValuesEnum,
AUTOMATIC or MANUAL certificate provisioning.
Returns:
The created DomainMapping object.
"""
ssl = self.messages.SslSettings(certificateId=certificate_id,
sslManagementType=management_type)
domain_mapping = self.messages.DomainMapping(id=domain, sslSettings=ssl)
request = self.messages.AppengineAppsDomainMappingsCreateRequest(
parent=self._FormatApp(),
domainMapping=domain_mapping)
operation = self.client.apps_domainMappings.Create(request)
return operations_util.WaitForOperation(self.client.apps_operations,
operation).response
def UpdateDomainMapping(self,
domain,
certificate_id,
no_certificate_id,
management_type):
"""Updates a domain mapping for the given application.
Args:
domain: str, the custom domain string.
certificate_id: str, a certificate id for the domain.
no_certificate_id: bool, remove the certificate id from the domain.
management_type: SslSettings.SslManagementTypeValueValuesEnum,
AUTOMATIC or MANUAL certificate provisioning.
Returns:
The updated DomainMapping object.
"""
mask_fields = []
if certificate_id or no_certificate_id:
mask_fields.append('sslSettings.certificateId')
if management_type:
mask_fields.append('sslSettings.sslManagementType')
ssl = self.messages.SslSettings(
certificateId=certificate_id, sslManagementType=management_type)
domain_mapping = self.messages.DomainMapping(id=domain, sslSettings=ssl)
if not mask_fields:
raise exceptions.MinimumArgumentException(
['--[no-]certificate-id', '--no_managed_certificate'],
'Please specify at least one attribute to the domain-mapping update.')
request = self.messages.AppengineAppsDomainMappingsPatchRequest(
name=self._FormatDomainMapping(domain),
domainMapping=domain_mapping,
updateMask=','.join(mask_fields))
operation = self.client.apps_domainMappings.Patch(request)
return operations_util.WaitForOperation(self.client.apps_operations,
operation).response
def _FormatDomainMapping(self, domain):
res = self._registry.Parse(
domain,
params={'appsId': self.project},
collection='appengine.apps.domainMappings')
return res.RelativeName()

View File

@@ -0,0 +1,162 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Functions for creating a client to talk to the App Engine Admin API."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import list_pager
from googlecloudsdk.api_lib.app import util
from googlecloudsdk.api_lib.app.api import appengine_api_client_base as base
from googlecloudsdk.calliope import base as calliope_base
VERSION_MAP = {
calliope_base.ReleaseTrack.GA: 'v1',
calliope_base.ReleaseTrack.ALPHA: 'v1alpha',
calliope_base.ReleaseTrack.BETA: 'v1beta'
}
def GetApiClientForTrack(release_track):
api_version = VERSION_MAP[release_track]
return AppengineFirewallApiClient.GetApiClient(api_version)
class AppengineFirewallApiClient(base.AppengineApiClientBase):
"""Client used by gcloud to communicate with the App Engine API."""
def __init__(self, client):
base.AppengineApiClientBase.__init__(self, client)
def Create(self, priority, source_range, action, description):
"""Creates a firewall rule for the given application.
Args:
priority: int, the priority of the rule between [1, 2^31-1].
The default rule may not be created, only updated.
source_range: str, the ip address or range to take action on.
action: firewall_rules_util.Action, optional action to take on matched
addresses.
description: str, an optional string description of the rule.
Returns:
The new firewall rule.
"""
rule = self.messages.FirewallRule(
priority=priority,
action=action,
description=description,
sourceRange=source_range)
request = self.messages.AppengineAppsFirewallIngressRulesCreateRequest(
parent=self._FormatApp(), firewallRule=rule)
return self.client.apps_firewall_ingressRules.Create(request)
def Delete(self, resource):
"""Deletes a firewall rule for the given application.
Args:
resource: str, the resource path to the firewall rule.
"""
request = self.messages.AppengineAppsFirewallIngressRulesDeleteRequest(
name=resource.RelativeName())
self.client.apps_firewall_ingressRules.Delete(request)
def List(self, matching_address=None):
"""Lists all ingress firewall rules for the given application.
Args:
matching_address: str, an optional ip address to filter matching rules.
Returns:
A list of FirewallRule objects.
"""
request = self.messages.AppengineAppsFirewallIngressRulesListRequest(
parent=self._FormatApp(), matchingAddress=matching_address)
return list_pager.YieldFromList(
self.client.apps_firewall_ingressRules,
request,
field='ingressRules',
batch_size_attribute='pageSize')
def Get(self, resource):
"""Gets a firewall rule for the given application.
Args:
resource: str, the resource path to the firewall rule.
Returns:
A FirewallRule object.
"""
request = self.messages.AppengineAppsFirewallIngressRulesGetRequest(
name=resource.RelativeName())
response = self.client.apps_firewall_ingressRules.Get(request)
return response
def Update(self,
resource,
priority,
source_range=None,
action=None,
description=None):
"""Updates a firewall rule for the given application.
Args:
resource: str, the resource path to the firewall rule.
priority: int, the priority of the rule.
source_range: str, optional ip address or range to take action on.
action: firewall_rules_util.Action, optional action to take on matched
addresses.
description: str, optional string description of the rule.
Returns:
The updated firewall rule.
Raises:
NoFieldsSpecifiedError: when no fields have been specified for the update.
"""
mask_fields = []
if action:
mask_fields.append('action')
if source_range:
mask_fields.append('sourceRange')
if description:
mask_fields.append('description')
rule = self.messages.FirewallRule(
priority=priority,
action=action,
description=description,
sourceRange=source_range)
if not mask_fields:
raise util.NoFieldsSpecifiedError()
request = self.messages.AppengineAppsFirewallIngressRulesPatchRequest(
name=resource.RelativeName(),
firewallRule=rule,
updateMask=','.join(mask_fields))
return self.client.apps_firewall_ingressRules.Patch(request)

View File

@@ -0,0 +1,191 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Functions for creating a client to talk to the App Engine Admin SSL APIs."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.app.api import appengine_api_client_base as base
from googlecloudsdk.calliope import base as calliope_base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.core import resources
from googlecloudsdk.core.util import files
SSL_VERSIONS_MAP = {
calliope_base.ReleaseTrack.GA: 'v1',
calliope_base.ReleaseTrack.ALPHA: 'v1alpha',
calliope_base.ReleaseTrack.BETA: 'v1beta'
}
def GetApiClientForTrack(release_track):
"""Retrieves a client based on the release track.
The API clients override the base class for each track so that methods with
functional differences can be overridden. The ssl-certificates api does not
have API changes for alpha, but output is formatted differently, so the alpha
override simply calls the new API.
Args:
release_track: calliope_base.ReleaseTrack, the release track of the command
Returns:
A client that calls appengine using the v1beta or v1alpha API.
"""
api_version = SSL_VERSIONS_MAP[release_track]
return AppengineSslApiClient.GetApiClient(api_version)
class AppengineSslApiClient(base.AppengineApiClientBase):
"""Client used by gcloud to communicate with the App Engine SSL APIs."""
def __init__(self, client):
base.AppengineApiClientBase.__init__(self, client)
self._registry = resources.REGISTRY.Clone()
# pylint: disable=protected-access
self._registry.RegisterApiByName('appengine', client._VERSION)
def CreateSslCertificate(self, display_name, cert_path, private_key_path):
"""Creates a certificate for the given application.
Args:
display_name: str, the display name for the new certificate.
cert_path: str, location on disk to a certificate file.
private_key_path: str, location on disk to a private key file.
Returns:
The created AuthorizedCertificate object.
Raises:
Error if the file does not exist or can't be opened/read.
"""
certificate_data = files.ReadFileContents(cert_path)
private_key_data = files.ReadFileContents(private_key_path)
cert = self.messages.CertificateRawData(
privateKey=private_key_data, publicCertificate=certificate_data)
auth_cert = self.messages.AuthorizedCertificate(
displayName=display_name, certificateRawData=cert)
request = self.messages.AppengineAppsAuthorizedCertificatesCreateRequest(
parent=self._FormatApp(), authorizedCertificate=auth_cert)
return self.client.apps_authorizedCertificates.Create(request)
def DeleteSslCertificate(self, cert_id):
"""Deletes an authorized certificate for the given application.
Args:
cert_id: str, the id of the certificate to delete.
"""
request = self.messages.AppengineAppsAuthorizedCertificatesDeleteRequest(
name=self._FormatSslCert(cert_id))
self.client.apps_authorizedCertificates.Delete(request)
def GetSslCertificate(self, cert_id):
"""Gets a certificate for the given application.
Args:
cert_id: str, the id of the certificate to retrieve.
Returns:
The retrieved AuthorizedCertificate object.
"""
request = self.messages.AppengineAppsAuthorizedCertificatesGetRequest(
name=self._FormatSslCert(cert_id),
view=(self.messages.AppengineAppsAuthorizedCertificatesGetRequest.
ViewValueValuesEnum.FULL_CERTIFICATE))
return self.client.apps_authorizedCertificates.Get(request)
def ListSslCertificates(self):
"""Lists all authorized certificates for the given application.
Returns:
A list of AuthorizedCertificate objects.
"""
request = self.messages.AppengineAppsAuthorizedCertificatesListRequest(
parent=self._FormatApp())
response = self.client.apps_authorizedCertificates.List(request)
return response.certificates
def UpdateSslCertificate(self,
cert_id,
display_name=None,
cert_path=None,
private_key_path=None):
"""Updates a certificate for the given application.
One of display_name, cert_path, or private_key_path should be set. Omitted
fields will not be updated from their current value. Any invalid arguments
will fail the entire command.
Args:
cert_id: str, the id of the certificate to update.
display_name: str, the display name for a new certificate.
cert_path: str, location on disk to a certificate file.
private_key_path: str, location on disk to a private key file.
Returns:
The created AuthorizedCertificate object.
Raises: InvalidInputError if the user does not specify both cert and key.
"""
if bool(cert_path) ^ bool(private_key_path):
missing_arg = '--certificate' if not cert_path else '--private-key'
raise exceptions.RequiredArgumentException(
missing_arg,
'The certificate and the private key must both be updated together.')
mask_fields = []
if display_name:
mask_fields.append('displayName')
cert_data = None
if cert_path and private_key_path:
certificate = files.ReadFileContents(cert_path)
private_key = files.ReadFileContents(private_key_path)
cert_data = self.messages.CertificateRawData(
privateKey=private_key, publicCertificate=certificate)
mask_fields.append('certificateRawData')
auth_cert = self.messages.AuthorizedCertificate(
displayName=display_name, certificateRawData=cert_data)
if not mask_fields:
raise exceptions.MinimumArgumentException([
'--certificate', '--private-key', '--display-name'
], 'Please specify at least one attribute to the certificate update.')
request = self.messages.AppengineAppsAuthorizedCertificatesPatchRequest(
name=self._FormatSslCert(cert_id),
authorizedCertificate=auth_cert,
updateMask=','.join(mask_fields))
return self.client.apps_authorizedCertificates.Patch(request)
def _FormatSslCert(self, cert_id):
res = self._registry.Parse(
cert_id,
params={'appsId': self.project},
collection='appengine.apps.authorizedCertificates')
return res.RelativeName()