mariotoffia / FluentDocker

Use docker, docker-compose local and remote in tests and your .NET core/full framework apps via a FluentAPI
Apache License 2.0
1.31k stars 97 forks source link

Support for Basic Authentication in WaitForHttp Method #314

Closed w1am closed 1 month ago

w1am commented 1 month ago

I'm using FluentDocker in my test setup and need to wait for the Kafka Schema Registry to be ready before proceeding. The Schema Registry service requires Basic Authentication. When I try to pass the credentials in the URL using the WaitForHttp method, I get a 401 Unauthorized error.

Here’s a snippet of my code:

return new Builder()
    .UseContainer()
    .FromComposeFile("docker-compose.kafka.yml")
    .ServiceName(servicePrefix)
    .WithEnvironment(env)
    .RemoveOrphans()
    .NoRecreate()
    .WaitForPort(kafkaServiceName, $"{SslBrokerPort.Value}/tcp", 30_000)
    .WaitForHttp(
        schemaRegistryName,
        $"http://admin:letmein@{Options.SchemaRegistryUrl}/subjects",
        30_000
    );

In the console, I see the following error message:

2024-07-25 15:09:48 [2024-07-25 11:09:48,384] INFO 192.168.80.1 - - [25/Jul/2024:11:09:48 +0000] "GET /subjects HTTP/1.1" 401 43 "-" "-" 2 (io.confluent.rest-utils.requests)

However, using the curl command with the same credentials works perfectly:

curl -X GET http://admin:letmein@127.0.0.1:2115/subjects

The response from the server indicates that the credentials are correctly accepted:

2024-07-25 15:11:19 [2024-07-25 11:11:19,432] INFO 192.168.80.1 - admin [25/Jul/2024:11:11:19 +0000] "GET /subjects HTTP/1.1" 200 2 "-" "curl/8.7.1" 163 (io.confluent.rest-utils.requests)

Question:

Is there a way to pass Basic Authentication credentials in the WaitForHttp method within FluentDocker? If not, would it be possible to add support for this feature?

w1am commented 1 month ago

After further investigation, I discovered that FluentDocker already has a solution for this scenario that I wasn't aware of initially. There's a method called Wait that allows for custom wait conditions. This method can be used to implement waiting with basic authentication. Here's an example of how I solved my problem:

public static class FluentDockerExtensions {
    public static CompositeBuilder WaitForHttp(
        this CompositeBuilder builder,
        string serviceName,
        string url,
        int timeout,
        string username,
        string password
    ) {
        return builder.Wait(
            serviceName,
            (service, attempt) => {
                var client    = new HttpClient();
                var authToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"));
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authToken);

                try {
                    var response = client.GetAsync(url).Result;
                    if (response.IsSuccessStatusCode) {
                        Console.WriteLine($"Successfully connected to {url} on attempt {attempt}");
                        return 0;
                    }
                } catch (Exception ex) {
                    Console.WriteLine($"Attempt {attempt} failed to connect to {url}: {ex.Message}");
                }

                if (attempt >= timeout / 1000) {
                    Console.WriteLine($"Max attempts reached. Failed to connect to {url}.");
                    return -1;
                }

                Thread.Sleep(1000);
                return 1;
            }
        );
    }
}