Skip to content

Módulo infra_test

O módulo infra_test fornece um framework completo de validação e teste de infraestrutura nativo ao Sloth Runner. Ele permite que você insira asserções de teste diretamente nas suas tasks para verificar o resultado de operações de deploy ou configuration management.

Visão Geral

O infra_test é inspirado em ferramentas como Testinfra e InSpec, mas é nativo e integrado ao Sloth Runner, permitindo testes de infraestrutura diretamente nas tasks sem dependências externas.

Características Principais

  • Execução Local e Remota: Todos os testes podem ser executados localmente ou delegados para agentes remotos
  • Asserções Nativas: Interrompe a execução da task em caso de falha
  • Zero Dependências: Não requer instalação de ferramentas externas
  • Integração Total: Funciona perfeitamente com o sistema de agents do Sloth Runner
  • Detecção Automática de Pacotes: Suporta apt, yum, pacman, apk e brew automaticamente
  • Validação de Versões: Verifica versões específicas de pacotes instalados

Módulos de Teste Disponíveis

O infra_test oferece 6 categorias de testes:

  1. 🗂️ Testes de Arquivo - Verifica existência, permissões, conteúdo e proprietários
  2. 🌐 Testes de Rede - Valida portas, conectividade TCP/UDP e ping
  3. ⚙️ Testes de Serviço - Verifica status de serviços systemd/init
  4. 🔄 Testes de Processo - Valida processos em execução
  5. 💻 Testes de Comando - Executa comandos e valida saídas
  6. 📦 Testes de Pacote - Verifica instalação e versões de pacotes (NOVO!)

Parâmetro Target

Todas as funções de teste aceitam um parâmetro opcional target para especificar onde o teste será executado:

Parâmetro target Comportamento
Omitido ou "local" Executa no agente local (onde a task está rodando)
String (nome do agente) O teste é delegado ao agente remoto especificado
"localhost" Força o teste no agente onde a task foi agendada

Referência Rápida de Funções

🗂️ Testes de Arquivo

  • file_exists(path, [target]) - Verifica existência
  • is_directory(path, [target]) - Verifica se é diretório
  • is_file(path, [target]) - Verifica se é arquivo
  • file_contains(path, pattern, [target]) - Verifica conteúdo
  • file_mode(path, mode, [target]) - Verifica permissões
  • file_owner(path, user, [target]) - Verifica proprietário
  • file_group(path, group, [target]) - Verifica grupo
  • file_size(path, bytes, [target]) - Verifica tamanho

🌐 Testes de Rede

  • port_is_listening(port, [target]) - Verifica porta aberta
  • port_is_tcp(port, [target]) - Verifica porta TCP
  • port_is_udp(port, [target]) - Verifica porta UDP
  • can_connect(host, port, [timeout]) - Testa conectividade TCP
  • ping(host, [count], [target]) - Testa conectividade ICMP

⚙️ Testes de Serviço

  • service_is_running(name, [target]) - Verifica se serviço está ativo
  • service_is_enabled(name, [target]) - Verifica se está habilitado

🔄 Testes de Processo

  • process_is_running(pattern, [target]) - Verifica processo
  • process_count(pattern, count, [target]) - Conta processos

💻 Testes de Comando

  • command_succeeds(cmd, [target]) - Verifica exit code 0
  • command_fails(cmd, [target]) - Verifica exit code != 0
  • command_stdout_contains(cmd, pattern, [target]) - Verifica saída
  • command_stderr_is_empty(cmd, [target]) - Verifica stderr vazio
  • command_output_equals(cmd, expected, [target]) - Verifica saída exata

📦 Testes de Pacote

  • package_is_installed(name, [target]) - Verifica instalação
  • package_version(name, version, [target]) - Verifica versão

Modelo de Retorno

  • Sucesso: A função não retorna nada (ou retorna true)
  • Falha: A função lança um erro que interrompe a execução da task e marca a task como falha

Testes de Arquivo (File Tests)

file_exists(path, [target])

Verifica se um arquivo ou diretório existe.

Parâmetros: - path (string): Caminho do arquivo ou diretório - target (string, opcional): Agente onde executar o teste

Exemplo:

local infra_test = require("infra_test")

workflow("test-deployment")
  :task("verify-config", function()
    -- Verifica localmente
    infra_test.file_exists("/etc/nginx/nginx.conf")

    -- Verifica em agente remoto
    infra_test.file_exists("/etc/nginx/nginx.conf", "web-server-01")
  end)
  :delegate_to("prod-agent")

