Integración de Concourse-CI y Atlassian Open DevOps
Muchos equipos utilizan sus propias herramientas para satisfacer sus necesidades o tienen herramientas heredadas que han utilizado durante años. Estas herramientas son esenciales para el proceso de desarrollo que sigue un equipo, pero no tienen integraciones estándar con Jira. Por suerte, es sencillo crear una integración personalizada con las API de REST de Atlassian que se encuentran en la documentación para desarrolladores de Cloud, Atlassian Developer. Concourse-CI es un producto de CI/CD que, en el momento en que se escribe este artículo, no está integrado en Atlassian Marketplace. Este artículo demuestra cómo crear una integración básica entre Jira y Concourse-CI mediante las API de REST de Atlassian.
Requisitos previos
Usa la documentación necesaria para configurar Docker, docker-compose y Concourse-CI. Concourse-CI se ejecuta en Docker y proporciona un guion de composición de Docker para simplificar los primeros pasos.
Obtén información sobre la aplicación de demostración ImageLabeller de Atlassian aquí. Este artículo demuestra cómo usar Concourse-CI para implementar el componente SubmitImage de ImageLabeller en AWS.
Docker
Configura Docker y docker-compose siguiendo la documentación asociada:
Docker: https://docs.docker.com/get-docker/
docker-compose: https://docs.docker.com/compose/install/
Concourse-CI
Una vez instalados Docker y docker-compose, puedes iniciar Concourse-CI con el archivo docker-compose.yml proporcionado.
Sigue la guía de inicio rápido de Concourse-CI para empezar con https://concourse-ci.org/quick-start.html#docker-compose-concourse. Esta guía requiere pasar las credenciales a Concourse-CI de forma segura. La guía utiliza la integración de AWS Secrets Manager de Concourse-CI para este propósito.
Integración de Concourse-CI con AWS Secrets Manager
Aquí tienes la documentación sobre cómo integrar Concourse-CI con AWS Secrets Manager. Sigue las instrucciones de la documentación para habilitar la integración y empezar.
El archivo docker-compose.yml que se utiliza para iniciar Concourse-CI debe modificarse ligeramente para que la integración funcione. Toma el archivo docker-compose.yml predeterminado que proporciona Concourse-CI y añade
CONCOURSE_AWS_SECRETSMANAGER_ACCESS_KEY, CONCOURSE_AWS_SECRETSMANAGER_SECRET_KEY y CONCOURSE_AWS_SECRETSMANAGER_REGION.
version: '3'
services:
concourse-db:
image: postgres
environment:
POSTGRES_DB: concourse
POSTGRES_PASSWORD: concourse_pass
POSTGRES_USER: concourse_user
PGDATA: /database
concourse:
image: concourse/concourse
command: quickstart
privileged: true
depends_on: [concourse-db]
ports: ["8080:8080"]
environment:
CONCOURSE_POSTGRES_HOST: concourse-db
CONCOURSE_POSTGRES_USER: concourse_user
CONCOURSE_POSTGRES_PASSWORD: concourse_pass
CONCOURSE_POSTGRES_DATABASE: concourse
CONCOURSE_EXTERNAL_URL: http://localhost:8080
CONCOURSE_ADD_LOCAL_USER: test:test
CONCOURSE_MAIN_TEAM_LOCAL_USER: test
# instead of relying on the default "detect"
CONCOURSE_WORKER_BAGGAGECLAIM_DRIVER: overlay
CONCOURSE_CLIENT_SECRET: Y29uY291cnNlLXdlYgo=
CONCOURSE_TSA_CLIENT_SECRET: Y29uY291cnNlLXdvcmtlcgo=
CONCOURSE_X_FRAME_OPTIONS: allow
CONCOURSE_CONTENT_SECURITY_POLICY: "*"
CONCOURSE_CLUSTER_NAME: tutorial
CONCOURSE_WORKER_CONTAINERD_DNS_SERVER: "8.8.8.8"
CONCOURSE_WORKER_RUNTIME: "containerd"
CONCOURSE_ENABLE_ACROSS_STEP: "true"
CONCOURSE_ENABLE_PIPELINE_INSTANCES: "true"
CONCOURSE_AWS_SECRETSMANAGER_ACCESS_KEY: <add access key>
CONCOURSE_AWS_SECRETSMANAGER_SECRET_KEY: <add secret key>
CONCOURSE_AWS_SECRETSMANAGER_REGION: <add a region>
Cuando la integración esté configurada y Concourse-CI esté en ejecución con la integración habilitada, añade los siguientes secretos a AWS Secrets Manager.
/concourse/main/bitbucket_username
/concourse/main/bitbucket_api_key
/concourse/main/bitbucket_ssh_key
/concourse/main/docker_username
/concourse/main/docker_api_key
/Concourse/Main/AWS_Access_Key_ID
/concourse/main/AWS_SECRET_ACCESS_KEY
/concourse/main/AWS_DEFAULT_REGION
Puede que haya que reemplazar los secretos de Bitbucket y Docker si el lector no usa Bitbucket para su código y JFrog como repositorio de Docker. Ajustar esto para que se adapte a las herramientas individuales del lector queda como un ejercicio.
Trabajar con Concourse-CI
Ejecuta docker ps -a antes y después de ejecutar docker-compose up -d para comprobar que Concourse-CI se ha iniciado correctamente.
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
docker-compose up -d
Creating network "restapiproject_default" with the default driver
Creating restapiproject_concourse-db_1 ... done
Creating restapiproject_concourse_1 ... done
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bd2b5afd0ac7 concourse/concourse "dumb-init /usr/loca…" 3 seconds ago Up 2 seconds 0.0.0.0:8080->8080/tcp restapiproject_concourse_1
bd9005b45636 postgres "docker-entrypoint.s…" 3 seconds ago Up 2 seconds 5432/tcp restapiproject_concourse-db_1
Ve a http://localhost:8080/ después de ejecutar fly -t tutorial login -c http://localhost:8080 -u test -p test
fly -t tutorial login -c http://localhost:8080 -u test -p test
logging in to team 'main'
target saved
No hay canalizaciones definidas en este momento.
Una canalización hello world
Configura una canalización hello world siguiendo esta documentación de Concourse-CI: https://concourse-ci.org/tutorial-hello-world.html. Esto es necesario para introducir la CLI de Fly y acostumbrarse a trabajar con Concourse-CI desde la línea de comandos.
La siguiente sección demuestra cómo implementar una AWS Lambda escrita en Golang en una sola región de AWS con Concourse-CI y cómo escribir una actualización de una incidencia de Jira como parte del proceso.
Implementa SubmitImage con Concourse-CI
Hay tres pasos para implementar la Lambda de SubmitImage con Concourse-CI. El primer paso es escribir una secuencia de comandos de bash sencillo que utilice la API de REST de Jira Cloud para escribir un comentario en una incidencia de Jira. Es la integración más sencilla que podemos crear. El segundo paso es crear una imagen de Docker con las herramientas necesarias para crear e implementar una AWS Lambda de Golang. El último paso es escribir un par de archivos de configuración de Concourse-CI. El primer archivo de configuración, parent.yml, supervisa el repositorio de SubmitImage en busca de nuevas ramas y crea nuevas canalizaciones para implementar las confirmaciones de esas ramas. El segundo archivo de configuración, child.yml, define el conjunto de pasos necesarios para implementar un cambio.
Paso 1: Actualiza las incidencias de Jira mediante la API de REST
Este script de actualización utiliza la API de REST de la plataforma Jira Cloud para escribir un comentario sobre una incidencia específica de Jira. Hay cinco parámetros obligatorios que se deben configurar cada vez que se ejecuta la secuencia de comandos. El nombre de usuario, el JiraAPIToken y el espacio de trabajo suelen ser los mismos para cada ejecución de una canalización en particular. El IssueKey dependerá del nombre de la rama en concreto que se esté implementando. Poner el identificador de incidencia de Jira en los nombres de las ramas y confirmar mensajes cuando se trabaja en un tema en particular son prácticas recomendadas de Jira. Este artículo asume que se siguen las prácticas recomendadas y que los nombres de las ramas son equivalentes al identificador de incidencia de Jira.
Cómo encontrar los parámetros
Nombre de usuario de Jira
El nombre de usuario de Jira es la dirección de correo electrónico utilizada para iniciar sesión en Jira.
Token de API de Jira
Ir a Configuración de la cuenta
Haz clic en Seguridad
Haz clic en Crear y gestionar tokens de API
Espacio de trabajo
Dada una URL de instancia de Jira como https://pmmquickstartguides01.atlassian.net/jira/software/projects/IM/boards/1?selectedIssue=IM-203, el espacio de trabajo es pmmquickstartguide01.
Clave de incidencia
Dada una URL de incidencia de Jira como https://pmmquickstartguides01.atlassian.net/jira/software/projects/IM/boards/1?selectedIssue=IM-203, la clave de incidencia de Jira es IM-203. Pon el identificador de incidencia de Jira en los mensajes de confirmación y los nombres de las ramas para que la integración pueda escribir las actualizaciones en la ubicación correcta.
Comentario
El comentario puede ser cualquier cosa.
La secuencia de comandos de actualización
La secuencia de comandos de actualización es una sencilla secuencia de comandos shell de bash que utiliza el punto final de la API de REST de comentarios de incidencias de Jira. Aquí tienes la documentación de esta llamada a la API. La secuencia de comandos se puede modificar para ofrecer una integración más profunda siguiendo el patrón proporcionado para realizar llamadas a la API adicionales. Copia esta secuencia de comandos en un archivo llamado concourse-ci-integration.sh y ponlo en un repositorio de Bitbucket o GitHub llamado UpdateScript.
#!/usr/bin/env bash
addCommentToIssue() {
printf "addCommentToIssue\n"
local jiraUsername=$1
shift
local jiraApiToken=$1
shift
local workspace=$1
shift
local issueKey=$1
shift
local comment=$1
shift
curl -s --request POST \
--url 'https://'"${workspace}"'.atlassian.net/rest/api/3/issue/'"${issueKey}"'/comment' \
--user "${jiraUsername}"':'"${jiraApiToken}" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '{
"body": {
"type": "doc",
"version": 1,
"content": [{
"type": "paragraph",
"content": [{
"text": "'"${comment}"'",
"type": "text"
}]
}]
}
}' | jq
}
main() {
printf "main\n"
while getopts ":c:k:o:u:t:w:" opt; do
case $opt in
c)
local comment=$OPTARG
;;
k)
local issueKey=$OPTARG
;;
o)
local op=$OPTARG
;;
u)
local jiraUsername=$OPTARG
;;
t)
local jiraApiToken=$OPTARG
;;
w)
local workspace=$OPTARG
;;
*)
printf "invalid option: -${OPTARG}\n" >&2
exit 1
;;
esac
done
case $op in
ac)
addCommentToIssue ${jiraUsername} ${jiraApiToken} ${workspace} ${issueKey} "${comment}"
;;
*)
printf "invalid op: ${op}\n" >&2
exit 1
;;
esac
}
main "$@"
Paso 2: Dockerfile personalizado
Crea un Dockerfile personalizado con las herramientas necesarias para crear e implementar una AWS Lambda escrita en Golang. El Dockerfile instala algunas herramientas y, a continuación, añade AWS SAM y Golang. La imagen Git clona la secuencia de comandos de actualización creada en el paso 1 de un repositorio de Bitbucket. Tienes que reemplazar este repositorio de Bitbucket por el repositorio que hayas creado para almacenar la secuencia de comandos de actualización. Docker crea este Dockerfile y lo envía a un repositorio de Docker.
Dockerfile
# syntax = docker/dockerfile:1.3
FROM ubuntu:20.04
MAINTAINER wmarusiak@atlassian.com
WORKDIR /workspace
RUN apt-get update \
&& apt-get -y upgrade \
&& apt-get -y install curl unzip tar openssh-client git jq
RUN curl https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip -L -o aws-sam-cli.zip \
&& mkdir sam-installation \
&& unzip aws-sam-cli.zip -d sam-installation \
&& ./sam-installation/install \
&& sam --version
RUN curl https://go.dev/dl/go1.18.2.linux-amd64.tar.gz -L -o go.tar.gz \
&& rm -rf /usr/local/go \
&& tar -C /usr/local -xzf go.tar.gz
RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan bitbucket.org >> ~/.ssh/known_hosts
RUN --mount=type=ssh git clone git@bitbucket.org:pmmquickstartguides01/updatescript.git
ENV PATH $PATH:/usr/local/go/bin
Paso 3: Archivos yaml de la canalización de implementación de Concourse-CI
Crea un nuevo repositorio llamado Concourse y coloca dos archivos en él. Parent.yml crea una canalización que supervisa un repositorio en busca de nuevas ramas que coincidan con una expresión regular. El branch_regex es IM-* y hace coincidir todas las ramas que empiecen por IM-. Parent.yml creará una nueva canalización con la configuración de child.yml cuando se cree una nueva rama que coincida con la expresión regular IM-*. Debes actualizar este archivo para que apunte a tu propia versión del repositorio de SubmitImage. El código de envío de la imagen se encuentra aquí. Copia el código de este repositorio a un repositorio con permisos de escritura.
parent.yml
resource_types:
- name: git-branches
type: registry-image
source:
repository: aoldershaw/git-branches-resource
resources:
- name: feature-branches
type: git-branches
source:
uri: git@bitbucket.org:pmmquickstartguides01/submitimage.git
branch_regex: IM-*
private_key: ((bitbucket_ssh_key))
- name: examples
type: git
source:
uri: git@bitbucket.org:pmmquickstartguides01/concourse.git
private_key: ((bitbucket_ssh_key))
jobs:
- name: set-feature-pipelines
plan:
- in_parallel:
- get: feature-branches
trigger: true
- get: examples
- load_var: branches
file: feature-branches/branches.json
- across:
- var: branch
values: ((.:branches))
set_pipeline: dev
file: examples/child.yml
vars: {branch: ((.:branch.name))}
Child.yml define una canalización con cuatro pasos. En primer lugar, ejecuta sam validate para comprobar que el archivo template.yml de AWS CloudFormation es válido. Luego ejecuta sam build para convertir el paquete SubmitImage en un artefacto desplegable. En tercer lugar, ejecuta Sam Deploy para implementar el código de SubmitImage actualizado en AWS. Por último, la canalización invoca la secuencia de comandos de actualización creado en el paso 1 para escribir un comentario sobre la incidencia de Jira correspondiente.
child.yml
resources:
- name: repo
type: git
source:
uri: git@bitbucket.org:pmmquickstartguides01/submitimage.git
branch: ((branch))
private_key: ((bitbucket_ssh_key))
jobs:
- name: deploy-submit-image
plan:
- get: repo
trigger: true
- task: run-sam-validate
config:
platform: linux
image_resource:
type: registry-image
source:
repository: docker.atl-paas.net/wmarusiak/ubuntuawssam
username: ((docker_username))
password: ((docker_api_key))
inputs: # add the get step as an input to this task
- name: repo
run:
path: sam
args: ["validate", "-t", "repo/template.yml"]
params:
AWS_ACCESS_KEY_ID: ((AWS_ACCESS_KEY_ID))
AWS_SECRET_ACCESS_KEY: ((AWS_SECRET_ACCESS_KEY))
AWS_DEFAULT_REGION: ((AWS_DEFAULT_REGION))
- task: run-sam-build
config:
platform: linux
image_resource:
type: registry-image
source:
repository: docker.atl-paas.net/wmarusiak/ubuntuawssam
username: ((docker_username))
password: ((docker_api_key))
inputs: # add the get step as an input to this task
- name: repo
run:
path: sam
args: ["build", "-t", "repo/template.yml"]
params:
AWS_ACCESS_KEY_ID: ((AWS_ACCESS_KEY_ID))
AWS_SECRET_ACCESS_KEY: ((AWS_SECRET_ACCESS_KEY))
AWS_DEFAULT_REGION: ((AWS_DEFAULT_REGION))
- task: run-sam-deploy
config:
platform: linux
image_resource:
type: registry-image
source:
repository: docker.atl-paas.net/wmarusiak/ubuntuawssam
username: ((docker_username))
password: ((docker_api_key))
inputs: # add the get step as an input to this task
- name: repo
run:
path: sam
args: ["deploy", "-t", "repo/template.yml", "--stack-name", "OpenDevOpsSubmitImage", "--s3-bucket", "open-devops-code-us-west-1-756685045356", "--capabilities", "CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"]
params:
AWS_ACCESS_KEY_ID: ((AWS_ACCESS_KEY_ID))
AWS_SECRET_ACCESS_KEY: ((AWS_SECRET_ACCESS_KEY))
AWS_DEFAULT_REGION: ((AWS_DEFAULT_REGION))
- task: run-update-script
config:
platform: linux
image_resource:
type: registry-image
source:
repository: docker.atl-paas.net/wmarusiak/ubuntuawssam
username: ((docker_username))
password: ((docker_api_key))
input:
-name: repo
run:
path: /workspace/updatescript/concourse-ci-integration.sh
args: ["-c", "successfully deployed submitImage via concourse-ci", "-k", "((branch))", "-o", "ac", "-u", "((bitbucket_username))", "-t", "((bitbucket_api_key))", "-w", "pmmquickstartguides01"]
Cómo iniciar la canalización principal
Ejecuta los tres comandos siguientes del directorio con parent.yml. El primer comando inicia sesión en la instancia de Concourse-CI que se ejecuta localmente. El segundo comando crea una canalización llamada wm según la configuración de parent.yml. El tercer comando desactiva la canalización de wm.
fly -t tutorial login -c http://localhost:8080 -u test -p test
fly -t tutorial set-pipeline -p wm -c parent.yml
fly -t tutorial unpause-pipeline -p wm
Ve al cliente web Concourse-CI después de ejecutar estos tres comandos para comprobar que la canalización wm está en funcionamiento.
Después de ejecutar fly set-pipeline con parent.yml, hay una canalización wm. Esta canalización supervisa SubmitImage en busca de nuevas ramas de funciones y crea una canalización para cada rama de funciones que coincida con la expresión regular de parent.yml.
Haz clic en la canalización de WM para ver los pasos que se ejecutan. Fíjate en que el paso feature-branches indica una rama IM-61. Esta es la única rama que existe actualmente en SubmitImage que coincide con la expresión regular en parent.yml. Haz clic en la canalización de implementación que se inició automáticamente para ver los pasos que sigue.
Ten en cuenta que hay un paso para obtener el repositorio de SubmitImage y que la rama es IM-61. Ten en cuenta también que hay pasos para run-sam-validate, run-sam-build, run-sam-deploy y run-update-script.
Cuando termine el proceso de desarrollo, vuelve a la incidencia IM-61 de Jira y observa que hay un nuevo comentario registrado hace un minuto que coincide con la cadena de comentarios de child.yml
En conclusión...
Esta guía demuestra cómo configurar una canalización para implementar automáticamente una AWS Lambda de Golang en una sola región de AWS mediante Concourse-CI. Esta guía también demuestra cómo usar una secuencia de comandos shell de bash para escribir una integración sencilla con Jira. La secuencia de comandos de integración se puede ampliar enormemente consultando más a fondo la documentación de la API de REST de Atlassian, disponible aquí.
Compartir este artículo
Tema siguiente
Lecturas recomendadas
Consulta estos recursos para conocer los tipos de equipos de DevOps o para estar al tanto de las novedades sobre DevOps en Atlassian.