647 lines
19 KiB
Python
647 lines
19 KiB
Python
# -*- 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.
|
|
"""Common flags for artifacts print-settings commands."""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import unicode_literals
|
|
|
|
import sys
|
|
import textwrap
|
|
|
|
from googlecloudsdk.api_lib.artifacts import exceptions as ar_exceptions
|
|
from googlecloudsdk.calliope import actions
|
|
from googlecloudsdk.calliope import arg_parsers
|
|
from googlecloudsdk.calliope import base
|
|
from googlecloudsdk.calliope.concepts import concepts
|
|
from googlecloudsdk.calliope.concepts import deps
|
|
from googlecloudsdk.command_lib.util.concepts import concept_parsers
|
|
from googlecloudsdk.core import properties
|
|
|
|
_PACKAGE_TYPE_CHOICES = {
|
|
'MAVEN': 'Maven package.',
|
|
'GO': 'Go standard library and third party packages.',
|
|
'NPM': 'NPM package.',
|
|
'PYTHON': 'Python package.',
|
|
'RUST': 'Rust package.',
|
|
'RUBYGEMS': 'RubyGems package.',
|
|
'COMPOSER': 'PHP Composer package.',
|
|
'NUGET': 'NuGet package.',
|
|
}
|
|
|
|
_EXPERIMENTAL_PACKAGE_TYPE_CHOICES = {}
|
|
|
|
|
|
def RepoAttributeConfig():
|
|
fts = [deps.PropertyFallthrough(properties.VALUES.artifacts.repository)]
|
|
return concepts.ResourceParameterAttributeConfig(
|
|
name='repository',
|
|
help_text='Repository of the {resource}.',
|
|
fallthroughs=fts)
|
|
|
|
|
|
def LocationAttributeConfig():
|
|
fts = [deps.PropertyFallthrough(properties.VALUES.artifacts.location)]
|
|
return concepts.ResourceParameterAttributeConfig(
|
|
name='location',
|
|
help_text='Location of the {resource}.',
|
|
fallthroughs=fts)
|
|
|
|
|
|
def PackageAttributeConfig():
|
|
return concepts.ResourceParameterAttributeConfig(
|
|
name='package',
|
|
help_text='Package of the {resource}.')
|
|
|
|
|
|
def GetRepoResourceSpec():
|
|
return concepts.ResourceSpec(
|
|
'artifactregistry.projects.locations.repositories',
|
|
resource_name='repository',
|
|
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
|
|
locationsId=LocationAttributeConfig(),
|
|
repositoriesId=RepoAttributeConfig())
|
|
|
|
|
|
def GetBetaRepoResourceSpec():
|
|
return concepts.ResourceSpec(
|
|
'artifactregistry.projects.locations.repositories',
|
|
resource_name='repository',
|
|
api_version='v1beta1',
|
|
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
|
|
locationsId=LocationAttributeConfig(),
|
|
repositoriesId=RepoAttributeConfig())
|
|
|
|
|
|
def GetLocationResourceSpec():
|
|
return concepts.ResourceSpec(
|
|
'artifactregistry.projects.locations',
|
|
resource_name='location',
|
|
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
|
|
locationsId=LocationAttributeConfig())
|
|
|
|
|
|
def GetFileResourceSpec():
|
|
return concepts.ResourceSpec(
|
|
'artifactregistry.projects.locations.repositories.files',
|
|
resource_name='file',
|
|
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
|
|
locationsId=LocationAttributeConfig(),
|
|
repositoriesId=RepoAttributeConfig())
|
|
|
|
|
|
def GetAttachmentResourceSpec():
|
|
return concepts.ResourceSpec(
|
|
'artifactregistry.projects.locations.repositories.attachments',
|
|
resource_name='attachment',
|
|
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
|
|
locationsId=LocationAttributeConfig(),
|
|
repositoriesId=RepoAttributeConfig(),
|
|
)
|
|
|
|
|
|
def GetPackageResourceSpec():
|
|
return concepts.ResourceSpec(
|
|
'artifactregistry.projects.locations.repositories.packages',
|
|
resource_name='package',
|
|
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
|
|
locationsId=LocationAttributeConfig(),
|
|
repositoriesId=RepoAttributeConfig(),
|
|
)
|
|
|
|
|
|
def GetVersionResourceSpec():
|
|
return concepts.ResourceSpec(
|
|
'artifactregistry.projects.locations.repositories.packages.versions',
|
|
resource_name='version',
|
|
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
|
|
locationsId=LocationAttributeConfig(),
|
|
repositoriesId=RepoAttributeConfig(),
|
|
packagesId=PackageAttributeConfig(),
|
|
)
|
|
|
|
|
|
def GetTagResourceSpec():
|
|
return concepts.ResourceSpec(
|
|
'artifactregistry.projects.locations.repositories.packages.tags',
|
|
resource_name='tag',
|
|
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
|
|
locationsId=LocationAttributeConfig(),
|
|
repositoriesId=RepoAttributeConfig(),
|
|
packagesId=PackageAttributeConfig(),
|
|
)
|
|
|
|
|
|
def GetScopeFlag():
|
|
return base.Argument(
|
|
'--scope',
|
|
help=('The scope to associate with the Artifact Registry registry. '
|
|
'If not specified, Artifact Registry is set as the default '
|
|
'registry.'))
|
|
|
|
|
|
def GetImagePathOptionalArg():
|
|
"""Gets IMAGE_PATH optional positional argument."""
|
|
help_txt = textwrap.dedent("""\
|
|
An Artifact Registry repository or a container image.
|
|
If not specified, default config values are used.
|
|
|
|
A valid docker repository has the format of
|
|
LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY-ID
|
|
|
|
A valid image has the format of
|
|
LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY-ID/IMAGE_PATH
|
|
""")
|
|
return base.Argument('IMAGE_PATH', help=help_txt, nargs='?')
|
|
|
|
|
|
def GetImageRequiredArg():
|
|
"""Gets IMAGE required positional argument."""
|
|
help_txt = textwrap.dedent("""\
|
|
A container image.
|
|
|
|
A valid container image has the format of
|
|
LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY-ID/IMAGE
|
|
|
|
A valid container image that can be referenced by tag or digest, has the format of
|
|
LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY-ID/IMAGE:tag
|
|
LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY-ID/IMAGE@sha256:digest
|
|
""")
|
|
return base.Argument('IMAGE', help=help_txt)
|
|
|
|
|
|
def GetDockerImageRequiredArg():
|
|
help_txt = textwrap.dedent("""\
|
|
Docker image - The container image that you want to tag.
|
|
|
|
A valid container image can be referenced by tag or digest, has the format of
|
|
LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY-ID/IMAGE:tag
|
|
LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY-ID/IMAGE@sha256:digest
|
|
""")
|
|
return base.Argument('DOCKER_IMAGE', help=help_txt)
|
|
|
|
|
|
def GetTagRequiredArg():
|
|
help_txt = textwrap.dedent("""\
|
|
Image tag - The container image tag.
|
|
|
|
A valid Docker tag has the format of
|
|
LOCATION-docker.pkg.dev/PROJECT-ID/REPOSITORY-ID/IMAGE:tag
|
|
""")
|
|
return base.Argument('DOCKER_TAG', help=help_txt)
|
|
|
|
|
|
def GetRepoFlag():
|
|
return concept_parsers.ConceptParser.ForResource(
|
|
'--repository',
|
|
GetRepoResourceSpec(),
|
|
('The Artifact Registry repository. If not specified, '
|
|
'the current artifacts/repository is used.'),
|
|
required=False)
|
|
|
|
|
|
def GetRequiredRepoFlag():
|
|
return concept_parsers.ConceptParser.ForResource(
|
|
'--repository',
|
|
GetRepoResourceSpec(),
|
|
('The Artifact Registry repository. If not specified, '
|
|
'the current artifacts/repository is used.'),
|
|
required=True)
|
|
|
|
|
|
def GetLocationFlag():
|
|
return concept_parsers.ConceptParser.ForResource(
|
|
'--location',
|
|
GetLocationResourceSpec(),
|
|
('The Artifact Registry repository location. If not specified, '
|
|
'the current artifacts/location is used.'),
|
|
required=True)
|
|
|
|
|
|
def GetRequiredFileFlag():
|
|
return concept_parsers.ConceptParser.ForResource(
|
|
'file',
|
|
GetFileResourceSpec(),
|
|
'The Artifact Registry file name.',
|
|
required=True,
|
|
)
|
|
|
|
|
|
def GetRequiredAttachmentFlag():
|
|
return concept_parsers.ConceptParser.ForResource(
|
|
'attachment',
|
|
GetAttachmentResourceSpec(),
|
|
'The Artifact Registry attachment name.',
|
|
required=True,
|
|
)
|
|
|
|
|
|
def GetOptionalAttachmentFlag():
|
|
return concept_parsers.ConceptParser.ForResource(
|
|
'attachment',
|
|
GetAttachmentResourceSpec(),
|
|
'The Artifact Registry attachment name.',
|
|
required=False,
|
|
)
|
|
|
|
|
|
def GetRequiredVersionFlag():
|
|
return concept_parsers.ConceptParser.ForResource(
|
|
'version',
|
|
GetVersionResourceSpec(),
|
|
'The Artifact Registry version name.',
|
|
required=True,
|
|
)
|
|
|
|
|
|
def GetRequiredTagFlag():
|
|
return concept_parsers.ConceptParser.ForResource(
|
|
'tag',
|
|
GetTagResourceSpec(),
|
|
'The Artifact Registry tag name.',
|
|
required=True,
|
|
)
|
|
|
|
|
|
def GetAllowOverwriteFlag():
|
|
return base.Argument(
|
|
'--allow-overwrite',
|
|
action='store_true',
|
|
default=False,
|
|
help='If specified, the command overwrites an existing file',
|
|
)
|
|
|
|
|
|
def GetRepoArg():
|
|
return concept_parsers.ConceptParser.ForResource(
|
|
'repository',
|
|
GetRepoResourceSpec(),
|
|
('The Artifact Registry repository. If not specified, '
|
|
'the current artifacts/repository is used.'),
|
|
required=True)
|
|
|
|
|
|
def GetRepoArgFromBeta():
|
|
return concept_parsers.ConceptParser.ForResource(
|
|
'repository',
|
|
GetBetaRepoResourceSpec(),
|
|
('The Artifact Registry repository. If not specified, '
|
|
'the current artifacts/repository is used.'),
|
|
required=True)
|
|
|
|
|
|
def GetOptionalLocationFlag():
|
|
return concept_parsers.ConceptParser.ForResource(
|
|
'--location',
|
|
GetLocationResourceSpec(),
|
|
('The Artifact Registry repository location. You can also set '
|
|
'--location=all to list repositories across all locations. '
|
|
'If you omit this flag, the default location is used if you set the '
|
|
'artifacts/location property. Otherwise, omitting this flag '
|
|
'lists repositories across all locations.'),
|
|
required=False)
|
|
|
|
|
|
def GetOptionalAALocationFlag():
|
|
return base.Argument(
|
|
'--location',
|
|
help=('If specified, all requests to Artifact Analysis for occurrences'
|
|
' will go to location specified'),
|
|
required=False,
|
|
)
|
|
|
|
|
|
def GetIncludeTagsFlag():
|
|
return base.Argument(
|
|
'--include-tags',
|
|
help=(
|
|
'If specified, tags associated with each image digest are displayed'
|
|
' up to a maximum of 100 tags per version.'
|
|
),
|
|
action='store_true',
|
|
required=False,
|
|
)
|
|
|
|
|
|
def GetDeleteTagsFlag():
|
|
return base.Argument(
|
|
'--delete-tags',
|
|
help='If specified, all tags associated with the image are deleted.',
|
|
action='store_true',
|
|
required=False)
|
|
|
|
|
|
def GetGCRDomainArg():
|
|
return base.Argument(
|
|
'DOMAIN',
|
|
help=(
|
|
'A Container Registry domain. Valid values are: [gcr.io, asia.gcr.io,'
|
|
' eu.gcr.io, us.gcr.io]'
|
|
),
|
|
)
|
|
|
|
|
|
def GetJsonKeyFlag(tool):
|
|
"""Gets Json Key Flag text based on specified tool."""
|
|
if tool == 'pypi' or tool == 'python':
|
|
return base.Argument(
|
|
'--json-key',
|
|
help=('Path to service account JSON key. If not specified, '
|
|
'output returns either credentials for an active service account '
|
|
'or a placeholder for the current user account.'))
|
|
elif tool in ('gradle', 'maven', 'npm'):
|
|
return base.Argument(
|
|
'--json-key',
|
|
help=('Path to service account JSON key. If not specified, '
|
|
'current active service account credentials or a placeholder for '
|
|
'gcloud credentials is used.'))
|
|
else:
|
|
raise ar_exceptions.ArtifactRegistryError(
|
|
'Invalid tool type: {}'.format(tool))
|
|
|
|
|
|
def GetShowAllMetadataFlag():
|
|
return base.Argument(
|
|
'--show-all-metadata',
|
|
action='store_true',
|
|
help='Include all metadata in the output. Metadata will be grouped by '
|
|
'Grafeas kind, with an additional section for intoto provenance '
|
|
'metadata.')
|
|
|
|
|
|
def GetShowDeploymentFlag():
|
|
return base.Argument(
|
|
'--show-deployment',
|
|
action='store_true',
|
|
help='Include deployment metadata in the output.')
|
|
|
|
|
|
def GetShowImageBasisFlag():
|
|
return base.Argument(
|
|
'--show-image-basis',
|
|
action='store_true',
|
|
help='Include base image metadata in the output.')
|
|
|
|
|
|
def GetShowPackageVulnerabilityFlag():
|
|
return base.Argument(
|
|
'--show-package-vulnerability',
|
|
action='store_true',
|
|
help='Include vulnerability metadata in the output.')
|
|
|
|
|
|
def GetShowBuildDetailsFlag():
|
|
return base.Argument(
|
|
'--show-build-details',
|
|
action='store_true',
|
|
help='Include build metadata in the output.')
|
|
|
|
|
|
def GetShowSbomReferencesFlag():
|
|
return base.Argument(
|
|
'--show-sbom-references',
|
|
action='store_true',
|
|
help='Include SBOM metadata in the output.')
|
|
|
|
|
|
def GetMetadataFilterFlag():
|
|
return base.Argument(
|
|
'--metadata-filter',
|
|
help=('Additional filter to fetch metadata for a given '
|
|
'qualified image reference.'))
|
|
|
|
|
|
def GetShowOccurrencesFlag():
|
|
return base.Argument(
|
|
'--show-occurrences',
|
|
action='store_true',
|
|
help='Show summaries of the various occurrence types.')
|
|
|
|
|
|
def GetShowOccurrencesFromFlag():
|
|
return base.Argument(
|
|
'--show-occurrences-from',
|
|
type=arg_parsers.BoundedInt(1, sys.maxsize, unlimited=True),
|
|
default=10,
|
|
help=('The number of the most recent images for which to '
|
|
'summarize occurrences.'))
|
|
|
|
|
|
def GetOccurrenceFilterFlag():
|
|
return base.Argument(
|
|
'--occurrence-filter',
|
|
default=(
|
|
'kind="BUILD" OR kind="IMAGE" OR kind="DISCOVERY" OR'
|
|
' kind="SBOM_REFERENCE"'
|
|
),
|
|
help='A filter for the occurrences which will be summarized.')
|
|
|
|
|
|
def GetVulnerabilitiesOccurrenceFilterFlag():
|
|
return base.Argument(
|
|
'--occurrence-filter',
|
|
help='A filter for the occurrences which will be summarized. See link for '
|
|
'officially supported filters: '
|
|
'https://cloud.google.com/container-analysis/docs/os-scanning-automatically#filtering')
|
|
|
|
|
|
def GetShowProvenanceFlag():
|
|
return base.Argument(
|
|
'--show-provenance',
|
|
action='store_true',
|
|
help='Include intoto provenance metadata in the output, in the '
|
|
'provenance_summary section. To see all build metadata in the output, '
|
|
'use --show-all-metadata or --show-build-details.')
|
|
|
|
|
|
def GetResourceURIArg():
|
|
"""Gets RESOURCE_URI required positional argument."""
|
|
return base.Argument(
|
|
'RESOURCE_URI',
|
|
help=('A container image in a Google Cloud registry (Artifact Registry '
|
|
'or Container Registry), or a local container image.'))
|
|
|
|
|
|
def GetListURIArg():
|
|
"""Gets list uri required positional argument."""
|
|
return base.Argument(
|
|
'URI',
|
|
help=('An URI identifying a container image or package in '
|
|
'Artifact Registry or Google Cloud Registry.'))
|
|
|
|
|
|
def GetRemoteFlag():
|
|
return base.Argument(
|
|
'--remote',
|
|
action='store_true',
|
|
default=False,
|
|
help=('Whether the container image is located remotely or '
|
|
'on your local machine.'))
|
|
|
|
|
|
def GetOnDemandScanningLocationFlag():
|
|
return base.Argument(
|
|
'--location',
|
|
choices={
|
|
'us': 'Perform analysis in the US',
|
|
'europe': 'Perform analysis in Europe',
|
|
'asia': 'Perform analysis in Asia',
|
|
},
|
|
default='us',
|
|
help=('The API location in which to perform package analysis. Consider '
|
|
'choosing a location closest to where you are located. Proximity '
|
|
'to the container image does not affect response time.'))
|
|
|
|
|
|
def GetOnDemandScanningFakeExtractionFlag():
|
|
return base.Argument(
|
|
'--fake-extraction',
|
|
action='store_true',
|
|
default=False,
|
|
hidden=True,
|
|
help=('Whether to use fake packages/versions instead of performing '
|
|
'extraction. This flag is for test purposes only.'))
|
|
|
|
|
|
def GetAdditionalPackageTypesFlag():
|
|
return base.Argument(
|
|
'--additional-package-types',
|
|
action=actions.DeprecationAction(
|
|
'--additional-package-types',
|
|
warn=(
|
|
'This flag is deprecated as scanning for all package types is '
|
|
'now the default. To skip scanning for specific package types, '
|
|
'use --skip-package-types.'
|
|
),
|
|
),
|
|
type=arg_parsers.ArgList(
|
|
choices=_PACKAGE_TYPE_CHOICES,
|
|
element_type=lambda package_type: package_type.upper(),
|
|
),
|
|
metavar='ADDITIONAL_PACKAGE_TYPES',
|
|
help=(
|
|
'A comma-separated list of package types to scan in addition to OS'
|
|
' packages.'
|
|
),
|
|
)
|
|
|
|
|
|
def GetExperimentalPackageTypesFlag():
|
|
return base.Argument(
|
|
'--experimental-package-types',
|
|
action=actions.DeprecationAction(
|
|
'--experimental-package-types',
|
|
warn=(
|
|
'This flag is deprecated as scanning for all package types is '
|
|
'now the default. To skip scanning for specific package types, '
|
|
'use --skip-package-types.'
|
|
),
|
|
),
|
|
type=arg_parsers.ArgList(
|
|
choices=_EXPERIMENTAL_PACKAGE_TYPE_CHOICES,
|
|
element_type=lambda package_type: package_type.upper(),
|
|
),
|
|
hidden=True,
|
|
metavar='EXPERIMENTAL_PACKAGE_TYPES',
|
|
help=(
|
|
'A comma-separated list of experimental package types to scan in'
|
|
' addition to OS packages and officially supported third party'
|
|
' packages.'
|
|
),
|
|
)
|
|
|
|
|
|
def GetSkipPackageTypesFlag():
|
|
return base.Argument(
|
|
'--skip-package-types',
|
|
type=arg_parsers.ArgList(
|
|
choices=_PACKAGE_TYPE_CHOICES,
|
|
element_type=lambda package_type: package_type.upper(),
|
|
),
|
|
metavar='SKIP_PACKAGE_TYPES',
|
|
help='A comma-separated list of package types to skip when scanning.',
|
|
)
|
|
|
|
|
|
def GetVerboseErrorsFlag():
|
|
return base.Argument(
|
|
'--verbose-errors',
|
|
action='store_true',
|
|
default=False,
|
|
hidden=True,
|
|
help=('Log internal errors.'))
|
|
|
|
|
|
def GetSkipExistingFlag():
|
|
return base.Argument(
|
|
'--skip-existing',
|
|
action='store_true',
|
|
default=False,
|
|
help=(
|
|
'If specified, skip uploading files that already exist in the'
|
|
' repository, and continue to upload the remaining files.'
|
|
),
|
|
)
|
|
|
|
|
|
def GetChunkSize():
|
|
return base.Argument(
|
|
'--chunk-size',
|
|
help=(
|
|
'If specified, the chunk size (bytes) to use for downloading the'
|
|
' package.'
|
|
),
|
|
)
|
|
|
|
|
|
def GetPlainRepoFlag():
|
|
"""Gets a simple --repository flag."""
|
|
return base.Argument(
|
|
'--repository',
|
|
help=(
|
|
'The Artifact Registry repository. If not specified, the current'
|
|
' artifacts/repository is used.'
|
|
),
|
|
)
|
|
|
|
|
|
def GetPlainLocationFlag():
|
|
"""Gets a simple --location flag."""
|
|
return base.Argument(
|
|
'--location',
|
|
help=(
|
|
'The Artifact Registry repository location. If not specified, the'
|
|
' current artifacts/location is used.'
|
|
),
|
|
)
|
|
|
|
|
|
def GetSeverityFlag():
|
|
"""Gets the --severity flag for platform logs."""
|
|
return base.Argument(
|
|
'--severity',
|
|
type=lambda x: x.upper(), # Ensure value is uppercase for enum
|
|
choices=[
|
|
'INFO',
|
|
'ERROR',
|
|
],
|
|
help=(
|
|
'The minimum severity level of logs to generate. This flag is only '
|
|
'applicable when using --enable. Values are case-insensitive.'
|
|
'If omitted when --enable is true, it defaults to logging all '
|
|
'severities.'
|
|
),
|
|
)
|