DDelonix RuntimeManual do contribuidor

Começar

Começa aqui

Esta página leva-te de «acabei de clonar o repositório» a «o meu primeiro pull request está em revisão», um passo de cada vez. Depois dela vais ter uma checkout que compila, um binário que consegues correr contra estado isolado, e um mapa de onde vai a tua mudança e que regras tem de respeitar. Cada passo diz o que fazer, o que deves ver, e que página explica os detalhes. Cada regra desta página tem um link para o sítio onde está escrita — se uma regra não tem link, não é uma regra.

Se uma palavra daqui for nova para ti, procura-a no glossário.

Dia 0 em 30 minutos#

O dia 0 segue a ordem do manual, mas só leva de cada parte o que precisas hoje: a ideia do motor (IaaS e cloud native), o host de que precisa (Preparar o teu ambiente, que se apoia nos primitivos em Fundações de Linux), uma compilação e uma corrida isolada (Clonar, compilar e testar, com todas as variáveis em Variáveis de ambiente), e onde as coisas estão (Estrutura do projecto). Volta às páginas completas quando um passo te mandar lá.

O que é o Delonix (5 minutos)#

O Delonix Runtime é um motor que corre containers e microVMs num nó, a par da rede e do armazenamento de que eles precisam. É:

  • declarativo — descreves recursos com os seus Kinds próprios (delonix api-resources lista-os) e o motor planeia e aplica a diferença;
  • daemonless — não é preciso nenhum serviço em segundo plano; cada comando é um processo que faz o seu trabalho e sai;
  • rootless-first — o caminho normal corre com o teu próprio utilizador sem privilégios.

Não sabe quem o usa: não existe no código nenhum conceito de plataforma, inquilino, conta ou facturação. Lê IaaS e cloud native para saber onde isto situa o motor numa cloud, e Arquitectura para como a fronteira é imposta; por agora, estas quatro frases chegam.

O que precisas (10 minutos)#

Um host Linux com cgroup v2 e user namespaces sem privilégios, a toolchain de Rust fixada no rust-toolchain.toml, e o protoc no teu PATH. A lista completa, e as armadilhas do host que parecem bugs do motor, estão em Preparar o teu ambiente. Lê pelo menos a secção Armadilhas conhecidas do host antes do passo 5 abaixo. Se «user namespace» ou «delegação de cgroup» forem palavras novas, a explicação à mão está em Fundações de Linux — não precisas dela para acabar o dia 0, mas vais precisar na primeira vez que um limite não se aplicar.

Cinco comandos que provam que o teu ambiente funciona (15 minutos)#

Corre-os a partir da raiz da tua checkout. Se algum deles não der a forma mostrada, pára e corrige-o antes de avançar — todos os passos seguintes dependem dele.

1. Clonar, com as tags.

shell
git clone https://github.com/angolardevops/delonix-runtime.git
cd delonix-runtime
git fetch --tags origin
git describe --tags --abbrev=0        # prints the newest release, e.g. vX.Y.Z

As tags importam: o gate de versão e o gate de contrato comparam o teu branch com elas (Clonar, compilar e testar).

2. Compilar a CLI.

shell
cargo build -p delonix-runtime-bin

Esperado: Finished na última linha e um binário em target/debug/delonix. Se parar com uma mensagem sobre o protoc, instala-o (Preparar o teu ambiente).

3. Correr os testes de um crate pequeno e puro.

shell
cargo test -p delonix-net-rules

O delonix-net-rules não tem dependência nenhuma (vê o seu Cargo.toml), por isso isto só prova que a tua toolchain compila e corre testes — nada sobre o host. Esperado: uma linha da forma test result: ok. N passed; 0 failed.

4. Correr o binário que acabaste de compilar.

shell
./target/debug/delonix --version
./target/debug/delonix --help

Esperado: --version imprime delonix <version> na primeira linha, uma descrição do motor numa linha na segunda, e depois uma linha da forma commit: <sha> · built: <date> · <licence>; entre releases a parte commit: diz também a que distância o build está da última tag (+N commits since vX.Y.Z). Segue-se um bloco curto get started:. --help imprime Usage: delonix [OPTIONS] <COMMAND>, uma lista Commands: e um COMMAND MAP.

Usa sempre ./target/debug/delonix, nunca um delonix encontrado no teu PATH — esse é uma release instalada e normalmente é mais antigo (Preparar o teu ambiente).

5. Correr um comando real, totalmente isolado.

