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,32 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command group `gcloud container attached`."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.projects import util
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.GA)
class Attached(base.Group):
"""Manage Attached clusters for running containers."""
category = base.COMPUTE_CATEGORY
def Filter(self, context, args):
del context, args

View File

@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command group `gcloud container attached clusters`."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.projects import util
from googlecloudsdk.core import log
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.GA)
class Clusters(base.Group):
"""Create and manage Attached clusters."""
@staticmethod
def Args(parser):
pass

View File

@@ -0,0 +1,69 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command to delete a registered AttachedCluster resource.."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.gkemulticloud import attached as api_util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.attached import resource_args
from googlecloudsdk.command_lib.container.gkemulticloud import command_util
from googlecloudsdk.command_lib.container.gkemulticloud import constants
from googlecloudsdk.command_lib.container.gkemulticloud import endpoint_util
from googlecloudsdk.command_lib.container.gkemulticloud import flags
_EXAMPLES = """
To delete an AttachedCluster resource named ``my-cluster'' managed in location
``us-west1'', run:
$ {command} my-cluster --location=us-west1
"""
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.GA)
class Delete(base.DeleteCommand):
"""Delete a registered AttachedCluster resource."""
detailed_help = {'EXAMPLES': _EXAMPLES}
@staticmethod
def Args(parser):
"""Register flags for this command."""
resource_args.AddAttachedClusterResourceArg(parser, 'to delete')
flags.AddValidateOnly(parser, 'cluster to delete')
flags.AddAllowMissing(parser, 'cluster')
flags.AddIgnoreErrors(parser, constants.ATTACHED, 'cluster')
base.ASYNC_FLAG.AddToParser(parser)
def Run(self, args):
"""Runs the delete command."""
location = resource_args.ParseAttachedClusterResourceArg(args).locationsId
with endpoint_util.GkemulticloudEndpointOverride(location):
cluster_ref = resource_args.ParseAttachedClusterResourceArg(args)
cluster_client = api_util.ClustersClient()
message = command_util.ClusterMessage(
cluster_ref.attachedClustersId, kind=constants.ATTACHED
)
command_util.DeleteWithIgnoreErrors(
args,
cluster_client,
cluster_ref,
message,
constants.ATTACHED_CLUSTER_KIND,
)

View File

@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command to describe an Attached cluster."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.gkemulticloud import attached as api_util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.attached import resource_args
from googlecloudsdk.command_lib.container.gkemulticloud import endpoint_util
_EXAMPLES = """
To describe a cluster named ``my-cluster'' managed in location ``us-west1'',
run:
$ {command} my-cluster --location=us-west1
"""
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.GA)
class Describe(base.DescribeCommand):
"""Describe an Attached cluster."""
detailed_help = {'EXAMPLES': _EXAMPLES}
@staticmethod
def Args(parser):
"""Registers flags for this command."""
resource_args.AddAttachedClusterResourceArg(parser, 'to describe')
def Run(self, args):
"""Runs the describe command."""
location = resource_args.ParseAttachedClusterResourceArg(args).locationsId
with endpoint_util.GkemulticloudEndpointOverride(location):
cluster_ref = resource_args.ParseAttachedClusterResourceArg(args)
cluster_client = api_util.ClustersClient()
return cluster_client.Get(cluster_ref)

View File

