Fonctions Cloud HTTP en Python

1. Introduction

b158ce75c3cccd6d.png

Python est un langage de programmation Open Source populaire, utilisé par les data scientists, les développeurs d'applications Web, les administrateurs système et plus encore.

Cloud Functions est une plate-forme de calcul sans serveur basée sur des événements. Cloud Functions vous permet d'écrire votre code sans vous soucier du provisionnement des ressources ni du scaling pour faire face à l'évolution des exigences.

Il existe deux types de fonctions Cloud :

  • Les fonctions HTTP répondent aux requêtes HTTP. Vous allez en créer quelques-uns dans cet atelier de programmation.
  • Les fonctions d'arrière-plan sont déclenchées par des événements, comme la publication d'un message sur Cloud Pub/Sub ou l'importation d'un fichier sur Cloud Storage. Cet atelier n'aborde pas ce problème, mais vous pouvez consulter la documentation pour en savoir plus.

efb3268e3b74ed4f.png

Cet atelier de programmation vous explique comment créer vos propres fonctions Cloud en Python.

Ce que vous allez faire

Dans cet atelier de programmation, vous allez publier une fonction Cloud qui, lorsqu'elle est appelée via HTTP, affiche la fonction " logo:

a7aaf656b78050fd.png

Points abordés

  • Écrire une fonction Cloud HTTP
  • Écrire une fonction Cloud HTTP qui accepte des arguments
  • Tester une fonction Cloud HTTP
  • Exécuter un serveur HTTP Python local pour essayer la fonction
  • Écrire une fonction Cloud HTTP qui renvoie une image

2. Préparation

Configuration de l'environnement au rythme de chacun

  1. Connectez-vous à la console Google Cloud, puis créez un projet ou réutilisez un projet existant. Si vous n'avez pas encore de compte Gmail ou Google Workspace, vous devez en créer un.

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • Le nom du projet est le nom à afficher pour les participants au projet. Il s'agit d'une chaîne de caractères non utilisée par les API Google. Vous pourrez toujours le modifier.
  • L'ID du projet est unique parmi tous les projets Google Cloud et non modifiable une fois défini. La console Cloud génère automatiquement une chaîne unique (en général, vous n'y accordez d'importance particulière). Dans la plupart des ateliers de programmation, vous devrez indiquer l'ID de votre projet (généralement identifié par PROJECT_ID). Si l'ID généré ne vous convient pas, vous pouvez en générer un autre de manière aléatoire. Vous pouvez également en spécifier un et voir s'il est disponible. Après cette étape, l'ID n'est plus modifiable et restera donc le même pour toute la durée du projet.
  • Pour information, il existe une troisième valeur (le numéro de projet) que certaines API utilisent. Pour en savoir plus sur ces trois valeurs, consultez la documentation.
  1. Vous devez ensuite activer la facturation dans la console Cloud pour utiliser les ressources/API Cloud. L'exécution de cet atelier de programmation est très peu coûteuse, voire sans frais. Pour désactiver les ressources et éviter ainsi que des frais ne vous soient facturés après ce tutoriel, vous pouvez supprimer le projet ou les ressources que vous avez créées. Les nouveaux utilisateurs de Google Cloud peuvent participer au programme d'essai sans frais pour bénéficier d'un crédit de 300 $.

Démarrer Cloud Shell

Bien que Google Cloud puisse être utilisé à distance depuis votre ordinateur portable, vous allez utiliser Cloud Shell dans cet atelier de programmation, un environnement de ligne de commande exécuté dans le cloud.

Activer Cloud Shell

  1. Dans Cloud Console, cliquez sur Activer Cloud Shell 853e55310c205094.png.

3c1dabeca90e44e5.png

Si vous démarrez Cloud Shell pour la première fois, un écran intermédiaire vous explique de quoi il s'agit. Si un écran intermédiaire s'est affiché, cliquez sur Continuer.

9c92662c6a846a5c.png

Le provisionnement et la connexion à Cloud Shell ne devraient pas prendre plus de quelques minutes.

9f0e51b578fecce5.png

Cette machine virtuelle contient tous les outils de développement nécessaires. Elle comprend un répertoire d'accueil persistant de 5 Go et s'exécute dans Google Cloud, ce qui améliore considérablement les performances du réseau et l'authentification. Une grande partie, voire la totalité, de votre travail dans cet atelier de programmation peut être effectué dans un navigateur.

Une fois connecté à Cloud Shell, vous êtes authentifié et le projet est défini sur votre ID de projet.

  1. Exécutez la commande suivante dans Cloud Shell pour vérifier que vous êtes authentifié :
gcloud auth list

Résultat de la commande

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Exécutez la commande suivante dans Cloud Shell pour vérifier que la commande gcloud connaît votre projet:
gcloud config list project

Résultat de la commande

[core]
project = <PROJECT_ID>

Si vous obtenez un résultat différent, exécutez cette commande :

