Beispiel #1
0
    def _DetermineImageFromArgs(self, args):
        """Gets the image to use for the build, given the user args.

    Args:
      args: argsparse object from the DeployGKE command.

    Returns:
      Full image string representation.
    """
        if args.tag:
            if (properties.VALUES.builds.check_tag.GetBool()
                    and 'gcr.io/' not in args.tag):
                raise c_exceptions.InvalidArgumentException(
                    '--tag',
                    'Tag value must be in the gcr.io/* or *.gcr.io/* namespace.'
                )
            return args.tag

        elif args.image:
            if (properties.VALUES.builds.check_tag.GetBool()
                    and 'gcr.io/' not in args.image):
                raise c_exceptions.InvalidArgumentException(
                    '--image',
                    'Image value must be in the gcr.io/* or *.gcr.io/* namespace.'
                )
            return args.image

        else:  # Default tag
            if args.app_name:
                default_name = args.app_name
            elif os.path.isdir(
                    args.source):  # I.e., the source is not a tarball
                default_name = os.path.basename(os.path.abspath(args.source))
            else:
                raise c_exceptions.OneOfArgumentsRequiredException(
                    ['--app-name', '--tag'],
                    'Cannot resolve default container image. Provide an app name with '
                    '--app-name to use as the container image, or provide a full '
                    'tag using --tag.')

            if args.app_version:
                default_tag = args.app_version
            elif git.IsGithubRepository(
                    args.source) and not git.HasPendingChanges(args.source):
                default_tag = git.GetGitHeadRevision(args.source)
                if not default_tag:
                    raise c_exceptions.OneOfArgumentsRequiredException(
                        ['--app-version', '--tag'],
                        'Cannot resolve default container tag using the Git commit SHA. '
                        'Provide an app version with --app-version to use as the '
                        'container tag, or provide a full tag using --tag.')
            else:
                raise c_exceptions.OneOfArgumentsRequiredException(
                    ['--app-version', '--tag'],
                    'Cannot resolve default container tag. '
                    'Provide an app version with --app-version to use as the '
                    'container tag, or provide a full tag using --tag.')

            return 'gcr.io/$PROJECT_ID/{name}:{tag}'.format(name=default_name,
                                                            tag=default_tag)
Beispiel #2
0
    def _DetermineImageFromArgs(self, args):
        """Gets the image to use for the build, given the user args.

    Args:
      args: argsparse object from the DeployGKE command.

    Returns:
      Full image string representation.
    """
        if args.tag_default:
            if args.app_name:
                default_name = args.app_name
            elif os.path.isdir(args.source):
                default_name = os.path.basename(os.path.abspath(args.source))
            else:
                raise c_exceptions.InvalidArgumentException(
                    '--tag-default',
                    'No default container image name available. Provide an '
                    'app name with --app-name, or provide a valid --tag.')

            if args.app_version:
                default_tag = args.app_version
            elif git.IsGithubRepository(
                    args.source) and not git.HasPendingChanges(args.source):
                default_tag = git.GetShortGitHeadRevision(args.source)
                if not default_tag:
                    raise c_exceptions.InvalidArgumentException(
                        '--tag-default',
                        'No default tag available, no commit sha at HEAD of source '
                        'repository available for tag. Provide an app version '
                        'with --app-version, or provide a valid --tag.')
            else:
                raise c_exceptions.InvalidArgumentException(
                    '--tag-default',
                    'No default container image tag available. Provide an app '
                    'version with --app-version, or provide a valid --tag.')

            return 'gcr.io/$PROJECT_ID/{name}:{tag}'.format(name=default_name,
                                                            tag=default_tag)

        if args.tag:
            if (properties.VALUES.builds.check_tag.GetBool()
                    and 'gcr.io/' not in args.tag):
                raise c_exceptions.InvalidArgumentException(
                    '--tag',
                    'Tag value must be in the gcr.io/* or *.gcr.io/* namespace.'
                )
            return args.tag

        if args.image:
            if (properties.VALUES.builds.check_tag.GetBool()
                    and 'gcr.io/' not in args.image):
                raise c_exceptions.InvalidArgumentException(
                    '--image',
                    'Image value must be in the gcr.io/* or *.gcr.io/* namespace.'
                )
            return args.image
