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

Allow starting a container without using the default process type #366

Merged
merged 9 commits into from
Mar 7, 2022

Conversation

Malax
Copy link
Member

@Malax Malax commented Mar 2, 2022

Allow starting a container without using the default process type. This enables us to test images with multiple process types, process types that take arguments as well as starting a container with a custom command.

To make the custom command feature useful, the current logs of the container are now accessible via the ContainerContext so you can match against it in tests.

Updated execd example to no longer use a bogus sleep 3600 default process as a workaround.

Changelog additions:

  • Allow starting a container without using the default process type. Removed PrepareContainerContext::start, added PrepareContainerContext::start_with_default_process, PrepareContainerContext::start_with_default_process_args, PrepareContainerContext::start_with_process, PrepareContainerContext::start_with_process_args, PrepareContainerContext::start_with_shell_command (#366)
  • Add ContainerContext::logs and ContainerContext::logs_follow to access the logs of the container. Useful when used in conjunction with the new PrepareContainerContext::start_with_shell_command method to get the shell command output. (#366)
  • Replaced container_context::ContainerExecResult with log::LogOutput which is now shared with ContainerContext::logs and ContainerContext::logs_follow. (#366)

Closes GUS-W-10539911
Closes #309

@Malax Malax force-pushed the malax/libcnb-test-non-default-process-type branch 5 times, most recently from bc3d51e to ca974ee Compare March 3, 2022 15:42
@Malax Malax marked this pull request as ready for review March 3, 2022 15:46
@Malax Malax requested review from edmorley, joshwlewis and hone March 3, 2022 15:46
libcnb-test/src/container_context.rs Outdated Show resolved Hide resolved
libcnb-test/src/container_context.rs Outdated Show resolved Hide resolved
libcnb-test/src/container_context.rs Outdated Show resolved Hide resolved
libcnb-test/CHANGELOG.md Outdated Show resolved Hide resolved
libcnb-test/Cargo.toml Outdated Show resolved Hide resolved
libcnb-test/src/container_context.rs Outdated Show resolved Hide resolved
libcnb-test/src/container_context.rs Show resolved Hide resolved
libcnb-test/src/container_context.rs Show resolved Hide resolved
@Malax Malax requested a review from edmorley March 7, 2022 09:29
@Malax Malax force-pushed the malax/libcnb-test-non-default-process-type branch from f209796 to b6e7b53 Compare March 7, 2022 11:05
@Malax Malax force-pushed the malax/libcnb-test-non-default-process-type branch from b6e7b53 to d55eae0 Compare March 7, 2022 11:36
@Malax Malax force-pushed the malax/libcnb-test-non-default-process-type branch from dd9740c to 181727b Compare March 7, 2022 14:03
libcnb-test/CHANGELOG.md Show resolved Hide resolved
@Malax Malax merged commit 424cdf9 into main Mar 7, 2022
@Malax Malax deleted the malax/libcnb-test-non-default-process-type branch March 7, 2022 14:20
Malax added a commit that referenced this pull request Mar 7, 2022
@Malax Malax mentioned this pull request Mar 7, 2022
Malax added a commit that referenced this pull request Mar 7, 2022
@schneems
Copy link
Contributor

schneems commented Jun 9, 2022

FWIW this was a meh upgrade experience. I got this error:

error[E0599]: no method named `start` found for mutable reference `&mut PrepareContainerContext<'_>` in the current scope
  --> tests/integration_test.rs:23:18
   |
23 |                 .start(|container| {
   |                  ^^^^^ method not found in `&mut PrepareContainerContext<'_>`

And so I check the readme which shows this code:

// In $CRATE_ROOT/tests/integration_test.rs
use libcnb_test::{IntegrationTest, BuildpackReference, assert_contains};

#[test]
fn test() {
    IntegrationTest::new("heroku/buildpacks:20", "test-fixtures/app")
        .buildpacks(vec![
            BuildpackReference::Other(String::from("heroku/openjdk")),
            BuildpackReference::Crate,
        ])
        .run_test(|context| {
            assert_contains!(context.pack_stdout, "---> Maven Buildpack");
            assert_contains!(context.pack_stdout, "---> Installing Maven");
            assert_contains!(context.pack_stdout, "---> Running mvn package");

            context.prepare_container().expose_port(12345).start(|container| {
                assert_eq!(
                    call_test_fixture_service(
                        container.address_for_port(12345).unwrap(),
                        "Hagbard Celine"
                    )
                    .unwrap(),
                    "enileC drabgaH"
                );
            });
        });
}

fn call_test_fixture_service(addr: std::net::SocketAddr, payload: &str) -> Result<String, ()> {
   unimplemented!()
}

So it looks like start method does exist, but there is some other problem.

I spelunk to find the context.rs file and see this change as the most recent history. Then kinda intuit from the commit name and the all the start_ methods that I should use start_with_default_process instead.

My thoughts:

  • Readme needs to be tested as this is invalid code that will not compile
  • Just because we're statically typed doesn't mean interface changes won't be intuitive. I'm wanting some kind of a "this is deprecated, use this instead" experience which I would expect from even a language like Ruby. Granted there you have to manually do it, and not everyone does. Maybe there are some better UX patterns or macros or crates or something that exist for when we rename something (not just methods, structs and implementations as well).

Overall it's not a HUGE deal. Just wanted to give stream-of-concious and in-the-moment feedback as I think it's valuable. Whether we choose to act on it or not.

schneems added a commit to heroku/buildpacks-ruby that referenced this pull request Jun 9, 2022
@edmorley
Copy link
Member

@schneems Thank you for the feedback!

Agree the libcnb-test README should be fixed - the code examples in repo root README are already tested in CI - this one is supposed to be too, but maybe the [test] annotation is interfering with that? I've filed #403.

These breaking changes were mentioned in the changelog (and the changelog gets inlined into Dependabot PRs for example, though you won't have such a PR since the Ruby Rust changes are not merged). However I agree this requires someone reading the changelog, so best if there is a more automatic solution.

Rust offers the ability to show such a warning, which we could use for future breaking changes:
https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-deprecated-attribute

That all said, it's worth bearing in mind that much of libcnb (particularly libcnb-test) is still at the very early pre-v1.0.0 stage (our team's buildpacks are the early adopters/guinea pigs for them), so we expect there to be more breaking changes/rough edges now, than there will be in the future. The changelog is there to try and make this slightly less confusing, so I would recommend reading it in the future :-)

edmorley added a commit to heroku/buildpacks-procfile that referenced this pull request Jun 17, 2022
Now that libcnb-test has added support for more methods of invoking
the built containers (see heroku/libcnb.rs#366),
we are able to add integration tests for more than just the `web` process
handling. 

Whilst the unit tests for this repository are very comprehensive, much of
the end buildpack behaviour depends on pack/lifecycle/the buildpack API
version, which aren't tested by the unit tests. For example buildpack API 0.6
changed the default process handling in a breaking way.

This PR:
- Adds five additional integration tests.
- Drops the usage of `ureq` / making HTTPS requests to the `web` process,
  in favour of a simpler `echo` / logs output based approach. The only reason
  the former approach was used was because of the "valid Procfile but not valid
  YAML" case, however that is now tested via a simpler `echo` fixture that includes
  the offending `"key: value"` substring, rather than  the `socat` example command.
- Drops usage of `tempfile` / manually writing files in the unit tests, in favour of
  reading files from the fixtures directory.
- Uses `indoc!` to assert against multi-line log output strings, to make the end
  output more clearly visible from the test, and also reduce the chance of false
  positives (for example, `Procfile declares types -> web` would previously
  successfully match against `Procfile declares types -> web, something-else`
  since there was no end of line marker).
- Uses the new `ignore = "reason"` support in Rust 1.61, to make it
  clearer why tests are being skipped when running `cargo test`.
- Adds `heroku/builder:22` to the trusted builders list, since otherwise
  it slows down builds, and also means log output has additional prefixes,
  which mess up the multi-line assertions. Longer term we will add this
  builder to Pack's built-in trusted builders list, and make `libcnb-test`
  pass `--trust-builder` to the `pack build` command:
  heroku/libcnb.rs#407

GUS-W-11311966.
edmorley added a commit to heroku/buildpacks-procfile that referenced this pull request Jun 20, 2022
Whilst the unit tests for this repository are very comprehensive, much
of the end buildpack behaviour depends on Pack CLI / `lifecycle` / the
buildpack API version, which aren't tested by the unit tests.

For example Buildpack API 0.6 changed the default process handling in a
breaking way:
https://github.com/buildpacks/spec/releases/tag/buildpack%2Fv0.6

Now that `libcnb-test` has added support for more methods of invoking
the built containers (see heroku/libcnb.rs#366),
we are able to add integration tests for more than just a single `web`
process example. 

This PR:
- Adds five additional integration tests.
- Drops the usage of `ureq` / making HTTPS requests to the `web` process,
  in favour of a simpler `echo` / logs output based approach. The only
  reason the former approach was used was because of the "valid Procfile
  but not valid YAML" case (see 741bbc0),
  however that is now tested via a simpler `echo` fixture that includes
  the offending `"key: value"` substring, rather than  the `socat`
  example command.
- Drops usage of `tempfile` / manually writing files in the unit tests,
  in favour of reading files from the fixtures directory.
- Uses `indoc!` to assert against multi-line log output strings, to make
  the end output more clearly visible from the test, and also reduce the
  chance of false positives (for example, `Procfile declares types -> web`
  would previously successfully match against
  `Procfile declares types -> web, something-else` since there was no end
  of line marker).
- Uses the new `ignore = "reason"` support in Rust 1.61, to make it
  clearer why (integration) tests are being skipped when running
  `cargo test`.
- Remove the redundant `.buildpacks(vec![BuildpackReference::Crate])`
  calls, since the current buildpack is the default when using
  `libcnb-test`'s `run_test()`.
- Removes `RUST_BACKTRACE=1` from the Circle CI configs, since in
  general the backtraces are noisy (making it harder to read the actual
  error) and add little value over the file+line number that's already
  output in the standard error message. In rare scenarios where the
  backtrace is actually useful, it can be enabled locally, or on a
  Circle CI run with a debugging commit pushed to the PR etc.
- Adds `heroku/builder:22` to the trusted builders list, since otherwise
  it slows down builds, and also means log output has additional prefixes,
  which mess up the multi-line assertions. Longer term we will add this
  builder to Pack's built-in trusted builders list, and also make
  `libcnb-test` pass `--trust-builder` to the `pack build` command:
  heroku/libcnb.rs#407

GUS-W-11311966.
@schneems
Copy link
Contributor

That all said, it's worth bearing in mind that much of libcnb (particularly libcnb-test) is still at the very early pre-v1.0.0 stage (our team's buildpacks are the early adopters/guinea pigs for them), so we expect there to be more breaking changes/rough edges now, than there will be in the future. The changelog is there to try and make this slightly less confusing, so I would recommend reading it in the future :-)

I think there's two components for me 1) is a pure technical "can we do this" and the other is the implementation side. You've answered the "can we do this" thank you.

I think the second component is more of a cultural "should we do this". I certainly understand the cost/time tradeoff. I personally fall into habits where I "play" much like I "practice". I.e. I tend to do things always or never. If I don't make a habit of going through deprecation cycles then when it's really critical to do so I'll likely forget. I believe you have more self control as well as awareness and also are good at reviewing PRs. I need more black and white policies as well as automated tooling.

I do want to push back on the "it's just us" narrative. While that's true for now, the longer we act that way, the more hostile the codebase will be to adoption by others (self fulfilling prophecy). Also even "just us" is a hefty cost/time tradeoff. I spent maybe 60 min (perhaps rounding up) investigating this. If we multiply by number of team mebers then taking an extra hour to reduce multiplicative time spent might not always come out to be the best investment but it's an investment and does have payback.

The other thing of value I gain from choosing to use deprecation cycles is that it informs the implementation and also forces us to think about "after this PR is merged how will we deprecate or remove this code if it's no longer needed". In the same way I believe in shipping tests with code as it's written as the tests will inform the API. Not so dissimilar to making plans to deprecate and remove a stack before it's even released.

Coming from Ruby the main thing's I'm hoping to gain from Rust are improved maintainability and part of that is library/ecosystem deprecations.

I don't think we disagree in a large number of these areas I am somewhat typing to get my thoughts out and clear on the subject. I'm curious in talking more about the path forward for some of this stuff regarding when a good time to flip the switch from "not now" to "now" might be. Alternatively, maybe we could make me the "deprecation czar" (or something) and I can do some of the legwork of trying to figure out the deprecation/upgrade path on breaking changes.

schneems added a commit that referenced this pull request Jun 24, 2022
In #366 (comment) I left feedback that stated the README was broken. I found we actually DO have doctests on it but even if you add a non-existent method in there it still passes. Why? Because the method has the `#[test]` line on it so it only compiles on the test environment and doctest does not use the test environment.

I removed the config declaration and added a comment explaining why.

In `lib.rs` I moved the ordering to match https://doc.rust-lang.org/rustdoc/write-documentation/documentation-tests.html but I don't think it has an impact.
schneems added a commit that referenced this pull request Jun 24, 2022
In #366 (comment) I left feedback that stated the README was broken. I found we actually DO have doctests on it but even if you add a non-existent method in there it still passes. Why? Because the method has the `#[test]` line on it so it only compiles on the test environment and doctest does not use the test environment.

I removed the config declaration and added a comment explaining why.

In `lib.rs` I moved the ordering to match https://doc.rust-lang.org/rustdoc/write-documentation/documentation-tests.html but I don't think it has an impact.
edmorley added a commit that referenced this pull request Jul 7, 2022
At the moment, there is a bit of a gulf between the longer form
README documentation we have in the GitHub repo, and the
reference documentation shown on `docs.rs` for each crate.

I believe this leads to people not really looking at `docs.rs` as much
when starting out (particularly if they are less familiar with Rust, so
aren't used to checking it by default), making discovery of features
and reference examples harder. For example, as seen in:
#366 (comment)
#402

By using the crate READMEs as the rustdocs for each crate's root
module, it means the content will show at the top of the crate's
`docs.rs` page - meaning they then show all docs, not just the
reference docs. We can then pitch the `docs.rs` page as the best
entrypoint for documentation, rather than repo READMEs.

As an added bonus, our READMEs will now all be linted for markdown
and code correctness (and not only `libcnb-test`'s README). (This is why
there are a few changes to the repo root README, to fix lint issues.)
edmorley added a commit that referenced this pull request Jul 7, 2022
At the moment, there is a bit of a gulf between the longer form
README documentation we have in the GitHub repo, and the
reference documentation shown on `docs.rs` for each crate.

I believe this leads to people not really looking at `docs.rs` as much
when starting out (particularly if they are less familiar with Rust, so
aren't used to checking it by default), making discovery of features
and reference examples harder. For example, as seen in:
#366 (comment)
#402

By using the crate READMEs as the rustdocs for each crate's root
module, it means the content will show at the top of the crate's
`docs.rs` page - meaning they then show all docs, not just the
reference docs. We can then pitch the `docs.rs` page as the best
entrypoint for documentation, rather than repo READMEs.

As an added bonus, our READMEs will now all be linted for markdown
and code correctness (and not only `libcnb-test`'s README). (This is why
there are a few changes to the repo root README, to fix lint issues.)
schneems added a commit to heroku/buildpacks-ruby that referenced this pull request Jan 31, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support running commands against the built container in integration tests
3 participants