Skip to content
Merged
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
27 changes: 27 additions & 0 deletions .chloggen/perses-crd.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern (e.g. dashboards, config, apply)
component: dashboards

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Accept PersesDashboard CRD files in `apply` and `dashboards create`

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [85]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext: |
PersesDashboard CRDs (perses.dev/v1alpha1 and perses.dev/v1alpha2) are now accepted as input.

# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with "chore" or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Default: '[user]'
change_logs: []
5 changes: 5 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,8 @@ Detailed guidelines are split into focused documents:
- @docs/testing.md — test strategies, integration tests, fixtures, mock server
- @docs/github-actions.md — how to create GitHub actions based on the `dash0` CLI, existing actions and their maintenance
- @docs/changelog-maintenance.md — when and how to create changelog entries

## GitHub issues

When creating GitHub issues, describe **what** and **why** — not **how**.
Issues should only contain the problem statement, user-facing behavior, and acceptance criteria.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ Currently only HTTP OTLP endpoints are supported.

Apply asset definitions from a file, directory, or stdin.
The input may contain multiple YAML documents separated by `---`.
Supported asset types: `Dashboard`, `CheckRule`, `SyntheticCheck`, and `View`.
Supported asset types: `Dashboard`, `PersesDashboard`, `CheckRule`, `SyntheticCheck`, and `View`.

