建立 API 要求和處理回應

本文件說明如何透過 Compute Engine API 建立 API 要求及處理 API 回應,所涵蓋的內容如下:

  • 如何建構要求內文。
  • 如何決定要求所需的資源 URI。
  • 如何處理 API 回應。
  • 如何判斷 API 要求是否成功。

本文件不會說明如何針對 API 進行授權。如要瞭解如何針對 API 進行授權,請參閱授權要求一文。

事前準備

建立 API 要求

Compute Engine API 會預期 API 要求採 JSON 格式。如要發出 API 要求,您可以使用 curl 或 httplib2 等工具直接發出 HTTP 要求,或使用其中一種可用的用戶端程式庫

當您發出 POSTUPDATEPATCH 等需要要求內文的 API 要求時,要求內文必須包含您想在該要求中設定的資源屬性。例如,下列 curl 指令會向執行個體資源 URI 發出 POST 要求。這項要求會使用要求內文中定義的屬性建立新執行個體:

curl -X POST -H "Authorization: Bearer [OAUTH_TOKEN]" -H "Content-Type: application/json"
https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/zones/[ZONE]/instances -d
'{
  "disks":[
    {
      "boot":"true",
      "initializeParams":{
        "sourceImage":"https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20160301"
      }
    }
  ],
  "machineType":"https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/zones/[ZONE]/machineTypes/n1-standard-1",
  "name":"[INSTANCE_NAME]",
  "networkInterfaces":[
    {
      "accessConfigs":[
        {
          "name":"external-nat",
          "type":"ONE_TO_ONE_NAT"
        }
      ],
      "network":"https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/global/networks/default"
    }
  ]
}'

在此範例中,要求內文是以 -d 標記表示,如下所示 (格式已經過調整以便查看):

{
  "disks":[
    {
      "boot":"true",
      "initializeParams":{
        "sourceImage":"https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20160301"
      }
    }
  ],
  "machineType":"https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/zones/[ZONE]/machineTypes/n1-standard-1",
  "name":"[EXAMPLE_INSTANCE]",
  "networkInterfaces":[
    {
      "accessConfigs":[
        {
          "name":"external-nat",
          "type":"ONE_TO_ONE_NAT"
        }
      ],
      "network":"https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/global/networks/default"
    }
  ]
}

請注意:

  • 參照其他資源時使用的是完整的資源 URI。例如,network 屬性對 default 網路使用的是完整 URI。

  • 映像檔 URI 的專案 ID (debian-cloud) 與您的專案 ID 不同,因為映像檔會根據其類型而屬於不同專案。例如,Compute Engine 提供給大眾使用的所有 Debian 映像檔皆是在 debian-cloud 專案上託管。

以下是其他使用 Python 和 Java 用戶端程式庫的 API 要求範例。

Python

def create_instance(compute, project, zone, name, bucket):
    # Get the latest Debian Jessie image.
    image_response = compute.images().getFromFamily(
        project='debian-cloud', family='debian-9').execute()
    source_disk_image = image_response['selfLink']

    # Configure the machine
    machine_type = "zones/%s/machineTypes/n1-standard-1" % zone
    startup_script = open(
        os.path.join(
            os.path.dirname(__file__), 'startup-script.sh'), 'r').read()
    image_url = "http://storage.googleapis.com/gce-demo-input/photo.jpg"
    image_caption = "Ready for dessert?"

    config = {
        'name': name,
        'machineType': machine_type,

        # Specify the boot disk and the image to use as a source.
        'disks': [
            {
                'boot': True,
                'autoDelete': True,
                'initializeParams': {
                    'sourceImage': source_disk_image,
                }
            }
        ],

        # Specify a network interface with NAT to access the public
        # internet.
        'networkInterfaces': [{
            'network': 'global/networks/default',
            'accessConfigs': [
                {'type': 'ONE_TO_ONE_NAT', 'name': 'External NAT'}
            ]
        }],

        # Allow the instance to access cloud storage and logging.
        'serviceAccounts': [{
            'email': 'default',
            'scopes': [
                'https://www.googleapis.com/auth/devstorage.read_write',
                'https://www.googleapis.com/auth/logging.write'
            ]
        }],

        # Metadata is readable from the instance and allows you to
        # pass configuration from deployment scripts to instances.
        'metadata': {
            'items': [{
                # Startup script is automatically executed by the
                # instance upon startup.
                'key': 'startup-script',
                'value': startup_script
            }, {
                'key': 'url',
                'value': image_url
            }, {
                'key': 'text',
                'value': image_caption
            }, {
                'key': 'bucket',
                'value': bucket
            }]
        }
    }

    return compute.instances().insert(
        project=project,
        zone=zone,
        body=config).execute()

