예제 #1
0
        protected CacheServerInfo ResolveCacheServer(
            ITracer tracer,
            CacheServerInfo cacheServer,
            CacheServerResolver cacheServerResolver,
            ServerGSDConfig serverGSDConfig)
        {
            CacheServerInfo resolvedCacheServer = cacheServer;

            if (cacheServer.Url == null)
            {
                string cacheServerName = cacheServer.Name;
                string error           = null;

                if (!cacheServerResolver.TryResolveUrlFromRemote(
                        cacheServerName,
                        serverGSDConfig,
                        out resolvedCacheServer,
                        out error))
                {
                    this.ReportErrorAndExit(tracer, error);
                }
            }
            else if (cacheServer.Name.Equals(CacheServerInfo.ReservedNames.UserDefined))
            {
                resolvedCacheServer = cacheServerResolver.ResolveNameFromRemote(cacheServer.Url, serverGSDConfig);
            }

            this.Output.WriteLine("Using cache server: " + resolvedCacheServer);
            return(resolvedCacheServer);
        }
        public bool TryResolveUrlFromRemote(
            string cacheServerName,
            ServerGSDConfig serverGSDConfig,
            out CacheServerInfo cacheServer,
            out string error)
        {
            if (string.IsNullOrWhiteSpace(cacheServerName))
            {
                throw new InvalidOperationException("An empty name is not supported");
            }

            cacheServer = null;
            error       = null;

            if (cacheServerName.Equals(CacheServerInfo.ReservedNames.Default, StringComparison.OrdinalIgnoreCase))
            {
                cacheServer =
                    serverGSDConfig.CacheServers.FirstOrDefault(cache => cache.GlobalDefault)
                    ?? this.CreateNone();
            }
            else
            {
                cacheServer = serverGSDConfig.CacheServers.FirstOrDefault(cache =>
                                                                          cache.Name.Equals(cacheServerName, StringComparison.OrdinalIgnoreCase));

                if (cacheServer == null)
                {
                    error = "No cache server found with name " + cacheServerName;
                    return(false);
                }
            }

            return(true);
        }
예제 #3
0
        protected override void Execute(GSDEnlistment enlistment)
        {
            this.BlockEmptyCacheServerUrl(this.CacheToSet);

            RetryConfig retryConfig = new RetryConfig(RetryConfig.DefaultMaxRetries, TimeSpan.FromMinutes(RetryConfig.FetchAndCloneTimeoutMinutes));

            using (ITracer tracer = new JsonTracer(GSDConstants.GSDEtwProviderName, "CacheVerb"))
            {
                string authErrorMessage;
                if (!this.TryAuthenticate(tracer, enlistment, out authErrorMessage))
                {
                    this.ReportErrorAndExit(tracer, "Authentication failed: " + authErrorMessage);
                }

                ServerGSDConfig serverGSDConfig = this.QueryGSDConfig(tracer, enlistment, retryConfig);

                CacheServerResolver cacheServerResolver = new CacheServerResolver(tracer, enlistment);
                string error = null;

                if (this.CacheToSet != null)
                {
                    CacheServerInfo cacheServer = cacheServerResolver.ParseUrlOrFriendlyName(this.CacheToSet);
                    cacheServer = this.ResolveCacheServer(tracer, cacheServer, cacheServerResolver, serverGSDConfig);

                    if (!cacheServerResolver.TrySaveUrlToLocalConfig(cacheServer, out error))
                    {
                        this.ReportErrorAndExit("Failed to save cache to config: " + error);
                    }

                    this.Output.WriteLine("You must remount GSD for this to take effect.");
                }
                else if (this.ListCacheServers)
                {
                    List <CacheServerInfo> cacheServers = serverGSDConfig.CacheServers.ToList();

                    if (cacheServers != null && cacheServers.Any())
                    {
                        this.Output.WriteLine();
                        this.Output.WriteLine("Available cache servers for: " + enlistment.RepoUrl);
                        foreach (CacheServerInfo cacheServer in cacheServers)
                        {
                            this.Output.WriteLine(cacheServer);
                        }
                    }
                    else
                    {
                        this.Output.WriteLine("There are no available cache servers for: " + enlistment.RepoUrl);
                    }
                }
                else
                {
                    string          cacheServerUrl = CacheServerResolver.GetUrlFromConfig(enlistment);
                    CacheServerInfo cacheServer    = cacheServerResolver.ResolveNameFromRemote(cacheServerUrl, serverGSDConfig);

                    this.Output.WriteLine("Using cache server: " + cacheServer);
                }
            }
        }