@@ -0,0 +1,74 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command to generate install manifest for Attached cluster."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.gkemulticloud import locations as api_util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.attached import flags as attached_flags
from googlecloudsdk.command_lib.container.attached import resource_args
from googlecloudsdk.command_lib.container.gkemulticloud import endpoint_util
from googlecloudsdk.command_lib.container.gkemulticloud import flags
from googlecloudsdk.core import log
_EXAMPLES = """
To generate install manifest for cluster named ``my-cluster'' managed in location ``us-west1'', run:
$ {command} my-cluster --location=us-west1 --platform-version=PLATFORM_VERSION
To store the manifest in a file named ``manifest.yaml'', run:
$ {command} my-cluster --location=us-west1 --platform-version=PLATFORM_VERSION --output-file=manifest.yaml
"""
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.GA)
class Describe(base.Command):
"""Generate Install Manifest for an Attached cluster."""
detailed_help = {'EXAMPLES': _EXAMPLES}
@staticmethod
def Args(parser):
"""Registers flags for this command."""
resource_args.AddAttachedClusterResourceArg(
parser, 'to generate install manifest'
)
attached_flags.AddPlatformVersion(parser)
attached_flags.AddProxyConfig(parser)
flags.AddOutputFile(parser, 'to store manifest')
def Run(self, args):
"""Runs the generate-install-manifest command."""
location = resource_args.ParseAttachedClusterResourceArg(args).locationsId
with endpoint_util.GkemulticloudEndpointOverride(location):
cluster_ref = resource_args.ParseAttachedClusterResourceArg(args)
client = api_util.LocationsClient()
resp = client.GenerateInstallManifest(cluster_ref, args=args)
if args.output_file:
log.WriteToFileOrStdout(
args.output_file,
resp.manifest,
overwrite=True,
binary=False,
private=True,
)
return None
return resp

View File

@@ -0,0 +1,76 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command to get credentials of an Attached cluster."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.gkemulticloud import attached as api_util
from googlecloudsdk.api_lib.container.gkemulticloud import util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.attached import resource_args
from googlecloudsdk.command_lib.container.gkemulticloud import endpoint_util
from googlecloudsdk.command_lib.container.gkemulticloud import flags
from googlecloudsdk.command_lib.container.gkemulticloud import kubeconfig
from googlecloudsdk.core import log
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.GA)
class GetCredentials(base.Command):
"""Get credentials of an Attached cluster."""
detailed_help = {
'EXAMPLES': kubeconfig.COMMAND_EXAMPLE,
'DESCRIPTION': kubeconfig.COMMAND_DESCRIPTION.format(
cluster_type='Attached cluster'
),
}
@staticmethod
def Args(parser):
resource_args.AddAttachedClusterResourceArg(parser, 'to get credentials')
flags.AddAuthProviderCmdPath(parser)
def Run(self, args):
"""Runs the get-credentials command."""
with endpoint_util.GkemulticloudEndpointOverride(
resource_args.ParseAttachedClusterResourceArg(args).locationsId,
self.ReleaseTrack(),
):
cluster_ref = resource_args.ParseAttachedClusterResourceArg(args)
cluster_client = api_util.ClustersClient()
log.status.Print('Fetching cluster endpoint and auth data.')
resp = cluster_client.Get(cluster_ref)
if (
resp.state
!= util.GetMessagesModule().GoogleCloudGkemulticloudV1AttachedCluster.StateValueValuesEnum.RUNNING
):
log.warning(
kubeconfig.NOT_RUNNING_MSG.format(cluster_ref.attachedClustersId)
)
context = kubeconfig.GenerateContext(
'attached',
cluster_ref.projectsId,
cluster_ref.locationsId,
cluster_ref.attachedClustersId,
)
kubeconfig.GenerateAttachedKubeConfig(
resp,
cluster_ref.attachedClustersId,
context,
args.auth_provider_cmd_path,
)

View File