Tudo o que vá além do --help lê e escreve estado do motor. Aponta as duas variáveis de estado para directórios de rascunho primeiro — meia isolação é pior que nenhuma (Clonar, compilar e testar; o que cada variável faz está em Variáveis de ambiente):

shell
export DELONIX_ROOT=$HOME/scratch/dlx/root
export DELONIX_NET_RUNTIME_DIR=/tmp/dlx-run      # keep it short: it holds unix sockets
mkdir -p "$DELONIX_ROOT" "$DELONIX_NET_RUNTIME_DIR"

./target/debug/delonix system info
./target/debug/delonix volume create hello
./target/debug/delonix volume ls
./target/debug/delonix volume inspect does-not-exist; echo "exit=$?"
./target/debug/delonix volume rm hello

Formas esperadas:

text
$ delonix system info
Delonix Engine <version>
  state root:         <your $DELONIX_ROOT>
  mode:               rootless (daemonless)
  cgroup2 delegated:  yes | no
  network infra:      down (comes up on demand)
  containers:         0 (0 running)
  events:             0

$ delonix volume ls
NAME    DRIVER   MOUNTPOINT                              SIZE
hello   local    <your $DELONIX_ROOT>/volumes/hello/_data   0 B

$ delonix volume inspect does-not-exist; echo "exit=$?"
error no such volume does-not-exist
exit=4

O que isto prova: a linha state root: é o teu directório de rascunho (por isso não estás a tocar em estado real), o motor corre rootless, e os erros levam uma classe no código de saída (4 = não existe — ver Introdução ao Rust para esta base de código). Se cgroup2 delegated: disser no, o container run recusa -m/--cpus/--cpu-weight nesta sessão (saída 69) e --cpuset/--io-weight não têm efeito; isso é uma configuração do host, explicada em Preparar o teu ambiente.

Quando acabares de experimentar com a rede mais tarde, desmonta a infra de rede isolada com as mesmas duas variáveis exportadas: ./target/debug/delonix net netns down.

A tua primeira contribuição, de ponta a ponta#

1. Escolhe alguma coisa#

  • Procura issues abertas com a etiqueta good first issue ou documentation no GitHub. Comenta na issue antes de começar, para duas pessoas não fazerem o mesmo trabalho.
  • Para tudo o que não seja trivial — um comando novo, um Kind de manifesto novo, uma mudança na configuração de namespaces ou cgroups, um backend novo — abre primeiro uma issue e acorda a abordagem (CONTRIBUTING.md, Fluxo de contribuição).
  • Vê os pull requests abertos, para não duplicares trabalho que já está em curso.

Boas primeiras áreas, porque são código puro com testes unitários e sem privilégios de host: um parser ou validador no crate da CLI, uma mensagem de erro que não diz o que fazer, uma entrada em português em falta em bins/delonix-runtime-bin/data/pt.po, ou uma página deste manual que esteja errada.

2. Abre um worktree a partir de origin/main#

Uma tarefa, um worktree, um branch — nunca edites uma checkout partilhada, nunca ponhas o worktree em /tmp (Um worktree por tarefa):

shell
git fetch --tags origin
git worktree add -b <topic>/<task> ../.worktrees/delonix-runtime/<task> origin/main
cd ../.worktrees/delonix-runtime/<task>
git log --oneline -- <path you will touch>      # what was already decided or fixed there

Ler primeiro o histórico da área faz parte do trabalho: muito deste código regista coisas que foram tentadas, medidas e mudadas (Parte da tag mais recente).

3. Descobre onde vai a mudança#

Usa a árvore de decisão em Onde vai a minha mudança? abaixo, e depois lê a secção de Os crates para esse crate. Se um caminho na tabela ainda não te diz nada, Estrutura do projecto explica cada directório de topo, e porque é que o directório de um crate é a sua camada.

4. Escreve o teste primeiro#

  • Uma função pura nova (parser, validador, construtor de argumentos, plano) leva um teste unitário no mesmo ficheiro, dentro de #[cfg(test)] mod tests. Um teste nunca toca no state root real: passa-lhe um directório temporário — ver Testes.
  • Uma correcção de bug leva um teste que falha sem a correcção. Reverte a tua correcção uma vez, corre o teste, vê-o falhar, e depois repõe a correcção. Um teste que passa de qualquer maneira não prova nada.
  • Uma mudança em namespaces, cgroups, no holder de rede ou no arranque de VMs precisa também de uma execução ao vivo com o estado isolado, porque os testes unitários não alcançam esses caminhos (Correr os testes).

5. Corre os gates locais#

Cada job de CI tem um comando local, listado em Os gates que a CI corre. No mínimo, antes de pedir revisão:

shell
cargo fmt --all --check
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo test --workspace --locked --no-fail-fast
python3 scripts/lang_ratchet.py
python3 scripts/arch_fitness.py
python3 scripts/dev_docs.py --check
python3 scripts/version_gate.py

Acrescenta os que correspondem ao que tocaste (os gates da superfície da CLI se acrescentaste um comando, o gate de contrato se tocaste em proto/, o gerador da documentação se o texto de ajuda mudou) — a tabela em Os gates que a CI corre diz quais.

6. Escreve o pull request#

Abre-o contra main e preenche todas as secções de .github/PULL_REQUEST_TEMPLATE.md. O modelo tem quatro partes, e os revisores lêem todas:

  • What does this change do, and why? — o porquê; o diff já mostra o quê.
  • How was this tested? — os gates que correste e, para código de runtime/namespace/cgroup/rede, o comando que correste ao vivo e a sua saída.
  • Checklist — build/clippy/fmt/test limpos; strings novas visíveis ao utilizador em inglês, envolvidas em po::t/po::tf, com uma entrada em português no pt.po; todos os pontos de entrada de um comando ligados; testes unitários para as funções puras novas; fronteiras de privilégio assinaladas.
  • Does this cross a privilege or namespace boundary? — mapeamento do user namespace, a netns do holder, o socket de controlo, setns/unshare, ou tratamento de caminhos conduzido por input do utilizador ou de um manifesto. Se não tiveres a certeza, di-lo.

Diz o que ficou provado e o que não foi validado, e porquê (Commits e pull requests).

7. O que os revisores verificam#

Os code owners em .github/CODEOWNERS revêem todas as mudanças. A checklist de revisão está em Convenções de código; os padrões cloud native contra os quais uma mudança é medida estão em Padrões cloud native. Lê as duas antes de abrires o PR, não depois da primeira ronda de comentários.

8. Depois do merge#

Remove o worktree e o branch — o branch sobrevive ao worktree remove:

shell
cd ../../../delonix-runtime    # from the worktree of step 2 back to the clone
git worktree remove ../.worktrees/delonix-runtime/<task>
git branch -D <topic>/<task>

Onde vai a minha mudança?#

flowchart TD
    Q{What are you changing?}
    Q -->|a boundary: new daemon, new privilege,<br/>new provider or port, layer structure,<br/>node contract, schema stability| ADR[Write an ADR first<br/>docs/adr/]
    Q -->|a CLI flag or subcommand| CLI[bins/delonix-runtime-bin/src/cmd/GROUP.rs]
    Q -->|a manifest Kind or field| KIND[delonix-stack kinds.rs<br/>+ cmd/KIND.rs + schema.rs]
    Q -->|network behaviour| NET{pure rule or dataplane?}
    NET -->|pure: CIDR, bridge name, IPAM math| NR[delonix-net-rules]
    NET -->|dataplane: holder, nftables, IPAM leases, CNI| SDN[delonix-sdn]
    Q -->|images, registry, build| OCI[delonix-oci<br/>+ cmd/build.rs, cmd/image.rs]
    Q -->|VMs| VM{local or remote?}
    VM -->|Cloud Hypervisor / libvirt / cloud-init| DVM[delonix-vm]
    VM -->|a remote management API| PROV[crates/providers/NAME<br/>implements VmBackend]
    Q -->|what the kubelet sees| CRI[delonix-cri]
    Q -->|a new crate| CRATE[LAYERS in arch_fitness.py<br/>+ crates/LAYER/ + root Cargo.toml]
    PROV --> ADR