예제 #4
0
        private bool TryValidateGSDVersion(GSDEnlistment enlistment, ITracer tracer, ServerGSDConfig config, out string errorMessage, out bool errorIsFatal)
        {
            errorMessage = null;
            errorIsFatal = false;

            using (ITracer activity = tracer.StartActivity("ValidateGSDVersion", EventLevel.Informational))
            {
                Version currentVersion = new Version(ProcessHelper.GetCurrentProcessVersion());

                IEnumerable <ServerGSDConfig.VersionRange> allowedGvfsClientVersions =
                    config != null
                    ? config.AllowedGSDClientVersions
                    : null;

                if (allowedGvfsClientVersions == null || !allowedGvfsClientVersions.Any())
                {
                    errorMessage = "WARNING: Unable to validate your GSD version" + Environment.NewLine;
                    if (config == null)
                    {
                        errorMessage += "Could not query valid GSD versions from: " + Uri.EscapeUriString(enlistment.RepoUrl);
                    }
                    else
                    {
                        errorMessage += "Server not configured to provide supported GSD versions";
                    }

                    return(false);
                }

                foreach (ServerGSDConfig.VersionRange versionRange in config.AllowedGSDClientVersions)
                {
                    if (currentVersion >= versionRange.Min &&
                        (versionRange.Max == null || currentVersion <= versionRange.Max))
                    {
                        activity.RelatedEvent(
                            EventLevel.Informational,
                            "GSDVersionValidated",
                            new EventMetadata
                        {
                            { "SupportedVersionRange", versionRange },
                        });

                        enlistment.SetGSDVersion(currentVersion.ToString());
                        return(true);
                    }
                }

                activity.RelatedError("GSD version {0} is not supported", currentVersion);
            }

            errorMessage = "ERROR: Your GSD version is no longer supported.  Install the latest and try again.";
            errorIsFatal = true;
            return(false);
        }
        public CacheServerInfo ResolveNameFromRemote(
            string cacheServerUrl,
            ServerGSDConfig serverGSDConfig)
        {
            if (string.IsNullOrWhiteSpace(cacheServerUrl))
            {
                throw new InvalidOperationException("An empty url is not supported");
            }

            if (this.InputMatchesEnlistmentUrl(cacheServerUrl))
            {
                return(this.CreateNone());
            }

            return
                (serverGSDConfig.CacheServers.FirstOrDefault(cache => cache.Url.Equals(cacheServerUrl, StringComparison.OrdinalIgnoreCase))
                 ?? new CacheServerInfo(cacheServerUrl, CacheServerInfo.ReservedNames.UserDefined));
        }
예제 #6
0
        private void InitializeServerConnection(
            ITracer tracer,
            GSDEnlistment enlistment,
            string cacheServerUrl,
            out GitObjectsHttpRequestor objectRequestor,
            out CacheServerInfo cacheServer)
        {
            RetryConfig retryConfig = this.GetRetryConfig(tracer, enlistment, TimeSpan.FromMinutes(RetryConfig.FetchAndCloneTimeoutMinutes));

            cacheServer = this.ResolvedCacheServer;
            ServerGSDConfig serverGSDConfig = this.ServerGSDConfig;

            if (!this.SkipVersionCheck)
            {
                string authErrorMessage;
                if (!this.TryAuthenticate(tracer, enlistment, out authErrorMessage))
                {
                    this.ReportErrorAndExit(tracer, "Unable to prefetch because authentication failed: " + authErrorMessage);
                }

                if (serverGSDConfig == null)
                {
                    serverGSDConfig = this.QueryGSDConfig(tracer, enlistment, retryConfig);
                }

                if (cacheServer == null)
                {
                    CacheServerResolver cacheServerResolver = new CacheServerResolver(tracer, enlistment);
                    cacheServer = cacheServerResolver.ResolveNameFromRemote(cacheServerUrl, serverGSDConfig);
                }

                this.ValidateClientVersions(tracer, enlistment, serverGSDConfig, showWarnings: false);

                this.Output.WriteLine("Configured cache server: " + cacheServer);
            }

            this.InitializeLocalCacheAndObjectsPaths(tracer, enlistment, retryConfig, serverGSDConfig, cacheServer);
            objectRequestor = new GitObjectsHttpRequestor(tracer, enlistment, cacheServer, retryConfig);
        }
예제 #7
0
        protected ServerGSDConfig QueryGSDConfig(ITracer tracer, GSDEnlistment enlistment, RetryConfig retryConfig)
        {
            ServerGSDConfig serverGSDConfig = null;
            string          errorMessage    = null;

            if (!this.ShowStatusWhileRunning(
                    () =>
            {
                using (ConfigHttpRequestor configRequestor = new ConfigHttpRequestor(tracer, enlistment, retryConfig))
                {
                    const bool LogErrors = true;
                    return(configRequestor.TryQueryGSDConfig(LogErrors, out serverGSDConfig, out _, out errorMessage));
                }
            },
                    "Querying remote for config",
                    suppressGvfsLogMessage: true))
            {
                this.ReportErrorAndExit(tracer, "Unable to query /gvfs/config" + Environment.NewLine + errorMessage);
            }

            return(serverGSDConfig);
        }
