Membuat permintaan API dan menangani respons


Dokumen ini menjelaskan cara membuat permintaan API dan menangani respons API dari Compute Engine API. Panduan ini membahas cara:

  • Membuat isi permintaan.
  • Menentukan URI resource yang diperlukan untuk permintaan.
  • Menangani respons API.
  • Menentukan apakah permintaan API berhasil.

Dokumen ini tidak membahas cara mengautentikasi ke API. Untuk mempelajari cara melakukan autentikasi ke API, baca Mengautentikasi ke Compute Engine.

Sebelum memulai

Membuat permintaan API

Compute Engine API mengharapkan permintaan API dalam format JSON. Untuk membuat permintaan API, Anda dapat membuat permintaan HTTP langsung, dengan menggunakan alat seperti curl atau httplib2, atau Anda dapat menggunakan salah satu library klien yang tersedia.

Saat Anda membuat permintaan API yang memerlukan isi permintaan, seperti permintaan POST, UPDATE, atau PATCH, isi permintaan berisi properti resource yang ingin Anda tetapkan dalam permintaan. Misalnya, perintah curl berikut membuat permintaan POST ke URI resource Instance. Permintaan tersebut akan membuat instance dengan properti yang ditentukan dalam isi permintaan. Isi permintaan ditunjukkan dengan flag -d:

curl -X POST -H "Authorization: Bearer [OAUTH_TOKEN]" -H "Content-Type: application/json"
https://compute.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-10-buster-v20210122"
      }
    }
  ],
  "machineType":"https://www.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/machineTypes/e2-standard-2",
  "name":"VM_NAME",
  "networkInterfaces":[
    {
      "accessConfigs":[
        {
          "name":"external-nat",
          "type":"ONE_TO_ONE_NAT"
        }
      ],
      "network":"https://www.googleapis.com/compute/v1/projects/PROJECT_ID/global/networks/default"
    }
  ]
}'

URI image memiliki project ID (debian-cloud) yang berbeda dengan project ID Anda karena image termasuk dalam project yang berbeda, bergantung pada jenis image. Misalnya, semua image Debian yang tersedia secara publik yang ditawarkan oleh Compute Engine dihosting di project debian-cloud.

Saat merujuk ke resource lain, gunakan URI resource yang sepenuhnya memenuhi syarat. Misalnya, properti network menggunakan URI yang sepenuhnya memenuhi syarat ke jaringan default.

Contoh permintaan API

Python

def create_instance(
    compute: object,
    project: str,
    zone: str,
    name: str,
    bucket: str,
) -> str:
    """Creates an instance in the specified zone.

    Args:
      compute: an initialized compute service object.
      project: the Google Cloud project ID.
      zone: the name of the zone in which the instances should be created.
      name: the name of the instance.
      bucket: the name of the bucket in which the image should be written.

    Returns:
      The instance object.
    """
    # Get the latest Debian Jessie image.
    image_response = (
        compute.images()
        .getFromFamily(project="debian-cloud", family="debian-11")
        .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")
    ).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(
      String.format(
          "https://www.googleapis.com/compute/v1/projects/%s/zones/%s/machineTypes/e2-standard-1",
          PROJECT_ID, ZONE_NAME));
  // Add Network Interface to be used by VM Instance.
  NetworkInterface ifc = new NetworkInterface();
  ifc.setNetwork(
      String.format(
          "https://www.googleapis.com/compute/v1/projects/%s/global/networks/default",
          PROJECT_ID));
  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(
      String.format(
          "https://www.googleapis.com/compute/v1/projects/%s/zones/%s/diskTypes/pd-standard",
          PROJECT_ID, ZONE_NAME));
  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(String.format("gs://%s/vm-startup.sh", PROJECT_ID));
  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();
}

Membuat URI resource

Di Compute Engine API, referensi ke resource Google Cloud lainnya diberikan sebagai URI yang sepenuhnya memenuhi syarat:

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

Setiap kali menentukan image, jenis mesin, jaringan, atau resource lainnya, Anda harus menyediakan URI ke resource saat menggunakan API. Alat klien seperti Google Cloud CLI dan konsol Google Cloud menyembunyikan kerumitan ini dan menangani pembuatan URI resource ini untuk Anda. Namun, saat berinteraksi dengan API secara langsung, Anda harus membuat URI resource ini sendiri.

Ada URI resource yang sedikit berbeda untuk tipe resource yang berbeda. Misalnya, resource zona memiliki spesifikasi zone dalam URI:

https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/machineTypes/e2-standard-2

Resource regional mengganti spesifikasi zone dengan spesifikasi region:

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

Demikian pula, resource global memiliki spesifikasi global:

https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/global/images/VM_NAME

Compute Engine API juga menerima URI parsial karena layanan dapat menyimpulkan informasi seperti project ID. Jadi, versi parsial dari URI sebelumnya berikut ini juga dapat diterima:

zones/ZONE/machineTypes/e2-standard-2
regions/REGION/addresses/ADDRESS_NAME
project/PROJECT_ID/global/images/VM_NAME

Pada URI parsial, baik URI zona maupun URI regional menghilangkan project ID, tetapi URI image tidak. Hal ini karena image yang tersedia secara publik yang ditawarkan oleh Compute Engine dihosting di project lain, seperti debian-cloud untuk semua image Debian dan ubuntu-os-cloud untuk semua image Ubuntu. Sebelum dapat menggunakan image ini, Anda harus menyediakan project ID yang sesuai. Jika Anda menghapus project ID untuk image, Compute Engine akan mencoba menemukan image tersebut di project Anda, dan permintaan akan gagal karena image tidak ada.

Namun, jika menggunakan image kustom milik project Anda (project yang sama tempat Anda membuat resource ini), Anda dapat menghilangkan spesifikasi project saat memberikan URI image.

Menentukan properti yang diperlukan

Dokumentasi referensi Compute Engine API, yang tersedia untuk API v1 maupun beta, menjelaskan semua kemungkinan properti yang dapat Anda tetapkan untuk resource tertentu. Dokumentasi referensi ini membedakan antara properti yang dapat diubah dan properti yang tidak dapat diubah (ditandai dengan [Output Only] dalam deskripsi properti), tetapi untuk menentukan properti yang diperlukan untuk resource, Anda perlu meninjau dokumentasi yang khusus untuk tugas tersebut.

Misalnya, jika Anda membuat instance, baca dokumentasi Membuat instance dari image untuk melihat properti API yang diperlukan untuk permintaan tersebut. Jika Anda ingin membuat alamat IP eksternal statis di API, baca dokumentasi Alamat IP eksternal statis.

Memvalidasi permintaan API

Untuk memvalidasi permintaan API:

  1. Di referensi Compute Engine API, temukan metode yang dipanggil kode Anda. Misalnya, v1/compute.instances.insert.
  2. Dari menu konten, klik Try It! Tindakan ini akan membuka jendela Try this API.

    Tombol Try It! pada menu konten.

  3. Di bagian Parameter permintaan, Anda tidak perlu memberikan project atau zona karena validasi tidak memerlukan pengiriman permintaan.

  4. Di bagian Isi permintaan, tempelkan permintaan Anda.

    Jendela Try this API, yang menampilkan kolom Isi permintaan untuk menunjukkan tempat menempelkan permintaan validasi.

Elemen permintaan yang salah format digarisbawahi dengan warna biru. Klik setiap bagian yang digarisbawahi untuk mengetahui informasi selengkapnya tentang masalah yang akan ditangani.

Menangani respons API

Jika Anda membuat permintaan yang mengubah data, Compute Engine akan menampilkan objek Operation yang kemudian dapat Anda polling untuk mendapatkan status operasi permintaan Anda. Resource Operation terlihat seperti ini:

{
 "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_VM",
 "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"
}