Mudança Onde vai (confirmado na árvore) Ler Regra e a sua fonte
Flag ou subcomando novo da CLI bins/delonix-runtime-bin/src/cmd/<group>.rs (um módulo por grupo); strings através de cmd/po.rs, com o português em data/pt.po; texto do manual em cmd/manual_entries.rs; lista de folhas em scripts/cli_baseline.tsv (scripts/cli-tree.sh --update). A validação pura de uma execução pertence a crates/contexts/delonix-compute/src/preflight.rs Acrescentar ou mudar um comando da CLI, A CLI LANG-01 (scripts/lang_ratchet.py); gate da superfície da CLI (scripts/cli-tree.sh --gate, scripts/docs_cli_gate.py); ligar todos os pontos de entrada (CONTRIBUTING.md)
Kind novo, ou um campo de um Kind Os factos do Kind: FACTS em crates/contexts/delonix-stack/src/kinds.rs. O seu tipo de spec e o apply: bins/delonix-runtime-bin/src/cmd/<kind>.rs. Campos actualizáveis a quente: hot_fields em crates/contexts/delonix-stack/src/reconcile.rs. O schema: TYPED_KINDS em cmd/schema.rs, e o docs/schema/v1/delonix.json publicado (delonix manifest schema) Reconciliação declarativa, delonix-stack Abre primeiro uma issue (CONTRIBUTING.md); o schema é gerado a partir do código (ADR-0007); os testes em kinds.rs e schema.rs falham quando uma tabela fica esquecida
Comportamento de rede Regras puras sem I/O: crates/foundation/delonix-net-rules/src/lib.rs. Dataplane (holder, socket de controlo, nftables, IPAM, CNI): crates/adapters/delonix-sdn/src/ (infra.rs, ipam.rs, cni.rs). O passo de rede do container run: crates/contexts/delonix-compute/src/network.rs. CLI: cmd/network.rs, cmd/net.rs, cmd/firewall.rs Rede de containers, delonix-sdn Rootless-first e nenhuma falha silenciosa (Regras de arquitectura); assinala a fronteira de privilégio no PR (SECURITY.md)
Imagens, registo, build crates/adapters/delonix-oci/src/ (registry.rs, build.rs, cas.rs, overlay.rs); CLI em cmd/build.rs, cmd/image.rs Delonixfile e VMfile, delonix-oci Os downloads são verificados por digest (SECURITY.md, âmbito da cadeia de fornecimento)
Estado persistido: um campo de registo, um store, locks de ficheiro, segredos em repouso Tipos de registo (Container, Vm): crates/contexts/delonix-compute/src/record.rs; as partes de dados simples (Status, ContainerFw): crates/foundation/delonix-model/src/records.rs. Como são guardados e trancados (Store, JsonStore, write_atomic*, SecretStore, CredVault): crates/adapters/delonix-state/src/ (store.rs, secret.rs, cred_vault.rs) delonix-state, Estado em disco, Concorrência Os campos novos de um registo levam #[serde(default)]; o read-modify-write passa pelo update (Estado e concorrência)
Comportamento de VMs neste nó crates/adapters/delonix-vm/src/lib.rs (o trait VmBackend e o registo de backends), cloudinit.rs; CLI em cmd/vm.rs, cmd/vmimage.rs, cmd/vmfile.rs Construir microVMs, Traits como portas ADR-0008 (os backends são registáveis)
Um backend de VM ou provider de armazenamento novo, atrás de uma API remota Um crate novo em crates/providers/, a implementar uma porta; registado na raiz de composição (cmd/vmbackends.rs) Providers, Camadas Primeiro um ADR (Quando escrever um ADR); ADR-0040
CRI (aquilo com que o kubelet fala) crates/interfaces/delonix-cri/src/ (runtime_svc.rs, runtime_svc/lifecycle.rs, streaming.rs) Kubernetes, delonix-cri ADR-0038
Um crate novo Uma entrada em LAYERS em scripts/arch_fitness.py, um directório em crates/<layer>/, e o seu caminho no [workspace.dependencies] do Cargo.toml raiz — tudo no mesmo commit Camadas scripts/arch_fitness.py (directório = camada, versões só na raiz)
Uma decisão que muda uma fronteira docs/adr/NNNN-title.md, antes do código Quando escrever um ADR docs/adr/README.md; os ADRs aceites são sucedidos, nunca reescritos

Se a tua mudança não encaixa em nenhuma linha, pergunta na issue antes de escrever código (ver Quando estás preso).

Regras que não podes quebrar#

Cada regra é imposta por um gate, por uma revisão, ou pelos dois. O link é o sítio onde está escrita.

