Crossplane: A Package-Based Approach to Platform Building

2023_03_Blog_OG_XP-Packaged-Platform-Building_1200x630
date icon

March 13, 2023

author icon

Dan Mangum

read time icon

Reading time: 6 min read

Share:

LinkedIn icon
Twitter icon
Facebook icon

What if onboarding a new software vendor was as easy as adding a dependency?

This article was originally posted on The New Stack here.

The process of onboarding a new vendor in a large enterprise is long and arduous. While organizations frequently have a uniform set of checks that any software must undergo before it can be adopted, the process inevitably leads to special cases being identified as unique to each vendor. This creates an inefficient iteration cycle that can last months — time in which the vendor could have been providing value to the customer.

If vendors were able to package their product in a way that allowed them to easily integrate into existing customer platforms, adoption could be drastically more efficient.

Crossplane Packages and API Dependencies

Crossplane is a framework for building control planes. Large cloud providers offer a suite of services that provide varying levels of abstraction over physical compute. To build the platform that can offer these services to thousands of customers, cloud providers have an underlying control plane that ensures the desired state of infrastructure that a customer has requested is aligned with the actual state.

If they are not already, the control plane will work to drive the two states to alignment. In simplest terms, the control plane is what translates from the interface exposed to users to actions required to satisfy their requests. A platform is the API that the control plane exposes.

In Crossplane, the interface, or APIs, as well as the translation logic is grouped into packages. Packages that include both APIs and logic are referred to as Providers, while those that strictly translate from an abstract interface to a more granular one are referred to as Configurations.

Much like libraries in a programming language, packages may declare dependencies on other packages and build upon their APIs. When a package is installed in a Kubernetes cluster where Crossplane is running, the package manager will evaluate its dependencies and install compatible versions of any that are not already present.

1
2
3
4
5
6
7
8
9
10
apiVersion: meta.pkg.crossplane.io/v1
kind: Configuration
metadata:
  name: my-org-infra
spec:
  crossplane:
    version: ">=v1.0.0"
  dependsOn:
    - provider: xpkg.upbound.io/upbound/provider-aws
      version: ">=v0.23.0"


For example, if a user wanted to build a Configuration package that presented a developer-friendly interface to requesting MySQL databases, they may define a translation from that interface to the RDS API offered by provider-aws, then declare a dependency on provider-aws, with optional version constraints. When their Configuration package is installed, Crossplane will ensure that provider-aws is also present.

If another user then wanted to include the MySQL API alongside other APIs in their control plane, they could either install the MySQL Configuration package directly or declare a dependency on it from another package that is installed. Either way, Crossplane will traverse the dependency tree, ensuring that a control plane with healthy packages will respond to the creation of the instance of any API with the desired behavior.

Defining a Platform with a Root Package

While Crossplane allows users to install many packages alongside one another, a common pattern has emerged of defining a single “root” package, which is an ancestor node of every other package that gets installed in a control plane. Doing so makes the entire API surface area reproducible by installing that one package. As an organization grows and evolves over time, that package can be expanded by defining new APIs or establishing new dependencies.

Furthermore, because using a root package is a convention rather than a technical constraint, the property of composability is not violated. Taking the previously mentioned MySQL Configuration package for example, a user may install it as the root package of their MySQL database control plane, while another user may depend on it as only one component of a much larger API surface for use cases like internal cloud platforms.

Declaring a Vendor Dependency

While the attributes of Crossplane packages enable platform builders to quickly add functionality to their control planes, they also present a unique distribution mechanism for vendors. When your customers all build on a common substrate, you need only target that substrate. Furthermore, the APIs you offer to your customers do not have to be “one size fits all” — there is room for customization both in front and behind the interface.

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: meta.pkg.crossplane.io/v1
kind: Configuration
metadata:
  name: my-org-infra
spec:
  crossplane:
    version: ">=v1.0.0"
  dependsOn:
    - provider: xpkg.upbound.io/upbound/provider-aws
      version: ">=v0.23.0"
    - configuration: xpkg.upbound.io/cool-vendor/dev-envs
      version: ">=v1.0.0"


As an example, a developer tools vendor may offer a product to enterprise customers that manages hosted development environments. The service needs to be distributed in a manner that it can be run on the customer’s cloud provider of choice, and the interface to deploy a development environment needs to support all configuration options that a customer could need. Typically, this would require a combination of scripts, deployment manifests and documentation.

However, when bundling as a Crossplane package, a vendor can specify dependencies on external infrastructure, such as a relational database or in-memory cache and rely on the customer’s control plane to provide them.

To make it concrete, a Configuration package to deploy the aforementioned hosted development environment product on AWS could establish a dependency on provider-aws, and include a Composition that created an RDS instance. When the customer adds the Configuration package as a dependency in their root package, Crossplane will verify the presence of a compatible version of provider-aws in their control plane.

When the customer interacts with the API offered by the vendor package, the creation of an RDS instance object in the cluster will result in the provisioning of an RDS instance in the customer’s AWS account, subject to all of the policies and constraints they already have in place. Because the vendor’s deployment ultimately depends upon the APIs that the customer’s control plane already offers, a successful deployment implies compliance.

Similarly, the customer is not required to interface with the vendor’s product via the interface that the vendor offers. A common pattern in the Crossplane community is to take an API and wrap it in a higher-level abstraction, optionally including additional resources that are to be provisioned alongside the original.

For example, if a customer wanted to create monitoring and alerting policies that are custom to their organization every time they deployed a developer environment, they could wrap the vendor’s developer environment API and their monitoring APIs in a higher-level abstraction, and only give developers access to interact with the abstract API.

Discovering Your Next Platform Component

To build platforms like the ones described in this post, customers need a place to discover packages and vendors need a means of distribution. The Crossplane community takes advantage of the automatic API documentation and robust discoverability features of the Upbound Marketplace. Search for your next platform component or publish your own, and feel free to reach out in Crossplane Slack if you have any questions!

This article was originally posted on The New Stack here.

Subscribe to the Upbound Newsletter