{"id":25649648,"url":"https://github.com/stuttgart-things/machinery","last_synced_at":"2026-06-06T09:01:08.578Z","repository":{"id":278218863,"uuid":"934797839","full_name":"stuttgart-things/machinery","owner":"stuttgart-things","description":"watches any crossplane managed resource with configurable status field extraction","archived":false,"fork":false,"pushed_at":"2026-05-31T10:28:58.000Z","size":230,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-31T12:13:52.165Z","etag":null,"topics":["crossplane","golang","mgmt","ui"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stuttgart-things.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-02-18T12:24:24.000Z","updated_at":"2026-05-31T10:29:00.000Z","dependencies_parsed_at":"2026-02-11T07:11:28.990Z","dependency_job_id":null,"html_url":"https://github.com/stuttgart-things/machinery","commit_stats":null,"previous_names":["stuttgart-things/maschinist","stuttgart-things/machinery"],"tags_count":29,"template":false,"template_full_name":null,"purl":"pkg:github/stuttgart-things/machinery","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stuttgart-things%2Fmachinery","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stuttgart-things%2Fmachinery/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stuttgart-things%2Fmachinery/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stuttgart-things%2Fmachinery/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stuttgart-things","download_url":"https://codeload.github.com/stuttgart-things/machinery/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stuttgart-things%2Fmachinery/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33975476,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-06T02:00:07.033Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["crossplane","golang","mgmt","ui"],"created_at":"2025-02-23T14:30:01.375Z","updated_at":"2026-06-06T09:01:08.559Z","avatar_url":"https://github.com/stuttgart-things.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# stuttgart-things/machinery\n\nKubernetes 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.\n\n## Architecture\n\n```\nBrowser (HTMX) ──\u003e HTTP :8080 ──┐\n                                 ├──\u003e Machinery Server ──\u003e Kubernetes API ──\u003e CRDs\nClient (gRPC)  ──\u003e gRPC :50051 ─┘        (dynamic client)\n```\n\n**Endpoints:**\n\n| Protocol | Port | Paths |\n|----------|------|-------|\n| gRPC | 50051 | `ResourceService.GetResources`, `.GetResourceDetail`, `.WatchResources` |\n| HTTP | 8080 | `/` (dashboard), `/resources` (table), `/detail` (detail view), `/health` |\n\n## Web Frontend\n\nHTMX-based dashboard at `http://localhost:8080`:\n\n- Auto-refreshing resource table (5s interval, stale-response guarded)\n- Multi-select filters by resource kind and namespace\n- Clickable rows for detail view with info fields\n- Ready/Not Ready status badges\n- Build info footer (version, commit, date)\n- Dark theme\n\n## gRPC API\n\n```protobuf\nservice ResourceService {\n  rpc GetResources(ResourceRequest) returns (ResourceListResponse);\n  rpc GetResourceDetail(ResourceDetailRequest) returns (ResourceStatus);\n  rpc WatchResources(ResourceRequest) returns (stream ResourceEvent);\n}\n\nmessage ResourceRequest {\n  int32 count = 1;    // -1 for all, max 1000\n  string kind = 2;    // e.g. \"VsphereVM\", comma-separated, or \"*\" for all\n}\n\nmessage ResourceDetailRequest {\n  string kind = 1;\n  string name = 2;\n  string namespace = 3;\n}\n\nmessage ResourceStatus {\n  string name = 1;\n  string kind = 2;\n  bool ready = 3;\n  string status_message = 4;\n  string connection_details = 5;\n  string namespace = 6;\n  map\u003cstring, string\u003e info_fields = 7;\n}\n\nenum EventType { ADDED = 0; MODIFIED = 1; DELETED = 2; }\n\nmessage ResourceEvent {\n  EventType type = 1;          // ADDED on subscribe (cache replay), then live\n  ResourceStatus resource = 2;\n}\n```\n\n`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.\n\n## CLI client\n\n[`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`.\n\n```bash\nmachinery-client list  --kind='*'                       # GetResources\nmachinery-client get   --kind=VsphereVM --name=demo-vm  # GetResourceDetail\nmachinery-client watch --kind='*'                       # WatchResources (live stream)\nmachinery-client health                                 # gRPC health probe\nmachinery-client version\n```\n\nConnection 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.\n\n## Configuration\n\nResource 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/).\n\n### Environment Variables\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `KUBECONFIG` | Path to kubeconfig file | in-cluster config |\n| `MACHINERY_CONFIG` | Path to JSON config file | built-in defaults |\n| `MACHINERY_AUTH_TOKEN` | Bearer token for the gRPC `auth` gate (see below); also picked up by `client/client.go` | unset |\n\n### gRPC auth (opt-in)\n\nThe 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:\n\n```json\n{\n  \"auth\": {\n    \"enabled\": true,\n    \"tokenFile\": \"/etc/machinery/auth-token\"\n  }\n}\n```\n\nToken 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.\n\nCaller side:\n\n```bash\ngrpcurl -H 'authorization: Bearer \u003ctoken\u003e' \\\n  -authority machinery-grpc.example.com \\\n  machinery-grpc.example.com:443 resourceservice.ResourceService/GetResources\n```\n\nor via `client/client.go`: `MACHINERY_AUTH_TOKEN=\u003ctoken\u003e go run client/client.go`.\n\n### Config File\n\nEach resource kind supports:\n\n- **`group`/`version`/`resource`** — GVR for the Kubernetes custom resource\n- **`connectionField`** — dot-separated path to extract a primary connection value (e.g., `status.share.ip`)\n- **`statusFields`** — dot-separated paths displayed as status indicators\n- **`infoFields`** — labeled fields for the detail view, each with a `label` and `path`\n\nField 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.\n\nSee [`examples/configs/`](examples/configs/) for drop-in JSON files, including a Gateway API example.\n\n```json\n{\n  \"port\": 50051,\n  \"httpPort\": 8080,\n  \"resources\": {\n    \"VsphereVM\": {\n      \"group\": \"resources.stuttgart-things.com\",\n      \"version\": \"v1alpha1\",\n      \"resource\": \"vspherevms\",\n      \"connectionField\": \"status.share.ip\",\n      \"statusFields\": [\"status.share.ip\"],\n      \"infoFields\": [\n        {\"label\": \"Datacenter\", \"path\": \"spec.vm.datacenter\"},\n        {\"label\": \"Template\", \"path\": \"spec.vm.template\"},\n        {\"label\": \"Network\", \"path\": \"spec.vm.network\"}\n      ]\n    }\n  }\n}\n```\n\n## Deployment\n\n### KCL Manifests\n\nMachinery 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`.\n\n```bash\n# Render manifests\nkcl run kcl/main.k -Y tests/kcl-deploy-profile.yaml\n\n# Apply directly\nkcl run kcl/main.k -Y tests/kcl-deploy-profile.yaml | kubectl apply -f -\n```\n\nFeatures: 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).\n\nSee [`kcl/README.md`](kcl/README.md) for all configuration options and manifest structure.\n\n### Flux CD\n\nMachinery can be deployed via [Flux CD](https://fluxcd.io/) using OCI-based Kustomizations.\n\n| Resource | Description |\n|----------|-------------|\n| [Flux app manifests](https://github.com/stuttgart-things/flux/tree/main/apps/machinery) | Upstream Flux Kustomization, OCIRepository, HTTPRoute, and Namespace |\n| [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 |\n\nThe 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.\n\n### PR preview environments\n\nLabelling a pull request with `preview` triggers `push-kustomize-pr.yaml` to publish `pr-\u003cn\u003e-\u003csha\u003e`-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.\n\n### Container Image\n\nBuilt with [ko](https://ko.build/) on `cgr.dev/chainguard/static:latest` (distroless). Version, commit, and date are injected via ldflags at build time.\n\n```\nghcr.io/stuttgart-things/machinery:\u003ctag\u003e\n```\n\n## Development\n\n### Prerequisites\n\n- Go 1.25+\n- [Task](https://taskfile.dev/) (optional, for task runner)\n- [KCL](https://www.kcl-lang.io/) (for rendering deployment manifests)\n- [protoc](https://grpc.io/docs/protoc-installation/) + Go plugins (for proto generation)\n\n### Run Locally\n\n```bash\n# Server\nexport KUBECONFIG=~/.kube/config\ngo run main.go\n\n# Client — the machinery-client CLI (see \"CLI client\" above)\ngo run ./cmd/machinery-client list  --kind='*'\ngo run ./cmd/machinery-client watch --kind='*'\n```\n\n`client/client.go` is a separate single-file smoke-test client driven by the\nenvironment variables below; `task client` runs it.\n\n### `client/client.go` environment variables\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `CLUSTERBOOK_SERVER` | Server address (host:port) | required |\n| `SECURE_CONNECTION` | Enable TLS (`true`/`false`) | `false` |\n| `TLS_CA_CERT` | Path to CA certificate | system CA pool |\n| `TLS_SKIP_VERIFY` | Skip cert verification (dev only) | `false` |\n\n### Tests\n\n```bash\ngo test -v -race ./...\n```\n\n### Proto Generation\n\n```bash\ntask proto\n```\n\n### Task Runner\n\n```bash\ntask --list   # list available tasks\ntask server   # run server\ntask client   # run client\n```\n\n## Links\n\n| Resource | URL |\n|----------|-----|\n| Documentation (GitHub Pages) | \u003chttps://stuttgart-things.github.io/machinery\u003e |\n| Container Image | `ghcr.io/stuttgart-things/machinery` |\n| OCI Kustomize Artifacts | `ghcr.io/stuttgart-things/machinery-kustomize` |\n| Changelog | [CHANGELOG.md](CHANGELOG.md) |\n\n## License\n\nApache-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstuttgart-things%2Fmachinery","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstuttgart-things%2Fmachinery","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstuttgart-things%2Fmachinery/lists"}