Regra Fonte
O motor não conhece nenhum consumidor. Nenhum produto, plataforma, control plane, consola ou agente que use o motor é nomeado em crates/, bins/, proto/ ou nos manifestos, comentários incluídos; nenhum inquilino, conta, plano ou facturação. «Identidade e fronteira do motor» no topo do AGENTS.md. Os consumidores nomeados são impostos por CONSUMER_NAMES em scripts/arch_fitness.py (uma lista fixa de nomes, casada por expressão regular); a proibição dos conceitos de inquilino, conta, plano e facturação não é casada por nenhum gate e é verificada em revisão
Daemonless. Nenhum processo residente por omissão; um novo precisa de um ADR com a evidência do que o systemd não conseguiu fazer. AGENTS.md (a mesma secção); Regras de arquitectura
Rootless-first. O caminho normal corre sem privilégios; o privilégio é um opt-in explícito e anunciado. Uma fronteira de privilégio nova precisa de um spike GO/NO-GO e de um ADR. AGENTS.md; Quando escrever um ADR
As dependências apontam para dentro, o directório é a camada, as versões vivem só na raiz. ADR-0040; scripts/arch_fitness.py
LANG-01: o código é em inglês. Identificadores, comentários e mensagens em inglês; português só através do pt.po. Língua; scripts/lang_ratchet.py
Alinhamento da versão. Não mudes a version do Cargo.toml raiz num PR de feature; o teu branch tem de conter a tag mais recente. Alinhamento da versão; scripts/version_gate.py
Um worktree por tarefa, fora de /tmp, ficheiros adicionados pelo nome, worktree e branch removidos no fim. Um worktree por tarefa
Nunca corras o motor, a bateria E2E ou o arnês de caos contra estado real. Exporta tanto DELONIX_ROOT como DELONIX_NET_RUNTIME_DIR; não definas E2E_SHARED_STATE=1 a não ser que estejas a diagnosticar o teu próprio host. Isolar o estado do motor, E2E, caos
As mudanças sensíveis para a segurança são assinaladas, e as vulnerabilidades são reportadas em privado, nunca numa issue ou PR público. SECURITY.md; Mudanças sensíveis para a segurança

Quando estás preso#

Procura por esta ordem — cada passo é mais barato do que o seguinte:

  1. Este manual. O README tem a lista de páginas; o glossário explica o vocabulário.
  2. AGENTS.md, organizado por área. É longo e em parte histórico (e em parte em português): usa-o para saber onde procurar, e depois confirma no código.
  3. O índice de ADRs, docs/adr/README.md — a decisão por trás de uma estrutura, e o que foi rejeitado.
  4. O histórico do ficheiro: git log --oneline -- <path> e git log -p -S '<symbol>'. As mensagens de commit aqui explicam o porquê.

Se continuares preso, pergunta no GitHub:

O modelo de relato de bug pede:

  • a saída de delonix --version;
  • a distro e a versão do kernel, rootless ou root, e se instalaste com o install.sh, descarregaste um binário, ou compilaste a partir do código-fonte;
  • o comando ou manifesto exacto que o provoca;
  • o que esperavas, e a saída completa, sem cortes do que aconteceu de facto;
  • se reproduz sempre, às vezes, ou só uma vez;
  • qualquer outra coisa que possa ser relevante.

Este manual recomenda ainda duas coisas que o modelo não pede, porque poupam uma ida e volta:

  • a saída inteira do --version do binário que correste, incluindo a linha commit: (entre releases todos os builds reportam o mesmo número de versão, e só o commit os distingue);
  • se DELONIX_ROOT/DELONIX_NET_RUNTIME_DIR estavam definidas, e o que já leste e tentaste (a página, a secção do AGENTS.md, o ADR).

Checklist de progresso#

  • [ ] Li o que é o Delonix e os quatro princípios (Arquitectura).
  • [ ] O meu host cumpre a Preparar o teu ambiente, e li as armadilhas conhecidas do host.
  • [ ] cargo build -p delonix-runtime-bin termina.
  • [ ] cargo test -p delonix-net-rules reporta test result: ok.
  • [ ] ./target/debug/delonix --help funciona, e deixei de usar o delonix do meu PATH.
  • [ ] delonix system info mostra o meu DELONIX_ROOT de rascunho como state root.
  • [ ] Escolhi uma issue e comentei nela (ou abri uma para uma mudança não trivial).
  • [ ] Trabalho no meu próprio worktree, criado a partir de origin/main.
  • [ ] Descobri onde vai a mudança e li a secção desse crate na Os crates.
  • [ ] Escrevi um teste que falha sem a minha mudança.
  • [ ] Os gates locais da Clonar, compilar e testar passam.
  • [ ] Li a Convenções de código e a Padrões cloud native, camada a camada.
  • [ ] O meu PR preenche todas as secções do modelo, incluindo o que não foi validado.
  • [ ] Depois do merge, removi o meu worktree e o meu branch.

Seguinte: IaaS e cloud native — onde o motor encaixa — o modelo mental de uma IaaS, que camada dela é este motor, e como os princípios cloud native aparecem nos seus ficheiros.