A logo showing the text blog.marcnuri.com
English
Inicio»Go»Cómo Testear Aplicaciones Kubernetes en Go con EnvTest: Una Guía Práctica

Entradas Recientes

  • Fabric8 Kubernetes Client 7.2.0 está disponible!
  • Conectarse a un servidor MCP con JavaScript y AI SDK
  • Conectarse a un servidor MCP con JavaScript y LangChain.js
  • El Futuro de las Herramientas para Desarrolladores en la era de la IA
  • Conectarse a un servidor Model Context Protocol (MCP) con Java y LangChain4j

Categorías

  • Antiguo
  • Front-end
  • Go
  • Herramientas
  • Industria y negocios
  • Inteligencia Artificial
  • Java
  • JavaScript
  • Operaciones
  • Personal
  • Proyectos personales

Archivos

  • mayo 2025
  • abril 2025
  • marzo 2025
  • febrero 2025
  • enero 2025
  • diciembre 2024
  • noviembre 2024
  • agosto 2024
  • junio 2024
  • mayo 2024
  • abril 2024
  • marzo 2024
  • febrero 2024
  • enero 2024
  • diciembre 2023
  • noviembre 2023
  • octubre 2023
  • septiembre 2023
  • agosto 2023
  • julio 2023
  • junio 2023
  • mayo 2023
  • abril 2023
  • marzo 2023
  • febrero 2023
  • enero 2023
  • diciembre 2022
  • noviembre 2022
  • octubre 2022
  • agosto 2022
  • julio 2022
  • mayo 2022
  • marzo 2022
  • febrero 2022
  • enero 2022
  • diciembre 2021
  • noviembre 2021
  • octubre 2021
  • septiembre 2021
  • agosto 2021
  • julio 2021
  • diciembre 2020
  • octubre 2020
  • agosto 2020
  • junio 2020
  • mayo 2020
  • marzo 2020
  • febrero 2020
  • enero 2020
  • noviembre 2019
  • octubre 2019
  • julio 2019
  • diciembre 2018
  • agosto 2018
  • julio 2018
  • junio 2018
  • mayo 2018
  • marzo 2018
  • febrero 2018
  • noviembre 2017
  • octubre 2017
  • agosto 2017
  • julio 2017
  • enero 2017
  • julio 2016
  • enero 2016
  • diciembre 2015
  • noviembre 2015
  • diciembre 2014
  • marzo 2014
  • febrero 2011
  • junio 2008
  • mayo 2008
  • abril 2008
  • enero 2008
  • junio 2007
  • mayo 2007
  • abril 2007
  • marzo 2007

Cómo Testear Aplicaciones Kubernetes en Go con EnvTest: Una Guía Práctica

2023-11-27 en Go etiquetado Go / Kubernetes / kubectl / client-go / Testing / Test-Driven Development (TDD) / Operadores por Marc Nuri | Última actualización: 2025-02-24
English version

Introducción

Cuando desarrollas aplicaciones que interactúan con un cluster Kubernetes (ya sean operadores, controladores o herramientas personalizadas), es fundamental implementer tests robustos. Los enfoques tradicionales pueden implicar iniciar un cluster completo con Minikube, usar TestContainers o incluso depender de un cluster de testing dedicado en CI/CD. Sin embargo, estas soluciones pueden ser intensivas en recursos o difíciles de integrar en todas tus pipelines.

Aquí es donde entra en juego EnvTest: una alternativa ligera y rápida proporcionada por el proyecto controller-runtime. EnvTest inicia un servidor API de Kubernetes y una instancia de etcd, lo que te permite realizar tests de integración que cubren las interacciones declarativas básicas con el servidor API sin la sobrecarga de un cluster completo.

En este artículo, te mostraré cómo configurar y usar EnvTest en tus proyectos Go, especialmente cuando testees operadores o controladores. Incluso volveré a mi ejemplo del plugin kubectl kill namespace para ilustrar escenarios de testing prácticos.

¿Qué es EnvTest?

EnvTest es un potente paquete dentro del proyecto controller-runtime diseñado para testear aplicaciones Kubernetes.

