protected void ValidateClientVersions(ITracer tracer, ScalarEnlistment enlistment, ServerScalarConfig scalarConfig, bool showWarnings) { this.CheckGitVersion(tracer, enlistment, out string gitVersion); enlistment.SetGitVersion(gitVersion); string errorMessage = null; bool errorIsFatal = false; if (!this.TryValidateScalarVersion(enlistment, tracer, scalarConfig, out errorMessage, out errorIsFatal)) { if (errorIsFatal) { this.ReportErrorAndExit(tracer, errorMessage); } else if (showWarnings) { this.Output.WriteLine(); this.Output.WriteLine(errorMessage); this.Output.WriteLine(); } } }
private void TestGetRoot( HashSet <string> paths, bool expectedResult, string directory, string expectedEnlistmentRoot, string expectedWorkingDirectoryRoot) { bool actualResult = ScalarEnlistment.TryGetScalarEnlistmentRoot( directory, out string enlistmentRoot, out string workingDirectoryRoot, path => paths.Contains(path)); actualResult.ShouldEqual(expectedResult); if (!expectedResult) { return; } enlistmentRoot.ShouldEqual(expectedEnlistmentRoot); workingDirectoryRoot.ShouldEqual(expectedWorkingDirectoryRoot); }
protected override void OnStart(string[] args) { if (this.serviceThread != null) { throw new InvalidOperationException("Cannot start service twice in a row."); } // TODO: 865304 Used for functional tests and development only. Replace with a smarter appConfig-based solution string serviceName = args.FirstOrDefault(arg => arg.StartsWith(ServiceNameArgPrefix)); if (serviceName != null) { this.serviceName = serviceName.Substring(ServiceNameArgPrefix.Length); } string serviceLogsDirectoryPath = ScalarPlatform.Instance.GetLogsDirectoryForGVFSComponent(this.serviceName); // Create the logs directory explicitly *before* creating a log file event listener to ensure that it // and its ancestor directories are created with the correct ACLs. this.CreateServiceLogsDirectory(serviceLogsDirectoryPath); this.tracer.AddLogFileEventListener( ScalarEnlistment.GetNewScalarLogFileName(serviceLogsDirectoryPath, ScalarConstants.LogFileTypes.Service), EventLevel.Verbose, Keywords.Any); try { this.serviceDataLocation = ScalarPlatform.Instance.GetSecureDataRootForScalarComponent(this.serviceName); this.repoRegistryLocation = ScalarPlatform.Instance.GetCommonAppDataRootForScalarComponent(ScalarConstants.RepoRegistry.RegistryDirectoryName); this.CreateAndConfigureProgramDataDirectories(); this.Start(); } catch (Exception e) { this.LogExceptionAndExit(e, nameof(this.OnStart)); } }
protected override void OnSessionChange(SessionChangeDescription changeDescription) { try { base.OnSessionChange(changeDescription); if (!ScalarEnlistment.IsUnattended(tracer: null)) { if (changeDescription.Reason == SessionChangeReason.SessionLogon) { this.tracer.RelatedInfo("SessionLogon detected, sessionId: {0}", changeDescription.SessionId); this.TryAssignUserFromSessionId(changeDescription.SessionId); } else if (changeDescription.Reason == SessionChangeReason.SessionLogoff) { this.tracer.RelatedInfo($"SessionLogoff detected {changeDescription.SessionId}"); } } } catch (Exception e) { this.LogExceptionAndExit(e, nameof(this.OnSessionChange)); } }
private void LogEnlistmentInfoAndSetConfigValues(ITracer tracer, GitProcess git, ScalarEnlistment enlistment) { string enlistmentId = Guid.NewGuid().ToString("N"); EventMetadata metadata = this.CreateEventMetadata(); metadata.Add("Enlistment", enlistment); metadata.Add("EnlistmentId", enlistmentId); metadata.Add("EnlistmentRoot", enlistment.EnlistmentRoot); metadata.Add("PhysicalDiskInfo", ScalarPlatform.Instance.GetPhysicalDiskInfo(enlistment.WorkingDirectoryRoot, sizeStatsOnly: false)); tracer.RelatedEvent(EventLevel.Informational, "EnlistmentInfo", metadata, Keywords.Telemetry); GitProcess.Result configResult = git.SetInLocalConfig(ScalarConstants.GitConfig.EnlistmentId, enlistmentId, replaceAll: true); if (configResult.ExitCodeIsFailure) { string error = "Could not update config with enlistment id, error: " + configResult.Errors; tracer.RelatedWarning(error); } }
private Result DoClone(string fullEnlistmentRootPathParameter, string normalizedEnlistmentRootPath) { Result cloneResult = null; cloneResult = this.TryCreateEnlistment(fullEnlistmentRootPathParameter, normalizedEnlistmentRootPath, out this.enlistment); if (!cloneResult.Success) { this.tracer.RelatedError($"Error while creating enlistment: {cloneResult.ErrorMessage}"); return(cloneResult); } this.tracer.AddLogFileEventListener( ScalarEnlistment.GetNewScalarLogFileName(this.enlistment.ScalarLogsRoot, ScalarConstants.LogFileTypes.Clone), EventLevel.Informational, Keywords.Any); this.tracer.WriteStartEvent( this.enlistment.EnlistmentRoot, this.enlistment.RepoUrl, this.CacheServerUrl, this.AddVerbDataToMetadata(new EventMetadata { { nameof(this.Branch), this.Branch }, { nameof(this.LocalCacheRoot), this.LocalCacheRoot }, { nameof(this.SingleBranch), this.SingleBranch }, { nameof(this.FullClone), this.FullClone }, { nameof(this.NoFetchCommitsAndTrees), this.NoFetchCommitsAndTrees }, { nameof(this.Unattended), this.Unattended }, { nameof(ScalarPlatform.Instance.IsElevated), ScalarPlatform.Instance.IsElevated() }, { "ProcessID", Process.GetCurrentProcess().Id }, { nameof(this.EnlistmentRootPathParameter), this.EnlistmentRootPathParameter }, { nameof(fullEnlistmentRootPathParameter), fullEnlistmentRootPathParameter }, })); this.cacheServerResolver = new CacheServerResolver(this.tracer, this.enlistment); this.cacheServer = this.cacheServerResolver.ParseUrlOrFriendlyName(this.CacheServerUrl); string resolvedLocalCacheRoot; if (string.IsNullOrWhiteSpace(this.LocalCacheRoot)) { if (!LocalCacheResolver.TryGetDefaultLocalCacheRoot(this.enlistment, out resolvedLocalCacheRoot, out string localCacheRootError)) { this.ReportErrorAndExit( this.tracer, $"Failed to determine the default location for the local Scalar cache: `{localCacheRootError}`"); } } else { resolvedLocalCacheRoot = Path.GetFullPath(this.LocalCacheRoot); } this.Output.WriteLine("Clone parameters:"); this.Output.WriteLine(" Repo URL: " + this.enlistment.RepoUrl); this.Output.WriteLine(" Branch: " + (string.IsNullOrWhiteSpace(this.Branch) ? "Default" : this.Branch)); this.Output.WriteLine(" Cache Server: " + this.cacheServer); this.Output.WriteLine(" Local Cache: " + resolvedLocalCacheRoot); this.Output.WriteLine(" Destination: " + this.enlistment.EnlistmentRoot); this.Output.WriteLine(" FullClone: " + this.FullClone); string authErrorMessage; if (!this.TryAuthenticate(this.tracer, this.enlistment, out authErrorMessage)) { this.ReportErrorAndExit(this.tracer, "Cannot clone because authentication failed: " + authErrorMessage); } this.retryConfig = this.GetRetryConfig(this.tracer, this.enlistment, TimeSpan.FromMinutes(RetryConfig.FetchAndCloneTimeoutMinutes)); this.serverScalarConfig = this.QueryScalarConfig(this.tracer, this.enlistment, this.retryConfig); this.cacheServer = this.ResolveCacheServer(this.tracer, this.cacheServer, this.cacheServerResolver, this.serverScalarConfig); this.ValidateClientVersions(this.tracer, this.enlistment, this.serverScalarConfig, showWarnings: true); using (this.objectRequestor = new GitObjectsHttpRequestor(this.tracer, this.enlistment, this.cacheServer, this.retryConfig)) { cloneResult = this.CreateScalarDirctories(resolvedLocalCacheRoot); if (!cloneResult.Success) { this.tracer.RelatedError(cloneResult.ErrorMessage); return(cloneResult); } this.ShowStatusWhileRunning( () => { cloneResult = this.CreateClone(); return(cloneResult.Success); }, "Cloning"); if (!cloneResult.Success) { this.tracer.RelatedError(cloneResult.ErrorMessage); return(cloneResult); } if (!this.NoFetchCommitsAndTrees) { ReturnCode result = this.Execute <RunVerb>( this.enlistment, verb => { verb.MaintenanceTask = ScalarConstants.VerbParameters.Maintenance.FetchTaskName; verb.SkipVersionCheck = true; verb.ResolvedCacheServer = this.cacheServer; verb.ServerScalarConfig = this.serverScalarConfig; }); if (result != ReturnCode.Success) { this.Output.WriteLine("\r\nError while fetching commits and trees @ {0}", fullEnlistmentRootPathParameter); return(cloneResult); } } this.ShowStatusWhileRunning( () => { cloneResult = this.CheckoutRepo(); return(cloneResult.Success); }, "Populating working directory"); } if (cloneResult.Success) { cloneResult = this.TryRegisterRepo(); } return(cloneResult); }
protected virtual bool IsUnattended() { return(ScalarEnlistment.IsUnattended(this.tracer)); }
protected abstract void Execute(ScalarEnlistment enlistment);
private string GetAlternatesPath(ScalarEnlistment enlistment) { return(Path.Combine(enlistment.WorkingDirectoryRoot, ScalarConstants.DotGit.Objects.Info.Alternates)); }
protected override void Execute(ScalarEnlistment enlistment) { using (JsonTracer tracer = new JsonTracer(ScalarConstants.ScalarEtwProviderName, RunVerbName)) { string cacheServerUrl = CacheServerResolver.GetUrlFromConfig(enlistment); string logFileName = ScalarEnlistment.GetNewScalarLogFileName( enlistment.ScalarLogsRoot, ScalarConstants.LogFileTypes.Maintenance, logId: this.StartedByService ? "service" : null); List <GitMaintenanceStep> steps = new List <GitMaintenanceStep>(); tracer.AddLogFileEventListener( logFileName, EventLevel.Informational, Keywords.Any); tracer.WriteStartEvent( enlistment.EnlistmentRoot, enlistment.RepoUrl, cacheServerUrl, this.AddVerbDataToMetadata( new EventMetadata { { nameof(this.MaintenanceTask), this.MaintenanceTask }, { nameof(this.PackfileMaintenanceBatchSize), this.PackfileMaintenanceBatchSize }, { nameof(this.EnlistmentRootPathParameter), this.EnlistmentRootPathParameter }, { nameof(this.StartedByService), this.StartedByService }, })); this.InitializeCachePaths(tracer, enlistment); PhysicalFileSystem fileSystem = new PhysicalFileSystem(); using (ScalarContext context = new ScalarContext(tracer, fileSystem, enlistment)) { try { GitObjectsHttpRequestor objectRequestor = null; CacheServerInfo cacheServer; GitObjects gitObjects; switch (this.MaintenanceTask) { case ScalarConstants.VerbParameters.Maintenance.AllTasksName: steps.Add(new ConfigStep(context)); this.InitializeServerConnection(tracer, enlistment, cacheServerUrl, out objectRequestor, out cacheServer); gitObjects = new GitObjects(tracer, enlistment, objectRequestor, fileSystem); steps.Add(new FetchStep(context, gitObjects, requireCacheLock: false, forceRun: !this.StartedByService)); steps.Add(new CommitGraphStep(context, requireObjectCacheLock: false)); steps.Add(new LooseObjectsStep(context, forceRun: !this.StartedByService)); steps.Add(new PackfileMaintenanceStep( context, forceRun: !this.StartedByService, batchSize: string.IsNullOrWhiteSpace(this.PackfileMaintenanceBatchSize) ? PackfileMaintenanceStep.DefaultBatchSizeBytes.ToString() : this.PackfileMaintenanceBatchSize)); break; case ScalarConstants.VerbParameters.Maintenance.LooseObjectsTaskName: this.FailIfBatchSizeSet(tracer); steps.Add(new LooseObjectsStep(context, forceRun: !this.StartedByService)); break; case ScalarConstants.VerbParameters.Maintenance.PackFilesTaskName: steps.Add(new PackfileMaintenanceStep( context, forceRun: !this.StartedByService, batchSize: string.IsNullOrWhiteSpace(this.PackfileMaintenanceBatchSize) ? PackfileMaintenanceStep.DefaultBatchSizeBytes.ToString() : this.PackfileMaintenanceBatchSize)); break; case ScalarConstants.VerbParameters.Maintenance.FetchTaskName: this.FailIfBatchSizeSet(tracer); this.InitializeServerConnection(tracer, enlistment, cacheServerUrl, out objectRequestor, out cacheServer); gitObjects = new GitObjects(tracer, enlistment, objectRequestor, fileSystem); steps.Add(new FetchStep(context, gitObjects, requireCacheLock: false, forceRun: !this.StartedByService)); break; case ScalarConstants.VerbParameters.Maintenance.CommitGraphTaskName: this.FailIfBatchSizeSet(tracer); steps.Add(new CommitGraphStep(context, requireObjectCacheLock: false)); break; case ScalarConstants.VerbParameters.Maintenance.ConfigTaskName: this.FailIfBatchSizeSet(tracer); steps.Add(new ConfigStep(context)); break; default: this.ReportErrorAndExit($"Unknown maintenance task requested: '{this.MaintenanceTask}'"); break; } foreach (GitMaintenanceStep step in steps) { this.ShowStatusWhileRunning(() => { step.Execute(); return(true); }, step.ProgressMessage); } } catch (VerbAbortedException) { throw; } catch (AggregateException aggregateException) { string error = $"AggregateException thrown while running '{this.MaintenanceTask}' task: {aggregateException.Message}"; tracer.RelatedError(this.CreateEventMetadata(aggregateException), error); foreach (Exception innerException in aggregateException.Flatten().InnerExceptions) { tracer.RelatedError( this.CreateEventMetadata(innerException), $"Unhandled {innerException.GetType().Name}: {innerException.Message}"); } this.ReportErrorAndExit(tracer, ReturnCode.GenericError, error); } catch (Exception e) { string error = $"Exception thrown while running '{this.MaintenanceTask}' task: {e.Message}"; tracer.RelatedError(this.CreateEventMetadata(e), error); this.ReportErrorAndExit(tracer, ReturnCode.GenericError, error); } } } }
protected override void Execute(ScalarEnlistment enlistment) { string diagnosticsRoot = Path.Combine(enlistment.EnlistmentRoot, ".scalarDiagnostics"); if (!Directory.Exists(diagnosticsRoot)) { Directory.CreateDirectory(diagnosticsRoot); } string archiveFolderPath = Path.Combine(diagnosticsRoot, "scalar_" + DateTime.Now.ToString("yyyyMMdd_HHmmss")); Directory.CreateDirectory(archiveFolderPath); using (FileStream diagnosticLogFile = new FileStream(Path.Combine(archiveFolderPath, "diagnostics.log"), FileMode.CreateNew)) using (this.diagnosticLogFileWriter = new StreamWriter(diagnosticLogFile)) { this.WriteMessage("Collecting diagnostic info into temp folder " + archiveFolderPath); this.WriteMessage(string.Empty); this.WriteMessage("scalar version " + ProcessHelper.GetCurrentProcessVersion()); GitVersion gitVersion = null; string error = null; if (!string.IsNullOrEmpty(enlistment.GitBinPath) && GitProcess.TryGetVersion(enlistment.GitBinPath, out gitVersion, out error)) { this.WriteMessage("git version " + gitVersion.ToString()); } else { this.WriteMessage("Could not determine git version. " + error); } this.WriteMessage(enlistment.GitBinPath); this.WriteMessage(string.Empty); this.WriteMessage("Enlistment root: " + enlistment.EnlistmentRoot); this.WriteMessage("Cache Server: " + CacheServerResolver.GetCacheServerFromConfig(enlistment)); string localCacheRoot; string gitObjectsRoot; this.GetLocalCachePaths(enlistment, out localCacheRoot, out gitObjectsRoot); string actualLocalCacheRoot = !string.IsNullOrWhiteSpace(localCacheRoot) ? localCacheRoot : gitObjectsRoot; this.WriteMessage("Local Cache: " + actualLocalCacheRoot); this.WriteMessage(string.Empty); this.PrintDiskSpaceInfo(actualLocalCacheRoot, this.EnlistmentRootPathParameter); this.RecordVersionInformation(); this.ShowStatusWhileRunning( () => { // .git this.CopyAllFiles(enlistment.WorkingDirectoryRoot, archiveFolderPath, ScalarConstants.DotGit.Root, copySubFolders: false); this.CopyAllFiles(enlistment.WorkingDirectoryRoot, archiveFolderPath, ScalarConstants.DotGit.Hooks.Root, copySubFolders: false); this.CopyAllFiles(enlistment.WorkingDirectoryRoot, archiveFolderPath, ScalarConstants.DotGit.Info.Root, copySubFolders: false); this.CopyAllFiles(enlistment.WorkingDirectoryRoot, archiveFolderPath, ScalarConstants.DotGit.Logs.Root, copySubFolders: true); this.CopyAllFiles(enlistment.WorkingDirectoryRoot, archiveFolderPath, ScalarConstants.DotGit.Refs.Root, copySubFolders: true); this.CopyAllFiles(enlistment.WorkingDirectoryRoot, archiveFolderPath, ScalarConstants.DotGit.Objects.Info.Root, copySubFolders: false); this.LogDirectoryEnumeration(enlistment.WorkingDirectoryRoot, Path.Combine(archiveFolderPath, ScalarConstants.DotGit.Objects.Root), ScalarConstants.DotGit.Objects.Pack.Root, "packs-local.txt"); this.LogLooseObjectCount(enlistment.WorkingDirectoryRoot, Path.Combine(archiveFolderPath, ScalarConstants.DotGit.Objects.Root), ScalarConstants.DotGit.Objects.Root, "objects-local.txt"); // local cache this.CopyLocalCacheData(archiveFolderPath, gitObjectsRoot); // service string commonAppDataRoot = ScalarPlatform.Instance.GetCommonAppDataRootForScalar(); string secureDataRoot = ScalarPlatform.Instance.GetSecureDataRootForScalar(); this.CopyAllFiles( commonAppDataRoot, archiveFolderPath, this.ServiceName, copySubFolders: true); if (!commonAppDataRoot.Equals(secureDataRoot)) { this.CopyAllFiles( secureDataRoot, archiveFolderPath, this.ServiceName, copySubFolders: true); } // service ui this.CopyAllFiles( ScalarPlatform.Instance.GetCommonAppDataRootForScalar(), archiveFolderPath, ScalarConstants.Service.UIName, copySubFolders: true); if (ScalarPlatform.Instance.UnderConstruction.SupportsScalarUpgrade) { // upgrader this.CopyAllFiles( ProductUpgraderInfo.GetParentLogDirectoryPath(), archiveFolderPath, DeprecatedUpgradeLogsDirectory, copySubFolders: true, targetFolderName: Path.Combine(ProductUpgraderInfo.UpgradeDirectoryName, DeprecatedUpgradeLogsDirectory)); this.CopyAllFiles( ProductUpgraderInfo.GetParentLogDirectoryPath(), archiveFolderPath, ProductUpgraderInfo.LogDirectory, copySubFolders: true, targetFolderName: Path.Combine(ProductUpgraderInfo.UpgradeDirectoryName, ProductUpgraderInfo.LogDirectory)); this.LogDirectoryEnumeration( ProductUpgraderInfo.GetUpgradeProtectedDataDirectory(), Path.Combine(archiveFolderPath, ProductUpgraderInfo.UpgradeDirectoryName), ProductUpgraderInfo.DownloadDirectory, "downloaded-assets.txt"); } if (ScalarPlatform.Instance.UnderConstruction.SupportsScalarConfig) { this.CopyFile(ScalarPlatform.Instance.GetSecureDataRootForScalar(), archiveFolderPath, LocalScalarConfig.FileName); } return(true); }, "Copying logs"); this.CopyAllFiles(enlistment.DotGitRoot, archiveFolderPath, ScalarConstants.DotGit.Logs.Name, copySubFolders: false); } string zipFilePath = archiveFolderPath + ".zip"; this.ShowStatusWhileRunning( () => { ZipFile.CreateFromDirectory(archiveFolderPath, zipFilePath); this.fileSystem.DeleteDirectory(archiveFolderPath); return(true); }, "Creating zip file"); this.Output.WriteLine(); this.Output.WriteLine("Diagnostics complete. All of the gathered info, as well as all of the output above, is captured in"); this.Output.WriteLine(zipFilePath); }
private Result DoClone(string fullEnlistmentRootPathParameter, string normalizedEnlistmentRootPath) { Result cloneResult = null; cloneResult = this.TryCreateEnlistment(fullEnlistmentRootPathParameter, normalizedEnlistmentRootPath, out this.enlistment); if (!cloneResult.Success) { this.tracer.RelatedError($"Error while creating enlistment: {cloneResult.ErrorMessage}"); return(cloneResult); } this.tracer.AddLogFileEventListener( ScalarEnlistment.GetNewScalarLogFileName(this.enlistment.ScalarLogsRoot, ScalarConstants.LogFileTypes.Clone), EventLevel.Informational, Keywords.Any); this.tracer.WriteStartEvent( this.enlistment.EnlistmentRoot, this.enlistment.RepoUrl, this.CacheServerUrl, this.AddVerbDataToMetadata(new EventMetadata { { nameof(this.Branch), this.Branch }, { nameof(this.LocalCacheRoot), this.LocalCacheRoot }, { nameof(this.SingleBranch), this.SingleBranch }, { nameof(this.FullClone), this.FullClone }, { nameof(this.NoFetchCommitsAndTrees), this.NoFetchCommitsAndTrees }, { nameof(this.Unattended), this.Unattended }, { nameof(ScalarPlatform.Instance.IsElevated), ScalarPlatform.Instance.IsElevated() }, { "ProcessID", Process.GetCurrentProcess().Id }, { nameof(this.EnlistmentRootPathParameter), this.EnlistmentRootPathParameter }, { nameof(fullEnlistmentRootPathParameter), fullEnlistmentRootPathParameter }, })); this.cacheServerResolver = new CacheServerResolver(this.tracer, this.enlistment); this.cacheServer = this.cacheServerResolver.ParseUrlOrFriendlyName(this.CacheServerUrl); string resolvedLocalCacheRoot; if (string.IsNullOrWhiteSpace(this.LocalCacheRoot)) { if (!LocalCacheResolver.TryGetDefaultLocalCacheRoot(this.enlistment, out resolvedLocalCacheRoot, out string localCacheRootError)) { this.ReportErrorAndExit( this.tracer, $"Failed to determine the default location for the local Scalar cache: `{localCacheRootError}`"); } } else { resolvedLocalCacheRoot = Path.GetFullPath(this.LocalCacheRoot); } // Determine what features of Git we have available to guide how we init/clone the repository var gitFeatures = GitFeatureFlags.None; string gitBinPath = ScalarPlatform.Instance.GitInstallation.GetInstalledGitBinPath(); this.tracer.RelatedInfo("Attempting to determine Git version for installation '{0}'", gitBinPath); if (GitProcess.TryGetVersion(gitBinPath, out var gitVersion, out string gitVersionError)) { this.tracer.RelatedInfo("Git installation '{0}' has version '{1}", gitBinPath, gitVersion); gitFeatures = gitVersion.GetFeatures(); }