Merge upstream changes
This commit is contained in:
commit
9b331673fe
67 changed files with 877 additions and 316 deletions
|
@ -56,7 +56,6 @@ namespace Microsoft.DotNet.Cli.Build
|
|||
{ "rhel_7_x64", false },
|
||||
{ "ubuntu_14_04_x64", false },
|
||||
{ "ubuntu_16_04_x64", false },
|
||||
{ "ubuntu_16_10_x64", false },
|
||||
{ "linux_x64", false }
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using NuGet.Protocol;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Build.UploadToLinuxPackageRepository
|
||||
{
|
||||
internal class AddPackageStrategy : IAzurelinuxRepositoryServiceHttpStrategy
|
||||
{
|
||||
private readonly IdInRepositoryService _idInRepositoryService;
|
||||
private readonly string _packageName;
|
||||
private readonly string _packageVersion;
|
||||
private readonly string _repositoryId;
|
||||
|
||||
public AddPackageStrategy(
|
||||
IdInRepositoryService idInRepositoryService,
|
||||
string packageName,
|
||||
string packageVersion,
|
||||
string repositoryId)
|
||||
{
|
||||
_idInRepositoryService = idInRepositoryService
|
||||
?? throw new ArgumentNullException(nameof(idInRepositoryService));
|
||||
_packageName = packageName;
|
||||
_packageVersion = packageVersion;
|
||||
_repositoryId = repositoryId;
|
||||
}
|
||||
|
||||
public async Task<string> Execute(HttpClient client, Uri baseAddress)
|
||||
{
|
||||
var debianUploadJsonContent = new Dictionary<string, string>
|
||||
{
|
||||
["name"] = _packageName,
|
||||
["version"] = AppendDebianRevisionNumber(_packageVersion),
|
||||
["fileId"] = _idInRepositoryService.Id,
|
||||
["repositoryId"] = _repositoryId
|
||||
}.ToJson();
|
||||
var content = new StringContent(debianUploadJsonContent,
|
||||
Encoding.UTF8,
|
||||
"application/json");
|
||||
|
||||
using (var response = await client.PostAsync(new Uri(baseAddress, "/v1/packages"), content))
|
||||
{
|
||||
if (!response.IsSuccessStatusCode)
|
||||
throw new FailedToAddPackageToPackageRepositoryException(
|
||||
$"request:{debianUploadJsonContent} response:{response.ToJson()}");
|
||||
return response.Headers.GetValues("Location").Single();
|
||||
}
|
||||
}
|
||||
|
||||
private static string AppendDebianRevisionNumber(string packageVersion)
|
||||
{
|
||||
return packageVersion + "-1";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Build.UploadToLinuxPackageRepository
|
||||
{
|
||||
public static class ExponentialRetry
|
||||
{
|
||||
public static IEnumerable<TimeSpan> Intervals
|
||||
{
|
||||
get
|
||||
{
|
||||
var seconds = 5;
|
||||
while (true)
|
||||
{
|
||||
yield return TimeSpan.FromSeconds(seconds);
|
||||
seconds *= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task ExecuteWithRetry(Func<Task<string>> action,
|
||||
Func<string, bool> isSuccess,
|
||||
int maxRetryCount,
|
||||
Func<IEnumerable<Task>> timer,
|
||||
string taskDescription = "")
|
||||
{
|
||||
var count = 0;
|
||||
foreach (var t in timer())
|
||||
{
|
||||
await t;
|
||||
var result = await action();
|
||||
if (isSuccess(result))
|
||||
return;
|
||||
count++;
|
||||
if (count == maxRetryCount)
|
||||
throw new RetryFailedException(
|
||||
$"Retry failed for {taskDescription} after {count} times with result: {result}");
|
||||
}
|
||||
throw new Exception("Timer should not be exhausted");
|
||||
}
|
||||
|
||||
public static IEnumerable<Task> Timer(IEnumerable<TimeSpan> interval)
|
||||
{
|
||||
return interval.Select(Task.Delay);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Build.UploadToLinuxPackageRepository
|
||||
{
|
||||
public class FailedToAddPackageToPackageRepositoryException : Exception
|
||||
{
|
||||
public FailedToAddPackageToPackageRepositoryException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public FailedToAddPackageToPackageRepositoryException()
|
||||
{
|
||||
}
|
||||
|
||||
public FailedToAddPackageToPackageRepositoryException(string message, Exception innerException) : base(message,
|
||||
innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using NuGet.Protocol;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Build.UploadToLinuxPackageRepository
|
||||
{
|
||||
internal class FileUploadStrategy : IAzurelinuxRepositoryServiceHttpStrategy
|
||||
{
|
||||
private readonly string _pathToPackageToUpload;
|
||||
|
||||
public FileUploadStrategy(string pathToPackageToUpload)
|
||||
{
|
||||
_pathToPackageToUpload = pathToPackageToUpload
|
||||
?? throw new ArgumentNullException(nameof(pathToPackageToUpload));
|
||||
}
|
||||
|
||||
public async Task<string> Execute(HttpClient client, Uri baseAddress)
|
||||
{
|
||||
var fileName = Path.GetFileName(_pathToPackageToUpload);
|
||||
|
||||
using (var content =
|
||||
new MultipartFormDataContent())
|
||||
{
|
||||
var url = new Uri(baseAddress, "/v1/files");
|
||||
content.Add(
|
||||
new StreamContent(
|
||||
new MemoryStream(
|
||||
File.ReadAllBytes(_pathToPackageToUpload))),
|
||||
"file",
|
||||
fileName);
|
||||
using (var message = await client.PostAsync(url, content))
|
||||
{
|
||||
if (!message.IsSuccessStatusCode)
|
||||
{
|
||||
throw new FailedToAddPackageToPackageRepositoryException(
|
||||
$"{message.ToJson()} failed to post file to {url} file name:{fileName} pathToPackageToUpload:{_pathToPackageToUpload}");
|
||||
}
|
||||
return await message.Content.ReadAsStringAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Build.UploadToLinuxPackageRepository
|
||||
{
|
||||
internal interface IAzurelinuxRepositoryServiceHttpStrategy
|
||||
{
|
||||
Task<string> Execute(HttpClient client, Uri baseAddress);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Build.UploadToLinuxPackageRepository
|
||||
{
|
||||
internal class IdInRepositoryService
|
||||
{
|
||||
public IdInRepositoryService(string id)
|
||||
{
|
||||
Id = id ?? throw new ArgumentNullException(nameof(id));
|
||||
}
|
||||
|
||||
public string Id { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Build.UploadToLinuxPackageRepository
|
||||
{
|
||||
internal class LinuxPackageRepositoryDestiny
|
||||
{
|
||||
private readonly string _password;
|
||||
private readonly string _server;
|
||||
private readonly string _username;
|
||||
|
||||
public LinuxPackageRepositoryDestiny(string username,
|
||||
string password,
|
||||
string server,
|
||||
string repositoryId)
|
||||
{
|
||||
_username = username ?? throw new ArgumentNullException(nameof(username));
|
||||
_password = password ?? throw new ArgumentNullException(nameof(password));
|
||||
_server = server ?? throw new ArgumentNullException(nameof(server));
|
||||
RepositoryId = repositoryId ?? throw new ArgumentNullException(nameof(repositoryId));
|
||||
}
|
||||
|
||||
public string RepositoryId { get; }
|
||||
|
||||
public Uri GetBaseAddress()
|
||||
{
|
||||
return new Uri($"https://{_server}");
|
||||
}
|
||||
|
||||
public string GetSimpleAuth()
|
||||
{
|
||||
return $"{_username}:{_password}";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Build.UploadToLinuxPackageRepository
|
||||
{
|
||||
internal class LinuxPackageRepositoryHttpPrepare
|
||||
{
|
||||
private readonly IAzurelinuxRepositoryServiceHttpStrategy _httpStrategy;
|
||||
private readonly LinuxPackageRepositoryDestiny _linuxPackageRepositoryDestiny;
|
||||
|
||||
public LinuxPackageRepositoryHttpPrepare(
|
||||
LinuxPackageRepositoryDestiny linuxPackageRepositoryDestiny,
|
||||
IAzurelinuxRepositoryServiceHttpStrategy httpStrategy
|
||||
)
|
||||
{
|
||||
_linuxPackageRepositoryDestiny = linuxPackageRepositoryDestiny
|
||||
?? throw new ArgumentNullException(nameof(linuxPackageRepositoryDestiny));
|
||||
_httpStrategy = httpStrategy ?? throw new ArgumentNullException(nameof(httpStrategy));
|
||||
}
|
||||
|
||||
public async Task<string> RemoteCall()
|
||||
{
|
||||
using (var handler = new HttpClientHandler())
|
||||
{
|
||||
using (var client = new HttpClient(handler))
|
||||
{
|
||||
var authHeader =
|
||||
Convert.ToBase64String(Encoding.UTF8.GetBytes((string) _linuxPackageRepositoryDestiny.GetSimpleAuth()));
|
||||
client.DefaultRequestHeaders.Authorization =
|
||||
new AuthenticationHeaderValue("Basic", authHeader);
|
||||
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
client.Timeout = TimeSpan.FromMinutes(10);
|
||||
|
||||
return await _httpStrategy.Execute(client, _linuxPackageRepositoryDestiny.GetBaseAddress());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Build.UploadToLinuxPackageRepository
|
||||
{
|
||||
internal class PullQueuedPackageStatus : IAzurelinuxRepositoryServiceHttpStrategy
|
||||
{
|
||||
private readonly QueueResourceLocation _queueResourceLocation;
|
||||
|
||||
public PullQueuedPackageStatus(QueueResourceLocation queueResourceLocation)
|
||||
{
|
||||
_queueResourceLocation = queueResourceLocation
|
||||
?? throw new ArgumentNullException(nameof(queueResourceLocation));
|
||||
}
|
||||
|
||||
public async Task<string> Execute(HttpClient client, Uri baseAddress)
|
||||
{
|
||||
using (var response = await client.GetAsync(new Uri(baseAddress, _queueResourceLocation.Location)))
|
||||
{
|
||||
if (!response.IsSuccessStatusCode)
|
||||
throw new FailedToAddPackageToPackageRepositoryException(
|
||||
"Failed to make request to " + _queueResourceLocation.Location);
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
return !body.Contains("status") ? "" : JObject.Parse(body)["status"].ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Build.UploadToLinuxPackageRepository
|
||||
{
|
||||
internal class QueueResourceLocation
|
||||
{
|
||||
public QueueResourceLocation(string location)
|
||||
{
|
||||
Location = location ?? throw new ArgumentNullException(nameof(location));
|
||||
}
|
||||
|
||||
public string Location { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Build.UploadToLinuxPackageRepository
|
||||
{
|
||||
public class RetryFailedException : Exception
|
||||
{
|
||||
public RetryFailedException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public RetryFailedException()
|
||||
{
|
||||
}
|
||||
|
||||
public RetryFailedException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Build.Framework;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Task = Microsoft.Build.Utilities.Task;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Build.UploadToLinuxPackageRepository
|
||||
{
|
||||
public class UploadToLinuxPackageRepository : Task
|
||||
{
|
||||
/// <summary>
|
||||
/// The Azure repository service user name.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Username { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Azure repository service Password.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Azure repository service URL ex: "tux-devrepo.corp.microsoft.com".
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Server { get; set; }
|
||||
|
||||
[Required]
|
||||
public string RepositoryId { get; set; }
|
||||
|
||||
[Required]
|
||||
public string PathOfPackageToUpload { get; set; }
|
||||
|
||||
[Required]
|
||||
public string PackageNameInLinuxPackageRepository { get; set; }
|
||||
|
||||
|
||||
[Required]
|
||||
public string PackageVersionInLinuxPackageRepository { get; set; }
|
||||
|
||||
|
||||
public override bool Execute()
|
||||
{
|
||||
ExecuteAsyncWithRetry().GetAwaiter().GetResult();
|
||||
return true;
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task ExecuteAsyncWithRetry()
|
||||
{
|
||||
await ExponentialRetry.ExecuteWithRetry(
|
||||
UploadAndAddpackageAndEnsureItIsReady,
|
||||
s => s == "",
|
||||
maxRetryCount: 3,
|
||||
timer: () => ExponentialRetry.Timer(ExponentialRetry.Intervals),
|
||||
taskDescription: $"running {nameof(UploadAndAddpackageAndEnsureItIsReady)}");
|
||||
}
|
||||
|
||||
private async Task<string> UploadAndAddpackageAndEnsureItIsReady()
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.LogMessage(
|
||||
MessageImportance.High,
|
||||
"Begin uploading Linux Package to feed service, RepositoryId {0}, Server {1}, Package to upload {2}.",
|
||||
RepositoryId,
|
||||
Server,
|
||||
PathOfPackageToUpload);
|
||||
|
||||
var linuxPackageRepositoryDestiny =
|
||||
new LinuxPackageRepositoryDestiny(Username, Password, Server, RepositoryId);
|
||||
var uploadResponse = await new LinuxPackageRepositoryHttpPrepare(
|
||||
linuxPackageRepositoryDestiny,
|
||||
new FileUploadStrategy(PathOfPackageToUpload)).RemoteCall();
|
||||
|
||||
var idInRepositoryService = new IdInRepositoryService(JObject.Parse(uploadResponse)["id"].ToString());
|
||||
|
||||
var addPackageResponse = await new LinuxPackageRepositoryHttpPrepare(
|
||||
linuxPackageRepositoryDestiny,
|
||||
new AddPackageStrategy(
|
||||
idInRepositoryService,
|
||||
PackageNameInLinuxPackageRepository,
|
||||
PackageVersionInLinuxPackageRepository,
|
||||
linuxPackageRepositoryDestiny.RepositoryId)).RemoteCall();
|
||||
|
||||
var queueResourceLocation = new QueueResourceLocation(addPackageResponse);
|
||||
|
||||
Func<Task<string>> pullQueuedPackageStatus = new LinuxPackageRepositoryHttpPrepare(
|
||||
linuxPackageRepositoryDestiny,
|
||||
new PullQueuedPackageStatus(queueResourceLocation)).RemoteCall;
|
||||
|
||||
await ExponentialRetry.ExecuteWithRetry(
|
||||
pullQueuedPackageStatus,
|
||||
s => s == "fileReady",
|
||||
5,
|
||||
() => ExponentialRetry.Timer(ExponentialRetry.Intervals),
|
||||
$"PullQueuedPackageStatus location: {queueResourceLocation.Location}");
|
||||
|
||||
Log.LogMessage(
|
||||
MessageImportance.High,
|
||||
"Upload to feed service is completed, queue resource location {0}",
|
||||
queueResourceLocation.Location);
|
||||
|
||||
return "";
|
||||
}
|
||||
catch (FailedToAddPackageToPackageRepositoryException e)
|
||||
{
|
||||
return e.ToString();
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
return e.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue