Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion GVFS/FastFetch/FastFetchVerb.cs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,9 @@ private int ExecuteWithExitCode()
string fastfetchLogFile = Enlistment.GetNewLogFileName(enlistment.FastFetchLogRoot, "fastfetch");
tracer.AddLogFileEventListener(fastfetchLogFile, EventLevel.Informational, Keywords.Any);

CacheServerInfo cacheServer = new CacheServerInfo(this.GetRemoteUrl(enlistment), null);
CacheServerInfo cacheServer = string.IsNullOrWhiteSpace(this.CacheServerUrl)
? CacheServerResolver.GetCacheServerFromConfig(enlistment)
: new CacheServerInfo(this.GetRemoteUrl(enlistment), null);

tracer.WriteStartEvent(
enlistment.PrimaryEnlistmentRoot,
Expand Down
38 changes: 37 additions & 1 deletion GVFS/GVFS.CommandLine.Tests/GvfsMainCliTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,12 +215,36 @@ public void Clone_FullCommandLine_ParsesCorrectly()
{
"clone", "https://example.com/repo", @"C:\Users\test\repo",
"--cache-server-url", "https://cache.test",
"--prefetch-cache-server-url", "https://prefetch-cache.test",
"--get-cache-server-url", "https://get-cache.test",
"--post-cache-server-url", "https://post-cache.test",
"--sizes-cache-server-url", "https://sizes-cache.test",
"-b", "develop",
"--single-branch",
"--no-mount",
"--no-prefetch"
});
Assert.That(parseResult.Errors, Is.Empty, "Full clone command should parse without errors");
Assert.Multiple(() =>
{
Assert.That(parseResult.GetValue((Option<string>)FindOptionOnCommand("clone", "--cache-server-url")), Is.EqualTo("https://cache.test"));
Assert.That(parseResult.GetValue((Option<string>)FindOptionOnCommand("clone", "--prefetch-cache-server-url")), Is.EqualTo("https://prefetch-cache.test"));
Assert.That(parseResult.GetValue((Option<string>)FindOptionOnCommand("clone", "--get-cache-server-url")), Is.EqualTo("https://get-cache.test"));
Assert.That(parseResult.GetValue((Option<string>)FindOptionOnCommand("clone", "--post-cache-server-url")), Is.EqualTo("https://post-cache.test"));
Assert.That(parseResult.GetValue((Option<string>)FindOptionOnCommand("clone", "--sizes-cache-server-url")), Is.EqualTo("https://sizes-cache.test"));
});
}

[TestCase("--prefetch-cache-server-url")]
[TestCase("--get-cache-server-url")]
[TestCase("--post-cache-server-url")]
[TestCase("--sizes-cache-server-url")]
public void Clone_EndpointCacheServerUrl_RejectsInvalidUrl(string optionName)
{
var parseResult = rootCommand.Parse(new[] { "clone", "https://example.com/repo", optionName, "not-a-url" });

Assert.That(parseResult.Errors, Has.Count.EqualTo(1));
Assert.That(parseResult.Errors[0].Message, Does.Contain("requires an absolute URL"));
}