Beispiel #3
0
    def Run(self, args):
        """This is what gets called when the user runs this command.

    Args:
      args: an argparse namespace. All the arguments that were provided to this
        command invocation.

    Returns:
      Some value that we want to have printed later.

    Raises:
      FailedDeployException: If the build is completed and not 'SUCCESS'.
    """

        if not args.source and not args.no_source:
            raise c_exceptions.InvalidArgumentException(
                '--no-source', 'To omit source, use the --no-source flag.')

        if args.no_source:
            if args.tag:
                raise c_exceptions.RequiredArgumentException(
                    'SOURCE', 'Source is required to build container image.')
            if args.config:
                raise c_exceptions.RequiredArgumentException(
                    'SOURCE',
                    'Source is required when specifying --config because it is a '
                    'relative path in the source directory.')

        do_build_and_push = args.image is None
        if not do_build_and_push and not args.config:
            args.no_source = True

        image = self._DetermineImageFromArgs(args)

        # Determine app_name
        if args.app_name:
            app_name = args.app_name
        else:
            app_name = self._ImageName(image)

        # Determine app_version
        app_version = None
        image_has_tag = '@' not in image and ':' in image
        if args.app_version:
            app_version = args.app_version
        elif image_has_tag:
            app_version = image.split(':')[-1]  # Set version to tag
        elif args.source:
            if git.IsGithubRepository(
                    args.source) and not git.HasPendingChanges(args.source):
                commit_sha = git.GetGitHeadRevision(args.source)
                if commit_sha:
                    app_version = commit_sha

        # Validate expose
        if args.expose and args.expose < 0:
            raise c_exceptions.InvalidArgumentException(
                '--expose', 'port number is invalid')

        # Determine gcs_staging_dir_bucket and gcs_staging_dir_object
        if args.gcs_staging_dir is None:
            gcs_staging_dir_bucket = staging_bucket_util.GetDefaultStagingBucket(
            )
            gcs_staging_dir_object = 'deploy'
        else:
            try:
                gcs_staging_dir_ref = resources.REGISTRY.Parse(
                    args.gcs_staging_dir, collection='storage.objects')
                gcs_staging_dir_object = gcs_staging_dir_ref.object
            except resources.WrongResourceCollectionException:
                gcs_staging_dir_ref = resources.REGISTRY.Parse(
                    args.gcs_staging_dir, collection='storage.buckets')
                gcs_staging_dir_object = None
            gcs_staging_dir_bucket = gcs_staging_dir_ref.bucket

        gcs_client = storage_api.StorageClient()
        try:
            gcs_client.CreateBucketIfNotExists(
                gcs_staging_dir_bucket,
                check_ownership=args.gcs_staging_dir is None)
        except storage_api.BucketInWrongProjectError:
            # If we're using the default bucket but it already exists in a different
            # project, then it could belong to a malicious attacker (b/33046325).
            raise c_exceptions.RequiredArgumentException(
                '--gcs-staging-dir',
                'A bucket with name {} already exists and is owned by '
                'another project. Specify a bucket using '
                '--gcs-staging-dir.'.format(gcs_staging_dir_bucket))

        if gcs_staging_dir_object:
            gcs_config_staging_path = '{}/{}/config'.format(
                gcs_staging_dir_bucket, gcs_staging_dir_object)
        else:
            gcs_config_staging_path = gcs_staging_dir_bucket

        if not args.no_source:
            staged_source = self._StageSource(args.source,
                                              gcs_staging_dir_bucket,
                                              gcs_staging_dir_object)
        else:
            staged_source = None

        messages = cloudbuild_util.GetMessagesModule()
        build_config = build_util.CreateBuild(
            messages,
            build_timeout=properties.VALUES.builds.timeout.Get(),
            build_and_push=do_build_and_push,
            staged_source=staged_source,
            image=image,
            dockerfile_path='Dockerfile',
            app_name=app_name,
            app_version=app_version,
            config_path=args.config,
            namespace=args.namespace,
            expose_port=args.expose,
            gcs_config_staging_path=gcs_config_staging_path,
            cluster=args.cluster,
            location=args.location,
            build_tags=([] if not args.app_name else [args.app_name]))

        client = cloudbuild_util.GetClientInstance()
        self._SubmitBuild(client, messages, build_config,
                          gcs_config_staging_path, args.config is None,
                          args.async_)
