Exemple #1
0
def CheckServiceAccountPermission(unused_repo_ref, repo_args, request):
    """Checks and grants key encrypt/decrypt permission for service account.

  Checks if Artifact Registry service account has encrypter/decrypter or owner
  role for the given key. If not, prompts users to grant key encrypter/decrypter
  permission to the service account. Operation would fail if users do not grant
  the permission.

  Args:
    unused_repo_ref: Repo reference input.
    repo_args: User input arguments.
    request: Create repository request.

  Returns:
    Create repository request.
  """
    if repo_args.kms_key:
        project_num = project_util.GetProjectNumber(GetProject(repo_args))
        service_account = _AR_SERVICE_ACCOUNT.format(project_num=project_num)
        policy = ar_requests.GetCryptoKeyPolicy(repo_args.kms_key)
        has_permission = False
        for binding in policy.bindings:
            if "serviceAccount:" + service_account in binding.members and (
                    binding.role
                    == "roles/cloudkms.cryptoKeyEncrypterDecrypter"
                    or binding.role == "roles/owner"):
                has_permission = True
                break
        if not has_permission:
            console_io.PromptContinue(
                prompt_string=
                ("\nGrant the Artifact Registry Service Account "
                 "permission to encrypt/decrypt with the selected key [{key_name}]"
                 .format(key_name=repo_args.kms_key)),
                cancel_on_no=True,
                cancel_string=
                ("The Artifact Registry Service Account needs permissions to "
                 "encrypt/decrypt on the selected key.\n"
                 "Learn more: https://cloud.google.com/artifact-registry/docs/cmek"
                 ))
            try:
                ar_requests.AddCryptoKeyPermission(
                    repo_args.kms_key, "serviceAccount:" + service_account)
            # We have checked the existence of the key when checking IAM bindings
            # So all 400s should be because the service account is problematic.
            # We are moving the permission check to the backend fairly soon anyway.
            except apitools_exceptions.HttpBadRequestError:
                msg = (
                    "The Artifact Registry service account may not exist, please "
                    "create the service account.\nLearn more: "
                    "https://cloud.google.com/artifact-registry/docs/cmek")
                raise ar_exceptions.ArtifactRegistryError(msg)

            log.status.Print(
                "Added Cloud KMS CryptoKey Encrypter/Decrypter Role to [{key_name}]"
                .format(key_name=repo_args.kms_key))
    return request
Exemple #2
0
def ListRepositories(args):
    """Lists repositories in a given project.

  If no location value is specified, list repositories across all locations.

  Args:
    args: User input arguments.

  Returns:
    List of repositories.
  """
    project = GetProject(args)
    location = args.location or properties.VALUES.artifacts.location.Get()
    location_list = ar_requests.ListLocations(project)
    if location and location.lower(
    ) not in location_list and location != "all":
        raise ar_exceptions.UnsupportedLocationError(
            "{} is not a valid location. Valid locations are [{}].".format(
                location, ", ".join(location_list)))

    loc_paths = []
    if location and location != "all":
        log.status.Print(
            "Listing items under project {}, location {}.\n".format(
                project, location))
        loc_paths.append("projects/{}/locations/{}".format(project, location))
    else:
        log.status.Print(
            "Listing items under project {}, across all locations.\n".format(
                project))
        loc_paths.extend([
            "projects/{}/locations/{}".format(project, loc)
            for loc in location_list
        ])

    pool_size = len(loc_paths) if loc_paths else 1
    pool = parallel.GetPool(pool_size)
    page_size = args.page_size
    try:
        pool.Start()
        results = pool.Map(
            lambda x: ar_requests.ListRepositories(x, page_size=page_size),
            loc_paths)
    except parallel.MultiError as e:
        error_set = set(err.content for err in e.errors)
        msg = "\n".join(error_set)
        raise ar_exceptions.ArtifactRegistryError(msg)
    finally:
        pool.Join()

    repos = []
    for sublist in results:
        repos.extend([repo for repo in sublist])
    repos.sort(key=lambda x: x.name.split("/")[-1])

    return repos
