Migrate to Control Plane Projects
If you're already running Crossplane and want to use Upbound Crossplane's control plane tooling, you can migrate to projects.
The next section shows you the steps to migrate existing Crossplane compositions into a control plane project.
Prerequisites
Make sure you have:
- An account on Upbound
- The up CLI installed
- A Docker-compatible container engine, such as Docker Desktop, running
Initialize a project
Log in to Upbound:
up login
A control plane project is the source-level representation of your control plane. Projects contain all the definitions and configurations needed to build a control plane.
up project init --scratch upbound-project && cd upbound-project
This command:
- Creates a new directory called
upbound-project
- Sets up the basic project structure with necessary configuration files
Review the project structure
upbound.yaml
The upbound.yaml
file is the main configuration that:
- Defines project metadata (name, organization)
- Sets configuration parameters for builds and deployments
This file is the project entry point and tells Upbound what this project is and where it belongs.
An upbound.yaml
is a superset of a Crossplane configuration crossplane.yaml
and replaces it.
apis/
directory
The apis/
directory is for your composite resource definitions (XRDs) and compositions.
- XRDs (Composite Resource Definitions): Define your custom resource APIs
- Compositions: Define the API implementation logic
Each composite type belongs in its own folder under the apis/
directory.
examples/
directory
The examples/
directory is for example instances of your composites.
functions/
directory
The functions/
directory contains embedded functions used in your composition and operation pipelines. Upbound calls these embedded functions because of the convention and workflow that a project implements:
- A project gets built into a Crossplane Configuration, which is an OCI package, and gets pushed to a repository.
- Functions defined in this directory get built into their own OCI package and get pushed into a sub-repository of the project.
- The embedded function convention enables project tooling to offer rich in-editor experiences in your preferred IDE.
Import dependencies
Because the upbound.yaml
is a superset of a crossplane.yaml
, you should
define your project's dependencies here. All packages your control plane
requires should be declared in the spec.dependsOn
field.
Use the up dependency add command to bring any providers, functions,
configurations, or other supported package types into your project's context.
Here's an example for adding a dependency on provider-aws-s3
:
up dependency add xpkg.upbound.io/upbound/provider-aws-s3
Project tooling uses this package information to generate resource schemas to power rich in-editor experiences in your IDE.
Import composite types
Move your existing composite types into the /apis
directory of your project. Each composite should belong to its own folder. The example below shows how this would look if you had composite types for Apps
and Buckets
:
.
└── upbound-project/
└── apis/
├── apps/
│ ├── composition.yaml
│ └── definition.yaml
├── buckets/
│ ├── composition.yaml
│ └── definition.yaml
└── ...
Want to define a new composite type? Use the project tooling to scaffold a new API.
Build and run your project
You can now build and deploy your project. Deploy it locally first:
up project run --local
Wait for the control plane to become ready, then confirm it's configured as you expect:
# Look for installed dependencies, such as providers
kubectl get pkg
# Look for installed composite types
kubectl get xrds
Build and push your project to an OCI registry such as the Upbound Marketplace to deploy your package into production:
up project build && up project push
Deploy it in production:
- Deploy on a self-managed UXP cluster.
- Deploy it on a control plane in a Space.
Optional: Refactor compositions
This step is highly recommended to enable a richer project-based experience, but isn't a requirement.
To take full advantage of the project experience, you should define the functions in your composition and operation pipelines as embedded functions. Upbound platform supports a multi-language experience.
Go-templating example
Go-templating is a popular function to use for building compositions. This example demonstrates how to refactor a composition built with this function into an embedded function.
Suppose the composition looks like this:
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: example
spec:
compositeTypeRef:
apiVersion: example.crossplane.io/v1beta1
kind: XBucket
mode: Pipeline
pipeline:
- step: create-a-bucket
functionRef:
name: function-go-templating
input:
apiVersion: gotemplating.fn.crossplane.io/v1beta1
kind: GoTemplate
source: Inline
inline:
template: |
apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
metadata:
annotations:
gotemplating.fn.crossplane.io/composition-resource-name: bucket
spec:
forProvider:
region: {{ .observed.composite.resource.spec.region }}
- step: automatically-detect-ready-composed-resources
functionRef:
name: function-auto-ready
Start by moving the composition into the project as described in the import composite types section.
Generate an embedded function in the project:
up function generate --language=go-templating compose-bucket apis/xbuckets/composition.yaml
This command does two things:
- scaffolds an embedded function for go-templating in the
functions/
directory. - appends the embedded function to the composition above.
Move the in-line templated YAML from the existing composition.yaml
and paste it in the embedded function's 01-compose.yaml.gotmpl
in the functions/compose-bucket/
directory. It should look like this:
# code: language=yaml
# yaml-language-server: $schema=../../.up/json/models/index.schema.json
apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
metadata:
annotations:
gotemplating.fn.crossplane.io/composition-resource-name: bucket
spec:
forProvider:
region: {{ .observed.composite.resource.spec.region }}
The comments at the top are directives used by the project tooling to support intellisense-style experiences in your IDE. Don't remove them.
Remove the create-a-bucket
step in the composition pipeline in the composition.yaml
. This step gets performed by the embedded function instead, which should already be added to the composition pipeline.
You're finished. You've refactored a composition to use embedded functions.
Next steps
Read the concept documentation to learn more about using projects.