Jika permintaan asli adalah untuk mengubah resource zona–misalnya, untuk mengambil snapshot disk atau menghentikan instance–Compute Engine menampilkan objek zoneOperations. Demikian pula, resource regional dan global masing-masing menampilkan objek regionOperations atau globalOperations. Anda bisa mendapatkan status operasi dengan membuat permintaan yang menggunakan metode get atau wait untuk resource Operation tertentu dan menyediakan name operasi.

Permintaan Anda belum selesai sampai status resource Operation ditampilkan sebagai DONE. Ini mungkin butuh waktu beberapa saat tergantung pada sifat permintaan Anda. Kemudian, setelah status resource Operation ditampilkan sebagai DONE, Anda dapat memeriksa apakah operasi berhasil dan apakah ada error.

Misalnya, respons berikut menunjukkan bahwa operasi sebelumnya kini selesai, yang ditentukan oleh status 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://compute.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://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances/EXAMPLE_VM
user: user@example.com
zone: https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE

Untuk mengonfirmasi, buat permintaan get ke resource untuk memeriksa bahwa resource tersebut ada dan sedang berjalan. Contoh:

GET /compute/v1/projects/PROJECT_ID/zones/ZONE/instances/EXAMPLE_VM

{
  "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_VM",
  "status": "RUNNING",
  "tags": {
    "fingerprint": "42WmSpB8rSM="
  },
  "zone": "https://www.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE"
}

Operasi polling

Anda dapat menulis beberapa kode untuk melakukan polling operasi secara berkala dengan permintaan get atau wait yang ditampilkan saat status operasi adalah DONE.

Dengan permintaan get, operasi akan segera ditampilkan, apa pun status operasinya. Anda perlu melakukan polling pada operasi secara berkala untuk mengetahui kapan operasi selesai.

Jika Anda membuat permintaan wait, permintaan tersebut akan ditampilkan saat operasinya adalah DONE atau jika permintaan mendekati batas waktu 2 menit. Anda dapat memilih untuk menggunakan wait atau get guna melakukan polling pada operasi, tetapi metode wait memberikan manfaat tertentu dibandingkan metode get:

  • Anda dapat menyiapkan klien agar lebih jarang melakukan polling status operasi, sehingga mengurangi penggunaan QPS Anda atas Compute Engine API.
  • Latensi rata-rata antara saat operasi selesai dan saat klien diberi tahu bahwa operasi telah selesai akan berkurang secara signifikan karena server merespons segera setelah operasi selesai.
  • Metode ini menyediakan waktu tunggu terbatas. Metode ini menunggu tidak lebih dari waktu tunggu HTTP default (2 menit), lalu menampilkan status operasi saat ini, yang mungkin berupa DONE atau masih dalam proses.

Metode wait adalah API upaya terbaik. Jika server kelebihan beban, permintaan mungkin akan ditampilkan sebelum mencapai batas waktu default, atau setelah menunggu hanya nol detik. Metode ini juga tidak dijamin hanya akan ditampilkan jika operasinya adalah DONE. Misalnya, jika permintaan mendekati batas waktu 2 menit, metode akan ditampilkan meskipun operasi belum selesai. Untuk memeriksa operasi Anda, sebaiknya gunakan metode wait atau get—dalam loop percobaan ulang dengan disela periode tidur—guna memeriksa status operasi secara berkala. Interval percobaan ulang maksimum tidak boleh melebihi periode retensi operasi minimum.

Contoh polling

Contoh berikut menggunakan metode get. Anda dapat mengganti metode get dengan metode wait:

Python

def wait_for_operation(
    compute: object,
    project: str,
    zone: str,
    operation: str,
) -> dict:
    """Waits for the given operation to complete.

    Args:
      compute: an initialized compute service object.
      project: the Google Cloud project ID.
      zone: the name of the zone in which the operation should be executed.
      operation: the operation ID.

    Returns:
      The result of the 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 = getLastWordFromUrl(operation.getZone()); // null for global/regional operations
  String region = getLastWordFromUrl(operation.getRegion());
  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 if (region != null) {
      Compute.RegionOperations.Get get = compute.regionOperations().get(PROJECT_ID, region, 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();
}