gcloud config set project <PROJECT_ID>

Résultat de la commande

Updated property [core/project].

Assurez-vous que les API Cloud Functions et Cloud Build sont activées

Exécutez la commande suivante à partir de Cloud Shell pour vous assurer que les API Cloud Functions et Cloud Build sont activées:

gcloud services enable \
  cloudfunctions.googleapis.com \
  cloudbuild.googleapis.com

Remarque: Cloud Build est appelé par la commande gcloud functions deploy et compile automatiquement votre code dans une image de conteneur.

Télécharger le code source

Dans le terminal Cloud Shell, exécutez les commandes suivantes:

REPO_NAME="codelabs"
REPO_URL="https://github.com/GoogleCloudPlatform/$REPO_NAME"
SOURCE_DIR="cloud-functions-python-http"

git clone --no-checkout --filter=blob:none --depth=1 $REPO_URL
cd $REPO_NAME
git sparse-checkout set $SOURCE_DIR
git checkout
cd $SOURCE_DIR

Vérifiez le contenu du répertoire source:

ls

Vous devriez disposer des fichiers suivants:

main.py  python-powered.png  test_main.py  web_app.py

3. Présentation des fonctions HTTP Cloud Functions

Les fonctions Cloud HTTP en Python sont écrites comme des fonctions Python standards. La fonction doit accepter un seul argument flask.Request, généralement nommé request.

main.py

import flask


