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

Update to libcnb 0.12.0 and Buildpack API 0.9 #150

Closed
wants to merge 4 commits into from

Conversation

edmorley
Copy link
Member

@edmorley edmorley commented Apr 28, 2023

libcnb 0.12.0 includes an upgrade from Buildpack APi 0.8 to 0.9:
https://github.com/heroku/libcnb.rs/releases/tag/v0.12.0

In buildpack API 0.9 implicit usage of bash via direct=false mode has been removed:
https://github.com/buildpacks/spec/releases/tag/buildpack%2Fv0.9
https://github.com/buildpacks/rfcs/blob/main/text/0093-remove-shell-processes.md

As such, this buildpack now needs to explicitly wrap the procfile commands in a bash -c "<command>" invocation, similar to what the Python CNB already does for Python Functions:
https://github.com/heroku/buildpacks-python/blob/76795e93cac436724b0f854c22b8df058418e728/src/salesforce_functions.rs#L57-L76

We're not using exec for the reasons mentioned in:
382d96e

I've also added an integration test for more complex procfile entries (such as those that use compound commands and env var interpolation) - since the existing tests didn't catch the exec issue.

See also:
https://buildpacks.io/docs/reference/spec/migration/buildpack-api-0.8-0.9/
https://buildpacks.io/docs/reference/spec/migration/platform-api-0.9-0.10/
https://github.com/buildpacks/spec/blob/main/buildpack.md#launchtoml-toml
https://github.com/buildpacks/spec/blob/main/platform.md#launcher

Closes #147.
Closes #148.
Closes #149.
GUS-W-13119392.

libcnb 0.12.0 includes an upgrade from Buildpack APi 0.8 to 0.9:
https://github.com/heroku/libcnb.rs/releases/tag/v0.12.0

In buildpack API 0.9 implicit usage of bash via `direct=false` mode has
been removed:
https://github.com/buildpacks/spec/releases/tag/buildpack%2Fv0.9
https://github.com/buildpacks/rfcs/blob/main/text/0093-remove-shell-processes.md

As such, this buildpack now needs to explicitly wrap the procfile commands in a
`bash -c "exec <command>"` invocation, similar to what the Python CNB already
does for Python Functions:
https://github.com/heroku/buildpacks-python/blob/76795e93cac436724b0f854c22b8df058418e728/src/salesforce_functions.rs#L57-L76

Closes #147.
Closes #148.
Closes #149.
GUS-W-13119392.
@edmorley edmorley self-assigned this Apr 28, 2023
@edmorley edmorley marked this pull request as ready for review April 28, 2023 14:06
@edmorley edmorley requested a review from a team as a code owner April 28, 2023 14:06
@Malax
Copy link
Member

Malax commented Apr 28, 2023

I'm unsure about the use of args here. The CNB spec states that they can be overridden by the user. From my understanding, this means that if the launcher is invoked with extra arguments the args will be replaced. If that is true, the runtime bug we discussed where it adds extra bits there would override the actual command set by the user in their Procfile.

This is not necessarily a bug and could be a feature, have you any thoughts around that?

src/launch.rs Outdated
args: Vec::<String>::new(),
direct: false,
command: vec![String::from("bash")],
args: vec![String::from("-c"), format!("exec {value}")],
Copy link
Member Author

Choose a reason for hiding this comment

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

Thinking about this more, I'm not sure we need the exec here, and in fact I wonder if it would break with procfile entries like foo && bar, since the command would end up being exec foo && bar, with the exec potentially only applying to foo.

Copy link
Member

Choose a reason for hiding this comment

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

Summoning our bash wizard @dzuelke. 🪄

@edmorley
Copy link
Member Author

I'm unsure about the use of args here. The CNB spec states that they can be overridden by the user.

Ah true.

