Merge pull request #956 from negz/whats-op-doc

First pass at Operations docs
This commit is contained in:
Nic Cope 2025-08-07 21:32:56 -07:00 committed by GitHub
commit 704d5dfe38
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 2416 additions and 26 deletions

View File

@ -0,0 +1,55 @@
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(find:*)",
"Bash(nix-shell:*)",
"Bash(vale:*)",
"Bash(grep:*)",
"Bash(git add:*)",
"Bash(git commit:*)",
"Bash(for file in /home/negz/control/crossplane/docs/content/v1.{18,19,20}/learn/feature-lifecycle.md)",
"Bash(do sed -i 's/won''''t be/aren''''t/g; s/will provide/provide/g' \"$file\")",
"Bash(done)",
"Bash(for file in /home/negz/control/crossplane/docs/content/v1.{18,19,20}/learn/release-cycle.md)",
"Bash(do sed -i 's/will be/are/g; s/won''''t be/aren''''t/g; s/will /\\0/g' \"$file\")",
"Bash(sed:*)",
"Bash(for version in v1.18 v1.19 v1.20)",
"Bash(do)",
"Bash(for file in /home/negz/control/crossplane/docs/content/$version/learn/release-cycle.md)",
"Bash(if [[ -f \"$file\" ]])",
"Bash(then)",
"Bash(fi)",
"Bash(for file in /home/negz/control/crossplane/docs/content/$version/guides/vault-injection.md)",
"Bash(for:*)",
"Bash(# Fix specific line-by-line issues in remaining files\nsed -i ''132s/will assign/assigns/'' /home/negz/control/crossplane/docs/content/v1.19/concepts/composition-revisions.md\nsed -i ''329s/will also/also/'' /home/negz/control/crossplane/docs/content/v1.19/concepts/composition-revisions.md\nsed -i ''294s/will automatically/automatically/'' /home/negz/control/crossplane/docs/content/v1.19/concepts/packages.md\nsed -i ''317s/will automatically/automatically/'' /home/negz/control/crossplane/docs/content/v1.19/concepts/providers.md\n\nsed -i ''132s/will assign/assigns/'' /home/negz/control/crossplane/docs/content/v1.20/concepts/composition-revisions.md\nsed -i ''329s/will also/also/'' /home/negz/control/crossplane/docs/content/v1.20/concepts/composition-revisions.md\nsed -i ''84s/will select/selects/'' /home/negz/control/crossplane/docs/content/v1.20/concepts/composition-revisions.md\nsed -i ''19s/will enforce/enforces/'' /home/negz/control/crossplane/docs/content/v1.20/guides/change-logs.md\n\nsed -i ''84s/will select/selects/'' /home/negz/control/crossplane/docs/content/v2.0-preview/composition/composition-revisions.md\nsed -i ''128s/will assign/assigns/'' /home/negz/control/crossplane/docs/content/v2.0-preview/composition/composition-revisions.md\nsed -i ''314s/will also/also/'' /home/negz/control/crossplane/docs/content/v2.0-preview/composition/composition-revisions.md\nsed -i ''293s/will automatically/automatically/'' /home/negz/control/crossplane/docs/content/v2.0-preview/packages/configurations.md\nsed -i ''317s/will automatically/automatically/'' /home/negz/control/crossplane/docs/content/v2.0-preview/packages/providers.md)",
"Bash(rg:*)",
"Bash(do sed -i 's/a large number of CRDs/many CRDs/g' \"content/$file/guides/crossplane-with-argo-cd.md\")",
"Bash(/dev/null)",
"Bash(hugo:*)",
"Bash(jq:*)",
"Bash(gh pr view:*)",
"Bash(gh pr diff:*)",
"WebFetch(domain:raw.githubusercontent.com)",
"Bash(htmltest:*)",
"WebFetch(domain:protobuf.dev)",
"WebFetch(domain:googleapis.dev)",
"Bash(mkdir:*)",
"Bash(mv:*)",
"Bash(true)",
"Bash(crossplane alpha render:*)",
"Bash(md5sum:*)",
"Bash(cp:*)",
"Bash(kubectl apply:*)",
"Bash(kubectl get:*)",
"Bash(kubectl describe:*)",
"Bash(kubectl delete:*)",
"Bash(/tmp/crank-fixed:*)",
"WebFetch(domain:github.com)",
"Bash(gh api:*)"
],
"additionalDirectories": [
"/home/negz/control/crossplane/crossplane"
]
}
}

272
CLAUDE.md Normal file
View File

