Creando un plugin para el cliente de CF

Introducción

Hace unos días publiqué en el repositorio oficial de la Cloud Foundry Community mi primer plugin apra el cliente de CF. Aquí el resultado:

https://plugins.cloudfoundry.org/#check-before-deploy

En este post aprenderemos a crear un plugin para el cliente de CF y a publicarlo en el repositorio oficial.

workflow 1

Para poder construir vuestro plugin necesitareis instalar el compilador de GO en vuestro ordenador. Tan facil como seguir las instrucciones de su página oficial GO install.

Una vez instalado, vamos por faena…

Construcción del plugin

Estructura inicial

Lo más fácil para iniciar vuestro plugin es copiar el archivo de la página de oficial de GITHUB. Este será el punto de partida basic_plugin.go.

A continuación pasamos a detallar los puntos claves del código que tenemos entre manos, empezaremos por el final del fichero:

Inicializando el plugin

func main() {
	plugin.Start(new(BasicPlugin))
}

Lo primero es añadir la llamada para que al instalar el plugin en el cliente de CF pueda leer el matedata. Este metadata, será otra función que tiene la información básica del plugin: version, descripciones y comandos a ejecutar.

Vemos que uno de los parámetros del constructor es «BasicPlugin», esta variable se declara al inicio del fichero y contiene la interface necesaria definida en el core del cliente de CF. Inicialmente lo tendremos vacío y le podremos poner el nombre que queramos.

type BasicPlugin struct{}

Metadata

Lo siguiente a tratar es el metadata, en el informaremos la versión, la descripción del plugin y su funcionamiento y su comandos disponibles.

func (c *BasicPlugin) GetMetadata() plugin.PluginMetadata {
	return plugin.PluginMetadata{
		Name: "MyBasicPlugin",
		Version: plugin.VersionType{
			Major: 1,
			Minor: 0,
			Build: 0,
		},
		MinCliVersion: plugin.VersionType{
			Major: 6,
			Minor: 7,
			Build: 0,
		},
		Commands: []plugin.Command{
			{
				Name:     "basic-plugin-command",
				HelpText: "Basic plugin command's help text",

				// UsageDetails is optional
				// It is used to show help of usage of each command
				UsageDetails: plugin.Usage{
					Usage: "basic-plugin-command\n   cf basic-plugin-command",
				},
			},
		},
	}
}

Como vemos a simple vista, indicamos el nombre del plugin en el campo «Name», las versiones del plugin «Version» y versión mínima del cliente de CF «MinCliVersion» y por último el los comandos que podemos ejecutar en «Commands».

Ejecución del plugin

Ha llegado el momento de programar las acciones que hemos definido y llega el último paso del plugin de ejemplo.

func (c *BasicPlugin) Run(cliConnection plugin.CliConnection, args []string) {
	// Ensure that we called the command basic-plugin-command
	if args[0] == "basic-plugin-command" {
		fmt.Println("Running the basic-plugin-command")
	}
}

En este caso, vemos como al ejecutar el cliente de CF en caso que el primer argumento sea nuestro nuevo plugin mostraremos por consola el mensaje «Running the basic-plugin-command».

En este punto es importante remarcar que el plugin es una extensión del core del cliente, por lo que lo que hacemos en realidad es añadir una extensión. Esto se ve al tener que controlar el primer parametro del plugin.

De momento no hemos hecho mucho, pero ya tenemos la primera base para trabajar.

Instalando el plugin de local

Con el fichero de ejemplo que hemos descargado de github procederemos a compliar e intalar el plugin.

Como recomendación guardar el fichero en %directorio de vuestro usuario%/go/<nombre_del_plugin>

Compilar plugin

Tan fácil como ir por terminal a la carpeta donde tenemos nuestro fichero go y ejecutar la sentencia «build»

go build .\plugin.go

En algunos casos podemos tener algún problema al compilar por falta de librerias, en ese caso las podemos importar mediante la instrucción «go get code.foundry.org/cli»

En ese momento, si todo ha ido bien, tendremos nuestro plugin a punto como vemos en la imagen:

De momento no nos preocupamos de donde tenemos los ficheros, lo habitual es tener una carpeta de build, pero estos pasos son a modo tutorial.

Instalando el plugin (local) al CLI CF

Todo listo, vamos a instalar nuestro plugin y a probarlo. Primero ejecutaremos la instrucción del cliente de CF que incluye el plugin de nuestro plugin local.

cf install-plugin .\plugin.exe -f

Como vemos se ha instalado correctamente, ya solo nos queda ejecutar el cliente de CF para ver el resultado:

Añadiendo comandos extra

Todos los comandos dentro de nuestro plugin se informan en el metadata (para que el usuario sepa de su existencia) y los podremos programar en la función «Run».

Para añadir un nuevo comando añadiremos una línea como la siguiente en la función metadata:

Como vemos, hemos añadido dos nuevos comandos, el primero es tipo flag, sirve para activar alguna funcionalidad… el típico «-algo» el segundo será un parámetro que tendrá asociado un valor de string como parámetro.

De momento, aunque les hemos puesto estos nombres, los parámetros añadidos no tienen mucha utilidad.

Para los próximos pasos utilizaremos la librería «flag», bastante útil para tratar los parámetros de entrada. Importaremos la librería al incio del fichero.

import (
	"flag"
	"fmt"
	"code.cloudfoundry.org/cli/plugin"
)

