Prueba del sistema de Cloud Functions con Cloud Build y Terraform

En este instructivo, se describe cómo automatizar las pruebas de extremo a extremo de una app compilada mediante Cloud Functions. Cloud Build ejecuta la canalización de prueba y HashiCorp Terraform configura y quita los recursos de Google Cloud necesarios para ejecutar las pruebas. Un activador de Cloud Build inicia la canalización después de cada confirmación de código.

La capacidad de prueba es una consideración clave cuando se diseñan apps y se evalúan las opciones de arquitectura. Crear y ejecutar con regularidad un conjunto integral de pruebas (incluidas las pruebas de unidades automatizadas, de integración y del sistema de extremo a extremo) es esencial para verificar que la app se comporte como esperas. Para obtener detalles sobre cómo abordar cada categoría de pruebas en diferentes situaciones de Cloud Functions, consulta la guía de prácticas recomendadas para realizar pruebas.

Por lo general, es sencillo crear y ejecutar pruebas de unidades, ya que estas pruebas están aisladas y son independientes del entorno de ejecución. Sin embargo, las pruebas de integración y del sistema son más complejas, en particular en un entorno de nube. La necesidad de realizar pruebas del sistema de extremo a extremo es relevante en especial para las apps que usan tecnologías sin servidores, como Cloud Functions. Por lo general, estas aplicaciones están impulsadas por eventos y tienen una estructura flexible, y es posible que se implementen por separado. Las pruebas integrales de extremo a extremo son esenciales para verificar que las funciones responden de forma correcta a los eventos dentro del entorno de ejecución de Google Cloud.

Arquitectura

En el siguiente diagrama de arquitectura, se muestran los componentes que usas en este instructivo.

Diagrama de arquitectura de proyectos de compilación y prueba

La arquitectura tiene los siguientes componentes:

  • Un proyecto build que aloja y ejecuta la canalización de Cloud Build.
  • Un proyecto test que aloja recursos de Google Cloud para la app de muestra sometida a prueba.
    • La app que se describe en el instructivo Supervisión del rendimiento web sin servidores se usa como app de muestra.
    • Los recursos de Google Cloud para la app de muestra se crean y se destruyen por cada iteración de compilación. La base de datos de Firestore es una excepción. Se crea una vez y se vuelve a usar en todas las compilaciones posteriores.

Objetivos

  • Crear una canalización de Cloud Build a fin de ejecutar pruebas de unidades y de extremo a extremo para una app de muestra compilada mediante Cloud Functions
  • Usar Terraform desde adentro de la compilación para configurar y destruir los recursos de Google Cloud que requiere la app
  • Usar un proyecto de prueba exclusivo de Google Cloud para mantener aislado el entorno de prueba
  • Crear un repositorio de Git en Cloud Source Repositories y agregar un activador de Cloud Build para ejecutar la compilación de extremo a extremo luego de una confirmación

Costos

En este instructivo, se usan los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costos en función del uso previsto, usa la calculadora de precios. Es posible que los usuarios nuevos de Google Cloud sean aptos para obtener una prueba gratuita.

Cuando finalices este instructivo, podrás borrar los recursos creados para evitar que se te siga facturando. Para obtener más información, consulta cómo hacer una limpieza.

Antes de comenzar

  1. Selecciona o crea un proyecto de Cloud. Este es el proyecto test que aloja la app de muestra.

    Ir a la página Selector de proyectos

  2. Anota el ID del proyecto de Google Cloud para el proyecto test. Necesitarás este ID en la siguiente sección para configurar tu entorno.
  3. Habilita las API necesarias Cloud Build, Cloud Functions y Cloud Source Repositories para ese proyecto.

    Habilitar las API

  4. En Cloud Console, ve a la página Firestore.
  5. Ir a la página de Firestore
  6. Crea una base de datos de Firestore.

    Aprender a crear una base de datos de Firestore

  7. Selecciona o crea otro proyecto de Google Cloud. Este es el proyecto build que aloja la canalización de Cloud Build.
  8. Ir a la página Administración de recursos
  9. Asegúrate de que la facturación esté habilitada para tus proyectos de Google Cloud.

    Aprende a habilitar la facturación

