Skip to content

Commit 8c6100b

Browse files
committed
Client guide
1 parent 96fdba2 commit 8c6100b

File tree

1 file changed

+293
-0
lines changed

1 file changed

+293
-0
lines changed

docs/client-guide.md

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
# Maestro Client Guide
2+
3+
This guide describes how to use Maestro's APIs to manage resources on target consumers (clusters).
4+
5+
## Architecture Overview
6+
7+
For detailed internal architecture and data flows, see [Maestro Overview](./maestro.md).
8+
9+
```mermaid
10+
flowchart LR
11+
subgraph Clients
12+
Admin["Admin"]
13+
Source["Source<br/>(e.g. CS)"]
14+
end
15+
16+
subgraph "Maestro Server"
17+
REST["REST API"]
18+
GRPC["gRPC API"]
19+
DB[(Database)]
20+
Broker["Broker"]
21+
end
22+
23+
Admin -->|"manage consumers"| REST
24+
Admin -->|"read status"| REST
25+
REST <--> DB
26+
27+
Source -->|"create/update/delete<br/>resource bundles"| GRPC
28+
Source -->|"subscribe to<br/>status updates"| GRPC
29+
GRPC <--> DB
30+
DB --> Broker
31+
```
32+
33+
## Client Libraries
34+
35+
### REST API Client (OpenAPI-generated)
36+
37+
**Package:** `github.com/openshift-online/maestro/pkg/api/openapi`
38+
39+
```go
40+
import "github.com/openshift-online/maestro/pkg/api/openapi"
41+
42+
client := openapi.NewAPIClient(cfg)
43+
44+
// Consumers
45+
client.DefaultAPI.ApiMaestroV1ConsumersGet(ctx) // List
46+
client.DefaultAPI.ApiMaestroV1ConsumersIdGet(ctx, id) // Get
47+
client.DefaultAPI.ApiMaestroV1ConsumersPost(ctx) // Create
48+
client.DefaultAPI.ApiMaestroV1ConsumersIdPatch(ctx, id) // Update
49+
client.DefaultAPI.ApiMaestroV1ConsumersIdDelete(ctx, id)// Delete
50+
51+
// Resource Bundles (read-only + delete)
52+
client.DefaultAPI.ApiMaestroV1ResourceBundlesGet(ctx) // List
53+
client.DefaultAPI.ApiMaestroV1ResourceBundlesIdGet(ctx, id) // Get (includes status)
54+
client.DefaultAPI.ApiMaestroV1ResourceBundlesIdDelete(ctx, id)// Delete
55+
```
56+
57+
### gRPC Client (for sending resources + watching status)
58+
59+
**Package:** `github.com/openshift-online/maestro/pkg/client/cloudevents/grpcsource`
60+
61+
For a complete working example, see [examples/manifestwork](../examples/manifestwork/). For gRPC authentication setup (mTLS or token-based), see [gRPC Authentication](./maestro.md#authentication-and-authorization).
62+
63+
```go
64+
import "github.com/openshift-online/maestro/pkg/client/cloudevents/grpcsource"
65+
66+
// Returns a workv1client.WorkV1Interface (standard OCM ManifestWork client)
67+
workClient, err := grpcsource.NewMaestroGRPCSourceWorkClient(
68+
ctx,
69+
logger,
70+
apiClient, // the openapi client (used internally for watching)
71+
grpcOpts, // gRPC connection options
72+
sourceID,
73+
)
74+
75+
// Then use it like a regular ManifestWork client
76+
workClient.ManifestWorks(clusterName).Create(ctx, manifestWork, metav1.CreateOptions{})
77+
workClient.ManifestWorks(clusterName).Update(ctx, manifestWork, metav1.UpdateOptions{})
78+
workClient.ManifestWorks(clusterName).Delete(ctx, name, metav1.DeleteOptions{})
79+
workClient.ManifestWorks(clusterName).Watch(ctx, metav1.ListOptions{}) // streams status updates
80+
```
81+
82+
## Consumer Lifecycle
83+
84+
A **Consumer** represents a target cluster. You must create a consumer before sending resources to it.
85+
86+
1. **Create consumer** via REST API (provides consumer name/ID)
87+
2. **Agent connects** using that consumer name
88+
3. **Send resources** via gRPC, specifying the consumer name as the target
89+
4. **Delete consumer** when the cluster is decommissioned (resources are cleaned up)
90+
91+
```go
92+
// 1. Create consumer via REST
93+
consumer, _, _ := client.DefaultAPI.ApiMaestroV1ConsumersPost(ctx).
94+
Consumer(openapi.Consumer{Name: openapi.PtrString("my-cluster")}).
95+
Execute()
96+
97+
// 2. Send resources via gRPC to that consumer
98+
workClient.ManifestWorks(*consumer.Name).Create(ctx, manifestWork, metav1.CreateOptions{})
99+
```
100+
101+
## Key Concepts
102+
103+
### Resource Bundles and ManifestWorks
104+
105+
- Maestro only supports **ResourceBundles**, not single resources
106+
- **ManifestWork** is the Kubernetes CR representation of a ResourceBundle
107+
- A ResourceBundle can contain one or more Kubernetes manifests
108+
- FeedbackRules can be defined inside the ManifestWork to extract data from applied resources. They are calculated by the agent and reported as part of the status.
109+
110+
### ManifestWork Structure
111+
112+
For detailed create/update/delete flow diagrams, see [Resource Flows](./maestro.md#maestro-resource-flow).
113+
114+
```
115+
ManifestWork
116+
├── metadata.generation ← MW spec version (you set this)
117+
├── spec.workload.manifests[] ← The K8s resources
118+
├── spec.manifestConfigs[] ← Feedback rules
119+
└── status
120+
├── conditions[]
121+
│ ├── type: Applied/Available
122+
│ └── observedGeneration ← MW version agent processed
123+
└── resourceStatus.manifests[]
124+
├── resourceMeta ← Which object
125+
├── conditions[] ← Did agent apply it? (no observedGen here)
126+
└── statusFeedback.jsonRaw ← Contains fields requested via feedbackRules
127+
(e.g., observedGeneration, conditions, replicas)
128+
```
129+
130+
## Update Strategy Types
131+
132+
| Type | Behavior | Use Case |
133+
|------|----------|----------|
134+
| **Update** | Uses Kubernetes PUT - replaces the entire object, last write wins | Normal apply when you own the full resource |
135+
| **CreateOnly** | Creates resource but never updates it | Bootstrap resources that shouldn't be modified |
136+
| **ServerSideApply** | Uses Kubernetes PATCH with field ownership tracking - only sends fields you manage, merges with other controllers | Recommended. Safe when multiple controllers touch the same resource |
137+
| **ReadOnly** | Does NOT apply. Only checks existence and collects status | Watch resources created by others |
138+
139+
### Example: ReadOnly for watching external resources
140+
141+
```go
142+
ManifestConfigs: []workv1.ManifestConfigOption{
143+
{
144+
ResourceIdentifier: workv1.ResourceIdentifier{
145+
Group: "apps",
146+
Resource: "deployments",
147+
Name: "some-existing-deployment", // created by something else
148+
Namespace: "kube-system",
149+
},
150+
UpdateStrategy: &workv1.UpdateStrategy{
151+
Type: workv1.UpdateStrategyTypeReadOnly, // Don't touch it, just watch
152+
},
153+
FeedbackRules: []workv1.FeedbackRule{
154+
{
155+
Type: workv1.JSONPathsType,
156+
JsonPaths: []workv1.JsonPath{
157+
{Name: "status", Path: ".status"},
158+
},
159+
},
160+
},
161+
},
162+
},
163+
```
164+
165+
## Feedback Rules
166+
167+
FeedbackRules tell the agent what data to extract from each applied resource and send back. Without them, you only get conditions (Applied/Available), not the actual resource status.
168+
169+
```go
170+
FeedbackRules: []workv1.FeedbackRule{
171+
{
172+
Type: workv1.JSONPathsType,
173+
JsonPaths: []workv1.JsonPath{
174+
{Name: "status", Path: ".status"}, // entire status object
175+
{Name: "observedGen", Path: ".status.observedGeneration"}, // specific field
176+
{Name: "replicas", Path: ".status.readyReplicas"}, // any field
177+
{Name: "resource", Path: "@"}, // entire resource (spec + status)
178+
},
179+
},
180+
},
181+
```
182+
183+
| Path | Returns |
184+
|------|---------|
185+
| `.status` | The resource's status object |
186+
| `.status.observedGeneration` | A specific field |
187+
| `@` | The entire resource (including spec and status) |
188+
189+
The extracted values appear in `status.resourceStatus.manifests[].statusFeedback.values[].fieldValue.jsonRaw` and must be parsed by the client.
190+
191+
## Deletion Behavior
192+
193+
For a visual sequence diagram of the delete flow, see [Resource Delete Flow](./maestro.md#maestro-resource-flow).
194+
195+
> **Note:** Maestro's database is a tracking layer, not the source of truth for what's running on clusters. If the database is wiped directly (bypassing the API), resources on target clusters remain intact. Source clients can resync to restore Maestro's tracking state.
196+
197+
### Deletion Flow
198+
199+
When you delete a ManifestWork/ResourceBundle, the following sequence occurs:
200+
201+
```
202+
1. User requests deletion (REST DELETE or gRPC Delete)
203+
204+
205+
2. Maestro marks resource as "deleting" (soft delete, sets deleted_at timestamp)
206+
207+
208+
3. Maestro sends delete CloudEvent to agent
209+
210+
211+
4. Agent deletes resources from target cluster (per PropagationPolicy)
212+
213+
214+
5. Agent sends confirmation CloudEvent back
215+
216+
217+
6. Maestro hard deletes the record from database
218+
```
219+
220+
### Propagation Policy
221+
222+
The `DeleteOption.PropagationPolicy` controls what happens to the resources on the target cluster:
223+
224+
| Policy | Behavior | Use Case |
225+
|--------|----------|----------|
226+
| **Foreground** | Delete resources and wait for them to be gone before completing | Default. Clean deletion. |
227+
| **Orphan** | Remove from Maestro tracking but leave resources running on cluster | Hand off to another system |
228+
| **SelectivelyOrphan** | Orphan only specific resources, delete others | Transfer ownership of some resources |
229+
230+
### Setting Delete Options
231+
232+
Delete options are set in the ManifestWork spec:
233+
234+
```go
235+
&workv1.ManifestWork{
236+
Spec: workv1.ManifestWorkSpec{
237+
DeleteOption: &workv1.DeleteOption{
238+
PropagationPolicy: workv1.DeletePropagationPolicyTypeForeground, // or Orphan
239+
},
240+
Workload: workv1.ManifestsTemplate{
241+
Manifests: []workv1.Manifest{...},
242+
},
243+
},
244+
}
245+
```
246+
247+
### Selective Orphan Example
248+
249+
Transfer ownership of a specific resource to another ManifestWork:
250+
251+
```go
252+
DeleteOption: &workv1.DeleteOption{
253+
PropagationPolicy: workv1.DeletePropagationPolicyTypeSelectivelyOrphan,
254+
SelectivelyOrphan: &workv1.SelectivelyOrphan{
255+
OrphaningRules: []workv1.OrphaningRule{
256+
{
257+
Group: "apps",
258+
Resource: "deployments",
259+
Name: "shared-component",
260+
Namespace: "default",
261+
},
262+
},
263+
},
264+
},
265+
```
266+
267+
### TTL Auto-Deletion
268+
269+
ManifestWorks can be automatically deleted after completion:
270+
271+
```go
272+
DeleteOption: &workv1.DeleteOption{
273+
PropagationPolicy: workv1.DeletePropagationPolicyTypeForeground,
274+
TTLSecondsAfterFinished: ptr.To(int64(3600)), // Delete 1 hour after completion
275+
},
276+
```
277+
278+
### Checking Deletion Status
279+
280+
A resource being deleted will have:
281+
- `deleted_at` timestamp set (visible in REST API response)
282+
- Status condition `type: Deleted` once agent confirms deletion
283+
284+
```go
285+
bundle, _, _ := client.DefaultAPI.ApiMaestroV1ResourceBundlesIdGet(ctx, id).Execute()
286+
if bundle.DeletedAt != nil {
287+
// Resource is being deleted
288+
}
289+
```
290+
291+
## Troubleshooting
292+
293+
If resources are not being applied or status is not being reported, see [Troubleshooting](./troubleshooting.md) for health checks, log analysis, and common issues.

0 commit comments

Comments
 (0)