Acione funções do Cloud Run através do Cloud Tasks


Este tutorial mostra como usar o Cloud Tasks numa aplicação do App Engine para acionar uma função do Cloud Run e enviar um email agendado.

Objetivos

  • Compreenda o código em cada um dos componentes.
  • Crie uma conta do SendGrid.
  • Transfira o código-fonte.
  • Implemente uma função do Cloud Run para receber pedidos do Cloud Tasks e enviar um email através da API SendGrid.
  • Crie uma fila do Cloud Tasks.
  • Crie uma conta de serviço para autenticar os seus pedidos do Cloud Tasks.
  • Implemente o código do cliente que permite a um utilizador enviar um email.

Custos

O Cloud Tasks, as funções do Cloud Run e o App Engine têm um nível gratuito. Por isso, desde que esteja a executar o tutorial no nível gratuito dos produtos indicados, não deve resultar em custos adicionais. Para mais informações, consulte a secção Preços.

Antes de começar

  1. Selecione ou crie um Google Cloud projeto.

    Aceda à página do App Engine

  2. Inicialize uma aplicação do App Engine no seu projeto:

    1. Na página Bem-vindo ao App Engine, clique em Criar aplicação.

    2. Selecione uma região para a sua aplicação. Esta localização vai servir como o parâmetro LOCATION_ID para os seus pedidos do Cloud Tasks, por isso, tome nota. Tenha em atenção que duas localizações, denominadas europe-west e us-central nos comandos do App Engine, são denominadas, respetivamente, europe-west1 e us-central1 nos comandos do Cloud Tasks.

    3. Selecione Node.js para o idioma e Standard para o ambiente.

    4. Se for apresentado o pop-up Ativar faturação, selecione a sua conta de faturação. Se não tiver atualmente uma conta de faturação, clique em Criar conta de faturação e siga o assistente.

    5. Na página Começar, clique em Seguinte. Vai tratar disto mais tarde.

  3. Ative as APIs Cloud Run Functions e Cloud Tasks.

    Ative as APIs

  4. Instale e inicialize a CLI gcloud.

Compreender o código

Esta secção explica o código da app e como funciona.

Criar a tarefa

A página de índice é publicada através de controladores no app.yaml. As variáveis necessárias para a criação de tarefas são transmitidas como variáveis de ambiente.

runtime: nodejs16

env_variables:
  QUEUE_NAME: "my-queue"
  QUEUE_LOCATION: "us-central1"
  FUNCTION_URL: "https://<region>-<project_id>.cloudfunctions.net/sendEmail"
  SERVICE_ACCOUNT_EMAIL: "<member>@<project_id>.iam.gserviceaccount.com"

# Handlers for serving the index page.
handlers:
  - url: /static
    static_dir: static
  - url: /
    static_files: index.html
    upload: index.html

Este código cria o ponto final /send-email. Este ponto final processa os envios de formulários da página de índice e transmite esses dados ao código de criação de tarefas.

app.post('/send-email', (req, res) => {
  // Set the task payload to the form submission.
  const {to_name, from_name, to_email, date} = req.body;
  const payload = {to_name, from_name, to_email};

  createHttpTaskWithToken(
    process.env.GOOGLE_CLOUD_PROJECT,
    QUEUE_NAME,
    QUEUE_LOCATION,
    FUNCTION_URL,
    SERVICE_ACCOUNT_EMAIL,
    payload,
    date
  );

  res.status(202).send('📫 Your postcard is in the mail! 💌');
});

Este código cria efetivamente a tarefa e envia-a para a fila do Cloud Tasks. O código cria a tarefa:

  • Especificar o tipo de destino como HTTP Request.

  • Especificar o HTTP method a usar e o URL do destino.

  • Definir o cabeçalho Content-Type como application/json para que as aplicações a jusante possam analisar a carga útil estruturada.

  • Adicionar um email de conta de serviço para que o Cloud Tasks possa fornecer credenciais ao destino do pedido, o que requer autenticação. A conta de serviço é criada separadamente.

  • Verificar se a entrada do utilizador para a data está no máximo de 30 dias e adicioná-la ao pedido como campo scheduleTime.

const MAX_SCHEDULE_LIMIT = 30 * 60 * 60 * 24; // Represents 30 days in seconds.

