Created
December 8, 2017 13:00
-
-
Save thefringeninja/5b9613d626b439f1a51c0d2e68303a7c to your computer and use it in GitHub Desktop.
Run Raven Tests in Ur Docker Container
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Net; | |
using System.Threading.Tasks; | |
using Docker.DotNet; | |
using Docker.DotNet.Models; | |
using Raven.Client.Documents; | |
using Raven.Client.Documents.Session; | |
using Raven.Client.Exceptions; | |
using Raven.Client.Exceptions.Database; | |
using Raven.Client.ServerWide; | |
using Raven.Client.ServerWide.Operations; | |
internal class RavenDatabaseManager : IDisposable | |
{ | |
private static readonly Uri s_DockerUri = new Uri(Environment.OSVersion.IsWindows() ? WindowsPipe : UnixPipe); | |
private readonly int _httpPort; | |
private readonly int _tcpPort; | |
private readonly string _databaseName; | |
private readonly TaskCompletionSource<object> _databaseCreated; | |
private readonly IDocumentStore _documentStore; | |
private const string RavenDbImage = "ravendb/ravendb"; | |
private const string ContainerName = "ravendb"; | |
private const string UnixPipe = "unix:///var/run/docker.sock"; | |
private const string WindowsPipe = "npipe://./pipe/docker_engine"; | |
public RavenDatabaseManager(int httpPort = 8080, int tcpPort = 38888) | |
{ | |
_httpPort = httpPort; | |
_tcpPort = tcpPort; | |
_databaseName = Guid.NewGuid().ToString("n"); | |
_databaseCreated = new TaskCompletionSource<object>(); | |
_documentStore = new DocumentStore | |
{ | |
Urls = new[] {$"http://localhost:{_httpPort}/"} | |
}.Initialize(); | |
Task.Run(CreateDatabase); | |
} | |
public Func<IAsyncDocumentSession> CreateSessionFactory() | |
{ | |
_databaseCreated.Task.Wait(TimeSpan.FromSeconds(60)); | |
return () => _documentStore.OpenAsyncSession(_databaseName); | |
} | |
private async Task CreateDatabase() | |
{ | |
try | |
{ | |
await TryStartContainer(); | |
await TryCreateDatabase().WithTimeout(TimeSpan.FromSeconds(60)); | |
_databaseCreated.SetResult(null); | |
} | |
catch(Exception ex) | |
{ | |
_databaseCreated.SetException(ex); | |
} | |
} | |
private async Task TryCreateDatabase() | |
{ | |
while(true) | |
{ | |
try | |
{ | |
await _documentStore.Admin.Server.SendAsync(new CreateDatabaseOperation(new DatabaseRecord(_databaseName))); | |
break; | |
} | |
catch(AllTopologyNodesDownException) | |
{ } | |
} | |
while(true) | |
{ | |
try | |
{ | |
await EnsureDatabaseExists(); | |
return; | |
} | |
catch (DatabaseDoesNotExistException){} | |
} | |
} | |
private async Task EnsureDatabaseExists() | |
{ | |
using(var session = _documentStore.OpenAsyncSession(_databaseName)) | |
{ | |
await session.StoreAsync(new DummyDocument()); | |
await session.SaveChangesAsync(); | |
} | |
using(var session = _documentStore.OpenAsyncSession(_databaseName)) | |
{ | |
var dummy = await session.LoadAsync<DummyDocument>(new DummyDocument().Id); | |
session.Delete(dummy); | |
await session.SaveChangesAsync(); | |
} | |
} | |
private async Task TryStartContainer() | |
{ | |
var config = new DockerClientConfiguration(s_DockerUri); | |
var client = config.CreateClient(); | |
var images = await client.Images.ListImagesAsync(new ImagesListParameters {MatchName = RavenDbImage}); | |
if(images.Count == 0) | |
{ | |
// No image found. Pulling latest .. | |
await client.Images.CreateImageAsync(new ImagesCreateParameters {FromImage = RavenDbImage, Tag = "latest"}, null, IgnoreProgress.Forever); | |
} | |
var containers = await client.Containers.ListContainersAsync(new ContainersListParameters {All = true}); | |
if(containers.Any(container => container.Image == RavenDbImage && container.Names.Any(name => name == $"/{ContainerName}"))) | |
{ | |
return; | |
} | |
await CreateContainer(client); | |
} | |
private async Task CreateContainer(IDockerClient client) | |
{ | |
try | |
{ | |
var container = await client.Containers.CreateContainerAsync( | |
new CreateContainerParameters | |
{ | |
Image = RavenDbImage, | |
Name = ContainerName, | |
Tty = true, | |
Env = new[] | |
{ | |
"UNSECURED_ACCESS_ALLOWED=PublicNetwork", | |
$"PUBLIC_SERVER_URL=http://localhost:{_httpPort}/", | |
$"PUBLIC_TCP_SERVER_URL=tcp://localhost:{_tcpPort}/" | |
}, | |
HostConfig = new HostConfig | |
{ | |
PortBindings = new Dictionary<string, IList<PortBinding>> | |
{ | |
["8080/tcp"] = new List<PortBinding> | |
{ | |
new PortBinding | |
{ | |
HostPort = _httpPort.ToString() | |
} | |
}, | |
["38888/tcp"] = new List<PortBinding> | |
{ | |
new PortBinding | |
{ | |
HostPort = _tcpPort.ToString() | |
} | |
} | |
} | |
} | |
}); | |
// Starting the container ... | |
var started = await client.Containers.StartContainerAsync(ContainerName, new ContainerStartParameters { }); | |
if(started) | |
{ | |
for(;;) | |
{ | |
var result = await client.Containers.ListContainersAsync(new ContainersListParameters | |
{ | |
All = true, | |
Filters = new Dictionary<string, IDictionary<string, bool>> | |
{ | |
["id"] = new Dictionary<string, bool> | |
{ | |
[container.ID] = true | |
}, | |
["health"] = new Dictionary<string, bool> | |
{ | |
["healthy"] = true | |
} | |
} | |
}); | |
if(result.Any()) | |
{ | |
return; | |
} | |
} | |
} | |
} | |
catch(DockerApiException ex) | |
when(ex.StatusCode == HttpStatusCode.BadRequest) | |
{ | |
} | |
} | |
public void Dispose() | |
{ | |
try | |
{ | |
_documentStore.Admin.Server.Send(new DeleteDatabasesOperation(_databaseName, true)); | |
} | |
catch(Exception) | |
{ } | |
_documentStore.Dispose(); | |
} | |
class IgnoreProgress : IProgress<JSONMessage> | |
{ | |
public static readonly IProgress<JSONMessage> Forever = new IgnoreProgress(); | |
public void Report(JSONMessage value) | |
{ } | |
} | |
class DummyDocument | |
{ | |
public string Id { get; } = "Dummy"; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment