Skip to content
This repository was archived by the owner on Nov 20, 2023. It is now read-only.

Commit c9561a4

Browse files
kkoziarskijkotalik
andauthored
Added tags to yaml definition to be used in CLI run filter (#528)
* fixed unit test: Services_UnrecognizedKey * TyeAssert - duplicated check removed * YAML service.tags added to parser and unit tests * filter services by tags - run argument + ApplicationFactory filter * filter ingress by tags * Make tags work for all scenarios Co-authored-by: Justin Kotalik <[email protected]>
1 parent 18712f9 commit c9561a4

19 files changed

+228
-56
lines changed

src/Microsoft.Tye.Core/ApplicationFactory.cs

+11-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
using System;
66
using System.Collections.Generic;
7-
using System.ComponentModel.DataAnnotations;
87
using System.IO;
98
using System.Linq;
109
using System.Runtime.InteropServices;
@@ -15,7 +14,7 @@ namespace Microsoft.Tye
1514
{
1615
public static class ApplicationFactory
1716
{
18-
public static async Task<ApplicationBuilder> CreateAsync(OutputContext output, FileInfo source)
17+
public static async Task<ApplicationBuilder> CreateAsync(OutputContext output, FileInfo source, ApplicationFactoryFilter? filter = null)
1918
{
2019
if (source is null)
2120
{
@@ -71,7 +70,11 @@ public static async Task<ApplicationBuilder> CreateAsync(OutputContext output, F
7170
root.Extensions.Add(extension);
7271
}
7372

74-
foreach (var configService in config.Services)
73+
var services = filter?.ServicesFilter != null ?
74+
config.Services.Where(filter.ServicesFilter).ToList() :
75+
config.Services;
76+
77+
foreach (var configService in services)
7578
{
7679
ServiceBuilder service;
7780
if (root.Services.Any(s => s.Name == configService.Name))
@@ -320,7 +323,11 @@ service is ProjectServiceBuilder project2 &&
320323
}
321324
}
322325

323-
foreach (var configIngress in config.Ingress)
326+
var ingresses = filter?.IngressFilter != null ?
327+
config.Ingress.Where(filter.IngressFilter).ToList() :
328+
config.Ingress;
329+
330+
foreach (var configIngress in ingresses)
324331
{
325332
var ingress = new IngressBuilder(configIngress.Name!);
326333
ingress.Replicas = configIngress.Replicas ?? 1;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Linq;
7+
using Microsoft.Tye.ConfigModel;
8+
9+
namespace Microsoft.Tye
10+
{
11+
public class ApplicationFactoryFilter
12+
{
13+
public Func<ConfigService, bool>? ServicesFilter { get; set; }
14+
public Func<ConfigIngress, bool>? IngressFilter { get; set; }
15+
16+
public static ApplicationFactoryFilter? GetApplicationFactoryFilter(string[] tags)
17+
{
18+
ApplicationFactoryFilter? filter = null;
19+
20+
if (tags != null && tags.Any())
21+
{
22+
filter = new ApplicationFactoryFilter
23+
{
24+
ServicesFilter = service => tags.Any(b => service.Tags.Contains(b)),
25+
IngressFilter = service => tags.Any(b => service.Tags.Contains(b))
26+
};
27+
}
28+
29+
return filter;
30+
}
31+
}
32+
}

src/Microsoft.Tye.Core/ConfigModel/ConfigIngress.cs

+1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ public class ConfigIngress
1414
public int? Replicas { get; set; }
1515
public List<ConfigIngressRule> Rules { get; set; } = new List<ConfigIngressRule>();
1616
public List<ConfigIngressBinding> Bindings { get; set; } = new List<ConfigIngressBinding>();
17+
public List<string> Tags { get; set; } = new List<string>();
1718
}
1819
}

src/Microsoft.Tye.Core/ConfigModel/ConfigService.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
using System.Collections.Generic;
66
using System.ComponentModel.DataAnnotations;
7-
using Tye;
87
using YamlDotNet.Serialization;
98

109
namespace Microsoft.Tye.ConfigModel
@@ -37,6 +36,7 @@ public class ConfigService
3736
[YamlMember(Alias = "env")]
3837
public List<ConfigConfigurationSource> Configuration { get; set; } = new List<ConfigConfigurationSource>();
3938
public List<BuildProperty> BuildProperties { get; set; } = new List<BuildProperty>();
39+
public List<string> Tags { get; set; } = new List<string>();
4040
public ConfigProbe? Liveness { get; set; }
4141
public ConfigProbe? Readiness { get; set; }
4242
}

src/Microsoft.Tye.Core/Serialization/ConfigIngressParser.cs

+17
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ private static void HandleIngressMapping(YamlMappingNode yamlMappingNode, Config
5959
}
6060
HandleIngressBindings((child.Value as YamlSequenceNode)!, configIngress.Bindings);
6161
break;
62+
case "tags":
63+
if (child.Value.NodeType != YamlNodeType.Sequence)
64+
{
65+
throw new TyeYamlException(child.Value.Start, CoreStrings.FormatExpectedYamlSequence(key));
66+
}
67+
68+
HandleIngressTags((child.Value as YamlSequenceNode)!, configIngress.Tags);
69+
break;
6270
default:
6371
throw new TyeYamlException(child.Key.Start, CoreStrings.FormatUnrecognizedKey(key));
6472
}
@@ -137,5 +145,14 @@ private static void HandleIngressBindingMapping(YamlMappingNode yamlMappingNode,
137145
}
138146
}
139147
}
148+
149+
private static void HandleIngressTags(YamlSequenceNode yamlSequenceNode, List<string> tags)
150+
{
151+
foreach (var child in yamlSequenceNode!.Children)
152+
{
153+
var tag = YamlParser.GetScalarValue(child);
154+
tags.Add(tag);
155+
}
156+
}
140157
}
141158
}