is_directory(path, [target])

Verifica se o caminho é um diretório.

Exemplo:

infra_test.is_directory("/var/www/html")
infra_test.is_directory("/opt/app", "app-server")

is_file(path, [target])

Verifica se o caminho é um arquivo regular.

Exemplo:

infra_test.is_file("/etc/hosts")
infra_test.is_file("/var/log/app.log", "log-server")

file_contains(path, pattern, [target])

Verifica se o arquivo contém uma string ou padrão regex.

Parâmetros: - path (string): Caminho do arquivo - pattern (string): String ou expressão regular a buscar - target (string, opcional): Agente onde executar

Exemplo:

-- Verifica string simples
infra_test.file_contains("/etc/nginx/nginx.conf", "worker_processes")

-- Verifica com regex
infra_test.file_contains("/var/log/app.log", "ERROR.*database", "app-server")

file_mode(path, mode, [target])

Verifica as permissões do arquivo.

Parâmetros: - path (string): Caminho do arquivo - mode (string): Permissões esperadas (ex: "644", "0644", "0o644") - target (string, opcional): Agente onde executar

Exemplo:

infra_test.file_mode("/etc/passwd", "644")
infra_test.file_mode("/root/.ssh/id_rsa", "0600", "bastion")

file_owner(path, user, [target])

Verifica se o proprietário (usuário) do arquivo corresponde.

Exemplo:

infra_test.file_owner("/var/www/html", "www-data")
infra_test.file_owner("/opt/app/config.yaml", "appuser", "app-server")

file_group(path, group, [target])

Verifica se o grupo do arquivo corresponde.

Exemplo:

infra_test.file_group("/var/www/html", "www-data")
infra_test.file_group("/etc/ssl/private", "ssl-cert", "web-server")

file_size(path, size_in_bytes, [target])

Verifica o tamanho exato do arquivo em bytes.

Exemplo:

infra_test.file_size("/etc/machine-id", 33)
infra_test.file_size("/var/cache/app.db", 1048576, "cache-server")


Testes de Rede e Porta (Network Tests)

port_is_listening(port, [target])

Verifica se a porta está aberta/escutando no alvo.

Exemplo:

infra_test.port_is_listening(80)
infra_test.port_is_listening(443, "web-server")
infra_test.port_is_listening(5432, "db-server")

port_is_tcp(port, [target])

Verifica se a porta está escutando usando o protocolo TCP.

Exemplo:

infra_test.port_is_tcp(22)
infra_test.port_is_tcp(3306, "mysql-server")

port_is_udp(port, [target])

Verifica se a porta está escutando usando o protocolo UDP.

Exemplo:

infra_test.port_is_udp(53)
infra_test.port_is_udp(123, "ntp-server")

can_connect(host, port, [timeout_ms])

Testa a conectividade TCP a partir do agente para um host externo/remoto.

Parâmetros: - host (string): Host de destino - port (number): Porta de destino - timeout_ms (number, opcional): Timeout em milissegundos (padrão: 5000)

Exemplo:

infra_test.can_connect("google.com", 443)
infra_test.can_connect("database.internal", 5432, 3000)

ping(host, [count], [target])

Testa a conectividade ICMP (ping) para um host.

Parâmetros: - host (string): Host de destino - count (number, opcional): Número de pacotes (padrão: 4) - target (string, opcional): Agente onde executar

Exemplo:

infra_test.ping("8.8.8.8")
infra_test.ping("internal-router", 10)
infra_test.ping("remote-server", 5, "edge-agent")


Testes de Serviço e Processo (Service & Process Tests)

service_is_running(name, [target])

Verifica se o serviço está ativo (via systemctl, service, etc.).

Exemplo:

infra_test.service_is_running("nginx")
infra_test.service_is_running("postgresql", "db-server")

service_is_enabled(name, [target])

Verifica se o serviço está habilitado para iniciar no boot.

Exemplo:

infra_test.service_is_enabled("docker")
infra_test.service_is_enabled("nginx", "web-server")

process_is_running(pattern, [target])

Verifica se um processo com um nome ou padrão de comando está em execução.

Exemplo:

infra_test.process_is_running("nginx")
infra_test.process_is_running("java.*spring-boot", "app-server")

process_count(pattern, count, [target])

Verifica se o número de processos corresponde a um valor exato.

Parâmetros: - pattern (string): Padrão para buscar processos - count (number): Número esperado de processos - target (string, opcional): Agente onde executar

Exemplo:

