Utiliser Cloud Tasks pour déclencher des fonctions Cloud Functions

Ce tutoriel explique comment utiliser Cloud Tasks dans une application Google App Engine pour déclencher une fonction Cloud Functions et envoyer un e-mail planifié.

Objectifs

  • Comprendre le code de chacun des composants
  • Créer un compte SendGrid
  • Téléchargez le code source.
  • Déployez une fonction Cloud pour recevoir des requêtes Cloud Tasks et envoyer un e-mail via l'API SendGrid.
  • Créez une file d'attente Cloud Tasks.
  • Créez un compte de service pour authentifier vos requêtes Cloud Tasks.
  • Déployez le code client permettant à un utilisateur d'envoyer un e-mail.

Coûts

Cloud Tasks, Cloud Functions et App Engine disposent d'une version gratuite. Par conséquent, si vous exécutez le didacticiel dans cette version gratuite des produits concernés, cela ne devrait pas entraîner de coûts supplémentaires. Pour en savoir plus, reportez-vous à la section Tarification.

Avant de commencer

  1. Sélectionnez ou créez un projet GCP.

    Accéder à la page App Engine

  2. Initialisez une application App Engine dans votre projet :

    1. Sur la page Bienvenue dans App Engine, cliquez sur Créer une application.

    2. Sélectionnez une région pour votre application. Cet emplacement servira de paramètre LOCATION_ID pour vos requêtes Cloud Tasks. Notez-le donc. Notez que deux sites appelés europe-west et us-central dans les commandes App Engine sont appelés respectivement europe-west1 et us-central1 dans les commandes Cloud Tasks.

    3. Sélectionnez Node.js pour le langage et Standard pour l'environnement.

    4. Si la fenêtre pop-up Activer la facturation s'affiche, sélectionnez votre compte de facturation. Si vous ne possédez pas de compte de facturation, cliquez sur Créer un compte de facturation et suivez les instructions de l'assistant.

    5. Sur la page Premiers pas, cliquez sur Suivant. Vous vous en occuperez plus tard.

  3. Activez les API Cloud Functions et Cloud Tasks.

    Activer les API

  4. Installez et initialisez le SDK Cloud.

Comprendre le code

Cette section décrit le code de l'application et son fonctionnement.

Créer la tâche

La page d'index est diffusée à l'aide de gestionnaires dans app.yaml. Les variables nécessaires à la création de tâches sont transmises en tant que variables d'environnement.

runtime: nodejs10

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

Ce code crée le point de terminaison /send-email. Ce point de terminaison gère les envois de formulaires depuis la page d'index et transmet ces données au code de création de tâche.

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! 💌');
});

Ce code crée la tâche et l'envoie à la file d'attente Cloud Tasks. Le code crée la tâche en effectuant ce qui suit :

  • Spécifier le type de cible comme HTTP Request.

  • Spécifier la méthode HTTP method à utiliser et la valeur URL de la cible.

  • Définir l'en-tête Content-Type sur application/json pour que les applications en aval puissent analyser la charge utile structurée.

  • Ajout d'une adresse e-mail de compte de service afin que Cloud Tasks puisse fournir des identifiants à la cible de la requête, ce qui nécessite une authentification. Le compte de service est créé séparément.

  • Vérifier que la date saisie par l'utilisateur est comprise dans la limite de 30 jours et l'ajouter à la requête en tant que champ 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,
      },
      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;

Création de l'e-mail

Ce code crée la fonction Cloud Functions qui est la cible de la requête Cloud Tasks. Il utilise le corps de la requête pour créer un e-mail et l'envoyer via l'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);
  }
};

Préparer l'application

Configurer SendGrid

  1. Créer un compte SendGrid

  2. Créez une clé API SendGrid :

    1. Connectez-vous à votre compte SendGrid.

    2. Dans le panneau de navigation de gauche, ouvrez Paramètres et cliquez sur Clés API.

    3. Cliquez sur Créer une clé API, puis sélectionnez l'accès restreint. Sous l'en-tête Mail Send (Envoi de courrier), sélectionnez Full Access (Accès complet).

    4. Copiez la clé API lorsqu'elle est affichée (vous ne la verrez qu'une seule fois, assurez-vous de la coller quelque part pour pouvoir l'utiliser ultérieurement).

Télécharger le code source

  1. Clonez le dépôt de l'exemple d'application sur votre machine locale :

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git
    
  2. Accédez au répertoire qui contient l'exemple de code :

    cd cloud-tasks/
    

