https://github.com/stuttgart-things/machinery
watches any crossplane managed resource with configurable status field extraction
https://github.com/stuttgart-things/machinery
crossplane golang mgmt ui
Last synced: about 2 months ago
JSON representation
watches any crossplane managed resource with configurable status field extraction
- Host: GitHub
- URL: https://github.com/stuttgart-things/machinery
- Owner: stuttgart-things
- License: apache-2.0
- Created: 2025-02-18T12:24:24.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2026-05-31T10:28:58.000Z (about 2 months ago)
- Last Synced: 2026-05-31T12:13:52.165Z (about 2 months ago)
- Topics: crossplane, golang, mgmt, ui
- Language: Go
- Homepage:
- Size: 225 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# stuttgart-things/machinery
Kubernetes resource dashboard and gRPC service for monitoring [Crossplane](https://www.crossplane.io/)-managed custom resources. Watches any CR with configurable status/info field extraction and exposes results via gRPC API and an HTMX web frontend.
## Architecture
```
Browser (HTMX) ──> HTTP :8080 ──┐
├──> Machinery Server ──> Kubernetes API ──> CRDs
Client (gRPC) ──> gRPC :50051 ─┘ (dynamic client)
```
**Endpoints:**
| Protocol | Port | Paths |
|----------|------|-------|
| gRPC | 50051 | `ResourceService.GetResources`, `.GetResourceDetail`, `.WatchResources` |
| HTTP | 8080 | `/` (dashboard), `/resources` (table), `/detail` (detail view), `/health` |
## Web Frontend
HTMX-based dashboard at `http://localhost:8080`:
- Auto-refreshing resource table (5s interval, stale-response guarded)
- Multi-select filters by resource kind and namespace
- Clickable rows for detail view with info fields
- Ready/Not Ready status badges
- Build info footer (version, commit, date)
- Dark theme
## gRPC API
```protobuf
service ResourceService {
rpc GetResources(ResourceRequest) returns (ResourceListResponse);
rpc GetResourceDetail(ResourceDetailRequest) returns (ResourceStatus);
rpc WatchResources(ResourceRequest) returns (stream ResourceEvent);
}
message ResourceRequest {
int32 count = 1; // -1 for all, max 1000
string kind = 2; // e.g. "VsphereVM", comma-separated, or "*" for all
}
message ResourceDetailRequest {
string kind = 1;
string name = 2;
string namespace = 3;
}
message ResourceStatus {
string name = 1;
string kind = 2;
bool ready = 3;
string status_message = 4;
string connection_details = 5;
string namespace = 6;
map info_fields = 7;
}
enum EventType { ADDED = 0; MODIFIED = 1; DELETED = 2; }
message ResourceEvent {
EventType type = 1; // ADDED on subscribe (cache replay), then live
ResourceStatus resource = 2;
}
```
`GetResources` / `GetResourceDetail` answer from a shared informer cache — one watch per configured kind, no per-request API-server calls. `WatchResources` is a server stream: the current state replays as `ADDED` events on subscribe, then live `ADDED` / `MODIFIED` / `DELETED` deltas follow.
## CLI client
[`machinery-client`](cmd/machinery-client/README.md) is the gRPC client for the service. Pre-built binaries for linux/darwin/windows (amd64/arm64) are attached to every [release](https://github.com/stuttgart-things/machinery/releases); or build from source with `go build -o machinery-client ./cmd/machinery-client`.
```bash
machinery-client list --kind='*' # GetResources
machinery-client get --kind=VsphereVM --name=demo-vm # GetResourceDetail
machinery-client watch --kind='*' # WatchResources (live stream)
machinery-client health # gRPC health probe
machinery-client version
```
Connection flags — `--server`, `--insecure`, `--ca-cert`, `--tls-skip-verify`, `--token`, `--token-file`, `--timeout`, `--json` — and their `MACHINERY_*` env-var equivalents are accepted on every subcommand. See [`cmd/machinery-client/README.md`](cmd/machinery-client/README.md) for the full reference, TLS/auth setup, and the `grpcurl` no-code path.
## Configuration
Resource types are configurable via JSON. Set `MACHINERY_CONFIG` to load a custom config file. Drop-in examples for common watch sets (VsphereVM, platform XRs, AnsibleRun, …) live in [`examples/configs/`](examples/configs/).
### Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| `KUBECONFIG` | Path to kubeconfig file | in-cluster config |
| `MACHINERY_CONFIG` | Path to JSON config file | built-in defaults |
| `MACHINERY_AUTH_TOKEN` | Bearer token for the gRPC `auth` gate (see below); also picked up by `client/client.go` | unset |
### gRPC auth (opt-in)
The gRPC `ResourceService` runs anonymous by default — appropriate for in-cluster usage where access is gated at the network/Gateway layer. To require a bearer token on every network-side RPC, set:
```json
{
"auth": {
"enabled": true,
"tokenFile": "/etc/machinery/auth-token"
}
}
```
Token resolution order: `auth.token` (inline) → `auth.tokenFile` → `$auth.tokenEnvVar` → `$MACHINERY_AUTH_TOKEN`. Startup fails fast if `enabled: true` but no token resolves. `/grpc.health.v1.Health/*` stays anonymous so liveness/readiness probes keep working. In-process calls from the HTMX frontend (`web.go`) bypass the interceptor by construction. Pair this with the GRPCRoute (see [`kcl/README.md`](kcl/README.md)) when exposing the service beyond the pod network.
Caller side:
```bash
grpcurl -H 'authorization: Bearer ' \
-authority machinery-grpc.example.com \
machinery-grpc.example.com:443 resourceservice.ResourceService/GetResources
```
or via `client/client.go`: `MACHINERY_AUTH_TOKEN= go run client/client.go`.
### Config File
Each resource kind supports:
- **`group`/`version`/`resource`** — GVR for the Kubernetes custom resource
- **`connectionField`** — dot-separated path to extract a primary connection value (e.g., `status.share.ip`)
- **`statusFields`** — dot-separated paths displayed as status indicators
- **`infoFields`** — labeled fields for the detail view, each with a `label` and `path`
Field extraction supports string, bool, and int64 scalars, plus slices: `[]string` joins comma-separated, `[]map` collapses to `namespace/name` pairs (useful for Gateway API's `spec.hostnames` and `spec.parentRefs`). For Gateway API kinds, readiness falls back from `status.conditions` to `status.parents[*].conditions[*]` automatically.
See [`examples/configs/`](examples/configs/) for drop-in JSON files, including a Gateway API example.
```json
{
"port": 50051,
"httpPort": 8080,
"resources": {
"VsphereVM": {
"group": "resources.stuttgart-things.com",
"version": "v1alpha1",
"resource": "vspherevms",
"connectionField": "status.share.ip",
"statusFields": ["status.share.ip"],
"infoFields": [
{"label": "Datacenter", "path": "spec.vm.datacenter"},
{"label": "Template", "path": "spec.vm.template"},
{"label": "Network", "path": "spec.vm.network"}
]
}
}
}
```
## Deployment
### KCL Manifests
Machinery uses [KCL](https://www.kcl-lang.io/) for Kubernetes manifests (no Helm). The KCL manifests are also the source for OCI kustomize artifacts pushed by the CI release workflow to `ghcr.io/stuttgart-things/machinery-kustomize`.
```bash
# Render manifests
kcl run kcl/main.k -Y tests/kcl-deploy-profile.yaml
# Apply directly
kcl run kcl/main.k -Y tests/kcl-deploy-profile.yaml | kubectl apply -f -
```
Features: gRPC/HTTP health probes, hardened security context (non-root, read-only rootfs, drop all capabilities), pod anti-affinity, optional kubeconfig secret mount, Gateway API HTTPRoute and GRPCRoute (opt-in, see [`kcl/README.md`](kcl/README.md) for the gRPC-via-gateway profile).
See [`kcl/README.md`](kcl/README.md) for all configuration options and manifest structure.
### Flux CD
Machinery can be deployed via [Flux CD](https://fluxcd.io/) using OCI-based Kustomizations.
| Resource | Description |
|----------|-------------|
| [Flux app manifests](https://github.com/stuttgart-things/flux/tree/main/apps/machinery) | Upstream Flux Kustomization, OCIRepository, HTTPRoute, and Namespace |
| [Cluster config example](https://github.com/stuttgart-things/stuttgart-things/blob/main/clusters/labul/vsphere/cd-mgmt-1/apps/machinery.yaml) | Per-cluster Flux Kustomization with `postBuild` variable substitution and config injection patches |
The upstream app manifests define the base deployment, while per-cluster configs customize version, hostname, gateway, and resource configuration via `postBuild.substitute` and strategic merge patches.
### PR preview environments
Labelling a pull request with `preview` triggers `push-kustomize-pr.yaml` to publish `pr--`-tagged image and kustomize artifacts. An Argo `ApplicationSet` ([`stuttgart-things/argocd@platforms/machinery-pr-preview`](https://github.com/stuttgart-things/argocd/tree/main/platforms/machinery-pr-preview)) then deploys them onto opt-in clusters and posts the preview URL back on the PR. Closing the PR tears the environment down and `cleanup-pr-artifacts.yaml` removes the OCI tags.
### Container Image
Built with [ko](https://ko.build/) on `cgr.dev/chainguard/static:latest` (distroless). Version, commit, and date are injected via ldflags at build time.
```
ghcr.io/stuttgart-things/machinery:
```
## Development
### Prerequisites
- Go 1.25+
- [Task](https://taskfile.dev/) (optional, for task runner)
- [KCL](https://www.kcl-lang.io/) (for rendering deployment manifests)
- [protoc](https://grpc.io/docs/protoc-installation/) + Go plugins (for proto generation)
### Run Locally
```bash
# Server
export KUBECONFIG=~/.kube/config
go run main.go
# Client — the machinery-client CLI (see "CLI client" above)
go run ./cmd/machinery-client list --kind='*'
go run ./cmd/machinery-client watch --kind='*'
```
`client/client.go` is a separate single-file smoke-test client driven by the
environment variables below; `task client` runs it.
### `client/client.go` environment variables
| Variable | Description | Default |
|----------|-------------|---------|
| `CLUSTERBOOK_SERVER` | Server address (host:port) | required |
| `SECURE_CONNECTION` | Enable TLS (`true`/`false`) | `false` |
| `TLS_CA_CERT` | Path to CA certificate | system CA pool |
| `TLS_SKIP_VERIFY` | Skip cert verification (dev only) | `false` |
### Tests
```bash
go test -v -race ./...
```
### Proto Generation
```bash
task proto
```
### Task Runner
```bash
task --list # list available tasks
task server # run server
task client # run client
```
## Links
| Resource | URL |
|----------|-----|
| Documentation (GitHub Pages) | |
| Container Image | `ghcr.io/stuttgart-things/machinery` |
| OCI Kustomize Artifacts | `ghcr.io/stuttgart-things/machinery-kustomize` |
| Changelog | [CHANGELOG.md](CHANGELOG.md) |
## License
Apache-2.0