A Kubernetes operator for Deco CMS that manages configuration resources and automatically injects them into Knative Services.
The Deco CMS operator manages Decofile custom resources, which represent configuration files for your Deco applications. The operator enables you to:
- Define JSON configuration data as Kubernetes custom resources (Decofiles)
- Automatically create ConfigMaps from Decofile resources
- Inject ConfigMaps into Knative Services via annotations using a mutating webhook
- ✅ Dual Source Support: Inline JSON or GitHub repository sources
- ✅ Automated ConfigMap Generation: Creates/updates ConfigMaps from Decofile resources
- ✅ Unified Format: All sources produce consistent
decofile.jsonformat - ✅ Special Filename Support: Preserves filenames with
%, spaces, and special characters - ✅ Owner References: Automatic cleanup when Decofiles are deleted
- ✅ Webhook-based Injection: Automatically injects ConfigMaps into Knative Services
- ✅ File Mounting: Mounts as
/app/decofile/decofile.jsonby default - ✅ DECO_RELEASE Env Var: Auto-injected for application discovery
- ✅ Custom Mount Paths: Configurable via annotations
- ✅ Label-Based Tracking: Pods labeled for easy discovery
- ✅ Automatic Reload: Notifies pods when ConfigMaps change
- ✅ Smart Delay: 60-second wait for kubelet sync
- ✅ Retry Logic: Exponential backoff with 5 attempts
- ✅ Direct Pod Communication: HTTP calls to reload endpoints
- ✅ Token Authentication: UUID tokens for secure reload requests
- ✅ Multi-Instance Ready: Built-in leader election for high availability
- ✅ Helm Support: Install with Helm for easy configuration
- ✅ CI/CD Pipeline: Automated builds and validation
- ✅ Complete Testing: Unit, integration, and e2e tests
From GitHub Release (Recommended):
# Install from release
helm install deco \
https://github.com/decocms/operator/releases/download/v0.1.0/deco-operator-0.1.0.tgz \
--namespace operator-system \
--create-namespace \
--wait
# With GitHub token for private repos
helm install deco \
https://github.com/decocms/operator/releases/download/v0.1.0/deco-operator-0.1.0.tgz \
--namespace operator-system \
--create-namespace \
--set github.token=ghp_your_token \
--waitFrom Source:
# Clone the repository
git clone https://github.com/decocms/operator.git
cd operator
# Install with Helm
helm install deco chart/ \
--namespace operator-system \
--create-namespace \
--wait# Check operator is running
kubectl get pods -n operator-system
# Check CRD is installed
kubectl get crd decofiles.deco.sites
# View operator logs
kubectl logs -n operator-system -l control-plane=controller-manager -f- Kubernetes cluster (1.16+)
- Helm 3.x (for installation)
- cert-manager (for webhook TLS)
- Knative Serving (for injection features)
See DEPLOYMENT.md for detailed deployment options, HELM.md for complete Helm documentation, and docs/RELOAD_AUTHENTICATION.md for securing reload endpoints.
The Decofile operator supports two source types for configuration data:
Define JSON configuration directly in the Decofile spec:
apiVersion: deco.sites/v1alpha1
kind: Decofile
metadata:
name: decofile-miess-01-main
namespace: sites-miess-01
spec:
source: inline
inline:
value:
config.json:
key: "value"
environment: "production"
data.json:
foo: "bar"Fetch configuration from a GitHub repository:
apiVersion: deco.sites/v1alpha1
kind: Decofile
metadata:
name: decofile-mysite-main
namespace: sites-mysite
spec:
source: github
github:
org: deco-sites
repo: mysite
commit: main # or specific commit SHA
path: .deco/blocks
secret: github-tokenGitHub Secret Setup:
Create a secret with your GitHub personal access token:
apiVersion: v1
kind: Secret
metadata:
name: github-token
namespace: sites-mysite
type: Opaque
stringData:
token: ghp_your_github_personal_access_token_hereApply it:
# For inline source
kubectl apply -f config/samples/deco.sites_v1alpha1_decofile.yaml
# For GitHub source
kubectl apply -f config/samples/github_secret.yaml
kubectl apply -f config/samples/deco.sites_v1alpha1_decofile_github.yamlThe operator will automatically create a ConfigMap named decofile-<name> with the data.
Add annotations to your Knative Service to automatically inject the Decofile:
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: my-site
namespace: sites-mysite # Namespace must start with "sites-" for "default" resolution
annotations:
deco.sites/decofile-inject: "default" # Resolves to "decofile-mysite-main"
# deco.sites/decofile-mount-path: "/custom/path" # Optional: custom mount directory
spec:
template:
spec:
containers:
- name: app
image: your-deco-app:latestWhat the webhook does automatically:
- ✅ Resolves the Decofile name (e.g.,
"default"→decofile-mysite-main) - ✅ Fetches the ConfigMap from the Decofile status
- ✅ Mounts ConfigMap as
/app/decofile/decofile.json(or custom path) - ✅ Injects
DECO_RELEASE=file:///app/decofile/decofile.jsonenvironment variable - ✅ Labels pod with
deco.sites/decofile: <name>for tracking
Your application receives:
DECO_RELEASEenv var pointing to the config file- Single JSON file with all configuration:
{"file1.json": {...}, "file2.json": {...}} - Auto-updates when Decofile changes (with 60s kubelet sync delay)
Specifies which Decofile to inject into the Service.
- Value:
"default"- Resolves todecofile-{site}-mainwhere{site}is extracted from the namespace by stripping thesites-prefix- Example: namespace
sites-miess-01→ sitemiess-01→ decofiledecofile-miess-01-main
- Example: namespace
- Value:
"<name>"- Uses the specified Decofile name directly
Required: Namespace must start with sites- when using "default"
Optional annotation to customize the mount path for the ConfigMap.
- Default:
/app/deco/.deco/blocks - Example:
/custom/config/path
Best for:
- Small configuration files
- Quick testing and development
- Static configurations
Pros:
- Simple and straightforward
- No external dependencies
- Immediate updates
Cons:
- Configuration stored in Kubernetes
- Harder to version control
- Manual updates required
Best for:
- GitOps workflows
- Versioned configurations
- Shared configurations across teams
- Large configuration files
Pros:
- Version control via Git
- Audit trail in Git history
- Easy rollbacks (change commit SHA)
- Centralized configuration management
- Supports private repositories
Cons:
- Requires GitHub access token
- Network dependency
- Slightly more complex setup
How it works:
- Controller fetches GitHub credentials from Kubernetes secret
- Downloads repository ZIP from
https://codeload.github.com/{org}/{repo}/zip/{commit} - Extracts files from specified path
- Creates ConfigMap with file contents
Security:
- Tokens stored in Kubernetes secrets
- Use read-only tokens (minimum required permissions)
- Supports private repositories
The Deco CMS Operator consists of three main components:
Manages the lifecycle of Decofile custom resources:
Responsibilities:
- Watches Decofile resources for changes
- Retrieves configuration from inline or GitHub sources
- Creates/updates ConfigMaps with unified
decofile.jsonformat - Detects ConfigMap changes and notifies affected pods
- Updates status with conditions and metadata
Source Implementations:
InlineSource- Parses inline JSON valuesGitHubSource- Downloads from GitHub repositories- Strategy pattern for easy extensibility
Change Notifications:
- Discovers pods via
deco.sites/decofilelabel - Calls
/.decofile/reloadon each pod with token authentication - Sends
Authorization: Token <uuid>header for security - Waits for pods to confirm reload before completing reconciliation
Intercepts Knative Service CREATE/UPDATE operations:
Injection Process:
- Checks for
deco.sites/decofile-injectannotation - Resolves Decofile name ("default" or explicit)
- Fetches ConfigMap name from Decofile status
- Mounts ConfigMap as
/app/decofile/directory - Injects
DECO_RELEASEenvironment variable - Labels pods with
deco.sites/decofilefor tracking
Features:
- Supports custom mount paths via annotation
- Handles "default" resolution from namespace
- TLS secured via cert-manager
Notifies pods about configuration changes:
Flow:
- Triggered when ConfigMap data changes
- Queries pods by Decofile label
- HTTP GET with
?delay=60000parameter - Pods wait for kubelet sync before reading file
- Retries with exponential backoff
- ✅ Leader Election: Only one controller instance reconciles
- ✅ Stateless Webhooks: All instances handle requests
- ✅ Horizontal Scaling: Configure via
replicaCountin Helm - ✅ Automatic Failover: Built into controller-runtime
- Go 1.21+
- Docker
- kubectl
- operator-sdk
- Install the CRDs:
make install- Run the controller:
make run# Run unit tests
make test
# Run with coverage
make test-coverage# Build binary
make build
# Build Docker image
make docker-build IMG=<your-registry>/decofile-operator:tag
# Push Docker image
make docker-push IMG=<your-registry>/decofile-operator:tagThe operator requires:
- Full access to Decofiles (our CRD)
- Full access to ConfigMaps
- Read access to Knative Services (for webhook)
The webhook is automatically configured with:
- TLS certificates via cert-manager
- Mutating webhook for Knative Services
- Validating webhook (optional, currently disabled)
- Check cert-manager is installed and running
- Verify the webhook configuration:
kubectl get mutatingwebhookconfiguration
kubectl get certificate -n decofile-operator-system- Check webhook logs:
kubectl logs -n decofile-operator-system deployment/decofile-operator-controller-manager- Check the Decofile resource:
kubectl get decofile -n <namespace> <name> -o yaml- Check controller logs:
kubectl logs -n decofile-operator-system deployment/decofile-operator-controller-managerFor inline source:
- Check the YAML syntax is valid
- Ensure
source: inlineis set - Verify
inline.valuecontains valid JSON
For GitHub source:
- Verify the secret exists and contains valid token
- Check GitHub credentials:
kubectl get secret github-token -n <namespace> -o yaml - Ensure org, repo, commit, and path are correct
- Check if repository is private (requires valid token)
- Verify network connectivity from cluster to GitHub
Common errors:
- "Decofile X not found": Ensure the Decofile exists in the same namespace
- "namespace does not start with 'sites-'": When using "default", the namespace must start with
sites-prefix - "does not have a ConfigMap created yet": Wait for controller to reconcile the Decofile
- "failed to get Decofile": Check the Decofile name and namespace
Common errors:
- "failed to download: status 404": Repository not found or token lacks access
- "failed to get secret": Secret doesn't exist in the namespace
- "secret does not contain 'token' key": Secret must have a
tokenfield - "failed to read zip": Invalid ZIP file or network issue
Debugging:
# Check controller logs
kubectl logs -n decofile-operator-system deployment/decofile-operator-controller-manager
# Check Decofile status
kubectl get decofile <name> -n <namespace> -o yaml
# Verify secret
kubectl get secret github-token -n <namespace> -o jsonpath='{.data.token}' | base64 -d
# Test GitHub access manually
curl -H "Authorization: token YOUR_TOKEN" https://codeload.github.com/org/repo/zip/mainTo uninstall the operator:
make undeployThis will remove:
- The operator deployment
- Webhook configurations
- RBAC resources
- CRDs (and all Decofile resources)
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
Copyright 2025.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.