Creazione di richieste API e gestione delle risposte

Questo documento descrive come creare richieste API e gestire Risposte dell'API Compute Engine. Spiega come:

  • Crea il corpo di una richiesta.
  • Determinare gli URI delle risorse necessari per una richiesta.
  • Gestire le risposte dell'API.
  • Determina se una richiesta API è riuscita.

Questo documento non spiega come eseguire l'autenticazione nell'API. Per scoprire come autenticarsi nell'API, leggere Esegui l'autenticazione in Compute Engine.

Prima di iniziare

Creazione di una richiesta API

L'API Compute Engine prevede che le richieste API siano in formato JSON. Per effettuare una richiesta API, puoi effettuare una richiesta HTTP diretta utilizzando come curl o httplib2, oppure puoi usare uno dei librerie client disponibili.

Quando effettui una richiesta API che richiede un corpo della richiesta, ad esempio POST, Richiesta UPDATE, o PATCH, il corpo della richiesta contiene proprietà della risorsa da impostare in questa richiesta. Ad esempio, il seguente comando curl effettua una richiesta POST all'URI della risorsa Instances. La richiesta crea un con le proprietà definite nel corpo della richiesta. Il corpo della richiesta è indicato dal 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"
    }
  ]
}'

L'URI dell'immagine ha un ID progetto diverso (debian-cloud) da quello del tuo ID progetto perché le immagini appartengono a progetti diversi, a seconda del tipo di immagine. Ad esempio, tutte le immagini Debian disponibili pubblicamente offerte Compute Engine è ospitato nel progetto debian-cloud.

Quando fai riferimento a un'altra risorsa, utilizza l'URI completo della risorsa. Ad esempio, la proprietà network utilizza un URI completo per Rete default.

Richieste API di esempio

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();
}

Creazione di URI delle risorse

Nell'API Compute Engine, viene fornito un riferimento a un'altra risorsa Google Cloud come URI completo:

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

Ogni volta che specifichi un'immagine, un tipo di macchina, una rete o qualsiasi altra risorsa, devi fornire l'URI della risorsa quando utilizzi l'API. Strumenti per i clienti come Google Cloud CLI e la console Google Cloud nascondono questa complessità a creare questi URI delle risorse, ma quando si interagisce con l'API devi creare personalmente questi URI delle risorse.

Esistono URI leggermente diversi per tipi di risorse diversi. Per Ad esempio, una risorsa di zona ha la specifica zone nell'URI:

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

Le risorse di regione sostituiscono la specifica zone con un valore region specifiche:

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

Analogamente, le risorse globali hanno la specifica global:

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

L'API Compute Engine accetta anche URI parziali perché il servizio può e dedurne informazioni come l'ID progetto. Pertanto, le seguenti versioni parziali sono accettabili anche URI precedenti:

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

Negli URI parziali, sia gli URI a livello di zona che quelli a livello di regione hanno omesso il progetto mentre l'URI dell'immagine non lo è. Ciò è dovuto al fatto che le immagini disponibili pubblicamente offerte da Compute Engine sono ospitati in altri progetti, come debian-cloud per tutte le immagini Debian e ubuntu-os-cloud per tutte le immagini Ubuntu. Prima di poter per utilizzare queste immagini, devi fornire l'ID progetto appropriato. Se ometti l'ID progetto per le immagini, Compute Engine tenta di trovare l'immagine nel tuo progetto e la richiesta non va a buon fine perché l'immagine non esiste.

Tuttavia, se utilizzi una immagine personalizzata che appartiene al tuo progetto (lo stesso progetto in cui stai creando questa risorsa ), puoi omettere la specifica del progetto quando fornisci un URI dell'immagine.

Determinazione delle proprietà obbligatorie

La documentazione di riferimento dell'API Compute Engine, disponibile sia per v1 e API beta, descrive tutte le possibili che puoi impostare per una risorsa specifica. La documentazione di riferimento fa una distinzione tra proprietà mutabili e immutabili (contrassegnato da un [Output Only] nella descrizione della proprietà), ma per determinare proprietà obbligatorie di una risorsa, devi consultare la documentazione specifici per quell'attività.

Ad esempio, se stai creando un'istanza, leggi il Creazione di un'istanza da un'immagine documentazione per vedere le proprietà API richieste per la richiesta. Se creare un indirizzo IP esterno statico nell'API, leggi le Indirizzi IP esterni statici documentazione.

