Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC to create a lifecycle crd #1483

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions rfcs/0000-lifecycle-crd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
`[Readable](https://github.com/your-name-or-org/kpack/blob/<pr-branch-name>/rfcs/0000-<my-feature>.md)`

**Problem:**

The CNB lifecycle is provided to kpack as in image reference in a configMap similar to how the completion and prepare
images are passed to the kpack controller. This means that every builder will always use the same exact lifecycle, with
no ability to specify an alternate one that may support different buildpack api or platform api versions. Due to kpack
support of windows, a custom lifecycle image with specific layers for the linux and windows binaries is required
(with a label specifying the layer diff id for each os). Because of this, a cve present in the lifecycle that has been
patched upstream, requires any user to build the kpack specific lifecycle image themselves if the kpack provided one
has not been bumped.

**Outcome:**

Users will be able to create a CR that references a lifecycle image. The file structure in the image must match
that of the [CNB Lifecycle image](https://hub.docker.com/r/buildpacksio/lifecycle/tags).

```
.
└── cnb/
└── lifecycle/
├── analyzer
├── builder
├── detector
├── exporter
├── launcher
├── lifecycle.toml
└── restorer
```

They will be able to use this lifecycle CR in any builder. A lifecycle will be shipped out of the box in kpack and
users will not be required to provide a lifecycle reference in the builder if they do not wish to override the default.
Users should see no difference in functionality compared to the existing kpack lifecycle if they do not choose to
provide their own lifecycle CR.

Instead of creating custom multi-layer lifecycle images, users can create a lifecycle CR for their desired runtime (i.e. windows or arm64).
During builder creation, kpack will validate that the referenced lifecycle CR has the same OS and architecture as the base image.

**Actions to take:**

A new ClusterLifecycle CRD will be created. It will be very similar in structure to the ClusterStack Resource.
We will create a cluster scoped version of this resource. In the future, we may decide to create a namespaced version
as well, but that is not in scope for this RFC. The name ClusterLifecycle aligns with kpack's existing cluster scoped
resources.

This is an example instance of the proposed CRD:
```yaml
apiVersion: kpack.io/v1alpha2
kind: ClusterLifecycle
metadata:
name: sample-cluster-lifecycle
spec:
serviceAccountRef:
name: sample-sa
namespace: sample-namespace
image: buildpacksio/lifecycle@sha256:f4ce143ea6bbc6b5be5f4d151aba8214324adb93bbd7e3b1f43cd134ad450bf7
```


The existing lifecycle reconciler can be updated to reconcile this new CRD instead of ConfigMaps.

A new field will be added to the ClusterBuilder/Builder resources allowing the user to provide a reference to a lifecycle CR.
This field will be optional and will default to the highest semantic version that is supported by the buildpacks in the builder and the platform.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that basically like how we hard-code the lifecycle version here?

lifecycleVersion = "0.17.2"

Would the future world deprecate that part of the code entirely? Like, if no ClusterLifecycle is specified do you get a default ClusterLifecycle or do you fallback to pulling the layer out of the lifecycleImage?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ya this would get deprecated and we would pull in an upstream lifecycle in the ci pipeline instead of building our own like this file is doing.

we would still ship a default ClusterLifecycle to ease burden on our users and avoid a breaking change on upgrade

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This field will be optional and will default to the highest semantic version that is supported by the buildpacks in the builder and the platform.
This field will be optional and will default to the highest semantic version that is supported by the platform.

Buildpacks are configured by the operator, so I don't think we can know which Buildpack APIs will be required... right? In theory (assuming we think most buildpacks in the wild have upgraded to at least Buildpack API 0.7 - since lifecycle 0.18.x and higher drops support for earlier APIs) it should be the highest semantic version that supports the Platform APIs here...right? In practice, that is going to mean the latest available lifecycle (until we deprecate more Platform APIs).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do validate that any buildpack referenced in the order is using buildpack apis supported by the lifecycle when constructing a builder

func (bb *builderBlder) validateBuilder(sortedBuildpacks []DescriptiveBuildpackInfo) error {
platformApis := append(bb.LifecycleMetadata.APIs.Platform.Deprecated, bb.LifecycleMetadata.APIs.Platform.Supported...)
err := validatePlatformApis(platformApis)
if err != nil {
return err
}
buildpackApis := append(bb.LifecycleMetadata.APIs.Buildpack.Deprecated, bb.LifecycleMetadata.APIs.Buildpack.Supported...)
for _, bpInfo := range sortedBuildpacks {
bpLayerInfo := bb.buildpackLayers[bpInfo].BuildpackLayerInfo
err := bpLayerInfo.supports(buildpackApis, bb.stackId, bb.mixins, relaxedMixinContract(platformApis))
if err != nil {
return errors.Wrapf(err, "validating buildpack %s", bpInfo)
}
}
return nil
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see - but that validation happens after the ClusterLifecycle is defined, no? So in the case that the provided lifecycle doesn't support a buildpack in the builder we just fail, right? (Instead of selecting a different lifecycle?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the only check we'd want when the cluster lifecycle is reconciled is the platform api compatibility. The buildpack api compatibility will continue to happen at builder creation

This is similar to how we determine the platform api to use for a build.
```yaml
apiVersion: kpack.io/v1alpha2
kind: Builder
metadata:
name: my-builder
spec:
tag: gcr.io/sample/builder
serviceAccountName: default
stack:
name: stack
kind: ClusterStack
lifecycle:
name: my-lifecycle
kind: ClusterLifecycle
order:
- group:
- id: paketo-buildpacks/java
```

```yaml
apiVersion: kpack.io/v1alpha2
kind: ClusterBuilder
metadata:
name: my-cluster-builder
spec:
tag: gcr.io/sample/builder
stack:
name: bionic-stack
kind: ClusterStack
lifecycle:
name: my-lifecycle
kind: ClusterLifecycle
serviceAccountRef:
name: default
namespace: default
order:
- group:
- id: paketo-buildpacks/java
```

Creating a CRD for the lifecycle will allow us to do the following:
- Provide status resource that shows more information about the lifecycle such as supported api versions
- Allow users to specify a particular lifecycle for their builder, mimicking behavior that currently exists in the pack cli
- Offer a clean shift to using the upstream lifecycle image if done in conjunction with deprecation of windows support

**Complexity:**

The complexity of this is not that high due to its similarity with the other kpack resources.

**Prior Art:**

- The pack cli allows a lifecycle image to be specified when creating a build
- Adding new resources to the builder that get added to a layer has been done before in kpack

**Alternatives:**

We could keep the existing experience.

**Risks:**
Exposing the user to the lifecycle can result in some confusion, but this can be mitigated by not making it a required
field on the builder and continuing to ship a lifecycle out of the box.
Loading