def hello_world(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - "Hello World! 👋"
    """
    response = "Hello World! 👋"

    return flask.Response(response, mimetype="text/plain")

# ...

Vous pouvez ouvrir le fichier dans l'éditeur de ligne de commande de votre choix (nano, vim ou emacs). Vous pouvez également l'ouvrir dans l'éditeur Cloud Shell après avoir défini le répertoire source en tant qu'espace de travail:

cloudshell open-workspace .

Déployons cette fonction en tant que fonction Cloud HTTP à l'aide de la commande gcloud functions deploy:

FUNCTION_NAME="hello_world"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

Résultat de la commande :

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

Remarques concernant les options gcloud functions deploy:

  • --runtime: spécifie l'environnement d'exécution du langage. Pour Python, il peut actuellement s'agir de python37, python38, python39, python310 ou python312. Consultez la page Environnements d'exécution.
  • --trigger-http: un point de terminaison sera attribué à la fonction. Les requêtes HTTP (POST, PUT, GET, DELETE et OPTIONS) envoyées au point de terminaison déclencheront l'exécution de la fonction.
  • --allow-unauthenticated: la fonction sera publique, ce qui permettra à tous les appelants sans vérifier l'authentification.
  • Pour en savoir plus, consultez la section sur le déploiement des fonctions gcloud.

Pour tester la fonction, vous pouvez cliquer sur l'URL httpsTrigger.url affichée dans le résultat de la commande ci-dessus. Vous pouvez également récupérer l'URL par programmation et appeler la fonction à l'aide des commandes suivantes:

URL=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

Vous devriez obtenir le résultat suivant:

Hello World! 👋

4. Écrire une fonction Cloud HTTP qui accepte des arguments

Les fonctions sont plus polyvalentes lorsqu'elles acceptent des arguments. Définissons une nouvelle fonction hello_name qui accepte un paramètre name:

main.py

# ...

def hello_name(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - "Hello {NAME}! 🚀" if "name=NAME" is defined in the GET request
    - "Hello World! 🚀" otherwise
    """
    name = request.args.get("name", "World")
    response = f"Hello {name}! 🚀"

    return flask.Response(response, mimetype="text/plain")

# ...

Déployons cette nouvelle fonction:

FUNCTION_NAME="hello_name"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

Résultat de la commande :

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

Pour tester la fonction, vous pouvez cliquer sur l'URL httpsTrigger.url affichée dans le résultat de la commande ci-dessus. Vous pouvez également récupérer l'URL par programmation et appeler la fonction à l'aide des commandes suivantes:

URL=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

Vous devriez obtenir le résultat par défaut:

Hello World! 🚀

Vous obtenez le résultat par défaut, car l'argument name n'est pas défini. Ajoutez un paramètre à l'URL:

curl -w "\n" $URL?name=YOUR%20NAME

Cette fois, vous obtenez une réponse personnalisée:

Hello YOUR NAME! 🚀

L'étape suivante consiste à ajouter des tests unitaires pour vous assurer que vos fonctions continuent de fonctionner comme prévu lorsque le code source est mis à jour.

5. Élaboration des tests

Les fonctions Cloud HTTP en Python sont testées à l'aide du module unittest de la bibliothèque standard. Il n'est pas nécessaire d'exécuter un émulateur ou une autre simulation pour tester votre fonction, mais uniquement du code Python normal.

Voici à quoi ressemble un test pour les fonctions hello_world et hello_name:

test_main.py

import unittest
import unittest.mock

import main


class TestHello(unittest.TestCase):
    def test_hello_world(self):
        request = unittest.mock.Mock()

        response = main.hello_world(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == "Hello World! 👋"

    def test_hello_name_no_name(self):
        request = unittest.mock.Mock(args={})

        response = main.hello_name(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == "Hello World! 🚀"

    def test_hello_name_with_name(self):
        name = "FirstName LastName"
        request = unittest.mock.Mock(args={"name": name})

        response = main.hello_name(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == f"Hello {name}! 🚀"
  1. Les tests Python sont écrits de la même manière que les autres fichiers Python. Ils commencent par un ensemble d'importations, puis définissent des classes et des fonctions.
  2. La déclaration de test est au format class TestHello(TestCase). Il doit s'agir d'une classe qui hérite de unittest.TestCase.
  3. La classe de test comporte des méthodes, chacune devant commencer par test_, qui représente des scénarios de test individuels.
  4. Chaque scénario de test teste l'une de nos fonctions en simulant le paramètre request (c'est-à-dire en le remplaçant par un faux objet avec les données spécifiques requises pour le test).
  5. Après avoir appelé chaque fonction, le test vérifie la réponse HTTP pour s'assurer qu'elle correspond à nos attentes.

Comme main.py dépend de flask, assurez-vous que le framework Flask est installé dans votre environnement de test:

pip install flask

L'installation de Flask génère un résultat semblable à celui-ci:

Collecting flask
...
Successfully installed ... flask-3.0.2 ...

Exécutez ces tests en local:

python -m unittest

Les trois tests unitaires doivent réussir:

...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

Ensuite, vous allez créer une fonction qui renvoie la valeur .

6. Écrire le code Fonction Cloud HTTP

Rendons une nouvelle fonction un peu plus divertissante en renvoyant la pour chaque requête:

a7aaf656b78050fd.png

La liste suivante présente le code nécessaire:

main.py

# ...

def python_powered(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - The official "Python Powered" logo
    """
    return flask.send_file("python-powered.png")

Déployez une nouvelle fonction python_powered:

FUNCTION_NAME="python_powered"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

Résultat de la commande :

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

Pour tester la fonction, cliquez sur l'URL httpsTrigger.url affichée dans le résultat de la commande ci-dessus. Si tout fonctionne correctement, la mention " dans un nouvel onglet du navigateur.

Vous allez maintenant créer une application qui vous permettra d'exécuter et d'essayer votre fonction localement avant de la déployer.

7. Exécuter la fonction en local

Vous pouvez exécuter une fonction HTTP localement en créant une application Web et en appelant votre fonction dans un routage. Vous pouvez l'ajouter dans le même répertoire que votre fonction. Le fichier nommé web_app.py présente le contenu suivant:

web_app.py

import flask

import main

app = flask.Flask(__name__)


@app.get("/")
def index():
    return main.python_powered(flask.request)


if __name__ == "__main__":
    # Local development only
    # Run "python web_app.py" and open http://localhost:8080
    app.run(host="localhost", port=8080, debug=True)
  1. Ce fichier crée une application Flask.
  2. Il enregistre une route à l'URL de base, qui est gérée par une fonction nommée index().
  3. La fonction index() appelle ensuite notre fonction python_powered, en lui transmettant la requête actuelle.

Assurez-vous que le framework Flask est installé dans votre environnement de développement:

pip install flask

L'installation de Flask génère un résultat semblable à celui-ci:

Collecting flask
...
Successfully installed ... flask-3.0.2 ...

Pour exécuter cette application en local, exécutez la commande suivante:

python web_app.py

Utilisez maintenant l'aperçu sur le Web de Cloud Shell pour tester l'application Web dans votre navigateur. Dans Cloud Shell, cliquez sur le bouton "Aperçu sur le Web" puis sélectionnez "Prévisualiser sur le port 8080" :

6c9ff9e5c692c58e.gif

Cloud Shell utilise son service proxy pour ouvrir l'URL dans une nouvelle fenêtre de navigateur. L'aperçu sur le Web restreint l'accès via HTTPS à votre compte utilisateur uniquement. Si tout fonctionne correctement, la mention " logo!

8e5c3ead11cfd103.png

8. Félicitations !

b158ce75c3cccd6d.png

Vous avez déployé des fonctions HTTP Cloud Functions à l'aide de fonctions idiomatiques qui gèrent les requêtes Web avec le framework Flask.

Les tarifs de Cloud Functions sont basés sur la fréquence à laquelle votre fonction est appelée, et incluent une version sans frais pour les fonctions rarement exécutées. Une fois que vous avez terminé de tester vos fonctions Cloud, vous pouvez les supprimer à l'aide de gcloud:

gcloud functions delete hello_world --quiet
gcloud functions delete hello_name --quiet
gcloud functions delete python_powered --quiet

Vous pouvez également supprimer les fonctions à partir de la console Google Cloud.

Nous espérons que vous apprécierez l'utilisation de Cloud Functions en Python.