@ -0,0 +1,272 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with
code in this repository.
## Project Overview
This is the repository for the [Crossplane documentation](https://docs.crossplane.io).
The documentation site is built using [Hugo](https://gohugo.io/) and hosted on
Netlify. The site provides comprehensive documentation for Crossplane, a cloud
native control plane framework.
## Development Commands
### Local Development
- `hugo server` - Start local development server on http://localhost:1313
- `hugo server --minify` - Start server with minified output
- `hugo --minify` - Build static site with minified output
### Prerequisites
- Hugo Extended version (required for SCSS/CSS processing)
- Node.js and npm (for PostCSS processing)
- Git (for content management)
- Vale (for style linting)
### Nix Development Environment
For Nix users, a `shell.nix` file is provided with all necessary dependencies:
```bash
nix-shell # Enter development environment with Hugo, Vale, Node.js, and utilities
```
The Nix shell includes:
- Hugo (extended version)
- Vale prose linter
- Node.js 20 with npm
- HTML validation tools
- Image processing utilities (ImageMagick)
- JSON/YAML processing tools (jq, yq)
### Building and Deployment
- Site automatically builds on Netlify using `netlify_build.sh`
- Uses Hugo version 0.119.0 (specified in netlify.toml)
- Production URL: https://docs.crossplane.io/
- Preview deployments available for PRs
## Repository Structure
### Content Organization
- `content/` - All documentation content organized by version
- `v1.18/`, `v1.19/`, `v1.20/` - Version-specific documentation
- `master/` - Next release documentation
- `v2.0-preview/` - Preview documentation for v2.0
- `contribute/` - Contributing guidelines and style guides
- `static/` - Static assets (images, icons, etc.)
- `themes/geekboot/` - Custom Hugo theme based on Geekdoc and Bootstrap
- `utils/` - Development utilities (Vale style checking, webpack config)
### Key Configuration Files
- `config.yaml` - Hugo site configuration
- `netlify.toml` - Netlify build and deployment configuration
- `netlify_build.sh` - Custom build script for version management
- `package.json` - PostCSS dependencies for CSS optimization
## Documentation Architecture
### Version Management
- Each Crossplane version has its own content directory
- Latest version (currently 1.20) is copied to `/latest` during build
- Version dropdown menu allows switching between versions
- Automatic redirects for EOL versions
### Content Types
- **Getting Started** - Installation and basic usage guides
- **Concepts** - Core Crossplane concepts and architecture
- **Guides** - How-to guides and advanced usage patterns
- **API Reference** - CRD documentation generated from YAML
- **CLI Reference** - Command reference documentation
### Hugo Features Used
- Custom shortcodes for enhanced functionality (tabs, hints, code highlighting)
- Front matter for metadata (version, weight, state, descriptions)
- Table of contents generation
- Syntax highlighting with line numbers
- Image optimization and processing
- RSS feeds for sections
## Writing Guidelines
### Style Guide Essentials
- Use active voice, avoid passive voice
- Present tense, avoid "will"
- Sentence-case headings
- Wrap lines at 80 characters
- Spell out numbers less than 10
- Use contractions (don't, can't, isn't)
- No Oxford commas
- U.S. English spelling and grammar
- Capitalize "Crossplane" and "Kubernetes" (never "k8s")
### Content Structure
- Each page requires front matter with `title` and `weight`
- Use `state: alpha` or `state: beta` for feature lifecycle
- Include `alphaVersion` and `betaVersion` for feature tracking
- Use descriptive link text, avoid "click here"
- Order brand names alphabetically (AWS, Azure, GCP)
### Code and Technical Content
- Use inline code style (backticks) for files, directories, paths
- Use angle brackets for placeholders (`<placeholder_name>`)
- Kubernetes objects: use UpperCamelCase for Kinds, kebab-case for names
- Use hover shortcodes to relate explanations to code examples
## Development Workflow
### Contributing Process
1. Clone the repository: `git clone https://github.com/crossplane/docs.git`
2. Set up development environment:
- **With Nix**: Run `nix-shell` to enter development environment
- **Without Nix**: Install Hugo Extended, Vale, and Node.js manually
3. Run `hugo server` for local development
4. Make changes to appropriate version directory in `/content`
5. Test locally at http://localhost:1313
6. Run `vale content/` to check style compliance
7. Submit PR for review
### Content Management
- Create new content as markdown files in appropriate version directories
- Use `_index.md` for section landing pages
- Include proper front matter for all pages
- Test with Vale linter for style compliance
- Images should be optimized and placed in appropriate directories
### Quality Assurance
- Vale linter enforces style guide compliance
- HTML validation with htmltest
- Automated Netlify preview deployments for all PRs
- Manual review process for content accuracy
## Build System Details
### Hugo Configuration
- Uses custom "geekboot" theme (based on Geekdoc + Bootstrap)
- Goldmark renderer with unsafe HTML enabled
- Syntax highlighting with line numbers and anchor links
- Module mounts for content and asset processing
- Table of contents generation (levels 1-9)
### CSS and Assets
- PostCSS with PurgeCSS for optimization
- Custom SCSS in theme directory
- Responsive design with Bootstrap framework
- Font loading for Avenir and Consolas
- Icon system with SVG assets
### Netlify Integration
- Environment-specific base URLs
- Automatic redirects for moved/deprecated content
- Build optimization with writeStats for PurgeCSS
- Deploy preview URLs for testing
## Common Tasks
### Adding New Documentation
1. Create markdown file in appropriate version directory
2. Add front matter with title, weight, and optional state
3. Follow style guide for writing
4. Add to multiple versions if needed
5. Test locally with Hugo server
### Version Management
- Copy content between version directories as needed
- Update version references in netlify_build.sh
- Ensure redirects are configured for moved content
- Test version switching functionality
### Style and Linting
- Run Vale linter: `vale content/`
- Check for style guide compliance
- Validate HTML structure
- Ensure proper image optimization
## Important Files
- `config.yaml` - Hugo site configuration and parameters
- `netlify_build.sh` - Build script with version management logic
- `shell.nix` - Nix development environment with all dependencies
- `content/contribute/` - Comprehensive contributing guidelines
- `themes/geekboot/layouts/` - Hugo templates and partials
- `utils/vale/` - Vale style checking configuration
## Vale Linting Guidelines
**CRITICAL: The documentation uses Vale for strict style enforcement. ALL errors and warnings MUST be fixed before merging. Writing Vale-compliant content from the start saves significant time - fixing linting issues after writing is much more time-consuming than avoiding them initially.**
Here are common issues to avoid:
### Common Vale Errors
**Spelling Issues:**
- **API field names**: Put in backticks (`lastScheduleTime`) rather than adding to dictionaries
- **Technical terms**: Add Crossplane-specific terms to `utils/vale/styles/Crossplane/crossplane-words.txt`
- **General tech terms**: Add to `utils/vale/styles/Crossplane/allowed-jargon.txt`
- **Hyphenated terms**: Add to `utils/vale/styles/Crossplane/spelling-exceptions.txt`
- **Resource kinds**: When referring to Kubernetes resource kinds (Operation, CronOperation), these are correct - use Vale disable comments for false positives
### Common Vale Warnings
**Headings:**
- Use sentence-case, not title-case: "How operations work" not "How Operations Work"
- Exception: Technical terms like CronOperation in headings need disable comments
- Use `<!-- vale Google.Headings = NO -->` around technical headings
**Word Choice Issues:**
- **Weasel words**: Avoid "many", "various", "numerous" → use "several", "multiple", "some"
- **Too wordy**: "terminate" → "stop", "monitor" → "check" (unless monitoring is the correct technical term)
- **Future tense**: "won't start" → "don't start", avoid "will" → use present tense
**Passive Voice:**
- "Operations are designed for" → "Operations focus on"
- "may be terminated" → "may stop"
- "being watched" → "under watch"
- "is needed" → "you need"
**Other Issues:**
- **Ordinal numbers**: "1st" → "first"
- **Adverbs**: Remove "gracefully", "correctly", "properly", "repeatedly"
- **Contractions**: Use "can't" instead of "cannot"
### Vale Disable Comments
Use disable comments for legitimate technical terms that trigger false positives:
```markdown
<!-- vale Google.Headings = NO -->
### CronOperation
<!-- vale Google.Headings = YES -->
<!-- vale write-good.TooWordy = NO -->
Monitor resource usage carefully.
<!-- vale write-good.TooWordy = YES -->
```
### Dictionary Management
- **`crossplane-words.txt`**: Crossplane-specific terms only (CronOperation, XRD, etc.)
- **`allowed-jargon.txt`**: General technical terms (kubectl, ConfigMap, etc.)
- **`spelling-exceptions.txt`**: Hyphenated terms (day-two, self-signed, etc.)
- Keep all dictionaries sorted alphabetically
### Testing Vale
**ALWAYS run Vale before considering documentation complete:**
```bash
# Check only warnings and errors (ignore suggestions)
vale --minAlertLevel=warning content/
# Get structured output for analysis
vale --output=JSON content/ | jq '.[][] | select(.Severity == "warning")'
```
**Remember: Writing documentation that follows these guidelines from the start is much faster than writing first and fixing Vale issues later. The time investment in learning these patterns pays off immediately.**
## Session Management
- **Pre-Compaction Analysis**: Before compacting chat history, provide a
structured session summary including:
- Documentation updates made and their impact
- Important learnings about Hugo, documentation patterns, or writing guidelines
- Potential updates to CLAUDE.md based on new documentation features or workflows
- Any recurring style or technical issues encountered

View File

@ -23,6 +23,8 @@ Crossplane organizes its documentation into the following sections:
* [Composition]({{<ref "composition">}}) covers the key concepts of composition.
* [Operations]({{<ref "operations">}}) covers the key concepts of operations.
* [Managed Resources]({{<ref "managed-resources">}}) covers the key concepts of
managed resources.

View File

@ -0,0 +1,367 @@
---
title: Get Started With Operations
weight: 300
state: alpha
alphaVersion: 2.0
---
This guide shows how to use Crossplane Operations to automate day-two
operational tasks. You create an `Operation` that checks SSL certificate
expiry for a website.
**Crossplane calls this _Operations_.** Operations run function pipelines to
perform tasks that don't fit the typical resource creation pattern - like
certificate monitoring, rolling upgrades, or scheduled maintenance.
An `Operation` looks like this:
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: Operation
metadata:
name: check-cert-expiry
spec:
mode: Pipeline
pipeline:
- step: check-certificate
functionRef:
name: crossplane-contrib-function-python
input:
apiVersion: python.fn.crossplane.io/v1beta1
kind: Script
script: |
import ssl
import socket
from datetime import datetime
from crossplane.function import request, response
def operate(req, rsp):
hostname = "google.com"
port = 443
# Get SSL certificate info
context = ssl.create_default_context()
with socket.create_connection((hostname, port)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
cert = ssock.getpeercert()
# Parse expiration date
expiry_date = datetime.strptime(cert['notAfter'], '%b %d %H:%M:%S %Y %Z')
days_until_expiry = (expiry_date - datetime.now()).days
# Return results in operation output
response.set_output(rsp, {
"hostname": hostname,
"certificateExpires": cert['notAfter'],
"daysUntilExpiry": days_until_expiry,
"status": "warning" if days_until_expiry < 30 else "ok"
})
```
<!-- vale Crossplane.Spelling = NO -->
**The `Operation` runs once to completion, like a Kubernetes `Job`.**
<!-- vale Crossplane.Spelling = YES -->
When you create the `Operation`, Crossplane runs the function pipeline. The
function checks SSL certificate expiry for google.com and returns the results
in the operation's output.
This basic example shows the concept. In the walkthrough below, you create
a more realistic `Operation` that reads Kubernetes `Ingress` resources and
annotates them with certificate expiry information for monitoring tools.
## Prerequisites
This guide requires:
* A Kubernetes cluster with at least 2 GB of RAM
* The Crossplane v2 preview [installed on the Kubernetes cluster]({{<ref "install">}}) with Operations enabled
{{<hint "tip">}}
Enable Operations by adding `--enable-operations` to Crossplane's startup
arguments. If using Helm:
```shell
helm upgrade --install crossplane crossplane-stable/crossplane \
--namespace crossplane-system \
--set args='{"--enable-operations"}'
```
{{</hint>}}
## Create an operation
Follow these steps to create your first `Operation`:
1. [Create a sample Ingress](#create-a-sample-ingress) for certificate checking
1. [Install the function](#install-the-function) you want to use for the
operation
1. [Create the Operation](#create-the-operation) that checks the `Ingress`
1. [Check the Operation](#check-the-operation) as it runs
### Create a sample Ingress
Create an `Ingress` that references a real hostname but doesn't route actual
traffic:
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-app
namespace: default
spec:
rules:
- host: google.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nonexistent-service
port:
number: 80
```
Save as `ingress.yaml` and apply it:
```shell
kubectl apply -f ingress.yaml
```
### Grant Ingress permissions
`Operations` need permission to access and change `Ingresses`. Create a `ClusterRole`
that grants Crossplane access to `Ingresses`:
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: operations-ingress-access
labels:
rbac.crossplane.io/aggregate-to-crossplane: "true"
rules:
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "list", "watch", "patch", "update"]
```
Save as `ingress-rbac.yaml` and apply it:
```shell
kubectl apply -f ingress-rbac.yaml
```
### Install the function
Operations use operation functions to implement their logic. Use the Python
function, which supports both composition and operations.
Create this function to install Python support:
```yaml
apiVersion: pkg.crossplane.io/v1
kind: Function
metadata:
name: crossplane-contrib-function-python
spec:
package: xpkg.crossplane.io/crossplane-contrib/function-python:v0.2.0
```
Save the function as `function.yaml` and apply it:
```shell
kubectl apply -f function.yaml
```
Check that Crossplane installed the function:
```shell {copy-lines="1"}
kubectl get -f function.yaml
NAME INSTALLED HEALTHY PACKAGE AGE
crossplane-contrib-function-python True True xpkg.crossplane.io/crossplane-contrib/function-python:v0.2.0 12s
```
### Create the operation
Create this `Operation` that monitors the `Ingress` certificate:
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: Operation
metadata:
name: ingress-cert-monitor
spec:
mode: Pipeline
pipeline:
- step: check-ingress-certificate
functionRef:
name: crossplane-contrib-function-python
requirements:
requiredResources:
- requirementName: ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
name: example-app
namespace: default
input:
apiVersion: python.fn.crossplane.io/v1beta1
kind: Script
script: |
import ssl
import socket
from datetime import datetime
from crossplane.function import request, response
def operate(req, rsp):
# Get the Ingress resource
ingress = request.get_required_resource(req, "ingress")
if not ingress:
response.set_output(rsp, {"error": "No ingress resource found"})
return
# Extract hostname from Ingress rules
hostname = ingress["spec"]["rules"][0]["host"]
port = 443
# Get SSL certificate info
context = ssl.create_default_context()
with socket.create_connection((hostname, port)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
cert = ssock.getpeercert()
# Parse expiration date
expiry_date = datetime.strptime(cert['notAfter'], '%b %d %H:%M:%S %Y %Z')
days_until_expiry = (expiry_date - datetime.now()).days
# Add warning if certificate expires soon
if days_until_expiry < 30:
response.warning(rsp, f"Certificate for {hostname} expires in {days_until_expiry} days")
# Annotate the Ingress with certificate expiry info
rsp.desired.resources["ingress"].resource.update({
"apiVersion": "networking.k8s.io/v1",
"kind": "Ingress",
"metadata": {
"name": ingress["metadata"]["name"],
"namespace": ingress["metadata"]["namespace"],
"annotations": {
"cert-monitor.crossplane.io/expires": cert['notAfter'],
"cert-monitor.crossplane.io/days-until-expiry": str(days_until_expiry),
"cert-monitor.crossplane.io/status": "warning" if days_until_expiry < 30 else "ok"
}
}
})
# Return results in operation output for monitoring
response.set_output(rsp, {
"ingressName": ingress["metadata"]["name"],
"hostname": hostname,
"certificateExpires": cert['notAfter'],
"daysUntilExpiry": days_until_expiry,
"status": "warning" if days_until_expiry < 30 else "ok"
})
```
Save the operation as `operation.yaml` and apply it:
```shell
kubectl apply -f operation.yaml
```
### Check the operation
Check that the `Operation` runs successfully:
```shell {copy-lines="1"}
kubectl get -f operation.yaml
NAME SYNCED SUCCEEDED AGE
ingress-cert-monitor True True 15s
```
{{<hint "tip">}}
`Operations` show `SUCCEEDED=True` when they complete successfully.
{{</hint>}}
Check the `Operation`'s detailed status:
```shell {copy-lines="1"}
kubectl describe operation ingress-cert-monitor
# ... metadata ...
Status:
Conditions:
Last Transition Time: 2024-01-15T10:30:15Z
Reason: PipelineSuccess
Status: True
Type: Succeeded
Last Transition Time: 2024-01-15T10:30:15Z
Reason: ValidPipeline
Status: True
Type: ValidPipeline
Pipeline:
Output:
Certificate Expires: Sep 29 08:34:02 2025 GMT
Days Until Expiry: 54
Hostname: google.com
Ingress Name: example-app
Status: ok
Step: check-ingress-certificate
```
{{<hint "tip">}}
The `status.pipeline` field shows the output returned by each function step.
Use this field for tracking what the operation accomplished.
{{</hint>}}
Check that the `Operation` annotated the `Ingress` with certificate information:
```shell {copy-lines="1"}
kubectl get ingress example-app -o yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-monitor.crossplane.io/days-until-expiry: "54"
cert-monitor.crossplane.io/expires: Sep 29 08:34:02 2025 GMT
cert-monitor.crossplane.io/status: ok
name: example-app
namespace: default
spec:
# ... ingress spec ...
```
{{<hint "tip">}}
This pattern shows how `Operations` can both read and change existing Kubernetes
resources. The `Operation` annotated the `Ingress` with certificate expiry
information that other tools can use for monitoring and alerting.
{{</hint>}}
## Clean up
Delete the resources you created:
```shell
kubectl delete -f operation.yaml
kubectl delete -f ingress.yaml
kubectl delete -f ingress-rbac.yaml
kubectl delete -f function.yaml
```
## Next steps
`Operations` are powerful building blocks for operational workflows. Learn more
about:
* [**`Operation` concepts**]({{<ref "../operations/operation">}}) - Core
`Operation` features and best practices
* [**`CronOperation`**]({{<ref "../operations/cronoperation">}}) - Schedule
operations to run automatically
* [**`WatchOperation`**]({{<ref "../operations/watchoperation">}}) - Trigger
operations when resources change
Explore the complete [Operations documentation]({{<ref "../operations">}}) for
advanced features and examples.

View File

@ -0,0 +1,7 @@
---
title: Operations
weight: 52
state: alpha
alphaVersion: 2.0
description: Understand Crossplane's Operations feature
---

View File

@ -0,0 +1,345 @@
---
title: CronOperation
weight: 120
state: alpha
alphaVersion: 2.0
description: CronOperations create Operations on a schedule for recurring tasks
---
A `CronOperation` creates [Operations]({{<ref "operation">}}) on a schedule,
like Kubernetes CronJobs. Use CronOperations for recurring operational tasks
such as database backups, certificate rotation, or periodic maintenance.
<!-- vale Google.Headings = NO -->
## How CronOperations work
<!-- vale Google.Headings = YES -->
CronOperations contain a template for an Operation and create new Operations
based on a cron schedule. Each scheduled run creates a new Operation that
executes once to completion.
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: CronOperation
metadata:
name: daily-backup
spec:
schedule: "0 2 * * *" # Daily at 2 AM
concurrencyPolicy: Forbid
successfulHistoryLimit: 5
failedHistoryLimit: 3
operationTemplate:
spec:
mode: Pipeline
pipeline:
- step: backup
functionRef:
name: function-database-backup
input:
apiVersion: fn.crossplane.io/v1beta1
kind: DatabaseBackupInput
retentionDays: 7
```
{{<hint "important">}}
CronOperations are an alpha feature. You must enable Operations by adding
`--enable-operations` to Crossplane's arguments.
{{</hint>}}
## Key features
- **Standard cron scheduling syntax** - Uses the same format as Kubernetes CronJobs
- **Configurable concurrency policies** (Allow, Forbid, Replace)
- **Automatic cleanup of old Operations** - Maintains history limits
- **Tracks run history and running operations** - Provides visibility into scheduled runs
## Scheduling
CronOperations use standard cron syntax:
```console {linenos=false,copy-lines="none"}
┌───────────── minute (0 - 59)
│ ┌───────────── hour (0 - 23)
│ │ ┌───────────── day of the month (1 - 31)
│ │ │ ┌───────────── month (1 - 12)
│ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday)
│ │ │ │ │
│ │ │ │ │
* * * * *
```
**Common schedule examples:**
- `"0 2 * * *"` - Every day at 2:00 AM
- `"0 0 * * 0"` - Every Sunday at midnight
- `"0 0 1 * *"` - Every month on the first at midnight
- `"*/15 * * * *"` - Every 15 minutes
## Concurrency policies
CronOperations support three concurrency policies:
- **Allow (default)**: Multiple Operations can run simultaneously. Use this
when operations don't interfere with each other.
- **Forbid**: New Operations don't start if previous ones are still running.
Use this for operations that can't run concurrently.
- **Replace**: New Operations stop running ones before starting. Use this
when you always want the latest operation to run.
## History management
Control the number of completed Operations to keep:
```yaml
spec:
successfulHistoryLimit: 5 # Keep 5 successful operations
failedHistoryLimit: 3 # Keep 3 failed operations for debugging
```
This helps balance debugging capabilities with resource usage.
## Common use cases
{{<hint "note">}}
The following examples use hypothetical functions for illustration. At launch,
only function-python supports operations.
{{</hint>}}
### Scheduled database backups
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: CronOperation
metadata:
name: postgres-backup
spec:
schedule: "0 3 * * *" # Daily at 3 AM
concurrencyPolicy: Forbid # Don't allow overlapping backups
operationTemplate:
spec:
mode: Pipeline
pipeline:
- step: backup
functionRef:
name: function-postgres-backup
input:
apiVersion: fn.crossplane.io/v1beta1
kind: PostgresBackupInput
instance: production-db
s3Bucket: db-backups
```
### Scheduled maintenance
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: CronOperation
metadata:
name: weekly-maintenance
spec:
schedule: "0 3 * * 0" # Weekly on Sunday at 3 AM
operationTemplate:
spec:
mode: Pipeline
pipeline:
- step: cleanup-logs
functionRef:
name: function-log-cleanup
input:
apiVersion: fn.crossplane.io/v1beta1
kind: LogCleanupInput
retentionDays: 30
- step: update-certificates
functionRef:
name: function-cert-renewal
```
### Periodic health checks
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: CronOperation
metadata:
name: health-check
spec:
schedule: "*/30 * * * *" # Every 30 minutes
operationTemplate:
spec:
mode: Pipeline
pipeline:
- step: check-cluster-health
functionRef:
name: function-health-check
input:
apiVersion: fn.crossplane.io/v1beta1
kind: HealthCheckInput
alertThreshold: 80
```
## Advanced configuration
### Complex scheduling patterns
Advanced cron schedule examples for specific use cases:
```yaml
# Weekdays only at 9 AM (Monday-Friday)
schedule: "0 9 * * 1-5"
# Every 4 hours during business days
schedule: "0 8,12,16 * * 1-5"
# First and last day of each month
schedule: "0 2 1,L * *"
# Every quarter (1st of Jan, Apr, Jul, Oct)
schedule: "0 2 1 1,4,7,10 *"
# Business hours only, every 2 hours
schedule: "0 9-17/2 * * 1-5"
```
### Starting deadline
CronOperations support a `startingDeadlineSeconds` field that controls how
long to wait after the scheduled time before considering it too late to
create the Operation:
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: CronOperation
metadata:
name: deadline-example
spec:
schedule: "0 9 * * 1-5" # Weekdays at 9 AM
startingDeadlineSeconds: 900 # 15 minutes
operationTemplate:
spec:
mode: Pipeline
pipeline:
- step: morning-tasks
functionRef:
name: function-morning-tasks
```
If the Operation can't start in 15 minutes of 9 AM (due to
controller downtime, resource constraints, etc.), the scheduled run is
skipped.
Skip operations for:
- **Time-sensitive operations** - Skip operations that become meaningless if delayed
- **Resource protection** - Prevent backup Operations piling up during outages
- **SLA compliance** - Ensure operations run in acceptable time windows
### Time zone considerations
{{<hint "important">}}
CronOperations use the cluster's local time zone, same as Kubernetes CronJobs.
To ensure consistent scheduling across different environments, consider:
1. **Standardize cluster time zones** - Use UTC in production clusters
2. **Document time zone assumptions** - Note expected time zone in comments
3. **Account for DST changes** - Be aware that some schedules may skip or repeat during transitions
{{</hint>}}
## Status and monitoring
CronOperations provide status information about scheduling:
```yaml
status:
conditions:
- type: Synced
status: "True"
reason: ReconcileSuccess
- type: Scheduling
status: "True"
reason: ScheduleActive
lastScheduleTime: "2024-01-15T10:00:00Z"
lastSuccessfulTime: "2024-01-15T10:02:30Z"
runningOperationRefs:
- name: daily-backup-1705305600
```
**Key status fields:**
- **Conditions**: Standard Crossplane conditions (Synced) and CronOperation-specific conditions:
- **Scheduling**: `True` when the CronOperation is actively scheduling operations, `False` when paused or has incorrect schedule syntax
- **`lastScheduleTime`**: When the CronOperation last created an Operation
- **`lastSuccessfulTime`**: When an Operation last completed successfully
- **`runningOperationRefs`**: Running Operations
### Events
CronOperations emit events for important activities:
- `CreateOperation` (Warning) - Scheduled operation creation failures
- `GarbageCollectOperations` (Warning) - Garbage collection failures
- `ReplaceRunningOperation` (Warning) - Running operation deletion failures
- `InvalidSchedule` (Warning) - Cron schedule parsing errors
<!-- vale write-good.TooWordy = NO -->
### Monitoring
<!-- vale write-good.TooWordy = YES -->
<!-- vale write-good.TooWordy = NO -->
Monitor CronOperations using:
<!-- vale write-good.TooWordy = YES -->
```shell
# Check CronOperation status
kubectl get cronoperation my-cronop
# View recent Operations created by the CronOperation
kubectl get operations -l crossplane.io/cronoperation=my-cronop
# Check events
kubectl get events --field-selector involvedObject.name=my-cronop
```
## Best practices
### Scheduling considerations
1. **Consider time zones** - CronOperations use the host's local time
(same as Kubernetes CronJobs)
1. **Plan for long-running operations** - Ensure operations complete before
next scheduled run
1. **Set reasonable history limits** - Balance debugging needs with cluster
resource usage
### Concurrency policies
1. **Choose appropriate concurrency policies**:
- **Forbid** for backups, maintenance, or operations that must complete
alone
- **Replace** for health checks or monitoring where latest data is most
important
- **Allow** for independent tasks that can run simultaneously
For general Operations best practices including function development and
operational considerations, see [Operation best practices]({{<ref "operation#best-practices">}}).
## Troubleshooting
<!-- vale Google.Headings = NO -->
### CronOperation not creating Operations
<!-- vale Google.Headings = YES -->
1. Check the cron schedule syntax
1. Verify the CronOperation has `Synced=True` condition
1. Look for events indicating schedule parsing errors
### Operations failing often
1. Check Operation events and logs
1. Verify function capabilities include `operation`
1. Review retry limits and adjust as needed
### Resource cleanup issues
1. Verify you set history limits appropriately
1. Check for events about garbage collection failures
## Next steps
- Learn about [Operation]({{<ref "operation">}}) for one-time operational tasks
- Learn about [WatchOperation]({{<ref "watchoperation">}}) for reactive operations
- [Get started with Operations]({{<ref "../get-started/get-started-with-operations">}}) to try scheduling your first operation

View File

@ -0,0 +1,592 @@
---
title: Operation
weight: 110
state: alpha
alphaVersion: 2.0
description: Operations run function pipelines once to completion for operational tasks
---
An `Operation` runs a function pipeline once to completion to perform operational
tasks that don't fit the typical resource creation pattern. Unlike compositions
that continuously reconcile desired state, Operations focus on tasks like
backups, rolling upgrades, configuration validation, and scheduled maintenance.
## How operations work
Operations are like Kubernetes Jobs - they run once to completion rather than
continuously reconciling. Like compositions, Operations use function pipelines
to implement their logic, but they're designed for operational workflows
instead of resource composition.
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: Operation
metadata:
name: backup-database
spec:
mode: Pipeline
pipeline:
- step: create-backup
functionRef:
name: function-database-backup
input:
apiVersion: fn.crossplane.io/v1beta1
kind: DatabaseBackupInput
database: production-db
retentionDays: 30
```
When you create this Operation, Crossplane:
1. **Validates** the operation and its function dependencies
2. **Executes** the function pipeline step by step
3. **Applies** any resources the functions create or change
4. **Updates** the Operation status with results and completion state
{{<hint "important">}}
Operations are an alpha feature. You must enable them by adding
`--enable-operations` to Crossplane's arguments.
{{</hint>}}
## Key characteristics
- **Runs once to completion** (like Kubernetes Jobs)
- **Uses function pipelines** (like Compositions)
- **Can create or change any Kubernetes resources**
- **Provides detailed status and output from each step**
- **Supports retry on failure with configurable limits**
## Operation functions vs composition functions
Operations and compositions both use function pipelines, but with important
differences:
**Composition Functions:**
- **Purpose**: Create and maintain resources
- **Lifecycle**: Continuous reconciliation
- **Input**: Observed composite resources
- **Output**: Desired composed resources
- **Ownership**: Creates owner references
**Operation Functions:**
- **Purpose**: Perform operational tasks
- **Lifecycle**: Run once to completion
- **Input**: Required resources only
- **Output**: Any Kubernetes resources
- **Ownership**: Force applies without owners
Functions can support both modes by declaring the appropriate capabilities in
their package metadata. Function authors declare this in the `crossplane.yaml`
file when building the function package:
```yaml
apiVersion: meta.pkg.crossplane.io/v1
kind: Function
metadata:
name: my-function
spec:
capabilities:
- composition
- operation
```
This allows Crossplane to know which modes the function supports and avoid
trying to use a composition-only function for operations.
## Common use cases
{{<hint "note">}}
The following examples use hypothetical functions for illustration. At launch,
only function-python supports operations.
{{</hint>}}
### Rolling upgrades
Use Operations for controlled rolling upgrades:
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: Operation
metadata:
name: cluster-upgrade
spec:
mode: Pipeline
pipeline:
- step: rolling-upgrade
functionRef:
name: function-cluster-upgrade
input:
apiVersion: fn.crossplane.io/v1beta1
kind: ClusterUpgradeInput
targetVersion: "1.28"
batches: [0.25, 0.5, 1.0] # 25%, 50%, then 100%
healthChecks: [Synced, Ready]
```
### One-time maintenance
Use Operations for specific maintenance tasks:
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: Operation
metadata:
name: certificate-rotation
spec:
mode: Pipeline
pipeline:
- step: rotate-certificates
functionRef:
name: function-cert-rotation
input:
apiVersion: fn.crossplane.io/v1beta1
kind: CertRotationInput
targetCertificates:
matchLabels:
rotate: "true"
```
## Advanced configuration
### Retry behavior
Operations automatically retry when they fail. Configure the retry limit to
control how often attempts occur:
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: Operation
metadata:
name: resilient-operation
spec:
retryLimit: 10 # Try up to 10 times before giving up (default: 5)
mode: Pipeline
pipeline:
- step: flaky-task
functionRef:
name: function-flaky-task
input:
apiVersion: fn.crossplane.io/v1beta1
kind: FlakyTaskInput
# Task that might fail due to temporary issues
timeout: "30s"
```
**Retry behavior:**
- Each retry resets the entire pipeline - if step 2 of 3 fails, the retry
starts from step 1
- Operations use exponential backoff: 1&nbsp;s, 2&nbsp;s, 4&nbsp;s, 8&nbsp;s, 16&nbsp;s, 32&nbsp;s, then 60&nbsp;s
max
- Operations track the number of failures in `status.failures`
- After reaching `retryLimit`, the Operation becomes
`Succeeded=False`
### Credentials
Operations can provide credentials to functions through Secrets:
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: Operation
metadata:
name: secure-backup
spec:
mode: Pipeline
pipeline:
- step: backup-with-credentials
functionRef:
name: function-backup
credentials:
- name: backup-creds
source: Secret
secretRef:
namespace: crossplane-system
name: backup-credentials
key: api-key
- name: database-creds
source: Secret
secretRef:
namespace: crossplane-system
name: database-credentials
key: connection-string
input:
apiVersion: fn.crossplane.io/v1beta1
kind: BackupInput
destination: s3://my-backup-bucket
```
### Multiple pipeline steps
Complex operations can use multiple pipeline steps:
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: Operation
metadata:
name: multi-step-deployment
spec:
mode: Pipeline
pipeline:
- step: validate-config
functionRef:
name: function-validator
input:
apiVersion: fn.crossplane.io/v1beta1
kind: ValidatorInput
configName: app-config
- step: backup-current
functionRef:
name: function-backup
input:
apiVersion: fn.crossplane.io/v1beta1
kind: BackupInput
target: current-deployment
- step: deploy-new-version
functionRef:
name: function-deploy
input:
apiVersion: fn.crossplane.io/v1beta1
kind: DeployInput
image: myapp:v2.0.0
strategy: rollingUpdate
- step: verify-health
functionRef:
name: function-health-check
input:
apiVersion: fn.crossplane.io/v1beta1
kind: HealthCheckInput
timeout: 300s
healthEndpoint: /health
```
### RBAC permissions
If your Operation needs to access resources that Crossplane doesn't have
permissions for by default, create a ClusterRole that aggregates to
Crossplane:
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: operation-additional-permissions
labels:
rbac.crossplane.io/aggregate-to-crossplane: "true"
rules:
# Additional permissions for Operations
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "list", "patch", "update"]
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list"]
# Add other resources your Operations need to access
```
This ClusterRole is automatically aggregated to Crossplane's main
ClusterRole, giving the Crossplane service account the permissions needed
for your Operations.
{{<hint "note">}}
The [RBAC manager]({{<ref "../guides/pods#rbac-manager-pod">}}) automatically
grants Crossplane access to Crossplane resources (MRs, XRs, etc.). You
only need to create more ClusterRoles for other Kubernetes resources
that your Operations need to access.
For more details on RBAC configuration, see the
[Compositions RBAC documentation]({{<ref "../composition/compositions#grant-access-to-composed-resources">}}).
{{</hint>}}
### Required resources
Operations can preload resources for functions to access:
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: Operation
metadata:
name: resource-aware-operation
spec:
mode: Pipeline
pipeline:
- step: process-deployment
functionRef:
name: function-processor
requirements:
requiredResources:
- requirementName: app-deployment
apiVersion: apps/v1
kind: Deployment
name: my-app
namespace: production
- requirementName: app-service
apiVersion: v1
kind: Service
name: my-app-service
namespace: production
input:
apiVersion: fn.crossplane.io/v1beta1
kind: ProcessorInput
action: upgrade
```
Functions access these resources through the standard request structure:
```python
from crossplane.function import request, response
def operate(req, rsp):
# Access required resources
deployment = request.get_required_resource(req, "app-deployment")
service = request.get_required_resource(req, "app-service")
if not deployment or not service:
response.set_output(rsp, {"error": "Required resources not found"})
return
# Process the resources
new_replicas = deployment["spec"]["replicas"] * 2
# Return updated resources with full GVK and metadata for server-side apply
rsp.desired.resources["app-deployment"].resource.update({
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": deployment["metadata"]["name"],
"namespace": deployment["metadata"]["namespace"]
},
"spec": {"replicas": new_replicas}
})
```
## Status and monitoring
Operations provide rich status information:
```yaml
status:
conditions:
- type: Synced
status: "True"
reason: ReconcileSuccess
- type: Succeeded
status: "True"
reason: PipelineSuccess
- type: ValidPipeline
status: "True"
reason: ValidPipeline
failures: 1 # Number of retry attempts
pipeline:
- step: create-backup
output:
backupId: "backup-20240115-103000"
size: "2.3GB"
appliedResourceRefs:
- apiVersion: "v1"
kind: "Secret"
namespace: "production"
name: "backup-secret"
- apiVersion: "apps/v1"
kind: "Deployment"
name: "updated-deployment"
```
**Key status fields:**
- **`conditions`**: Standard Crossplane conditions (Synced) and Operation-specific conditions:
- **`Succeeded`**: `True` when the operation completed successfully, `False` when it failed
- **`ValidPipeline`**: `True` when all functions have the required `operation` capability
- **`failures`**: Number of times the operation has failed and retried
- **`pipeline`**: Output from each function step for tracking progress
- **`appliedResourceRefs`**: References to all resources the Operation created or modified
### Events
Operations emit Kubernetes events for important activities:
- Function run results and warnings
- Resource apply failures
- Operation lifecycle events (creation, completion, failure)
### Troubleshooting operations
**Check operation status:**
```shell
kubectl get operation my-operation -o wide
```
**View detailed information:**
```shell
kubectl describe operation my-operation
```
**Common failure scenarios:**
1. **Operations do nothing** - Operations feature not enabled:
```yaml
# Operation exists but has no status conditions and never progresses
status: {}
```
*Solution*: enable Operations by adding `--enable-operations` to Crossplane's startup arguments.
2. **ValidPipeline condition is False** - Function doesn't support operations:
```yaml
conditions:
- type: ValidPipeline
status: "False"
reason: InvalidFunctionCapability
message: "Function function-name doesn't support operations"
```
*Solution*: use a function that declares `operation` capability.
3. **Succeeded condition is False** - Function run failed:
```yaml
conditions:
- type: Succeeded
status: "False"
reason: PipelineFailure
message: "Function returned error: connection timeout"
```
*Solution*: view function logs and fix the underlying issue.
4. **Resource apply failures** - View events for details:
```shell
kubectl get events --field-selector involvedObject.name=my-operation
```
**Debug function runs:**
```shell
# View function logs
kubectl logs -n crossplane-system deployment/function-python
# Check operation events
kubectl get events --field-selector involvedObject.kind=Operation
# Inspect operation status in detail
kubectl get operation my-operation -o jsonpath='{.status.pipeline}' | jq '.'
```
## Resource management
Operations can create or change any Kubernetes resources using server-side
apply with force ownership. This means:
**What Operations can do:**
- Create new resources of any kind
- Change existing resources by taking ownership of specific fields
- Apply changes that may conflict with other controllers
**What Operations can't do:**
- Delete resources (current limitation of alpha implementation)
- Establish owner references (resources aren't garbage collected)
- Continuously maintain desired state (they run once)
{{<hint "important">}}
Use caution with Operations that change resources managed by other controllers.
Operations force ownership when applying changes, which can cause conflicts.
{{</hint>}}
## Test an operation
You can preview the output of any Operation using the Crossplane CLI. You
don't need a Crossplane control plane to do this. The Crossplane CLI uses Docker
Engine to run functions.
{{<hint "tip">}}
See the [Crossplane CLI docs]({{<ref "../cli">}}) to
learn how to install and use the Crossplane CLI.
{{< /hint >}}
{{<hint "important">}}
Running `crossplane alpha render op` requires [Docker](https://www.docker.com).
{{< /hint >}}
Provide an operation, composition functions, and any required resources to render
the output locally.
```shell
crossplane alpha render op operation.yaml functions.yaml --required-resources=ingress.yaml
```
`crossplane alpha render op` prints the Operation status and any resources the
operation functions created or modified. It shows what would happen if you
applied the Operation to a cluster.
```yaml
---
# Operation status showing function results
apiVersion: ops.crossplane.io/v1alpha1
kind: Operation
metadata:
name: ingress-cert-monitor
status:
conditions:
- type: Succeeded
status: "True"
reason: PipelineSuccess
pipeline:
- step: check-ingress-certificate
output:
certificateExpires: "Sep 29 08:34:02 2025 GMT"
daysUntilExpiry: 53
hostname: google.com
ingressName: example-app
status: ok
---
# Modified Ingress resource with certificate annotations
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-monitor.crossplane.io/expires: Sep 29 08:34:02 2025 GMT
cert-monitor.crossplane.io/days-until-expiry: "53"
cert-monitor.crossplane.io/status: ok
name: example-app
namespace: default
spec:
# ... ingress spec unchanged
```
Use `--required-resources` to provide resources that your operation functions
need access to. You can specify multiple files or use glob patterns:
```shell
# Multiple specific files
crossplane alpha render op operation.yaml functions.yaml \
--required-resources=deployment.yaml,service.yaml,configmap.yaml
# Glob pattern for all YAML files in a directory
crossplane alpha render op operation.yaml functions.yaml \
--required-resources="resources/*.yaml"
```
{{<hint "tip">}}
Use the `crossplane alpha render op` command to test your Operations locally
before deploying them to a cluster. The command helps validate function logic
and required resource access patterns.
{{</hint>}}
## Best practices
### Operation-specific practices
1. **Plan for rollback** - Design operations to be reversible when possible,
because Operations don't auto rollback like Compositions
1. **Make operations idempotent** - Operations should be safe to retry if they
fail partway through
1. **Use required resources** - Prepopulate functions with needed resources for
efficiency rather than requesting them during running
### Function development
1. **Declare capabilities** - Explicitly declare `operation` capability in
function metadata to enable Operations support
1. **Return meaningful output** - Use the output field to track what the
operation accomplished for monitoring and debugging
## Next steps
- [Get started with Operations]({{<ref "../get-started/get-started-with-operations">}}) to create your first Operation
- Learn about [CronOperation]({{<ref "cronoperation">}}) for scheduled operations
- Learn about [WatchOperation]({{<ref "watchoperation">}}) for reactive operations

View File

@ -0,0 +1,599 @@
---
title: WatchOperation
weight: 130
state: alpha
alphaVersion: 2.0
description: WatchOperations create Operations when watched resources change
---
A `WatchOperation` creates [Operations]({{<ref "operation">}}) when watched
Kubernetes resources change. Use WatchOperations for reactive operational
workflows such as backing up databases before deletion, validating
configurations after updates, or triggering alerts when resources fail.
<!-- vale Google.Headings = NO -->
## How WatchOperations work
<!-- vale Google.Headings = YES -->
WatchOperations watch specific Kubernetes resources and create new Operations
whenever those resources change. The changed resource is automatically injected
into the Operation for the function to process.
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: WatchOperation
metadata:
name: config-validator
spec:
watch:
apiVersion: v1
kind: ConfigMap
matchLabels:
validate: "true"
concurrencyPolicy: Allow
operationTemplate:
spec:
mode: Pipeline
pipeline:
- step: validate
functionRef:
name: function-config-validator
input:
apiVersion: fn.crossplane.io/v1beta1
kind: ConfigValidatorInput
rules:
- required: ["database.url", "database.port"]
- format: "email"
field: "notification.email"
- step: notify
functionRef:
name: function-slack-notifier
input:
apiVersion: fn.crossplane.io/v1beta1
kind: SlackNotifierInput
channel: "#alerts"
severity: "warning"
```
{{<hint "important">}}
WatchOperations are an alpha feature. You must enable Operations by adding
`--enable-operations` to Crossplane's arguments.
{{</hint>}}
## Key features
- **Watches any Kubernetes resource type** - Not limited to Crossplane resources
- **Supports namespace and label filtering** - Target specific resources
- **Automatically injects changed resources** - Functions receive the triggering resource
- **Configurable concurrency policies** - Control operation creation
## Resource watching
WatchOperations can watch any Kubernetes resource with flexible filtering:
### Watch all resources of a type
```yaml
spec:
watch:
apiVersion: apps/v1
kind: Deployment
```
### Watch resources in a specific namespace
```yaml
spec:
watch:
apiVersion: v1
kind: ConfigMap
namespace: production
```
### Watch resources with specific labels
```yaml
spec:
watch:
apiVersion: example.org/v1
kind: Database
matchLabels:
backup: "enabled"
environment: "production"
```
### Watch cluster-scoped resources
```yaml
spec:
watch:
apiVersion: v1
kind: Node
matchLabels:
node-role.kubernetes.io/worker: ""
```
## Resource injection
<!-- vale write-good.TooWordy = NO -->
When a WatchOperation creates an Operation, it automatically injects the changed
resource using the special requirement name
`ops.crossplane.io/watched-resource`. Functions can access this resource without
explicitly requesting it.
<!-- vale write-good.TooWordy = YES -->
For example, when a ConfigMap with label `validate: "true"` changes, the
WatchOperation creates an Operation like this:
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: Operation
metadata:
name: config-validator-abc123
spec:
mode: Pipeline
pipeline:
- step: validate
functionRef:
name: function-config-validator
requirements:
requiredResources:
- requirementName: ops.crossplane.io/watched-resource
apiVersion: v1
kind: ConfigMap
name: my-config
namespace: default
# ... other pipeline steps from operationTemplate
```
The watched resource is automatically available to functions in
`req.required_resources` under the special name
`ops.crossplane.io/watched-resource`.
## Concurrency policies
WatchOperations support the same concurrency policies as CronOperations:
- **Allow (default)**: Multiple Operations can run simultaneously. Use this
when operations don't interfere with each other.
- **Forbid**: New Operations don't start if previous ones are still running.
Use this for operations that can't run concurrently.
- **Replace**: New Operations stop running ones before starting. Use this
when you always want the latest operation to run.
## Common use cases
{{<hint "note">}}
The following examples use hypothetical functions for illustration. At launch,
only function-python supports operations.
{{</hint>}}
### Configuration validation
Validate ConfigMaps when they change:
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: WatchOperation
metadata:
name: config-validator
spec:
watch:
apiVersion: v1
kind: ConfigMap
matchLabels:
validate: "true"
operationTemplate:
spec:
mode: Pipeline
pipeline:
- step: validate-config
functionRef:
name: function-config-validator
input:
apiVersion: fn.crossplane.io/v1beta1
kind: ConfigValidatorInput
rules:
- required: ["database.host", "database.port"]
- format: "email"
field: "notification.email"
```
### Database backup on deletion
Backup databases before they're deleted:
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: WatchOperation
metadata:
name: backup-on-deletion
spec:
watch:
apiVersion: rds.aws.crossplane.io/v1alpha1
kind: Instance
# Note: Watching for deletion requires function logic
# to check deletion timestamp
operationTemplate:
spec:
mode: Pipeline
pipeline:
- step: create-backup
functionRef:
name: function-rds-backup
input:
apiVersion: fn.crossplane.io/v1beta1
kind: RDSBackupInput
retentionDays: 30
```
### Resource failure alerting
Alert when resources enter a failed state:
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: WatchOperation
metadata:
name: failure-alerts
spec:
watch:
apiVersion: example.org/v1
kind: App
matchLabels:
alert: "enabled"
operationTemplate:
spec:
mode: Pipeline
pipeline:
- step: check-status
functionRef:
name: function-status-checker
input:
apiVersion: fn.crossplane.io/v1beta1
kind: StatusCheckerInput
alertConditions:
- type: "Ready"
status: "False"
- step: send-alert
functionRef:
name: function-alertmanager
input:
apiVersion: fn.crossplane.io/v1beta1
kind: AlertInput
severity: "critical"
```
## Advanced configuration
### Advanced watch patterns
Complex resource watching with multiple conditions:
```yaml
# Watch Deployments in specific namespaces with multiple label conditions
apiVersion: ops.crossplane.io/v1alpha1
kind: WatchOperation
metadata:
name: multi-condition-watcher
spec:
watch:
apiVersion: apps/v1
kind: Deployment
namespace: production # Only production namespace
matchLabels:
app.kubernetes.io/managed-by: "crossplane"
environment: "prod"
backup-required: "true"
operationTemplate:
spec:
mode: Pipeline
pipeline:
- step: backup-deployment
functionRef:
name: function-deployment-backup
```
```yaml
# Watch custom resources across all namespaces
apiVersion: ops.crossplane.io/v1alpha1
kind: WatchOperation
metadata:
name: database-lifecycle-manager
spec:
watch:
apiVersion: database.example.io/v1
kind: PostgreSQLInstance
# No namespace specified = watch all namespaces
matchLabels:
lifecycle-management: "enabled"
operationTemplate:
spec:
mode: Pipeline
pipeline:
- step: lifecycle-check
functionRef:
name: function-database-lifecycle
input:
apiVersion: fn.crossplane.io/v1beta1
kind: DatabaseLifecycleInput
checkDeletionTimestamp: true
autoBackup: true
```
### Cross-resource workflows
WatchOperations can watch one resource type and dynamically fetch related
resources. Here's a WatchOperation that watches Ingresses and manages
certificates:
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: WatchOperation
metadata:
name: ingress-certificate-manager
spec:
watch:
apiVersion: networking.k8s.io/v1
kind: Ingress
matchLabels:
auto-cert: "enabled"
operationTemplate:
spec:
mode: Pipeline
pipeline:
- step: manage-certificates
functionRef:
name: function-cert-manager
input:
apiVersion: fn.crossplane.io/v1beta1
kind: CertManagerInput
issuer: "letsencrypt-prod"
renewBefore: "720h" # 30 days
```
The function examines the watched Ingress and dynamically requests related
resources:
```python
from crossplane.function import request, response
def operate(req, rsp):
# Access the watched Ingress resource
ingress = request.get_required_resource(req, "ops.crossplane.io/watched-resource")
if not ingress:
response.fatal(rsp, "No watched resource found")
return
# Extract the service name from the Ingress backend
rules = ingress.get("spec", {}).get("rules", [])
if not rules:
response.fatal(rsp, "Could not extract service name from ingress")
return
backend = rules[0].get("http", {}).get("paths", [{}])[0].get("backend", {})
service_name = backend.get("service", {}).get("name")
if not service_name:
response.fatal(rsp, "Could not extract service name from ingress")
return
ingress_namespace = ingress.get("metadata", {}).get("namespace", "default")
# CRITICAL: Always request the same resources to ensure requirement
# stabilization. Crossplane calls the function repeatedly until
# requirements don't change.
response.require_resources(
rsp,
name="related-service",
api_version="v1",
kind="Service",
match_name=service_name,
namespace=ingress_namespace
)
# Check if the service is available and process accordingly
service = request.get_required_resource(req, "related-service")
if service:
# Success: Both resources available
response.set_output(rsp, {
"status": "success",
"message": "Certificate management completed",
"ingress_host": ingress.get("spec", {}).get("rules", [{}])[0].get("host"),
"service_name": service.get("metadata", {}).get("name")
})
return
# Waiting: Service not available yet
response.set_output(rsp, {
"status": "waiting",
"message": f"Waiting for service '{service_name}' to be available"
})
```
{{<hint "important">}}
**Critical resource stabilization pattern**: functions must return the **same
requirements** in each iteration to signal completion. The function in the
preceding example always calls `response.require_resources()` regardless of
whether the service exists. This ensures Crossplane knows when to stop calling
the function.
Common mistake: only requesting resources when missing breaks the stabilization
contract and causes timeout errors.
{{</hint>}}
This pattern allows functions to:
1. Examine the watched resource (injected automatically)
2. Dynamically determine what other resources the function needs
3. Request those resources consistently using `response.require_resources()`
4. Process all resources when available, or provide status when waiting
## Status and monitoring
WatchOperations provide status information about watching:
```yaml
status:
conditions:
- type: Synced
status: "True"
reason: ReconcileSuccess
- type: Watching
status: "True"
reason: WatchActive
watchingResources: 12
runningOperationRefs:
- name: config-validator-anjda
- name: config-validator-f0d92
```
**Key status fields:**
- **Conditions**: Standard Crossplane conditions (Synced) and WatchOperation-specific conditions:
- **Watching**: `True` when the WatchOperation is actively watching resources, `False` when paused or failed
- **`watchingResources`**: Number of resources under watch
- **`runningOperationRefs`**: Running Operations created by this WatchOperation
### Events
WatchOperations emit events for important activities:
- `EstablishWatched` (Warning) - Watch establishment failures
- `TerminateWatched` (Warning) - Watch termination failures
- `GarbageCollectOperations` (Warning) - Operation cleanup failures
- `CreateOperation` (Warning) - Operation creation failures
- `ReplaceRunningOperation` (Warning) - Operation replacement failures
<!-- vale write-good.TooWordy = NO -->
### Monitoring
<!-- vale write-good.TooWordy = YES -->
<!-- vale write-good.TooWordy = NO -->
Monitor WatchOperations using:
<!-- vale write-good.TooWordy = YES -->
```shell
# Check WatchOperation status
kubectl get watchoperation my-watchop
# View recent Operations created by the WatchOperation
kubectl get operations -l crossplane.io/watchoperation=my-watchop
# Check watched resource count
kubectl describe watchoperation my-watchop
# Check events
kubectl get events --field-selector involvedObject.name=my-watchop
```
## Best practices
### Resource selection
1. **Use specific label selectors** - Prevent unnecessary Operations with
precise filtering
1. **Avoid high-churn resources** - Be careful watching frequently changing
resources
1. **Start small** - Begin with narrow selectors and expand as needed
### Event handling
<!-- vale write-good.TooWordy = NO -->
1. **Implement event filtering** - Check generation, deletion timestamp,
and status conditions
to avoid processing irrelevant changes
1. **Monitor operation volume** - Popular resources can create numerous
Operations
<!-- vale write-good.TooWordy = YES -->
### Concurrency policies
1. **Choose appropriate concurrency policies**:
- **Allow** for independent processing that can run in parallel
- **Forbid** for operations that must complete before processing new
changes
- **Replace** for status-checking or monitoring where only latest state
matters
### History management
Like CronOperations, WatchOperations automatically clean up completed Operations:
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: WatchOperation
metadata:
name: config-validator
spec:
watch:
apiVersion: v1
kind: ConfigMap
successfulHistoryLimit: 10 # Keep 10 successful Operations (default: 3)
failedHistoryLimit: 5 # Keep 5 failed Operations (default: 1)
operationTemplate:
# Operation template here
```
### Watched resource injection
<!-- vale write-good.TooWordy = NO -->
WatchOperations automatically inject the changed resource into the created
Operation using a special requirement name
`ops.crossplane.io/watched-resource`:
<!-- vale write-good.TooWordy = YES -->
```python
from crossplane.function import request, response
def operate(req, rsp):
# Access the resource that triggered this Operation
watched_resource = request.get_required_resource(req, "ops.crossplane.io/watched-resource")
if not watched_resource:
response.set_output(rsp, {"error": "No watched resource found"})
return
# Process based on the watched resource
if watched_resource["kind"] == "ConfigMap":
config_data = watched_resource["data"]
# Validate configuration...
```
The watched resource is available in the function's `required_resources` map
without needing to declare it in the Operation template.
For general Operations best practices including function development and
operational considerations, see [Operation best practices]({{<ref "operation#best-practices">}}).
## Troubleshooting
<!-- vale Google.Headings = NO -->
### WatchOperation not creating Operations
<!-- vale Google.Headings = YES -->
1. Verify the WatchOperation has `Watching=True` condition
1. Check that watched resources exist and match the selector
1. Ensure resources are actually changing
1. Look for events indicating watch establishment failures
<!-- vale Google.Headings = NO -->
<!-- vale write-good.Weasel = NO -->
### Too many Operations created
<!-- vale write-good.Weasel = YES -->
<!-- vale Google.Headings = YES -->
1. Refine label selectors to match fewer resources
1. Consider using `Forbid` or `Replace` concurrency policy
1. Check if resources are changing more frequently than expected
1. Review function logic to ensure it's not causing resource updates
### Operations failing to process watched resources
1. Verify function capabilities include `operation`
1. Check that functions handle the `ops.crossplane.io/watched-resource`
1. Review function logs for processing errors
1. Ensure functions can handle the specific resource types under watch
## Next steps
- Learn about [Operation]({{<ref "operation">}}) for one-time operational tasks
- Learn about [CronOperation]({{<ref "cronoperation">}}) for scheduled operations
- [Get started with Operations]({{<ref "../get-started/get-started-with-operations">}}) to create your first reactive operation

View File

@ -46,13 +46,14 @@ involved in writing a controller.
## Crossplane components
Crossplane has three major components:
Crossplane has four major components:
* [Composition](#composition)
* [Managed resources](#managed-resources)
* [Operations](#operations)
* [Package manager](#package-manager)
You can use all three components to build your control plane, or pick only the
You can use all four components to build your control plane, or pick only the
ones you need.
### Composition
@ -223,6 +224,68 @@ GCP, Terraform, Helm, GitHub, etc to support Crossplane v2 soon.
<!-- vale gitlab.FutureTense = YES -->
{{</hint>}}
### Operations
Operations let you run operational tasks using function pipelines.
While composition and managed resources focus on creating and managing
infrastructure, operations handle tasks that don't fit the typical resource
creation pattern - like certificate monitoring, rolling upgrades, or scheduled
maintenance.
**Operations run function pipelines to completion like a Kubernetes Job.**
Instead of continuously managing resources, they perform specific tasks and
report the results.
<!-- vale Google.WordList = NO -->
Say you want your control plane to watch SSL certificates on Kubernetes
`Ingress` resources. When someone creates an Operation, the control plane
should check the certificate and annotate the `Ingress` with expiry information.
<!-- vale Google.WordList = YES -->
```mermaid
flowchart TD
user(User)
subgraph control [Control Plane]
operation(SSL Monitor Operation)
subgraph crossplane [Operation Engine]
fn(Python Function)
end
ingress(Ingress API)
end
subgraph ext [External System]
cert(SSL Certificate)
end
user -- create --> operation
crossplane watch@<-- watch --> operation
crossplane -- read --> ingress
crossplane -- check --> cert
crossplane -- annotate --> ingress
watch@{animate: true}
```
Operations support three modes:
* **Operation** - Run once to completion
* **CronOperation** - Run on a scheduled basis
* **WatchOperation** - Run when resources change
You can use operations alongside composition and managed resources to build
complete operational workflows for your control plane.
Follow [Get Started with Operations]({{<ref "../get-started/get-started-with-operations">}})
to see how operations work.
{{<hint "note">}}
Operations are an alpha feature available in Crossplane v2.
{{</hint>}}
### Package manager
The Crossplane package manager lets you install new managed resources and

View File

@ -6,11 +6,12 @@ description: Learn what's new in the Crossplane v2 preview
**Crossplane v2 makes Crossplane more useful, more intuitive, and less
opinionated.**
Crossplane v2 makes three major changes:
Crossplane v2 makes four major changes:
* **Composite resources are now namespaced**
* **Managed resources are now namespaced**
* **Composition supports any Kubernetes resource**
* **Operations enable operational workflows**
**Crossplane v2 is better suited to building control planes for applications,
not just infrastructure.** It removes the need for awkward abstractions like
@ -214,6 +215,48 @@ resources like MRs or XRs. Read
to learn how to grant Crossplane access.
{{</hint>}}
## Operations enable operational workflows
Crossplane v2 introduces Operations - a new way to run operational tasks using
function pipelines.
Operations handle tasks that don't fit the typical resource creation pattern.
Things like certificate monitoring, rolling upgrades, scheduled maintenance, or
responding to resource changes.
**Operations run function pipelines to completion, like a Kubernetes Job.**
Instead of continuously managing resources, they perform specific tasks and
report the results.
```yaml
apiVersion: ops.crossplane.io/v1alpha1
kind: CronOperation
metadata:
name: cert-monitor
spec:
schedule: "0 6 * * *" # Daily at 6 AM
mode: Pipeline
pipeline:
- step: check-certificates
functionRef:
name: crossplane-contrib-function-python
# function checks SSL certificates and reports status
```
Operations support three modes:
* **Operation** - Run once to completion
* **CronOperation** - Run on a scheduled basis
* **WatchOperation** - Run when resources change
Operations can read existing resources and optionally change them. This enables
workflows like annotating resources with operational data, triggering
maintenance tasks, or implementing custom operational policies.
{{<hint "note">}}
Operations are an alpha feature in Crossplane v2.
{{</hint>}}
## Backward compatibility
Crossplane v2 makes the following breaking changes:

View File

@ -14,13 +14,16 @@ CEL
CI
CLI
cloud-native
cluster-scoped
cluster-wide
ClusterRole
ClusterRoles
cluster-scoped
cluster-wide
command-line
ConfigMap
ConfigMaps
CRD
cron
CronJobs
crt
CSS
CUE
@ -39,6 +42,7 @@ float64
GitOps
Go
gRPC
hostname
IAM
imagePullSecret
init.sh
@ -46,12 +50,12 @@ IRSA
JSONPath
key-pair
key-value
KV
kv
kube-apiserver
kube-controller-manager
kubeconfig
kube-controller-manager
kubectl
kv
KV
metrics-server
minikube
multi-platform
@ -62,8 +66,9 @@ NOTES.txt
OCI
OIDC
PersistentVolumeClaim
Pre-releases
Prepopulate
pre-releases
Pre-releases
PriorityClass
proselint
protobuf
@ -101,5 +106,6 @@ TLS
tolerations
UI
VM
walkthrough
webhooks.enabled
YAML
YAML

View File

@ -39,4 +39,4 @@ Velero
VSCode
Webpack
write-good
Zendesk
Zendesk

View File

@ -8,25 +8,30 @@ CombineFromComposite
CombineFromEnvironment
CombineToComposite
CombineToEnvironment
composition.yaml
CompositeResourceDefinition
CompositeResourceDefinitions
composition-only
CompositionRevision
CompositionRevisions
composition.yaml
config
Configs
CONTRIBUTING.md
ControllerConfig
ControllerConfigs
CRDs
CRs
CronJobs
CronOperation
CronOperations
CronOperation-specific
Crossplane
crossplane-admin
crossplane-browse
crossplane-edit
Crossplane's
crossplane-view
crossplane.yaml
Crossplane's
CRs
CUE
definition.yaml
deletionPolicy
@ -45,6 +50,7 @@ function-environment-configs
function-extra-resources
function-go-templating
function-patch-and-transform
function-python
function-template-python
HealthyPackageRevision
Helm-like
@ -57,6 +63,7 @@ LateInitialize
managementPolicies
MR
MRs
Operation-specific
PatchSet
PatchSets
ProviderConfig
@ -65,6 +72,7 @@ ProviderRevision
RunFunctionRequest
RunFunctionResponse
Sigstore
SSL
StoreConfig
StoreConfigs
ToCompositeFieldPath
@ -74,15 +82,19 @@ TrimPrefix
TrimSuffix
UnhealthyPackageRevision
UnknownPackageRevisionHealth
ValidPipeline
WatchOperation
WatchOperations
WatchOperation-specific
XCluster
XNetwork
xpkg
xpkg.crossplane.io
xpkg.upbound.io
XR
XR's
XRC
XRD
XRD's
XRDs
XRs
XR's
XRs

View File

@ -8,19 +8,19 @@ europe-central2
GCP
GCP's
GKE
provider-upjet-aws
provider-upjet-gcp
provider-upjet-azure
provider-aws
provider-aws-iam
provider-aws-s3
provider-gcp
provider-helm
provider-kubernetes
provider-upjet-aws
provider-upjet-azure
provider-upjet-gcp
Pub/Sub
PubSub
S3
us-central1
us-east-2
VPC
xpkg.crossplane.io
xpkg.crossplane.io

View File

@ -1,5 +1,3 @@
/tab
/tabs
backporting
built-in
call-outs
@ -7,53 +5,82 @@ ClusterRoles`
comma-separated
conformant
cross-reference
Cross-resource
cross-resource
Cross-resource
datastore
day-two
double-check
double-checks
dry-run
dual-pushes
e.g.
end-points
end-to-end
event-driven
free-form
function-based
google.com
hands-on
hardcode
high-churn
how-to
idempotency
in-depth
in-memory
Job.
least-privilege
left-hand
long-running
Long-running
low-risk
low-traffic
multi-cluster
multi-region
multi-tenant
Multi-step
multi-tenancy
multi-tenant
namespace-scoped
non-empty
non-Kubernetes
non-production
one-time
One-time
Operation-level
per-element
performant
per-object
per-resource
poll-interval
pre-existing
preload
pre-provisioned
pre-release
race-conditions
read-only
ready-made
resource-intensive
resource-specific
right-hand
run-time
self-signed
self-service
self-signed
space-delimited
status-checking
step-by-step
subresources
System-level
/tab
/tabs
third-party
Time-sensitive
top-level
unpause
untrusted
UpperCamelCase
UpperCamelCased
user-defined
user-provided
v2
validators
version-specific
backporting
webhook-based