/// <inheritdoc />
        protected override async Task ContainerStarting()
        {
            await base.ContainerStarting();

            _logger.LogDebug("Starting reaper ...");
            await ResourceReaper.StartAsync(DockerClient);
        }
Exemple #2
0
        public async Task ShouldReapContainersWhenReaperStops()
        {
            // act
            ResourceReaper.Dispose();

            // assert
            var ryukStopped = false;

            while (!ryukStopped)
            {
                try
                {
                    await _dockerClient.Containers.InspectContainerAsync(ResourceReaper.GetRyukContainerId());
                }
                catch (DockerContainerNotFoundException)
                {
                    ryukStopped = true;
                }
            }

            var exception = await Record.ExceptionAsync(async() =>
                                                        await _dockerClient.Containers.InspectContainerAsync(_container.ContainerId));

            Assert.IsType <DockerContainerNotFoundException>(exception);
        }
Exemple #3
0
        /// <inheritdoc />
        public async Task <string> RunAsync(ITestcontainersConfiguration configuration, CancellationToken ct = default)
        {
            // TODO: Workaround until we have a Windows Docker image of Ryuk
            var isWindowsEngineEnabled = await this.GetIsWindowsEngineEnabled(ct)
                                         .ConfigureAwait(false);

            if (!isWindowsEngineEnabled && ResourceReaper.DefaultSessionId.ToString("D").Equals(configuration.Labels[ResourceReaper.ResourceReaperSessionLabel], StringComparison.OrdinalIgnoreCase))
            {
                _ = await ResourceReaper.GetAndStartDefaultAsync(configuration.DockerEndpointAuthConfig, ct)
                    .ConfigureAwait(false);
            }

            if (!await this.images.ExistsWithNameAsync(configuration.Image.FullName, ct)
                .ConfigureAwait(false))
            {
                var authConfig = default(DockerRegistryAuthenticationConfiguration).Equals(configuration.DockerRegistryAuthConfig)
          ? this.registryAuthenticationProvider.GetAuthConfig(configuration.Image.GetHostname()) : configuration.DockerRegistryAuthConfig;

                await this.images.CreateAsync(configuration.Image, authConfig, ct)
                .ConfigureAwait(false);
            }

            var id = await this.containers.RunAsync(configuration, ct)
                     .ConfigureAwait(false);

            return(id);
        }
        public async Task ResourceReaperShouldTimeoutIfInitializationFails()
        {
            var resourceReaperTask = ResourceReaper.GetAndStartNewAsync(this.sessionId, null, "nginx");

            _ = await Assert.ThrowsAsync <ResourceReaperException>(() => resourceReaperTask);

            Assert.Equal(new[] { ResourceReaperState.Created, ResourceReaperState.InitializingConnection }, this.stateChanges);
        }
        public async Task GetAndStartNewAsyncShouldBeCancellableDuringInitializingConnection()
        {
            ResourceReaper.StateChanged += this.CancelOnInitializingConnection;

            var resourceReaperTask = ResourceReaper.GetAndStartNewAsync(this.sessionId, null, "nginx", TimeSpan.FromSeconds(60), this.cts.Token);

            _ = await Assert.ThrowsAsync <ResourceReaperException>(() => resourceReaperTask);

            Assert.Equal(new[] { ResourceReaperState.Created, ResourceReaperState.InitializingConnection }, this.stateChanges);
        }
        public async Task GetAndStartNewAsyncShouldBeCancellableDuringContainerStart()
        {
            ResourceReaper.StateChanged += this.CancelOnCreated;

            var resourceReaperTask = ResourceReaper.GetAndStartNewAsync(this.sessionId, null, "nginx", TimeSpan.FromSeconds(60), this.cts.Token);

            _ = await Assert.ThrowsAnyAsync <OperationCanceledException>(() => resourceReaperTask);

            Assert.Equal(new[] { ResourceReaperState.Created }, this.stateChanges);
        }
Exemple #7
0
        public async Task InitializeAsync()
        {
            if (Docker.Exists(DockerResource.Container, DefaultRyukContainerName))
            {
                var resourceReaper = await ResourceReaper.GetAndStartDefaultAsync()
                                     .ConfigureAwait(false);

                await resourceReaper.DisposeAsync()
                .ConfigureAwait(false);
            }
        }
Exemple #8
0
        public async Task ShouldReconnectIfConnectionDrops()
        {
            // arrange
            ResourceReaper.KillTcpConnection();

            // act
            ResourceReaper.RegisterFilterForCleanup(new LabelsFilter("key", "value"));

            // assert
            Assert.True(await ResourceReaper.IsConnected());
        }
        public async Task DisposeAsyncShouldAwaitConnectionTerminatedState()
        {
            // Given
            var resourceReaper = await ResourceReaper.GetAndStartNewAsync(this.sessionId)
                                 .ConfigureAwait(false);

            // When
            await resourceReaper.DisposeAsync()
            .ConfigureAwait(false);

            // Then
            Assert.Equal(Enum.GetValues(typeof(ResourceReaperState)).Cast <ResourceReaperState>(), this.stateChanges);
        }
        private async Task CreateNetwork(CancellationToken ct)
        {
            _logger.LogInformation("Creating network: {}", NetworkName);

            _logger.LogDebug("Starting reaper ...");
            await ResourceReaper.StartAsync(DockerClient, _loggerFactory);

            _logger.LogDebug("Adding session labels to network: {}", ResourceReaper.SessionId);
            foreach (var label in ResourceReaper.Labels)
            {
                Labels.Add(label.Key, label.Value);
            }

            var response = await DockerClient.Networks.CreateNetworkAsync(
                new NetworksCreateParameters { Name = NetworkName, CheckDuplicate = true, Labels = Labels }, ct);

            NetworkId = response.ID;
        }
 public Task InitializeAsync()
 {
     return(Task.WhenAll(ResourceReaper.GetAndStartNewAsync(this.SessionId), this.volume.CreateAsync()));
 }