@@ -0,0 +1,160 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command to import an Attached cluster."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.gkemulticloud import attached as api_util
from googlecloudsdk.api_lib.container.gkemulticloud import locations as loc_util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.attached import cluster_util
from googlecloudsdk.command_lib.container.attached import flags as attached_flags
from googlecloudsdk.command_lib.container.attached import resource_args
from googlecloudsdk.command_lib.container.fleet import kube_util
from googlecloudsdk.command_lib.container.gkemulticloud import command_util
from googlecloudsdk.command_lib.container.gkemulticloud import constants
from googlecloudsdk.command_lib.container.gkemulticloud import endpoint_util
from googlecloudsdk.command_lib.container.gkemulticloud import flags
from googlecloudsdk.command_lib.run import pretty_print
from googlecloudsdk.core import exceptions
from googlecloudsdk.core.console import console_io
from googlecloudsdk.core.util import retry
import six
_EXAMPLES = """
To import the fleet membership of an attached cluster in fleet ``FLEET_MEMBERSHIP'' managed in location ``us-west1'', run:
$ {command} --location=us-west1 --platform-version=PLATFORM_VERSION --fleet-membership=FLEET_MEMBERSHIP --distribution=DISTRIBUTION --context=CLUSTER_CONTEXT --kubeconfig=KUBECONFIG_PATH
"""
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.GA)
@base.DefaultUniverseOnly
class Import(base.Command):
"""Import fleet membership for an Attached cluster."""
detailed_help = {'EXAMPLES': _EXAMPLES}
@staticmethod
def Args(parser):
"""Registers flags for this command."""
resource_args.AddLocationResourceArg(parser, 'to import attached cluster.')
resource_args.AddFleetMembershipResourceArg(parser)
attached_flags.AddPlatformVersion(parser)
attached_flags.AddDistribution(parser, required=True)
attached_flags.AddKubectl(parser)
attached_flags.AddProxyConfig(parser)
attached_flags.AddSkipClusterAdminCheck(parser)
flags.AddValidateOnly(parser, 'cluster to import')
base.ASYNC_FLAG.AddToParser(parser)
def Run(self, args):
"""Runs the import command."""
location_ref = args.CONCEPTS.location.Parse()
fleet_membership_ref = args.CONCEPTS.fleet_membership.Parse()
with endpoint_util.GkemulticloudEndpointOverride(location_ref.locationsId):
manifest = self._get_manifest(
args, location_ref, fleet_membership_ref.membershipsId
)
import_resp = ''
with kube_util.KubernetesClient(
kubeconfig=attached_flags.GetKubeconfig(args),
context=attached_flags.GetContext(args),
enable_workload_identity=True,
) as kube_client:
if not attached_flags.GetSkipClusterAdminCheck(args):
kube_client.CheckClusterAdminPermissions()
try:
if not flags.GetValidateOnly(args):
pretty_print.Info('Creating in-cluster install agent')
kube_client.Apply(manifest)
retryer = retry.Retryer(
max_retrials=constants.ATTACHED_INSTALL_AGENT_VERIFY_RETRIES
)
retryer.RetryOnException(
cluster_util.verify_install_agent_deployed,
args=(kube_client,),
sleep_ms=constants.ATTACHED_INSTALL_AGENT_VERIFY_WAIT_MS,
)
import_resp = self._import_attached_cluster(
args, location_ref, fleet_membership_ref
)
except retry.RetryException as e:
self._remove_manifest(args, kube_client, manifest)
# last_result[1] holds information about the last exception the
# retryer caught. last_result[1][1] holds the exception type and
# last_result[1][2] holds the exception value. The retry exception is
# not useful to users, so reraise whatever error caused it to timeout.
if e.last_result[1]:
exceptions.reraise(e.last_result[1][1], e.last_result[1][2])
raise
except console_io.OperationCancelledError:
msg = """To manually clean up the in-cluster install agent, run:
$ gcloud {} container attached clusters generate-install-manifest --location={} --platform-version={} --format="value(manifest)" {} | kubectl delete -f -
AFTER the attach operation completes.
""".format(
six.text_type(self.ReleaseTrack()).lower(),
location_ref.locationsId,
attached_flags.GetPlatformVersion(args),
fleet_membership_ref.membershipsId,
)
pretty_print.Info(msg)
raise
except: # pylint: disable=broad-except
self._remove_manifest(args, kube_client, manifest)
raise
self._remove_manifest(args, kube_client, manifest)
return import_resp
def _get_manifest(self, args, location_ref, memberships_id):
location_client = loc_util.LocationsClient()
resp = location_client.GenerateInstallManifestForImport(
location_ref, memberships_id, args=args
)
return resp.manifest
def _remove_manifest(self, args, kube_client, manifest):
if not flags.GetValidateOnly(args):
pretty_print.Info('Deleting in-cluster install agent')
kube_client.Delete(manifest)
def _import_attached_cluster(self, args, location_ref, fleet_membership_ref):
cluster_client = api_util.ClustersClient()
message = command_util.ClusterMessage(
fleet_membership_ref.RelativeName(),
action='Importing',
kind=constants.ATTACHED,
)
return command_util.Import(
location_ref=location_ref,
resource_client=cluster_client,
fleet_membership_ref=fleet_membership_ref,
args=args,
message=message,
kind=constants.ATTACHED_CLUSTER_KIND,
)

View File

