Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/workflows/release-datumctl-inventory.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Release datumctl-inventory plugin

# Builds the datumctl-inventory CLI plugin and appends its archives to the
# GitHub release that triggered this run — the same release/tag the operator
# uses. Runs in parallel with publish.yaml (which builds the operator image)
# on the same `release: published` event.

on:
release:
types: ['published']

permissions:
contents: write

jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Install Syft CLI
run: curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sudo sh -s -- -b /usr/local/bin

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: "~> v2"
args: release --clean -f .goreleaser.datumctl-inventory.yaml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ kind

# compiled manager binary from `go build ./cmd/inventory`
/inventory

# datumctl-inventory plugin build artifact
/datumctl-inventory
cmd/datumctl-inventory/datumctl-inventory
62 changes: 62 additions & 0 deletions .goreleaser.datumctl-inventory.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# GoReleaser config for the datumctl-inventory plugin.
#
# This repo's primary artifact is the inventory operator container image
# (see .github/workflows/publish.yaml). This config builds ONLY the
# datumctl-inventory CLI plugin and appends its archives to the SAME GitHub
# release as the operator — the plugin shares the operator's tag/version.
#
# Triggered by release-datumctl-inventory.yaml on `release: published`.
#
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
version: 2

project_name: datumctl-inventory

before:
hooks:
- go mod download

builds:
- id: datumctl-inventory
main: ./cmd/datumctl-inventory
binary: datumctl-inventory
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
ldflags:
- "-X main.version=v{{.Version}}"

archives:
- id: datumctl-inventory
# name template makes OS/Arch match `uname` output, matching the convention
# datumctl's plugin installer expects (datumctl-inventory_Darwin_arm64, ...).
name_template: >-
{{ .ProjectName }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else }}{{ .Arch }}{{ end }}
formats: [tar.gz]
format_overrides:
- goos: windows
formats: [zip]

checksum:
name_template: checksums.txt

sboms:
- artifacts: archive

# Append plugin archives to the operator's existing GitHub release for this
# tag rather than creating a separate release.
release:
mode: append

changelog:
disable: true
63 changes: 63 additions & 0 deletions cmd/datumctl-inventory/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# datumctl-inventory

A [datumctl](https://github.com/datum-cloud/datumctl) plugin that provides a
read view over the Datum Cloud physical inventory served by this operator
(`inventory.miloapis.com/v1alpha1`). Once installed it is invoked as
`datumctl inventory ...`.

## Commands

| Command | Description |
|---|---|
| `datumctl inventory providers` | List providers |
| `datumctl inventory regions` | List regions |
| `datumctl inventory sites [--region R] [--provider P]` | List sites |
| `datumctl inventory clusters [--region R] [--site S]` | List clusters |
| `datumctl inventory nodes [--region R] [--site S] [--cluster C]` | List nodes |
| `datumctl inventory tree [--region R]` | region → site → node hierarchy |
| `datumctl inventory summary` | Fleet-wide counts |

All subcommands accept `-o table|json|yaml` (default `table`).

`--region`, `--site`, and `--cluster` filter server-side using the
`topology.inventory.miloapis.com/*` labels the operator propagates onto
inventory objects. `--provider` filters on the site's `providerRef`.

Inventory objects are cluster-scoped on the Datum Cloud platform root, so the
plugin talks to the platform API directly and takes no organization or project
scope.

## How it works

datumctl injects context via environment variables and execs the plugin. The
plugin reads `DATUM_API_HOST`, fetches a short-lived token through the
credentials helper (`plugin.Token()`), and builds a controller-runtime client
against the platform root using this repo's own typed API
(`go.miloapis.com/inventory/api/v1alpha1`). See the
[datumctl plugin docs](https://github.com/datum-cloud/datumctl/blob/main/docs/developer/plugins.md).

## Build

```sh
go build -o datumctl-inventory ./cmd/datumctl-inventory
```

The version reported in `--plugin-manifest` is set via
`-ldflags "-X main.version=<version>"` at release time.

## Releases

The plugin **shares the operator's version and tag**. When an `inventory`
release is published, `.github/workflows/release-datumctl-inventory.yaml` runs
goreleaser and *appends* `datumctl-inventory_{OS}_{Arch}` archives plus
`checksums.txt` to that same GitHub release (alongside the operator image
published by `publish.yaml`). So plugin `vX.Y.Z` == operator `vX.Y.Z`.

## Local use

Build the binary onto your `PATH` named `datumctl-inventory`, then:

```sh
datumctl plugin trust inventory # unmanaged plugins must be trusted once
datumctl inventory summary
```
45 changes: 45 additions & 0 deletions cmd/datumctl-inventory/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: AGPL-3.0-only

package main

import (
"fmt"
"strings"

"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"

"go.datum.net/datumctl/plugin"
inventoryv1alpha1 "go.miloapis.com/inventory/api/v1alpha1"
)

// newClient builds a controller-runtime client against the Datum Cloud platform
// root, where inventory objects live. It reads the API host from the context
// datumctl injects and fetches a fresh token via the credentials helper.
func newClient() (client.Client, error) {
ctx := plugin.Context()
if ctx.APIHost == "" {
return nil, fmt.Errorf("DATUM_API_HOST is not set; run this via 'datumctl inventory ...' (not the bare binary)")
}
token, err := plugin.Token()
if err != nil {
return nil, fmt.Errorf("get credentials: %w", err)
}

host := ctx.APIHost
if !strings.Contains(host, "://") {
host = "https://" + host
}

scheme := runtime.NewScheme()
if err := inventoryv1alpha1.AddToScheme(scheme); err != nil {
return nil, fmt.Errorf("build scheme: %w", err)
}

c, err := client.New(&rest.Config{Host: host, BearerToken: token}, client.Options{Scheme: scheme})
if err != nil {
return nil, fmt.Errorf("build API client: %w", err)
}
return c, nil
}
Loading