const createHttpTaskWithToken = async function (
  project = 'my-project-id', // Your GCP Project id
  queue = 'my-queue', // Name of your Queue
  location = 'us-central1', // The GCP region of your queue
  url = 'https://example.com/taskhandler', // The full url path that the request will be sent to
  email = '<member>@<project-id>.iam.gserviceaccount.com', // Cloud IAM service account
  payload = 'Hello, World!', // The task HTTP request body
  date = new Date() // Intended date to schedule task
) {
  // Imports the Google Cloud Tasks library.
  const {v2beta3} = require('@google-cloud/tasks');

  // Instantiates a client.
  const client = new v2beta3.CloudTasksClient();

  // Construct the fully qualified queue name.
  const parent = client.queuePath(project, location, queue);

  // Convert message to buffer.
  const convertedPayload = JSON.stringify(payload);
  const body = Buffer.from(convertedPayload).toString('base64');

  const task = {
    httpRequest: {
      httpMethod: 'POST',
      url,
      oidcToken: {
        serviceAccountEmail: email,
        audience: url,
      },
      headers: {
        'Content-Type': 'application/json',
      },
      body,
    },
  };

  const convertedDate = new Date(date);
  const currentDate = new Date();

  // Schedule time can not be in the past.
  if (convertedDate < currentDate) {
    console.error('Scheduled date in the past.');
  } else if (convertedDate > currentDate) {
    const date_diff_in_seconds = (convertedDate - currentDate) / 1000;
    // Restrict schedule time to the 30 day maximum.
    if (date_diff_in_seconds > MAX_SCHEDULE_LIMIT) {
      console.error('Schedule time is over 30 day maximum.');
    }
    // Construct future date in Unix time.
    const date_in_seconds =
      Math.min(date_diff_in_seconds, MAX_SCHEDULE_LIMIT) + Date.now() / 1000;
    // Add schedule time to request in Unix time using Timestamp structure.
    // https://googleapis.dev/nodejs/tasks/latest/google.protobuf.html#.Timestamp
    task.scheduleTime = {
      seconds: date_in_seconds,
    };
  }

  try {
    // Send create task request.
    const [response] = await client.createTask({parent, task});
    console.log(`Created task ${response.name}`);
    return response.name;
  } catch (error) {
    // Construct error for Stackdriver Error Reporting
    console.error(Error(error.message));
  }
};

module.exports = createHttpTaskWithToken;

Criar o email

Este código cria a função do Cloud Run que é o destino do pedido do Cloud Tasks. Usa o corpo do pedido para criar um email e enviá-lo através da API SendGrid.

const sendgrid = require('@sendgrid/mail');

/**
 * Responds to an HTTP request from Cloud Tasks and sends an email using data
 * from the request body.
 *
 * @param {object} req Cloud Function request context.
 * @param {object} req.body The request payload.
 * @param {string} req.body.to_email Email address of the recipient.
 * @param {string} req.body.to_name Name of the recipient.
 * @param {string} req.body.from_name Name of the sender.
 * @param {object} res Cloud Function response context.
 */
exports.sendEmail = async (req, res) => {
  // Get the SendGrid API key from the environment variable.
  const key = process.env.SENDGRID_API_KEY;
  if (!key) {
    const error = new Error(
      'SENDGRID_API_KEY was not provided as environment variable.'
    );
    error.code = 401;
    throw error;
  }
  sendgrid.setApiKey(key);

  // Get the body from the Cloud Task request.
  const {to_email, to_name, from_name} = req.body;
  if (!to_email) {
    const error = new Error('Email address not provided.');
    error.code = 400;
    throw error;
  } else if (!to_name) {
    const error = new Error('Recipient name not provided.');
    error.code = 400;
    throw error;
  } else if (!from_name) {
    const error = new Error('Sender name not provided.');
    error.code = 400;
    throw error;
  }

  // Construct the email request.
  const msg = {
    to: to_email,
    from: 'postcard@example.com',
    subject: 'A Postcard Just for You!',
    html: postcardHTML(to_name, from_name),
  };

  try {
    await sendgrid.send(msg);
    // Send OK to Cloud Task queue to delete task.
    res.status(200).send('Postcard Sent!');
  } catch (error) {
    // Any status code other than 2xx or 503 will trigger the task to retry.
    res.status(error.code).send(error.message);
  }
};

A preparar a aplicação

Configurar o SendGrid

  1. Crie uma conta do SendGrid.

  2. Crie uma chave da API SendGrid:

    1. Inicie sessão na sua conta do SendGrid.

    2. No menu de navegação do lado esquerdo, abra Definições e clique em Chaves de API.

    3. Clique em Criar chave da API e selecione acesso restrito. No cabeçalho Mail Send, selecione Acesso total.

    4. Copie a chave API quando for apresentada (só a vê uma vez. Certifique-se de que a cola noutro local para a poder usar mais tarde).

Transferir o código-fonte

  1. Clone o repositório da app de exemplo para a sua máquina local:

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git
    
  2. Altere para o diretório que contém o código de exemplo:

    cd cloud-tasks/
    

Implementar a função do Cloud Run

  1. Navegue para o diretório function/:

    cd function/
    
  2. Implemente a função:

    gcloud functions deploy sendEmail --runtime nodejs14 --trigger-http \
      --no-allow-unauthenticated \
      --set-env-vars SENDGRID_API_KEY=SENDGRID_API_KEY \

    Substitua SENDGRID_API_KEY pela sua chave da API.

    Este comando usa sinalizadores:

    • --trigger-http para especificar o tipo de acionador de funções do Cloud Run.

    • --no-allow-unauthenticated para especificar que a invocação da função requer autenticação.

    • --set-env-var para definir as suas credenciais do SendGrid

  3. Defina o controlo de acesso para a função de modo a permitir apenas utilizadores autenticados.

    1. Selecione a função sendEmail na IU das funções do Cloud Run.

    2. Se não vir informações de autorizações para sendEmail, clique em MOSTRAR PAINEL DE INFORMAÇÕES no canto superior direito.

    3. Clique no botão Adicionar diretores acima.

    4. Defina Novos diretores como allAuthenticatedUsers.

    5. Defina a função.

      • Funções de primeira geração (1.ª geração): defina a função como Cloud Function Invoker
      • Funções de segunda geração (2.ª geração): defina a função como Cloud Run Invoker
    6. Clique em GUARDAR.

Criar uma fila do Cloud Tasks

  1. Crie uma fila com o seguinte comando gcloud:

    gcloud tasks queues create my-queue --location=LOCATION

    Substitua LOCATION pela sua localização preferida para a fila, por exemplo, us-west2. Se não especificar a localização, a CLI gcloud escolhe a predefinição.

  2. Verifique se foi criado com êxito:

    gcloud tasks queues describe my-queue --location=LOCATION

    Substitua LOCATION pela localização da fila.

Criar uma conta de serviço

O pedido Cloud Tasks tem de fornecer credenciais no cabeçalho Authorization para que a função do Cloud Run autentique o pedido. Esta conta de serviço permite que o Cloud Tasks crie e adicione um token OIDC para esse fim.

  1. Na IU de contas de serviço, clique em +CRIAR CONTA DE SERVIÇO.

  2. Adicione um nome da conta de serviço(nome a apresentar simples) e selecione criar.

  3. Defina a Função e clique em Continuar.

    • Funções de primeira geração (1.ª geração): defina a função como Cloud Function Invoker
    • Funções de segunda geração (2.ª geração): defina a função como Cloud Run Invoker
  4. Selecione Concluído.

Implementar o ponto final e o criador de tarefas no App Engine

  1. Navegue para o diretório app/:

    cd ../app/
    
  2. Atualize as variáveis no app.yaml com os seus valores:

    env_variables:
      QUEUE_NAME: "my-queue"
      QUEUE_LOCATION: "us-central1"
      FUNCTION_URL: "https://<region>-<project_id>.cloudfunctions.net/sendEmail"
      SERVICE_ACCOUNT_EMAIL: "<member>@<project_id>.iam.gserviceaccount.com"

    Para encontrar a sua posição na fila, use o seguinte comando:

    gcloud tasks queues describe my-queue --location=LOCATION

    Substitua LOCATION pela localização da fila.

    Para encontrar o URL da função, use o seguinte comando:

    gcloud functions describe sendEmail
  3. Implemente a aplicação no ambiente padrão do App Engine com o seguinte comando:

    gcloud app deploy
  4. Abra a aplicação para enviar um postal como email:

    gcloud app browse

Limpar

Depois de concluir o tutorial, pode limpar os recursos que criou para que deixem de usar a quota e incorrer em custos. As secções seguintes descrevem como eliminar ou desativar estes recursos.

Eliminar recursos

Pode limpar os recursos que criou no Google Cloud para que não ocupem quota e não lhe sejam faturados no futuro. As secções seguintes descrevem como eliminar ou desativar estes recursos.

Elimine a função do Cloud Run

  1. Aceda à página Funções do Cloud Run na Google Cloud consola.

    Aceda à página de funções do Cloud Run.

  2. Clique nas caixas de verificação junto às suas funções.

  3. Clique no botão Eliminar na parte superior da página e confirme a eliminação.

Elimine a fila do Cloud Tasks

  1. Abra a página de filas do Cloud Tasks na consola.

    Aceda à página de filas do Cloud Tasks

  2. Selecione o nome da fila que quer eliminar e clique em Eliminar fila.

  3. Confirme a ação.

Eliminar o projeto

A forma mais fácil de eliminar a faturação é eliminar o projeto que criou para o tutorial.

Para eliminar o projeto:

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

O que se segue?