Vamos a añadir algo de lógica para ver cómo se comportan, para ello en la función «Run» añadiremos las siguientes líneas:

func (c *BasicPlugin) Run(cliConnection plugin.CliConnection, args []string) {

	plugin := flag.NewFlagSet("basic-plugin-command", flag.ExitOnError)
	flag := plugin.Bool("NewCommandFlag", false, "Description")
	string := plugin.String("NewCommandString", "", "Description")

	err := plugin.Parse(args[1:])
	if err != nil {
		fmt.Println("ERROR:>")
		fmt.Println(err)
	}

	if *flag {
		fmt.Println("Flag Activate")
	}

	if *string != "" {
		fmt.Println("String Activate",*string)
	}

}

El primer bloque nos encargamos de extraer nuestro plugin de la llamada que realiza el usuario. Una vez extraído, los dos siguientes códigos nos permiten extraer los comandos que queremos definir como Flag y String.

flag := plugin.Bool("NewCommandFlag", false, "Description")

Al llamar a la función Bool, le pasamos el comando a obtener (si existe), el valor por defecto y una descripción por si falla.

string := plugin.String("NewCommandString", "", "Description")

De igual manera que en el caso anterior, la función String tiene como parámetro el comando a obtener (también si existe) el valor por defecto y una descripción.

El siguiente bloque permite validar si se han pasado los parámetros correctamente y desplazamos el puntero de parametros a la posición 1 donde tenemos los parametros propios de nuestro plugin.

err := plugin.Parse(args[1:])
if err != nil {
    fmt.Println("ERROR:>")
    fmt.Println(err)
}

Ya solo nos queda añadir la lógica propia de cada comando, en este caso, solo mostramos descriptivos.

if *flag {
	fmt.Println("Flag Activate")
}

if *string != "" {
	fmt.Println("String Activate",*string)
}

Ya solo nos queda volver a compilar e instalar el plugin. Una vez realizado… lo podemos lanzar de la siguiente manera:

cf basic-plugin-command -h
cf basic-plugin-command -NewCommandString MyString --NewCommandFlag 

Llamando a otros comandos

Muchas veces será necesario llamar a otros comandos del cliente CF para poder obtener datos sin tener que implementar llamadas a la API, para ello hay dos maneras de proceder.

Utilizar la función de lanzamiento de comandos o mirar si la librería del cliente tiene incorporada la llamada a un comando. Esto parece un poco complicado, pero con un ejemplo se ve mejor.

Lanzando un commando CF

Mediante la librería cliConnection, que tiene los datos de connectividad del cliente CF, podemos invocar como llamada alguno de los comandos estándar del cliente.

Por ejemplo extraemos los servicios disponibles de nuestra cuenta de CF

command_result, errorCliCommand := cliConnection.CliCommandWithoutTerminalOutput("marketplace", "-s", resource.Parameters.Service)

En este caso, al llamar «CliCommandWithoutTerminalOutput» con el comando a lanzar y sus opciones, tendremos en la variable command_result el string con el resultado de esa consulta. Igual que si lo hiciéramos nosotros mismos des del cliente.

Para poder ver como funciona, añadiremos a nuestra función «Run» la instrucción anterior. También añadiremos un print para extraer el resultado por consola.

Y aquí el resultado:

Ahora solo seria necesario parsear el String en busca de la información necesaria.

Llamando a una función del cliente CF

Otra manera de realizar la llamada es buscar si existe dentro de la librería «cliConnection». La documentación la tenéis todos los métodos posibles: DOC.md

Para ver un ejemplo. Obtendremos los datos de un servicio concreto. Para ello utilizaremos la siguiente función:

service,errorCliCommand = cliConnection.GetService("MyService")
fmt.Println(service.Guid,*string)

En la documentación podemos ver la estructura de salida que podemos utilizar

Si añadimos este código en la función «Run», compilamos y volvemos a ejecutar (acordaros de cambiar el servicio 😉 ) el plugin tendremos un resultado parecido a al siguiente:

En este caso, hemos extraído el ID del servicio como ejemplo.

Publicando el plugin

Una vez tenemos creado y probado el plugin, podemos subirlo a la página oficial de repositorios de la communidad Cloud Foundry.

En este apartado no realizaré guia ya que está bastante clara en el mismo repositorio. Pero si que aprovecharé para dar algunos consejos.

Lo primero es hacer fork del repositorio padre para modificar el respositorio:

https://github.com/cloudfoundry/cli-plugin-repo

Consejos para la publicación

  • Asegurate que los commits que realices al repositorio están con tu nombre. Esto es tan fácil como validar tu nombre de usuario del cliente GIT que uses mediante «git config –global user.name».
  • Acuérdate de firmar la declaración de autoría «EasyCLA«. No te preocupes si no sabes de qué hablo, en caso de faltar en el pull request te pedirá que lo hagas.
  • Para obtener el SHA1 de los ficheros existen programas en windows bastantes utiles como http://implbits.com/products/hashtab/ , pero acuerdate que la validación se del repositorio es case-sensitive, pasa tus caracteres a minúscula antes.
  • Si no sabes donde alojar tus ficheros compilados una buena idea es añadirlos como release en GITHUB

Hasta aquí cómo construir vuestro plugin, os dejo el que he creado como ejemplo por si os puede servir de inspiración.

https://github.com/enric11/cf-cli-check-before-deploy/

Como siempre os digo, Suscribete y comparte en redes para estar a la última.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *