C# + DockerCompose - дождитесь, пока контейнер Docker MS SQL Server будет запущен, прежде чем пытаться подключиться

Я пытаюсь создать интеграционные тесты для моих микросервисов, аналогичные подходу Spotify.

Я все еще работаю над тем, как раскрутить базу данных. В настоящее время у меня есть проект.NET Core 2.0 с FluentDocker v2.2.15 и DbUp 4.1.0.

Я использую FluentDocker для вызова DockerCompose и запуска своих служб, включая контейнер SQL Server.

var hosts = new Hosts().Discover();
var dockerHost = hosts.FirstOrDefault(x => x.IsNative) ?? hosts.FirstOrDefault(x => x.Name == "default");

if (dockerHost == null)
{
    return;
}

var composeFile = Args["composeFile"];

var result = dockerHost.Host.ComposeUp(composeFile: composeFile);

а затем я использую DbUp для запуска своих скриптов и заполнения базы данных.

var connectionString = Args["connectionString"];
var scriptsPath = Args["scriptsPath"];

EnsureDatabase.For.SqlDatabase(connectionString);

var upgradeEngine = DeployChanges.To.SqlDatabase(connectionString).WithScriptsFromFileSystem(scriptsPath).Build();

var result = upgradeEngine.PerformUpgrade();

Я могу запустить это успешно, когда я даю SQL Server достаточно времени для запуска, например, при отладке. Однако, если я запускаю это на полной скорости, тогда DbUp пытается подключиться к SQL Server, когда он еще не готов. FluentDocker имеет WaitForPort метод, но он, похоже, не работает с DockerCompose API.

Я хотел бы знать, есть ли способ подождать, пока порт SQL Server 1433 будет реагировать, прежде чем запускать сценарии (исключая недетерминированные тактики, такие как await Task.Delay) или если есть альтернативные библиотеки, которые позволяют мне иметь такой контроль.

Спасибо

1 ответ

Ты можешь использовать WaitForPort, WaitForProcess, WaitForHttp или пользовательская лямбда Wait функции по созданию в FluentDocker v2.6.2. Например:

Имеется файл docker-compose:

version: '3.3'
services:
  db:
    image: mysql:5.7
    volumes:
    - db_data:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: somewordpress
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress

  wordpress:
    depends_on:
    - db
    image: wordpress:latest
    ports:
    - "8000:80"
    restart: always
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
volumes:
  db_data:

Файл указывает, что WordPress зависит от БД, и, таким образом, сначала запускается БД, а затем создается экземпляр WordPress. Однако для обеспечения того, чтобы в рамках using пункт, что WordPress Web работает, вам нужно использовать HTTP, чтобы определить именно это. Вы можете использовать Fluent API для создания экземпляра и ожидания запуска службы следующим образом.

  var file = Path.Combine(Directory.GetCurrentDirectory(),
    (TemplateString) "Resources/ComposeTests/WordPress/docker-compose.yml");

using (new Builder()
            .UseContainer()
            .UseCompose()
            .FromFile(file)
            .RemoveOrphans()
            .Wait("wordpress", (service, cnt) => {
                if (cnt > 60) throw new FluentDockerException("Failed to wait for wordpress service");

                var res = HttpExtensions.DoRequest("http://localhost:8000/wp-admin/install.php").Result;            
                return (res.Code == HttpStatusCode.OK && 
                        res.Body.IndexOf("https://wordpress.org/", StringComparison.Ordinal) != -1) ? 0 : 500;
              })
            .Build().Start())
  {
    // Since we have waited - this shall now always work.       
    var installPage = await "http://localhost:8000/wp-admin/install.php".Wget();
    Assert.IsTrue(installPage.IndexOf("https://wordpress.org/", StringComparison.Ordinal) != -1);
  }

(Приведенный выше пример является более громоздким WaitForHttp но используя собственную лямбду, чтобы проиллюстрировать смысл).

Таким образом, вы можете даже использовать соединение БД и запросить таблицу перед продолжением. Возвращаемые значения выше нуля - это время ожидания следующего теста. Ноль и ниже успешно завершат ожидание. Исключение прервет ожидание (и потерпит неудачу).

В приведенном выше примере используется синтаксис FluentAPI, но вы можете вручную добавить хук к контейнеру компоновки и использовать расширения самостоятельно.

Ура, Марио

Другие вопросы по тегам