Java

public static Operation startInstance(Compute compute, String instanceName) throws IOException {
  System.out.println("================== Starting New Instance ==================");

  // Create VM Instance object with the required properties.
  Instance instance = new Instance();
  instance.setName(instanceName);
  instance.setMachineType(
      "https://www.googleapis.com/compute/v1/projects/"
      + PROJECT_ID + "/zones/" + ZONE_NAME + "/machineTypes/n1-standard-1");

  // Add Network Interface to be used by VM Instance.
  NetworkInterface ifc = new NetworkInterface();
  ifc.setNetwork("https://www.googleapis.com/compute/v1/projects/" + PROJECT_ID + "/global/networks/default");
  List<AccessConfig> configs = new ArrayList<>();
  AccessConfig config = new AccessConfig();
  config.setType(NETWORK_INTERFACE_CONFIG);
  config.setName(NETWORK_ACCESS_CONFIG);
  configs.add(config);
  ifc.setAccessConfigs(configs);
  instance.setNetworkInterfaces(Collections.singletonList(ifc));

  // Add attached Persistent Disk to be used by VM Instance.
  AttachedDisk disk = new AttachedDisk();
  disk.setBoot(true);
  disk.setAutoDelete(true);
  disk.setType("PERSISTENT");
  AttachedDiskInitializeParams params = new AttachedDiskInitializeParams();
  // Assign the Persistent Disk the same name as the VM Instance.
  params.setDiskName(instanceName);
  // Specify the source operating system machine image to be used by the VM Instance.
  params.setSourceImage(SOURCE_IMAGE_PREFIX + SOURCE_IMAGE_PATH);
  // Specify the disk type as Standard Persistent Disk
  params.setDiskType("https://www.googleapis.com/compute/v1/projects/" + PROJECT_ID + "/zones/"
                     + ZONE_NAME + "/diskTypes/pd-standard");
  disk.setInitializeParams(params);
  instance.setDisks(Collections.singletonList(disk));

  // Initialize the service account to be used by the VM Instance and set the API access scopes.
  ServiceAccount account = new ServiceAccount();
  account.setEmail("default");
  List<String> scopes = new ArrayList<>();
  scopes.add("https://www.googleapis.com/auth/devstorage.full_control");
  scopes.add("https://www.googleapis.com/auth/compute");
  account.setScopes(scopes);
  instance.setServiceAccounts(Collections.singletonList(account));

  // Optional - Add a startup script to be used by the VM Instance.
  Metadata meta = new Metadata();
  Metadata.Items item = new Metadata.Items();
  item.setKey("startup-script-url");
  // If you put a script called "vm-startup.sh" in this Google Cloud Storage
  // bucket, it will execute on VM startup.  This assumes you've created a
  // bucket named the same as your PROJECT_ID.
  // For info on creating buckets see: https://cloud.google.com/storage/docs/cloud-console#_creatingbuckets
  item.setValue("gs://" + PROJECT_ID + "/vm-startup.sh");
  meta.setItems(Collections.singletonList(item));
  instance.setMetadata(meta);

  System.out.println(instance.toPrettyString());
  Compute.Instances.Insert insert = compute.instances().insert(PROJECT_ID, ZONE_NAME, instance);
  return insert.execute();
}

建立資源 URI

