Accesso ai metadati delle istanze

La piattaforma Google Cloud fornisce un server di metadati che conosce i dettagli della tua istanza App Engine, ad esempio l'ID progetto contenente, gli account di servizio e i token utilizzati dagli account di servizio. Puoi accedere a questi dati utilizzando semplici richieste HTTP: non sono necessarie librerie client.

Questa pagina mostra come accedere ai metadati dell'istanza dal file Java 8 di cui è stato eseguito il deployment applicazione runtime effettuando chiamate HTTP agli endpoint del server di metadati appropriati.

Un modo utile per utilizzare questa API è ottenere il token dell'account di servizio e fornirlo come token di accesso nell'intestazione Authorization di una delle API Google Cloud per autenticare l'applicazione a quel determinato servizio API. Consulta l'API Google Cloud Translation documentazione di un esempio di come vengono utilizzati questi token di connessione.

Identificazione dell'endpoint di metadati da utilizzare

La tabella seguente elenca gli endpoint per i quali è possibile effettuare richieste HTTP metadati specifici. Il server dei metadati è accessibile all'indirizzo http://metadata.google.internal.

Endpoint metadati Descrizione
/computeMetadata/v1/project/numeric-project-id Il numero del progetto assegnato al tuo progetto.
/computeMetadata/v1/project/project-id L'ID progetto assegnato al progetto.
/computeMetadata/v1/instance/zone La zona in cui è in esecuzione l'istanza.
/computeMetadata/v1/instance/service-accounts/default/aliases
/computeMetadata/v1/instance/service-accounts/default/email L'indirizzo email dell'account di servizio predefinito assegnato al progetto.
/computeMetadata/v1/instance/service-accounts/default/ Elenca tutti gli account di servizio predefiniti per il progetto.
/computeMetadata/v1/instance/service-accounts/default/scopes Elenca tutti gli ambiti supportati per i service account predefiniti.
/computeMetadata/v1/instance/service-accounts/default/token Restituisce il token di autenticazione che può essere utilizzato per autenticare l'applicazione in altre API Google Cloud.

Ad esempio, per recuperare l'ID progetto, invia una richiesta a http://metadata.google.internal/computeMetadata/v1/project/project-id.

Inviare richieste di metadati

Il seguente codice di esempio recupera e mostra tutti i metadati disponibili per l'istanza, ad eccezione del token dell'account di servizio.

@SuppressWarnings("serial")
// With @WebServlet annotation the webapp/WEB-INF/web.xml is no longer required.
@WebServlet(name = "Metadata", description = "Metadata: Write info about GAE Standard",
    urlPatterns = "/metadata")
public class MetadataServlet extends HttpServlet {

  private final String[] metaPath = {
      "/computeMetadata/v1/project/numeric-project-id", //  (pending)
      "/computeMetadata/v1/project/project-id",
      "/computeMetadata/v1/instance/zone",
      "/computeMetadata/v1/instance/service-accounts/default/aliases",
      "/computeMetadata/v1/instance/service-accounts/default/email",
      "/computeMetadata/v1/instance/service-accounts/default/",
      "/computeMetadata/v1/instance/service-accounts/default/scopes",
      // Tokens work - but are a security risk to display
      //      "/computeMetadata/v1/instance/service-accounts/default/token"
  };

  final String[] metaServiceAcct = {
      "/computeMetadata/v1/instance/service-accounts/{account}/aliases",
      "/computeMetadata/v1/instance/service-accounts/{account}/email",
      "/computeMetadata/v1/instance/service-accounts/{account}/scopes",
      // Tokens work - but are a security risk to display
      //     "/computeMetadata/v1/instance/service-accounts/{account}/token"
  };

  private final String metadata = "http://metadata.google.internal";
  private TemplateEngine templateEngine;

  // Use OkHttp from Square as it's quite easy to use for simple fetches.
  private final OkHttpClient ok = new OkHttpClient.Builder()
      .readTimeout(500, TimeUnit.MILLISECONDS)  // Don't dawdle
      .writeTimeout(500, TimeUnit.MILLISECONDS)
      .build();