Exemple #3
0
def DeleteDockerImage(args):
  """Deletes a Docker digest or image.

  If input is an image, delete the image along with its resources.

  If input is an image identified by digest, delete the digest.
  If input is an image identified by tag, delete the digest and the tag.
  If --delete-tags is specified, delete all tags associated with the image
  digest.

  Args:
    args: user input arguments.

  Returns:
    The long-running operation from DeletePackage API call.
  """
  image, version_or_tag = _ParseDockerImage(args.IMAGE, _INVALID_IMAGE_ERROR)
  _ValidateDockerRepo(image.docker_repo.GetRepositoryName())
  client = ar_requests.GetClient()
  messages = ar_requests.GetMessages()
  if not version_or_tag:
    console_io.PromptContinue(
        message="\nThis operation will delete all tags and images for " +
        image.GetDockerString() + ".",
        cancel_on_no=True)
    return ar_requests.DeletePackage(client, messages, image.GetPackageName())

  else:
    tags_to_delete = []
    docker_version = version_or_tag
    if isinstance(version_or_tag, DockerTag):
      docker_version = DockerVersion(
          version_or_tag.image,
          ar_requests.GetVersionFromTag(client, messages,
                                        version_or_tag.GetTagName()))
      tags_to_delete.append(version_or_tag)
    existing_tags = _GetDockerVersionTags(client, messages, docker_version)
    if args.delete_tags:
      tags_to_delete.extend(existing_tags)

    if len(existing_tags) != len(tags_to_delete):
      raise ar_exceptions.ArtifactRegistryError(
          "Cannot delete image {} because it is tagged. "
          "Existing tags are:\n- {}".format(
              args.IMAGE,
              "\n- ".join(tag.GetDockerString() for tag in existing_tags)))

    _LogResourcesToDelete(docker_version, tags_to_delete)
    console_io.PromptContinue(
        message="\nThis operation will delete the above resources.",
        cancel_on_no=True)

    for tag in tags_to_delete:
      ar_requests.DeleteTag(client, messages, tag.GetTagName())
    return ar_requests.DeleteVersion(client, messages,
                                     docker_version.GetVersionName())
def GetVersionFromTag(client, messages, tag):
    """Gets a version name by a tag name."""
    get_tag_req = messages.ArtifactregistryProjectsLocationsRepositoriesPackagesTagsGetRequest(
        name=tag)
    get_tag_res = client.projects_locations_repositories_packages_tags.Get(
        get_tag_req)
    if not get_tag_res.version or len(get_tag_res.version.split("/")) != 10:
        raise ar_exceptions.ArtifactRegistryError(
            "Internal error. Corrupted tag: {}".format(tag))
    return get_tag_res.version.split("/")[-1]
Exemple #5
0
def _GetDockerPackagesAndVersions(docker_repo, include_tags, is_nested=False):
    """Gets a list of packages with versions for a Docker repository."""
    client = ar_requests.GetClient()
    messages = ar_requests.GetMessages()
    img_list = []
    for pkg in ar_requests.ListPackages(client, messages,
                                        docker_repo.GetRepositoryName()):
        parts = pkg.name.split("/")
        if len(parts) != 8:
            raise ar_exceptions.ArtifactRegistryError(
                "Internal error. Corrupted package name: {}".format(pkg.name))
        img = DockerImage(DockerRepo(parts[1], parts[3], parts[5]), parts[7])
        img_list.extend(_GetDockerVersions(img, include_tags, is_nested))
    return img_list
Exemple #6
0
def DeletePackageTags(pkg_ref, pkg_args, request):
    """Deletes tags associate with the specified package."""
    if not pkg_args.delete_tags:
        return request
    client = _GetClientForResource(pkg_ref)
    messages = _GetMessagesForResource(pkg_ref)
    list_tags_req = messages.ArtifactregistryProjectsLocationsRepositoriesPackagesTagsListRequest(
        parent=pkg_ref.RelativeName())
    list_tags_res = client.projects_locations_repositories_packages_tags.List(
        list_tags_req)
    for tag in list_tags_res.tags:
        delete_tag_req = messages.ArtifactregistryProjectsLocationsRepositoriesPackagesTagsDeleteRequest(
            name=tag.name)
        err = client.projects_locations_repositories_packages_tags.Delete(
            delete_tag_req)
        if not isinstance(err, messages.Empty):
            raise ar_exceptions.ArtifactRegistryError(
                "Failed to delete tag {}: {}".format(tag.name, err))
    return request
Exemple #7
0
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))