infra_test.process_count("nginx", 4)
infra_test.process_count("worker", 8, "worker-node")


Testes de Comando e Saída (Command & Output Tests)

command_succeeds(cmd, [target])

Verifica se o comando retorna o código de saída 0.

Exemplo:

infra_test.command_succeeds("which docker")
infra_test.command_succeeds("systemctl is-active nginx", "web-server")

command_fails(cmd, [target])

Verifica se o comando retorna um código de saída diferente de zero.

Exemplo:

infra_test.command_fails("systemctl is-active fake-service")
infra_test.command_fails("test -f /nonexistent", "app-server")

command_stdout_contains(cmd, pattern, [target])

Verifica se a saída padrão do comando contém uma string ou regex.

Parâmetros: - cmd (string): Comando a executar - pattern (string): String ou regex a buscar na saída - target (string, opcional): Agente onde executar

Exemplo:

infra_test.command_stdout_contains("cat /etc/os-release", "Ubuntu")
infra_test.command_stdout_contains("docker --version", "version 20", "docker-host")

command_stderr_is_empty(cmd, [target])

Verifica se a saída de erro do comando está vazia.

Exemplo:

infra_test.command_stderr_is_empty("ls /home")
infra_test.command_stderr_is_empty("cat /etc/hosts", "web-server")

command_output_equals(cmd, expected_output, [target])

Verifica se a saída padrão é exatamente igual ao valor esperado.

Parâmetros: - cmd (string): Comando a executar - expected_output (string): Saída esperada - target (string, opcional): Agente onde executar

Exemplo:

infra_test.command_output_equals("whoami", "root")
infra_test.command_output_equals("cat /etc/hostname", "web-01", "web-server")


Testes de Pacote (Package Tests)

package_is_installed(package_name, [target])

Verifica se um pacote está instalado no sistema. O módulo detecta automaticamente o gerenciador de pacotes disponível (apt/dpkg, yum/rpm, pacman, apk, brew).

Parâmetros: - package_name (string): Nome do pacote - target (string, opcional): Agente onde executar o teste

Gerenciadores Suportados: - Debian/Ubuntu: dpkg - RedHat/CentOS/Fedora: rpm - Arch Linux: pacman - Alpine Linux: apk - macOS: brew

Exemplo:

local infra_test = require("infra_test")

-- Verifica se nginx está instalado localmente
infra_test.package_is_installed("nginx")

-- Verifica em agente remoto
infra_test.package_is_installed("postgresql", "db-server")

-- Verifica múltiplos pacotes
infra_test.package_is_installed("docker-ce")
infra_test.package_is_installed("docker-compose")
infra_test.package_is_installed("git")

package_version(package_name, expected_version, [target])

Verifica a versão de um pacote instalado. Aceita versão exata ou prefixo.

Parâmetros: - package_name (string): Nome do pacote - expected_version (string): Versão esperada (ou prefixo da versão) - target (string, opcional): Agente onde executar o teste

Exemplo:

-- Verifica versão exata
infra_test.package_version("nginx", "1.18.0")

-- Verifica prefixo de versão (ex: 1.18.x)
infra_test.package_version("nginx", "1.18", "web-server")

-- Verifica versão major
infra_test.package_version("postgresql", "14", "db-server")


Exemplos Completos

Exemplo 1: Teste de Deploy de Aplicação

local infra_test = require("infra_test")
local pkg = require("pkg")

workflow("deploy-and-test-app")
  :task("install-nginx", function()
    pkg.install("nginx")
  end)

  :task("verify-installation", function()
    -- Verifica se o pacote foi instalado
    infra_test.package_is_installed("nginx")

    -- Verifica se os arquivos existem
    infra_test.file_exists("/usr/sbin/nginx")
    infra_test.file_exists("/etc/nginx/nginx.conf")

    -- Verifica se o serviço está rodando e habilitado
    infra_test.service_is_running("nginx")
    infra_test.service_is_enabled("nginx")

    -- Verifica se a porta está aberta
    infra_test.port_is_tcp(80)

    -- Verifica se o processo está ativo
    infra_test.process_is_running("nginx")
  end)

  :task("verify-config", function()
    -- Verifica permissões e proprietário
    infra_test.file_mode("/etc/nginx/nginx.conf", "644")
    infra_test.file_owner("/var/www/html", "www-data")

    -- Verifica conteúdo da configuração
    infra_test.file_contains("/etc/nginx/nginx.conf", "worker_processes")
  end)

  :delegate_to("web-server-01")

