Close

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.

secretos de Bitbucket y Docker

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

Configuración de la cuenta de tokens de API de JIRA

Haz clic en Seguridad

seguridad de la cuenta

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.

imagen del cliente web de Concourse-CI

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.

Canalización fly set

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.

Canalización wm después de la canalización fly set

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.

Resumen de la incidencia de Jira IM 61

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í.

Warren Marusiak
Warren Marusiak

Warren is a Canadian developer from Vancouver, BC with over 10 years of experience. He came to Atlassian from AWS in January of 2021.

Compartir este artículo

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.

Ilustración de Devops

La comunidad de DevOps

Ilustración de Devops

Ruta de aprendizaje de DevOps

Ilustración de un mapa

Pruébalo gratis

Suscríbete para recibir el boletín de DevOps

Thank you for signing up