  // Setup to pretty print returned json
  private final Gson gson = new GsonBuilder()
      .setPrettyPrinting()
      .create();
  private final JsonParser jp = new JsonParser();

  // Fetch Metadata
  String fetchMetadata(String key) throws IOException {
    Request request = new Request.Builder()
        .url(metadata + key)
        .addHeader("Metadata-Flavor", "Google")
        .get()
        .build();

    Response response = ok.newCall(request).execute();
    return response.body().string();
  }

  String fetchJsonMetadata(String prefix) throws IOException {
    Request request = new Request.Builder()
        .url(metadata + prefix)
        .addHeader("Metadata-Flavor", "Google")
        .get()
        .build();

    Response response = ok.newCall(request).execute();

    // Convert json to prety json
    return gson.toJson(jp.parse(response.body().string()));
  }

  @Override
  public void init() {
    // Setup ThymeLeaf
    ServletContextTemplateResolver templateResolver =
        new ServletContextTemplateResolver(this.getServletContext());

    templateResolver.setPrefix("/WEB-INF/templates/");
    templateResolver.setSuffix(".html");
    templateResolver.setCacheTTLMs(Long.valueOf(1200000L)); // TTL=20m

    // Cache is set to true by default. Set to false if you want templates to
    // be automatically updated when modified.
    templateResolver.setCacheable(true);

    templateEngine = new TemplateEngine();
    templateEngine.setTemplateResolver(templateResolver);
  }

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    String defaultServiceAccount = "";
    WebContext ctx = new WebContext(req, resp, getServletContext(), req.getLocale());

    resp.setContentType("text/html");

    String environment =
        (String) System.getProperties().get("com.google.appengine.runtime.environment");
    ctx.setVariable("production", environment);

    // The metadata server is only on a production system
    if (environment.equals("Production")) {

      TreeMap<String, String> m = new TreeMap<>();

      for (String key : metaPath) {
        m.put(key, fetchMetadata(key));
        if (key.contains("default/email")) {
          defaultServiceAccount = m.get(key);
        }
      }

      ctx.setVariable("Metadata", m.descendingMap());

      m = new TreeMap<>();
      for (String key : metaServiceAcct) {
        // substitute a service account for {account}
        key = key.replace("{account}", defaultServiceAccount);
        m.put(key, fetchMetadata(key));
      }
      ctx.setVariable("sam", m.descendingMap());

      // Recursivly get all info about service accounts -- Note tokens are leftout by default.
      ctx.setVariable("rsa",
          fetchJsonMetadata("/computeMetadata/v1/instance/service-accounts/?recursive=true"));
      // Recursivly get all data on Metadata server.
      ctx.setVariable("ram", fetchJsonMetadata("/?recursive=true"));
    }

    templateEngine.process("index", ctx, resp.getWriter());

  }
}

Nel codice campione, nota il controllo per verificare che l'app sia in esecuzione e produzione. Se l'app viene eseguita localmente, dalle richieste non verranno restituiti metadati.

Inoltre, è bene notare l'utilizzo del file JSON Gson di Google serializzatore / deserializzatore, il protocollo OkHttp e HTTP2, oltre al modello Thymeleaf di un sistema operativo completo. Non sono obbligatori, ma sono librerie utili per la tua in modo programmatico a gestire i progetti.

Eseguire l'app in locale

Il server di metadati è disponibile per le applicazioni di cui è stato eseguito il deployment: in esecuzione localmente sul server di sviluppo non è supportato. Puoi aggiungere un controllo dell'ambiente al codice per prevedere i risultati dei metadati solo se l'app è in esecuzione in produzione, come mostrato nell'esempio di codice fornito sopra:

String environment =
      (String) System.getProperties().get("com.google.appengine.runtime.environment");
  ctx.setVariable("production", environment);

  // The metadata server is only on a production system
  if (environment.equals("Production")) {
     ... //show metadata results
   }