And in fact, in the RFC the example migrations they give (I've only just spotted these) use command rather than args for everything:
https://github.com/buildpacks/rfcs/blob/main/text/0093-remove-shell-processes.md#buildpack-provided-process-types
(there is also a typo in their examples, ie the missing "bash" array element, which I'll open a PR for upstream)

Which demonstrates the issues with prefixing the command with `exec`.
Since:
- for single command procfile entires, using `bash -c <command>` already implicitly uses exec (making `exec` redundant)
- for more complex compound command procfile entries exec should not be used otherwise later commands won't run (eg only `foo` would get run in `exec foo && bar`)
@edmorley edmorley marked this pull request as draft April 28, 2023 15:30
@edmorley
Copy link
Member Author

I'll explore the command vs args aspect mentioned above in more detail next week. In the meantime I've converted this back to a draft :-)

@edmorley
Copy link
Member Author

edmorley commented May 2, 2023

I'm closing this out for now since I discovered some additional complications around switching to direct=true that require compatibility changes in other buildpacks/components before we can make the switch here.

Short version:

  • When declaring a process in a buildpack, it turns out it's only safe to use direct=true when all other buildpacks that might be used alongside that buildpack no longer need implicit bash support.
  • Since cnb-shim currently relies upon implicit bash support, that means that none of our buildpacks can use direct=true until cnb-shim is updated (unless the buildpacks don't need to support being run alongside shimmed buildpacks).

Long version:

  • When using the deprecated direct=false mode, the upstream CNB launcher not only wraps the command in a bash -c invocation, but also sources any profile.d/ scripts from all buildpacks, plus any .profile script in the app source.
  • As soon as a process definition is switched to direct=true, the profile.d/ script magic for scripts contributed by other buildpacks stops happening when running that process, even if those other buildpacks are still using older buildpack API versions where direct=false is still supported. ie: A change in one buildpack (where the process type is being defined) is able to break another buildpack (the buildpack using profile.d), even though each buildpack individually is conforming to its declared Buildpack API version. (This subtlety is unfortunately missing from the upstream migration notes: https://buildpacks.io/docs/reference/spec/migration/buildpack-api-0.8-0.9/)
  • The upstream profile buildpack appears at first glance like it could help, however, it only handles the .profile script case, and not the buildpack .profile.d/ scripts, so something needs to be done for .profile.d/ scripts otherwise buildpacks using them will break.
  • Buildpacks using .profile.d/ scripts will fall into two categories: (1) native CNBs (most of which don't use .profile.d/ since they already use .exec.d/ or else can use the native env/ directory features instead), (2) shimmed CNBs (which heavily rely upon .profile.d/ scripts). The latter is the main concern.
  • Currently cnb-shim has some procfile handling of its own (since it combines any process types defined via the bin/release feature, along with any in the user's procfile). In theory, if we didn't update cnb-shim (leaving it using direct=false and older buildpack API version), then the shimmed buildpacks wouldn't break. However, in our builder images we run procfile-cnb after the shimmed buildpack, which overwrites the processes created by cnb-shim's own process handling. (And even if we stopped running procfile-cnb in our "classic" builder images, that doesn't help users using their own third party shimmed buildpacks with our other builder images.)
  • Therefore, we still need to solve the issue for shimmed CNBs, which would require updating cnb-shim to make it add an exec.d binary as part of the build, that then at runtime runs the profile.d scripts (to ensure any non-env var side effects happen) and then extracts the env vars and exports them in the key-value pair format expected of the exec.d binaries.
  • Whilst this direct=true compatibility issue isn't unique to procfile-cnb (for example it would also affect the Ruby CNB if it switched to direct=true for it's automatic console process type), I would guess that the majority of process types being declared come from procfile-cnb, so its going to have the biggest impact on compatibility.
  • Therefore, it seems we should hold off changing procfile-cnb to use direct=true (and by implication, hold off updating it to Buildpack API 0.9 / latest libcnb.rs ) until we have the time to work on cnb-shim (or otherwise decide its future), since otherwise it will pretty much break all shimmed CNBs

@edmorley
Copy link
Member Author

edmorley commented May 2, 2023

I've filed #151 to track migrating away from direct=false once cnb-shim has been updated.

@edmorley edmorley deleted the edmorley/libcnb-0.12.0 branch February 28, 2024 08:06
edmorley added a commit that referenced this pull request Jul 2, 2024
The `Procfile` format supports specifying compound bash
commands like:

```
web: foo && bar
```

This buildpack currently supports these, however, there is no
integration test coverage of them.

Having coverage is important, since some of the approaches
for implementing the `bash -c` wrapping don't support usage
of compound commands, as seen in:
#150 (comment)

As such I've added a new integration test for this, which also tests
quoting (in case a future buildpack implementation starts trying to
parse/escape the commands) and variable interpolation.

This test case was extracted from #150 (which itself was superseded).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants