Integração entre o Concourse-CI e o Atlassian Open DevOps

Muitas equipes criam ferramentas próprias para atender as necessidades ou têm ferramentas antigas que usam há anos. São ferramentas essenciais para o processo de desenvolvimento adotado pelas equipes, mas não têm integrações prontas para uso com o Jira. No entanto, é fácil criar a integração personalizada usando as APIs REST da Atlassian contidas na Documentação do Cloud para desenvolvedores - Atlassian Developer . O Concourse-CI é um produto de integração/implementação contínuas (IC) que, no momento da redação deste artigo, não tem integração do Atlassian Marketplace. Este artigo demonstra como gerar uma integração básica entre o Jira e o Concourse-CI usando as APIs REST da Atlassian.
Pré-requisitos
Use a documentação necessária para configurar o Docker, o Docker Compose e o Concourse-CI. O Concourse-CI é executado no Docker e dispo script do Docker Compose para simplificar o início.
Saiba mais sobre o ImageLabeller, o aplicativo de demonstração da Atlassian, aqui. Este artigo demonstra como usar o Concourse-cI para implementar o componente SubmitImage do ImageLabeller no AWS.
Docker
Configure o Docker e o docker-compose seguindo a documentação associada:
Docker Compose: https://docs.docker.com/compose/install/
Concourse-CI
Assim que o Docker e o Docker Compose estiverem instalados, o Concourse-CI pode ser iniciado usando o arquivo docker-compose.yml disponibilizado.
Siga o guia de início rápido para começar a usar o Concourse-CI https://concourse-ci.org/quick-start.html#docker-compose-concourse. Este guia exige a transferência segura de credenciais para o Concourse-CI. O guia utiliza a integração entre o Concourse-CI e o AWS Secrets Manager para essa finalidade.
Integração entre o Concourse-CI e o AWS Secrets Manager
Aqui está a documentação sobre como realizar a integração entre o Concourse-CI e o AWS Secrets Manager. Siga as instruções na documentação para ativar e começar a usar a integração.
Para que a integração funcione, é necessário fazer algumas modificações no arquivo docker-compose.yml, usado para iniciar o Concourse-CI. Pegue o arquivo docker-compose.yml padrão oferecido pelo Concourse-CI e adicione
CONCOURSE_AWS_SECRETSMANAGER_ACCESS_KEY, CONCOURSE_AWS_SECRETSMANAGER_SECRET_KEY e 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>Depois que a integração estiver configurada e o Concourse-CI estiver funcionando com a integração ativada, adicione os segredos a seguir ao 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
Pode ser necessário substituir os segredos do Bitbucket e do Docker se você não estiver usando o Bitbucket para a codificação e o JFrog como repositório do Docker. Como um exercício, faça os ajustes necessários para adequar as ferramentas que você utiliza.

Como trabalhar com o Concourse-CI
Execute o comando docker ps -a antes e depois do comando docker-compose up -d para ver se o Concourse-ci foi iniciado sem erros.
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_1Acesse http://localhost:8080/ depois de executar o comando 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 savedNão há pipelines definidos no momento.
Pipeline hello world
Configure o pipeline hello world seguindo a documentação do Concourse-CI a seguir: https://concourse-ci.org/tutorial-hello-world.html. Isso é necessário para introduzir a interface de linha de comando (CLI) Fly e se acostumar a trabalhar com o Concourse-CI a partir da linha de comando.
A próxima seção demonstra como implementar o AWS Lambda criado em Golang em uma única região do AWS com o Concourse-CI e como criar a atualização para o item do Jira como parte do processo.
Implementar o SubmitImage com o Concourse-CI
Há três etapas para implementar o SubmitImage no Lambda com o Concourse-CI. A primeira etapa é criar um script bash simples que use a API REST do Jira Cloud para escrever um comentário em item do Jira. Essa é a integração mais simples que pode ser criada. A segunda etapa é criar a imagem do Docker com as ferramentas necessárias para gerar e implementar o AWS Lambda criado em Golang. A etapa final é criar um par de arquivos de configuração do Concourse-CI. O primeiro arquivo de configuração, o parent.yml, monitora o repositório SubmiImage em busca de novas ramificações e cria novos pipelines para implementar confirmações dessas ramificações. O segundo arquivo de configuração, o child.yml, define o conjunto necessário de etapas para implementar a alteração.
Etapa 1 - Atualizar itens do Jira por meio da API REST
Esse script de atualização usa a API REST da plataforma Jira Cloud para escrever um comentário em um item específico do Jira. Há cinco parâmetros necessários que devem ser definidos toda vez que o script é executado. O jiraUserName (nome de usuário do Jira), o jiraApiToken (token de API do Jira) e a área de trabalho costumam ser os mesmos para cada execução do pipeline específico. A IssueKey (chave do item) vai depender do nome da ramificação específica que está sendo implantada. Uma prática recomendada do Jira é colocar o ID do item do Jira nos nomes das ramificações e enviar mensagens de confirmação ao trabalhar em um item específico. Este artigo pressupõe que as práticas recomendadas estão sendo seguidas e que os nomes das ramificações são equivalentes ao ID do item do Jira.
Como encontrar os parâmetros
Nome de usuário do Jira
O nome de usuário do Jira é o endereço de e-mail usado para fazer login no Jira.
Token de API do Jira
Vá para Configurações da conta