```bash
dash0 apply -f assets.yaml
Expand All @@ -157,6 +157,7 @@ dash0 dashboards list
dash0 dashboards get <id>
dash0 dashboards get <id> -o yaml
dash0 dashboards create -f dashboard.yaml
dash0 dashboards create -f persesdashboard.yaml
dash0 dashboards update [id] -f dashboard.yaml
dash0 dashboards delete <id> [--force]
```
Expand All @@ -173,6 +174,7 @@ dash0 check-rules update [id] -f rule.yaml
dash0 check-rules delete <id> [--force]
```

Both `apply` and `dashboards create` also accept PersesDashboard CRD files.
Both `apply` and `check-rules create` also accept PrometheusRule CRD files.

### Synthetic checks
Expand Down
70 changes: 36 additions & 34 deletions docs/cli-naming-conventions.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,46 +12,46 @@ Use the word "asset" consistently where appropriate.
## Standard CRUD Subcommands for Assets
All asset commands (`dashboards`, `check-rules`, `views`, `synthetic-checks`) use these subcommands:

| Subcommand | Alias | Description |
|------------|----------|-------------|
| `list` | `ls` | List all assets |
| `get` | - | Get a single asset by ID |
| `create` | `add` | Create a new asset from a file |
| Subcommand | Alias | Description |
|------------|----------|--------------------------------------|
| `list` | `ls` | List all assets |
| `get` | - | Get a single asset by ID |
| `create` | `add` | Create a new asset from a file |
| `update` | - | Update an existing asset from a file |
| `delete` | `remove` | Delete an asset by ID |
| `delete` | `remove` | Delete an asset by ID |

## Config Profiles Subcommands
The `config profiles` command uses:

| Subcommand | Alias | Description |
|------------|----------|-------------|
| `list` | `ls` | List all profiles |
| `create` | `add` | Create a new profile |
| `update` | - | Update an existing profile |
| `delete` | `remove` | Delete a profile |
| `select` | `activate` | Set the active profile |
| Subcommand | Alias | Description |
|------------|------------|----------------------------|
| `list` | `ls` | List all profiles |
| `create` | `add` | Create a new profile |
| `update` | - | Update an existing profile |
| `delete` | `remove` | Delete a profile |
| `select` | `activate` | Set the active profile |

## Teams Subcommands (experimental)
The `teams` command manages organizational teams (not assets — no dataset, no YAML input, no `apply`):

| Subcommand | Alias | Description |
|------------|----------|-------------|
| `list` | `ls` | List all teams |
| `get` | - | Get team details (members + accessible assets) |
| `create` | `add` | Create a team (flag-based, not file-based) |
| `update` | - | Update team display settings |
| `delete` | `remove` | Delete a team |
| `list-members` | - | List members of a team |
| `add-members` | - | Add members to a team |
| `remove-members` | - | Remove members from a team |
| Subcommand | Alias | Description |
|------------------|----------|-------------------------------------------------|
| `list` | `ls` | List all teams |
| `get` | - | Get team details (members + accessible assets) |
| `create` | `add` | Create a team (flag-based, not file-based) |
| `update` | - | Update team display settings |
| `delete` | `remove` | Delete a team |
| `list-members` | - | List members of a team |
| `add-members` | - | Add members to a team |
| `remove-members` | - | Remove members from a team |

## Members Subcommands (experimental)
The `members` command manages organization membership:

| Subcommand | Alias | Description |
|------------|----------|-------------|
| `list` | `ls` | List all organization members |
| `invite` | `add` | Invite members by email |
| Subcommand | Alias | Description |
|------------|----------|--------------------------------------|
| `list` | `ls` | List all organization members |
| `invite` | `add` | Invite members by email |
| `remove` | `delete` | Remove members from the organization |

## Aliases
Expand All @@ -64,13 +64,14 @@ The `members` command manages organization membership:
## Asset Kind Display Names
In user-facing output (success messages, dry-run listings, error messages), use human-readable names for asset kinds — **not** PascalCase identifiers:

| Kind identifier | Display name |
|-------------------|--------------------|
| `Dashboard` | Dashboard |
| `CheckRule` | Check rule |
| `SyntheticCheck` | Synthetic check |
| `View` | View |
| `PrometheusRule` | PrometheusRule |
| Kind identifier | Display name |
|--------------------|-----------------|
| `Dashboard` | Dashboard |
| `CheckRule` | Check rule |
| `SyntheticCheck` | Synthetic check |
| `View` | View |
| `PrometheusRule` | PrometheusRule |
| `PersesDashboard` | PersesDashboard |

For example: `Check rule "High Error Rate" created successfully`, not `CheckRule "High Error Rate" created successfully`.

Expand All @@ -85,4 +86,5 @@ For example: `Check rule "High Error Rate" created successfully`, not `CheckRule
The `apply` command and the individual CRUD subcommands (e.g., `check-rules create`, `dashboards create`) must have the same expressiveness.
Any asset format accepted by `apply` must also be accepted by the corresponding `create` command, and vice versa.
For example, `dash0 apply -f prometheusrule.yaml` and `dash0 check-rules create -f prometheusrule.yaml` both accept PrometheusRule CRD files.
Similarly, `dash0 apply -f persesdashboard.yaml` and `dash0 dashboards create -f persesdashboard.yaml` both accept PersesDashboard CRD files.
Shared parsing and import logic lives in `internal/asset/` so that both code paths stay in sync.
30 changes: 28 additions & 2 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,13 @@ $ dash0 dashboards create -f dashboard.yaml
Dashboard "My Dashboard" created successfully
```

`dashboards create` also accepts PersesDashboard CRD files (`perses.dev/v1alpha1` and `perses.dev/v1alpha2`).

```bash
$ dash0 dashboards create -f persesdashboard.yaml
Dashboard "My Perses Dashboard" created
```

`check-rules create` also accepts PrometheusRule CRD files.
Each alerting rule in the CRD is created as a separate check rule (recording rules are skipped):

Expand Down Expand Up @@ -324,7 +331,7 @@ Aliases: `remove`

| Asset type | Command | Notes |
|------------|---------|-------|
| Dashboards | `dash0 dashboards <subcommand>` | |
| Dashboards | `dash0 dashboards <subcommand>` | `create` also accepts PersesDashboard CRD files |
| Check rules | `dash0 check-rules <subcommand>` | `create` also accepts PrometheusRule CRD files |
| Synthetic checks | `dash0 synthetic-checks <subcommand>` | |
| Views | `dash0 views <subcommand>` | |
Expand All @@ -348,9 +355,14 @@ Hidden files and directories (starting with `.`) are skipped.
All documents are validated before any are applied.
If any document fails validation, no changes are made.

Supported `kind` values: `Dashboard`, `CheckRule`, `PrometheusRule`, `SyntheticCheck`, `View`.
Supported `kind` values: `Dashboard`, `PersesDashboard`, `CheckRule`, `PrometheusRule`, `SyntheticCheck`, `View`.
A single file may contain multiple documents separated by `---`.

> [!NOTE]
> The `-f` flag accepts a single path.
> Do not use shell glob patterns like `-f assets/*` — the shell expands the glob into multiple arguments and only the first file is passed to `-f`.
> Use `-f assets/` (the directory) instead.

Examples:

```bash
Expand Down Expand Up @@ -388,6 +400,20 @@ spec:
name: Production Overview
```

PersesDashboard (Perses CRD, converted to a Dashboard on import):

```yaml
apiVersion: perses.dev/v1alpha1
kind: PersesDashboard
metadata:
name: my-perses-dashboard
spec:
display:
name: My Perses Dashboard
duration: 5m
panels: {}
```

Check rule:

```yaml
Expand Down
31 changes: 28 additions & 3 deletions internal/apply/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Each document must have a "kind" field specifying the asset type. Use '-f -' to
When a directory is specified, all .yaml and .yml files are discovered recursively. Hidden files and directories (starting with '.') are skipped. All documents are validated before any are applied; if any document fails validation, no changes are made.

Supported asset types:
- Dashboard
- Dashboard (or PersesDashboard CRD)
- CheckRule (or PrometheusRule CRD)
- SyntheticCheck
- View
Expand All @@ -66,6 +66,9 @@ If an asset exists, it will be updated. If it doesn't exist, it will be created.
# Validate a directory without applying
dash0 apply -f dashboards/ --dry-run`,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
return fmt.Errorf("unexpected arguments: %s\nTo apply multiple files, pass a directory with -f instead of a glob pattern", strings.Join(args, " "))
}
if flags.File == "" {
return fmt.Errorf("file is required; use -f to specify the file (use '-' for stdin)")
}
Expand Down Expand Up @@ -161,7 +164,7 @@ func runApply(ctx context.Context, flags *applyFlags) error {
if doc.kind == "" {
validationErrors = append(validationErrors, fmt.Sprintf("%s: missing 'kind' field", doc.location()))
} else if !isValidKind(doc.kind) {
validationErrors = append(validationErrors, fmt.Sprintf("%s: unsupported kind %q (supported: Dashboard, CheckRule, PrometheusRule, SyntheticCheck, View)", doc.location(), doc.kind))
validationErrors = append(validationErrors, fmt.Sprintf("%s: unsupported kind %q (supported: Dashboard, PersesDashboard, CheckRule, PrometheusRule, SyntheticCheck, View)", doc.location(), doc.kind))
}
}
if len(validationErrors) > 0 {
Expand Down Expand Up @@ -321,6 +324,14 @@ func parseDocumentHeader(data []byte) (kind, name, id string, err error) {
name = asset.ExtractPrometheusRuleName(&promRule)
id = asset.ExtractPrometheusRuleID(&promRule)

case "persesdashboard":
var perses asset.PersesDashboard
if err := sigsyaml.Unmarshal(data, &perses); err != nil {
return "", "", "", fmt.Errorf("failed to decode document: %w", err)
}
name = asset.ExtractPersesDashboardName(&perses)
id = asset.ExtractPersesDashboardID(&perses)

default:
var raw map[string]any
if err := sigsyaml.Unmarshal(data, &raw); err != nil {
Expand Down Expand Up @@ -510,7 +521,7 @@ func readDirectory(dirPath string) ([]assetDocument, error) {

func isValidKind(kind string) bool {
switch normalizeKind(kind) {
case "dashboard", "checkrule", "syntheticcheck", "view", "prometheusrule":
case "dashboard", "checkrule", "syntheticcheck", "view", "prometheusrule", "persesdashboard":
return true
default:
return false
Expand Down Expand Up @@ -562,6 +573,20 @@ func applyDocument(ctx context.Context, apiClient dash0api.Client, doc assetDocu
}
return applyPrometheusRule(ctx, apiClient, &promRule, dataset)

case "persesdashboard":
var perses asset.PersesDashboard
if err := sigsyaml.Unmarshal(doc.raw, &perses); err != nil {
return nil, fmt.Errorf("failed to parse PersesDashboard: %w", err)
}
result, err := asset.ImportPersesDashboard(ctx, apiClient, &perses, dataset)
if err != nil {
return nil, client.HandleAPIError(err, client.ErrorContext{
AssetType: "dashboard",
AssetName: asset.ExtractPersesDashboardName(&perses),
})
}
return []applyResult{{kind: "Dashboard", name: result.Name, id: result.ID, action: applyAction(result.Action)}}, nil

case "syntheticcheck":
var check dash0api.SyntheticCheckDefinition
if err := sigsyaml.Unmarshal(doc.raw, &check); err != nil {
Expand Down
Loading
Loading