|
| 1 | ++++ |
| 2 | +title="The finer points of image extensions" |
| 3 | +weight=406 |
| 4 | ++++ |
| 5 | + |
| 6 | +# Guidance for extension authors |
| 7 | + |
| 8 | +## During detect |
| 9 | + |
| 10 | +### Expressing provided dependencies through the build plan |
| 11 | + |
| 12 | +The [build plan](/docs/reference/spec/buildpack-api#build-plan) is a mechanism for inter-buildpack communication. |
| 13 | +Through the build plan, buildpacks may express the dependencies they require, as well as those they provide. |
| 14 | +The lifecycle uses information from the build plan to determine whether a group of buildpacks is compatible - that is, whether for every buildpack in the group, its required dependencies are provided by a buildpack that comes before it. |
| 15 | + |
| 16 | +Extensions can use the build plan too - but they are only allowed to provide dependencies, they cannot require any. |
| 17 | +Note that because there is a separate order for extensions that is prepended to each buildpack group during the `detect` phase, |
| 18 | +all extension "provides" come before all buildpack "requires" in the build plan. |
| 19 | + |
| 20 | +During the `detect` phase, the lifecycle sets a `CNB_OUTPUT_DIR` environment variable when executing each `./bin/detect`. |
| 21 | +If using a build plan, extensions should write the plan to `$CNB_OUTPUT_DIR/plan.toml`. |
| 22 | + |
| 23 | +## During generate |
| 24 | + |
| 25 | +### Configuring build args |
| 26 | + |
| 27 | +During the `generate` phase, extensions may output (in addition to Dockerfiles) an `extend-config.toml` |
| 28 | +containing build-time arguments for Dockerfiles. |
| 29 | +(Not to be confused with the `build` phase, "build" here refers to the application of Dockerfiles to a base image, |
| 30 | +similar to a `docker build`). |
| 31 | + |
| 32 | +Arguments may be configured for builder image extension or runtime base image extension or both, |
| 33 | +according to the schema defined in the [spec](https://github.com/buildpacks/spec/blob/main/image_extension.md#extend-configtoml-toml). |
| 34 | + |
| 35 | +During the `generate` phase, the lifecycle sets a `CNB_OUTPUT_DIR` environment variable when executing each `./bin/generate`. |
| 36 | +If using an `extend-config.toml`, extensions should write the config to `$CNB_OUTPUT_DIR/extend-config.toml`. |
| 37 | + |
| 38 | +### Invalidating the build cache with the UUID build arg |
| 39 | + |
| 40 | +Whenever possible, the application of Dockerfiles to a base image will use a caching mechanism |
| 41 | +similar to that of a `docker build`. |
| 42 | +The lifecycle, for example, uses [`kaniko`](https://github.com/GoogleContainerTools/kaniko) to implement the `extender`. |
| 43 | + |
| 44 | +However, there may be times when caching is not desired - for example, when fetching the "latest" available version of a package. |
| 45 | +In such cases, Dockerfiles can use the `build_id` build argument to invalidate the cache for all instructions that follow. |
| 46 | + |
| 47 | +As an example: |
| 48 | + |
| 49 | +```bash |
| 50 | +RUN echo "this instruction may be found in the cache" |
| 51 | + |
| 52 | +ARG build_id=0 |
| 53 | +RUN echo ${build_id} |
| 54 | + |
| 55 | +RUN echo "this instruction should never be found in the cache, as the value above will change" |
| 56 | +``` |
| 57 | + |
| 58 | +Note that `build_id` is defaulted to `0` as a best practice. |
| 59 | + |
| 60 | +### Re-setting the user/group with build args |
| 61 | + |
| 62 | +Dockerfiles from image extensions may contain `USER root` instructions in order to perform actions that would not be possible |
| 63 | +when running as a non-root user. |
| 64 | + |
| 65 | +However, for security reasons, the final user after all Dockerfiles have been applied should _not_ be root. |
| 66 | +To reset the user to its original value (before the application of the current Dockerfile), |
| 67 | +Dockerfiles should make use of `user_id` and `group_id` build arguments, as seen below: |
| 68 | + |
| 69 | +```bash |
| 70 | +ARG user_id=1000 |
| 71 | +ARG group_id=1000 |
| 72 | +USER ${user_id}:${group_id} |
| 73 | +``` |
| 74 | + |
| 75 | +### Making 'rebasable' changes |
| 76 | + |
| 77 | +Image layers generated from extensions are "above the rebasable seam" - that is, |
| 78 | +after swapping the runtime image to an updated version through a `rebase` operation, |
| 79 | +the extension layers will be persisted in the rebased application image. |
| 80 | + |
| 81 | +Unlike buildpack layers, extension layers are _not_ always safe to rebase. |
| 82 | +Extension layers _may_ be safe to rebase if: |
| 83 | +* the changes they introduce are purely additive (no modification of pre-existing files from the base image), or |
| 84 | +* any modified pre-existing files are safe to exclude from rebase (the file from the extension layer will override any updated version of the file from the new runtime base image) |
| 85 | + |
| 86 | +By default, the lifecycle will assume that any extension layers are _not_ rebasable. |
| 87 | +To indicate otherwise, `run.Dockerfile`s should include: |
| 88 | + |
| 89 | +```bash |
| 90 | +LABEL io.buildpacks.rebasable=true |
| 91 | +``` |
| 92 | + |
| 93 | +If all `run.Dockerfile`s set this label to `true`, the application image will contain the label `io.buildpacks.rebasable=true`. |
| 94 | +Otherwise, the application image will contain the label `io.buildpacks.rebasable=false`. |
| 95 | +`pack rebase` requires a `--force` flag if the application image contains `io.buildpacks.rebasable=false`. |
| 96 | + |
| 97 | +Extension authors should take great care (and perform testing) to ensure that any layers marked as rebasable are in fact rebasable. |
| 98 | + |
| 99 | +## In general |
| 100 | + |
| 101 | +### Choosing an extension ID |
| 102 | + |
| 103 | +Extension IDs must be globally unique to extensions, but extensions and buildpacks can share the same ID. |
| 104 | + |
| 105 | +### Expressing information in extension.toml |
| 106 | + |
| 107 | +Just like `buildpack.toml`, an `extension.toml` can contain additional metadata to describe its behavior. |
| 108 | +See the [spec](https://github.com/buildpacks/spec/blob/main/image_extension.md#extensiontoml-toml) for more information. |
| 109 | + |
| 110 | +### Pre-populating output |
| 111 | + |
| 112 | +The root directory for a typical extension might look like the following: |
| 113 | + |
| 114 | +``` |
| 115 | +. |
| 116 | +├── bin |
| 117 | +│ ├── detect |
| 118 | +│ ├── generate |
| 119 | +├── extension.toml |
| 120 | +``` |
| 121 | + |
| 122 | +But it could also look like any of the following: |
| 123 | + |
| 124 | +#### ./bin/detect is optional! |
| 125 | + |
| 126 | +If `./bin/detect` is missing, |
| 127 | +the extension is assumed to pass detection and |
| 128 | +the lifecycle will interpret the contents of `./detect` as the contents of `$CNB_OUTPUT_DIR`. |
| 129 | + |
| 130 | +``` |
| 131 | +. |
| 132 | +├── bin |
| 133 | +│ ├── generate |
| 134 | +├── detect |
| 135 | +│ ├── plan.toml |
| 136 | +├── extension.toml |
| 137 | +``` |
| 138 | + |
| 139 | +#### ./bin/generate is optional! |
| 140 | + |
| 141 | +If `./bin/generate` is missing, |
| 142 | +the lifecycle will interpret the contents of `./generate` as the contents of `$CNB_OUTPUT_DIR`. |
| 143 | + |
| 144 | +``` |
| 145 | +├── bin |
| 146 | +│ ├── detect |
| 147 | +├── generate |
| 148 | +│ ├── build.Dockerfile |
| 149 | +│ ├── run.Dockerfile |
| 150 | +├── extension.toml |
| 151 | +``` |
| 152 | + |
| 153 | +#### It's all optional! |
| 154 | + |
| 155 | +``` |
| 156 | +├── detect |
| 157 | +│ ├── plan.toml |
| 158 | +├── generate |
| 159 | +│ ├── build.Dockerfile |
| 160 | +│ ├── run.Dockerfile |
| 161 | +├── extension.toml |
| 162 | +``` |
0 commit comments