516 lines
16 KiB
Python
516 lines
16 KiB
Python
# -*- 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.
|
|
"""Design Center Command Lib Flags."""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import unicode_literals
|
|
|
|
from googlecloudsdk.calliope import arg_parsers
|
|
from googlecloudsdk.calliope import base
|
|
from googlecloudsdk.calliope.concepts import concepts
|
|
from googlecloudsdk.command_lib.util.concepts import concept_parsers
|
|
from googlecloudsdk.core import yaml
|
|
|
|
|
|
def GetProjectResourceSpec():
|
|
return concepts.ResourceSpec(
|
|
'designcenter.projects',
|
|
resource_name='project',
|
|
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
|
|
)
|
|
|
|
|
|
def GetProjectResourceArg(
|
|
arg_name='project',
|
|
help_text=None,
|
|
positional=False,
|
|
required=True,
|
|
):
|
|
"""Constructs and returns the Project Resource Argument."""
|
|
help_text = help_text or 'Project ID.'
|
|
return concept_parsers.ConceptParser.ForResource(
|
|
'{}{}'.format('' if positional else '--', arg_name),
|
|
GetProjectResourceSpec(),
|
|
help_text,
|
|
required=required,
|
|
)
|
|
|
|
|
|
def LocationAttributeConfig():
|
|
return concepts.ResourceParameterAttributeConfig(
|
|
name='location',
|
|
help_text='The Cloud location for the {resource}.',
|
|
)
|
|
|
|
|
|
def GetLocationResourceSpec():
|
|
return concepts.ResourceSpec(
|
|
'designcenter.projects.locations',
|
|
resource_name='location',
|
|
locationsId=LocationAttributeConfig(),
|
|
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
|
|
)
|
|
|
|
|
|
def GetLocationResourceArg(
|
|
arg_name='location',
|
|
help_text=None,
|
|
positional=False,
|
|
required=True,
|
|
):
|
|
"""Constructs and returns the Location Resource Argument."""
|
|
help_text = help_text or 'Location.'
|
|
|
|
return concept_parsers.ConceptParser.ForResource(
|
|
'{}{}'.format('' if positional else '--', arg_name),
|
|
GetLocationResourceSpec(),
|
|
help_text,
|
|
required=required,
|
|
)
|
|
|
|
|
|
def SpaceResourceAttributeConfig(arg_name, help_text):
|
|
"""Helper function for constructing ResourceAttributeConfig."""
|
|
return concepts.ResourceParameterAttributeConfig(
|
|
name=arg_name,
|
|
help_text=help_text,
|
|
)
|
|
|
|
|
|
def GetSpaceResourceSpec(arg_name='space', help_text=None):
|
|
"""Constructs and returns the Resource specification for Space."""
|
|
return concepts.ResourceSpec(
|
|
'designcenter.projects.locations.spaces',
|
|
resource_name='space',
|
|
spacesId=SpaceResourceAttributeConfig(arg_name, help_text),
|
|
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
|
|
locationsId=LocationAttributeConfig(),
|
|
)
|
|
|
|
|
|
def GetSpaceResourceArg(
|
|
arg_name='space', help_text=None, positional=True, required=True
|
|
):
|
|
"""Constructs and returns the Space ID Resource Argument."""
|
|
help_text = help_text or 'The Space ID.'
|
|
|
|
return concept_parsers.ConceptParser.ForResource(
|
|
'{}{}'.format('' if positional else '--', arg_name),
|
|
GetSpaceResourceSpec(arg_name, help_text),
|
|
help_text,
|
|
required=required,
|
|
)
|
|
|
|
|
|
# -- Revision Resource and Flags --
|
|
|
|
|
|
def CatalogAttributeConfig():
|
|
"""Creates an attribute config for the catalog."""
|
|
return concepts.ResourceParameterAttributeConfig(
|
|
name='catalog',
|
|
help_text='The ID of the catalog.'
|
|
)
|
|
|
|
|
|
def CatalogTemplateAttributeConfig():
|
|
"""Creates an attribute config for the template."""
|
|
return concepts.ResourceParameterAttributeConfig(
|
|
name='template',
|
|
help_text='The ID of the template.'
|
|
)
|
|
|
|
|
|
def GetCatalogTemplateRevisionResourceSpec():
|
|
"""Creates the resource spec for a revision."""
|
|
return concepts.ResourceSpec(
|
|
'designcenter.projects.locations.spaces.catalogs.templates.revisions',
|
|
resource_name='revision',
|
|
revisionsId=concepts.ResourceParameterAttributeConfig(
|
|
name='revision', help_text='The ID of the revision to create.'
|
|
),
|
|
templatesId=CatalogTemplateAttributeConfig(),
|
|
catalogsId=CatalogAttributeConfig(),
|
|
spacesId=SpaceResourceAttributeConfig('space', 'The ID of the space.'),
|
|
locationsId=LocationAttributeConfig(),
|
|
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
|
|
)
|
|
|
|
|
|
def AddDescribeLocationFlags(parser):
|
|
GetLocationResourceArg(positional=True).AddToParser(parser)
|
|
|
|
|
|
def AddGetIamPolicyFlags(parser):
|
|
GetSpaceResourceArg().AddToParser(parser)
|
|
|
|
|
|
def AddSetIamPolicyFlags(parser):
|
|
GetSpaceResourceArg().AddToParser(parser)
|
|
|
|
|
|
def AddTestIamPermissionsFlags(parser):
|
|
GetSpaceResourceArg().AddToParser(parser)
|
|
|
|
|
|
def AddCreateCatalogTemplateRevisionFlags(parser):
|
|
"""Adds all flags for the create revision command.
|
|
|
|
Args:
|
|
parser: An argparse.ArgumentParser-like object. It is mocked out in tests.
|
|
"""
|
|
# This defines the resource argument for the revision.
|
|
# By using the full resource spec and a positional name ('revision'), the
|
|
# SDK automatically creates a positional argument for the revision ID and
|
|
# flags for all its parents (--template, --catalog, --space, etc.).
|
|
concept_parsers.ConceptParser.ForResource(
|
|
'revision',
|
|
GetCatalogTemplateRevisionResourceSpec(),
|
|
'The revision to create.',
|
|
required=True,
|
|
).AddToParser(parser)
|
|
parser.add_argument('--description', help='A description for the revision.')
|
|
|
|
source_group = parser.add_group(mutex=True, required=True)
|
|
|
|
dev_connect_group = source_group.add_group(
|
|
help='Flags for Developer Connect source.'
|
|
)
|
|
dev_connect_group.add_argument(
|
|
'--developer-connect-repo',
|
|
help=(
|
|
'The Developer Connect repository to use as a source. Example: '
|
|
'`projects/my-project/locations/us-central1/connections/my-connection/'
|
|
'gitRepositoryLinks/my-repo`'
|
|
),
|
|
required=True,
|
|
)
|
|
dev_connect_group.add_argument(
|
|
'--developer-connect-repo-ref',
|
|
help=(
|
|
'The Git ref (branch or tag) within the repository to use. Example:'
|
|
' `"refs/tags/v1.0.0"` or `"refs/heads/main"` or '
|
|
'`"refs/commits/269b518b99d06b31ff938a2d182e75f5e41941c7"`.'
|
|
),
|
|
required=True,
|
|
)
|
|
dev_connect_group.add_argument(
|
|
'--developer-connect-repo-dir',
|
|
help=(
|
|
'The directory within the repository to use. Example:'
|
|
' `"modules/my-product"`'
|
|
),
|
|
required=True,
|
|
)
|
|
|
|
git_source_group = source_group.add_group(
|
|
help='Flags for Git source.'
|
|
)
|
|
git_source_group.add_argument(
|
|
'--git-source-repo',
|
|
help='Git repository for Git source. Example:'
|
|
' `GoogleCloudPlatform/terraform-google-cloud-run`',
|
|
required=True,
|
|
)
|
|
git_source_group.add_argument(
|
|
'--git-source-ref-tag',
|
|
help='Git reference tag for Git source. Example: `"v1.0.0"`',
|
|
required=True,
|
|
)
|
|
git_source_group.add_argument(
|
|
'--git-source-dir',
|
|
help=(
|
|
'Git directory for Git source. Example: `"modules/my-product"`.'
|
|
' This field is optional.'
|
|
),
|
|
required=False,
|
|
)
|
|
|
|
source_group.add_argument(
|
|
'--application-template-revision-source',
|
|
help=(
|
|
'Application template revision to use as source. Example:'
|
|
' `projects/my-project/locations/us-central1/spaces/my-space/catalogs/my-catalog/templates/my-template/revisions/r1`'
|
|
),
|
|
required=False,
|
|
)
|
|
|
|
source_group.add_argument(
|
|
'--gcs-source-uri',
|
|
help=(
|
|
'Google Cloud Storage URI for source. Example:'
|
|
' `gs://my-bucket/my-template`.'
|
|
),
|
|
required=False,
|
|
)
|
|
|
|
oci_repo_group = source_group.add_group(help='Flags for OCI Repo source.')
|
|
oci_repo_group.add_argument(
|
|
'--oci-repo-uri',
|
|
help=(
|
|
'OCI Repo URI for OCI Repo source. Example:'
|
|
' `oci://us-west1-docker.pkg.dev/my-project/my-repo/my-chart`'
|
|
),
|
|
required=True,
|
|
)
|
|
oci_repo_group.add_argument(
|
|
'--oci-repo-version',
|
|
help=(
|
|
'OCI Repo version for OCI Repo source. Example: `"1.0.0"`. This field'
|
|
' is optional.'
|
|
),
|
|
required=False,
|
|
)
|
|
|
|
parser.add_argument(
|
|
'--metadata',
|
|
type=arg_parsers.YAMLFileContents(),
|
|
help=(
|
|
'Path to a local YAML file containing the template metadata. Example:'
|
|
' `"path/to/metadata.yaml"`.'
|
|
),
|
|
)
|
|
|
|
|
|
def AddRegisterWithApphubFlags(parser):
|
|
"""Adds all flags for the register with apphub command.
|
|
|
|
Args:
|
|
parser: An argparse.ArgumentParser-like object. It is mocked out in tests.
|
|
"""
|
|
GetSpaceResourceArg(
|
|
arg_name='space',
|
|
help_text='The ID of the space.',
|
|
).AddToParser(parser)
|
|
source_group = parser.add_group(mutex=True, required=True)
|
|
source_group.add_argument(
|
|
'--apphub-application-uri',
|
|
help=(
|
|
'AppHub application URI to register the deployed resource using'
|
|
' application template as source in the space.'
|
|
' Format: `projects/{projectId}/locations/{locationId}/applications/'
|
|
'{applicationId}'
|
|
),
|
|
)
|
|
source_group.add_argument(
|
|
'--adc-application-uri',
|
|
help=(
|
|
'The Application Design Center application URI to register the'
|
|
' deployed resources with the AppHub application using Application'
|
|
' Design Center application as source in the space. Format:'
|
|
' `projects/{projectId}/locations/{locationId}/spaces/{spaceId}/applications/'
|
|
'{applicationId}`'
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
'--tfstate-location',
|
|
help='Path to the Terraform state file (e.g., `terraform.tfstate`).',
|
|
)
|
|
base.ASYNC_FLAG.AddToParser(parser)
|
|
|
|
|
|
def AddTfStateSourceFlags(parser):
|
|
"""Adds flags for specifying the Terraform state source.
|
|
|
|
Args:
|
|
parser: An argparse.ArgumentParser-like object. It is mocked out in tests.
|
|
"""
|
|
tfstate_source_group = parser.add_group(mutex=True, required=True)
|
|
tfstate_source_group.add_argument(
|
|
'--terraform-state',
|
|
help=(
|
|
'The Terraform state (tfstate) content as a raw JSON string.'
|
|
' Example: \'{"version":4, "resources": [...]}\''
|
|
),
|
|
)
|
|
tfstate_source_group.add_argument(
|
|
'--tfstate-signed-gcs-uri',
|
|
help=(
|
|
'A securely signed Cloud Storage URI pointing to the tfstate file.'
|
|
' Example: `https://storage.googleapis.com/my-bucket/tfstate.json'
|
|
'?x-goog-signature=...`'
|
|
),
|
|
)
|
|
|
|
|
|
def AddServiceAccountFlag(parser):
|
|
"""Adds the service account flag.
|
|
|
|
Args:
|
|
parser: An argparse.ArgumentParser-like object. It is mocked out in tests.
|
|
"""
|
|
parser.add_argument(
|
|
'--service-account',
|
|
help=(
|
|
'The email address of the service account to use for this operation.'
|
|
' Format: `projects/{PROJECT}/serviceAccounts/{EMAIL_ADDRESS}`'
|
|
),
|
|
required=False,
|
|
)
|
|
|
|
|
|
def AddRegisterDeployedApplicationFlags(parser):
|
|
"""Adds flags for the register deployed application command.
|
|
|
|
Args:
|
|
parser: An argparse.ArgumentParser-like object. It is mocked out in tests.
|
|
"""
|
|
concept_parsers.ConceptParser.ForResource(
|
|
'APPLICATION',
|
|
GetApplicationResourceSpec(),
|
|
'The application with which the resources will be registered.',
|
|
required=True).AddToParser(parser)
|
|
AddTfStateSourceFlags(parser)
|
|
AddServiceAccountFlag(parser)
|
|
base.ASYNC_FLAG.AddToParser(parser)
|
|
|
|
|
|
def AddRegisterDeployedResourcesFlags(parser):
|
|
"""Adds flags for the register deployed resources command.
|
|
|
|
Args:
|
|
parser: An argparse.ArgumentParser-like object. It is mocked out in tests.
|
|
"""
|
|
GetSpaceResourceArg(
|
|
arg_name='space',
|
|
help_text='The parent space.',
|
|
).AddToParser(parser)
|
|
parser.add_argument(
|
|
'--apphub-application',
|
|
help=(
|
|
'The name of the AppHub Application. Format:'
|
|
' `projects/{project}/locations/{location}/applications/{application}`'
|
|
),
|
|
required=True,
|
|
)
|
|
AddTfStateSourceFlags(parser)
|
|
AddServiceAccountFlag(parser)
|
|
base.ASYNC_FLAG.AddToParser(parser)
|
|
|
|
|
|
def AddInferConnectionsFlags(parser):
|
|
"""Adds all flags for the infer connections command.
|
|
|
|
Args:
|
|
parser: An argparse.ArgumentParser-like object. It is mocked out in tests.
|
|
"""
|
|
base.ASYNC_FLAG.AddToParser(parser)
|
|
GetSpaceResourceArg('space', 'The space ID.').AddToParser(parser)
|
|
parser.add_argument(
|
|
'--catalog-template-revision-uris',
|
|
type=arg_parsers.ArgList(),
|
|
metavar='CATALOG_TEMPLATE_REVISION_URI',
|
|
help=(
|
|
'A comma-separated list of catalog template revision URIs to infer'
|
|
' connections for. If not provided, the system infers connections'
|
|
' for the latest revisions of all catalog templates in all the'
|
|
' catalogs present in the space.'
|
|
' Format:'
|
|
' `projects/{projectId}/locations/{locationId}/spaces/{spaceId}/'
|
|
'catalogs/{catalogId}/templates/{templateId}/revisions/{revisionId}`'
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
'--use-gemini',
|
|
action='store_true',
|
|
default=False,
|
|
help='Use Gemini to infer connections.',
|
|
)
|
|
|
|
|
|
def GetApplicationTemplateResourceSpec(arg_name='application_template_id'):
|
|
"""Constructs and returns the Resource specification for Application Template."""
|
|
return concepts.ResourceSpec(
|
|
'designcenter.projects.locations.spaces.applicationTemplates',
|
|
resource_name='application_template',
|
|
applicationTemplatesId=concepts.ResourceParameterAttributeConfig(
|
|
name=arg_name, help_text='The ID of the application template.'
|
|
),
|
|
spacesId=SpaceResourceAttributeConfig('space', 'The ID of the space.'),
|
|
locationsId=LocationAttributeConfig(),
|
|
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
|
|
)
|
|
|
|
|
|
def AddApplicationTemplateResourceArg(parser, verb):
|
|
"""Adds the Application Template resource argument to the given parser."""
|
|
concept_parsers.ConceptParser.ForResource(
|
|
'APPLICATION_TEMPLATE',
|
|
GetApplicationTemplateResourceSpec(),
|
|
'The application template {} IaC.'.format(verb),
|
|
required=True).AddToParser(parser)
|
|
|
|
|
|
def GetApplicationResourceSpec(arg_name='application'):
|
|
"""Constructs and returns the Resource specification for Application."""
|
|
return concepts.ResourceSpec(
|
|
'designcenter.projects.locations.spaces.applications',
|
|
resource_name='application',
|
|
applicationsId=concepts.ResourceParameterAttributeConfig(
|
|
name=arg_name, help_text='The ID of the application.'
|
|
),
|
|
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
|
|
locationsId=LocationAttributeConfig(),
|
|
spacesId=SpaceResourceAttributeConfig('space', 'The ID of the space.'),
|
|
)
|
|
|
|
|
|
def AddApplicationResourceArg(parser, verb):
|
|
"""Adds the Application resource argument to the given parser."""
|
|
concept_parsers.ConceptParser.ForResource(
|
|
'APPLICATION',
|
|
GetApplicationResourceSpec(),
|
|
'The application {} IaC.'.format(verb),
|
|
required=True).AddToParser(parser)
|
|
|
|
|
|
def AddImportIacFlags(parser):
|
|
"""Adds flags for import-iac commands to the given parser."""
|
|
source_group = parser.add_mutually_exclusive_group(required=True)
|
|
source_group.add_argument(
|
|
'--gcs-uri',
|
|
help=('The Cloud Storage URI of the Terraform code '
|
|
'(e.g., gs://my-bucket/iac).'),
|
|
)
|
|
source_group.add_argument(
|
|
'--iac-module-from-file',
|
|
type=arg_parsers.FileContents(),
|
|
help=('Path to a local YAML or JSON file containing the IaC module '
|
|
'definition.'),
|
|
)
|
|
parser.add_argument(
|
|
'--allow-partial-import',
|
|
action='store_true',
|
|
default=False,
|
|
help=('If set, partially import valid IaC changes and ignore invalid '
|
|
'ones.'),
|
|
)
|
|
parser.add_argument(
|
|
'--validate-iac',
|
|
action='store_true',
|
|
default=False,
|
|
help='Validate the IaC without performing the import.',
|
|
)
|
|
|
|
|
|
def ParseIacModuleFile(file_contents):
|
|
"""Parses YAML or JSON file contents into a Python dict."""
|
|
try:
|
|
return yaml.load(file_contents)
|
|
except Exception as e:
|
|
raise arg_parsers.ArgumentTypeError(
|
|
'Failed to parse IaC module file: {}'.format(e))
|