Convalida delle richieste API

Per convalidare le richieste API:

  1. Nel riferimento per l'API Compute Engine, per trovare il metodo chiamato dal codice. Ad esempio: v1/compute.instances.insert.
  2. Nel menu dei contenuti, fai clic su Prova! Viene visualizzata la finestra Prova questa API finestra.

    Il pulsante Prova! nel menu dei contenuti.

  3. In Parametri di richiesta, non devi fornire un progetto o zone perché la convalida non richiede l'invio della richiesta.

  4. In Request body (Corpo della richiesta), incolla la richiesta.

    La finestra Prova questa API, in cui viene visualizzato il campo Corpo della richiesta per indicare dove incollare una richiesta di convalida.

Gli elementi della richiesta non corretti vengono sottolineati in blu. Fai clic su ciascun sottolineata per ulteriori informazioni sul problema da risolvere.

Gestione delle risposte dell'API

Se effettui una richiesta che muta (altera) i dati, Compute Engine restituisce un oggetto Operation che puoi sottoporre a polling per ottenere lo stato di le operazioni per la tua richiesta. La risorsa Operation ha il seguente aspetto:

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

Se la richiesta originale deve mutare (alter) una risorsa di zona, ad esempio in eseguire lo snapshot di un disco o arrestare un'istanza. Compute Engine restituisce uno snapshot zoneOperations . Analogamente, le risorse regionali e globali restituiscono regionOperations oppure globalOperations . Puoi ottenere lo stato di un'operazione eseguendo una che utilizza il metodo get o wait per l'oggetto Operation e che fornisce il valore name dell'operazione.

La tua richiesta non è completa finché lo stato della risorsa Operation non torna come DONE. Questa operazione può richiedere del tempo, a seconda della natura della richiesta. Poi, dopo che lo stato della risorsa Operation torna come DONE, puoi controllare se l'operazione è riuscita e se si sono verificati errori.

Ad esempio, la seguente risposta indica che l'operazione precedente è ora completato, specificato dallo stato 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

Per confermare, invia una richiesta get alla risorsa per verificare che esista. in esecuzione. Ad esempio:

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"
}

Operazioni di polling

Puoi scrivere del codice per eseguire periodicamente il polling dell'operazione con un get o wait che viene restituita quando lo stato dell'operazione è DONE.

Con una richiesta get, l'operazione viene restituita immediatamente, indipendentemente dal stato dell'operazione. Per sapere quando è possibile, devi effettuare un polling dell'operazione periodicamente al termine dell'operazione.

Se effettui una richiesta wait, la richiesta viene restituita quando l'operazione è DONE o se la richiesta si avvicina alla scadenza di 2 minuti. Puoi scegliere di utilizzare wait o get per eseguire un polling delle tue operazioni, ma il metodo wait fornisce determinati vantaggi rispetto al metodo get:

  • Puoi configurare i tuoi clienti in modo che eseguano sondaggi per lo stato dell'operazione meno frequentemente, riducendo l'utilizzo di QPS dell'API Compute Engine.
  • La latenza media tra il completamento dell'operazione e il momento in cui il client che l'operazione viene eseguita è significativamente ridotta poiché il server risponde non appena viene completata l'operazione.
  • Il metodo fornisce attese limitate. Il metodo non attende più del valore timeout HTTP predefinito (2 minuti), quindi restituisce lo stato attuale della che potrebbe essere DONE o ancora in corso.

Il metodo wait è un'API best effort. Se il server è sovraccarico, la richiesta potrebbe essere restituita prima di raggiungere la scadenza predefinita o dopo aver atteso zero secondi. Inoltre, non è garantito che il metodo restituisca solo se è DONE. Ad esempio, se la richiesta si avvicina al giorno scadenza, il metodo restituisce anche se l'operazione non viene completata. Per verificare per le tue operazioni, ti consigliamo di utilizzare l'wait o Metodo get: in un ciclo di nuovo tentativo con una pausa tra un periodo e l'altro, per eseguire periodicamente eseguire il polling dello stato dell'operazione. L'intervallo massimo tra i nuovi tentativi non deve superare Il periodo minimo di conservazione delle operazioni.

Esempio di sondaggio

Nei seguenti esempi viene utilizzato il metodo get. Puoi sostituire il metodo get con il metodo 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();
}