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: Lifecycle descriptor #20

Merged
merged 5 commits into from
Aug 15, 2019
Merged
Changes from 3 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
69 changes: 69 additions & 0 deletions text/0000-lifecycle-descriptor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Meta
[meta]: #meta
- Name: Lifecycle Descriptor
- Start Date: 08/05/2019
- CNB Pull Request: (leave blank)
- CNB Issue: (leave blank)
- Supersedes: N/A

# Summary
[summary]: #summary

The CNB specification defines platform/lifecycle and lifecycle/buildpack contracts. When we inevitable introduce a breaking change to either contract, we will need a way to indicate to tools like `pack` which version of each contract a given lifecycle implements. I propose adding a `lifecycle.toml` file to all lifecycles specifying these api versions. Eventually this file could also be used to provide additional metadata or indicate the availability of non-breaking additive features.

# Motivation
[motivation]: #motivation

We should do this so `pack` and other platforms can gracefully handle breaking changes in the platform/lifecycle and lifecycle/buildpack APIs. This file also provides a natural extension point for a lifecycle to provide useful metadata to users/platforms or advertise non-breaking features.

`pack` currently uses the lifecycle version to make inferences about the platform/lifecycle and lifecycle/buildpack API versions. However, this has drawbacks. We want `pack` to work with newer lifecycles when there are no breaking changes. By explicitly noting breaking changes in the lifecycle/platform contract we can make `pack` helpfully fail when it encounters an `api` version it isn't familiar with, but continue to work successfully w/ new lifecycles that don't introduce breaking changes.

# What it is
[what-it-is]: #what-it-is

Lifecycle can include a `lifecycle.toml` at it's root. Initially, the only supported fields will be the `api.platform`, `api.buildpack` and `lifecycle.version`.

```toml
[api]
platform = "<major>.<minor>"
buildpack = "<major>.<minor>"
Copy link
Member

@jkutner jkutner Aug 14, 2019

Choose a reason for hiding this comment

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

It does seem that having a single api version in #19 would make defining support in lifecycle a better experience. Is it possible that a user wants a combination of two versions (platform and buildpack) where there is not lifecycle version that matches?

I guess my point is: the purpose of two api versions is to prevent people from having to unnecessarily bump both when only one has changed. But unless we plan on supporting version ranges, people may end up having to bump both anyways.

Copy link
Member Author

Choose a reason for hiding this comment

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

The reason I think defining two version is important is that a breaking change in the platform/lifecycle interface doesn't necessarily imply a breaking change in the lifecycle/buildpack interface and vice versa. If we only had one api version, something like change a detector flag name would require every buildpack author to bump the api version in their buildpack.toml to indicate compatibility with the new lifecycle. That doesn't seem right to me.


[lifecycle]
Copy link
Contributor

Choose a reason for hiding this comment

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

Wonder if a top-level lifecycle field would be necessary. I guess we do the same for buildpack.toml, so it's probably fine.

version = "<major>.<minor>.<patch>"
```

The format of a api version would be <major>.<minor> or <major> (with a minor of 0 implied). A change to the major version of an API would indicate a non-backwards-compatible change, while a change to the minor version would indicate availability of one or more additional features. For example, a lifecycle that specifies version 1.2 of the buildpack api supports buildpacks that specify versions 1.0, 1.1, 1.2, but not those the specify 1.3.
ekcasey marked this conversation as resolved.
Show resolved Hide resolved

When `lifeycle.toml` exists but either of the `api.x` fields is omitted, `pack` will be assume that the lifecycle implements the latest API known to that version of `pack`. When `lifecycle.toml` is omitted, `pack` will initially warn users and assume the lifecycle implements the oldest known api version, for backwards compatibility. Eventually `pack` may require the presence of `lifecycle.toml`.

# How it Works
[how-it-works]: #how-it-works

When `pack create-builder` creates a builder. The lifecycle API versions will be added to the `io.buildpacks.builder.metadata` label.

Example:
```json
{
...
"lifecycle": {"version": "0.4.0", "api": {"platform": "1.0", "buildpack": "1.1"}}
}
```

`pack` would ensure that the `api.buildpack` version matches the `api` version provided by all of the buildpacks that are added to the given builder.
ekcasey marked this conversation as resolved.
Show resolved Hide resolved

When `pack build` executes, it will check `api.platform` and it will either execute the correct commands for the given platform API, or fail if the platform API version is not supported. For example, if in the future we combine restorer/analyzer and cacher/exporter, then the platform API version of the next lifecycle release would be incremented. This will allow `pack` to know which binaries to execute with which flags.

When `pack build` is run with the `--buildpack` flag, `pack` may use the buildpack api version from the builder metadata to determine whether the buildpack supplied with the `--buildpack` flag is compatible with the builder's lifecycle.

If `lifecycle.toml` specifies a lifecycle version and `builder.toml` also provides a lifecycle version, `pack` will ensure the versions match. If `builder.toml` does not specify a lifecycle version but `lifecycle.toml` does, the version from `lifecycle.toml` will be used.

# Drawbacks
[drawbacks]: #drawbacks

The concept of API version is a new number that could possibly confuse users. It doesn't map cleanly to an existing concept like spec version. We could document `api` version in the spec to mitigate this concern, but it still has the potential to introduce confusion by increasing the number of versions in the ecosystem.

# Alternatives
[alternatives]: #alternatives

- We can continue to use lifecycle version as a standin for lifecycle/platform and lifecycle/buildpack API versions (we can encode `pack` with knowledge of the behavior of specific lifecycle versions). However this will not allow us to decide whether it is safe to run specific commands or execute specific buildpacks when a lifecycle is newer than a given `pack` version.
- Instead of introducing new version numbers we can use spec versions to represent the same concepts