# -*- coding: utf-8 -*- # # Copyright 2014 Google LLC. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """This module holds common flags used by the gcloud app commands.""" from __future__ import absolute_import from __future__ import division from __future__ import unicode_literals import argparse from googlecloudsdk.api_lib.app import logs_util from googlecloudsdk.api_lib.storage import storage_util from googlecloudsdk.appengine.api import appinfo from googlecloudsdk.calliope import arg_parsers from googlecloudsdk.calliope import base from googlecloudsdk.command_lib.app import exceptions from googlecloudsdk.core import log from googlecloudsdk.core.docker import constants from googlecloudsdk.core.docker import docker DOMAIN_FLAG = base.Argument( 'domain', help=('A valid domain which may begin with a wildcard, such as: ' '`example.com` or `*.example.com`')) CERTIFICATE_ID_FLAG = base.Argument( 'id', help=('The id of the certificate. This identifier is printed upon' ' creation of a new certificate. Run `{parent_command}' ' list` to view existing certificates.')) LAUNCH_BROWSER = base.Argument( '--launch-browser', action='store_true', default=True, dest='launch_browser', help='Launch a browser if possible. When disabled, only displays the URL.') IGNORE_CERTS_FLAG = base.Argument( '--ignore-bad-certs', action='store_true', default=False, hidden=True, help='THIS ARGUMENT NEEDS HELP TEXT.') IGNORE_FILE_FLAG = base.Argument( '--ignore-file', help='Override the .gcloudignore file and use the specified file instead.') FIREWALL_PRIORITY_FLAG = base.Argument( 'priority', help=('An integer between 1 and 2^32-1 which indicates the evaluation order' ' of rules. Lowest priority rules are evaluated first. The handle ' '`default` may also be used to refer to the final rule at priority' ' 2^32-1 which is always present in a set of rules.')) LEVEL = base.Argument( '--level', help='Filter entries with severity equal to or higher than a given level.', required=False, default='any', choices=logs_util.LOG_LEVELS) LOGS = base.Argument( '--logs', help=('Filter entries from a particular set of logs. Must be a ' 'comma-separated list of log names (request_log, stdout, stderr, ' 'etc).'), required=False, default=logs_util.DEFAULT_LOGS, metavar='APP_LOG', type=arg_parsers.ArgList(min_length=1)) SERVER_FLAG = base.Argument( '--server', hidden=True, help='THIS ARGUMENT NEEDS HELP TEXT.') SERVICE = base.Argument( '--service', '-s', help='Limit to specific service.', required=False) VERSION = base.Argument( '--version', '-v', help='Limit to specific version.', required=False) def AddServiceVersionSelectArgs(parser, short_flags=False): """Add arguments to a parser for selecting service and version. Args: parser: An argparse.ArgumentParser. short_flags: bool, whether to add short flags `-s` and `-v` for service and version respectively. """ parser.add_argument( '--service', *['-s'] if short_flags else [], required=False, help='The service ID.') parser.add_argument( '--version', *['-v'] if short_flags else [], required=False, help='The version ID.') def AddCertificateIdFlag(parser, include_no_cert): """Add the --certificate-id flag to a domain-mappings command.""" certificate_id = base.Argument( '--certificate-id', help=('A certificate id to use for this domain. May not be used on a ' 'domain mapping with automatically managed certificates. Use the ' '`gcloud app ssl-certificates list` to see available certificates ' 'for this app.')) if include_no_cert: group = parser.add_mutually_exclusive_group() certificate_id.AddToParser(group) group.add_argument( '--no-certificate-id', action='store_true', help='Do not associate any certificate with this domain.') else: certificate_id.AddToParser(parser) def AddCertificateManagementFlag(parser): """Adds common flags to a domain-mappings command.""" certificate_argument = base.ChoiceArgument( '--certificate-management', choices=['automatic', 'manual'], help_str=('Type of certificate management. \'automatic\' will provision ' 'an SSL certificate automatically while \'manual\' requires ' 'the user to provide a certificate id to provision.')) certificate_argument.AddToParser(parser) def AddSslCertificateFlags(parser, required): """Add the common flags to an ssl-certificates command.""" parser.add_argument( '--display-name', required=required, help='A display name for this certificate.') parser.add_argument( '--certificate', required=required, metavar='LOCAL_FILE_PATH', help="""\ The file path for the new certificate to upload. Must be in PEM x.509 format including the header and footer. """) parser.add_argument( '--private-key', required=required, metavar='LOCAL_FILE_PATH', help="""\ The file path to a local RSA private key file. The private key must be PEM encoded with header and footer and must be 2048 bits or fewer. """) def AddFirewallRulesFlags(parser, required): """Add the common flags to a firewall-rules command.""" parser.add_argument('--source-range', required=required, help=('An IP address or range in CIDR notation or' ' the ```*``` wildcard to match all traffic.')) parser.add_argument('--action', required=required, choices=['ALLOW', 'DENY'], type=lambda x: x.upper(), help='Allow or deny matched traffic.') parser.add_argument( '--description', help='A text description of the rule.') def ValidateDockerBuildFlag(unused_value): raise argparse.ArgumentTypeError("""\ The --docker-build flag no longer exists. Docker images are now built remotely using Google Container Builder. To run a Docker build on your own host, you can run: docker build -t gcr.io// . gcloud docker push gcr.io// gcloud app deploy --image-url=gcr.io// If you don't already have a Dockerfile, you must run: gcloud beta app gen-config first to get one. """) DOCKER_BUILD_FLAG = base.Argument( '--docker-build', hidden=True, help='THIS ARGUMENT NEEDS HELP TEXT.', type=ValidateDockerBuildFlag) LOG_SEVERITIES = ['debug', 'info', 'warning', 'error', 'critical'] def GetCodeBucket(app, project): """Gets a bucket reference for a Cloud Build. Args: app: App resource for this project project: str, The name of the current project. Returns: storage_util.BucketReference, The bucket to use. """ # Attempt to retrieve the default appspot bucket, if one can be created. log.debug('No bucket specified, retrieving default bucket.') if not app.codeBucket: raise exceptions.DefaultBucketAccessError(project) return storage_util.BucketReference.FromUrl(app.codeBucket) VERSION_TYPE = arg_parsers.RegexpValidator( appinfo.MODULE_VERSION_ID_RE_STRING, 'May only contain lowercase letters, digits, and hyphens. ' 'Must begin and end with a letter or digit. Must not exceed 63 characters.') def ValidateImageUrl(image_url, services): """Check the user-provided image URL. Ensures that: - it is consistent with the services being deployed (there must be exactly one) - it is an image in a supported Docker registry Args: image_url: str, the URL of the image to deploy provided by the user services: list, the services to deploy Raises: MultiDeployError: if image_url is provided and more than one service is being deployed docker.UnsupportedRegistryError: if image_url is provided and does not point to one of the supported registries """ # Validate the image url if provided, and ensure there is a single service # being deployed. if image_url is None: return if len(services) != 1: raise exceptions.MultiDeployError() for registry in constants.ALL_SUPPORTED_REGISTRIES: if image_url.startswith(registry): return raise docker.UnsupportedRegistryError(image_url)