Configura el entorno

En este instructivo, ejecutarás comandos en Cloud Shell. Cloud Shell es un entorno de shell con el SDK de Cloud ya instalado, en el que se incluye la herramienta de línea de comandos de gcloud, y que ya tiene establecidos los valores para tu proyecto actual. La inicialización de Cloud Shell puede tomar varios minutos.

  1. En Cloud Console para el proyecto build, abre Cloud Shell.

    Abrir Cloud Shell

  2. Configura una variable para el ID del proyecto de Google Cloud test que copiaste antes:

    export TEST_PROJECT=your-test-project-id
    

    Reemplaza lo siguiente:

    • your-test-project-id: el ID del proyecto de Google Cloud test.
  3. Establece el ID del proyecto y el número del proyecto build de Google Cloud actual como variables:

    export BUILD_PROJECT=$(gcloud config get-value core/project)
    export BUILD_PROJECT_NUM=$(gcloud projects list) \
        --filter="$BUILD_PROJECT" --format="value(PROJECT_NUMBER)"
    
  4. Establece una variable para la región de implementación:

    export REGION=us-central1
    

    Aunque en este instructivo se usa la región us-central1, puedes cambiarla a cualquier región en la que Cloud Functions esté disponible.

  5. Clona el repositorio que contiene el código de la app de muestra que se usa en este instructivo:

    git clone \
        https://github.com/GoogleCloudPlatform/solutions-serverless-web-monitoring.git
    
  6. Ve al directorio del proyecto:

    cd solutions-serverless-web-monitoring
    

Crea una infraestructura de prueba mediante Terraform

En este instructivo, se usa Terraform para crear y destruir los recursos de Google Cloud dentro del proyecto de prueba de forma automática. Crear recursos independientes para cada compilación ayuda a mantener las pruebas aisladas entre sí. Cuando aíslas las pruebas, las compilaciones pueden ocurrir de forma simultánea, y las aserciones de prueba se pueden realizar en los recursos específicos. Destruir los recursos al final de cada compilación ayuda a minimizar los costos.

En este instructivo, se implementa la app descrita en el instructivo Supervisión web sin servidores. La app consta de un conjunto funciones de Cloud Functions, depósitos de Cloud Storage, recursos de Pub/Sub y una base de datos de Firestore. La configuración de Terraform define los pasos necesarios para crear estos recursos. Terraform no implementa la base de datos de Firestore; la base de datos se crea una vez y se vuelve a usar en todas las pruebas.

En la siguiente muestra de código del archivo de configuración main.tf de Terraform, se muestran los pasos necesarios para implementar trace de Cloud Functions. Consulta el archivo completo para obtener la configuración completa.

data "archive_file" "local_tracer_source" {
  type        = "zip"
  source_dir  = "./functions/tracer"
  output_path = "${var.local_output_path}/tracer.zip"
}

resource "google_storage_bucket_object" "gcs_tracer_source" {
  name   = "tracer.zip"
  bucket = "${google_storage_bucket.bucket_source_archives.name}"
  source = "${data.archive_file.local_tracer_source.output_path}"
}

resource "google_cloudfunctions_function" "function_tracer" {
  name = "tracer-${var.suffix}"
  project = "${var.project_id}"
  region = "${var.region}"
  available_memory_mb = "1024"
  entry_point = "trace"
  runtime = "nodejs8"
  trigger_http = "true"
  source_archive_bucket = "${google_storage_bucket.bucket_source_archives.name}"
  source_archive_object = "${google_storage_bucket_object.gcs_tracer_source.name}"
  environment_variables = {
    BUCKET_METRICS = "${google_storage_bucket.bucket_metrics.name}"
    ALLOWED_HOSTS = "${var.allowed_hosts}"
  }
}

