Preparar e compilar
Clonar, compilar e testar
Antes de leres: Preparar o teu ambiente: a toolchain fixada, o protoc, e um host que passa nas suas verificações.
Esta página assume o host de Preparar o teu ambiente: a toolchain de Rust
fixada e o protoc. Depois dela consegues compilar e instalar a tua árvore, correr cada gate
de CI localmente, e correr a bateria E2E e o arnês de caos sem tocar em estado real do motor.
Tudo aqui corre a partir da raiz da tua checkout — idealmente um git worktree: um directório
de trabalho separado com o seu próprio branch, um por tarefa, criado a partir de origin/main (o
comando está em Começa aqui, passo 2; as
regras estão em Fluxo de contribuição).
Clonar#
git clone https://github.com/angolardevops/delonix-runtime.git
cd delonix-runtime
git fetch --tags origin # several gates compare against the release tags
Compilar#
cargo build --workspace # every crate and every binary
cargo build -p delonix-runtime-bin # just the `delonix` CLI
cargo build --release -p delonix-runtime-bin # what the docs generator and the CLI gates use
O workspace produz estes binários, a partir destes pacotes:
| Binário | Pacote | Saída |
|---|---|---|
delonix (a CLI) |
delonix-runtime-bin |
target/debug/delonix ou target/release/delonix |
delonix-cri |
delonix-cri |
target/ |
delonix-mcp |
delonix-mcp-bin |
target/ |
delonix-mgmt |
delonix-mgmt-bin |
target/ |
O workflow de release compila exactamente estes quatro pacotes. Se definires CARGO_TARGET_DIR, os
binários vão para lá em vez de target/.
Duas notas práticas:
- Testa sempre o binário que compilaste (
./target/debug/delonix), nunca umdelonixno teuPATH— esse é uma release instalada, muitas vezes várias versões atrás. - Se trabalhas em vários worktrees, apontá-los todos para um
CARGO_TARGET_DIRpartilhado poupa disco, mas dois builds a correr ao mesmo tempo sobre ele esperam um pelo outro e podem invalidar os artefactos um do outro. Um directório de target por worktree é mais lento da primeira vez e previsível depois disso.
Instalar a tua build localmente#
A maioria das mudanças nunca precisa de uma build instalada: corre ./target/debug/delonix a partir
do teu worktree. Instala só quando precisas de um caminho estável — uma unit do systemd, um script
noutra shell, um kubelet a falar com o delonix-cri. Antes e depois de instalar, confirma que
build estás a correr:
./target/debug/delonix --version # commit: <hash> (+N commits since vX.Y.Z) · built: <date>
command -v delonix # which `delonix` your shell would run instead
A linha commit: vem de bins/delonix-runtime-bin/build.rs (DELONIX_GIT_HASH,
DELONIX_GIT_SINCE). Entre releases todas as builds levam o mesmo número de versão, por isso o
commit é a única forma de distinguir a tua build da publicada.
Como o delonix encontra os seus binários de servidor#
delonix serve cri, delonix serve api e delonix mcp não contêm os servidores: fazem exec de
delonix-cri, delonix-mgmt e delonix-mcp (exec_server em
bins/delonix-runtime-bin/src/cmd/serve.rs). A procura é:
- o ficheiro com esse nome ao lado do
delonixem execução; - senão, o nome no
PATH.
O delonix passa ao servidor a sua própria versão em DELONIX_DISPATCH_VERSION, e um servidor de
outra release recusa-se a arrancar. Passa-se também a si próprio em DELONIX_BIN, para que o
servidor volte a chamar a mesma CLI. Um servidor arrancado directamente (por exemplo por uma unit)
encontra a CLI através de DELONIX_BIN, depois de um delonix ao seu lado, depois do PATH
(cli_bin em crates/contexts/delonix-node/src/dispatch.rs). Mantém juntos os quatro
binários de uma mesma build; uma mistura da tua build com uma release é recusada, ou corre código
que não querias testar.
delonix cluster kubeadm e delonix image vm build procuram o delonix-cri pela sua própria ordem
(resolve_cri_bin em bins/delonix-runtime-bin/src/cmd/vmimage.rs): --cri-bin, depois ao lado
do delonix, depois um cargo build --release -p delonix-cri se o directório actual estiver dentro
de uma checkout do código-fonte, e só então um download do asset publicado.
Compila os quatro antes de os instalares:
cargo build --release -p delonix-runtime-bin -p delonix-cri -p delonix-mgmt-bin -p delonix-mcp-bin
Opção A — corrê-la a partir do worktree (a mais segura)#
Nada é copiado, por isso nada fora da tua checkout a pode apanhar por acidente:
alias delonix-dev="$PWD/target/release/delonix"
delonix-dev --version
Os servidores são encontrados porque estão ao lado dele em target/release/. No Ubuntu 23.10+ este
caminho precisa do seu próprio perfil AppArmor (ver
AppArmor).
Opção B — instalar para o teu utilizador em ~/.local/bin#
install -d ~/.local/bin
install -m 0755 target/release/delonix target/release/delonix-cri \
target/release/delonix-mgmt target/release/delonix-mcp ~/.local/bin/
hash -r # forget the path your shell cached
command -v delonix && delonix --version
Se também estiver instalada uma release em /usr/local/bin, ganha o directório que aparecer
primeiro no PATH.
AppArmor. O scripts/install.sh escreve um perfil, /etc/apparmor.d/delonix, associado a
<install dir>/delonix, e só em hosts com
kernel.apparmor_restrict_unprivileged_userns=1. Um binário que copiaste para um caminho novo não
está coberto. Não voltes a correr o instalador para "mover" esse perfil numa máquina que também usa
uma instalação publicada: o ficheiro do perfil é reescrito, e o binário publicado perde-o. Acrescenta
antes um segundo perfil com outro nome — a mesma forma que o instalador escreve, por isso não
substitui nada:
printf 'abi <abi/4.0>,\ninclude <tunables/global>\nprofile delonix-dev %s flags=(unconfined) {\n userns,\n}\n' \
"$HOME/.local/bin/delonix" | sudo tee /etc/apparmor.d/delonix-dev >/dev/null
sudo apparmor_parser -r /etc/apparmor.d/delonix-dev
Não verificado aqui: este comando reproduz o install.sh (o bloco do AppArmor) com outro nome de
perfil e outro ficheiro; não foi carregado num host com a restrição activa enquanto esta página era
escrita.
O instalador acrescenta também completion da shell, man pages e ficheiros de sintaxe para editores, mas só na sua fase do binário. Para a tua própria build, gera-os a partir do binário se os quiseres:
mkdir -p ~/.local/share/bash-completion/completions
delonix completion shell bash > ~/.local/share/bash-completion/completions/delonix
delonix man --dir ~/.local/share/man
Opção C — instalação de sistema em /usr/local/bin#
sudo install -m 0755 target/release/delonix target/release/delonix-cri \
target/release/delonix-mgmt target/release/delonix-mcp /usr/local/bin/
Só numa máquina onde nenhum workload Delonix esteja em uso. O binário instalado não é só um comando:
- as units de arranque escritas por
delonix system boot enablearrancamExecStart=<exe> container start <name>, onde<exe>é o caminho do binário que correu oenable(bins/delonix-runtime-bin/src/cmd/boot.rs, prefixo de unitdelonix-boot-); substituir esse ficheiro muda o que sobe depois do próximo reboot; - o
dist/delonix-cri.servicecorre/usr/local/bin/delonix-cri, por isso num nó Kubernetes o kubelet recebe a tua build no próximo reinício dessa unit; - os processos de longa duração arrancados antes (o pin de rede e o processo de controlo, os supervisores de containers) continuam a correr o código com que arrancaram, por isso durante algum tempo correm duas builds lado a lado.
Verifica primeiro:
delonix container ls -a; delonix vm ls
ls ~/.config/systemd/user/delonix-boot-* /etc/systemd/system/delonix-* 2>/dev/null
pgrep -a delonix
Preparar o host#
O scripts/install.sh faz dois trabalhos separados. Só o primeiro diz respeito ao binário:
| Parte | O que faz | Flag que a salta ou activa |
|---|---|---|
| Binário | descarrega uma release, verifica a assinatura minisign e o sha256, instala o delonix (mais delonix-mcp, delonix-mgmt, e delonix-cri com --with-cri), depois completion, man pages, sintaxe para editores e a extensão de editor |
saltada com --no-binary; --user escolhe ~/.local/bin |
| Pacotes do host | slirp4netns, uidmap, nftables, iproute2, conntrack |
sempre |
| Identidade rootless | intervalos em /etc/subuid e /etc/subgid para o teu utilizador |
sempre |
| AppArmor | perfil para <dir>/delonix quando a restrição de userns está activa |
sempre (quando a restrição está ligada) |
| Debian antigo | kernel. quando está a 0 |
sempre (quando necessário) |
| Dependências de VM | libvirt, qemu, ferramentas de cloud-init; Cloud Hypervisor e o seu firmware descarregados do upstream | saltadas com --no-vm |
| Provider de VM por omissão | providers.yaml com defaultProvider: libvirt (ADR-0054): /etc/delonix/, ou ~/.config/delonix/ com --user; escrito só se não existir, nunca reescrito |
--vm-provider cloud-hypervisor muda o default; saltado com --no-vm |
| Afinação do kernel | /, / |
saltada com --no-tune |
| Delegação de cgroup | drop-in do user@.service, só se ainda não estiver delegado |
saltada com --no-delegate |
| Aceleradores | CDI da NVIDIA e grupo render, só quando há uma GPU |
saltados com --no-gpu |
| Opt-ins | portas abaixo de 1024 (--low-ports), construção de imagens de VM (--with-image-build), afinação para escala (--production) |
desligados por omissão |
Para preparares um host para a tua própria build sem descarregar nenhuma release do Delonix,
corre o instalador a partir da tua checkout com --no-binary:
bash scripts/install.sh --no-binary # add --no-vm if you do not need VM dependencies
bash scripts/install.sh --help # the full flag list, from the script header
Com --no-binary, o perfil AppArmor é escrito para o directório do delonix que o
command -v delonix encontrar (ou /usr/local/bin se não houver nenhum) — a mesma cautela de cima
aplica-se numa máquina com uma instalação publicada. O script usa sudo para os passos do host.
Depois pergunta ao binário se o host está pronto (só leitura):
delonix system doctor # every prerequisite, and how to fix each; --strict exits non-zero on a failure
delonix system info # state root, rootless, cgroup delegation, network infra
Ver Diagnosticar o host para o significado de cada verificação.
Usar um state root isolado#
Uma build instalada usa por omissão o teu state root real: os mesmos containers, redes e volumes
que a release. Exporta primeiro DELONIX_ROOT e DELONIX_NET_RUNTIME_DIR (ver
Isolar o estado do motor), e ver
Variáveis de ambiente para todas as outras variáveis que a tua
build lê.
Desinstalar e reverter#
Não há flag de desinstalação no install.sh. Remove o que copiaste:
rm -f ~/.local/bin/delonix ~/.local/bin/delonix-cri ~/.local/bin/delonix-mgmt ~/.local/bin/delonix-mcp
hash -r
sudo apparmor_parser -R /etc/apparmor.d/delonix-dev && sudo rm /etc/apparmor.d/delonix-dev # if you added it
Para voltares a um binário publicado, corre o instalador outra vez; ele substitui os binários no seu directório de instalação pela release que indicares:
curl -fsSL https://github.com/angolardevops/delonix-runtime/releases/latest/download/install.sh | bash -s -- --user
curl -fsSL https://github.com/angolardevops/delonix-runtime/releases/latest/download/install.sh | bash -s -- --version vX.Y.Z
Os ficheiros de completion e as man pages que geraste à mão não são removidos por nenhum dos dois
passos. Termina com delonix --version para confirmares o commit onde voltaste a estar.
Correr os testes#
cargo test --workspace # the whole suite
cargo test -p delonix-sdn # one crate
cargo test -p delonix-stack -- reconcile # tests whose path contains "reconcile"
cargo test -p delonix-stack -- --exact kinds::tests::nenhum_kind_aparece_duas_vezes
Os testes que precisam de privilégios ou de um host real saltam-se a si próprios em vez de falharem,
por isso a suite tem significado num portátil e na CI. Alguns testes ao vivo estão marcados com
#[ignore] e nomeiam o comando para os correr no seu doc comment (por exemplo em
crates/adapters/delonix-vm/src/lib.rs); corre-os só numa máquina que é tua:
cargo test -p <crate> -- --ignored <test-name>
Um cargo test verde prova a lógica pura. Não prova que uma mudança em namespaces, cgroups, no
holder de rede ou no arranque de VMs funciona — isso precisa de uma corrida ao vivo (ver
Bateria end-to-end e Arnês de caos).
Os gates que a CI corre#
| Job de CI | O que verifica |
|---|---|
fmt |
rustfmt |
lang |
lang ratchet |
arch |
arch fitness |
contract |
contract gate |
version |
version gate |
cli-surface |
cli surface |
clippy |
clippy -D warnings |
test |
test |
test-arm64 |
test (arm64) |
deny |
cargo-deny |
fuzz |
fuzz (60s smoke, per target) |
script-tests |
script tests (Python gates) |
perf-probe |
perf probe (environment and bench) |
perf |
perf gate (regression against the baseline) |
release-verify |
release verify |
docs |
generated docs and valid examples |
Todos os jobs de .github/workflows/ci.yml podem ser reproduzidos localmente. Corre os que
correspondem ao que tocaste antes de fazeres push; corre todos antes de pedires revisão.
| Job | Comando local | Falha quando |
|---|---|---|
fmt |
cargo fmt --all --check |
o código não está formatado pelo rustfmt (configuração por omissão) |
lang |
python3 scripts/ |
identificadores, comentários ou mensagens em português aumentam — ou diminuem sem baixar o scripts/ no mesmo commit (--list mostra-os, --update baixa a linha de base) |
arch |
python3 scripts/ |
uma dependência vai contra a direcção das camadas, um crate está no directório errado, um crate membro fixa a versão de uma dependência, um nome de consumidor aparece no código, ou um ratchet de dívida se move (--list, --update) |
arch |
python3 scripts/ |
um facto gerado em docs/dev/ está desactualizado — corre python3 scripts/ e faz commit |
contract |
python3 scripts/ |
o contrato de nó em proto/delonix/node/v1 não está limpo segundo o buf format, falha o buf lint, quebra a compatibilidade com a última tag, não tem um mapeamento HTTP, ou o docs/api/openapi.yaml não é o gerado (--update reescreve-o). Precisa de protoc, buf v1.73.0 e protoc-gen-openapi v0.7.1 no PATH, e das tags |
version |
python3 scripts/ |
a versão do workspace não é a tag mais recente que o commit contém (ver Fluxo de contribuição) ou o branch não contém a tag mais recente. Precisa das tags |
cli-surface |
cargo build --release -p delonix-runtime-bin && scripts/ |
uma folha da CLI foi acrescentada, removida ou reclassificada sem actualizar o scripts/ no mesmo commit (scripts/) |
cli-surface |
python3 scripts/ |
um comando delonix … citado na documentação actual não existe na árvore do binário |
clippy |
cargo clippy --workspace --all-targets --locked -- -D warnings |
qualquer aviso |
test |
cargo build --workspace --locked && cargo test --workspace --locked --no-fail-fast |
qualquer teste falha |
deny |
cargo deny check advisories licenses sources |
um aviso RUSTSEC, uma licença ou fonte não permitida (deny.toml) |
docs |
cargo build --release -p delonix-runtime-bin && python3 docs/ |
o site em commit não é o que o gerador produz a partir deste binário |
docs |
. e . |
um exemplo publicado usa uma forma obsoleta ou tem referências por resolver |
O cli-tree.sh e o docs_cli_gate.py lêem a árvore a partir do --help real do binário; define
DELONIX_BIN=/path/to/delonix para escolher qual binário. O docs/gen.py usa por omissão
target/release/delonix e precisa do módulo Python markdown. O job docs também gera as
man pages (delonix man --dir <dir> --index) e verifica-as com groff -mandoc -ww -z.
Workflows à parte, não exigidos em todas as mudanças: o chaos.yml corre o arnês de caos num runner
limpo (e reporta skipped quando o runner bloqueia user namespaces), o release.yml publica uma
tag, e o vm-image.yml / vm-appliances.yml constroem imagens de VM.
Isolar o estado do motor#
Tudo o que vá além do --help toca no estado do motor. Por omissão esse é o teu estado real: os
teus containers, redes, volumes e o holder de rede. Antes de correres o motor para testar —
à mão, através do e2e.sh, ou através de qualquer script — aponta as duas raízes de estado para
um directório descartável:
export DELONIX_ROOT=$HOME/scratch/dlx/root # containers, images, networks, IPAM, volumes
export DELONIX_NET_RUNTIME_DIR=/tmp/dlx-run # the holder's control and slirp sockets
mkdir -p "$DELONIX_ROOT" "$DELONIX_NET_RUNTIME_DIR"
As duas, sempre. Meia isolação é pior do que nenhuma. Os sockets de rede e os pidfiles são
resolvidos separadamente: os pidfiles vivem debaixo da raiz de estado, enquanto os sockets de
controlo e do slirp do holder vivem num directório de runtime (por omissão /tmp/delonix-net-<uid>).
Quando duas raízes de estado acabaram no mesmo directório de runtime, cada uma leu o seu próprio
pidfile (ausente), concluiu que não havia infra de rede, e arrancou ou desmontou infra por cima dos
sockets da outra. Num host de desenvolvimento com workloads vivos isto acabou com a raiz real a
reconstruir a sua infra de rede e a reiniciar containers reais.
O motor deriva agora um sufixo a partir de um DELONIX_ROOT que não seja o de omissão para o
directório de runtime (runtime_dir/root_suffix em crates/adapters/delonix-sdn/src/infra.rs), o
que fecha essa colisão no caso comum. Continua a exportar as duas na mesma: torna a isolação
explícita, mantém o caminho do socket curto e sob o teu controlo, e é o que o scripts/e2e.sh e o
scripts/chaos.sh fazem (o e2e preenche a variável que não tiveres exportado).
Mantém o DELONIX_NET_RUNTIME_DIR curto: um caminho de socket unix com mais de cerca de 108 bytes
falha com path must be shorter than SUN_LEN. O e2e.sh recusa um directório de runtime com mais de
80 bytes.
Quando terminares, desmonta a infra de rede isolada com as mesmas duas variáveis exportadas:
./target/debug/delonix net netns down
Receitas de imagens de VM (scripts/verify-images.sh)#
As receitas em images/ são verificadas de duas maneiras. Um teste unitário no crate da CLI
(vmspec::every_shipped_recipe_is_valid_and_complete) falha se uma receita deixar de ser lida ou
apontar para um ficheiro ou um builder que não existe. O scripts/verify-images.sh vai mais longe:
constrói offline as quatro distros de cloud image num DELONIX_ROOT isolado e lê o qcow2 resultante
contra o que a receita declarou; o --self-test prova que as verificações conseguem falhar numa imagem
que ninguém construiu. Precisa de libguestfs-tools (ver Construir microVMs) e não
faz parte dos gates da CI. As fases --packages, --profile, --boot e --appliance existem mas
não tinham sido corridas quando a v4.2.0 foi lançada.
Bateria end-to-end (scripts/e2e.sh)#
O e2e.sh corre a CLI contra o kernel real: o --help de todas as folhas, mais execuções reais de
uma grande parte da superfície, e imprime um relatório PASS/FAIL/SKIP/XFAIL (detalhe em JSONL em
$OUT/results.jsonl, por omissão OUT=/tmp/delonix-e2e).
./scripts/e2e.sh # uses ./target/debug/delonix
./scripts/e2e.sh ./target/release/delonix
- Isola-se por omissão: define
DELONIX_ROOTeDELONIX_NET_RUNTIME_DIRpara directórios próprios (a menos que exportes as duas primeiro) e desmonta a infra que arrancou.E2E_SHARED_STATE=1corre contra o estado real da máquina — só para diagnosticar um host. - O código de saída é diferente de zero quando uma verificação falha, ou quando uma verificação
marcada como defeito conhecido (
XFAIL) passa inesperadamente. Os SKIPs não fazem falhar a corrida mas são listados num bloco próprio: uma verificação saltada não provou nada. - Precisa de acesso à rede para fazer pull de imagens; as secções cujas pré-condições faltem saltam-se com a razão.
- Uma corrida verde significa que o
--helpde todas as folhas foi verificado e que algumas folhas foram executadas. Lê o cabeçalho do script para saber o que é executado e o que não é.
Arnês de caos (scripts/chaos.sh)#
O arnês de caos parte de propósito um motor a correr — mata o holder, enche o disco, faz attaches concorrentes, applies parciais — e reporta se degradou da forma que promete.
scripts/chaos.sh # every scenario, ./target/debug/delonix
scripts/chaos.sh holder_kill oom # selected scenarios
scripts/chaos.sh --keep scale # leave the sandbox up for a post-mortem
scripts/chaos.sh --clean # tear the kept sandbox down
- Redirecciona sempre as duas raízes para o seu sandbox (
DELONIX_CHAOS_DIR, por omissão/tmp/dlx-chaos) e nunca toca nos containers, redes ou registos do motor real. Os directórios de imagens (images,layers,blobs) são symlinks para o teu store real, para evitar downloads: na prática o arnês só os lê, mas um cenário que escrevesse uma imagem escreveria no store real. - Recusa-se a correr numa máquina ocupada (carga acima de um limiar, partilhado com o
scripts/bench.shatravés doscripts/bancada.sh): sob carga, os cenários falham por razões que pertencem à bancada, não ao produto.--max-load Nmuda o limiar;--forcecorre na mesma e marca o veredicto como não publicável. - O código de saída só é 0 quando nenhum cenário falha. Os SKIPs são listados à parte.
- Alguns cenários precisam de recursos externos e saltam-se sem eles (por exemplo o
truenas_destroyprecisa deDELONIX_CHAOS_TRUENAS_URL/_USER/_PASS).
Os directórios descartáveis debaixo de /tmp servem para estes sandboxes efémeros. Os teus
worktrees não — ver Fluxo de contribuição.
Seguinte: Estrutura do projecto — o mapa do repositório: o que é cada directório, quem o muda, e o que é gerado.