O autor escolheu a Diversity in Tech Fund para receber uma doação como parte do programa Write for DOnations.
O InSpec é um framework open-source de auditoria e teste automatizado usado para descrever e testar preocupações, recomendações ou requisitos regulatórios. Ele foi projetado para ser inteligível e independente de plataforma. Os desenvolvedores podem trabalhar com o InSpec localmente ou usando SSH, WinRM ou Docker para executar testes, portanto, é desnecessário instalar quaisquer pacotes na infraestrutura que está sendo testada.
Embora com o InSpec você possa executar testes diretamente em seus servidores, existe um potencial de erro humano que poderia causar problemas em sua infraestrutura. Para evitar esse cenário, os desenvolvedores podem usar o Kitchen para criar uma máquina virtual e instalar um sistema operacional de sua escolha nas máquinas em que os testes estão sendo executados. O Kitchen é um executor de testes, ou ferramenta de automação de teste, que permite testar o código de infraestrutura em uma ou mais plataformas isoladas. Ele também suporta muitos frameworks de teste e é flexível com uma arquitetura de plug-in de driver para várias plataformas, como Vagrant, AWS, DigitalOcean, Docker, LXC containers, etc.
Neste tutorial, você escreverá testes para seus playbooks Ansible em execução em um Droplet Ubuntu 18.04 da DigitalOcean. Você usará o Kitchen como executor de teste e o InSpec para escrever os testes. No final deste tutorial, você poderá testar o deploy do seu playbook Ansible.
Antes de começar com este guia, você precisará de uma conta na DigitalOcean além do seguinte:
Ruby
em sua máquina. Você pode instalar o Ruby seguindo o tutorial para sua distribuição na série: How To Install and Set Up a Local Programming Environment for Ruby.Você instalou o ChefDK como parte dos pré-requisitos que vem empacotados com o kitchen. Neste passo, você configurará o Kitchen para se comunicar com a DigitalOcean.
Antes de inicializar o Kitchen, você criará e se moverá para um diretório de projeto. Neste tutorial, o chamaremos de ansible_testing_dir
.
Execute o seguinte comando para criar o diretório:
- mkdir ~/ansible_testing_dir
E então passe para ele:
- cd ~/ansible_testing_dir
Usando o gem
instale o pacote kitchen-digitalocean
em sua máquina local. Isso permite que você diga ao kitchen
para usar o driver da DigitalOcean ao executar testes:
- gem install kitchen-digitalocean
No diretório do projeto, você executará o comando kitchen init
especificando ansible_playbook
como o provisionador e digitalocean
como o driver ao inicializar o Kitchen:
- kitchen init --provisioner=ansible_playbook --driver=digitalocean
Você verá a seguinte saída:
Outputcreate kitchen.yml
create chefignore
create test/integration/default
Isso criou o seguinte no diretório do projeto:
test/integration/default
é o diretório no qual você salvará seus arquivos de teste.
chefignore
é o arquivo que você usaria para garantir que certos arquivos não sejam carregados para o Chef Infra Server, mas você não o usará neste tutorial.
kitchen.yml
é o arquivo que descreve sua configuração de teste: o que você deseja testar e as plataformas de destino.
Agora, você precisa exportar suas credenciais da DigitalOcean como variáveis de ambiente para ter acesso para criar Droplets a partir da sua CLI. Primeiro, inicie com seu token de acesso da DigitalOcean executando o seguinte comando:
- export DIGITALOCEAN_ACCESS_TOKEN="SEU_TOKEN_DE_ACESSO_DIGITALOCEAN"
Você também precisa obter seu número de ID da chave SSH; note que SEU_ID_DE_CHAVE_SSH_DIGITALOCEAN
deve ser o ID numérico da sua chave SSH, não o nome simbólico. Usando a API da DigitalOcean, você pode obter o ID numérico de suas chaves com o seguinte comando:
- curl -X GET https://api.digitalocean.com/v2/account/keys -H "Authorization: Bearer $DIGITALOCEAN_ACCESS_TOKEN"
Após este comando, você verá uma lista de suas chaves SSH e metadados relacionados. Leia a saída para encontrar a chave correta e identificar o número de ID nela:
Output...
{"id":seu-ID-numérico,"fingerprint":"fingerprint","public_key":"ssh-rsa sua-chave-ssh","name":"nome-da-sua-chave-ssh"
...
Nota: Se você deseja tornar sua saída mais legível para obter seus IDs numéricos, você pode encontrar e baixar o jq
com base no seu sistema operacional na página de download do jq. Agora, você pode executar o comando anterior fazendo um pipe para o jq
da seguinte maneira:
- curl -X GET https://api.digitalocean.com/v2/account/keys -H "Authorization: Bearer $DIGITALOCEAN_ACCESS_TOKEN" | jq
Você verá as informações da chave SSH formatadas de forma semelhante a:
Output{
"ssh_keys": [
{
"id": ID_DA_SUA_CHAVE_SSH,
"fingerprint": "2f:d0:16:6b",
"public_key": "ssh-rsa AAAAB3NzaC1yc2 example@example.local",
"name": "sannikay"
}
],
}
Depois de identificar seus IDs numéricos de SSH, exporte-os com o seguinte comando:
- export DIGITALOCEAN_SSH_KEY_IDS="SEU_ID_DE_CHAVE_SSH_DIGITALOCEAN"
Você inicializou o kitchen
e configurou as variáveis de ambiente para suas credenciais da DigitalOcean. Agora você vai criar e executar testes em seus Droplets diretamente da linha de comando.
Neste passo, você criará um playbook e roles (funções) que configurará o Nginx e o Node.js no Droplet criado pelo kitchen
no próximo passo. Seus testes serão executados no playbook para garantir que as condições especificadas no playbook sejam atendidas.
Para começar, crie um diretório roles
para as roles ou funções Nginx e Node.js:
- mkdir -p roles/{nginx,nodejs}/tasks
Isso criará uma estrutura de diretórios da seguinte maneira:
roles
├── nginx
│ └── tasks
└── nodejs
└── tasks
Agora, crie um arquivo main.yml
no diretório roles/nginx/tasks
usando o seu editor preferido:
- nano roles/nginx/tasks/main.yml
Neste arquivo, crie uma tarefa ou task que configura e inicia o Nginx adicionando o seguinte conteúdo:
---
- name: Update cache repositories and install Nginx
apt:
name: nginx
update_cache: yes
- name: Change nginx directory permission
file:
path: /etc/nginx/nginx.conf
mode: 0750
- name: start nginx
service:
name: nginx
state: started
Depois de adicionar o conteúdo, salve e saia do arquivo.
Em roles/nginx/tasks/main.yml
você define uma tarefa que atualizará o repositório de cache do seu Droplet, o que equivale a executar o comando apt update
manualmente em um servidor. Essa tarefa também altera as permissões do arquivo de configuração do Nginx e inicia o serviço Nginx.
Você também criará um arquivo main.yml
em roles/nodejs/tasks
para definir uma tarefa que configure o Node.js.
- nano roles/nodejs/tasks/main.yml
Adicione as seguintes tarefas a este arquivo:
---
- name: Update caches repository
apt:
update_cache: yes
- name: Add gpg key for NodeJS LTS
apt_key:
url: "https://deb.nodesource.com/gpgkey/nodesource.gpg.key"
state: present
- name: Add the NodeJS LTS repo
apt_repository:
repo: "deb https://deb.nodesource.com/node_{{ NODEJS_VERSION }}.x {{ ansible_distribution_release }} main"
state: present
update_cache: yes
- name: Install Node.js
apt:
name: nodejs
state: present
Salve e saia do arquivo quando terminar.
Em roles/nodejs/tasks/main.yml
, você primeiro define uma tarefa que atualizará o repositório de cache do seu Droplet. Em seguida, na próxima tarefa, você adiciona a chave GPG para o Node.js, que serve como um meio de verificar a autenticidade do repositório apt
do Node.js. As duas tarefas finais adicionam o repositório apt
do Node.js e o instalam.
Agora você definirá suas configurações do Ansible, como variáveis, a ordem em que você deseja que suas roles sejam executadas e configurações de privilégios de superusuário. Para fazer isso, você criará um arquivo chamado playbook.yml
, que serve como um entry point para o Kitchen. Quando você executa seus testes, o Kitchen inicia no seu arquivo playbook.yml
e procura as roles a serem executadas, que são seus arquivos roles/nginx/tasks/main.yml
e roles/nodejs/tasks/main.yml
.
Execute o seguinte comando para criar o playbook.yml
:
- nano playbook.yml
Adicione o seguinte conteúdo ao arquivo:
---
- hosts: all
become: true
remote_user: ubuntu
vars:
NODEJS_VERSION: 8
Salve e saia do arquivo.
Você criou as roles do playbook do Ansible com as quais executará seus testes para garantir que as condições especificadas no playbook sejam atendidas.
Neste passo, você escreverá testes para verificar se o Node.js está instalado no seu Droplet. Antes de escrever seu teste, vejamos o formato de um exemplo de teste InSpec. Como em muitos frameworks de teste, o código InSpec se assemelha a uma linguagem natural. O InSpec possui dois componentes principais, o assunto a ser examinado e o estado esperado desse assunto:
describe '<entity>' do
it { <expectation> }
end
Em block A, as palavras-chave do
e end
definem um bloco ou block
. A palavra-chave describe
é comumente conhecida como conjuntos ou suites de testes, que contêm casos de teste. A palavra-chave it
é usada para definir os casos de teste.
<entity>
é o assunto que você deseja examinar, por exemplo, um nome de pacote, serviço, arquivo ou porta de rede. O <expectation>
especifica o resultado desejado ou o estado esperado, por exemplo, o Nginx deve ser instalado ou deve ter uma versão específica. Você pode verificar a documentação da InSpec DSL para aprender mais sobre a linguagem InSpec.
Outro exemplo de bloco de teste InSpec:
control 'Pode ser qualquer coisa única' do
impact 0.7
title 'Um título inteligível'
desc 'Uma descrição opcional'
describe '<entity>' do
it { <expectation> }
end
end
A diferença entre o bloco A e o bloco B é o bloco control
. O bloco control
é usado como um meio de controle regulatório, recomendação ou requisito. O bloco control
tem um nome; geralmente um ID único, metadados como desc
, title
, impact
e, finalmente, agrupam blocos describe
relacionados para implementar as verificações.
desc
, title
, e impact
definem metadados que descrevem completamente a importância do controle, seu objetivo, com uma descrição sucinta e completa. impact
define um valor numérico que varia de 0.0
a 1.0
onde 0.0
a <0.01
é classificado como sem impacto, 0.01
a <0.4
é classificado como baixo impacto, 0.4
a <0.7
é classificado como médio impacto, 0,7
a <0,9
é classificado como alto impacto, 0,9
a 1,0
é classificado como controle crítico.
Agora, vamos implementar um teste. Usando a sintaxe do bloco A, você usará o recurso package
do InSpec para testar se o Node.js
está instalado no sistema. Você irá criar um arquivo chamado sample.rb
em seu diretório test/integration/default
para seus testes.
Crie o sample.rb
:
- nano test/integration/default/sample.rb
Adicione o seguinte ao seu arquivo:
describe package('nodejs') do
it { should be_installed }
end
Aqui seu teste está usando o recurso package
para verificar se o node.js está instalado.
Salve e saia do arquivo quando terminar.
Para executar este teste, você precisa editar kitchen.yml
para especificar o playbook que você criou anteriormente e para adicionar às suas configurações.
Abra seu arquivo kitchen.yml
:
- nano ansible_testing_dir/kitchen.yml
Substitua o conteúdo de kitchen.yml
com o seguinte:
---
driver:
name: digitalocean
provisioner:
name: ansible_playbook
hosts: test-kitchen
playbook: ./playbook.yml
verifier:
name: inspec
platforms:
- name: ubuntu-18
driver_config:
ssh_key: CAMINHO_PARA_SUA_CHAVE_PRIVADA_SSH
tags:
- inspec-testing
region: fra1
size: 1gb
private_networking: false
verifier:
inspec_tests:
- test/integration/default
suites:
- name: default
As opções de platform
incluem o seguinte:
name
: A imagem que você está usando.
driver_config
: A configuração do seu Droplet da DigitalOcean. Você está especificando as seguintes opções para driver_config
:
ssh_key
: Caminho para SUA_CHAVE_SSH_PRIVADA
. Sua SUA_CHAVE_SSH_PRIVADA
está localizada no diretório que você especificou ao criar sua chave ssh
.tags
: As tags associadas ao seu Droplet.region
: A region
ou região onde você deseja que seu Droplet seja hospedado.size
: A memória que você deseja que seu Droplet tenha.verifier
: Isso define que o projeto contém testes InSpec.
inspec_tests
especifica que os testes existem no diretório test/integration/default
do projeto.Observe que name
e region
usam abreviações. Você pode verificar na documentação do test-kitchen
as abreviações que você pode usar.
Depois de adicionar sua configuração, salve e saia do arquivo.
Execute o comando kitchen test
para executar o teste. Isso verificará se o Node.js está instalado — ele falhará propositalmente, porque você atualmente não possui a role Node.js no seu arquivo playbook.yml
:
- kitchen test
Você verá uma saída semelhante à seguinte:
Output: failing test results-----> Starting Kitchen (v1.24.0)
-----> Cleaning up any prior instances of <default-ubuntu-18>
-----> Destroying <default-ubuntu-18>...
DigitalOcean instance <145268853> destroyed.
Finished destroying <default-ubuntu-18> (0m2.63s).
-----> Testing <default-ubuntu-18>
-----> Creating <default-ubuntu-18>...
DigitalOcean instance <145273424> created.
Waiting for SSH service on 138.68.97.146:22, retrying in 3 seconds
[SSH] Established
(ssh ready)
Finished creating <default-ubuntu-18> (0m51.74s).
-----> Converging <default-ubuntu-18>...
$$$$$$ Running legacy converge for 'Digitalocean' Driver
-----> Installing Chef Omnibus to install busser to run tests
PLAY [all] *********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Downloading files from <default-ubuntu-18>
Finished converging <default-ubuntu-18> (0m55.05s).
-----> Setting up <default-ubuntu-18>...
$$$$$$ Running legacy setup for 'Digitalocean' Driver
Finished setting up <default-ubuntu-18> (0m0.00s).
-----> Verifying <default-ubuntu-18>...
Loaded tests from {:path=>". ansible_testing_dir.test.integration.default"}
Profile: tests from {:path=>"ansible_testing_dir/test/integration/default"} (tests from {:path=>"ansible_testing_dir.test.integration.default"})
Version: (not specified)
Target: ssh://root@138.68.97.146:22
System Package nodejs
× should be installed
expected that System Package nodejs is installed
Test Summary: 0 successful, 1 failure, 0 skipped
>>>>>> ------Exception-------
>>>>>> Class: Kitchen::ActionFailed
>>>>>> Message: 1 actions failed.
>>>>>> Verify failed on instance <default-ubuntu-18>. Please see .kitchen/logs/default-ubuntu-18.log for more details
>>>>>> ----------------------
>>>>>> Please see .kitchen/logs/kitchen.log for more details
>>>>>> Also try running `kitchen diagnose --all` for configuration
4.54s user 1.77s system 5% cpu 2:02.33 total
A saída informa que seu teste está falhando porque você não possui o Node.js instalado no Droplet que você provisionou com o kitchen
. Você corrigirá seu teste adicionando a role nodejs
ao seu arquivo playbook.yml
e executará o teste novamente.
Edite o arquivo playbook.yml
para incluir a role nodejs
:
- nano playbook.yml
Adicione as seguintes linhas destacadas ao seu arquivo:
---
- hosts: all
become: true
remote_user: ubuntu
vars:
NODEJS_VERSION: 8
roles:
- nodejs
Salve e feche o arquivo.
Agora, você executará novamente o teste usando o comando kitchen test
:
- kitchen test
Você verá a seguinte saída:
Output......
Target: ssh://root@46.101.248.71:22
System Package nodejs
✔ should be installed
Test Summary: 1 successful, 0 failures, 0 skipped
Finished verifying <default-ubuntu-18> (0m4.89s).
-----> Destroying <default-ubuntu-18>...
DigitalOcean instance <145512952> destroyed.
Finished destroying <default-ubuntu-18> (0m2.23s).
Finished testing <default-ubuntu-18> (2m49.78s).
-----> Kitchen is finished. (2m55.14s)
4.86s user 1.77s system 3% cpu 2:56.58 total
Seu teste agora passa porque você tem o Node.js instalado usando a role nodejs
.
Aqui está um resumo do que o Kitchen está fazendo em Test Action
:
O Kitchen interromperá a execução em seu Droplet se encontrar algum problema. Isso significa que, se o seu playbook do Ansible falhar, o InSpec não será executado e o seu Droplet não será destruído. Isso permite que você inspecione o estado da instância e corrija quaisquer problemas. O comportamento da ação final de destruição pode ser substituído, se desejado. Verifique a ajuda da CLI para a flag --destroy
executando o comando kitchen help test
.
Você escreveu seus primeiros testes e os executou no seu playbook com uma instância falhando antes de corrigir o problema. Em seguida, você estenderá seu arquivo de teste.
Neste passo, você adicionará mais casos de teste ao seu arquivo de teste para verificar se os módulos do Nginx estão instalados no seu Droplet e se o arquivo de configuração tem as permissões corretas.
Edite seu arquivo sample.rb
para adicionar mais casos de teste:
- nano test/integration/default/sample.rb
Adicione os seguintes casos de teste ao final do arquivo:
. . .
control 'nginx-modules' do
impact 1.0
title 'NGINX modules'
desc 'The required NGINX modules should be installed.'
describe nginx do
its('modules') { should include 'http_ssl' }
its('modules') { should include 'stream_ssl' }
its('modules') { should include 'mail_ssl' }
end
end
control 'nginx-conf' do
impact 1.0
title 'NGINX configuration'
desc 'The NGINX config file should owned by root, be writable only by owner, and not writeable or and readable by others.'
describe file('/etc/nginx/nginx.conf') do
it { should be_owned_by 'root' }
it { should be_grouped_into 'root' }
it { should_not be_readable.by('others') }
it { should_not be_writable.by('others') }
it { should_not be_executable.by('others') }
end
end
Esses casos de teste verificam se os módulos nginx-modules
no seu Droplet incluem http_ssl
, stream_ssl
e mail_ssl
. Você também está verificando as permissões do arquivo /etc/nginx/nginx.conf
.
Você está usando as palavras-chave it
e its
para definir seu teste. A palavra-chave its
é usada apenas para acessar propriedades de resources. Por exemplo, modules
é uma propriedade de nginx
.
Salve e saia do arquivo depois de adicionar os casos de teste.
Agora execute o comando kitchen test
para testar novamente:
- kitchen test
Você verá a seguinte saída:
Output...
Target: ssh://root@104.248.131.111:22
↺ nginx-modules: NGINX modules
↺ The `nginx` binary not found in the path provided.
× nginx-conf: NGINX configuration (2 failed)
× File /etc/nginx/nginx.conf should be owned by "root"
expected `File /etc/nginx/nginx.conf.owned_by?("root")` to return true, got false
× File /etc/nginx/nginx.conf should be grouped into "root"
expected `File /etc/nginx/nginx.conf.grouped_into?("root")` to return true, got false
✔ File /etc/nginx/nginx.conf should not be readable by others
✔ File /etc/nginx/nginx.conf should not be writable by others
✔ File /etc/nginx/nginx.conf should not be executable by others
System Package nodejs
✔ should be installed
Profile Summary: 0 successful controls, 1 control failure, 1 control skipped
Test Summary: 4 successful, 2 failures, 1 skipped
Você verá que alguns dos testes estão falhando. Você irá corrigi-los adicionando a role nginx
ao seu arquivo playbook e executando novamente o teste. No teste que falhou, você está verificando módulos nginx
e permissões de arquivo que não estão presentes atualmente no seu servidor.
Abra seu arquivo playbook.yml
:
- nano ansible_testing_dir/playbook.yml
Adicione a seguinte linha destacada às suas roles:
---
- hosts: all
become: true
remote_user: ubuntu
vars:
NODEJS_VERSION: 8
roles:
- nodejs
- nginx
Salve e feche o arquivo quando terminar.
Em seguida, execute seus testes novamente:
- kitchen test
Você verá a seguinte saída:
Output...
Target: ssh://root@104.248.131.111:22
✔ nginx-modules: NGINX version
✔ Nginx Environment modules should include "http_ssl"
✔ Nginx Environment modules should include "stream_ssl"
✔ Nginx Environment modules should include "mail_ssl"
✔ nginx-conf: NGINX configuration
✔ File /etc/nginx/nginx.conf should be owned by "root"
✔ File /etc/nginx/nginx.conf should be grouped into "root"
✔ File /etc/nginx/nginx.conf should not be readable by others
✔ File /etc/nginx/nginx.conf should not be writable by others
✔ File /etc/nginx/nginx.conf should not be executable by others
System Package nodejs
✔ should be installed
Profile Summary: 2 successful controls, 0 control failures, 0 controls skipped
Test Summary: 9 successful, 0 failures, 0 skipped
Depois de adicionar a role nginx
ao playbook, todos os seus testes agora passam. A saída mostra que os módulos http_ssl
, stream_ssl
e mail_ssl
estão instalados em seu Droplet e as permissões corretas estão definidas para o arquivo de configuração.
Quando terminar, ou não precisar mais do seu Droplet, você poderá destruí-lo executando o comando kitchen destroy
para excluí-lo após executar seus testes:
- kitchen destroy
Após este comando, você verá uma saída semelhante a:
Output-----> Starting Kitchen (v1.24.0)
-----> Destroying <default-ubuntu-18>...
Finished destroying <default-ubuntu-18> (0m0.00s).
-----> Kitchen is finished. (0m5.07s)
3.79s user 1.50s system 82% cpu 6.432 total
Você escreveu testes para o seu playbook, executou os testes e corrigiu os testes com falha para garantir que todos os testes sejam aprovados. Agora você está pronto para criar um ambiente virtual, escrever testes para o seu Playbook Ansible e executar seu teste no ambiente virtual usando o Kitchen.
Agora você tem uma base flexível para testar seu deployment Ansible, que lhe permite testar seus playbooks antes de executar em um servidor ativo. Você também pode empacotar seu teste em um perfil. Você pode usar perfis para compartilhar seu teste através do Github ou do Chef Supermarket e executá-lo facilmente em um servidor ativo.
Para detalhes mais abrangentes sobre o InSpec e o Kitchen, consulte a documentação oficial do InSpec e a documentação oficial do Kitchen.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!