// prevent unauthenticated invocations
resource "google_cloudfunctions_function_iam_binding" "tracer_disallow_unauthenticated" {
  project = "${var.project_id}"
  region = "${var.region}"
  cloud_function = "${google_cloudfunctions_function.function_tracer.name}"
  role = "roles/cloudfunctions.invoker"
  members = [
  ]
  depends_on = [
    google_cloudfunctions_function.function_tracer
  ]
}

En este paso, ejecutarás la configuración de Terraform para implementar los recursos de prueba. En un paso posterior, Cloud Build implementará los recursos de forma automática.

  1. En Cloud Shell, inicializa Terraform:

    docker run -v $(pwd):/app -w /app hashicorp/terraform:0.12.0 init
    

    Usa la imagen de Docker pública de Terraform. Docker ya está instalado en Cloud Shell. El directorio de trabajo actual se activa como un volumen para que el contenedor de Docker pueda leer el archivo de configuración de Terraform.

  2. Crea los recursos mediante el comando apply de Terraform:

    docker run -v $(pwd):/app -w /app hashicorp/terraform:0.12.0 apply \
        --auto-approve \
        -var "project_id=$TEST_PROJECT" \
        -var "region=$REGION" \
        -var "suffix=$TEST_PROJECT"
    

    El comando incluye variables que especifican el proyecto de Google Cloud y la región donde deseas que se creen los recursos de prueba. También incluye un sufijo que se usa a fin de crear recursos con nombres para este paso. En un paso posterior, Cloud Build proporciona un sufijo adecuado de forma automática.

    La operación toma unos minutos en completarse.

  3. Confirma que los recursos se crearon en el proyecto test:

    gcloud functions list --project $TEST_PROJECT
    

    En el resultado, se muestran tres funciones de Cloud Functions cuyos nombres terminan con el sufijo proporcionado antes.

Ejecuta pruebas de extremo a extremo

En esta sección, ejecutarás pruebas de extremo a extremo en la infraestructura de prueba que implementaste en la sección anterior.

En el siguiente fragmento de código, se muestran las pruebas. Las pruebas verifican tanto la situación de éxito como la de error. La canalización de prueba se puede resumir de la siguiente manera:

  • Primero, la prueba invoca la función trace. Esta llamada inicia un flujo de eventos a través de la app que activa otras funciones.
  • Luego, la prueba verifica el comportamiento de cada función y confirma que los objetos se escriben en Cloud Storage, los resultados se conservan en Firestore y se generan las alertas de Pub/Sub en caso de fallas.
def test_e2e_pass():
  run_pipeline('http://www.example.com/', True)

def test_e2e_fail():
  run_pipeline('https://cloud.google.com/docs/tutorials', False)

def run_pipeline(url, should_pass):
  """Triggers the web analysis pipeline and verifies outputs of each stage.

  Args:
    url (str): The page to analyze.
    should_pass (bool): Whether the page should load within the threshold time.
  """
  trace_response = call_tracer(url)
  filename = assert_tracer_response(trace_response)
  assert_gcs_objects(filename)
  assert_firestore_doc(filename, should_pass)
  assert_pubsub_message(should_pass)

  # clean up
  delete_gcs_objects(filename)
  delete_firestore_doc(filename)

Para ejecutar las pruebas de extremo a extremo, sigue estos pasos:

  1. En Cloud Shell, crea un entorno virtualenv nuevo. La utilidad virtualenv ya está instalada en Cloud Shell.

    virtualenv venv
    
  2. Activa el entorno virtualenv:

    source venv/bin/activate
    
  3. Instala las bibliotecas de Python necesarias:

    pip install -r requirements.txt
    
  4. Ejecuta las pruebas de extremo a extremo:

    python -m pytest e2e/ --tfstate terraform.tfstate
    

    Debes pasar el archivo de estado de Terraform, que contiene detalles de los recursos de prueba creados en la sección anterior.

    Las pruebas pueden tomar unos minutos en completarse. Aparecerá un mensaje que indica que se aprobaron dos pruebas. Puedes ignorar las advertencias.

  5. Quita los recursos de prueba mediante el comando destroy de Terraform:

    docker run -v $(pwd):/app -w /app hashicorp/terraform:0.12.0 destroy \
        --auto-approve \
        -var "project_id=$TEST_PROJECT" \
        -var "region=$REGION" \
        -var "suffix=$TEST_PROJECT"
    
  6. Confirma que los recursos se destruyen:

    gcloud functions list --project $TEST_PROJECT
    

    Ya no hay funciones de Cloud Functions con nombres que terminen con el sufijo proporcionado antes.

