|
| 1 | +# host-management-openstack |
| 2 | + |
| 3 | +Kubernetes controller that manages bare metal host power state via OpenStack Ironic. Watches HostLease custom resources (defined in bare-metal-fulfillment-operator) and reconciles desired power state (`spec.poweredOn`) through Ironic APIs. Optionally integrates with AAP for provisioning workflows via osac-operator's shared provisioning lifecycle. |
| 4 | + |
| 5 | +## Critical Rules |
| 6 | + |
| 7 | +- **No CRDs in this repo** — HostLease is owned by bare-metal-fulfillment-operator; install it before deploying |
| 8 | +- **Always `make manifests`** after modifying RBAC markers in controller code |
| 9 | +- **Never edit** `config/rbac/role.yaml` — it is generated by controller-gen |
| 10 | +- **Always `go mod tidy`** before committing |
| 11 | +- Run `make lint test` before committing |
| 12 | + |
| 13 | +## Dev Environment |
| 14 | + |
| 15 | +**Language**: Go (see `go.mod`) | **Framework**: controller-runtime (Kubebuilder v4) | **Build tool**: Make | **Container tool**: Podman (default) | **Test framework**: Ginkgo v2 + Gomega | **Linter**: golangci-lint (see `Makefile`) |
| 16 | + |
| 17 | +```bash |
| 18 | +make build # Build manager binary |
| 19 | +make test # Unit tests (uses envtest, excludes e2e) |
| 20 | +make test-e2e # E2E tests (auto-creates/tears down Kind cluster host-management-openstack-test-e2e) |
| 21 | +make lint # golangci-lint |
| 22 | +make lint-fix # golangci-lint run --fix |
| 23 | +make fmt && make vet # Format and vet |
| 24 | + |
| 25 | +make manifests # Generate RBAC, CRD, and webhook manifests via controller-gen (only RBAC output today — no local CRDs or webhooks) |
| 26 | +make generate # Generate DeepCopy methods via controller-gen |
| 27 | + |
| 28 | +make run # Run controller locally (requires HOSTLEASE_NAMESPACE env var) |
| 29 | +make deploy IMG=<registry>/host-management-openstack:tag |
| 30 | +make undeploy # Remove operator from cluster |
| 31 | + |
| 32 | +make image-build IMG=<registry>/host-management-openstack:tag # Build with Podman |
| 33 | +make image-push IMG=<registry>/host-management-openstack:tag |
| 34 | +make docker-buildx PLATFORMS=linux/amd64,linux/arm64 # Multi-arch build |
| 35 | +make build-installer IMG=... # Generate dist/install.yaml (consolidated manifest) |
| 36 | + |
| 37 | +pre-commit run --all-files # Run all pre-commit hooks |
| 38 | +``` |
| 39 | + |
| 40 | +## Repository Structure |
| 41 | + |
| 42 | +```text |
| 43 | +host-management-openstack/ |
| 44 | +├── cmd/ |
| 45 | +│ ├── main.go # Operator entry point (manager setup, AAP config, namespace filtering) |
| 46 | +│ └── main_test.go # Entry point tests |
| 47 | +├── internal/ |
| 48 | +│ ├── controller/ # HostLease reconciliation (power sync, provisioning lifecycle, finalizers) |
| 49 | +│ └── management/ # Power management abstraction (pluggable backend interface, OpenStack impl) |
| 50 | +├── config/ |
| 51 | +│ ├── default/ # Kustomize default overlay |
| 52 | +│ ├── manager/ # Manager deployment manifest |
| 53 | +│ ├── rbac/ # Generated RBAC rules |
| 54 | +│ ├── prometheus/ # Prometheus monitoring config (commented out by default) |
| 55 | +│ ├── network-policy/ # Network policy definitions (commented out by default) |
| 56 | +│ └── samples/ # Example HostLease CR |
| 57 | +├── test/ |
| 58 | +│ ├── e2e/ # End-to-end tests (Kind cluster) |
| 59 | +│ └── utils/ # Test utilities |
| 60 | +├── Makefile # Build, test, lint, deploy targets |
| 61 | +├── go.mod # Go dependencies (see go.mod) |
| 62 | +└── .golangci.yml # Linter configuration |
| 63 | +``` |
| 64 | + |
| 65 | +## Resources Managed |
| 66 | + |
| 67 | +This operator manages no CRDs. It watches **HostLease** CRs (from bare-metal-fulfillment-operator) filtered by `spec.hostClass == "openstack"` and reconciles their power state via Ironic using `spec.externalHostID` as the Ironic node UUID. |
| 68 | + |
| 69 | +## Architecture |
| 70 | + |
| 71 | +```text |
| 72 | +HostLease CR (spec.poweredOn, spec.externalHostID) |
| 73 | + ↓ (reconcile, filtered by hostClass == "openstack") |
| 74 | +HostLease Controller (named "openstack-host") |
| 75 | + ├── (power sync via) Management Client → OpenStack Ironic |
| 76 | + └── (optional provisioning via) osac-operator/pkg/provisioning → AAP |
| 77 | +``` |
| 78 | + |
| 79 | +### Key Subsystems |
| 80 | + |
| 81 | +| Package | Purpose | |
| 82 | +|---------|---------| |
| 83 | +| `internal/controller/` | HostLease reconciliation: finalizers, power sync (60s recheck), AAP provisioning (30s poll) | |
| 84 | +| `internal/management/` | Power management abstraction with pluggable backend; OpenStack impl via gophercloud v2 | |
| 85 | +| `cmd/main.go` | Manager setup, AAP client config (`OSAC_AAP_*` env vars), namespace filtering (`HOSTLEASE_NAMESPACE`) | |
| 86 | + |
| 87 | +## CI |
| 88 | + |
| 89 | +GitHub Actions (`.github/workflows/`): |
| 90 | +- **build-image.yaml** — runs tests, builds + pushes container image to GHCR |
| 91 | +- **pre-commit.yaml** — pre-commit hooks + golangci-lint on PRs |
| 92 | + |
| 93 | +## Code Quality |
| 94 | + |
| 95 | +- **golangci-lint** (see `Makefile`) with dupl, errcheck, ginkgolinter, goconst, gocyclo, govet, ineffassign, lll, misspell, prealloc, revive, staticcheck, unconvert, unused; formatters: gofmt, goimports (see `.golangci.yml`) |
| 96 | +- **Pre-commit hooks**: trailing-whitespace, check-merge-conflict, end-of-file-fixer, check-added-large-files, check-case-conflict, check-json, check-symlinks, detect-private-key, yamllint --strict (excludes `config/`), golangci-lint run --fix |
| 97 | +- **Tests**: Ginkgo v2 + Gomega with envtest for unit tests; Kind cluster for e2e (`test/e2e/`) |
| 98 | + |
| 99 | +## Container Security |
| 100 | + |
| 101 | +- **Base images**: Go toolset (builder, see `Containerfile`), gcr.io/distroless/static:nonroot (runtime) |
| 102 | +- **Multi-stage build**: CGO_ENABLED=0, runs as non-root user 65532 |
| 103 | +- **Default registry**: ghcr.io/osac-project/host-management-openstack:latest |
| 104 | + |
| 105 | +## Code Generation Flow |
| 106 | + |
| 107 | +1. Modify RBAC markers in controller code (`internal/controller/`) |
| 108 | +2. Run `make manifests` → runs controller-gen for RBAC, CRD, and webhook output; only RBAC is produced today (HostLease CRD is external) |
| 109 | +3. Run `make generate` → regenerates DeepCopy methods if API types change |
| 110 | + |
| 111 | +## Test Structure |
| 112 | + |
| 113 | +- `internal/controller/hostlease_controller_test.go` — reconciliation scenarios with envtest |
| 114 | +- `internal/management/openstack_internal_test.go` — OpenStack client unit tests |
| 115 | +- `internal/management/management_test.go` — backend factory tests |
| 116 | +- `cmd/main_test.go` — entry point tests |
| 117 | +- `test/e2e/` — end-to-end tests with Kind cluster (named `host-management-openstack-test-e2e`) |
| 118 | +- `test/utils/` — shared test utilities |
| 119 | +- **ENVTEST_K8S_VERSION**: Auto-detected from k8s.io/api version in go.mod |
| 120 | + |
| 121 | +## Cross-Component Integration |
| 122 | + |
| 123 | +- **bare-metal-fulfillment-operator** — owns the HostLease CRD; must be installed first |
| 124 | +- **osac-operator** — provides `pkg/provisioning` (lifecycle state machine) and `pkg/aap` (AAP client); update with `go get github.com/osac-project/osac-operator@<sha> && go mod tidy` |
| 125 | +- **fulfillment-service** — creates HostLease resources during bare metal provisioning |
0 commit comments