在上述範例中,只要您需要參照其他 Google Cloud Platform 資源,就必須提供如下的完整 URI:

https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/zones/[ZONE]/[RESOURCE_TYPE]/[SPECIFIC_RESOURCE]

在使用 API 的情況下,每當您指定映像檔、機器類型、網路或任何其他資源時,就必須提供該資源的 URI。gcloud 和 Google Cloud Platform 主控台等用戶端工具可讓您省下麻煩,自動為您建立這些資源 URI,但如果您是直接與 API 互動,就必須自行建立這些資源 URI。

資源的 URI 會因資源類型而稍有不同。例如,區域資源的 URI 中包含 zone 規格,如下所示:

https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/zones/[ZONE]/machineTypes/n1-standard-1

地區資源包含的不是 zone 規格,而是 region 規格:

https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/regions/[REGION]/addresses/[ADDRESS_NAME]

最後,全球資源包含的是 global 規格:

https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/global/images/[IMAGE_NAME]

Compute Engine API 也接受不完整的 URI,因為伺服器可推測專案 ID 等資訊。以下 URI 是上述 URI 的不完整版本,但同樣可使用:

zones/[ZONE]/machineTypes/n1-standard-1
regions/[REGION]/addresses/[ADDRESS_NAME]
project/[PROJECT_ID]/global/images/[IMAGE_NAME]

在上述不完整 URI 中,區域和地區 URI 皆省略了專案 ID,映像檔 URI 則未省略。這是因為 Compute Engine 提供給大眾使用的映像檔是透過其他專案託管,例如所有 Debian 映像檔都是透過 debian-cloud 託管,所有 Ubuntu 映像檔則是透過 ubuntu-os-cloud 託管。如要使用這些映像檔,您必須提供適當的專案 ID。如果您省略映像檔的專案 ID,Compute Engine 會嘗試在您的專案中找出該映像檔,要求則會因映像檔不存在而失敗。

另一方面,如果您使用屬於您專案 (即您要在當中建立這項資源的專案) 的自訂映像檔,則提供映像檔 URI 時可省略專案規格。

判斷必要屬性

v1Beta 版 API 的 API 參考資料文件中說明您可以為特定資源設定的所有屬性。參考文件對可修改和不可修改 (屬性說明中標有 [Output Only]) 的屬性做出區分,但如要判斷資源的必要屬性,您必須查看該項工作專屬的說明文件。

舉例來說,如果您要建立新執行個體,請參閱建立和啟動執行個體說明文件,以瞭解要求所需的 API 屬性。如果您想在 API 中建立靜態外部 IP 位址,請參閱靜態外部 IP 位址說明文件。

此外,您也可以在 API Explorer 中驗證 API 要求,藉此輕鬆快速檢查程式碼。

處理 API 回應

如果您發出資料修改要求,Compute Engine 會傳回 Operation 物件供您進行輪詢,藉此取得要求的狀態。Operation 資源看起來如下:

{
 "kind": "compute#operation",
 "id": "7127550864823803662",
 "name": "operation-1458856416788-52ed27a803e22-1c3bd86a-9e95017b",
 "zone": "https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/zones/[ZONE]",
 "operationType": "insert",
 "targetLink": "https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/zones/[ZONE]/instances/[EXAMPLE_INSTANCE]",
 "targetId": "4132355614508179214",
 "status": "PENDING",
 "user": "user@example.com",
 "progress": 0,
 "insertTime": "2016-03-24T14:53:37.788-07:00",
 "selfLink": "https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/zones/[ZONE]/operations/operation-1458856416788-52ed27a803e22-1c3bd86a-9e95017b"
}

如果原始要求是修改執行個體或磁碟等區域資源,Compute Engine 會傳回 zoneOperations 物件。同樣地,地區和全球資源會分別傳回 regionOperationsglobalOperations 物件。如要取得作業的狀態,您可以對特定 Operation 資源執行 GET 要求,並在要求中提供作業的 name

到 Operation 資源的狀態傳回 DONE 為止,要求才算完成。視要求的性質而定,這項作業可能要花上一段時間。作業的狀態傳回 DONE 後,您應該要檢查作業是否成功以及是否有任何錯誤。

舉例來說,以下回應指出上述作業的狀態為 DONE,表示其現已完成:

endTime: '2016-03-24T14:54:07.119-07:00'
id: '7127550864823803662'
insertTime: '2016-03-24T14:53:37.788-07:00'
kind: compute#operation
name: operation-1458856416788-52ed27a803e22-1c3bd86a-9e95017b
operationType: insert
progress: 100
selfLink: https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/zones/[ZONE]/operations/operation-1458856416788-52ed27a803e22-1c3bd86a-9e95017b
startTime: '2016-03-24T14:53:38.397-07:00'
status: DONE
targetId: '4132355614508179214'
targetLink: https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/zones/[ZONE]/instances/[EXAMPLE_INSTANCE]
user: phunl@google.com
zone: https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/zones/[ZONE]

如要確認,請向資源發出 GET 要求,檢查該資源是否存在且/或正在執行。例如:

GET /compute/v1/projects/[PROJECT_ID]/zones/[ZONE]/instances/[EXAMPLE_INSTANCE]

{
  "cpuPlatform": "Intel Haswell",
  "creationTimestamp": "2016-03-24T14:53:37.170-07:00",
  "disks": [
    ..[snip]..
  "selfLink": "https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/zones/[ZONE]/instances/[EXAMPLE_INSTANCE]",
  "status": "RUNNING",
  "tags": {
    "fingerprint": "42WmSpB8rSM="
  },
  "zone": "https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/zones/[ZONE]"
}

輪詢作業

您可能不想花時間發出個別要求來取得作業的狀態,並希望作業能夠成功傳回。在這種情況下,請改為寫一些程式碼來定期針對作業進行輪詢,並在作業狀態為 DONE 時傳回結果。以下是使用 Python 和 Java 進行輪詢的範例。

Python

def wait_for_operation(compute, project, zone, operation):
    print('Waiting for operation to finish...')
    while True:
        result = compute.zoneOperations().get(
            project=project,
            zone=zone,
            operation=operation).execute()

        if result['status'] == 'DONE':
            print("done.")
            if 'error' in result:
                raise Exception(result['error'])
            return result

        time.sleep(1)

Java

/**
 * Wait until {@code operation} is completed.
 * @param compute the {@code Compute} object
 * @param operation the operation returned by the original request
 * @param timeout the timeout, in millis
 * @return the error, if any, else {@code null} if there was no error
 * @throws InterruptedException if we timed out waiting for the operation to complete
 * @throws IOException if we had trouble connecting
 */
public static Operation.Error blockUntilComplete(
    Compute compute, Operation operation, long timeout) throws Exception {
  long start = System.currentTimeMillis();
  final long pollInterval = 5 * 1000;
  String zone = operation.getZone();  // null for global/regional operations
  if (zone != null) {
    String[] bits = zone.split("/");
    zone = bits[bits.length - 1];
  }
  String status = operation.getStatus();
  String opId = operation.getName();
  while (operation != null && !status.equals("DONE")) {
    Thread.sleep(pollInterval);
    long elapsed = System.currentTimeMillis() - start;
    if (elapsed >= timeout) {
      throw new InterruptedException("Timed out waiting for operation to complete");
    }
    System.out.println("waiting...");
    if (zone != null) {
      Compute.ZoneOperations.Get get = compute.zoneOperations().get(PROJECT_ID, zone, opId);
      operation = get.execute();
    } else {
      Compute.GlobalOperations.Get get = compute.globalOperations().get(PROJECT_ID, opId);
      operation = get.execute();
    }
    if (operation != null) {
      status = operation.getStatus();
    }
  }
  return operation == null ? null : operation.getError();
}
本頁內容對您是否有任何幫助?請提供意見:

傳送您對下列選項的寶貴意見...

這個網頁
Compute Engine 說明文件