Envía la canalización de Cloud Build

En esta sección, usarás Cloud Build para automatizar la canalización de pruebas.

Configura los permisos de Cloud Build

Ejecuta Cloud Build mediante una cuenta de servicio de Cloud Build. Las pruebas del sistema que ejecutó la compilación crean y, además, interactúan con las funciones de Cloud Functions, los depósitos de Cloud Storage, los recursos de Pub/Sub y los documentos de Firestore. Para hacer esto, Cloud Build requiere lo siguiente:

En este procedimiento, agregarás las funciones adecuadas y, luego, la cuenta de servicio de Cloud Build.

  1. En Cloud Shell, agrega las funciones de IAM adecuadas a la cuenta de servicio predeterminada de Cloud Build:

    for role in cloudfunctions.developer pubsub.editor storage.admin datastore.user; do \
        gcloud projects add-iam-policy-binding $TEST_PROJECT \
        --member="serviceAccount:$BUILD_PROJECT_NUM@cloudbuild.gserviceaccount.com" \
        --role="roles/$role"; \
        done
    
  2. Agrega la cuenta de servicio de Cloud Build como un serviceAccountUser de la cuenta de servicio de App Engine dentro del proyecto de prueba:

    gcloud iam service-accounts add-iam-policy-binding \
        $TEST_PROJECT@appspot.gserviceaccount.com \
        --member="serviceAccount:$BUILD_PROJECT_NUM@cloudbuild.gserviceaccount.com" \
        --role=roles/iam.serviceAccountUser \
        --project $TEST_PROJECT
    

Envía una compilación manual

La compilación realiza cuatro tareas lógicas:

  • Ejecutar pruebas de unidades
  • Implementar la app de muestra
  • Ejecutar pruebas de extremo a extremo
  • Destruir la app de muestra

Revisa el siguiente fragmento de código del archivo cloudbuild.yaml. En el fragmento, se ilustran los pasos individuales de Cloud Build para implementar la app de muestra mediante Terraform y ejecutar las pruebas de extremo a extremo.

# setup Terraform using public terraform Docker image
- id: terraform-init
  name: hashicorp/terraform:0.12.0
  args: ['init']

# deploy the required GCP resources
- id: terraform-apply
  name: hashicorp/terraform:0.12.0
  args: ['apply', '-auto-approve']
  env:
    - 'TF_VAR_project_id=$_TEST_PROJECT_ID'
    - 'TF_VAR_region=$_REGION'
    - 'TF_VAR_suffix=$BUILD_ID'

# run end-to-end tests to verify live interactions
- id: end-to-end-tests
  name: 'python:3.7-slim'
  entrypoint: /bin/sh
  args:
    - -c
    - 'pip install -r requirements.txt && python -m pytest e2e --tfstate terraform.tfstate'

Para enviar una compilación manual a Cloud Build y ejecutar pruebas de extremo a extremo, haz lo siguiente:

  • En Cloud Shell, ingresa lo siguiente:

    gcloud builds submit --config cloudbuild.yaml \
        --substitutions=_REGION=$REGION,_TEST_PROJECT_ID=$TEST_PROJECT
    

    La compilación toma varios minutos en ejecutarse. Se producen los siguientes pasos de compilación:

    • Cloud Build usa sustituciones a fin de proporcionar variables que especifiquen el proyecto y la región de Google Cloud para los recursos de prueba que crees.

    • Cloud Build ejecuta la compilación en el proyecto build de Google Cloud. Los recursos de prueba se crean en el proyecto test separado.

    Los registros de compilación se transmiten a Cloud Shell para que puedas seguir el progreso de la compilación. La transmisión de registros finaliza cuando se completa la compilación. Se muestran mensajes que indican que el paso de compilación final terraform-destroy se completó de forma correcta.

Automatiza la ejecución de pruebas

Un principio clave de la integración continua (CI) es ejecutar con regularidad un conjunto de pruebas integrales automatizadas. Por lo general, la canalización de prueba de compilación se ejecuta para cada confirmación en un repositorio de código compartido. Esta configuración ayuda a comprobar que cada confirmación en el repositorio compartido se pruebe y se valide, lo que permite que tu equipo detecte problemas con anticipación.

En las siguientes secciones, realizarás las acciones a continuación:

  • Crear un repositorio de Git en Cloud Source Repositories
  • Agregar un activador de Cloud Build para ejecutar la compilación de extremo a extremo en cada confirmación
  • Enviar código al repositorio para activar la compilación

Crea un repositorio de Cloud Source Repository y un activador de Cloud Build

  1. En Cloud Shell, crea un Cloud Source Repository nuevo:

    gcloud source repos create serverless-web-monitoring
    
  2. En Cloud Console, abre la página Activadores de Cloud Build.

    Ir a la página Activadores

  3. Haz clic en Crear activador.

    Se abre la página Crear activador.

  4. Completa las siguientes opciones:

    • En el campo Nombre, escribe end-to-end-tests.
    • En Evento, selecciona Enviar a una rama.
    • En Fuente, selecciona severless-web-monitoring como tu repositorio y ^master$ como la rama.
    • En Configuración de compilación, selecciona Archivo de configuración de Cloud Build (YAML o JSON).
    • En el campo Ubicación del archivo de configuración de Cloud Build, escribe cloudbuild.yaml.
    • Para agregar una sustitución de variable a fin de especificar la región de Google Cloud donde se crearán los recursos de prueba, haz clic en Agregar variable:

      • Variable: _REGION
      • Valor: your-test-region

        Reemplaza lo siguiente:

        • your-test-region: El valor de la variable $REGION en Cloud Shell.
    • Si deseas agregar otra alternativa para especificar el ID del proyecto que alojará los recursos de prueba, haz clic en Agregar variable:

      • Variable: _TEST_PROJECT_ID
      • Valor: your-test-project

        Reemplaza lo siguiente:

        • your-test-project: El valor de la variable $TEST_PROJECT en Cloud Shell.
  5. Haz clic en Crear para guardar el activador de compilación.

Comienza la compilación

  1. En Cloud Shell, agrega el repositorio como un repositorio remoto nuevo en tu archivo de configuración de git:

    git remote add csr \
        https://source.developers.google.com/p/$BUILD_PROJECT/r/serverless-web-monitoring
    
  2. Para activar la compilación, envía el código al repositorio:

    git push csr master
    
  3. Enumera las compilaciones más recientes:

    gcloud builds list --limit 3
    

    En el resultado, se muestra una compilación en estado WORKING, lo que indica que la compilación se activó como se esperaba.

  4. Copia el ID de la compilación WORKING para el siguiente paso.

  5. Transmite los registros de compilación a Cloud Console:

    gcloud builds log --stream build-id
    

    Reemplaza lo siguiente:

    • build-id: El ID de la compilación WORKING que copiaste en el paso anterior.

    La transmisión de registros finaliza cuando se completa la compilación. Se muestran mensajes que indican que el paso de compilación final terraform-destroy se realizó de forma correcta y que la compilación se completó.

Realice una limpieza

Para evitar que se apliquen cargos a tu cuenta de Google Cloud por los recursos usados en este instructivo, borra el proyecto que contiene los recursos o conserva el proyecto y borra los recursos individuales.

Borra el proyecto

  1. En Cloud Console, ve a la página Administrar recursos.

    Ir a Administrar recursos

  2. En la lista de proyectos, elige el proyecto que quieres borrar y haz clic en Borrar.
  3. En el diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrar el proyecto.

Próximos pasos