Exemple #12
0
 public async Task InitializeAsync()
 {
     this.resourceReaper = await ResourceReaper.GetAndStartNewAsync()
                           .ConfigureAwait(false);
 }
 private bool IsTest(ResourceReaper resourceReaper)
 {
     return(resourceReaper.SessionId.Equals(this.sessionId));
 }
        /// <summary>
        /// Runs the docker image build command to build this image
        /// </summary>
        /// <inheritdoc />
        public override async Task <string> Resolve(CancellationToken ct = default)
        {
            if (ct.IsCancellationRequested)
            {
                return(null);
            }

            if (DeleteOnExit)
            {
                ResourceReaper.RegisterImageForCleanup(ImageName, DockerClient);
            }

            _logger.LogDebug("Begin building image: {}", ImageName);

            var tempTarPath = Path.Combine(Path.GetTempPath(), ImageName.Replace('/', '_') + ".tar");

            try
            {
                using (var tempFile = new FileStream(tempTarPath, FileMode.Create))
                    using (var tarArchive = TarArchive.CreateOutputTarArchive(tempFile))
                    {
                        if (!string.IsNullOrWhiteSpace(BasePath))
                        {
                            // the algorithm here is carefully crafted to minimise the use of
                            // Path.GetFullPath. Path.GetFullPath is used very sparingly and
                            // completely avoided in loops. The reason is because Path.GetFullPath
                            // is a very expensive call and can reduce CPU time by at least 1 order
                            // of magnitude if avoided
                            var fullBasePath = Path.GetFullPath(OS.NormalizePath(BasePath));

                            var ignoreFullPaths = GetIgnores(fullBasePath);

                            // sending a full path will result in entries with full path
                            var allFullPaths = GetAllFilesInDirectory(fullBasePath);

                            // a thread pool that is starved can decrease the performance of
                            // this method dramatically. Using `AsParallel()` will circumvent such issues.
                            // as a result, methods and classes used by this needs to be thread safe.
                            var validFullPaths = allFullPaths
                                                 .AsParallel()
                                                 .Where(f => !IsFileIgnored(ignoreFullPaths, f));

                            foreach (var fullPath in validFullPaths)
                            {
                                // we can safely perform a substring without expanding the paths
                                // using Path.GetFullPath because we know fullBasePath has already been
                                // expanded and the paths in validFullPaths are derived from fullBasePath
                                var relativePath = fullPath.Substring(fullBasePath.Length);

                                // if fullBasePath does not end with directory separator,
                                // relativePath will start with directory separator and that should not be the case
                                if (relativePath.StartsWith(Path.DirectorySeparatorChar.ToString()))
                                {
                                    relativePath = relativePath.Substring(1);
                                }

                                await new MountableFile(fullPath)
                                .TransferTo(tarArchive, relativePath, ct)
                                .ConfigureAwait(false);
                            }

                            _logger.LogDebug("Transferred base path [{}] into tar archive", BasePath);
                        }

                        foreach (var entry in Transferables)
                        {
                            var destinationPath = entry.Key;
                            var transferable    = entry.Value;
                            await transferable
                            .TransferTo(tarArchive, destinationPath, ct)
                            .ConfigureAwait(false);

                            _logger.LogDebug("Transferred [{}] into tar archive", destinationPath);
                        }

                        tarArchive.Close();
                    }

                if (ct.IsCancellationRequested)
                {
                    return(null);
                }

                var buildImageParameters = new ImageBuildParameters
                {
                    Dockerfile = DockerfilePath,
                    Labels     = DeleteOnExit ? ResourceReaper.Labels : null,
                    Tags       = new List <string> {
                        ImageName
                    }
                };

                using (var tempFile = new FileStream(tempTarPath, FileMode.Open))
                {
                    var output =
                        await DockerClient.Images.BuildImageFromDockerfileAsync(tempFile, buildImageParameters, ct);

                    using (var reader = new StreamReader(output))
                    {
                        while (!reader.EndOfStream)
                        {
                            _logger.LogTrace(reader.ReadLine());
                        }
                    }
                }
            }
            finally
            {
                File.Delete(tempTarPath);
            }

            _logger.LogInformation("Dockerfile image built: {}", ImageName);

            // we should not catch exceptions thrown by inspect because the image is
            // expected to be available since we've just built it
            var image = await DockerClient.Images.InspectImageAsync(ImageName, ct);

            ImageId = image.ID;

            return(ImageId);
        }