src/Microsoft.Tye.Core/Serialization/ConfigServiceParser.cs

+17-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.Collections.Generic;
6-
using System.Linq;
7-
using Microsoft.Build.Evaluation;
86
using Microsoft.Tye.ConfigModel;
97
using YamlDotNet.RepresentationModel;
108

@@ -131,6 +129,14 @@ private static void HandleServiceNameMapping(YamlMappingNode yamlMappingNode, Co
131129
service.Readiness = new ConfigProbe();
132130
HandleServiceProbe((YamlMappingNode)child.Value, service.Readiness!);
133131
break;
132+
case "tags":
133+
if (child.Value.NodeType != YamlNodeType.Sequence)
134+
{
135+
throw new TyeYamlException(child.Value.Start, CoreStrings.FormatExpectedYamlSequence(key));
136+
}
137+
138+
HandleServiceTags((child.Value as YamlSequenceNode)!, service.Tags);
139+
break;
134140
default:
135141
throw new TyeYamlException(child.Key.Start, CoreStrings.FormatUnrecognizedKey(key));
136142
}
@@ -444,5 +450,14 @@ private static void HandleServiceBuildPropertyNameMapping(YamlMappingNode yamlMa
444450
}
445451
}
446452
}
453+
454+
private static void HandleServiceTags(YamlSequenceNode yamlSequenceNode, List<string> tags)
455+
{
456+
foreach (var child in yamlSequenceNode!.Children)
457+
{
458+
var tag = YamlParser.GetScalarValue(child);
459+
tags.Add(tag);
460+
}
461+
}
447462
}
448463
}

src/Microsoft.Tye.Core/Serialization/YamlParser.cs

+2
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,14 @@ public ConfigApplication ParseConfigApplication()
6262
service.Bindings ??= new List<ConfigServiceBinding>();
6363
service.Configuration ??= new List<ConfigConfigurationSource>();
6464
service.Volumes ??= new List<ConfigVolume>();
65+
service.Tags ??= new List<string>();
6566
}
6667

6768
foreach (var ingress in app.Ingress)
6869
{
6970
ingress.Bindings ??= new List<ConfigIngressBinding>();
7071
ingress.Rules ??= new List<ConfigIngressRule>();
72+
ingress.Tags ??= new List<string>();
7173
}
7274

