Crie um módulo Ansible para integrar seu Google Agenda
Aprenda como escrever um módulo Ansible em Go para integrar o Google Agenda ao seu fluxo de trabalho de automação.
Em um artigo anterior, explorei como o Ansible pode ser integrado ao Google Calendar para gerenciamento de mudanças, mas não entrei em detalhes do módulo Ansible que foi criado para essa finalidade. Neste artigo, abordarei seus detalhes básicos.
Embora a maioria dos módulos Ansible sejam escritos em Python (veja este exemplo), essa não é a única opção que você tem. Você pode usar outras linguagens de programação, se preferir. E se você gosta de Go, esse post é para você!
(Pacote Gophers Gratuito de Maria Letta, Licença Free Gophers v1.0, modificado por Nicolas Leiva)
Se você é novo no Go, verifique estas dicas para começar.
Ansible e vá
Existem pelo menos quatro maneiras diferentes de executar um programa Go no Ansible:
- Instale o Go e execute seu código Go com o comando
go run
do Ansible. - Compile seu código Go para diferentes plataformas antes da execução. Em seguida, chame o binário adequado do Ansible, com base nos fatos coletados do host.
-
Execute seu código Go ou binário compilado de um contêiner com a coleção
containers.podman
. Algo como:- name: Run Go container podman_container: name: go_test_container image: golang command: go version rm: true log_options: "path={{ log_file }}"
- Crie um pacote RPM do seu código Go com algo como NFPM e instale-o no sistema do host de destino. Você pode então chamá-lo com o shell ou comando dos módulos Ansible.
A execução de um pacote ou contêiner RPM não é específica do Go (casos 3 e 4), então vou me concentrar nas duas primeiras opções.
API do Google Agenda
Você precisará interagir com a API do Google Agenda, que fornece exemplos de código para diferentes linguagens de programação. O do Go será a base do seu módulo Ansible.
A parte complicada é ativar a API Calendar e fazer download das credenciais geradas no Google API Console (Credentials
> + CREATE CREDENTIALS
> OAuth client ID
> Aplicativo de desktop
).
(Nicolas Leiva, CC BY-SA 4.0)
A seta mostra onde fazer o download das credenciais do cliente OAuth 2.0 (arquivo JSON) depois de criá-las em Credenciais de API.
Chamando o módulo do Ansible
O módulo calendar
leva o tempo
para validar como um argumento. O exemplo abaixo fornece o horário atual. Normalmente, você pode obter isso em fatos Ansible (ansible_date_time
). A saída JSON do módulo é registrada em uma variável chamada output
para ser usada em uma tarefa subsequente:
- name: Check if timeslot is taken
calendar:
time: "{{ ansible_date_time.iso8601 }}"
register: output
Você pode estar se perguntando onde fica o código do módulo calendar
e como o Ansible o executa. Por favor, tenha paciência comigo por um momento; Chegarei a isso depois de cobrir outras peças do quebra-cabeça.
Empregue a lógica do tempo
Com as nuances da API do Calendário resolvidas, você pode interagir com a API e criar uma função Go para capturar a lógica do módulo. O tempo
é obtido dos argumentos de entrada - no manual acima - como o tempo inicial (min
) da janela de tempo para validação (escolhi arbitrariamente uma duração de uma hora ):
func isItBusy(min string) (bool, error) {
...
// max -> min.Add(1 * time.Hour)
max, err := maxTime(min)
// ...
srv, err := calendar.New(client)
// ...
freebusyRequest := calendar.FreeBusyRequest{
TimeMin: min,
TimeMax: max,
Items: []*calendar.FreeBusyRequestItem{&cal},
}
// ...
freebusyRequestResponse, err := freebusyRequestCall.Do()
// ...
if len(freebusyRequestResponse.Calendars[name].Busy) == 0 {
return false, nil
}
return true, nil
}
Ele envia um FreeBusyRequest
ao Google Agenda com o horário inicial e de término da janela de tempo (min
e max
). Ele também cria um cliente de calendário (srv
) para autenticar a conta corretamente usando o arquivo JSON com as credenciais do cliente OAuth 2.0. Em troca, você recebe uma lista de eventos durante esse período.
Se você não obtiver nenhum evento, a função retornará busy=false
. No entanto, se houver pelo menos um evento durante esta janela de tempo, significa busy=true
. Você pode conferir o código completo em meu repositório GitHub.
Processe a entrada e crie uma resposta
Agora, como o código Go lê os argumentos de entrada do Ansible e, por sua vez, gera uma resposta que o Ansible pode processar? A resposta depende se você está executando a CLI Go (interface de linha de comando) ou apenas executando um binário de programa Go pré-compilado (ou seja, opções 1 e 2 acima).
Ambas as opções têm seus prós e contras. Se você usar o Go CLI, poderá passar os argumentos da maneira que preferir. No entanto, para torná-lo consistente com o funcionamento dos binários executados no Ansible, ambas as alternativas lerão um arquivo JSON nos exemplos apresentados aqui.
Lendo os argumentos
Conforme mostrado no snippet de código Go abaixo, um arquivo de entrada é processado e o Ansible fornece um caminho para ele quando chama um binário.
O conteúdo do arquivo é desempacotado em uma instância (moduleArg
) de uma estrutura definida anteriormente (ModuleArgs
). É assim que você informa ao código Go quais dados espera receber. Este método permite que você obtenha acesso ao time
especificado no playbook via moduleArg.time
, que é então passado para a função lógica de tempo (isItBusy
) para processamento:
// ModuleArgs are the module inputs
type ModuleArgs struct {
Time string
}
func main() {
...
argsFile := os.Args[1]
text, err := ioutil.ReadFile(argsFile)
...
var moduleArgs ModuleArgs
err = json.Unmarshal(text, &moduleArgs)
...
busy, err := isItBusy(moduleArg.time)
...
}
Gerando uma resposta
Os valores a serem retornados são atribuídos a uma instância de um objeto Response
. O Ansible precisará que esta resposta inclua alguns sinalizadores booleanos (Changed
e Failed
). Você pode adicionar qualquer outro campo necessário; neste caso, um valor booleano Busy
é transportado para sinalizar a resposta da função lógica de tempo.
A resposta é organizada em uma mensagem que você imprime e o Ansible pode ler:
// Response are the values returned from the module
type Response struct {
Msg string `json:"msg"`
Busy bool `json:"busy"`
Changed bool `json:"changed"`
Failed bool `json:"failed"`
}
func returnResponse(r Response) {
...
response, err = json.Marshal(r)
...
fmt.Println(string(response))
...
}
Você pode conferir o código completo no GitHub.
Executar um código binário ou Go instantaneamente?
Uma das coisas legais sobre Go é que você pode compilar um programa Go para rodar em diferentes sistemas operacionais e arquiteturas de destino. Os arquivos binários que você compila podem ser executados no host de destino sem instalar o Go ou qualquer dependência.
Essa flexibilidade funciona bem com o Ansible, que fornece os detalhes do host de destino (ansible_system
e ansible_architecture
) por meio de fatos do Ansible. Neste exemplo, a arquitetura de destino é corrigida durante a compilação (x86_64
), mas binários para macOS, Linux e Windows são gerados (via make compile
). Isso produz os três arquivos incluídos na pasta library
da função go_role
no formato: calendar_$system
:
⇨ tree roles/go_role/
roles/go_role/
├── library
│ ├── calendar_darwin
│ ├── calendar_linux
│ ├── calendar_windows
│ └── go_run
└── tasks
├── Darwin.yml
├── Go.yml
├── Linux.yml
├── main.yml
└── Win32NT.yml
A função go_role
que empacota o módulo calendar
é estruturada para substituir $system
do nome do arquivo em execução com base no ansible_system
descoberto durante o tempo de execução; dessa forma, essa função pode ser executada em qualquer um desses sistemas operacionais de destino. Quão legal é isso!? Você pode testar isso com make test-ansible
.
Alternativamente, se Go estiver instalado no sistema de destino, você poderá executar o comando go run
para executar o código. Se quiser instalar o Go primeiro como parte do seu manual, você pode usar esta função (veja este exemplo).
Como você executa o comando go run
do Ansible? Uma opção é usar os módulos shell
ou command
. No entanto, este caso usa um módulo Bash bônus para estender este exercício para incluir outra linguagem de programação.
Módulo bônus: Bash
O arquivo go_run
na pasta library
go_role
role é o código Bash real usado para executar o código Go em sistemas com Go instalado. Quando o Ansible executa este módulo Bash, ele passa um arquivo para ele com os argumentos do módulo definidos no playbook, que você pode importar no Bash com source $1
. Isso fornece acesso à variável time
. Caso contrário, você o obtém do sistema com date --iso-8601=seconds
.
ISO 8601 e RFC 3339 tornam os carimbos de data/hora interoperáveis entre Ansible, Bash e Go. Não há necessidade de analisar ou transformar dados entre eles.
#!/bin/bash
source $1
# Fail if time is not set or add a sane default
if [ -z "$time" ]; then
time=$(date --iso-8601=seconds)
fi
printf '{"Name": "%s", "Time": "%s"}' "$name" "$time" > $file
go run *.go $file
Com as entradas em mãos, um arquivo JSON é gerado com printf
. Este arquivo é passado como argumento para o código Go por meio do comando go run
. Você pode testar isso com make test-manual-bash
. Confira o código completo no GitHub.
Usando a saída do módulo como condicional
A resposta do módulo calendar
(output
) agora pode ser usada como condicional para determinar se a próxima tarefa deve ser executada ou não:
tasks:
- shell: echo "Run this only when not busy!"
when: not output.busy
Se você quiser evitar executar a tarefa anterior para obter a resposta e, em vez disso, usar a saída do módulo diretamente em sua instrução when
, um plugin Action pode ajudar.
Outra alternativa, especialmente se Go não for sua praia, é algo como este plugin que meu bom amigo Paulo escreveu depois de discutirmos esse caso de uso específico.
Embora o Ansible e a maioria de seus módulos sejam escritos em Python, ele pode ser perfeitamente integrado a ferramentas ou scripts que usam outra linguagem de programação. Isto é fundamental para melhorar a eficiência e a agilidade operacional sem a necessidade de desmontagem e substituição.
Isto apareceu originalmente no Medium como Get yourself GOing with Ansible sob uma licença CC BY-SA 4.0 e foi republicado com permissão.