Aquí tienes algunos puntos clave:

  • Ligero y Rápido: Inicia un servidor API local y etcd, proporcionándote un entorno casi idéntico a un cluster sin necesidad de toda la infraestructura de Kubernetes.
  • Eficiente en Recursos: Perfecto para desarrollo local y pipelines de CI/CD donde iniciar un cluster completo es complicado.
  • Centrado en Operaciones Declarativas: Ideal cuando tus tests implican crear, actualizar o eliminar recursos Kubernetes, en lugar de simular Pod scheduling u operaciones de nodos completas.

Nota

EnvTest no es adecuado para aplicaciones que requieran comportamientos completos de cluster (por ejemplo, programación o ejecución de Pods). Para esos casos, deberías considerar usar Minikube, TestContainers o un cluster de Kubernetes basado en la nube.

Configurando EnvTest

La mayoría de las guías se basan en que los binarios de EnvTest ya estén instalados en el sistema. Algunas de ellas te muestran cómo descargarlos usando un Makefile, mientras que otras ni siquiera mencionan cómo obtenerlos.

En esta sección, te mostraré cómo usar el paquete EnvTest directamente desde tu código Go, sin depender de ninguna herramienta externa.

Creando la función setupEnvTest

Comencemos creando una función setupEnvTest encargada de configurar el entorno EnvTest. El siguiente fragmento de código contiene pseudo-código para describir la función:

func setupEnvTest() *envtest.Environment {
	// Download EnvTest binaries
	// Create a new instance of the EnvTest environment
}

Esta función es responsable de descargar si es necesario los binarios de EnvTest, y de crear una nueva instancia del entorno EnvTest para dichos binarios.

Centrémonos en la primera parte, la descarga de los binarios.

func setupEnvTest() *envtest.Environment {
	envTestDir, err := store.DefaultStoreDir()
	if err != nil {
		panic(err)
	}
	envTest := &env.Env{
		FS:  afero.Afero{Fs: afero.NewOsFs()},
		Out: os.Stdout,
		Client: &remote.HTTPClient{
			IndexURL: remote.DefaultIndexURL,
		},
		Platform: versions.PlatformItem{
			Platform: versions.Platform{
				OS:   runtime.GOOS,
				Arch: runtime.GOARCH,
			},
		},
		Version: versions.AnyVersion,
		Store:   store.NewAt(envTestDir),
	}
	envTest.CheckCoherence()
	workflows.Use{}.Do(envTest)
}

Vamos a analizar el código paso a paso:

  • Primero, obtenemos el directorio por defecto de EnvTest usando la función store.DefaultStoreDir(). De esta forma, podemos reutilizar cualquier binario que ya haya sido descargado por otros tests o herramientas externas.
  • A continuación, creamos una nueva instancia de la estructura env.Env. Esta estructura proporciona toda la información necesaria como el sistema operativo, la arquitectura, la versión, etc. para descargar los binarios.
  • Después, validamos e inicializamos el entorno usando la función envTest.CheckCoherence().
  • Por último, usamos la función workflows.Use{}.Do(envTest) para descargar los binarios si es necesario.

La siguiente parte de la lógica de la función se encarga de crear una nueva instancia del entorno EnvTest:

func setupEnvTest() *envtest.Environment {
	// Create a new instance of the EnvTest environment
	versionDir := envTest.Platform.Platform.BaseName(*envTest.Version.AsConcrete())
	return &envtest.Environment{
		BinaryAssetsDirectory: filepath.Join(envTestDir, "k8s", versionDir),
	}
}

Empleamos la función envTest.Platform.Platform.BaseName(*envTest.Version.AsConcrete()) para obtener el directorio que contiene los binarios de la versión de EnvTest que hemos descargado. A continuación, creamos una nueva instancia de la estructura envtest.Environment, pasando el directorio donde se encuentran los binarios.

Ahora que ya tenemos la función setupEnvTest lista, continuemos viendo cómo usarla en nuestros tests.

Usando EnvTest para Tests de Integración

En esta sección, te mostraré cómo usar la función setupEnvTest que creamos en la sección anterior para testear el plugin kubectl kill namespace que analicé en una publicación anterior. Puedes encontrar el código completo de los tests en el archivo killns_test.go del proyecto.

Ya que el plugin kubectl kill namespace se encarga de eliminar Namespaces, voy a centrarme en testear la lógica de eliminación de Namespaces. Para ello utilizaré subtests para iniciar el entorno EnvTest una sola vez y ejecutar cada escenario de test en un subtest diferente.

El siguiente fragmento de código muestra la configuración y la limpieza del entorno, y uno de los subtests de la función TestKillNamespace:

func TestKillNamespace(t *testing.T) {
	envTest := setupEnvTest()
	envTestConfig, err := envTest.Start()
	if err != nil {
		t.Errorf("Error starting test environment: %s", err)
		return
	}
	defer func() {
		if stopErr := envTest.Stop(); stopErr != nil {
			panic(stopErr)
		}
	}()
	// Test with no namespace
	// Test with existent namespace
	t.Run("With existent namespace with finalizer", func(t *testing.T) {
		// Given
		client, _ := kubernetes.NewForConfig(envTestConfig)
		client.CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{
			ObjectMeta: metav1.ObjectMeta{
				Name: "finalizer",
			},
			Spec: corev1.NamespaceSpec{Finalizers: []corev1.FinalizerName{"kubernetes"}},
		}, metav1.CreateOptions{})
		// When
		KillNamespace(envTestConfig, "finalizer")
		// Then
		_, err := client.CoreV1().Namespaces().Get(context.TODO(), "finalizer", metav1.GetOptions{})
		if err.Error() != "namespaces \"finalizer\" not found" {
			t.Errorf("Namespace should have been deleted, but it wasn't")
		}
	})
}

Desglose del Test:

  • Inicio del Entorno:
    Comienzo el test llamando a la función setupEnvTest para inicializar el entorno EnvTest.
    A continuación, inicio el entorno llamando a la función envTest.Start().
    Si se produce algún error al iniciar el entorno, fallo el test y retorno.
  • Detención del Entorno:
    Utilizo una sentencia defer para detener y limpiar el entorno una vez que el test finaliza.
    De esta forma, me aseguro de que el entorno siempre se detiene, incluso si el test falla.
  • Inicialización del Cliente:
    Inicializo una instancia del Cliente de Kubernetes client-go pasando la estructura rest.Config que obtengo de la función envTest.Start().
  • Subtests para Claridad:
    Utilizo la funcionalidad de subtests de Go (t.Run) para agrupar los diferentes escenarios que queremos testear.
    De esta forma, puedo mantener el código del test organizado y fácil de leer.
  • Escenario de Test:
    • Creo un nuevo Namespace con un finalizador usando el cliente (assemble/given).
    • A continuación, llamo a la función KillNamespace que queremos testear (act/when).
    • Por último, compruebo que el Namespace fue eliminado intentando obtenerlo usando el cliente (assert/then).

Conclusión

En esta publicación, te he mostrado cómo aprovechar el paquete EnvTest de controller-runtime para realizar tests de integración ligeros pero efectivos para aplicaciones Kubernetes escritas en Go. He explicado cómo configurar el entorno de testing, descargar los binarios necesarios de forma programática y usar EnvTest en escenarios prácticos como testear la eliminación de Namespaces.

Integrando estas prácticas en tu flujo de desarrollo, puedes lograr ciclos de feedback más rápidos, tests más fiables y pipelines de CI/CD más eficientes, todo ello sin la sobrecarga de gestionar un cluster Kubernetes completo.

Para ver el código fuente completo y más ejemplos, visita el repositiorio de GitHub.

Twitter iconFacebook iconLinkedIn iconPinterest iconEmail icon

Navegador de artículos
Cómo encontrar el digest (sha256) de una imagen Docker multi-plataformaCómo crear un plugin de kubectl con client-go para eliminar un Namespace
© 2007 - 2025 Marc Nuri