Beispiel #4
0
    def _CreateBuildFromArgs(self, args, messages):
        """Creates the Cloud Build config from the arguments.

    Args:
      args: argsparse object from the DeployGKE command.
      messages: Cloud Build messages module.

    Returns:
      messages.Build, the Cloud Build config.
    """
        build = messages.Build(steps=[], tags=_CLOUD_BUILD_DEPLOY_TAGS)

        if args.app_name:
            build.tags.append(args.app_name)

        build_timeout = properties.VALUES.builds.timeout.Get()

        if build_timeout is not None:
            try:
                # A bare number is interpreted as seconds.
                build_timeout_secs = int(build_timeout)
            except ValueError:
                build_timeout_duration = times.ParseDuration(build_timeout)
                build_timeout_secs = int(build_timeout_duration.total_seconds)
            build.timeout = six.text_type(build_timeout_secs) + 's'

        if args.source is None:
            if args.tag or args.tag_default:
                raise c_exceptions.RequiredArgumentException(
                    'SOURCE',
                    'required to build container image provided by --tag or --tag-default.'
                )
            if args.config:
                raise c_exceptions.RequiredArgumentException(
                    'SOURCE',
                    'required because --config is a relative path in the '
                    'source directory.')

        if args.source and args.image and not args.config:
            raise c_exceptions.InvalidArgumentException(
                'SOURCE', 'Source should not be provided when no Kubernetes '
                'configs and no docker builds are required.')

        if args.tag_default:
            if args.app_name:
                default_name = args.app_name
            elif os.path.isdir(args.source):
                default_name = os.path.basename(os.path.abspath(args.source))
            else:
                raise c_exceptions.InvalidArgumentException(
                    '--tag-default',
                    'No default container image name available. Please provide an '
                    'app name with --app-name, or provide a valid --tag.')

            if args.app_version:
                default_tag = args.app_version
            elif git.IsGithubRepository(
                    args.source) and not git.HasPendingChanges(args.source):
                default_tag = git.GetShortGitHeadRevision(args.source)
                if not default_tag:
                    raise c_exceptions.InvalidArgumentException(
                        '--tag-default',
                        'No default tag available, no commit sha at HEAD of source '
                        'repository available for tag. Please provide an app version '
                        'with --app-version, or provide a valid --tag.')
            else:
                raise c_exceptions.InvalidArgumentException(
                    '--tag-default',
                    'No default container image tag available. Please provide an app '
                    'version with --app-version, or provide a valid --tag.')

            args.tag = 'gcr.io/$PROJECT_ID/{name}:{tag}'.format(
                name=default_name, tag=default_tag)

        if args.tag:
            if (properties.VALUES.builds.check_tag.GetBool()
                    and 'gcr.io/' not in args.tag):
                raise c_exceptions.InvalidArgumentException(
                    '--tag',
                    'Tag value must be in the gcr.io/* or *.gcr.io/* namespace.'
                )
            build.steps.append(
                messages.BuildStep(
                    name='gcr.io/cloud-builders/docker',
                    args=[
                        'build', '--network', 'cloudbuild', '--no-cache', '-t',
                        args.tag, '.'
                    ],
                ))
            build.steps.append(
                messages.BuildStep(name='gcr.io/cloud-builders/docker',
                                   args=['push', args.tag]))

        if args.image and (properties.VALUES.builds.check_tag.GetBool()
                           and 'gcr.io/' not in args.image):
            raise c_exceptions.InvalidArgumentException(
                '--image',
                'Image value must be in the gcr.io/* or *.gcr.io/* namespace.')

        if args.expose and args.expose < 0:
            raise c_exceptions.InvalidArgumentException(
                'EXPOSE', 'port number is invalid')

        self._StageSourceAndConfigFiles(args, messages, build)

        image = args.image if args.image else args.tag

        deploy_step = messages.BuildStep(
            name=_GKE_DEPLOY_PROD,
            args=[
                'run',
                '--image={}'.format(image),
                '--cluster={}'.format(args.cluster),
                '--location={}'.format(args.location),
                '--namespace={}'.format(args.namespace),
                '--output=output',
                '--label=gcb-build-id=$BUILD_ID',
            ],
        )
        image_name = image.split('/')[-1]
        image_with_digest = image_name.split('@')
        image_with_tag = image_name.split(':')
        if args.app_name:
            deploy_step.args.append('--app={}'.format(args.app_name))
        else:
            if len(image_with_digest) > 1:
                deploy_step.args.append('--app={}'.format(
                    image_with_digest[0]))
            else:
                deploy_step.args.append('--app={}'.format(image_with_tag[0]))

        if args.app_version:
            deploy_step.args.append('--version={}'.format(args.app_version))
        elif len(image_with_digest) == 1 and len(image_with_tag) > 1:
            deploy_step.args.append('--version={}'.format(image_with_tag[1]))
        elif args.source:
            if git.IsGithubRepository(
                    args.source) and not git.HasPendingChanges(args.source):
                short_sha = git.GetShortGitHeadRevision(args.source)
                if short_sha:
                    deploy_step.args.append('--version={}'.format(short_sha))

        if args.config:
            deploy_step.args.append('--filename={}'.format(args.config))
        if args.expose:
            deploy_step.args.append('--expose={}'.format(args.expose))
        if build.timeout is not None:
            deploy_step.args.append('--timeout={}'.format(build.timeout))

        # Append before the gsutil copy step
        build.steps.insert(-1, deploy_step)
        return build