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: Execution Environments #274

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
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
185 changes: 185 additions & 0 deletions text/0000-execution-environments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# Meta
[meta]: #meta
- Name: Execution Environments
- Start Date: 2023-01-11
- Author(s): @hone
- Status: Draft <!-- Acceptable values: Draft, Approved, On Hold, Superseded -->
- RFC Pull Request: (leave blank)
- CNB Pull Request: (leave blank)
- CNB Issue: (leave blank)
- Supersedes: (put "N/A" unless this replaces an existing RFC, then link to that RFC)

# Summary
[summary]: #summary

Add support for different execution environments for buildpacks, with testing as the first use case.

# Definitions
[definitions]: #definitions

- Execution Environment - The target environment the OCI image is expected to be run in, i.e. production, test, development.

# Motivation
[motivation]: #motivation

The main way Buildpacks are being used is building production images, but this is only one piece of the CI/CD pipeline. Without a solid buildpack test environment story, users will be required alternatives for building their environment. A testing environment, while different, shares many of the same broad strokes ultimately producing an execution environment. This is something Buildpacks are well suited to solve.
hone marked this conversation as resolved.
Show resolved Hide resolved

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

## Test Environments

One of the goals in this design is to minimize the changes needed. The Buildpack lifecycle is already well suited to produce environments:

```
App Source -> OCI Image (Production Environment) -> Execute (Launch Process)
```

To create a test environment, it can follow the same flow:

```
App Source -> OCI Image (Test Environment) -> Execute (Test Process)
```

### Division of Responsibility
With the test OCI Image, a platform can execute the tests in the pipeline as they see fit. This means a bulk of the responsibilities are platform concerns:

- Set which environment to build for
- Decide which buildpacks to execute
Copy link
Member

Choose a reason for hiding this comment

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

Isn't this a lifecycle concern? i.e., when processing a group within an order, should it skip buildpacks that do not declare exec-env matching the desired env?

Copy link
Member Author

Choose a reason for hiding this comment

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

When talking to @jabrown85, I thought the Platform/Builder provide the order.toml to lifecycle?

Copy link
Member Author

Choose a reason for hiding this comment

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

From WG, lifecycle will decide which buildpacks to run based on the execution environment being passed along by the platform.