예제 #8
0
        private bool TryDetermineLocalCacheAndInitializePaths(
            ITracer tracer,
            GSDEnlistment enlistment,
            ServerGSDConfig serverGSDConfig,
            CacheServerInfo currentCacheServer,
            string localCacheRoot,
            out string errorMessage)
        {
            errorMessage = null;
            LocalCacheResolver localCacheResolver = new LocalCacheResolver(enlistment);

            string error;
            string localCacheKey;

            if (!localCacheResolver.TryGetLocalCacheKeyFromLocalConfigOrRemoteCacheServers(
                    tracer,
                    serverGSDConfig,
                    currentCacheServer,
                    localCacheRoot,
                    localCacheKey: out localCacheKey,
                    errorMessage: out error))
            {
                errorMessage = "Error determining local cache key: " + error;
                return(false);
            }

            EventMetadata metadata = new EventMetadata();

            metadata.Add("localCacheRoot", localCacheRoot);
            metadata.Add("localCacheKey", localCacheKey);
            metadata.Add(TracingConstants.MessageKey.InfoMessage, "Initializing cache paths");
            tracer.RelatedEvent(EventLevel.Informational, "CloneVerb_TryDetermineLocalCacheAndInitializePaths", metadata);

            enlistment.InitializeCachePathsFromKey(localCacheRoot, localCacheKey);

            return(true);
        }
예제 #9
0
            protected void InitializeLocalCacheAndObjectsPaths(
                ITracer tracer,
                GSDEnlistment enlistment,
                RetryConfig retryConfig,
                ServerGSDConfig serverGSDConfig,
                CacheServerInfo cacheServer)
            {
                string error;

                if (!RepoMetadata.TryInitialize(tracer, Path.Combine(enlistment.EnlistmentRoot, GSDPlatform.Instance.Constants.DotGSDRoot), out error))
                {
                    this.ReportErrorAndExit(tracer, "Failed to initialize repo metadata: " + error);
                }

                this.InitializeCachePathsFromRepoMetadata(tracer, enlistment);

                // Note: Repos cloned with a version of GSD that predates the local cache will not have a local cache configured
                if (!string.IsNullOrWhiteSpace(enlistment.LocalCacheRoot))
                {
                    this.EnsureLocalCacheIsHealthy(tracer, enlistment, retryConfig, serverGSDConfig, cacheServer);
                }

                RepoMetadata.Shutdown();
            }
예제 #10
0
        protected override void Execute(GSDEnlistment enlistment)
        {
            string errorMessage            = null;
            string mountExecutableLocation = null;

            using (JsonTracer tracer = new JsonTracer(GSDConstants.GSDEtwProviderName, "ExecuteMount"))
            {
                PhysicalFileSystem fileSystem = new PhysicalFileSystem();
                GitRepo            gitRepo    = new GitRepo(tracer, enlistment, fileSystem);
                GSDContext         context    = new GSDContext(tracer, fileSystem, gitRepo, enlistment);

                if (!HooksInstaller.InstallHooks(context, out errorMessage))
                {
                    this.ReportErrorAndExit("Error installing hooks: " + errorMessage);
                }

                CacheServerInfo cacheServer = this.ResolvedCacheServer ?? CacheServerResolver.GetCacheServerFromConfig(enlistment);

                tracer.AddLogFileEventListener(
                    GSDEnlistment.GetNewGSDLogFileName(enlistment.GSDLogsRoot, GSDConstants.LogFileTypes.MountVerb),
                    EventLevel.Verbose,
                    Keywords.Any);
                tracer.WriteStartEvent(
                    enlistment.EnlistmentRoot,
                    enlistment.RepoUrl,
                    cacheServer.Url,
                    new EventMetadata
                {
                    { "Unattended", this.Unattended },
                    { "IsElevated", GSDPlatform.Instance.IsElevated() },
                    { "NamedPipeName", enlistment.NamedPipeName },
                    { nameof(this.EnlistmentRootPathParameter), this.EnlistmentRootPathParameter },
                });

                RetryConfig     retryConfig     = null;
                ServerGSDConfig serverGSDConfig = this.DownloadedGSDConfig;
                if (!this.SkipVersionCheck)
                {
                    string authErrorMessage;
                    if (!this.TryAuthenticate(tracer, enlistment, out authErrorMessage))
                    {
                        this.Output.WriteLine("    WARNING: " + authErrorMessage);
                        this.Output.WriteLine("    Mount will proceed, but new files cannot be accessed until GSD can authenticate.");
                    }

                    if (serverGSDConfig == null)
                    {
                        if (retryConfig == null)
                        {
                            retryConfig = this.GetRetryConfig(tracer, enlistment);
                        }

                        serverGSDConfig = this.QueryGSDConfig(tracer, enlistment, retryConfig);
                    }

                    this.ValidateClientVersions(tracer, enlistment, serverGSDConfig, showWarnings: true);

                    CacheServerResolver cacheServerResolver = new CacheServerResolver(tracer, enlistment);
                    cacheServer = cacheServerResolver.ResolveNameFromRemote(cacheServer.Url, serverGSDConfig);
                    this.Output.WriteLine("Configured cache server: " + cacheServer);
                }

                this.InitializeLocalCacheAndObjectsPaths(tracer, enlistment, retryConfig, serverGSDConfig, cacheServer);

                if (!this.ShowStatusWhileRunning(
                        () => { return(this.PerformPreMountValidation(tracer, enlistment, out mountExecutableLocation, out errorMessage)); },
                        "Validating repo"))
                {
                    this.ReportErrorAndExit(tracer, errorMessage);
                }

                if (!this.SkipVersionCheck)
                {
                    string error;
                    if (!RepoMetadata.TryInitialize(tracer, enlistment.DotGSDRoot, out error))
                    {
                        this.ReportErrorAndExit(tracer, error);
                    }

                    try
                    {
                        GitProcess git = new GitProcess(enlistment);
                        this.LogEnlistmentInfoAndSetConfigValues(tracer, git, enlistment);
                    }
                    finally
                    {
                        RepoMetadata.Shutdown();
                    }
                }

                if (!this.ShowStatusWhileRunning(
                        () => { return(this.TryMount(tracer, enlistment, mountExecutableLocation, out errorMessage)); },
                        "Mounting"))
                {
                    this.ReportErrorAndExit(tracer, errorMessage);
                }

                if (!this.Unattended)
                {
                    tracer.RelatedInfo($"{nameof(this.Execute)}: Registering for automount");

                    if (this.ShowStatusWhileRunning(
                            () => { return(this.RegisterMount(enlistment, out errorMessage)); },
                            "Registering for automount"))
                    {
                        tracer.RelatedInfo($"{nameof(this.Execute)}: Registered for automount");
                    }
                    else
                    {
                        this.Output.WriteLine("    WARNING: " + errorMessage);
                        tracer.RelatedInfo($"{nameof(this.Execute)}: Failed to register for automount");
                    }
                }
            }
        }
예제 #11
0
        public override void Execute()
        {
            int exitCode = 0;

            this.ValidatePathParameter(this.EnlistmentRootPathParameter);
            this.ValidatePathParameter(this.LocalCacheRoot);

            string fullEnlistmentRootPathParameter;
            string normalizedEnlistmentRootPath = this.GetCloneRoot(out fullEnlistmentRootPathParameter);

            if (!string.IsNullOrWhiteSpace(this.LocalCacheRoot))
            {
                string fullLocalCacheRootPath = Path.GetFullPath(this.LocalCacheRoot);

                string errorMessage;
                string normalizedLocalCacheRootPath;
                if (!GSDPlatform.Instance.FileSystem.TryGetNormalizedPath(fullLocalCacheRootPath, out normalizedLocalCacheRootPath, out errorMessage))
                {
                    this.ReportErrorAndExit($"Failed to determine normalized path for '--local-cache-path' path {fullLocalCacheRootPath}: {errorMessage}");
                }

                if (normalizedLocalCacheRootPath.StartsWith(
                        Path.Combine(normalizedEnlistmentRootPath, GSDConstants.WorkingDirectoryRootName),
                        StringComparison.OrdinalIgnoreCase))
                {
                    this.ReportErrorAndExit("'--local-cache-path' cannot be inside the src folder");
                }
            }

            this.CheckNotInsideExistingRepo(normalizedEnlistmentRootPath);
            this.BlockEmptyCacheServerUrl(this.CacheServerUrl);

            try
            {
                GSDEnlistment enlistment;
                Result        cloneResult = new Result(false);

                CacheServerInfo cacheServer     = null;
                ServerGSDConfig serverGSDConfig = null;

                using (JsonTracer tracer = new JsonTracer(GSDConstants.GSDEtwProviderName, "GSDClone"))
                {
                    cloneResult = this.TryCreateEnlistment(fullEnlistmentRootPathParameter, normalizedEnlistmentRootPath, out enlistment);
                    if (cloneResult.Success)
                    {
                        tracer.AddLogFileEventListener(
                            GSDEnlistment.GetNewGSDLogFileName(enlistment.GSDLogsRoot, GSDConstants.LogFileTypes.Clone),
                            EventLevel.Informational,
                            Keywords.Any);
                        tracer.WriteStartEvent(
                            enlistment.EnlistmentRoot,
                            enlistment.RepoUrl,
                            this.CacheServerUrl,
                            new EventMetadata
                        {
                            { "Branch", this.Branch },
                            { "LocalCacheRoot", this.LocalCacheRoot },
                            { "SingleBranch", this.SingleBranch },
                            { "NoMount", this.NoMount },
                            { "NoPrefetch", this.NoPrefetch },
                            { "Unattended", this.Unattended },
                            { "IsElevated", GSDPlatform.Instance.IsElevated() },
                            { "NamedPipeName", enlistment.NamedPipeName },
                            { "ProcessID", Process.GetCurrentProcess().Id },
                            { nameof(this.EnlistmentRootPathParameter), this.EnlistmentRootPathParameter },
                            { nameof(fullEnlistmentRootPathParameter), fullEnlistmentRootPathParameter },
                        });

                        CacheServerResolver cacheServerResolver = new CacheServerResolver(tracer, enlistment);
                        cacheServer = cacheServerResolver.ParseUrlOrFriendlyName(this.CacheServerUrl);

                        string resolvedLocalCacheRoot;
                        if (string.IsNullOrWhiteSpace(this.LocalCacheRoot))
                        {
                            string localCacheRootError;
                            if (!LocalCacheResolver.TryGetDefaultLocalCacheRoot(enlistment, out resolvedLocalCacheRoot, out localCacheRootError))
                            {
                                this.ReportErrorAndExit(
                                    tracer,
                                    $"Failed to determine the default location for the local GSD cache: `{localCacheRootError}`");
                            }
                        }
                        else
                        {
                            resolvedLocalCacheRoot = Path.GetFullPath(this.LocalCacheRoot);
                        }

                        this.Output.WriteLine("Clone parameters:");
                        this.Output.WriteLine("  Repo URL:     " + enlistment.RepoUrl);
                        this.Output.WriteLine("  Branch:       " + (string.IsNullOrWhiteSpace(this.Branch) ? "Default" : this.Branch));
                        this.Output.WriteLine("  Cache Server: " + cacheServer);
                        this.Output.WriteLine("  Local Cache:  " + resolvedLocalCacheRoot);
                        this.Output.WriteLine("  Destination:  " + enlistment.EnlistmentRoot);

                        string authErrorMessage;
                        if (!this.TryAuthenticate(tracer, enlistment, out authErrorMessage))
                        {
                            this.ReportErrorAndExit(tracer, "Cannot clone because authentication failed: " + authErrorMessage);
                        }

                        RetryConfig retryConfig = this.GetRetryConfig(tracer, enlistment, TimeSpan.FromMinutes(RetryConfig.FetchAndCloneTimeoutMinutes));
                        serverGSDConfig = this.QueryGSDConfig(tracer, enlistment, retryConfig);

                        cacheServer = this.ResolveCacheServer(tracer, cacheServer, cacheServerResolver, serverGSDConfig);

                        this.ValidateClientVersions(tracer, enlistment, serverGSDConfig, showWarnings: true);

                        this.ShowStatusWhileRunning(
                            () =>
                        {
                            cloneResult = this.TryClone(tracer, enlistment, cacheServer, retryConfig, serverGSDConfig, resolvedLocalCacheRoot);
                            return(cloneResult.Success);
                        },
                            "Cloning",
                            normalizedEnlistmentRootPath);
                    }

                    if (!cloneResult.Success)
                    {
                        tracer.RelatedError(cloneResult.ErrorMessage);
                    }
                }

                if (cloneResult.Success)
                {
                    if (!this.NoPrefetch)
                    {
                        ReturnCode result = this.Execute <PrefetchVerb>(
                            enlistment,
                            verb =>
                        {
                            verb.Commits             = true;
                            verb.SkipVersionCheck    = true;
                            verb.ResolvedCacheServer = cacheServer;
                            verb.ServerGSDConfig     = serverGSDConfig;
                        });

                        if (result != ReturnCode.Success)
                        {
                            this.Output.WriteLine("\r\nError during prefetch @ {0}", fullEnlistmentRootPathParameter);
                            exitCode = (int)result;
                        }
                    }

                    if (this.NoMount)
                    {
                        this.Output.WriteLine("\r\nIn order to mount, first cd to within your enlistment, then call: ");
                        this.Output.WriteLine("gvfs mount");
                    }
                    else
                    {
                        this.Execute <MountVerb>(
                            enlistment,
                            verb =>
                        {
                            verb.SkipMountedCheck    = true;
                            verb.SkipVersionCheck    = true;
                            verb.ResolvedCacheServer = cacheServer;
                            verb.DownloadedGSDConfig = serverGSDConfig;
                        });

                        GitProcess git = new GitProcess(enlistment);
                        git.ForceCheckoutAllFiles();
                    }
                }
                else
                {
                    this.Output.WriteLine("\r\nCannot clone @ {0}", fullEnlistmentRootPathParameter);
                    this.Output.WriteLine("Error: {0}", cloneResult.ErrorMessage);
                    exitCode = (int)ReturnCode.GenericError;
                }
            }
            catch (AggregateException e)
            {
                this.Output.WriteLine("Cannot clone @ {0}:", fullEnlistmentRootPathParameter);
                foreach (Exception ex in e.Flatten().InnerExceptions)
                {
                    this.Output.WriteLine("Exception: {0}", ex.ToString());
                }

                exitCode = (int)ReturnCode.GenericError;
            }
            catch (VerbAbortedException)
            {
                throw;
            }
            catch (Exception e)
            {
                this.ReportErrorAndExit("Cannot clone @ {0}: {1}", fullEnlistmentRootPathParameter, e.ToString());
            }

            Environment.Exit(exitCode);
        }