[Test]
Expand Down Expand Up @@ -342,7 +366,19 @@ public void Repair_FullCommandLine_ParsesCorrectly()
[Test]
public void Clone_HasAllExpectedOptions()
{
var expected = new[] { "--cache-server-url", "--branch", "--single-branch", "--no-mount", "--no-prefetch", "--local-cache-path" };
var expected = new[]
{
"--cache-server-url",
"--prefetch-cache-server-url",
"--get-cache-server-url",
"--post-cache-server-url",
"--sizes-cache-server-url",
"--branch",
"--single-branch",
"--no-mount",
"--no-prefetch",
"--local-cache-path",
};
foreach (var optName in expected)
{
Assert.That(FindOptionOnCommand("clone", optName), Is.Not.Null,
Expand Down
4 changes: 4 additions & 0 deletions GVFS/GVFS.Common/GVFSConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ public static class GitConfig
public const string MountId = GVFSPrefix + "mount-id";
public const string EnlistmentId = GVFSPrefix + "enlistment-id";
public const string CacheServer = GVFSPrefix + "cache-server";
public const string PrefetchCacheServer = GVFSPrefix + "prefetch.cache-server";
public const string GetCacheServer = GVFSPrefix + "get.cache-server";
public const string PostCacheServer = GVFSPrefix + "post.cache-server";
public const string SizesCacheServer = GVFSPrefix + "sizes.cache-server";
public const string DeprecatedCacheEndpointSuffix = ".cache-server-url";
public const string HooksPrefix = GitConfig.GVFSPrefix + "clone.default-";
public const string GVFSTelemetryId = GitConfig.GVFSPrefix + "telemetry-id";
Expand Down
12 changes: 10 additions & 2 deletions GVFS/GVFS.Common/Git/GitObjects.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@ public virtual bool TryDownloadPrefetchPacks(GitProcess gitProcess, long latestT
"{0}?lastPackTimestamp={1}",
this.GitObjectRequestor.CacheServer.PrefetchEndpointUrl,
latestTimestamp)),
fallbackEndPointGenerator: () => new Uri(
string.Format(
"{0}?lastPackTimestamp={1}",
this.GitObjectRequestor.CacheServer.GlobalPrefetchEndpointUrl,
latestTimestamp)),
requestBodyGenerator: () => null,
cancellationToken: CancellationToken.None,
acceptType: new MediaTypeWithQualityHeaderValue(GVFSConstants.MediaTypes.PrefetchPackFilesAndIndexesMediaType));
Expand All @@ -188,18 +193,21 @@ public virtual bool TryDownloadPrefetchPacks(GitProcess gitProcess, long latestT

if (!result.Succeeded)
{
Uri requestUri = result.Result?.RequestUri
?? new Uri(this.GitObjectRequestor.CacheServer.PrefetchEndpointUrl);
string requestAuthority = HttpRequestor.GetAuthorityForTelemetry(requestUri);
if (result.Result != null && result.Result.HttpStatusCodeResult == HttpStatusCode.NotFound)
{
EventMetadata warning = CreateEventMetadata();
warning.Add(TracingConstants.MessageKey.WarningMessage, "The server does not support " + GVFSConstants.Endpoints.GVFSPrefetch);
warning.Add(nameof(this.GitObjectRequestor.CacheServer.PrefetchEndpointUrl), this.GitObjectRequestor.CacheServer.PrefetchEndpointUrl);
warning.Add("PrefetchEndpointUrl", requestAuthority);
activity.RelatedEvent(EventLevel.Warning, "CommandNotSupported", warning);
}
else
{
EventMetadata error = CreateEventMetadata(result.Error);
error.Add("latestTimestamp", latestTimestamp);
error.Add(nameof(this.GitObjectRequestor.CacheServer.PrefetchEndpointUrl), this.GitObjectRequestor.CacheServer.PrefetchEndpointUrl);
error.Add("PrefetchEndpointUrl", requestAuthority);
activity.RelatedWarning(error, "DownloadPrefetchPacks failed.", Keywords.Telemetry);
}
}
Expand Down
86 changes: 83 additions & 3 deletions GVFS/GVFS.Common/Http/CacheServerInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,105 @@ public class CacheServerInfo

[JsonConstructor]
public CacheServerInfo(string url, string name, bool globalDefault = false)
: this(url, name, globalDefault, null, null, null, null)
{
}

public CacheServerInfo(
string url,
string name,
bool globalDefault,
string prefetchCacheServerUrl,
string getCacheServerUrl,
string postCacheServerUrl,
string sizesCacheServerUrl)
{
this.Url = url;
this.Name = name;
this.GlobalDefault = globalDefault;
this.PrefetchCacheServerUrl = prefetchCacheServerUrl;
this.GetCacheServerUrl = getCacheServerUrl;
this.PostCacheServerUrl = postCacheServerUrl;
this.SizesCacheServerUrl = sizesCacheServerUrl;

if (this.Url != null)
{
this.ObjectsEndpointUrl = this.Url + ObjectsEndpointSuffix;
this.PrefetchEndpointUrl = this.Url + PrefetchEndpointSuffix;
this.SizesEndpointUrl = this.Url + SizesEndpointSuffix;
}

this.GlobalPrefetchEndpointUrl = GetEndpointUrl(this.Url, PrefetchEndpointSuffix);
this.GlobalSizesEndpointUrl = GetEndpointUrl(this.Url, SizesEndpointSuffix);
this.PrefetchEndpointUrl = GetEndpointUrl(prefetchCacheServerUrl ?? this.Url, PrefetchEndpointSuffix);
this.ObjectsGetEndpointUrl = GetEndpointUrl(getCacheServerUrl ?? this.Url, ObjectsEndpointSuffix);
this.ObjectsPostEndpointUrl = GetEndpointUrl(postCacheServerUrl ?? this.Url, ObjectsEndpointSuffix);
this.SizesEndpointUrl = GetEndpointUrl(sizesCacheServerUrl ?? this.Url, SizesEndpointSuffix);
}

public string Url { get; }
public string Name { get; }
public bool GlobalDefault { get; }

[JsonIgnore]
public string PrefetchCacheServerUrl { get; }

[JsonIgnore]
public string GetCacheServerUrl { get; }

[JsonIgnore]
public string PostCacheServerUrl { get; }

[JsonIgnore]
public string SizesCacheServerUrl { get; }

public string ObjectsEndpointUrl { get; }
public string PrefetchEndpointUrl { get; }
public string SizesEndpointUrl { get; }

[JsonIgnore]
public string ObjectsGetEndpointUrl { get; }

[JsonIgnore]
public string ObjectsPostEndpointUrl { get; }

[JsonIgnore]
public string GlobalPrefetchEndpointUrl { get; }

[JsonIgnore]
public string GlobalSizesEndpointUrl { get; }

public CacheServerInfo WithEndpointOverrides(
string prefetchCacheServerUrl,
string getCacheServerUrl,
string postCacheServerUrl,
string sizesCacheServerUrl)
{
return new CacheServerInfo(
this.Url,
this.Name,
this.GlobalDefault,
prefetchCacheServerUrl,
getCacheServerUrl,
postCacheServerUrl,
sizesCacheServerUrl);
}

public CacheServerInfo WithEndpointOverridesFrom(CacheServerInfo cacheServer)
{
return this.WithEndpointOverrides(
cacheServer.PrefetchCacheServerUrl,
cacheServer.GetCacheServerUrl,
cacheServer.PostCacheServerUrl,
cacheServer.SizesCacheServerUrl);
}

public bool HasValidUrl()
{
return Uri.IsWellFormedUriString(this.Url, UriKind.Absolute);
return IsValidUrl(this.Url);
}

public static bool IsValidUrl(string url)
{
return Uri.IsWellFormedUriString(url, UriKind.Absolute);
}

public bool IsNone(string repoUrl)
Expand Down Expand Up @@ -64,5 +139,10 @@ public static class ReservedNames
public const string Default = "Default";
public const string UserDefined = "User Defined";
}

private static string GetEndpointUrl(string cacheServerUrl, string endpointSuffix)
{
return cacheServerUrl == null ? null : cacheServerUrl + endpointSuffix;
}
}
}
52 changes: 51 additions & 1 deletion GVFS/GVFS.Common/Http/CacheServerResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,20 @@ public CacheServerResolver(

public static CacheServerInfo GetCacheServerFromConfig(Enlistment enlistment)
{
GitProcess git = enlistment.CreateGitProcess();
string url = GetUrlFromConfig(enlistment);
string prefetchCacheServerUrl = GetEndpointUrlFromConfig(git, GVFSConstants.GitConfig.PrefetchCacheServer);
string getCacheServerUrl = GetEndpointUrlFromConfig(git, GVFSConstants.GitConfig.GetCacheServer);
string postCacheServerUrl = GetEndpointUrlFromConfig(git, GVFSConstants.GitConfig.PostCacheServer);
string sizesCacheServerUrl = GetEndpointUrlFromConfig(git, GVFSConstants.GitConfig.SizesCacheServer);
return new CacheServerInfo(
url,
url == enlistment.RepoUrl ? CacheServerInfo.ReservedNames.None : null);
url == enlistment.RepoUrl ? CacheServerInfo.ReservedNames.None : null,
globalDefault: false,
prefetchCacheServerUrl,
getCacheServerUrl,
postCacheServerUrl,
sizesCacheServerUrl);
}

