Как вывести список папок и проектов внутри папок в GCP

В моей организации GCP, имеющей следующую иерархию Org--> folder--> folder--> folder--> projects--> Resources. У меня есть учетная запись службы глобального просмотра, у которой есть необходимое разрешение для перечисления проектов и папок. Я использую клиентскую библиотеку GCP JAVA, но вижу список папок, которые мне нужно использовать https://cloudresourcemanager.googleapis.com/v2/folders который находится в v2 Cloud Resource Manager API, но для перечисления проектов мне нужно https://cloudresourcemanager.googleapis.com/v1/projectsкоторый находится в v1 Cloud Resource Manager API. Есть ли способ использовать клиентскую библиотеку GCP JAVA, чтобы объединить папки и проекты?

3 ответа

Насколько я могу судить, не существует единого метода, который позволил бы перечислить как папки, так и проекты.
Если вы считаете, что эта функция должна быть доступна, вы можете создать запрос функции .

В качестве альтернативы есть сценарий bash, который использует команды gcloud для получения аналогичного результата:

      #!/usr/bin/env bash

: "${ORGANIZATION:?Need to export ORGANIZATION and it must be non-empty}"

# gcloud format
FORMAT="csv[no-heading](name,displayName.encode(base64))"

# Enumerates Folders recursively
folders()
{
  LINES=("$@")
  for LINE in ${LINES[@]}
  do
    # Parses lines of the form folder,name
    VALUES=(${LINE//,/ })
    FOLDER=${VALUES[0]}
    # Decodes the encoded name
    NAME=$(echo ${VALUES[1]} | base64 --decode)
    echo "Folder: ${FOLDER} (${NAME})"
    folders $(gcloud resource-manager folders list \
      --folder=${FOLDER} \
      --format="${FORMAT}")
  done
}

# Start at the Org
echo "Org: ${ORGANIZATION}"
LINES=$(gcloud resource-manager folders list \
  --organization=${ORGANIZATION} \
  --format="${FORMAT}")

# Descend
folders ${LINES[0]}

Вы просите самый ужасный API в Google Cloud. Это плохой и ужасный API, и ваша проблема является результатом этого ... В любом случае, мне потребовалось несколько часов, чтобы попробовать, и я могу предложить вам использовать Discovery API (такой простой в python и очень мало документированных и трудных для использования в Ява). Действительно, вы не можете использовать автоматически сгенерированную клиентскую библиотеку, вам нужно использовать прямой вызов API или Discovery API.

Во-первых, добавьте эту зависимость в определение Maven

              <dependency>
            <groupId>com.google.apis</groupId>
            <artifactId>google-api-services-discovery</artifactId>
            <version>v1-rev20190129-1.31.0</version>
        </dependency>

Затем использование Discovery API.

      // Build the discovery API object
        HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
        Discovery discovery = (new Discovery.Builder(httpTransport,jsonFactory,null)).build();

// Prepare your credential for the calls
        GoogleCredentials credential = GoogleCredentials.getApplicationDefault();
        HttpRequestFactory requestFactory = httpTransport.createRequestFactory(new HttpCredentialsAdapter(credential));

// Discover the API V1 of resource manager
        RestDescription apiV1 = discovery.apis().getRest("cloudresourcemanager", "v1").execute();

// Discover the API V2 of resource manager
        RestDescription apiV2 = discovery.apis().getRest("cloudresourcemanager", "v2").execute();

//Get a Method in the v1, here list project
        RestMethod methodListProject = apiV1.getResources().get("projects").getMethods().get("list");

//Get a Method in the v2, here list folders
        RestMethod methodListFolder = apiV2.getResources().get("folders").getMethods().get("list");

/////////////////////// V1 call /////////////////////

//Create the URL to call, with no query parameter here
        GenericUrl urlProjectList = new GenericUrl(UriTemplate.expand(apiV1.getBaseUrl() + methodListProject.getPath(), null, true));
        System.out.println(urlProjectList);

//Prepare the request
        HttpRequest requestProjectList = requestFactory.buildRequest(methodListProject.getHttpMethod(), urlProjectList, null);

//Execute and print the result
        System.out.println(requestProjectList.execute().parseAsString());

/////////////////////// V2 call /////////////////////

//Prepare the parameter for the call    
        JsonSchema param = new JsonSchema();
        param.set(
                "parent", String.format("organizations/%s", "<OrganisationID>"));


//Create the URL to call, with the query parameter
        GenericUrl urlFolderList = new GenericUrl(UriTemplate.expand(apiV1.getBaseUrl() + methodListFolder.getPath(), param, true));
        System.out.println(urlFolderList);

//Prepare the request
        HttpRequest requestFolderList = requestFactory.buildRequest(methodListFolder.getHttpMethod(), urlFolderList, null);

//Execute and print the result
        System.out.println(requestFolderList.execute().parseAsString());

Оба работают в одном коде в одно и то же время. Не очень читабельно. Я рекомендую вам обернуть это в классы, которые соответствуют вашим требованиям для лучшей читаемости / повторного использования.

Вам нужно много использовать описание API, чтобы знать и понимать, какие существуют методы и их параметры.

Решение и описание Python опубликованы в GCP Resource Manager — все перечислено Джоан Грау .

Код работает довольно хорошо без изменений:

      from googleapiclient.discovery import build
import google.auth

credentials, _ = google.auth.default()

# V1 is needed to call all methods except for the ones related to folders
rm_v1_client = build('cloudresourcemanager', 'v1', credentials=credentials, cache_discovery=False) 

# V2 is needed to call folder-related methods
rm_v2_client = build('cloudresourcemanager', 'v2', credentials=credentials, cache_discovery=False) 

ORGANIZATION_ID = '[MY-ORG-ID]'

def listAllProjects():
    # Start by listing all the projects under the organization
    filter='parent.type="organization" AND parent.id="{}"'.format(ORGANIZATION_ID)
    projects_under_org = rm_v1_client.projects().list(filter=filter).execute()

    # Get all the project IDs
    all_projects = [p['projectId'] for p in projects_under_org['projects']]

    # Now retrieve all the folders under the organization
    parent="organizations/"+ORGANIZATION_ID
    folders_under_org = rm_v2_client.folders().list(parent=parent).execute()

    # Make sure that there are actually folders under the org
    if not folders_under_org:
        return all_projects

    # Now sabe the Folder IDs
    folder_ids = [f['name'].split('/')[1] for f in folders_under_org['folders']]

    # Start iterating over the folders
    while folder_ids:
        # Get the last folder of the list
        current_id = folder_ids.pop()
        
        # Get subfolders and add them to the list of folders
        subfolders = rm_v2_client.folders().list(parent="folders/"+current_id).execute()
        
        if subfolders:
            folder_ids.extend([f['name'].split('/')[1] for f in subfolders['folders']])
        
        # Now, get the projects under that folder
        filter='parent.type="folder" AND parent.id="{}"'.format(current_id)
        projects_under_folder = rm_v1_client.projects().list(filter=filter).execute()
        
        # Add projects if there are any
        if projects_under_folder:
            all_projects.extend([p['projectId'] for p in projects_under_folder['projects']])

    # Finally, return all the projects
    return all_projects

if __name__=='__main__':
    print(listAllProjects())

Единственное отличие от блога заключается в том, что я создал специальную роль на уровне организации и применил ее к учетной записи службы. Роль включает в себя эти разрешения диспетчера ресурсов.

  • resourcesmanager.folders.get
  • resourcesmanager.folders.list
  • resourcesmanager.projects.get
  • resourcesmanager.projects.list

Приведенный выше код извлекает проекты из подпапок, но не обрабатывает разбиение на страницы, если проектов или папок больше, чем размер страницы по умолчанию. Это может быть проблемой для крупных организаций.

Я внес некоторые изменения для управления нумерацией страниц. Обратите внимание, что я установил pageSize=2, чтобы проверить его работу, но удалил его, чтобы вернуться к значению по умолчанию в рабочей среде.

Эта версия функции также возвращает список идентификаторов всех обнаруженных папок.

      from googleapiclient import discovery
from googleapiclient.discovery import build

def listAllProjects(credentials, ORGANIZATION_ID):
    # V1 is needed to call all methods except for the ones related to folders
    all_projects = []
    folder_ids = []

    rm_v1_client = build('cloudresourcemanager', 'v1', credentials=credentials, cache_discovery=False)
    # V2 is needed to call folder-related methods
    rm_v2_client = build('cloudresourcemanager', 'v2', credentials=credentials, cache_discovery=False)

    # Start by listing all the projects under the organization
    filter='parent.type="organization" AND parent.id="{}"'.format(ORGANIZATION_ID)

    request = rm_v1_client.projects().list(pageSize=2, filter=filter)

    while request is not None:
        response = request.execute()
        if response:
            projects_in_org_page = response['projects']
            # Get all the project IDs
            all_projects.extend(projects_in_org_page)
        request = rm_v1_client.projects().list_next(previous_request=request, previous_response=response)

    # Now retrieve all the folders under the organization
    parent="organizations/"+ORGANIZATION_ID
    #folders_under_org = rm_v2_client.folders().list(parent=parent).execute()

    request = rm_v2_client.folders().list(pageSize=2, parent=parent)

    while request is not None:
        response = request.execute()
        if response:
            # Now save the Folder IDs
            folder_ids_in_page = [f['name'].split('/')[1] for f in response['folders']]
            # Get all the project IDs
            folder_ids.extend(folder_ids_in_page)
        request = rm_v2_client.folders().list_next(previous_request=request, previous_response=response)

    # Make sure that there are actually folders under the org
    if not folder_ids:
        return all_projects, folder_ids

    found_folder_ids = folder_ids.copy() # because folder_ids will be popped till it empties
    # Start iterating over the folders
    while folder_ids:
        # Get the last folder of the list
        current_id = folder_ids.pop()
        # Get subfolders and add them to the list of folders
        subfolders = rm_v2_client.folders().list(pageSize=2, parent="folders/"+current_id).execute()

        if subfolders:
            folder_ids.extend([f['name'].split('/')[1] for f in subfolders['folders']])
            found_folder_ids.extend(folder_ids_in_page)

        request = rm_v2_client.folders().list(pageSize=2, parent="folders/"+current_id)
        while request is not None:
            response = request.execute()
            if response:
                # Now save the Folder IDs
                folder_ids_in_page = [f['name'].split('/')[1] for f in response['folders']]
                # Get all the project IDs
                folder_ids.extend(folder_ids_in_page)
                found_folder_ids.extend(folder_ids_in_page)
            request = rm_v2_client.folders().list_next(previous_request=request, previous_response=response)

        # Now, get the projects under that folder
        filter='parent.type="folder" AND parent.id="{}"'.format(current_id)
        projects_under_folder = rm_v1_client.projects().list(filter=filter).execute()
        request =  rm_v1_client.projects().list(filter=filter)

        while request is not None:
            response = request.execute()
            if response:
                projects_under_folder_page = response['projects']
                # Get all the project IDs
                all_projects.extend(projects_under_folder_page)
            request = rm_v1_client.projects().list_next(previous_request=request, previous_response=response)

    # Finally, return all the projects
    return all_projects, found_folder_ids
Другие вопросы по тегам