mirror of https://github.com/crossplane/docs.git
Merge pull request #956 from negz/whats-op-doc
First pass at Operations docs
This commit is contained in:
commit
704d5dfe38
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
title: Operations
|
||||
weight: 52
|
||||
state: alpha
|
||||
alphaVersion: 2.0
|
||||
description: Understand Crossplane's Operations feature
|
||||
---
|
||||
|
|
@ -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
|
||||
|
|
@ -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 s, 2 s, 4 s, 8 s, 16 s, 32 s, then 60 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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -39,4 +39,4 @@ Velero
|
|||
VSCode
|
||||
Webpack
|
||||
write-good
|
||||
Zendesk
|
||||
Zendesk
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue