/// <summary> /// Calls the 'scalar maintenance' verb /// </summary> /// <param name="task">Maintenance task to run</param> /// <param name="repoRoot">Repo to maintain</param> /// <param name="sessionId">Ignored</param> /// <returns> /// true if the maintenance verb succeeded, and false otherwise /// </returns> /// <remarks> /// 'CallMaintenance' should only be called for repos that are owned by /// the owner of the current process. /// /// 'launchctl asuser' *could* be used to launch has an arbitrary user, /// however, it is not used because it does not pass back the output/errors /// of the maintenance verb correctly. /// /// On Mac this method: /// /// - Is only called by Scalar.Service /// - Is only called for repos owned by the same user that's running Scalar.Service /// /// And so there is no need to use 'launchctl'. /// </remarks> public bool CallMaintenance(MaintenanceTasks.Task task, string repoRoot, int sessionId) { string taskVerbName = MaintenanceTasks.GetVerbTaskName(task); string arguments = $"run {taskVerbName} \"{repoRoot}\" --{ScalarConstants.VerbParameters.InternalUseOnly} {this.internalVerbJson}"; ProcessResult result = this.processLauncher.LaunchProcess(this.scalarBinPath, arguments, repoRoot); if (result.ExitCode != 0) { EventMetadata metadata = new EventMetadata(); metadata.Add("Area", "ScalarVerbRunner"); metadata.Add(nameof(this.scalarBinPath), this.scalarBinPath); metadata.Add(nameof(arguments), arguments); metadata.Add(nameof(repoRoot), repoRoot); metadata.Add(nameof(result.ExitCode), result.ExitCode); metadata.Add(nameof(result.Output), result.Output); metadata.Add(nameof(result.Errors), result.Errors); this.tracer.RelatedError(metadata, $"{nameof(this.CallMaintenance)}: Maintenance verb failed"); return(false); } return(true); }
private bool CallScalarMaintenance(MaintenanceTasks.Task task, string repoRoot, CurrentUser currentUser) { string taskVerbName = MaintenanceTasks.GetVerbTaskName(task); return(currentUser.RunAs( Configuration.Instance.ScalarLocation, $"run {taskVerbName} \"{repoRoot}\" --{ScalarConstants.VerbParameters.InternalUseOnly} {this.internalVerbJson}")); }
public void CallMaintenance_LaunchesVerbUsingCorrectArgs() { MaintenanceTasks.Task task = MaintenanceTasks.Task.FetchCommitsAndTrees; string taskVerbName = MaintenanceTasks.GetVerbTaskName(task); string scalarBinPath = Path.Combine(this.scalarPlatform.Constants.ScalarBinDirectoryPath, this.scalarPlatform.Constants.ScalarExecutableName); string expectedArgs = $"run {taskVerbName} \"{ExpectedActiveRepoPath}\" --{ScalarConstants.VerbParameters.InternalUseOnly} {new InternalVerbParameters(startedByService: true).ToJson()}"; Mock <MacScalarVerbRunner.ScalarProcessLauncher> procLauncherMock = new Mock <MacScalarVerbRunner.ScalarProcessLauncher>(MockBehavior.Strict, this.tracer); procLauncherMock.Setup(mp => mp.LaunchProcess( scalarBinPath, expectedArgs, ExpectedActiveRepoPath)) .Returns(new ProcessResult(output: string.Empty, errors: string.Empty, exitCode: 0)); MacScalarVerbRunner verbProcess = new MacScalarVerbRunner(this.tracer, procLauncherMock.Object); verbProcess.CallMaintenance(task, ExpectedActiveRepoPath, ExpectedActiveUserId); procLauncherMock.VerifyAll(); }
private void RunMaintenanceTaskForRepos(UserAndSession registeredUser) { EventMetadata metadata = new EventMetadata(); metadata.Add(nameof(this.task), MaintenanceTasks.GetVerbTaskName(this.task)); metadata.Add(nameof(registeredUser.UserId), registeredUser.UserId); metadata.Add(nameof(registeredUser.SessionId), registeredUser.SessionId); int reposSkipped = 0; int reposSuccessfullyRemoved = 0; int repoRemovalFailures = 0; int reposMaintained = 0; int reposInRegistryForUser = 0; bool maintenancePaused = false; string rootPath; string errorMessage; string traceMessage = null; IEnumerable <ScalarRepoRegistration> reposForUser = this.repoRegistry.GetRegisteredRepos().Where( x => x.UserId.Equals(registeredUser.UserId, StringComparison.InvariantCultureIgnoreCase)); foreach (ScalarRepoRegistration repoRegistration in reposForUser) { ++reposInRegistryForUser; if (maintenancePaused || this.IsMaintenancePaused(out traceMessage)) { metadata[nameof(traceMessage)] = traceMessage; maintenancePaused = true; ++reposSkipped; continue; } rootPath = Path.GetPathRoot(repoRegistration.NormalizedRepoRoot); metadata[nameof(repoRegistration.NormalizedRepoRoot)] = repoRegistration.NormalizedRepoRoot; metadata[nameof(rootPath)] = rootPath; metadata.Remove(nameof(errorMessage)); if (!string.IsNullOrWhiteSpace(rootPath) && !this.fileSystem.DirectoryExists(rootPath)) { ++reposSkipped; // If the volume does not exist we'll assume the drive was removed or is encrypted, // and we'll leave the repo in the registry (but we won't run maintenance on it). this.tracer.RelatedEvent( EventLevel.Informational, $"{nameof(this.RunMaintenanceTaskForRepos)}_SkippedRepoWithMissingVolume", metadata); continue; } if (!this.fileSystem.DirectoryExists(repoRegistration.NormalizedRepoRoot)) { // The repo is no longer on disk (but its volume is present) // Unregister the repo if (this.repoRegistry.TryUnregisterRepo(repoRegistration.NormalizedRepoRoot, out errorMessage)) { ++reposSuccessfullyRemoved; this.tracer.RelatedEvent( EventLevel.Informational, $"{nameof(this.RunMaintenanceTaskForRepos)}_RemovedMissingRepo", metadata); } else { ++repoRemovalFailures; metadata[nameof(errorMessage)] = errorMessage; this.tracer.RelatedEvent( EventLevel.Warning, $"{nameof(this.RunMaintenanceTaskForRepos)}_FailedToRemoveRepo", metadata); } continue; } ++reposMaintained; this.tracer.RelatedEvent( EventLevel.Informational, $"{nameof(this.RunMaintenanceTaskForRepos)}_CallingMaintenance", metadata); this.scalarVerb.CallMaintenance(this.task, repoRegistration.NormalizedRepoRoot, registeredUser.SessionId); } metadata.Add(nameof(reposInRegistryForUser), reposInRegistryForUser); metadata.Add(nameof(reposSkipped), reposSkipped); metadata.Add(nameof(reposSuccessfullyRemoved), reposSuccessfullyRemoved); metadata.Add(nameof(repoRemovalFailures), repoRemovalFailures); metadata.Add(nameof(reposMaintained), reposMaintained); metadata.Add(nameof(maintenancePaused), maintenancePaused); this.tracer.RelatedEvent( EventLevel.Informational, $"{nameof(this.RunMaintenanceTaskForRepos)}_MaintenanceSummary", metadata); }