7375
return app;

src/Microsoft.Tye.Core/StandardOptions.cs

+16
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,22 @@ public static Option Environment
3030
}
3131
}
3232

33+
public static Option Tags
34+
{
35+
get
36+
{
37+
return new Option("--tags", "--filter")
38+
{
39+
Argument = new Argument<List<string>>("tags")
40+
{
41+
Arity = ArgumentArity.OneOrMore
42+
},
43+
Description = "Filter the group of running services by tag.",
44+
Required = false
45+
};
46+
}
47+
}
48+
3349
public static Option Force
3450
{
3551
get

src/tye/BuildHost.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ namespace Microsoft.Tye
1111
{
1212
public static class BuildHost
1313
{
14-
public static async Task BuildAsync(IConsole console, FileInfo path, Verbosity verbosity, bool interactive)
14+
public static async Task BuildAsync(IConsole console, FileInfo path, Verbosity verbosity, bool interactive, string[] tags)
1515
{
1616
var output = new OutputContext(console, verbosity);
17-
var application = await ApplicationFactory.CreateAsync(output, path);
17+
var filter = ApplicationFactoryFilter.GetApplicationFactoryFilter(tags);
18+
var application = await ApplicationFactory.CreateAsync(output, path, filter);
1819
if (application.Services.Count == 0)
1920
{
2021
throw new CommandException($"No services found in \"{application.Source.Name}\"");

src/tye/GenerateHost.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@ namespace Microsoft.Tye
1313
{
1414
public static class GenerateHost
1515
{
16-
public static async Task GenerateAsync(IConsole console, FileInfo path, Verbosity verbosity, bool interactive, string ns)
16+
public static async Task GenerateAsync(IConsole console, FileInfo path, Verbosity verbosity, bool interactive, string ns, string[] tags)
1717
{
1818
var output = new OutputContext(console, verbosity);
1919

2020
output.WriteInfoLine("Loading Application Details...");
21-
var application = await ApplicationFactory.CreateAsync(output, path);
21+
var filter = ApplicationFactoryFilter.GetApplicationFactoryFilter(tags);
22+
var application = await ApplicationFactory.CreateAsync(output, path, filter);
2223
if (application.Services.Count == 0)
2324
{
2425
throw new CommandException($"No services found in \"{application.Source.Name}\"");

src/tye/Program.BuildCommand.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,18 @@ public static Command CreateBuildCommand()
2121
CommonArguments.Path_Required,
2222
StandardOptions.Interactive,
2323
StandardOptions.Verbosity,
24+
StandardOptions.Tags
2425
};
2526

26-
command.Handler = CommandHandler.Create<IConsole, FileInfo, Verbosity, bool>((console, path, verbosity, interactive) =>
27+
command.Handler = CommandHandler.Create<IConsole, FileInfo, Verbosity, bool, string[]>((console, path, verbosity, interactive, tags) =>
2728
{
2829
// Workaround for https://github.com/dotnet/command-line-api/issues/723#issuecomment-593062654
2930
if (path is null)
3031
{
3132
throw new CommandException("No project or solution file was found.");
3233
}
3334

34-
return BuildHost.BuildAsync(console, path, verbosity, interactive);
35+
return BuildHost.BuildAsync(console, path, verbosity, interactive, tags);
3536
});
3637

3738
return command;

src/tye/Program.DeployCommand.cs

+8-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.CommandLine;
88
using System.CommandLine.Invocation;
99
using System.IO;
10+
using System.Linq;
1011
using System.Text;
1112
using System.Threading.Tasks;
1213
using Microsoft.Tye.ConfigModel;
@@ -23,6 +24,7 @@ public static Command CreateDeployCommand()
2324
StandardOptions.Interactive,
2425
StandardOptions.Verbosity,
2526
StandardOptions.Namespace,
27+
StandardOptions.Tags,
2628
};
2729

2830
command.AddOption(new Option(new[] { "-f", "--force" })
@@ -31,7 +33,7 @@ public static Command CreateDeployCommand()
3133
Required = false
3234
});
3335

34-
command.Handler = CommandHandler.Create<IConsole, FileInfo, Verbosity, bool, bool, string>(async (console, path, verbosity, interactive, force, @namespace) =>
36+
command.Handler = CommandHandler.Create<IConsole, FileInfo, Verbosity, bool, bool, string, string[]>(async (console, path, verbosity, interactive, force, @namespace, tags) =>
3537
{
3638
// Workaround for https://github.com/dotnet/command-line-api/issues/723#issuecomment-593062654
3739
if (path is null)
@@ -42,12 +44,15 @@ public static Command CreateDeployCommand()
4244
var output = new OutputContext(console, verbosity);
4345

4446
output.WriteInfoLine("Loading Application Details...");
45-
var application = await ApplicationFactory.CreateAsync(output, path);
47+
48+
var filter = ApplicationFactoryFilter.GetApplicationFactoryFilter(tags);
49+
50+
var application = await ApplicationFactory.CreateAsync(output, path, filter);
4651
if (application.Services.Count == 0)
4752
{
4853
throw new CommandException($"No services found in \"{application.Source.Name}\"");
4954
}
50-
if (!String.IsNullOrEmpty(@namespace))
55+
if (!string.IsNullOrEmpty(@namespace))
5156
{
5257
application.Namespace = @namespace;
5358
}

src/tye/Program.GenerateCommand.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,22 @@ public static Command CreateGenerateCommand()
2222
StandardOptions.Interactive,
2323
StandardOptions.Verbosity,
2424
StandardOptions.Namespace,
25+
StandardOptions.Tags
2526
};
2627

2728
// This is a super-secret VIP-only command! It's useful for testing, but we're
2829
// not documenting it right now.
2930
command.IsHidden = true;
3031

31-
command.Handler = CommandHandler.Create<IConsole, FileInfo, Verbosity, bool, string>((console, path, verbosity, interactive, @namespace) =>
32+
command.Handler = CommandHandler.Create<IConsole, FileInfo, Verbosity, bool, string, string[]>((console, path, verbosity, interactive, @namespace, tags) =>
3233
{
3334
// Workaround for https://github.com/dotnet/command-line-api/issues/723#issuecomment-593062654
3435
if (path is null)
3536
{
3637
throw new CommandException("No project or solution file was found.");
3738
}
3839

39-
return GenerateHost.GenerateAsync(console, path, verbosity, interactive, @namespace);
40+
return GenerateHost.GenerateAsync(console, path, verbosity, interactive, @namespace, tags);
4041
});
4142

4243
return command;

src/tye/Program.PushCommand.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public static Command CreatePushCommand()
1818
CommonArguments.Path_Required,
1919
StandardOptions.Interactive,
2020
StandardOptions.Verbosity,
21+
StandardOptions.Tags
2122
};
2223

2324
command.AddOption(new Option(new[] { "-f", "--force" })
@@ -26,7 +27,7 @@ public static Command CreatePushCommand()
2627
Required = false
2728
});
2829

29-
command.Handler = CommandHandler.Create<IConsole, FileInfo, Verbosity, bool, bool>(async (console, path, verbosity, interactive, force) =>
30+
command.Handler = CommandHandler.Create<IConsole, FileInfo, Verbosity, bool, bool, string[]>(async (console, path, verbosity, interactive, force, tags) =>
3031
{
3132
// Workaround for https://github.com/dotnet/command-line-api/issues/723#issuecomment-593062654
3233
if (path is null)
@@ -37,7 +38,9 @@ public static Command CreatePushCommand()
3738
var output = new OutputContext(console, verbosity);
3839

3940
output.WriteInfoLine("Loading Application Details...");
40-
var application = await ApplicationFactory.CreateAsync(output, path);
41+
var filter = ApplicationFactoryFilter.GetApplicationFactoryFilter(tags);
42+
43+
var application = await ApplicationFactory.CreateAsync(output, path, filter);
4144
if (application.Services.Count == 0)
4245
{
4346
throw new CommandException($"No services found in \"{application.Source.Name}\"");

0 commit comments

Comments
 (0)