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

WaitFor command exit with 0 #702

Open
tisonkun opened this issue Jul 20, 2024 · 9 comments
Open

WaitFor command exit with 0 #702

tisonkun opened this issue Jul 20, 2024 · 9 comments
Labels
enhancement New feature or request question Further information is requested

Comments

@tisonkun
Copy link

Some image, like Postgres, would have a best match wait strategy like pg_isready returns 0.

Maybe we can implement such a wait strategy for testing with command?

@DDtKey
Copy link
Collaborator

DDtKey commented Jul 20, 2024

Hi @tisonkun 👋

Could you elaborate on this, please?

In general, both WaitFor(starting from 0.20.0) and CmdWaitFor supports waiting for exit code.

Also, Image implementation can override exec_after_start, returning ExecCommands with their own CmdWaitFor (including exit code)

But I'm not sure I got the request correctly

@tisonkun
Copy link
Author

Also, Image implementation can override exec_after_start, returning ExecCommands with their own CmdWaitFor (including exit code)

This seems the way I'd like to go.

If exec_after_start can block start and thus start will finish after exec_after_start success (with retry), then it sounds a good solution.

@DDtKey
Copy link
Collaborator

DDtKey commented Jul 21, 2024

Yes, these commands are executed before returning Container instance form the start

But there is no retries of the command itself. We retry only the check of the exit status (because it might be a long-running command). Workaround could be to include retry logic in the command itself (loop for example)

But sounds like a candidate for a separate config option 🤔

@tisonkun
Copy link
Author

@DDtKey I try:

    fn exec_after_start(&self, _: ContainerState) -> Result<Vec<ExecCommand>, TestcontainersError> {
        Ok(vec![ExecCommand::new([
            "pg_isready",
            "-U",
            USERNAME,
            "-d",
            "postgres",
        ])
        .with_cmd_ready_condition(CmdWaitFor::exit_code(0))])
    }

But it panics with called Result::unwrap()on anErr value: Exec(ExitCodeMismatch { expected: 0, actual: 2 }) insteadof retry until exit with 0.

@DDtKey
Copy link
Collaborator

DDtKey commented Jul 22, 2024

Yes, as I mentioned above - there is no retry of the command itself for now. It seems to be a candidate for a separate feature(?).

But as a workaround you may consider to use while or until loops with bash for example 🤔

Something like:

until pg_isready -U USERNAME -d postgres; do sleep 1; done

@DDtKey
Copy link
Collaborator

DDtKey commented Jul 22, 2024

Btw, speaking of postgres, do such conditions not work for you?

https://github.com/testcontainers/testcontainers-rs-modules-community/blob/66bbad597d4bbed30ef210e6a0afdb64089a3bb7/src/postgres/mod.rs#L86 (based on this issue)

These ones are widely used across different implementations of testcontainers (Java, Go, etc)

I'd like to know if there are any issues, because it may improve the existing community module.

@DDtKey DDtKey added the question Further information is requested label Jul 23, 2024
@ns-sjorgedeaguiar
Copy link
Contributor

ns-sjorgedeaguiar commented Aug 15, 2024

Here's my workaround to wait until a command finishes and printing errors when the exit code is not 0:

let mut result = self
    .container
    .exec(ExecCommand::new(cmd))
    .await
    .expect("Failed to execute command in container");

let mut timeout = Duration::from_secs(5);
let mut exit_code = result.exit_code().await?;
loop {
    if exit_code.is_some() {
        break;
    }

    tokio::time::sleep(Duration::from_millis(100)).await;
    timeout -= Duration::from_millis(100);

    if timeout.as_millis() <= 0 {
        return Err("Timeout while waiting command to finish".into());
    }

    exit_code = result.exit_code().await?;
}

match exit_code {
    Some(0) => {}
    _ => {
        let code = result.exit_code().await?.unwrap_or(-1);
        let mut buffer = String::new();
        let mut stderr = result.stderr();
        stderr.read_to_string(&mut buffer).await?;
        buffer.split('\n').for_each(|line| {
            if !line.is_empty() {
                eprintln!("stderr: {}", line);
            }
        });
        return Err(format!("Failed to execute command (code = {})", code).into());
    }
};

Might be useful for you case as well.

@DDtKey
Copy link
Collaborator

DDtKey commented Aug 15, 2024

I think ability to retry command should be incorporated into testcontainers. See no issues with that.
Let's keep this item, I'll implement it later if if no one willing to contribute appears (any help is appreciated since I have limited bandwidth and a number of needed features)

@DDtKey DDtKey added the enhancement New feature or request label Sep 10, 2024
@AntoinePrv
Copy link

I came looking for this for Postgres as well. The message on stdout can do for me, but it would be nice to be able to use pg_isready.

For instance, testcontainers Node has:

.withWaitStrategy(Wait.forSuccessfulCommand(`pg_isready -d ${POSTGRES_DB}`))

https://node.testcontainers.org/features/wait-strategies/#shell-command

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request question Further information is requested
Projects
None yet
Development

No branches or pull requests

4 participants