Déployer la fonction Cloud Functions

  1. Accédez au répertoire function/ :

    cd function/
    
  2. Déployez la fonction en remplaçant sendgrid_api_key par votre clé :

    gcloud functions deploy sendEmail --runtime nodejs8 --trigger-http --set-env-vars SENDGRID_API_KEY=sendgrid_api_key
    

    Cette commande utilise des options :

    • --trigger-http pour spécifier le type de déclencheur Cloud Functions.

    • --set-env-var pour définir vos identifiants SendGrid

  3. Définissez le contrôle d'accès de la fonction pour autoriser uniquement les utilisateurs authentifiés.

    1. Sélectionnez la fonction sendEmail dans l'interface utilisateur de Cloud Functions.

    2. Si vous ne voyez pas les informations d'autorisation pour sendEmail, cliquez sur AFFICHER LE PANNEAU D'INFORMATIONS dans l'angle supérieur droit.

    3. Sous l'en-tête Demandeur Cloud Functions, supprimez le membre allUsers.

    4. Cliquez sur le bouton Ajouter des membres ci-dessus.

    5. Définissez Nouveaux membres sur allAuthenticatedUsers.

    6. Définissez le rôle sur Cloud Function Invoker.

    7. Cliquez sur ENREGISTRER.

Créer une file d'attente Cloud Tasks

  1. Créez une file d'attente à l'aide de la commande gcloud suivante :

    gcloud tasks queues create my-queue
    
  2. Vérifiez qu'elle a bien été créée :

    gcloud tasks queues describe my-queue
    

Créer un compte de service

La requête Cloud Tasks doit fournir des identifiants dans l'en-tête Authorization pour que la fonction Cloud puisse authentifier la requête. Ce compte de service permet à Cloud Tasks de créer et d'ajouter un jeton OIDC à cette fin.

  1. Dans l'interface utilisateur IAM et administration | Comptes de service, cliquez sur + CRÉER UN COMPTE DE SERVICE.

  2. Ajoutez un nom de compte de service (nom à afficher convivial) et sélectionnez créer.

  3. Sélectionnez le rôle Demandeur Cloud Functions, puis cliquez sur Continuer.

  4. Sélectionnez OK.

Déployer le point de terminaison et le créateur de tâches dans App Engine

  1. Accédez au répertoire app/ :

    cd ../app/
    
  2. Mettez à jour les variables dans app.yaml avec vos valeurs :

    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"

    Pour trouver l'emplacement de la file d'attente, utilisez la commande suivante :

    gcloud tasks queues describe my-queue
    

    Pour trouver l'URL de votre fonction, utilisez la commande suivante :

    gcloud functions describe sendEmail
    
  3. Déployez l'application dans l'environnement standard App Engine à l'aide de la commande suivante :

    gcloud app deploy
    
  4. Ouvrez l'application pour envoyer un courrier de validation par e-mail :

    gcloud app browse
    

Nettoyer

Une fois que vous avez terminé ce tutoriel, vous pouvez procéder au nettoyage des ressources que vous avez créées sur Google Cloud afin qu'elles ne soient plus comptabilisées dans votre quota et qu'elles ne vous soient plus facturées. Dans les sections suivantes, nous allons voir comment supprimer ou désactiver ces ressources.

Supprimer des ressources

Vous pouvez procéder au nettoyage des ressources que vous avez créées sur GCP afin qu'elles ne soient plus comptabilisées dans votre quota et qu'elles ne vous soient plus facturées. Dans les sections suivantes, nous allons voir comment supprimer ou désactiver ces ressources.

Supprimer la fonction Cloud

  1. Accédez à la page Cloud Functions dans Cloud Console.

    Accéder à la page Cloud Functions

  2. Cochez les cases situées en regard de vos fonctions.

  3. Cliquez sur le bouton Supprimer en haut de la page et confirmez la suppression.

Supprimer la file d'attente Cloud Tasks

  1. Ouvrez la page des files d'attente Cloud Tasks dans la console.

    Accéder à la page des files d'attente Cloud Tasks

  2. Sélectionnez le nom de la file d'attente que vous souhaitez supprimer, puis cliquez sur Supprimer la file d'attente.

  3. Confirmez l'opération.

Supprimer le projet

Le moyen le plus simple d'empêcher la facturation est de supprimer le projet que vous avez créé pour ce tutoriel.

Pour supprimer le projet :

  1. Dans Cloud Console, accédez à la page Gérer les ressources.

    Accéder à la page Gérer les ressources

  2. Dans la liste des projets, sélectionnez le projet que vous souhaitez supprimer, puis cliquez sur Supprimer .
  3. Dans la boîte de dialogue, saisissez l'ID du projet, puis cliquez sur Arrêter pour supprimer le projet.

Étape suivante