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
31 changes: 31 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,34 @@ jobs:

- name: Test
run: go test ./... -v

crd-drift:
name: Embedded CRDs in sync with butler-api
runs-on: ubuntu-latest
steps:
- name: Checkout butler-cli
uses: actions/checkout@v4
with:
path: butler-cli

# butler-api is the single source of truth for the CRD schemas. butler-api is
# public, so the default token can read it.
- name: Checkout butler-api
uses: actions/checkout@v4
with:
repository: butlerdotdev/butler-api
path: butler-api

- name: Re-sync embedded CRDs from butler-api
working-directory: butler-cli
run: BUTLER_API_PATH="$GITHUB_WORKSPACE/butler-api" ./hack/sync-crds.sh

- name: Fail if the committed copies drifted
working-directory: butler-cli
run: |
if ! git diff --exit-code internal/adm/bootstrap/manifests/crds/; then
echo "::error::Embedded bootstrap CRDs are out of sync with butler-api." \
"Run 'make sync-crds' against an up-to-date butler-api checkout and commit the result." >&2
exit 1
fi
echo "Embedded CRDs match butler-api."
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,13 @@ tidy:
generate:
go generate ./...

# Sync the embedded bootstrap CRDs from butler-api (the single source of truth).
# Override the source with BUTLER_API_PATH=/path/to/butler-api. The crd-drift CI job
# fails if the committed copies differ from a fresh sync.
.PHONY: sync-crds
sync-crds:
./hack/sync-crds.sh

# Documentation
.PHONY: docs
docs: build
Expand Down
56 changes: 56 additions & 0 deletions hack/sync-crds.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/bin/bash
# hack/sync-crds.sh - Sync CRDs from butler-api into the embedded bootstrap manifests.
#
# butleradm bootstrap embeds these CRD YAMLs (see manifests/embed.go) and applies them
# when standing up a management cluster. butler-api is the single source of truth for the
# CRD schemas; this script is the only supported way to refresh the embedded copies.
# Do NOT hand-edit files under internal/adm/bootstrap/manifests/crds/ - they are
# overwritten here and any manual change is reverted (and caught by the crd-drift CI job).
#
# Usage:
# ./hack/sync-crds.sh [BUTLER_API_PATH]
# BUTLER_API_PATH=/path/to/butler-api ./hack/sync-crds.sh
# Defaults to the sibling checkout ../butler-api.
#
# Copyright 2026 The Butler Authors.
# SPDX-License-Identifier: Apache-2.0
set -uo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
BUTLER_API_PATH="${1:-${BUTLER_API_PATH:-$REPO_ROOT/../butler-api}}"
CRD_SOURCE="$BUTLER_API_PATH/config/crd/bases"
CRD_DEST="$REPO_ROOT/internal/adm/bootstrap/manifests/crds"

if [[ ! -d "$CRD_SOURCE" ]]; then
echo "ERROR: CRD source not found: $CRD_SOURCE" >&2
echo "Pass the butler-api checkout path as \$1 or set BUTLER_API_PATH." >&2
exit 1
fi

mkdir -p "$CRD_DEST"
echo "Syncing CRDs from: $CRD_SOURCE"

# The embedded copies are raw controller-gen output, applied verbatim by the bootstrap
# orchestrator, so this is a straight copy (no Helm templating, unlike butler-charts).
synced=0
for src in "$CRD_SOURCE"/*.yaml; do
[[ -e "$src" ]] || continue
b="$(basename "$src")"
cp "$src" "$CRD_DEST/$b"
echo "SYNC: $b"
synced=$((synced + 1))
done

# Prune embedded CRDs that no longer exist upstream so the embedded set stays exactly
# equal to butler-api's (a CRD deleted in butler-api must not linger here).
for dst in "$CRD_DEST"/*.yaml; do
[[ -e "$dst" ]] || continue
b="$(basename "$dst")"
if [[ ! -f "$CRD_SOURCE/$b" ]]; then
echo "PRUNE: $b (no longer in butler-api)"
rm -f "$dst"
fi
done

echo "Done: $synced CRDs synced to ${CRD_DEST#"$REPO_ROOT"/}"
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,16 @@ spec:
description: Icon is an emoji or icon identifier for UI display.
maxLength: 8
type: string
iconData:
description: |-
IconData is a base64-encoded SVG used for inline icon rendering
in the addon catalog UI. Allows self-contained operation without
external image hosting, which is required for air-gapped deployments.
Custom addon authors set this field to embed their own project logo.
When present, takes precedence over the Icon emoji field.
Tracked in butlerdotdev/butler-api#37.
maxLength: 131072
type: string
links:
description: Links provides URLs for documentation, source, etc.
properties:
Expand Down Expand Up @@ -204,6 +214,20 @@ spec:
Platform addons are installed during cluster bootstrap and cannot
be uninstalled via the UI. They appear in a separate section.
type: boolean
tier:
description: |-
Tier controls which directory the addon is placed in during GitOps
export: "infrastructure" or "apps". Infrastructure-tier addons
provide CRDs or platform prerequisites that must exist before
workloads that consume them. When set, this takes precedence over
the Platform field for directory placement. When empty, inferred
from Platform: platform addons default to "infrastructure",
non-platform addons default to "apps".
See ADR-015 for design rationale. Tracked in butler-controller#79.
enum:
- infrastructure
- apps
type: string
required:
- category
- chart
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,26 @@ spec:
spec:
description: ButlerConfigSpec defines the desired state of ButlerConfig.
properties:
audit:
description: Audit configures the platform audit log.
properties:
bufferSize:
default: 10000
description: BufferSize is the in-memory ring buffer capacity
for recent audit queries.
format: int32
maximum: 100000
minimum: 100
type: integer
enabled:
default: true
description: Enabled controls whether audit logging is active.
type: boolean
webhookURL:
description: WebhookURL is an optional URL to POST audit events
to for SIEM integration.
type: string
type: object
controlPlaneExposure:
description: |-
ControlPlaneExposure configures how tenant control planes are exposed.
Expand Down Expand Up @@ -324,6 +344,15 @@ spec:
minimum: 1
type: integer
type: object
defaultTimeServers:
description: |-
DefaultTimeServers are the platform-wide default NTP servers for Talos worker nodes.
Can be overridden per-provider via ProviderConfig.spec.network.timeServers
or per-cluster via TenantCluster.spec.timeServers.
If empty, falls back to pool.ntp.org.
items:
type: string
type: array
gitProvider:
description: |-
GitProvider configures the default Git provider for GitOps operations.
Expand Down Expand Up @@ -425,6 +454,14 @@ spec:
- Disabled
type: string
type: object
notifications:
description: Notifications configures real-time notification forwarding.
properties:
webhookURL:
description: WebhookURL is an optional URL to POST notifications
to (Slack, PagerDuty, Teams, etc).
type: string
type: object
observability:
description: Observability configures platform-level observability
(pipeline, collection defaults).
Expand Down
Loading
Loading