|
| 1 | +# Components Module |
| 2 | + |
| 3 | +## Design Overview |
| 4 | + |
| 5 | +Action-based plugin architecture for AKS node provisioning. Each component models a discrete node operation (download binaries, configure services, join cluster) as a **protobuf Action** with `Metadata`, `Spec` (desired state), and `Status` (result). |
| 6 | + |
| 7 | +### Architecture |
| 8 | + |
| 9 | +``` |
| 10 | +Caller → Hub (gRPC server) → component handler |
| 11 | +``` |
| 12 | + |
| 13 | +- **Hub** (`services/actions/hub.go`): Central dispatcher mapping protobuf type URLs to `Server` implementations. |
| 14 | +- **Registration**: Each versioned package registers via `init()` in `exports.go`; top-level `exports.go` triggers all registrations via blank imports. |
| 15 | +- **In-memory gRPC** (`services/inmem/`): Same-process action invocation without network overhead. |
| 16 | + |
| 17 | +### Two-Layer Package Structure |
| 18 | + |
| 19 | +- **Parent package** (e.g. `cni/`): Protobuf definitions, defaulting, validation, redaction — the data model layer. |
| 20 | +- **Versioned sub-package** (e.g. `cni/v20260301/`): Concrete implementation of actions. |
| 21 | + |
| 22 | +Components are independent of each other. Shared utilities come from `pkg/`. |
| 23 | + |
| 24 | +## Key Principles |
| 25 | + |
| 26 | +1. **Idempotency** — Every action is safe to re-run. Compare current state with desired state; only write/restart when something changed. |
| 27 | +2. **Declarative Spec/Status** — Actions declare desired state in `Spec` and report actual state in `Status`. |
| 28 | +3. **Default → Validate pipeline** — Use `api.DefaultAndValidate[M]()` for consistent processing. |
| 29 | +4. **Security** — Sensitive fields (tokens, secrets) must be redacted via `Redact()` before leaving the Hub. |
| 30 | +5. **Separation of concerns** — Data models (proto + defaulting + redaction) in parent package; logic in versioned sub-package. |
| 31 | + |
| 32 | +## Coding Conventions |
| 33 | + |
| 34 | +- **Go** with `gofmt`/`goimports`. Lint via `golangci-lint` (errcheck, gosec, govet, staticcheck, unused). Use `make fmt`, `make lint`, and `make check` to run these. |
| 35 | +- **Protobuf edition 2024**, opaque API with builder pattern. Run `make generate` to regenerate `.pb.go` files from `.proto` definitions. |
| 36 | +- **Naming**: packages lowercase (`cni`, `kubelet`); files snake_case (`kubelet_config.go`); unexported action types camelCase (`downloadCNIBinariesAction`); factory functions `new*Action()`. |
| 37 | +- **Compile-time interface checks**: `var _ actions.Server = (*myAction)(nil)`. |
| 38 | +- **Errors**: Use gRPC status codes (`status.Errorf(codes.Internal, ...)`). Wrap with `fmt.Errorf("context: %w", err)`. |
| 39 | +- **Embedded assets**: `//go:embed assets/*` with `text/template` for systemd units and config files. |
| 40 | +- **Comments**: Explain *why*, not just *what*. Include upstream reference URLs where relevant. |
| 41 | + |
| 42 | +## Adding a New Component |
| 43 | + |
| 44 | +1. Create `components/<name>/` with `action.proto`, `defaulting.go`, `redact.go`. |
| 45 | +2. Create `components/<name>/v<component version>/` with `exports.go` (init registration) and action implementation files. |
| 46 | +3. Add blank import in `components/exports.go`. |
| 47 | +4. Implement `actions.Server` interface; assert at compile time. |
| 48 | +5. Place templates in `assets/` subdirectory; embed with `//go:embed`. |
| 49 | + |
| 50 | +## Testing |
| 51 | + |
| 52 | +- Table-driven tests, parallel execution (`t.Parallel()`), `t.TempDir()` for filesystem isolation. |
| 53 | +- Test idempotency explicitly. |
| 54 | +- Action structs accept interfaces for dependencies to enable test doubles. |
| 55 | +- Run: `make test`, `make test-coverage`, `make test-race`. |
0 commit comments