@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command to list Attached clusters."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.gkemulticloud import attached as api_util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.attached import resource_args
from googlecloudsdk.command_lib.container.gkemulticloud import constants
from googlecloudsdk.command_lib.container.gkemulticloud import endpoint_util
_EXAMPLES = """
To lists all clusters managed in location ``us-west1'', run:
$ {command} --location=us-west1
"""
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.GA)
class Describe(base.ListCommand):
"""List Attached clusters."""
detailed_help = {'EXAMPLES': _EXAMPLES}
@staticmethod
def Args(parser):
resource_args.AddLocationResourceArg(parser, 'to list')
parser.display_info.AddFormat(constants.ATTACHED_CLUSTERS_FORMAT)
def Run(self, args):
"""Runs the list command."""
location_ref = args.CONCEPTS.location.Parse()
with endpoint_util.GkemulticloudEndpointOverride(location_ref.locationsId):
cluster_client = api_util.ClustersClient()
items, _ = cluster_client.List(location_ref, args.page_size, args.limit)
return items

View File

@@ -0,0 +1,217 @@
# -*- coding: utf-8 -*- #
# Copyright 2025 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command to register an Attached cluster with the fleet.
This command performs the full end-to-end steps required to attach a cluster.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import json
from googlecloudsdk.api_lib.container.gkemulticloud import attached as api_util
from googlecloudsdk.api_lib.container.gkemulticloud import locations as loc_util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.attached import cluster_util
from googlecloudsdk.command_lib.container.attached import flags as attached_flags
from googlecloudsdk.command_lib.container.attached import resource_args
from googlecloudsdk.command_lib.container.fleet import kube_util
from googlecloudsdk.command_lib.container.gkemulticloud import command_util
from googlecloudsdk.command_lib.container.gkemulticloud import constants
from googlecloudsdk.command_lib.container.gkemulticloud import endpoint_util
from googlecloudsdk.command_lib.container.gkemulticloud import errors
from googlecloudsdk.command_lib.container.gkemulticloud import flags
from googlecloudsdk.command_lib.run import exceptions as run_exceptions
from googlecloudsdk.command_lib.run import pretty_print
from googlecloudsdk.core import exceptions
from googlecloudsdk.core.console import console_io
from googlecloudsdk.core.util import retry
import six
# Command needs to be in one line for the docgen tool to format properly.
_EXAMPLES = """
Register a cluster to a fleet.
To register a cluster with a private OIDC issuer, run:
$ {command} my-cluster --location=us-west1 --platform-version=PLATFORM_VERSION --fleet-project=FLEET_PROJECT_NUM --distribution=DISTRIBUTION --context=CLUSTER_CONTEXT --has-private-issuer
To register a cluster with a public OIDC issuer, run:
$ {command} my-cluster --location=us-west1 --platform-version=PLATFORM_VERSION --fleet-project=FLEET_PROJECT_NUM --distribution=DISTRIBUTION --context=CLUSTER_CONTEXT --issuer-url=https://ISSUER_URL
To specify a kubeconfig file, run:
$ {command} my-cluster --location=us-west1 --platform-version=PLATFORM_VERSION --fleet-project=FLEET_PROJECT_NUM --distribution=DISTRIBUTION --context=CLUSTER_CONTEXT --has-private-issuer --kubeconfig=KUBECONFIG_PATH
To register and set cluster admin users, run:
$ {command} my-cluster --location=us-west1 --platform-version=PLATFORM_VERSION --fleet-project=FLEET_PROJECT_NUM --distribution=DISTRIBUTION --context=CLUSTER_CONTEXT --issuer-url=https://ISSUER_URL --admin-users=USER1,USER2
To specify custom tolerations and labels for system component pods, run:
$ {command} my-cluster --location=us-west1 --platform-version=PLATFORM_VERSION --fleet-project=FLEET_PROJECT_NUM --distribution=DISTRIBUTION --context=CLUSTER_CONTEXT --system-component-tolerations=TOLERATIONS --system-component-labels=LABELS
where TOLERATIONS have the format:
key=value:Effect:NoSchedule (examples: key1=value1:Equal:NoSchedule,key2:Exists:PreferNoSchedule, :Exists:NoExecute)
and LABELS have the format:
key=value (examples: key1=value1,key2="")
"""
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.GA)
@base.DefaultUniverseOnly
class Register(base.CreateCommand):
"""Register an Attached cluster."""
detailed_help = {'EXAMPLES': _EXAMPLES}
@staticmethod
def Args(parser):
"""Registers flags for this command."""
resource_args.AddAttachedClusterResourceArg(parser, 'to register')
attached_flags.AddPlatformVersion(parser)
attached_flags.AddRegisterOidcConfig(parser)
attached_flags.AddDistribution(parser, required=True)
attached_flags.AddAdminUsers(parser)
attached_flags.AddKubectl(parser)
attached_flags.AddProxyConfig(parser)
attached_flags.AddSkipClusterAdminCheck(parser)
attached_flags.AddSystemComponentTolerations(parser)
attached_flags.AddSystemComponentLabels(parser)
flags.AddAnnotations(parser)
flags.AddValidateOnly(parser, 'cluster to create')
flags.AddFleetProject(parser)
flags.AddDescription(parser)
flags.AddLogging(parser, True)
flags.AddMonitoringConfig(parser, True, True)
flags.AddBinauthzEvaluationMode(parser)
flags.AddAdminGroups(parser)
flags.AddWorkloadVulnerabilityScanning(parser)
flags.AddResourceManagerTags(parser)
parser.display_info.AddFormat(constants.ATTACHED_CLUSTERS_FORMAT)
def Run(self, args):
location = resource_args.ParseAttachedClusterResourceArg(args).locationsId
if (
attached_flags.GetHasPrivateIssuer(args)
and attached_flags.GetDistribution(args) == 'eks'
):
raise run_exceptions.ArgumentError(
'Distributions of type "eks" cannot use the `has-private-issuer`'
' flag.'
)
# Validate system component tolerations early to fail fast.
attached_flags.GetSystemComponentTolerations(args)
with endpoint_util.GkemulticloudEndpointOverride(location):
cluster_ref = resource_args.ParseAttachedClusterResourceArg(args)
manifest = self._get_manifest(args, cluster_ref)
with kube_util.KubernetesClient(
kubeconfig=attached_flags.GetKubeconfig(args),
context=attached_flags.GetContext(args),
enable_workload_identity=True,
) as kube_client:
if not attached_flags.GetSkipClusterAdminCheck(args):
kube_client.CheckClusterAdminPermissions()
if attached_flags.GetHasPrivateIssuer(args):
pretty_print.Info('Fetching cluster OIDC information')
issuer_url, jwks = self._get_authority(kube_client)
setattr(args, 'issuer_url', issuer_url)
setattr(args, 'oidc_jwks', jwks)
try:
if not flags.GetValidateOnly(args):
pretty_print.Info('Creating in-cluster install agent')
kube_client.Apply(manifest)
retryer = retry.Retryer(
max_retrials=constants.ATTACHED_INSTALL_AGENT_VERIFY_RETRIES
)
retryer.RetryOnException(
cluster_util.verify_install_agent_deployed,
args=(kube_client,),
sleep_ms=constants.ATTACHED_INSTALL_AGENT_VERIFY_WAIT_MS,
)
create_resp = self._create_attached_cluster(args, cluster_ref)
except retry.RetryException as e:
self._remove_manifest(args, kube_client, manifest)
# last_result[1] holds information about the last exception the
# retryer caught. last_result[1][1] holds the exception type and
# last_result[1][2] holds the exception value. The retry exception is
# not useful to users, so reraise whatever error caused it to timeout.
if e.last_result[1]:
exceptions.reraise(e.last_result[1][1], e.last_result[1][2])
raise
except console_io.OperationCancelledError:
msg = """To manually clean up the in-cluster install agent, run:
$ gcloud container attached clusters generate-install-manifest --location={} --platform-version={} --format="value(manifest)" {} | kubectl delete -f -
AFTER the attach operation completes.
""".format(
location,
attached_flags.GetPlatformVersion(args),
cluster_ref.attachedClustersId,
)
pretty_print.Info(msg)
raise
except: # pylint: disable=broad-except
self._remove_manifest(args, kube_client, manifest)
raise
self._remove_manifest(args, kube_client, manifest)
return create_resp
def _get_manifest(self, args, cluster_ref):
location_client = loc_util.LocationsClient()
resp = location_client.GenerateInstallManifest(cluster_ref, args=args)
return resp.manifest
def _remove_manifest(self, args, kube_client, manifest):
if not flags.GetValidateOnly(args):
pretty_print.Info('Deleting in-cluster install agent')
kube_client.Delete(manifest)
def _get_authority(self, kube_client):
openid_config_json = six.ensure_str(
kube_client.GetOpenIDConfiguration(), encoding='utf-8'
)
issuer_url = json.loads(openid_config_json).get('issuer')
if not issuer_url:
raise errors.MissingOIDCIssuerURL(openid_config_json)
jwks = kube_client.GetOpenIDKeyset()
return issuer_url, jwks
def _create_attached_cluster(self, args, cluster_ref):
cluster_client = api_util.ClustersClient()
message = command_util.ClusterMessage(
cluster_ref.attachedClustersId,
action='Creating',
kind=constants.ATTACHED,
)
return command_util.Create(
resource_ref=cluster_ref,
resource_client=cluster_client,
args=args,
message=message,
kind=constants.ATTACHED_CLUSTER_KIND,
)