Exemplo 2: Validação Multi-Agent

local infra_test = require("infra_test")

workflow("test-infrastructure")
  :task("test-web-servers", function()
    -- Testa múltiplos servidores web
    local servers = {"web-01", "web-02", "web-03"}

    for _, server in ipairs(servers) do
      print("Testing " .. server)

      infra_test.service_is_running("nginx", server)
      infra_test.port_is_listening(80, server)
      infra_test.port_is_listening(443, server)
      infra_test.file_exists("/var/www/html/index.html", server)
    end
  end)

  :task("test-connectivity", function()
    -- Testa conectividade entre servidores
    infra_test.can_connect("db-server.internal", 5432)
    infra_test.can_connect("cache-server.internal", 6379)
    infra_test.ping("load-balancer", 5)
  end)

Exemplo 3: Teste de Configuração Completa

local infra_test = require("infra_test")
local systemd = require("systemd")

workflow("deploy-microservice")
  :task("create-service", function()
    systemd.create_service("myapp", {
      description = "My Application",
      exec_start = "/opt/myapp/bin/start.sh",
      user = "appuser",
      working_directory = "/opt/myapp"
    })

    systemd.enable("myapp")
    systemd.start("myapp")
  end)

  :task("validate-deployment", function()
    -- Verifica estrutura de diretórios
    infra_test.is_directory("/opt/myapp")
    infra_test.is_directory("/opt/myapp/bin")
    infra_test.is_directory("/opt/myapp/logs")

    -- Verifica arquivos
    infra_test.is_file("/opt/myapp/bin/start.sh")
    infra_test.file_mode("/opt/myapp/bin/start.sh", "755")
    infra_test.file_owner("/opt/myapp", "appuser")

    -- Verifica serviço
    infra_test.service_is_running("myapp")
    infra_test.service_is_enabled("myapp")

    -- Verifica processo
    infra_test.process_is_running("myapp")

    -- Verifica porta da aplicação
    infra_test.port_is_listening(8080)

    -- Testa endpoint da aplicação
    infra_test.command_succeeds("curl -s http://localhost:8080/health")
    infra_test.command_stdout_contains(
      "curl -s http://localhost:8080/health",
      "\"status\":\"up\""
    )
  end)

  :delegate_to("app-server-prod")

Exemplo 4: Teste de Segurança

local infra_test = require("infra_test")

workflow("security-audit")
  :task("check-file-permissions", function()
    -- Verifica permissões críticas
    infra_test.file_mode("/etc/passwd", "644")
    infra_test.file_mode("/etc/shadow", "640")
    infra_test.file_mode("/root/.ssh/id_rsa", "600")

    -- Verifica proprietários
    infra_test.file_owner("/etc/shadow", "root")
    infra_test.file_group("/etc/shadow", "shadow")
  end)

  :task("check-services", function()
    -- Verifica que serviços desnecessários não estão rodando
    infra_test.command_fails("systemctl is-active telnet")
    infra_test.command_fails("systemctl is-active ftp")

    -- Verifica que serviços críticos estão rodando
    infra_test.service_is_running("sshd")
    infra_test.service_is_running("fail2ban")
  end)

  :task("check-firewall", function()
    -- Verifica regras de firewall
    infra_test.command_succeeds("iptables -L | grep -q 'Chain INPUT'")
    infra_test.command_stdout_contains(
      "iptables -L INPUT",
      "ACCEPT.*tcp.*dpt:ssh"
    )
  end)

  :delegate_to("prod-server")

Exemplo 5: Teste de Pacotes e Dependências

local infra_test = require("infra_test")
local pkg = require("pkg")

workflow("setup-development-environment")
  :task("install-packages", function()
    pkg.install("git")
    pkg.install("docker-ce")
    pkg.install("nodejs")
    pkg.install("python3")
  end)

  :task("verify-packages", function()
    -- Verifica se todos os pacotes foram instalados
    infra_test.package_is_installed("git")
    infra_test.package_is_installed("docker-ce")
    infra_test.package_is_installed("nodejs")
    infra_test.package_is_installed("python3")

    -- Verifica versões específicas
    infra_test.package_version("nodejs", "18")
    infra_test.package_version("python3", "3.10")

    -- Verifica binários disponíveis
    infra_test.command_succeeds("which git")
    infra_test.command_succeeds("which docker")
    infra_test.command_succeeds("which node")
    infra_test.command_succeeds("which python3")

    -- Verifica versões via comando
    infra_test.command_stdout_contains("node --version", "v18")
    infra_test.command_stdout_contains("python3 --version", "Python 3.10")
  end)

  :task("verify-docker-service", function()
    infra_test.service_is_running("docker")
    infra_test.service_is_enabled("docker")
    infra_test.port_is_listening(2375)
  end)

  :delegate_to("dev-machine")