- How to execute the tests
- What is the test result format like [TAP](https://en.wikipedia.org/wiki/Test_Anything_Protocol)?
- How to process the test results
- What to do with the results
Copy link
Member

Choose a reason for hiding this comment

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

Is this ever something we would want to spec? Having a consistent way for buildpacks to e.g., dump test output could help ensure portability across platforms.

Copy link
Member Author

Choose a reason for hiding this comment

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

I could see us wanting to do that, but I wasn't sure how much we wanted to impose standards there.


This narrows the scope of the Buildpack responsibilities to what it's already good at:

- Produce Test Environment
- How to launch the process
- launch.toml with test process marked as default (recommendation)

## Setting the Execution Enviroment for Build + Buildpack
A platform will set the `CNB_EXEC_ENV` env var to the execution environment desired. Buildpacks can than read this env var to branch or switch on logic needed based on the execution environment.

In addition, Builder Authors, Buildpack Authors, and App Developers will be able to specify various options to a specific execution enviroment using the `exec-env` key.

## Development Environments
The specifics of creating development enviroments are out of scope of this RFC, but it's not hard to extrapolate how these kind of changes can assist in creating Buildpacks for development environments.

# How it Works
Copy link
Member

Choose a reason for hiding this comment

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

It would be nice to see the pack flow as well

Copy link
Member

Choose a reason for hiding this comment

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

Should we add anything to the app image to designate it as being built for a particular environment? To avoid users accidentally deploying a test image in production...

I could see folks wanting to use the same tag when re-building a test image for production, in order to use previously cached dependencies.

Copy link
Member Author

Choose a reason for hiding this comment

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

@natalieparellano that's a good question. Do you think cached dependencies should be shared b/t different execution environments?

Copy link
Member Author

Choose a reason for hiding this comment

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

Is a label the best place to designate the execution environment?

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

## `exec-env` key in TOML

In order to support additional execution environments an `exec-env` key will be added to various TOML tables in the project. The value can be any string with `all` having special meaning. `all` will apply to all execution environments and will be the default if not specified. This should make it backwards compatible and optional. When `exec-env` is not set to `all`, the table settings will only be applied to that execution environment.
Copy link
Member

@joshwlewis joshwlewis Mar 23, 2023

Choose a reason for hiding this comment

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

What would consume this new key? If a platform or user ran pack build -e CNB_EXEC_ENV=test my-app:test, a buildpack might contribute new launch entries flagged for test, but how would a platform or user determine which of the launch entries were all or test? My guess is at the very least, we'd want this key to show up in the io.buildpacks.build.metadata process-types label data?

Copy link
Contributor

Choose a reason for hiding this comment

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

We should consider wanting multiple values. This might make [] mean all, which is kind of odd.

exec-env: ["staging", "production"] # but not uat


### Project Descriptor - `project.toml` (App Developers)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add an example of a full project.toml that is used for producing a test image and a production image?

Copy link
Member Author

Choose a reason for hiding this comment

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

Just for Project Descriptor?


This file be extended by adding `exec-env` to the following tables:

`[[io.buildpacks.group]]`
`[[io.buildpacks.pre.group]]`
`[[io.buildpacks.post.group.env]]`
`[[io.buildpacks.build.env]]`

### `builder.toml` (Builder Authors)

The only table that `exec-env` will be added to is `[[order.group.env]]`.
Copy link
Member

Choose a reason for hiding this comment

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

Do you mean [[order.group]]?


### `buildpack.toml` (Buildpack Authors)

The only table that `exec-env` will be added to is `[[buildpack.order.group]]`. This only is applicable for composite buildpacks.

## `CNB_EXEC_ENV` Environment Variable

This env var will reserve the following values:

* production
* test
* development

### Buildpack API
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we perhaps get a conceptual example of what a buildpack author might do with this new execution environment information?

I have in my head a language buildpack author may...

  • install test group dependencies
  • create ruby-tests process / ruby-tests-verbose process
  • Skip cleaning up things they might clean up otherwise for production images
  • Set env vars to test mode operation (RAILS_ENV or similar)


A buildpack author will be able to determine the execution environment their buildpack is expected to build for by reading the `CNB_EXEC_ENV` environment variable. If this value is not set, a Buildpack Author can assume it's set to `production`.

### Platform API

It will be up to the platform to set the environment variable `CNB_EXEC_ENV`. If this value is set, `lifecycle` MUST NOT override this value. If the value is not set, `lifecycle` will set it to `production`.

# Migration
[migration]: #migration

In order to allow the Buildpack API and Platform API to be updated independently, both APIs will have a fallback to the `production` value. For `1.0`, it would be nice to drop this behavior and assume it will always be set.

This also touches Project Descriptor, but since the platform controls the Platform API and the Project Descriptor Extension API it shouldn't cause any issues.

# Drawbacks
[drawbacks]: #drawbacks

While the design tries to stay simple, it still adds new additions to the spec.

* env var to Buildpack API
* env var to Platform API
* field to Project Descriptor, `builder.toml`, and `buildpack.toml`.

# Alternatives
[alternatives]: #alternatives

## Separate Execution Environment Project Descriptor files

When using `Dockerfile`, it's common to create a separate one for other execution environments like `Dockerfile.test`. This makes the file clean and easy to read, but comes at the cost of duplicating setup/code.

With Project Descriptor, this would remove the need for the `exec-env` key at the cost of replicating buildpack groups. It does bring a big benefit by easily unlocking the `builder` key in Project Descriptor to be specific to an execution environment. This would apply to any fields (not tables) that exist at the `[io.buildpacks]` level. There are some other options if this is desired, where a new table could be created for hoisting fields into that would allow us to add the `exec-env` field.

# Prior Art
[prior-art]: #prior-art

## [Heroku Testpack API](https://devcenter.heroku.com/articles/testpack-api)

As part of the classic buildpack design, Heroku extended the API to include support for tests. It added a `bin/test-compile` phase, which is like the normal `bin/compile` phase but specific for setting things up for test. `bin/test` was called for executing tests. This design let a buildpack have a specific codepath for tests and also do intelligent logic for executing tests based on the source code. For example [in Ruby](https://github.com/heroku/heroku-buildpack-ruby/blob/main/bin/support/ruby_test#L62-L76), the buildpack author can run `bin/rspec` if it detects `rspec`, `bin/rails test` when using rails, or default to `rake test`.

There were some flaws in this design. Though it's clean to separate production and test code paths, they end up sharing a lot of code. Many of the bash based Heroku buildpacks would just [call `bin/compile`](https://github.com/heroku/heroku-buildpack-nodejs/blob/main/bin/test-compile#L24) with different parameters/env vars.

## [GOOGLE_DEVMODE](https://cloud.google.com/docs/buildpacks/service-specific-configs#google_devmode)
Copy link

Choose a reason for hiding this comment

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

Similar to devmode, I just ran across live reloading in the Paketo python buildpacks https://paketo.io/docs/howto/python/#using-bp_live_reload_enabled


These are specific to the Google Cloud Buildpacks for setting a development code path to work with skaffold.

## [Develop API](https://github.com/buildpacks/spec/pull/71)

The original Cloud Native Buildpacks spec included a Develop API, but it was never implemented.

# Unresolved Questions
[unresolved-questions]: #unresolved-questions

- "env" is overloaded as a word since we also use it for environment variables. Is there a better word here?
Copy link
Member

Choose a reason for hiding this comment

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

How about "mode"?

Copy link
Member

Choose a reason for hiding this comment

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

I like "context", but that's just as overloaded. How about "purpose = test" or "intent = test"?

- Should there be builders that are specific to an execution environment?
- Should the execution environments be an enum or flexible as a string?
- enums will help encourage standardization across buildpacks and platforms.
- strings can help account for use cases we haven't thought of yet.
- Should buildpacks be allowed specify allowlist execution environments?
Copy link
Member

@natalieparellano natalieparellano Feb 2, 2023

Choose a reason for hiding this comment

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

Are there any downsides to doing this? It would be more flexible and possibly avoid some duplication within orders (we could keep all as a special value)

- What changes are needed in the buildpack registry?

# Spec. Changes (OPTIONAL)
[spec-changes]: #spec-changes

See ["How it Works"](#how-it-works).

# History
[history]: #history

<!--
## Amended
### Meta
[meta-1]: #meta-1
- Name: (fill in the amendment name: Variable Rename)
- Start Date: (fill in today's date: YYYY-MM-DD)
- Author(s): (Github usernames)
- Amendment Pull Request: (leave blank)

### Summary

A brief description of the changes.

### Motivation

Why was this amendment necessary?
--->