Clique em Segurança

Clique em Criar e gerenciar tokens de API
Espaço de trabalho
Para a URL de instância do Jira, como https://pmmquickstartguides01.atlassian.net/jira/software/projects/IM/boards/1?selectedIssue=IM-203 a área de trabalho é pmmquickstartguide01.
Chave do item
Para a URL de item do Jira, como https://pmmquickstartguides01.atlassian.net/jira/software/projects/IM/boards/1?selectedIssue=IM-203 a chave do item do Jira é GI-203. Insira o ID do item do Jira nas mensagens de confirmação e nos nomes das ramificações para que a integração possa gravar as atualizações no local correto.
Comentário
O comentário pode ser qualquer coisa.
O script de atualização
O script de atualização é um script de shell bash simples que usa o terminal da API REST de comentário de itens do Jira. Aqui está a documentação para essa chamada de API. O script pode ser modificado para disponibilizar a integração mais profunda seguindo o padrão disponibilizado para fazer chamadas adicionais à API. Copie esse script para um arquivo chamado concourse-ci-integration.sh e o coloque em um repositório do Bitbucket ou do GitHub chamado 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 "$@"Etapa 2 - Dockerfile personalizado
Crie um Dockerfile personalizado com as ferramentas necessárias para gerar e implementar o AWS Lambda criado em Golang. O Dockerfile instala alguns utilitários e adiciona o AWS SAM e o Golang. A imagem do Git clona o script de atualização criado na Etapa 1 a partir de um repositório do Bitbucket. É necessário substituir esse repositório do Bitbucket por qualquer repositório criado para armazenar o script de atualização. O Docker cria esse Dockerfile e o envia para um repositório 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/binEtapa 3 - Arquivos yaml do pipeline de implementação do Concourse-CI
Crie um novo repositório chamado Concourse e adicione dois arquivos nele. O arquivo parent.yml cria um pipeline que monitora um repositório em busca de novas ramificações que correspondam a expressão regular. A expressão regular da ramificação (branch_regex) é IM-* que corresponde a todas as ramificações que começam com IM-. O parent.yml vai criar um novo pipeline usando a configuração no arquivo child.yml quando a nova ramificação que corresponda à expressão regular IM-* for criada. Esse arquivo deve ser atualizado para especificar a sua própria versão do repositório SubmitImage. O código do SubmitImage pode ser encontrado aqui. Copie o código desse repositório para um repositório com permissões de gravação.
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))}O arquivo child.yml define o pipeline com quatro etapas. Primeiro, ele executa o comando sam validate para verificar se o arquivo template.yml do AWS CloudFormation é válido. Em seguida, o comando sam build é executado para criar o pacote SubmitImage em um artefato implementável. Em terceiro lugar, o comando sam deploy é executado para implementar o código atualizado do SubmiImage no AWS. Por fim, o pipeline chama o script de atualização criado na etapa 1 para escrever um comentário no item correspondente do Jira.
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"]Como iniciar o pipeline pai
Execute os três comandos a seguir no diretório com o arquivo parent.yml. O primeiro comando efetua login na instância do Concourse-CI em execução local. O segundo comando cria um pipeline chamado wm com base na configuração existente no arquivo parent.yml. O terceiro comando aciona o pipeline 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 wmAcesse o cliente web do Concourse-CI depois de executar esses três comandos para ver se o pipeline wm está funcionando.

Depois de executar o comando fly set-pipeline com o arquivo parent.yml, tem o pipeline wm. Esse pipeline monitora o SubmitImage em busca de novas ramificações de função e cria um pipeline para cada ramificação de função que corresponda à expressão regular existente em parent.yml.

Clique no pipeline wm para ver as etapas executadas. Observe que a etapa feature-branches (ramificações de função) lista a ramificação chamada GI-61. Essa é a única ramificação no momento em SubmitImage que corresponde à expressão regular existente em parent.yml. Clique no pipeline dev com início automático para ver as etapas executadas.

Observe que há uma etapa para obter o repositório SubmitImage e que a ramificação é GI-61. Observe também que há etapas run-sam-validate, run-sam-build, run-sam-deploy e run-update-script.

Depois que o pipeline dev terminar de realizar todos os procedimentos, volte ao item GI-61 do Jira e observe que há um novo comentário registrado há um minuto que corresponde à sequência de comentários do arquivo child.yml
Conclusão…
Este guia demonstra como configurar o pipeline para implementação automática do AWS Lambda criado em Golang em uma única região do AWS usando o Concourse-CI. Ele também demonstra como usar um script de shell bash para criar a integração simples com o Jira. O script de integração pode ser expandido com o aprofundamento na documentação da API REST da Atlassian disponível aqui.