View File

@@ -0,0 +1,80 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command to update an Attached cluster."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.gkemulticloud import attached as api_util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.attached import flags as attached_flags
from googlecloudsdk.command_lib.container.attached import resource_args
from googlecloudsdk.command_lib.container.gkemulticloud import command_util
from googlecloudsdk.command_lib.container.gkemulticloud import constants
from googlecloudsdk.command_lib.container.gkemulticloud import endpoint_util
from googlecloudsdk.command_lib.container.gkemulticloud import flags
# Command needs to be in one line for the docgen tool to format properly.
_EXAMPLES = """
To update a cluster named ``my-cluster'' managed in location ``us-west1'', run:
$ {command} my-cluster --location=us-west1 --description=testcluster
"""
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.GA)
@base.DefaultUniverseOnly
class Update(base.UpdateCommand):
"""Update an Attached cluster."""
detailed_help = {'EXAMPLES': _EXAMPLES}
@staticmethod
def Args(parser):
resource_args.AddAttachedClusterResourceArg(parser, 'to update')
flags.AddDescription(parser, required=False)
flags.AddClearDescription(parser)
flags.AddAnnotations(parser)
flags.AddValidateOnly(parser, 'update of the cluster')
flags.AddLogging(parser, True)
flags.AddMonitoringConfig(parser, False, True)
flags.AddBinauthzEvaluationMode(parser)
flags.AddAdminGroupsForUpdate(parser)
flags.AddWorkloadVulnerabilityScanning(parser)
attached_flags.AddAdminUsersForUpdate(parser)
attached_flags.AddPlatformVersion(parser, required=False)
attached_flags.AddProxyConfig(parser)
base.ASYNC_FLAG.AddToParser(parser)
parser.display_info.AddFormat(constants.ATTACHED_CLUSTERS_FORMAT)
def Run(self, args):
"""Runs the update command."""
location = resource_args.ParseAttachedClusterResourceArg(args).locationsId
with endpoint_util.GkemulticloudEndpointOverride(location):
cluster_ref = resource_args.ParseAttachedClusterResourceArg(args)
cluster_client = api_util.ClustersClient()
message = command_util.ClusterMessage(
cluster_ref.attachedClustersId, action='Updating'
)
return command_util.Update(
resource_ref=cluster_ref,
resource_client=cluster_client,
args=args,
message=message,
kind=constants.ATTACHED_CLUSTER_KIND,
)