예제 #12
0
        private Result TryClone(
            JsonTracer tracer,
            GSDEnlistment enlistment,
            CacheServerInfo cacheServer,
            RetryConfig retryConfig,
            ServerGSDConfig serverGSDConfig,
            string resolvedLocalCacheRoot)
        {
            Result pipeResult;

            using (NamedPipeServer pipeServer = this.StartNamedPipe(tracer, enlistment, out pipeResult))
            {
                if (!pipeResult.Success)
                {
                    return(pipeResult);
                }

                using (GitObjectsHttpRequestor objectRequestor = new GitObjectsHttpRequestor(tracer, enlistment, cacheServer, retryConfig))
                {
                    GitRefs refs = objectRequestor.QueryInfoRefs(this.SingleBranch ? this.Branch : null);

                    if (refs == null)
                    {
                        return(new Result("Could not query info/refs from: " + Uri.EscapeUriString(enlistment.RepoUrl)));
                    }

                    if (this.Branch == null)
                    {
                        this.Branch = refs.GetDefaultBranch();

                        EventMetadata metadata = new EventMetadata();
                        metadata.Add("Branch", this.Branch);
                        tracer.RelatedEvent(EventLevel.Informational, "CloneDefaultRemoteBranch", metadata);
                    }
                    else
                    {
                        if (!refs.HasBranch(this.Branch))
                        {
                            EventMetadata metadata = new EventMetadata();
                            metadata.Add("Branch", this.Branch);
                            tracer.RelatedEvent(EventLevel.Warning, "CloneBranchDoesNotExist", metadata);

                            string errorMessage = string.Format("Remote branch {0} not found in upstream origin", this.Branch);
                            return(new Result(errorMessage));
                        }
                    }

                    if (!enlistment.TryCreateEnlistmentFolders())
                    {
                        string error = "Could not create enlistment directory";
                        tracer.RelatedError(error);
                        return(new Result(error));
                    }

                    string localCacheError;
                    if (!this.TryDetermineLocalCacheAndInitializePaths(tracer, enlistment, serverGSDConfig, cacheServer, resolvedLocalCacheRoot, out localCacheError))
                    {
                        tracer.RelatedError(localCacheError);
                        return(new Result(localCacheError));
                    }

                    Directory.CreateDirectory(enlistment.GitObjectsRoot);
                    Directory.CreateDirectory(enlistment.GitPackRoot);
                    Directory.CreateDirectory(enlistment.BlobSizesRoot);

                    return(this.CreateClone(tracer, enlistment, objectRequestor, refs, this.Branch));
                }
            }
        }
예제 #13
0
            private void EnsureLocalCacheIsHealthy(
                ITracer tracer,
                GSDEnlistment enlistment,
                RetryConfig retryConfig,
                ServerGSDConfig serverGSDConfig,
                CacheServerInfo cacheServer)
            {
                if (!Directory.Exists(enlistment.LocalCacheRoot))
                {
                    try
                    {
                        tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: Local cache root: {enlistment.LocalCacheRoot} missing, recreating it");
                        Directory.CreateDirectory(enlistment.LocalCacheRoot);
                    }
                    catch (Exception e)
                    {
                        EventMetadata metadata = new EventMetadata();
                        metadata.Add("Exception", e.ToString());
                        metadata.Add("enlistment.LocalCacheRoot", enlistment.LocalCacheRoot);
                        tracer.RelatedError(metadata, $"{nameof(this.EnsureLocalCacheIsHealthy)}: Exception while trying to create local cache root");

                        this.ReportErrorAndExit(tracer, "Failed to create local cache: " + enlistment.LocalCacheRoot);
                    }
                }

                // Validate that the GitObjectsRoot directory is on disk, and that the GSD repo is configured to use it.
                // If the directory is missing (and cannot be found in the mapping file) a new key for the repo will be added
                // to the mapping file and used for BOTH the GitObjectsRoot and BlobSizesRoot
                PhysicalFileSystem fileSystem = new PhysicalFileSystem();

                if (Directory.Exists(enlistment.GitObjectsRoot))
                {
                    bool gitObjectsRootInAlternates = false;

                    string alternatesFilePath = this.GetAlternatesPath(enlistment);
                    if (File.Exists(alternatesFilePath))
                    {
                        try
                        {
                            using (Stream stream = fileSystem.OpenFileStream(
                                       alternatesFilePath,
                                       FileMode.Open,
                                       FileAccess.Read,
                                       FileShare.ReadWrite,
                                       callFlushFileBuffers: false))
                            {
                                using (StreamReader reader = new StreamReader(stream))
                                {
                                    while (!reader.EndOfStream)
                                    {
                                        string alternatesLine = reader.ReadLine();
                                        if (string.Equals(alternatesLine, enlistment.GitObjectsRoot, StringComparison.OrdinalIgnoreCase))
                                        {
                                            gitObjectsRootInAlternates = true;
                                        }
                                    }
                                }
                            }
                        }
                        catch (Exception e)
                        {
                            EventMetadata exceptionMetadata = new EventMetadata();
                            exceptionMetadata.Add("Exception", e.ToString());
                            tracer.RelatedError(exceptionMetadata, $"{nameof(this.EnsureLocalCacheIsHealthy)}: Exception while trying to validate alternates file");

                            this.ReportErrorAndExit(tracer, $"Failed to validate that alternates file includes git objects root: {e.Message}");
                        }
                    }
                    else
                    {
                        tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: Alternates file not found");
                    }

                    if (!gitObjectsRootInAlternates)
                    {
                        tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: GitObjectsRoot ({enlistment.GitObjectsRoot}) missing from alternates files, recreating alternates");
                        string error;
                        if (!this.TryCreateAlternatesFile(fileSystem, enlistment, out error))
                        {
                            this.ReportErrorAndExit(tracer, $"Failed to update alternates file to include git objects root: {error}");
                        }
                    }
                }
                else
                {
                    tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: GitObjectsRoot ({enlistment.GitObjectsRoot}) missing, determining new root");

                    if (cacheServer == null)
                    {
                        cacheServer = CacheServerResolver.GetCacheServerFromConfig(enlistment);
                    }

                    string error;
                    if (serverGSDConfig == null)
                    {
                        if (retryConfig == null)
                        {
                            if (!RetryConfig.TryLoadFromGitConfig(tracer, enlistment, out retryConfig, out error))
                            {
                                this.ReportErrorAndExit(tracer, "Failed to determine GSD timeout and max retries: " + error);
                            }
                        }

                        serverGSDConfig = this.QueryGSDConfig(tracer, enlistment, retryConfig);
                    }

                    string             localCacheKey;
                    LocalCacheResolver localCacheResolver = new LocalCacheResolver(enlistment);
                    if (!localCacheResolver.TryGetLocalCacheKeyFromLocalConfigOrRemoteCacheServers(
                            tracer,
                            serverGSDConfig,
                            cacheServer,
                            enlistment.LocalCacheRoot,
                            localCacheKey: out localCacheKey,
                            errorMessage: out error))
                    {
                        this.ReportErrorAndExit(tracer, $"Previous git objects root ({enlistment.GitObjectsRoot}) not found, and failed to determine new local cache key: {error}");
                    }

                    EventMetadata metadata = new EventMetadata();
                    metadata.Add("localCacheRoot", enlistment.LocalCacheRoot);
                    metadata.Add("localCacheKey", localCacheKey);
                    metadata.Add(TracingConstants.MessageKey.InfoMessage, "Initializing and persisting updated paths");
                    tracer.RelatedEvent(EventLevel.Informational, "GSDVerb_EnsureLocalCacheIsHealthy_InitializePathsFromKey", metadata);
                    enlistment.InitializeCachePathsFromKey(enlistment.LocalCacheRoot, localCacheKey);

                    tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: Creating GitObjectsRoot ({enlistment.GitObjectsRoot}), GitPackRoot ({enlistment.GitPackRoot}), and BlobSizesRoot ({enlistment.BlobSizesRoot})");
                    try
                    {
                        Directory.CreateDirectory(enlistment.GitObjectsRoot);
                        Directory.CreateDirectory(enlistment.GitPackRoot);
                    }
                    catch (Exception e)
                    {
                        EventMetadata exceptionMetadata = new EventMetadata();
                        exceptionMetadata.Add("Exception", e.ToString());
                        exceptionMetadata.Add("enlistment.LocalCacheRoot", enlistment.LocalCacheRoot);
                        exceptionMetadata.Add("enlistment.GitObjectsRoot", enlistment.GitObjectsRoot);
                        exceptionMetadata.Add("enlistment.GitPackRoot", enlistment.GitPackRoot);
                        exceptionMetadata.Add("enlistment.BlobSizesRoot", enlistment.BlobSizesRoot);
                        tracer.RelatedError(exceptionMetadata, $"{nameof(this.InitializeLocalCacheAndObjectsPaths)}: Exception while trying to create objects, pack, and sizes folders");

                        this.ReportErrorAndExit(tracer, "Failed to create objects, pack, and sizes folders");
                    }

                    tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: Creating new alternates file");
                    if (!this.TryCreateAlternatesFile(fileSystem, enlistment, out error))
                    {
                        this.ReportErrorAndExit(tracer, $"Failed to update alterates file with new objects path: {error}");
                    }

                    tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: Saving git objects root ({enlistment.GitObjectsRoot}) in repo metadata");
                    RepoMetadata.Instance.SetGitObjectsRoot(enlistment.GitObjectsRoot);

                    tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: Saving blob sizes root ({enlistment.BlobSizesRoot}) in repo metadata");
                    RepoMetadata.Instance.SetBlobSizesRoot(enlistment.BlobSizesRoot);
                }

                // Validate that the BlobSizesRoot folder is on disk.
                // Note that if a user performed an action that resulted in the entire .gvfscache being deleted, the code above
                // for validating GitObjectsRoot will have already taken care of generating a new key and setting a new enlistment.BlobSizesRoot path
                if (!Directory.Exists(enlistment.BlobSizesRoot))
                {
                    tracer.RelatedInfo($"{nameof(this.EnsureLocalCacheIsHealthy)}: BlobSizesRoot ({enlistment.BlobSizesRoot}) not found, re-creating");
                    try
                    {
                        Directory.CreateDirectory(enlistment.BlobSizesRoot);
                    }
                    catch (Exception e)
                    {
                        EventMetadata exceptionMetadata = new EventMetadata();
                        exceptionMetadata.Add("Exception", e.ToString());
                        exceptionMetadata.Add("enlistment.BlobSizesRoot", enlistment.BlobSizesRoot);
                        tracer.RelatedError(exceptionMetadata, $"{nameof(this.InitializeLocalCacheAndObjectsPaths)}: Exception while trying to create blob sizes folder");

                        this.ReportErrorAndExit(tracer, "Failed to create blob sizes folder");
                    }
                }
            }
