Пример #1
0
        private static async Task <int> GetHostPortAsync(DockerCli dockerCli, string containerName, int portInContainer)
        {
            // We are depending on Docker to open ports in the host dynamically in order for our tests to be able to
            // run in parallel without worrying about port collision and flaky tests. However it appears that Docker
            // opens up a port in the host only after an attempt was made to open up a port in the container. Since a
            // port in the container could open up late because of the way a web application works (like it might be
            // initializing something which could take time), we are introducing delay for the application to be up
            // before trying to invoke the 'docker port' command.
            for (var i = 0; i < MaxRetryCount; i++)
            {
                await Task.Delay(DelayBetweenRetries);

                // Example output from `docker port <container-name> <container-port>`:
                // "0.0.0.0:32774"
                var getPortMappingResult = dockerCli.GetPortMapping(containerName, portInContainer);
                if (getPortMappingResult.ExitCode == 0)
                {
                    var stdOut      = getPortMappingResult.StdOut?.Trim().ReplaceNewLine();
                    var portMapping = stdOut?.Split(":");
                    Assert.NotNull(portMapping);
                    Assert.True(
                        (portMapping.Length == 2),
                        "Did not get the port mapping in expected format. StdOut: " + getPortMappingResult.StdOut);
                    var hostPort = Convert.ToInt32(portMapping[1]);
                    return(hostPort);
                }
                else if (getPortMappingResult.StdErr.Contains("No such container"))
                {
                    break;
                }
            }

            throw new InvalidOperationException($"Could not retreive the host port of the container {containerName}:{portInContainer}");
        }
Пример #2
0
        public static DockerRunCommandResult Run(
            this DockerCli dockerCli,
            string imageId,
            EnvironmentVariable environmentVariable,
            DockerVolume volume,
            string portMapping,
            string link,
            bool runContainerInBackground,
            string commandToExecuteOnRun,
            string[] commandArguments)
        {
            var environmentVariables = new List <EnvironmentVariable>();

            if (environmentVariable != null)
            {
                environmentVariables.Add(environmentVariable);
            }

            var volumes = new List <DockerVolume>();

            if (volume != null)
            {
                volumes.Add(volume);
            }

            return(dockerCli.Run(
                       imageId,
                       environmentVariables,
                       volumes,
                       portMapping,
                       link,
                       runContainerInBackground,
                       commandToExecuteOnRun,
                       commandArguments));
        }
Пример #3
0
        //  The sequence of steps are:
        //  1.  Copies the sample app from host machine's git repo to a different folder (under 'tmp/<reserved-name>' folder
        //      in CI machine and in the 'bin\debug\' folder in non-CI machine). The copying is necessary to not muck
        //      with git tracked samples folder and also a single sample could be used for verification in multiple tests.
        //  2.  Volume mounts the directory to the build image and build it.
        //  3.  Volume mounts the same directory to runtime image and runs the application.
        //  4.  A func supplied by the user is retried to the max of 10 retries between a delay of 1 second.
        public static async Task BuildRunAndAssertAppAsync(
            ITestOutputHelper output,
            IEnumerable <DockerVolume> volumes,
            string buildImage,
            string buildCmd,
            string[] buildArgs,
            string runtimeImageName,
            List <EnvironmentVariable> environmentVariables,
            int port,
            string link,
            string runCmd,
            string[] runArgs,
            Func <int, Task> assertAction)
        {
            var dockerCli = new DockerCli();

            // Build
            var buildAppResult = dockerCli.Run(new DockerRunArguments
            {
                ImageId = buildImage,
                EnvironmentVariables = environmentVariables,
                Volumes = volumes,
                RunContainerInBackground = false,
                CommandToExecuteOnRun    = buildCmd,
                CommandArguments         = buildArgs,
            });

            await RunAssertsAsync(
                () =>
            {
                Assert.True(buildAppResult.IsSuccess);
                return(Task.CompletedTask);
            },
                buildAppResult,
                output);

            // Run
            await RunAndAssertAppAsync(
                runtimeImageName,
                output,
                volumes,
                environmentVariables,
                port,
                link,
                runCmd,
                runArgs,
                assertAction,
                dockerCli);
        }
