/// <summary> /// Initializes a new instance of the <see cref="XCopyDeployTestStepProcessor"/> class. /// </summary> /// <param name="testFileLocation"> /// The function that takes the name of the test step and returns the full path to the directory containing the files for the /// given test step. /// </param> /// <param name="reportFileUploader"> /// The function that is used to upload the report files for the current test step. /// </param> /// <param name="diagnostics">The object that provides the diagnostics methods for the application.</param> /// <param name="fileSystem">The object that provides access to the file system.</param> /// <param name="sectionBuilder">The section builder.</param> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="testFileLocation"/> is <see langword="null" />. /// </exception> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="reportFileUploader"/> is <see langword="null" />. /// </exception> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="diagnostics"/> is <see langword="null" />. /// </exception> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="fileSystem"/> is <see langword="null" />. /// </exception> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="sectionBuilder"/> is <see langword="null" />. /// </exception> public XCopyDeployTestStepProcessor( RetrieveFileDataForTestStep testFileLocation, UploadReportFilesForTestStep reportFileUploader, SystemDiagnostics diagnostics, IFileSystem fileSystem, ITestSectionBuilder sectionBuilder) : base(testFileLocation, reportFileUploader, diagnostics) { { Lokad.Enforce.Argument(() => fileSystem); Lokad.Enforce.Argument(() => sectionBuilder); } m_FileSystem = fileSystem; m_SectionBuilder = sectionBuilder; }
/// <summary> /// Transfers the report files back to the host. /// </summary> /// <param name="sectionBuilder">The object that stores the report messages.</param> /// <param name="testStep">The test step for which the files should be uploaded.</param> /// <param name="additionalFilesToInclude">A collection containing the additional files that should be included in the report.</param> protected void TransferReportFiles( ITestSectionBuilder sectionBuilder, TestStep testStep, IEnumerable<string> additionalFilesToInclude = null) { try { var filesToTransfer = new Dictionary<FileInfo, DirectoryInfo>(); foreach (var file in testStep.ReportFiles) { if (File.Exists(file.Path)) { filesToTransfer.Add(new FileInfo(file.Path), new DirectoryInfo(Path.GetDirectoryName(file.Path))); } } foreach (var directory in testStep.ReportDirectories) { if (Directory.Exists(directory.Path)) { var files = Directory.GetFiles(directory.Path, "*.*", SearchOption.AllDirectories); foreach (var file in files) { filesToTransfer.Add(new FileInfo(file), new DirectoryInfo(directory.Path)); } } } if (additionalFilesToInclude != null) { foreach (var file in additionalFilesToInclude) { if (File.Exists(file)) { filesToTransfer.Add(new FileInfo(file), new DirectoryInfo(Path.GetDirectoryName(file))); } } } if (testStep.ReportIncludesSystemLog) { var logFile = ConsoleExecuteConstants.LogPath(); if (File.Exists(logFile)) { filesToTransfer.Add(new FileInfo(logFile), new DirectoryInfo(Path.GetDirectoryName(logFile))); } } m_ReportFileUploader(testStep.Order, filesToTransfer); sectionBuilder.AddInformationMessage( string.Format( CultureInfo.InvariantCulture, "Uploaded report files for test step: {0}", testStep.Order)); } catch (Exception e) { sectionBuilder.AddWarningMessage( string.Format( CultureInfo.InvariantCulture, "Failed to upload report files for test step: {0}. Error was: {1}", testStep.Order, e)); } }
/// <summary> /// Initializes a new instance of the <see cref="ConsoleExecuteTestStepProcessor"/> class. /// </summary> /// <param name="testFileLocation"> /// The function that takes the name of the test step and returns the full path to the directory containing the files for the /// given test step. /// </param> /// <param name="reportFileUploader"> /// The function that is used to upload the report files for the current test step. /// </param> /// <param name="diagnostics">The object that provides the diagnostics methods for the application.</param> /// <param name="runner">The object that is used to execute console applications.</param> /// <param name="fileSystem">The object that provides access to the file system.</param> /// <param name="sectionBuilder">The section builder.</param> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="testFileLocation"/> is <see langword="null" />. /// </exception> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="reportFileUploader"/> is <see langword="null" />. /// </exception> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="diagnostics"/> is <see langword="null" />. /// </exception> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="runner"/> is <see langword="null" />. /// </exception> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="fileSystem"/> is <see langword="null" />. /// </exception> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="sectionBuilder"/> is <see langword="null" />. /// </exception> public ConsoleExecuteTestStepProcessor( RetrieveFileDataForTestStep testFileLocation, UploadReportFilesForTestStep reportFileUploader, SystemDiagnostics diagnostics, IRunConsoleApplications runner, IFileSystem fileSystem, ITestSectionBuilder sectionBuilder) : base(testFileLocation, reportFileUploader, diagnostics) { { Lokad.Enforce.Argument(() => runner); Lokad.Enforce.Argument(() => fileSystem); Lokad.Enforce.Argument(() => sectionBuilder); } m_Runner = runner; m_FileSystem = fileSystem; m_SectionBuilder = sectionBuilder; }
/// <summary> /// Constructs a new active environment proxy. /// </summary> /// <param name="environment">The specification for the environment.</param> /// <param name="preTerminateEnvironment">The action executed just prior to terminating the environment.</param> /// <param name="postTerminateEnvironment">The action executed after terminating the environment.</param> /// <param name="commands">The object that provides the commands used to communicate with the environment.</param> /// <param name="notifications">The object that provides notifications from the environment.</param> /// <param name="uploads">The object that tracks the files available for upload.</param> /// <param name="sectionBuilder"> /// The object used to write information to the report about the starting and stopping of the environment. /// </param> /// <returns>A new active environment proxy object.</returns> protected abstract IActiveEnvironment ConstructEnvironmentProxy( MachineDescription environment, Action preTerminateEnvironment, Action postTerminateEnvironment, IExecuteTestStepsCommands commands, ITestExecutionNotifications notifications, IStoreUploads uploads, ITestSectionBuilder sectionBuilder);
/// <summary> /// Load the environment. /// </summary> /// <param name="environment">The specification for the environment.</param> /// <param name="pingTimeoutInMilliseconds">The number of milliseconds that the ping operation should wait for a response.</param> /// <param name="maximumWaitTimeInMilliseconds"> /// The number of milliseconds that the operation should wait for the machine to come online. /// </param> /// <param name="pingCycleTimeInMilliseconds">The amount of time the operation should wait between consecutive ping operations.</param> /// <param name="sectionBuilder"> /// The object used to write information to the report about the starting and stopping of the environment. /// </param> /// <returns>An action that should be used to return the environment to its previous state in case the environment load fails.</returns> protected abstract Action LoadEnvironment( MachineDescription environment, int pingTimeoutInMilliseconds, int maximumWaitTimeInMilliseconds, int pingCycleTimeInMilliseconds, ITestSectionBuilder sectionBuilder);
private void ShutdownVirtualMachine(MachineDescription environment, ITestSectionBuilder sectionBuilder) { var specification = environment as HypervMachineDescription; if (specification == null) { throw new InvalidEnvironmentSpecificationException(); } // See if the host machine is alive var host = specification.HostMachineId; var hostSpecification = m_EnvironmentById(host); if (hostSpecification == null) { sectionBuilder.AddErrorMessage( string.Format( CultureInfo.InvariantCulture, "Could not locate VM host: {0}", hostSpecification.NetworkName)); sectionBuilder.FinalizeAndStore(false); throw new CouldNotLoadEnvironmentException(); } Diagnostics.Log( LevelToLog.Debug, HyperVConstants.LogPrefix, string.Format( CultureInfo.InvariantCulture, "Terminating Hyper-V virtual machine {0} [image: {1}] on host {2}", specification.Name, specification.Image, hostSpecification.Name)); // Connect to the Hyper-V host for the given environment var virtualMachine = new HypervVirtualMachine(specification.Image); try { virtualMachine.Terminate(); } catch (FailedToRestoreEnvironmentException) { sectionBuilder.AddErrorMessage( string.Format( CultureInfo.InvariantCulture, "Failed to shut down virtual machine: {0}", specification.NetworkName)); sectionBuilder.FinalizeAndStore(false); throw; } var killTime = DateTimeOffset.Now + TimeSpan.FromMilliseconds(GlobalConstants.DefaultMaximumMachineShutdownTime); while ((virtualMachine.State != HypervVirtualMachineState.TurnedOff) && (DateTimeOffset.Now <= killTime)) { Thread.Sleep(10); } try { virtualMachine.RestoreToSnapshot(specification.SnapshotToReturnTo); } catch (FailedToRestoreEnvironmentException) { sectionBuilder.AddErrorMessage( string.Format( CultureInfo.InvariantCulture, "Failed to restore virtual machine snapshot [{0}] for machine {1}", specification.SnapshotToReturnTo, specification.NetworkName)); sectionBuilder.FinalizeAndStore(false); throw; } Diagnostics.Log( LevelToLog.Debug, HyperVConstants.LogPrefix, string.Format( CultureInfo.InvariantCulture, "Hyper-V virtual machine {0} terminated", specification.Name)); sectionBuilder.AddInformationMessage( string.Format( CultureInfo.InvariantCulture, "Virtual machine shut down: {0}", specification.NetworkName)); sectionBuilder.FinalizeAndStore(true); }
/// <summary> /// Creates a new active environment based on the given specification. /// </summary> /// <param name="environment">The specification that provides the configuration for the active environment.</param> /// <param name="sectionBuilder"> /// The object used to write information to the report about the starting and stopping of the environment. /// </param> /// <param name="onUnload">The action that is executed upon test completion.</param> /// <returns>A new active environment.</returns> public IActiveEnvironment Load( MachineDescription environment, ITestSectionBuilder sectionBuilder, Action<string> onUnload) { { Lokad.Enforce.Argument(() => environment); Lokad.Enforce.Argument(() => onUnload); } Diagnostics.Log( LevelToLog.Trace, MachineConstants.LogPrefix, string.Format( CultureInfo.InvariantCulture, "Starting environment: {0}", environment.Name)); var pingTimeout = m_Configuration.HasValueFor(SharedConfigurationKeys.PingTimeoutInMilliseconds) ? m_Configuration.Value<int>(SharedConfigurationKeys.PingTimeoutInMilliseconds) : GlobalConstants.DefaultPingTimeoutInMilliseconds; var signInTimeout = m_Configuration.HasValueFor(SharedConfigurationKeys.MaximumNetworkSignInTimeInMilliseconds) ? m_Configuration.Value<int>(SharedConfigurationKeys.MaximumNetworkSignInTimeInMilliseconds) : GlobalConstants.DefaultMaximumNetworkSignInTimeInMilliseconds; var cycleTime = m_Configuration.HasValueFor(SharedConfigurationKeys.PingCycleTimeInMilliseconds) ? m_Configuration.Value<int>(SharedConfigurationKeys.PingCycleTimeInMilliseconds) : GlobalConstants.DefaultPingCycleTimeInMilliseconds; var emergencyShutdown = LoadEnvironment(environment, pingTimeout, signInTimeout, cycleTime, sectionBuilder); var endpoint = FindMachineEndpoint(environment.NetworkName, signInTimeout, cycleTime); if (endpoint == null) { Diagnostics.Log( LevelToLog.Error, MachineConstants.LogPrefix, string.Format( CultureInfo.InvariantCulture, "Could not find remote endpoint on environment: {0}", environment.Name)); emergencyShutdown(); throw new CouldNotLoadEnvironmentException(); } Diagnostics.Log( LevelToLog.Info, MachineConstants.LogPrefix, string.Format( CultureInfo.InvariantCulture, "Successfully started {0}", environment.Name)); var commands = m_Commands.CommandsFor<IExecuteTestStepsCommands>(endpoint); var notifications = m_Notifications.NotificationsFor<ITestExecutionNotifications>(endpoint); return ConstructEnvironmentProxy( environment, () => DisconnectCommunication(endpoint), () => onUnload(environment.Id), commands, notifications, m_Uploads, sectionBuilder); }
/// <summary> /// Constructs a new active environment proxy. /// </summary> /// <param name="environment">The specification for the environment.</param> /// <param name="preTerminateEnvironment">The action executed just prior to terminating the environment.</param> /// <param name="postTerminateEnvironment">The action executed after terminating the environment.</param> /// <param name="commands">The object that provides the commands used to communicate with the environment.</param> /// <param name="notifications">The object that provides notifications from the environment.</param> /// <param name="uploads">The object that tracks the files available for upload.</param> /// <param name="sectionBuilder"> /// The object used to write information to the report about the starting and stopping of the environment. /// </param> /// <returns>A new active environment proxy object.</returns> protected override IActiveEnvironment ConstructEnvironmentProxy( MachineDescription environment, Action preTerminateEnvironment, Action postTerminateEnvironment, IExecuteTestStepsCommands commands, ITestExecutionNotifications notifications, IStoreUploads uploads, ITestSectionBuilder sectionBuilder) { Action shutDownAction = () => { preTerminateEnvironment(); ShutdownVirtualMachine(environment, sectionBuilder); postTerminateEnvironment(); }; return new ActiveHypervEnvironment( environment.Id, shutDownAction, commands, notifications, uploads); }
/// <summary> /// Load the environment. /// </summary> /// <param name="environment">The specification for the environment.</param> /// <param name="pingTimeoutInMilliseconds">The number of milliseconds that the ping operation should wait for a response.</param> /// <param name="maximumWaitTimeInMilliseconds"> /// The number of milliseconds that the operation should wait for the machine to come online. /// </param> /// <param name="pingCycleTimeInMilliseconds">The amount of time the operation should wait between consecutive ping operations.</param> /// <param name="sectionBuilder"> /// The object used to write information to the report about the starting and stopping of the environment. /// </param> /// <returns>An action that should be used to return the environment to its previous state in case the environment load fails.</returns> protected override Action LoadEnvironment( MachineDescription environment, int pingTimeoutInMilliseconds, int maximumWaitTimeInMilliseconds, int pingCycleTimeInMilliseconds, ITestSectionBuilder sectionBuilder) { var specification = environment as HypervMachineDescription; if (specification == null) { throw new InvalidEnvironmentSpecificationException(); } // See if the host machine is alive var host = specification.HostMachineId; var hostSpecification = m_EnvironmentById(host); if (hostSpecification == null) { sectionBuilder.AddErrorMessage( string.Format( CultureInfo.InvariantCulture, "Unable to locate host machine: {0}", hostSpecification.NetworkName)); sectionBuilder.FinalizeAndStore(false); throw new CouldNotLoadEnvironmentException(); } // See if the host is awake and if not try to wake it up. var physicalHostSpecification = hostSpecification as PhysicalMachineDescription; var canStartRemotely = physicalHostSpecification != null && physicalHostSpecification.CanStartRemotely; if (!MachineHelpers.WakeNetworkedMachineAndWaitForNetworkSignIn( hostSpecification.NetworkName, hostSpecification.MacAddress, Diagnostics, canStartRemotely, pingTimeoutInMilliseconds, maximumWaitTimeInMilliseconds, pingCycleTimeInMilliseconds)) { sectionBuilder.AddErrorMessage( string.Format( CultureInfo.InvariantCulture, "Unable to start host machine: {0}", hostSpecification.NetworkName)); sectionBuilder.FinalizeAndStore(false); throw new CouldNotLoadEnvironmentException(); } // Connect to the Hyper-V host for the given environment var virtualMachine = new HypervVirtualMachine(specification.Image); if (virtualMachine.State == HypervVirtualMachineState.Paused || virtualMachine.State == HypervVirtualMachineState.Running) { sectionBuilder.AddWarningMessage( string.Format( CultureInfo.InvariantCulture, "Virtual machine running unexpectedly: {0}", specification.NetworkName)); sectionBuilder.AddWarningMessage( string.Format( CultureInfo.InvariantCulture, "Resetting virtual machine: {0}", specification.NetworkName)); KillAndResetVirtualMachine(virtualMachine, specification); } virtualMachine.Start(); Diagnostics.Log( LevelToLog.Debug, HyperVConstants.LogPrefix, string.Format( CultureInfo.InvariantCulture, "Started Hyper-V virtual machine {0} [image: {1}] on host {2}", specification.Name, specification.Image, hostSpecification.Name)); sectionBuilder.AddInformationMessage( string.Format( CultureInfo.InvariantCulture, "Started machine: {0}", specification.NetworkName)); return () => { Diagnostics.Log( LevelToLog.Debug, HyperVConstants.LogPrefix, string.Format( CultureInfo.InvariantCulture, "Terminating Hyper-V virtual machine {0} [image: {1}] on host {2}", specification.Name, specification.Image, hostSpecification.Name)); KillAndResetVirtualMachine(virtualMachine, specification); sectionBuilder.AddErrorMessage( string.Format( CultureInfo.InvariantCulture, "Terminated machine: {0}", specification.NetworkName)); sectionBuilder.FinalizeAndStore(false); }; }
private bool DownloadTestData( EndpointId callingEndpoint, UploadToken token, string storageDirectory, ITestSectionBuilder builder, out string testFile) { testFile = string.Empty; try { testFile = DownloadTestData(callingEndpoint, token, storageDirectory); m_Diagnostics.Log( LevelToLog.Debug, ExecutorServiceConstants.LogPrefix, string.Format( CultureInfo.InvariantCulture, Resources.Log_Messages_DownloadedTestData_WithFile, testFile)); builder.AddInformationMessage(Resources.ReportSection_Info_DownloadedTestData); } catch (Exception) { builder.AddErrorMessage(Resources.ReportSection_Error_FailedToDownloadTestData); builder.FinalizeAndStore(false); m_TestExecutionEvents.RaiseOnTestCompletion(TestExecutionResult.Failed); return false; } return true; }
private static void RollbackExecutedStepsOnFailure( List<Tuple<TestStep, IParticipateInCleanUp>> executedSteps, SystemDiagnostics diagnostics, ITestSectionBuilder sectionWriter) { try { for (int i = executedSteps.Count - 1; i > -1; i--) { var pair = executedSteps[i]; diagnostics.Log( LevelToLog.Info, ExecutorConstants.LogPrefix, string.Format( CultureInfo.InvariantCulture, Resources.Log_Messages_CleaningUpTestStep_WithStepAndTestStepType, pair.Item1.Order, pair.Item1.GetType())); pair.Item2.CleanUp(pair.Item1); } } catch (Exception e) { diagnostics.Log( LevelToLog.Error, ExecutorConstants.LogPrefix, string.Format( CultureInfo.InvariantCulture, Resources.Log_Messages_FailedToCleanUp_WithError, e)); sectionWriter.AddErrorMessage( string.Format( CultureInfo.InvariantCulture, Resources.Reporting_TestExecution_CleanupFailed_WithException, e)); } }
/// <summary> /// Activates the given machine and returns an object that can be used to manipulate /// the machine. /// </summary> /// <param name="currentContext">The current data storage context.</param> /// <param name="machine">The description of the machine.</param> /// <param name="environment">The description of the environment.</param> /// <param name="sectionBuilder"> /// The object used to write information to the report about the starting and stopping of the machine. /// </param> /// <returns>An object that is used to manipulate the active machine.</returns> private IActiveEnvironment ActivateMachineForEnvironment( IProvideTestingContext currentContext, MachineDescription machine, TestEnvironment environment, ITestSectionBuilder sectionBuilder) { lock (m_Lock) { { Lokad.Enforce.Argument(() => machine); Lokad.Enforce.With<ArgumentException>( m_Activators.Any(a => a.EnvironmentTypeToLoad == machine.GetType()), Resources.Exceptions_Messages_NoActivatorHasBeenRegisteredForTheEnvironment); } var activator = m_Activators.Find(a => a.EnvironmentTypeToLoad == machine.GetType()); Debug.Assert(activator != null, "We should have found an activator."); var activeEnvironment = activator.Load(machine, sectionBuilder, OnEnvironmentUnloaded); currentContext.MarkMachineAsActive(machine.Id); currentContext.TestEnvironmentSupportedByMachine(environment.Id, machine.Id); return activeEnvironment; } }
/// <summary> /// Load the environment. /// </summary> /// <param name="environment">The specification for the environment.</param> /// <param name="pingTimeoutInMilliseconds">The number of milliseconds that the ping operation should wait for a response.</param> /// <param name="maximumWaitTimeInMilliseconds"> /// The number of milliseconds that the operation should wait for the machine to come online. /// </param> /// <param name="pingCycleTimeInMilliseconds">The amount of time the operation should wait between consecutive ping operations.</param> /// <param name="sectionBuilder"> /// The object used to write information to the report about the starting and stopping of the environment. /// </param> /// <returns>An action that should be used to return the environment to its previous state in case the environment load fails.</returns> protected override Action LoadEnvironment( MachineDescription environment, int pingTimeoutInMilliseconds, int maximumWaitTimeInMilliseconds, int pingCycleTimeInMilliseconds, ITestSectionBuilder sectionBuilder) { var specification = environment as PhysicalMachineDescription; if (specification == null) { throw new InvalidEnvironmentSpecificationException(); } if (!MachineHelpers.WakeNetworkedMachineAndWaitForNetworkSignIn( specification.NetworkName, specification.MacAddress, Diagnostics, specification.CanStartRemotely, pingTimeoutInMilliseconds, maximumWaitTimeInMilliseconds, pingCycleTimeInMilliseconds)) { sectionBuilder.AddErrorMessage( string.Format( CultureInfo.InvariantCulture, "Unable to start machine: {0}", specification.NetworkName)); sectionBuilder.FinalizeAndStore(false); throw new CouldNotLoadEnvironmentException(); } sectionBuilder.AddInformationMessage( string.Format( CultureInfo.InvariantCulture, "Started machine: {0}", specification.NetworkName)); return () => { // Do nothing really. We can't shut machines down remotely. }; }
/// <summary> /// Constructs a new active environment proxy. /// </summary> /// <param name="environment">The specification for the environment.</param> /// <param name="preTerminateEnvironment">The action executed just prior to terminating the environment.</param> /// <param name="postTerminateEnvironment">The action executed after terminating the environment.</param> /// <param name="commands">The object that provides the commands used to communicate with the environment.</param> /// <param name="notifications">The object that provides notifications from the environment.</param> /// <param name="uploads">The object that tracks the files available for upload.</param> /// <param name="sectionBuilder"> /// The object used to write information to the report about the starting and stopping of the environment. /// </param> /// <returns>A new active environment proxy object.</returns> protected override IActiveEnvironment ConstructEnvironmentProxy( MachineDescription environment, Action preTerminateEnvironment, Action postTerminateEnvironment, IExecuteTestStepsCommands commands, ITestExecutionNotifications notifications, IStoreUploads uploads, ITestSectionBuilder sectionBuilder) { Action shutDownAction = () => { preTerminateEnvironment(); postTerminateEnvironment(); sectionBuilder.AddInformationMessage( string.Format( CultureInfo.InvariantCulture, "Terminated machine: {0}", environment.NetworkName)); sectionBuilder.FinalizeAndStore(true); }; return new ActivePhysicalMachineEnvironment( environment.Id, shutDownAction, commands, notifications, uploads); }