예제 #14
0
        protected void ValidateClientVersions(ITracer tracer, GSDEnlistment enlistment, ServerGSDConfig gvfsConfig, bool showWarnings)
        {
            this.CheckGitVersion(tracer, enlistment, out string gitVersion);
            enlistment.SetGitVersion(gitVersion);

            this.GetGSDHooksPathAndCheckVersion(tracer, out string hooksVersion);
            enlistment.SetGSDHooksVersion(hooksVersion);

            string errorMessage = null;
            bool   errorIsFatal = false;

            if (!this.TryValidateGSDVersion(enlistment, tracer, gvfsConfig, out errorMessage, out errorIsFatal))
            {
                if (errorIsFatal)
                {
                    this.ReportErrorAndExit(tracer, errorMessage);
                }
                else if (showWarnings)
                {
                    this.Output.WriteLine();
                    this.Output.WriteLine(errorMessage);
                    this.Output.WriteLine();
                }
            }
        }
예제 #15
0
        public bool TryQueryGSDConfig(bool logErrors, out ServerGSDConfig serverGSDConfig, out HttpStatusCode?httpStatus, out string errorMessage)
        {
            serverGSDConfig = null;
            httpStatus      = null;
            errorMessage    = null;

            Uri    gvfsConfigEndpoint;
            string gvfsConfigEndpointString = this.repoUrl + GSDConstants.Endpoints.GSDConfig;

            try
            {
                gvfsConfigEndpoint = new Uri(gvfsConfigEndpointString);
            }
            catch (UriFormatException e)
            {
                EventMetadata metadata = new EventMetadata();
                metadata.Add("Method", nameof(this.TryQueryGSDConfig));
                metadata.Add("Exception", e.ToString());
                metadata.Add("Url", gvfsConfigEndpointString);
                this.Tracer.RelatedError(metadata, "UriFormatException when constructing Uri", Keywords.Network);

                return(false);
            }

            long requestId = HttpRequestor.GetNewRequestId();
            RetryWrapper <ServerGSDConfig> retrier = new RetryWrapper <ServerGSDConfig>(this.RetryConfig.MaxAttempts, CancellationToken.None);

            if (logErrors)
            {
                retrier.OnFailure += RetryWrapper <ServerGSDConfig> .StandardErrorHandler(this.Tracer, requestId, "QueryGvfsConfig");
            }

            RetryWrapper <ServerGSDConfig> .InvocationResult output = retrier.Invoke(
                tryCount =>
            {
                using (GitEndPointResponseData response = this.SendRequest(
                           requestId,
                           gvfsConfigEndpoint,
                           HttpMethod.Get,
                           requestContent: null,
                           cancellationToken: CancellationToken.None))
                {
                    if (response.HasErrors)
                    {
                        return(new RetryWrapper <ServerGSDConfig> .CallbackResult(response.Error, response.ShouldRetry));
                    }

                    try
                    {
                        string configString    = response.RetryableReadToEnd();
                        ServerGSDConfig config = JsonConvert.DeserializeObject <ServerGSDConfig>(configString);
                        return(new RetryWrapper <ServerGSDConfig> .CallbackResult(config));
                    }
                    catch (JsonReaderException e)
                    {
                        return(new RetryWrapper <ServerGSDConfig> .CallbackResult(e, shouldRetry: false));
                    }
                }
            });

            if (output.Succeeded)
            {
                serverGSDConfig = output.Result;
                httpStatus      = HttpStatusCode.OK;
                return(true);
            }

            GitObjectsHttpException httpException = output.Error as GitObjectsHttpException;

            if (httpException != null)
            {
                httpStatus   = httpException.StatusCode;
                errorMessage = httpException.Message;
            }

            if (logErrors)
            {
                this.Tracer.RelatedError(
                    new EventMetadata
                {
                    { "Exception", output.Error.ToString() }
                },
                    $"{nameof(this.TryQueryGSDConfig)} failed");
            }

            return(false);
        }