Description
Background
Porch is primarily built as an aggregated APIServer with the PackageRevision and PackageRevisionResources resources, as well as their subresources, implemented that way rather than as CRDs. Up until recently the only datastore used for these resources were git/oci, although with #3579 we also store data pertaining to each PackageRevision in CRs as well.
It has become clear that in order to make the resources provided by the aggregated APIServer behave like regular resources, it is not possible to only rely on storing data in git commits. That we recently started using CRs in addition to git is a consequence of this (We also discussed alternatives here), but having to storage backends for a resource leads to other issues. It is also non-trivial to implement all aspects of the Kubernetes API through an aggregated APIServer when using a different storage backend that etcd. Examples are proper resourceVersions, generations, and correct semantics for watches.
As a result, we have discussed migrating the PackageRevision type to a CRD.
Details
Implementing the PackageRevision type as a CRD has many benefits, as CRDs already provide all the operations that are expected from KRM resources. But it also comes with limitations as compared to an aggregated apiserver:
- CRDs does not support other subresources than “status” and “scale’.
- Validation is more limited, although some of this can be handled with webhooks.
- All “logic” must be handled asynchronously by controllers.
As a result, switching to using a CRD involves addressing at least the following items (more will probably come up):
- Migrate the repository polling logic into a controller that will create PackageRevisions through the regular API rather than doing it internally.
- Figure out how we can handle both deletion of PackageRevision (The PackageRevision resource is delete from the Kubernetes API and the underlying data in git/oci is removed) and unregistration of a PackageRevision (The PackageRevision resource is deleted from the Kubernetes API, but the data remains in git). The latter is typically needed when a repository is removed from Porch, which also requires that the PackageRevisions in the Repository are also removed.
- Figure out how to handle what we currently implement as subresources, primarily the “approve” subresource.
- All “logic” related to changes in state for PackageRevisions must happen in controllers, and therefore will happen asynchronously. We need to identify what logic we actually do have related to PackageRevisions that will not be automatically happened by the APIServer with CRDs. As an example, all syncing to git will need to happen asynchronously.
The PackageRevisionResources type will almost certainly still need to be supported through the aggregated APIServer. Storing package content in CRDs does not seem like a feasible solution. But we need to determine how the PackageRevisionResources type will align with a PackageRevision type that is a CRD. Currently, we consider them as two separate views of the same underlying resource. We have discussed an alternative where we only allow push and pull of resources through the PackageRevisionResource, but it needs more thought. If we could have a PackageRevision CRD with subresources backed with an aggregated APIServer, it seems like we would like to have something like packagerevisions/push
and packagerevisions/pull
, but combining CRDs and aggregated APIServer in this way is not possible.
How do we get there?
We essentially have two ways to approach this.
Full rewrite now
This is taking the “quick(?) and painful” approach and do the full rewrite in one go. This seems very risky as this is a major change to Porch with several challenges, and we will almost certainly discover additional challenges on the way.
Gradual rewrite
So instead of trying to do all these changes in one massive effort, we can do this gradually.
What makes this approach compelling, is that we can make all the necessary changes without actually migrating to using a CRD. We can migrate away from using subresources, move the polling of repositories, and migrate logic to controllers using the current type from the Aggregated APIServer, but it allows us to combine this effort with adding new features (that hopefully doesn’t increase our dependency on the aggregated APIServer). This also allows to reconsider this change if discover that it is misguided.
The path forward is essentially to start addressing the issues listed above and start documenting additional issues we come across.