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

Use shiplift HTTP Docker client library instead of CLI #216

Closed
wants to merge 22 commits into from

Conversation

ywalterh
Copy link
Contributor

@ywalterh ywalterh commented Sep 30, 2020

Followed the discussion of issue #204

  • Added shiplift library implementing basic Docker trait

  • Make shiplift implementation feature equivilent

  • Replace Cli in library and pass all test

  • Figure out Drop pattern

  • Implement GenericImageAsync

  • Implement a canned image in Async trait

  • Add more integration test to shiplift client

Worklog:

  • Creating and running container working, using tokio::runtime to get around the fact tha shiplift is mostly async
  • Created DockerAsync trait and all associated type in async
  • Figured out how to handle logging in async.
  • Use futures::executor::block_on to run Drop rm

Creating and running container working, using tokio::runtime to get
around the fact tha shiplift is mostly async
@ywalterh
Copy link
Contributor Author

Hey @thomaseizinger , shiplift library is pretty good so far. (At least the git repo is, 0.6.0 is pretty outdated)

I have a directional question: shiplift is async by nature and testcontainers-rs aren't doing that yet. Do you think I should start making the Docker trait async or use tokio::runtime to hack around it to emulate what Cli module is doing right now.

I attached some WIP code, the Docker trait is very helpful.

@thomaseizinger
Copy link
Collaborator

I have a directional question: shiplift is async by nature and testcontainers-rs aren't doing that yet. Do you think I should start making the Docker trait async or use tokio::runtime to hack around it to emulate what Cli module is doing right now.

Yes, I think making the trait async is the way to go. The migration of existing code while certainly possible would be quite tedious.

Maybe we can start with adding a DockerAsync trait that mimics the current interface but has all its functions async? Users could then choose which trait to import and we can deprecate the old one eventually (or maybe just leave it).

For async-traits, you will need https://docs.rs/async-trait/0.1.41/async_trait/.

@thomaseizinger
Copy link
Collaborator

Also, thanks for picking this up, I am excited to land this :)

@ywalterh
Copy link
Contributor Author

ywalterh commented Oct 5, 2020

I have a directional question: shiplift is async by nature and testcontainers-rs aren't doing that yet. Do you think I should start making the Docker trait async or use tokio::runtime to hack around it to emulate what Cli module is doing right now.

Yes, I think making the trait async is the way to go. The migration of existing code while certainly possible would be quite tedious.

Maybe we can start with adding a DockerAsync trait that mimics the current interface but has all its functions async? Users could then choose which trait to import and we can deprecate the old one eventually (or maybe just leave it).

For async-traits, you will need https://docs.rs/async-trait/0.1.41/async_trait/.

Got it, I'll get started on async trait route then. It'll save me a lot of dancing around with log handling too. I think it's a good approach.

src/clients/shiplift.rs Outdated Show resolved Hide resolved
src/core/container_async.rs Outdated Show resolved Hide resolved
@ywalterh ywalterh changed the title [WIP] Use shiplift Docker client library instead of cli Use shiplift HTTP Docker client library instead of CLI Nov 17, 2020
@ywalterh
Copy link
Contributor Author

@thomaseizinger ready for some constructive feedback!

Copy link
Collaborator

@thomaseizinger thomaseizinger left a comment

Choose a reason for hiding this comment

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

First, thank you so much for working on this @ywalterh! :)
Using an HTTP-based docker client is something I've envisioned from the very beginning! It is nice to see it happening now!

I've left several comments with many of them being fairly small.

There is one bigger topic regarding refactoring how wait_until_ready works. I believe it would be worthwhile try and get rid of the ImageAsync trait so that we don't have to redefine all images and they are automatically compatible with several clients.

Let me know if you are still keen to work on this! If you do, it would be good to have this refactoring as a separate commit at the beginning of this PR (or maybe even as a completely separate PR and we rebase this on top of that) so that I can easily revert the async stuff in the releases branches until shiplift cuts a release!

@@ -18,6 +18,10 @@ rand = "0.7"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sha2 = "0.9"
shiplift = {git = "https://github.com/softprops/shiplift" }
tokio = { version = "0.2", features = ["macros"] }
futures = "0.3.6"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we reduce the dependency footprint here by disabling default features and only taking what we need?

@@ -32,4 +36,5 @@ rusoto_sqs = "0.45"
spectral = "0.6"
reqwest = { version = "0.10", features = ["blocking"] }
json = "0.12"
tokio = { version = "0.2", features = ["macros"] }
tokio-test = "0.2.1"
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we are not actually using this!

@@ -18,6 +18,10 @@ rand = "0.7"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sha2 = "0.9"
shiplift = {git = "https://github.com/softprops/shiplift" }
tokio = { version = "0.2", features = ["macros"] }
Copy link
Collaborator

Choose a reason for hiding this comment

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

I believe we are only using tokio::test, correct? Then this can move to dev-dependencies!

// this is doing unnecessary stuff
let network_list_optons = NetworkListOptions::default();
let networks = client.networks().list(&network_list_optons).await.unwrap();
println!("{:?}", networks);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This should be a log statement if we want to keep it.

fn drop(&mut self) {
let guard = self.created_networks.read().expect("failed to lock RwLock");
for network in guard.iter() {
block_on(async { self.client.networks().get(network).delete().await.unwrap() });
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: This should do the trick as well :)

Suggested change
block_on(async { self.client.networks().get(network).delete().await.unwrap() });
block_on(self.client.networks().get(network).delete()).unwrap();

}

/// Log streams of running container (Stream<Item = Vec<u8>>).
#[derive(derivative::Derivative)]
Copy link
Collaborator

Choose a reason for hiding this comment

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

I've dropped the Derivative dependency in dev already. Given that we are not actually including any fields within the debug output, we can easily construct it manually and can therefore avoid this dependency!

self
}

pub fn with_mapped_port<P: Into<Port>>(mut self, port: P) -> Self {
Copy link
Collaborator

Choose a reason for hiding this comment

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

We are moving this functionality into the client with #228 to make images easier to implement. Please adapt this implementation accordingly!

pub async fn logs<'a>(&'a self) -> LogsAsync<'a> {
let id_clone: String = String::from(&self.id);
self.docker_client
.logs(Box::leak(id_clone.into_boxed_str()))
Copy link
Collaborator

Choose a reason for hiding this comment

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

This Box::leak is a bit concerning. I think we have a memory leak here if I am reading the docs correctly.

Feel free to change the signature of .logs if you don't want to mess around with manual dropping here.

///
/// Most implementations will very likely want to make use of this to wait for a particular
/// message to be emitted.
async fn wait_until_ready<D: DockerAsync + Sync>(
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is the only function within the ImageAsync trait that is actually async. I think we should refactor this whole "waiting" concept in a way that works for blocking and async clients. That would allow us to reuse the same image definition for both blocking and async clients. Currently, we need to redefine every image with the ImageAsync trait to use it with an async client :/

One idea on how to achieve this would be to remove some flexibility in what images can do here. In particular, there seem to be primarily three things that images do to determine that they are ready:

  • Wait for a message on stdout
  • Wait for a message on stderr
  • Wait for an arbitrary amount of time

I think we can model that be having a function that returns a Vec<WaitFor> where WaitFor is:

enum WaitFor {
	Message { message: String, stream: Stream },
	Time { ms: u64 }
}

@@ -18,6 +18,10 @@ rand = "0.7"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sha2 = "0.9"
shiplift = {git = "https://github.com/softprops/shiplift" }
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's hope they are cutting a release soon! We can't release with git dependencies so if we merge this before hand, we will need to do some manual work to exclude this code on every release. That shouldn't be too hard to so it is not actually a blocker for merging this PR!

GitFlow for the win! 😁

Copy link
Collaborator

Choose a reason for hiding this comment

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

To make this easier, could you squash all of the commits into a single one once we ready to merge this so I can just revert that when we make releases?

@thomaseizinger thomaseizinger linked an issue Nov 17, 2020 that may be closed by this pull request
@ywalterh
Copy link
Contributor Author

First, thank you so much for working on this @ywalterh! :)
Using an HTTP-based docker client is something I've envisioned from the very beginning! It is nice to see it happening now!

I've left several comments with many of them being fairly small.

There is one bigger topic regarding refactoring how wait_until_ready works. I believe it would be worthwhile try and get rid of the ImageAsync trait so that we don't have to redefine all images and they are automatically compatible with several clients.

Let me know if you are still keen to work on this! If you do, it would be good to have this refactoring as a separate commit at the beginning of this PR (or maybe even as a completely separate PR and we rebase this on top of that) so that I can easily revert the async stuff in the releases branches until shiplift cuts a release!

No problem at all. Sorry I was on Thanksgiving break and didn't reply to this sooner. Let me address these issues first and look into the feasibility of removing ImageAsync.

thomaseizinger added a commit that referenced this pull request Feb 1, 2021
Making this feature declarative has several advantages:

- It reduces the amount of code one needs to write for a new image.
- It avoids the need for an `ImageAsync` trait once we introduce that,
see discussions in #216 for details.
- It allows for a re-usable abstraction of waiting for a specific
amount of time, which fixes #39.
thomaseizinger added a commit that referenced this pull request Feb 1, 2021
Making this feature declarative has several advantages:

- It reduces the amount of code one needs to write for a new image.
- It avoids the need for an `ImageAsync` trait once we introduce that,
see discussions in #216 for details.
- It allows for a re-usable abstraction of waiting for a specific
amount of time, which fixes #39.
thomaseizinger added a commit that referenced this pull request Feb 2, 2021
Making this feature declarative has several advantages:

- It reduces the amount of code one needs to write for a new image.
- It avoids the need for an `ImageAsync` trait once we introduce that,
see discussions in #216 for details.
- It allows for a re-usable abstraction of waiting for a specific
amount of time, which fixes #39.
thomaseizinger added a commit that referenced this pull request Feb 2, 2021
Making this feature declarative has several advantages:

- It reduces the amount of code one needs to write for a new image.
- It avoids the need for an `ImageAsync` trait once we introduce that,
see discussions in #216 for details.
- It allows for a re-usable abstraction of waiting for a specific
amount of time, which fixes #39.
bors bot added a commit that referenced this pull request Feb 2, 2021
253: Replace `wait_until_ready` with declarative `ready_conditions` r=thomaseizinger a=thomaseizinger

Making this feature declarative has several advantages:

- It reduces the amount of code one needs to write for a new image.
- It avoids the need for an `ImageAsync` trait once we introduce that,
see discussions in #216 for details. cc @ywalterh
- It allows for a re-usable abstraction of waiting for a specific
amount of time, which fixes #39.

Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
@thomaseizinger
Copy link
Collaborator

Closing this in favor of #268.

Thank you for all the hard work @ywalterh !

bonomat pushed a commit that referenced this pull request Feb 19, 2021
Making this feature declarative has several advantages:

- It reduces the amount of code one needs to write for a new image.
- It avoids the need for an `ImageAsync` trait once we introduce that,
see discussions in #216 for details.
- It allows for a re-usable abstraction of waiting for a specific
amount of time, which fixes #39.
thomaseizinger added a commit that referenced this pull request Sep 2, 2021
Making this feature declarative has several advantages:

- It reduces the amount of code one needs to write for a new image.
- It avoids the need for an `ImageAsync` trait once we introduce that,
see discussions in #216 for details.
- It allows for a re-usable abstraction of waiting for a specific
amount of time, which fixes #39.
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.

Provide an async API
2 participants