View File

@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command to get Anthos Multi-Cloud server configuration for Attached clusters."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.gkemulticloud import locations as api_util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.attached import resource_args
from googlecloudsdk.command_lib.container.gkemulticloud import constants
from googlecloudsdk.command_lib.container.gkemulticloud import endpoint_util
from googlecloudsdk.core import log
_EXAMPLES = """
To return supported Attached cluster valid platform versions in location ``us-west1'', run:
$ {command} --location=us-west1
"""
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.GA)
class GetServerConfig(base.Command):
"""Get Anthos Multi-Cloud server configuration for Attached clusters."""
detailed_help = {'EXAMPLES': _EXAMPLES}
@staticmethod
def Args(parser):
resource_args.AddLocationResourceArg(parser, 'to get server configuration')
parser.display_info.AddFormat(constants.ATTACHED_SERVER_CONFIG_FORMAT)
def Run(self, args):
"""Runs the get-server-config command."""
location_ref = args.CONCEPTS.location.Parse()
with endpoint_util.GkemulticloudEndpointOverride(location_ref.locationsId):
log.status.Print(
'Fetching server config for {location}'.format(
location=location_ref.locationsId
)
)
client = api_util.LocationsClient()
return client.GetAttachedServerConfig(location_ref)

View File

@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command group `gcloud container attached operations`."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.projects import util
from googlecloudsdk.core import log
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.GA)
class Operations(base.Group):
"""Manage Anthos Multi-Cloud long running operations for Attached clusters."""
@staticmethod
def Args(parser):
pass

View File

@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command to describe an operation."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.gkemulticloud import operations as op_api_util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.attached import resource_args
from googlecloudsdk.command_lib.container.gkemulticloud import endpoint_util
_EXAMPLES = """
To describe an operation in location ``us-west1'', run:
$ {command} OPERATION_ID --location=us-west1
"""
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.GA)
class Describe(base.DescribeCommand):
"""Describe an operation."""
detailed_help = {'EXAMPLES': _EXAMPLES}
@staticmethod
def Args(parser):
"""Registers flags for this command."""
resource_args.AddOperationResourceArg(parser, 'to describe')
def Run(self, args):
"""Runs the describe command."""
with endpoint_util.GkemulticloudEndpointOverride(
resource_args.ParseOperationResourceArg(args).locationsId,
self.ReleaseTrack(),
):
op_client = op_api_util.OperationsClient()
op_ref = resource_args.ParseOperationResourceArg(args)
return op_client.Get(op_ref)

View File

@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Commmand to list operations."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.gkemulticloud import operations as op_api_util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.attached import resource_args
from googlecloudsdk.command_lib.container.gkemulticloud import endpoint_util
from googlecloudsdk.command_lib.container.gkemulticloud import operations
_EXAMPLES = """
To list all operations in location ``us-west1'', run:
$ {command} --location=us-west1
"""
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.GA)
class List(base.ListCommand):
"""List operations."""
detailed_help = {'EXAMPLES': _EXAMPLES}
@staticmethod
def Args(parser):
"""Registers flags for this command."""
resource_args.AddLocationResourceArg(parser, 'to list operations')
operations.AddFormat(parser)
def Run(self, args):
"""Runs the describe command."""
release_track = self.ReleaseTrack()
location_ref = args.CONCEPTS.location.Parse()
with endpoint_util.GkemulticloudEndpointOverride(
location_ref.locationsId, release_track
):
op_client = op_api_util.OperationsClient()
items, empty = op_client.List(
location_ref, args.page_size, args.limit, parent_field='name'
)
if not empty:
# ListOperations returns AWS, Azure, and attached operations.
# Add a filter for attached operations.
operations.AddFilter(args, 'attached')
return items

View File

@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command to wait for an operation to complete."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.gkemulticloud import operations as op_api_util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.attached import resource_args
from googlecloudsdk.command_lib.container.gkemulticloud import endpoint_util
_EXAMPLES = """
To wait for an operation in location ``us-west1'' to complete, run:
$ {command} OPERATION_ID --location=us-west1
"""
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.GA)
class Describe(base.DescribeCommand):
"""Wait for an operation to complete."""
detailed_help = {'EXAMPLES': _EXAMPLES}
@staticmethod
def Args(parser):
"""Registers flags for this command."""
resource_args.AddOperationResourceArg(parser, 'to wait for')
def Run(self, args):
"""Runs the wait command."""
with endpoint_util.GkemulticloudEndpointOverride(
resource_args.ParseOperationResourceArg(args).locationsId,
self.ReleaseTrack(),
):
op_client = op_api_util.OperationsClient()
op_ref = resource_args.ParseOperationResourceArg(args)
op_client.Wait(
op_ref,
'Waiting for operation {} to complete'.format(op_ref.RelativeName()),
)
return op_client.Get(op_ref)