Пример #4
0
        //  The sequence of steps are:
        //  1.  Copies the sample app from host machine's git repo to a different folder (under 'tmp/<reserved-name>' folder
        //      in CI machine and in the 'bin\debug\' folder in non-CI machine). The copying is necessary to not muck
        //      with git tracked samples folder and also a single sample could be used for verification in multiple tests.
        //  2.  Volume mounts the directory to the build image and build it.
        //  3.  Volume mounts the same directory to runtime image and runs the application.
        //  4.  A func supplied by the user is retried to the max of 10 retries between a delay of 1 second.
        public static async Task BuildRunAndAssertAppAsync(
            ITestOutputHelper output,
            List <DockerVolume> volumes,
            string buildCmd,
            string[] buildArgs,
            string runtimeImageName,
            List <EnvironmentVariable> environmentVariables,
            string portMapping,
            string link,
            string runCmd,
            string[] runArgs,
            Func <Task> assertAction)
        {
            var dockerCli = new DockerCli();

            // Build
            var buildAppResult = dockerCli.Run(
                Settings.BuildImageName,
                environmentVariables: environmentVariables,
                volumes: volumes,
                portMapping: null,
                link: null,
                runContainerInBackground: false,
                commandToExecuteOnRun: buildCmd,
                commandArguments: buildArgs);

            await RunAssertsAsync(
                () =>
            {
                Assert.True(buildAppResult.IsSuccess);
                return(Task.CompletedTask);
            },
                buildAppResult,
                output);

            // Run
            await RunAndAssertAppAsync(
                runtimeImageName,
                output,
                volumes,
                environmentVariables,
                portMapping,
                link,
                runCmd,
                runArgs,
                assertAction,
                dockerCli);
        }
Пример #5
0
 public static DockerRunCommandResult Run(
     this DockerCli dockerCli,
     string imageId,
     string commandToExecuteOnRun,
     string[] commandArguments)
 {
     return(Run(
                dockerCli,
                imageId,
                environmentVariable: null,
                volume: null,
                portMapping: null,
                link: null,
                runContainerInBackground: false,
                commandToExecuteOnRun,
                commandArguments));
 }
Пример #6
0
 public static DockerRunCommandResult Run(
     this DockerCli dockerCli,
     string imageId,
     List <EnvironmentVariable> environmentVariables,
     List <DockerVolume> volumes,
     string portMapping,
     string link,
     bool runContainerInBackground,
     string commandToExecuteOnRun,
     string[] commandArguments)
 {
     return(dockerCli.Run(
                imageId,
                environmentVariables,
                volumes,
                portMapping,
                link,
                runContainerInBackground,
                commandToExecuteOnRun,
                commandArguments));
 }
Пример #7
0
        public static async Task RunAndAssertAppAsync(
            string imageName,
            ITestOutputHelper output,
            IEnumerable <DockerVolume> volumes,
            List <EnvironmentVariable> environmentVariables,
            int port,
            string link,
            string runCmd,
            string[] runArgs,
            Func <int, Task> assertAction,
            DockerCli dockerCli)
        {
            DockerRunCommandProcessResult runResult = null;
            var showDebugInfo = true;

            try
            {
                // Docker run the runtime container as a foreground process. This way we can catch any errors
                // that might occur when the application is being started.
                runResult = dockerCli.RunAndDoNotWaitForProcessExit(new DockerRunArguments
                {
                    ImageId = imageName,
                    EnvironmentVariables = environmentVariables,
                    Volumes               = volumes,
                    PortInContainer       = port,
                    Link                  = link,
                    CommandToExecuteOnRun = runCmd,
                    CommandArguments      = runArgs,
                });

                // An exception could have occurred when a Docker process failed to start.
                Assert.Null(runResult.Exception);
                Assert.False(runResult.Process.HasExited);

                var hostPort = await GetHostPortAsync(dockerCli, runResult.ContainerName, portInContainer : port);

                for (var i = 0; i < MaxRetryCount; i++)
                {
                    try
                    {
                        // Make sure the process is still alive and fail fast if not.
                        Assert.False(runResult.Process.HasExited);
                        await assertAction(hostPort);

                        showDebugInfo = false;
                        break;
                    }
                    catch (Exception ex) when(ex.InnerException is IOException ||
                                              ex.InnerException is SocketException)
                    {
                        if (i == MaxRetryCount - 1)
                        {
                            throw;
                        }

                        await Task.Delay(DelayBetweenRetries);
                    }
                }
            }
            finally
            {
                if (runResult != null)
                {
                    // Stop the container so that shared resources (like ports) are disposed.
                    dockerCli.StopContainer(runResult.ContainerName);

                    // Access the output and error streams after the process has exited
                    if (showDebugInfo)
                    {
                        output.WriteLine(runResult.GetDebugInfo());
                    }
                }
            }
        }
Пример #8
0
        //  The sequence of steps are:
        //  1.  Copies the sample app from host machine's git repo to a different folder (under 'tmp/<reserved-name>' folder
        //      in CI machine and in the 'bin\debug\' folder in non-CI machine). The copying is necessary to not muck
        //      with git tracked samples folder and also a single sample could be used for verification in multiple tests.
        //  2.  Volume mounts the directory to the build image and build it.
        //  3.  Volume mounts the same directory to runtime image and runs the application.
        //  4.  A func supplied by the user is retried to the max of 10 retries between a delay of 1 second.
        public static async Task BuildRunAndAssertAppAsync(
            ITestOutputHelper output,
            DockerVolume volume,
            string buildCmd,
            string[] buildArgs,
            string runtimeImageName,
            List <EnvironmentVariable> environmentVariables,
            string portMapping,
            string link,
            string runCmd,
            string[] runArgs,
            Func <Task> assertAction)
        {
            var dockerCli = new DockerCli();

            // Build
            var buildAppResult = dockerCli.Run(
                Settings.BuildImageName,
                volume,
                commandToExecuteOnRun: buildCmd,
                commandArguments: buildArgs);

            await RunAssertsAsync(
                () =>
            {
                Assert.True(buildAppResult.IsSuccess);
                return(Task.CompletedTask);
            },
                buildAppResult.GetDebugInfo());

            // Run
            DockerRunCommandProcessResult runResult = null;

            try
            {
                // Docker run the runtime container as a foreground process. This way we can catch any errors
                // that might occur when the application is being started.
                runResult = dockerCli.RunAndDoNotWaitForProcessExit(
                    runtimeImageName,
                    environmentVariables,
                    volumes: new List <DockerVolume> {
                    volume
                },
                    portMapping,
                    link,
                    runCmd,
                    runArgs);

                await RunAssertsAsync(
                    () =>
                {
                    // An exception could have occurred when a docker process failed to start.
                    Assert.Null(runResult.Exception);
                    Assert.False(runResult.Process.HasExited);
                    return(Task.CompletedTask);
                },
                    runResult.GetDebugInfo());

                for (var i = 0; i < MaxRetryCount; i++)
                {
                    await Task.Delay(TimeSpan.FromSeconds(DelayBetweenRetriesInSeconds));

                    try
                    {
                        // Make sure the process is still alive and fail fast if not alive.
                        await RunAssertsAsync(
                            async() =>
                        {
                            Assert.False(runResult.Process.HasExited);
                            await assertAction();
                        },
                            runResult.GetDebugInfo());

                        break;
                    }
                    catch (Exception ex) when(ex.InnerException is IOException || ex.InnerException is SocketException)
                    {
                        if (i == MaxRetryCount - 1)
                        {
                            output.WriteLine(runResult.GetDebugInfo());
                            throw;
                        }
                    }
                }
            }
            finally
            {
                if (runResult != null && runResult.Exception == null)
                {
                    // Stop the container so that shared resources (like ports) are disposed.
                    dockerCli.StopContainer(runResult.ContainerName);
                }
            }

            async Task RunAssertsAsync(Func <Task> action, string message)
            {
                try
                {
                    await action();
                }
                catch (Exception)
                {
                    output.WriteLine(message);
                    throw;
                }
            }
        }