зеркало из https://github.com/microsoft/Atlas.git
Https paths for workflow argument (#62)
* Adding blueprint provider for https paths * enables you to deploy with a publc url as the workflow argument * logs references to template files as they're used, like any http calls * github repo urls are re-written as the corresponding raw file location * Updating readme to show running from url in Getting Started Resolves #59
This commit is contained in:
Родитель
04848c3c88
Коммит
075bcfbf1f
18
README.md
18
README.md
|
@ -44,7 +44,16 @@ If you want to use a package manager:
|
|||
|
||||
## Getting Started
|
||||
|
||||
From a console window, `mkdir demo` to create a new subfolder.
|
||||
An existing workflow can be executed directly from a public web server. You
|
||||
can run any of the [examples][Atlas Examples] in this repository with the `atlas deploy` command:
|
||||
|
||||
```
|
||||
atlas deploy https://github.com/Microsoft/Atlas/tree/master/examples/101-messages
|
||||
```
|
||||
|
||||
#### Creating a new workflow
|
||||
|
||||
To create a new workflow, from a console window execute `mkdir demo` to create a new subfolder.
|
||||
|
||||
Add a `demo/workflow.yaml` file to declare operations:
|
||||
|
||||
|
@ -55,7 +64,7 @@ operations:
|
|||
- message: "All values: {{ json . }}"
|
||||
```
|
||||
|
||||
Add a `hello/values.yaml` file to declare defaults:
|
||||
Add a `demo/values.yaml` file to declare defaults:
|
||||
|
||||
```
|
||||
info:
|
||||
|
@ -77,7 +86,9 @@ Atlas
|
|||
- All values: {"info": {"greeting": "Hello", "name": "Atlas"}}
|
||||
```
|
||||
|
||||
Clone the [Atlas Examples](https://github.com/Microsoft/Atlas/tree/master/examples) folder to see additional
|
||||
#### Exploring the examples
|
||||
|
||||
You can also clone the Atlas GitHub repo to explore the [examples][Atlas Examples] and see
|
||||
kinds of operations Atlas can perform.
|
||||
|
||||
```
|
||||
|
@ -183,6 +194,7 @@ email to ensure we received your original message. Further information, includin
|
|||
the [Security TechCenter](https://technet.microsoft.com/en-us/security/default).
|
||||
|
||||
[Atlas Logo]: https://github.com/Microsoft/Atlas/raw/master/docs/icon-128.png
|
||||
[Atlas Examples]: https://github.com/Microsoft/Atlas/tree/master/examples
|
||||
[Handlebars]: http://handlebarsjs.com/
|
||||
[YAML]: http://yaml.org/
|
||||
[JSON]: http://json.org/
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.Atlas.CommandLine.Blueprints
|
||||
namespace Microsoft.Atlas.CommandLine.Blueprints.Providers
|
||||
{
|
||||
public class ArchiveBlueprintPackage : IBlueprintPackage
|
||||
{
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.Atlas.CommandLine.Blueprints
|
||||
namespace Microsoft.Atlas.CommandLine.Blueprints.Providers
|
||||
{
|
||||
public class ArchiveBlueprintPackageProvider : IBlueprintPackageProvider
|
||||
{
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.Atlas.CommandLine.Blueprints
|
||||
namespace Microsoft.Atlas.CommandLine.Blueprints.Providers
|
||||
{
|
||||
public class DirectoryBlueprintPackage : IBlueprintPackage
|
||||
{
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.Atlas.CommandLine.Blueprints
|
||||
namespace Microsoft.Atlas.CommandLine.Blueprints.Providers
|
||||
{
|
||||
public class DirectoryBlueprintPackageProvider : IBlueprintPackageProvider
|
||||
{
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using Microsoft.Atlas.CommandLine.OAuth2;
|
||||
|
||||
namespace Microsoft.Atlas.CommandLine.Blueprints.Providers
|
||||
{
|
||||
internal class HttpsFilesBlueprintPackage : IBlueprintPackage
|
||||
{
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private UriParts _blueprintUri;
|
||||
private HttpClient _httpClient;
|
||||
|
||||
public HttpsFilesBlueprintPackage(IHttpClientFactory httpClientFactory, UriParts blueprintUri)
|
||||
{
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_blueprintUri = blueprintUri;
|
||||
_httpClient = _httpClientFactory.Create(auth: null);
|
||||
}
|
||||
|
||||
public bool Exists(string path)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Head, _blueprintUri.Append(path).ToString());
|
||||
var response = _httpClient.SendAsync(request).GetAwaiter().GetResult();
|
||||
|
||||
var statusCode = (int)response.StatusCode;
|
||||
if (statusCode == 404)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
return true;
|
||||
}
|
||||
|
||||
public TextReader OpenText(string path)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, _blueprintUri.Append(path).ToString());
|
||||
var response = _httpClient.SendAsync(request).GetAwaiter().GetResult();
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var text = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
|
||||
return new StringReader(text);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using Microsoft.Atlas.CommandLine.OAuth2;
|
||||
|
||||
namespace Microsoft.Atlas.CommandLine.Blueprints.Providers
|
||||
{
|
||||
public class HttpsFilesBlueprintPackageProvider : IBlueprintPackageProvider
|
||||
{
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
|
||||
public HttpsFilesBlueprintPackageProvider(IHttpClientFactory httpClientFactory)
|
||||
{
|
||||
_httpClientFactory = httpClientFactory;
|
||||
}
|
||||
|
||||
public IBlueprintPackage TryGetBlueprintPackage(string blueprint)
|
||||
{
|
||||
if (!blueprint.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var uriParts = UriParts.Parse(blueprint);
|
||||
uriParts.RewriteGitHubUris();
|
||||
uriParts.RemoveWorkflowYaml();
|
||||
return new HttpsFilesBlueprintPackage(_httpClientFactory, uriParts);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
|
||||
namespace Microsoft.Atlas.CommandLine.Blueprints.Providers
|
||||
{
|
||||
public class UriParts
|
||||
{
|
||||
private static readonly HostString _github = new HostString("github.com");
|
||||
private static readonly HostString _githubusercontent = new HostString("raw.githubusercontent.com");
|
||||
|
||||
private static readonly PathString _tree = new PathString("/tree");
|
||||
private static readonly PathString _blob = new PathString("/blob");
|
||||
private static readonly PathString _workflowYaml = new PathString("/workflow.yaml");
|
||||
|
||||
public string Scheme { get; set; }
|
||||
|
||||
public HostString Host { get; set; }
|
||||
|
||||
public PathString Path { get; set; }
|
||||
|
||||
public QueryString Query { get; set; }
|
||||
|
||||
public FragmentString Fragment { get; set; }
|
||||
|
||||
public static UriParts Parse(string uri)
|
||||
{
|
||||
UriHelper.FromAbsolute(
|
||||
uri,
|
||||
out var scheme,
|
||||
out var host,
|
||||
out var path,
|
||||
out var query,
|
||||
out var fragment);
|
||||
|
||||
return new UriParts
|
||||
{
|
||||
Scheme = scheme,
|
||||
Host = host,
|
||||
Path = path,
|
||||
Query = query,
|
||||
Fragment = fragment,
|
||||
};
|
||||
}
|
||||
|
||||
public UriParts Append(string path)
|
||||
{
|
||||
return new UriParts
|
||||
{
|
||||
Scheme = Scheme,
|
||||
Host = Host,
|
||||
Path = Path + new PathString("/" + path),
|
||||
Query = Query,
|
||||
Fragment = Fragment,
|
||||
};
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Scheme + "://" + Host + Path + Query + Fragment;
|
||||
}
|
||||
|
||||
public bool RewriteGitHubUris()
|
||||
{
|
||||
if (!Host.Equals(_github))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var segments = SplitPathSegments(Path);
|
||||
if (segments.Count < 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!segments[2].Equals(_tree) && !segments[2].Equals(_blob))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
segments.RemoveAt(2);
|
||||
|
||||
Host = _githubusercontent;
|
||||
Path = segments.Aggregate(default(PathString), (a, b) => a + b);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemoveWorkflowYaml()
|
||||
{
|
||||
var result = new List<PathString>();
|
||||
|
||||
var segments = SplitPathSegments(Path);
|
||||
if (segments.Count < 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!segments.Last().Equals(_workflowYaml))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
segments.RemoveAt(segments.Count() - 1);
|
||||
Path = segments.Aggregate(default(PathString), (a, b) => a + b);
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<PathString> SplitPathSegments(PathString path)
|
||||
{
|
||||
var segments = new List<PathString>();
|
||||
var slashIndex = 0;
|
||||
while (slashIndex < path.Value.Length)
|
||||
{
|
||||
var nextIndex = path.Value.IndexOf('/', slashIndex + 1);
|
||||
if (nextIndex < 0)
|
||||
{
|
||||
nextIndex = path.Value.Length;
|
||||
}
|
||||
|
||||
segments.Add(new PathString(path.Value.Substring(slashIndex, nextIndex - slashIndex)));
|
||||
slashIndex = nextIndex;
|
||||
}
|
||||
|
||||
return segments;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@
|
|||
<PackageReference Include="Handlebars.Net" Version="1.9.5" />
|
||||
<PackageReference Include="JmesPath.Net" Version="1.0.101" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.2" />
|
||||
|
|
|
@ -7,6 +7,7 @@ using System.Reflection;
|
|||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Atlas.CommandLine.Accounts;
|
||||
using Microsoft.Atlas.CommandLine.Blueprints;
|
||||
using Microsoft.Atlas.CommandLine.Blueprints.Providers;
|
||||
using Microsoft.Atlas.CommandLine.Commands;
|
||||
using Microsoft.Atlas.CommandLine.ConsoleOutput;
|
||||
using Microsoft.Atlas.CommandLine.JsonClient;
|
||||
|
@ -84,6 +85,7 @@ namespace Microsoft.Atlas.CommandLine
|
|||
.AddTransient<IPatternMatcherFactory, PatternMatcherFactory>()
|
||||
|
||||
.AddTransient<IBlueprintManager, BlueprintManager>()
|
||||
.AddTransient<IBlueprintPackageProvider, HttpsFilesBlueprintPackageProvider>()
|
||||
.AddTransient<IBlueprintPackageProvider, DirectoryBlueprintPackageProvider>()
|
||||
.AddTransient<IBlueprintPackageProvider, ArchiveBlueprintPackageProvider>()
|
||||
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Atlas.CommandLine.Blueprints.Providers;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace Microsoft.Atlas.CommandLine.Tests.Blueprints.Providers
|
||||
{
|
||||
[TestClass]
|
||||
public class UriPartsTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void UriIsParsedIntoParts()
|
||||
{
|
||||
var parts = UriParts.Parse("https://example.com/one/two?three=four&five=six#/seven");
|
||||
Assert.AreEqual("https", parts.Scheme);
|
||||
Assert.AreEqual(new HostString("example.com"), parts.Host);
|
||||
Assert.AreEqual(new PathString("/one") + new PathString("/two"), parts.Path);
|
||||
Assert.AreEqual(QueryString.Create("three", "four") + QueryString.Create("five", "six"), parts.Query);
|
||||
Assert.AreEqual(new FragmentString("#/seven"), parts.Fragment);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void UriBecomesStringAgain()
|
||||
{
|
||||
var parts = new UriParts
|
||||
{
|
||||
Scheme = "https",
|
||||
Host = new HostString("example.com"),
|
||||
Path = new PathString("/one") + new PathString("/two"),
|
||||
Query = QueryString.Create("three", "four") + QueryString.Create("five", "six"),
|
||||
Fragment = new FragmentString("#/seven"),
|
||||
};
|
||||
|
||||
Assert.AreEqual("https://example.com/one/two?three=four&five=six#/seven", parts.ToString());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow("https://github.com/name1/name2/tree/master/stable/workflow-name", "https://raw.githubusercontent.com/name1/name2/master/stable/workflow-name")]
|
||||
[DataRow("https://github.com/name1/name2/tree/master/stable/workflow-name/", "https://raw.githubusercontent.com/name1/name2/master/stable/workflow-name/")]
|
||||
[DataRow("https://github.com/name1/name2/blob/master/README.md", "https://raw.githubusercontent.com/name1/name2/master/README.md")]
|
||||
public void GitHubPathsChangedToRawFilePaths(string originalPath, string expectedPath)
|
||||
{
|
||||
var parts = UriParts.Parse(originalPath);
|
||||
parts.RewriteGitHubUris();
|
||||
|
||||
Assert.AreEqual(expectedPath, parts.ToString());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow("https://github.com/name1/name2/tree/master/stable/workflow-name/workflow.yaml", "https://github.com/name1/name2/tree/master/stable/workflow-name")]
|
||||
[DataRow("https://raw.githubusercontent.com/name1/name2/master/stable/workflow-name/workflow.yaml", "https://raw.githubusercontent.com/name1/name2/master/stable/workflow-name")]
|
||||
[DataRow("https://anywhere.com/path/workflow.yaml?x=4#y", "https://anywhere.com/path?x=4#y")]
|
||||
public void WorkflowYamlRemovedFromPath(string originalPath, string expectedPath)
|
||||
{
|
||||
var parts = UriParts.Parse(originalPath);
|
||||
parts.RemoveWorkflowYaml();
|
||||
|
||||
Assert.AreEqual(expectedPath, parts.ToString());
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче