Skip to content
Open
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
106 changes: 69 additions & 37 deletions client/pkg/client/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ import (
func Example() {
// This example shows how to use Omni client to access resources.

// Setup versions information. You can embed that into `go build` too.
// Setup version information. You can embed this into the binary via `go build -ldflags`.
version.Name = "omni"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't we generate version via abbrev_tag?

version.SHA = "build SHA"
version.Tag = "v0.9.1"

// For this example we will use Omni service account.
// You can create your service account in advance:
// For this example we will use an Omni service account.
// You can create one in advance:
//
// omnictl serviceaccount create example.account
// Created service account "example.account" with public key ID "<REDACTED>"
Expand All @@ -36,29 +36,29 @@ func Example() {
// OMNI_ENDPOINT=https://<account>.omni.siderolabs.io:443
// OMNI_SERVICE_ACCOUNT_KEY=base64encodedkey
//
// Note: Store the service account key securely, it will not be displayed again
// Note: Store the service account key securely, it will not be displayed again.

ctx := context.Background()

// Creating a new client.
client, err := client.New("https://<account>.omni.siderolabs.io:443", client.WithServiceAccount(
// Create a new client.
c, err := client.New("https://<account>.omni.siderolabs.io:443", client.WithServiceAccount(
"base64encodedkey", // From the generated service account.
))
Comment on lines +43 to 46
if err != nil {
log.Panicf("failed to create omni client %s", err)
}

// Omni service is using COSI https://github.com/cosi-project/runtime/.
// The same client is used to get resources in Talos.
st := client.Omni().State()

defer func() {
if e := client.Close(); e != nil {
if e := c.Close(); e != nil {
log.Printf("failed to close client %s", e)
}
Comment on lines 51 to 54
}()

// Getting the resources from the Omni state.
// Omni uses COSI (https://github.com/cosi-project/runtime) for resource management,
// the same runtime used in Talos.
st := c.Omni().State()

// List all machine statuses.
machines, err := safe.StateList[*omni.MachineStatus](ctx, st, omni.NewMachineStatus("").Metadata())
Copy link
Copy Markdown
Member

@Unix4ever Unix4ever May 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we touch this code we can update it with newer versions of the method:

Suggested change
machines, err := safe.StateList[*omni.MachineStatus](ctx, st, omni.NewMachineStatus("").Metadata())
machines, err := safe.ReaderListAll[*omni.MachineStatus](ctx, st)

if err != nil {
log.Panicf("failed to get machines %s", err)
Expand All @@ -72,34 +72,25 @@ func Example() {
for item := range machines.All() {
log.Printf("machine %s, connected: %t", item.Metadata(), item.TypedSpec().Value.GetConnected())

// Check cluster assignment for a machine.
// Find a machine which is allocated into a cluster for the later use.
if c, ok := item.Metadata().Labels().Get(omni.LabelCluster); ok && machine == nil {
cluster = c
// Find a machine that is allocated to a cluster, for use in the Talos API example below.
if clusterName, ok := item.Metadata().Labels().Get(omni.LabelCluster); ok && machine == nil {
cluster = clusterName
machine = item
}
}

// Creating an empty cluster via template.
// Alternative is to use template.Load to load a cluster template.
template := template.WithCluster("example.cluster")

if _, err = template.Sync(ctx, st); err != nil {
log.Panicf("failed to sync cluster %s", err)
}
// Create an empty cluster via a template.
// Use template.Load to load a cluster template from YAML instead.
tmpl := template.WithCluster("example.cluster")

if _, err = template.Sync(ctx, st); err != nil {
if _, err = tmpl.Sync(ctx, st); err != nil {
log.Panicf("failed to sync cluster %s", err)
}

log.Printf("sync cluster")

// Delete cluster.
if _, err = template.Delete(ctx, st); err != nil {
log.Panicf("failed to delete the cluster %s", err)
}
log.Printf("synced cluster")

if _, err = template.Delete(ctx, st); err != nil {
// Delete the cluster.
if _, err = tmpl.Delete(ctx, st); err != nil {
log.Panicf("failed to delete the cluster %s", err)
}

Expand All @@ -112,12 +103,12 @@ func Example() {
return
}

// Using Talos through Omni.
// Use cluster and machine which we previously found.
cpuInfo, err := client.Talos().WithCluster(
// Use the Talos API through Omni.
// You can use machine UUID as Omni will properly resolve it into machine IP.
cpuInfo, err := c.Talos().WithCluster(
cluster,
).WithNodes(
machine.Metadata().ID(), // You can use machine UUID as Omni will properly resolve it into machine IP.
machine.Metadata().ID(),
).CPUInfo(ctx, &emptypb.Empty{})
if err != nil {
log.Panicf("failed to read machine CPU info %s", err)
Expand All @@ -133,9 +124,50 @@ func Example() {
}
}

// Talking to Omni specific APIs: getting talosconfig.
_, err = client.Management().Talosconfig(ctx)
// Get the talosconfig for the cluster.
_, err = c.Management().Talosconfig(ctx)
if err != nil {
log.Panicf("failed to get talosconfig %s", err)
}

// Resource management via the COSI state API.
// The following example uses MachineClass as a representative user-managed resource to demonstrate
// create, update, and destroy operations. The same patterns apply to any other user-managed resource type.
//
// MachineClass selects machines using label selector expressions.
// Each entry in MatchLabels is a comma-separated set of conditions (AND within one entry, OR across entries).
machineClass := omni.NewMachineClass("test")
machineClass.Metadata().Labels().Set("my-label", "my-value")

machineClass.TypedSpec().Value.MatchLabels = []string{
"omni.sidero.dev/arch = amd64, omni.sidero.dev/cpus > 2", // amd64 machines with more than 2 CPUs
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

if err = st.Create(ctx, machineClass); err != nil {
log.Panicf("failed to create machine class %s", err)
}
Comment on lines +146 to +148

// Update the machine class: add an OR condition to also match arm64 machines.
updated, err := safe.StateUpdateWithConflicts(ctx, st, machineClass.Metadata(), func(res *omni.MachineClass) error {
res.TypedSpec().Value.MatchLabels = append(res.TypedSpec().Value.MatchLabels, "omni.sidero.dev/arch = arm64")

return nil
})
if err != nil {
log.Panicf("failed to update machine class %s", err)
}
Comment on lines +156 to +158

log.Printf("updated machine class labels: %v", updated.TypedSpec().Value.MatchLabels)

// For upsert (create-or-update) semantics, safe.StateModify or safe.StateModifyWithResult can be used instead.

// Destroy the machine class.
//
// The correct deletion sequence for COSI resources is: call Teardown, wait until all finalizers are
// cleared (controllers holding them will release upon seeing the teardown phase), then call Destroy.
// Calling only Teardown is not sufficient, and calling Destroy directly without Teardown first will fail
// if any finalizers are present. TeardownAndDestroy handles this full sequence.
if err = st.TeardownAndDestroy(ctx, machineClass.Metadata()); err != nil {
log.Panicf("failed to destroy machine class %s", err)
}
Comment on lines +170 to +172
}
Loading