Exemplo 6: Auditoria de Pacotes Multi-Agent

local infra_test = require("infra_test")

workflow("audit-packages")
  :task("audit-web-servers", function()
    local servers = {"web-01", "web-02", "web-03"}
    local required_packages = {
      "nginx",
      "certbot",
      "ufw",
      "fail2ban"
    }

    for _, server in ipairs(servers) do
      print("Auditing " .. server)

      for _, pkg_name in ipairs(required_packages) do
        infra_test.package_is_installed(pkg_name, server)
      end

      -- Verifica versão do nginx
      infra_test.package_version("nginx", "1.18", server)

      -- Verifica que pacotes inseguros não estão instalados
      infra_test.command_fails("dpkg -l telnetd", server)
      infra_test.command_fails("dpkg -l rsh-server", server)
    end
  end)

  :task("audit-database-servers", function()
    local db_servers = {"db-01", "db-02"}

    for _, server in ipairs(db_servers) do
      print("Auditing database: " .. server)

      -- Verifica pacotes do PostgreSQL
      infra_test.package_is_installed("postgresql-14", server)
      infra_test.package_is_installed("postgresql-contrib", server)

      -- Verifica serviço
      infra_test.service_is_running("postgresql", server)
      infra_test.port_is_listening(5432, server)

      -- Verifica versão
      infra_test.command_stdout_contains(
        "psql --version",
        "14.",
        server
      )
    end
  end)

Melhores Práticas

  1. Organize Testes por Contexto: Agrupe testes relacionados em tasks separadas
  2. Use Nomes Descritivos: Nomeie suas tasks de forma clara (ex: "verify-nginx-config")
  3. Teste Progressivamente: Comece com testes básicos (existência) e avance para testes complexos (conteúdo, permissões)
  4. Teste em Múltiplos Agentes: Use o parâmetro target para validar configurações em vários servidores
  5. Combine com Módulos: Integre infra_test com pkg, systemd, e outros módulos para ciclos completos de deploy+teste
  6. Valide Pacotes: Sempre verifique se pacotes foram instalados corretamente após operações de instalação
  7. Use Versões Específicas: Para ambientes de produção, valide versões específicas de pacotes críticos

Casos de Uso Recomendados

1. Deploy com Validação

Combine instalação de pacotes com validação imediata:

workflow("deploy-with-validation")
  :task("install", function()
    pkg.install("nginx")
  end)
  :task("validate", function()
    infra_test.package_is_installed("nginx")
    infra_test.service_is_running("nginx")
    infra_test.port_is_listening(80)
  end)

2. Auditoria de Conformidade

Valide que todos os servidores estão em conformidade:

workflow("compliance-check")
  :task("check-security-packages", function()
    infra_test.package_is_installed("fail2ban")
    infra_test.package_is_installed("ufw")
    infra_test.service_is_running("fail2ban")
  end)

3. Validação de Dependências

Verifique que todas as dependências necessárias estão presentes:

workflow("check-dependencies")
  :task("verify", function()
    local deps = {"python3", "python3-pip", "python3-venv"}
    for _, dep in ipairs(deps) do
      infra_test.package_is_installed(dep)
    end
  end)

Notas Importantes

  • ⚠️ Todos os testes são síncronos e bloqueiam a execução até completarem
  • ⚠️ Uma falha em qualquer teste interrompe a task imediatamente
  • ⚠️ Testes em agentes remotos requerem que o agente esteja conectado e ativo
  • ⚠️ Comandos shell são executados com sh -c, portanto use sintaxe POSIX-compatível

Diferenças com Outras Ferramentas

vs Testinfra

  • ✅ Integrado nativamente ao Sloth Runner (sem Python/pip)
  • ✅ Usa o sistema de agents nativo
  • ✅ Sintaxe Lua consistente com o resto do workflow

vs InSpec

  • ✅ Mais leve e sem dependências Ruby
  • ✅ Integração total com tasks e workflows
  • ✅ Execução em tempo real durante o deploy

vs Serverspec

  • ✅ Não requer instalação de gems
  • ✅ Melhor performance para testes rápidos
  • ✅ Suporte nativo a execução paralela (via goroutines)