Note this function is in development. Please test in your environment before using it to protect critical workloads.
function-deletion-protection is a Crossplane function that blocks deletion of objects by creating
Crossplane Usages.
When the function is run as a step in a Composition Pipeline, it looks for
resources with the label protection.fn.crossplane.io/block-deletion: "true".
When run in an Operation, a Usage will be generated for any matched resource.
- Overview
- Crossplane v1 and v2 Compatibility
- Installing and Using the Function
- Running as an Operation
- Building
- Taskfile Support
The function works by creating a Crossplane Usage for an Object.
When a Usage is created, Crossplane will add the protected resource to a Webhook
that blocks any deletion requests until the Usage has been removed from the
Cluster. See
Usages for more
information.
Attempts to delete an Object with a Usage will be rejected by an admission webhook:
$ kubectl delete XNetwork/configuration-aws-network
Error from server (This resource is in-use by 1 usage(s), including the *v1beta1.Usage "configuration-aws-network-26d898-fn-protection" with reason: "created by function-deletion-protection via label protection.fn.crossplane.io/block-deletion".): admission webhook "nousages.protection.crossplane.io" denied the request: This resource is in-use by 1 usage(s), including the *v1beta1.Usage "configuration-aws-network-26d898-fn-protection" with reason: "created by function-deletion-protection via label protection.fn.crossplane.io/block-deletion".The Function can run in a Crossplane Composition, or as a Crossplane Operation.
By default, this function creates v2 Usages using the
protection.crossplane.io API Group in Crossplane version 2.0 or higher. The
function has the ability to generate v1 Usages by setting enableV1Mode: true
in the function Input.
Crossplane v1 Usages are Cluster-scoped and cannot be used to protect
namespaced resources like Claims. The function validates this constraint and
will return a fatal error if it encounters a namespaced resource with the
deletion protection label when enableV1Mode is true. This validation ensures
users cannot accidentally attempt to create incompatible v1 Usages for namespaced
resources.
The function can be installed into a Crossplane cluster using the following manifest:
apiVersion: pkg.crossplane.io/v1
kind: Function
metadata:
name: crossplane-contrib-function-deletion-protection
spec:
package: xpkg.upbound.io/crossplane-contrib/function-deletion-protection:v0.2.2Releases are posted to Releases and the Upbound Marketplace.
When run in a Composition
Pipeline
this function monitors resources in a Composition for the
protection.fn.crossplane.io/block-deletion label and creates corresponding
Usage objects to prevent accidental deletion.
See examples/composition for a complete working example.
The function creates Usages for:
- Composite resources (XRs) when labeled
- Composed resources when labeled. If a Composed resources is protected, the parent Composite will also be protected.
Resources can be labeled outside of the Composition using kubectl label. The
function will check if either the desired or observed state is labeled:
apiVersion: ec2.aws.upbound.io/v1beta1
kind: VPC
metadata:
labels:
protection.fn.crossplane.io/block-deletion: "true"
name: my-vpcThe function monitors the Composite and all Composed resources. In this case
since the label is applied to a Cluster-scoped resource it will generate a
ClusterUsage:
apiVersion: protection.crossplane.io/v1beta1
kind: ClusterUsage
metadata:
name: my-vpc-2a782e-fn-protection
spec:
of:
apiVersion: ec2.aws.upbound.io/v1beta1
kind: VPC
resourceRef:
name: my-vpc
reason: created by function-deletion-protection via label protection.fn.crossplane.io/block-deletionIf the resource is Namespaced a Usage will be created in the Resource's
namespace.
The label can be applied to the resource in the Composition (the "Desired" state), or it can be applied to the Resource in the cluster (the "Observed" state). If the Desired and Observed labels conflict, the function will default to creating the Usage.
The function provides granular reason strings to help identify why a Usage was created:
created by function-deletion-protection via label protection.fn.crossplane.io/block-deletion- A resource was protected because it has theprotection.fn.crossplane.io/block-deletion: "true"labelcreated by function-deletion-protection because a composed resource is protected- A Composite resource was protected because one of its composed resources is protectedcreated by function-deletion-protection by an Operation- A resource was protected by a regular Operation (with the label)created by function-deletion-protection by a WatchOperation- A resource was protected by a WatchOperation (automatic protection)
These reason strings appear in the Usage's spec.reason field and in deletion
rejection messages, making it easy to understand why a resource cannot be
deleted.
When invoked by a
WatchOperation
any Kubernetes resource on the Cluster that matches the watch conditions will
have a Usage generated. In this example Namespaces with the block-deletion: "true" label will trigger an Operation:
---
apiVersion: ops.crossplane.io/v1alpha1
kind: WatchOperation
metadata:
name: block-namespace-deletion
spec:
watch:
apiVersion: v1
kind: Namespace
matchLabels:
block-deletion: "true"
operationTemplate:
spec:
mode: Pipeline
pipeline:
- step: block-deletion
functionRef:
name: crossplane-contrib-function-deletion-protection
input:
apiVersion: protection.fn.crossplane.io/v1beta1
kind: Input
cacheTTL: 1h
enableV1Mode: falseSee examples/operations for a complete working example with RBAC configuration. Operations are a Crossplane 2.x feature.
Setting cacheTTL configures the Function Response
Cache.
This can reduce the number of times the function is called.
- step: protect-resources
functionRef:
name: crossplane-contrib-function-protection
input:
apiVersion: protection.fn.crossplane.io/v1beta1
kind: Input
cacheTTL: 10mThere is a Compatibility mode for generating Crossplane v1 Usages by setting
enableV1Mode: true in the Function's input. When this setting is enabled, v1
Usages will be created. Please note that this feature will be removed when
upstream Crossplane deprecates v1 APIs.
Important: v1 Usages are cluster-scoped only. If you attempt to protect a
namespaced resource (like a Claim) with enableV1Mode: true, the function will
return a fatal error with a message explaining the incompatibility.
- step: protect-resources
functionRef:
name: crossplane-contrib-function-protection
input:
apiVersion: protection.fn.crossplane.io/v1beta1
kind: Input
enableV1Mode: trueWhen this feature is enabled, the function will generate v1 Cluster-scoped
Usages using the apiextensions.crossplane.io/v1beta1 API Group:
apiVersion: apiextensions.crossplane.io/v1beta1
kind: Usage
metadata:
name: ...If a Namespaced resource is encountered while enableV1Mode: true, you the function will return an error similar to the following:
crossplane: error: cannot render composite resource: pipeline step "protect-resources" returned a fatal result: cannot process composed resources: cannot protect namespaced resource (kind: InternetGateway, name: configuration-aws-network-ff951409171d, namespace: test) with enableV1Mode=true. v1 usages only support cluster-scoped resources.
To build the Docker image for both arm64 and amd64 and save the results in a
tar file, run:
export VERSION=0.2.1
# Build the function's runtime image
docker buildx build --platform linux/amd64 . --tag=test:v1 --target=image --output type=docker,dest=function-deletion-protection-runtime-amd64-v${VERSION}.tar
docker buildx build --platform linux/arm64 . --tag=test:v1 --target=image --output type=docker,dest=function-deletion-protection-runtime-arm64-v${VERSION}.tarNext, build the Crossplane Package:
export VERSION=0.2.1
crossplane xpkg build -f package --embed-runtime-image-tarball=function-deletion-protection-runtime-amd64-v${VERSION}.tar -o function-deletion-protection-amd64-v${VERSION}.xpkg
crossplane xpkg build -f package --embed-runtime-image-tarball=function-deletion-protection-runtime-arm64-v${VERSION}.tar -o function-deletion-protection-arm64-v${VERSION}.xpkgThese packages can be pushed to any Docker-compatible registry:
export VERSION=0.2.1
crossplane xpkg push index.docker.io/steve/function-deletion-protection:v0${VERSION} --package-files function-deletion-protection-amd64-v${VERSION}.xpkg,function-deletion-protection-arm64-v${VERSION}.xpkgThis project has several Taskfile tasks:
task --list
task: Available tasks for this project:
* build-docker: Builds the function into a deployable Docker image and saves it as a tar file.
* build-xpkg: Creates a Crossplane .xpkg file from a Docker tar file.
* clean: Removes Artifacts
* push-xpkg: Pushes Crossplane package to an OCI repository. Please ensure the repository exists before pushing.