public static string GetUrlFromConfig(Enlistment enlistment)
Expand Down Expand Up @@ -129,6 +139,22 @@ public bool TrySaveUrlToLocalConfig(CacheServerInfo cache, out string error)
return result.ExitCodeIsSuccess;
}

public bool TrySaveEndpointUrlsToLocalConfig(CacheServerInfo cache, out string error)
{
GitProcess git = this.enlistment.CreateGitProcess();

if (!TrySaveEndpointUrl(git, GVFSConstants.GitConfig.PrefetchCacheServer, cache.PrefetchCacheServerUrl, out error) ||
!TrySaveEndpointUrl(git, GVFSConstants.GitConfig.GetCacheServer, cache.GetCacheServerUrl, out error) ||
!TrySaveEndpointUrl(git, GVFSConstants.GitConfig.PostCacheServer, cache.PostCacheServerUrl, out error) ||
!TrySaveEndpointUrl(git, GVFSConstants.GitConfig.SizesCacheServer, cache.SizesCacheServerUrl, out error))
{
return false;
}

error = null;
return true;
}

private static string GetValueFromConfig(GitProcess git, string configName, bool localOnly)
{
GitProcess.ConfigResult result =
Expand All @@ -144,6 +170,30 @@ private static string GetValueFromConfig(GitProcess git, string configName, bool
return value;
}

private static string GetEndpointUrlFromConfig(GitProcess git, string configName)
{
string url = GetValueFromConfig(git, configName, localOnly: true);
if (url != null && !CacheServerInfo.IsValidUrl(url))
{
throw new InvalidRepoException($"Invalid value for {configName}: '{url}' is not an absolute URL.");
}

return url;
}

private static bool TrySaveEndpointUrl(GitProcess git, string configName, string url, out string error)
{
error = null;
if (url == null)
{
return true;
}

GitProcess.Result result = git.SetInLocalConfig(configName, url, replaceAll: true);
error = result.Errors;
return result.ExitCodeIsSuccess;
}

private static string GetDeprecatedCacheConfigSettingName(Enlistment enlistment)
{
string sectionUrl =
Expand Down
Loading
Loading