Skip to content

Commit 8925d97

Browse files
authored
Giuliov/issue 142 (#143)
* first attempt * fix test * Check if mapping was deleted (#142) * review exit codes
1 parent 6c5e51e commit 8925d97

23 files changed

+119
-58
lines changed

src/aggregator-cli/CommandBase.cs

+8-5
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,18 @@ internal int Run(CancellationToken cancellationToken)
4343
t.Wait(cancellationToken);
4444
cancellationToken.ThrowIfCancellationRequested();
4545
int rc = t.Result;
46-
if (rc != 0)
46+
if (rc == ExitCodes.Success)
4747
{
48-
Logger.WriteError("Failed!");
48+
Logger.WriteSuccess("Succeeded");
49+
}
50+
else if (rc == ExitCodes.NotFound)
51+
{
52+
Logger.WriteWarning("Not found");
4953
}
5054
else
5155
{
52-
Logger.WriteSuccess("Succeeded");
56+
Logger.WriteError("Failed!");
5357
}
54-
5558
return rc;
5659
}
5760
catch (Exception ex)
@@ -61,7 +64,7 @@ internal int Run(CancellationToken cancellationToken)
6164
? ex.Message
6265
: ex.InnerException.Message
6366
);
64-
return 99;
67+
return ExitCodes.Unexpected;
6568
}
6669
}
6770

src/aggregator-cli/ExitCodes.cs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace aggregator.cli
6+
{
7+
internal static class ExitCodes
8+
{
9+
public const int Success = 0;
10+
public const int Failure = 1;
11+
public const int InvalidArguments = 2;
12+
public const int NotFound = 3;
13+
public const int Unexpected = 99;
14+
}
15+
}

src/aggregator-cli/Instances/ConfigureInstanceCommand.cs

+6-6
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,16 @@ internal override async Task<int> RunAsync(CancellationToken cancellationToken)
3636
.BuildAsync(cancellationToken);
3737
var instances = new AggregatorInstances(context.Azure, context.Logger, context.Naming);
3838
var instance = context.Naming.Instance(Name, ResourceGroup);
39-
bool ok = false;
4039
if (Authentication)
4140
{
42-
ok = await instances.ChangeAppSettingsAsync(instance, Location, SaveMode, cancellationToken);
43-
} else
41+
bool ok = await instances.ChangeAppSettingsAsync(instance, Location, SaveMode, cancellationToken);
42+
return ok ? ExitCodes.Success : ExitCodes.Failure;
43+
}
44+
else
4445
{
4546
context.Logger.WriteError($"Unsupported command option(s)");
47+
return ExitCodes.InvalidArguments;
4648
}
47-
48-
return ok ? 0 : 1;
4949
}
5050
}
51-
}
51+
}

src/aggregator-cli/Instances/InstallInstanceCommand.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@ internal override async Task<int> RunAsync(CancellationToken cancellationToken)
4444
if (!validHostingPlanSkus.Contains(HostingPlanSku))
4545
{
4646
Logger.WriteError($"Invalid value for hostingPlanSku: must be one of {String.Join(",", validHostingPlanSkus)}");
47-
return 2;
47+
return ExitCodes.InvalidArguments;
4848
}
4949
if (!validHostingPlanTiers.Contains(HostingPlanTier))
5050
{
5151
Logger.WriteError($"Invalid value for hostingPlanTier: must be one of {String.Join(",", validHostingPlanTiers)}");
52-
return 2;
52+
return ExitCodes.InvalidArguments;
5353
}
5454
var tuning = new AggregatorInstances.InstanceFineTuning
5555
{
@@ -65,7 +65,7 @@ internal override async Task<int> RunAsync(CancellationToken cancellationToken)
6565
var instances = new AggregatorInstances(context.Azure, context.Logger, context.Naming);
6666
var instance = context.Naming.GetInstanceCreateNames(Name, ResourceGroup);
6767
bool ok = await instances.AddAsync(instance, Location, RequiredVersion, SourceUrl, tuning, cancellationToken);
68-
return ok ? 0 : 1;
68+
return ok ? ExitCodes.Success : ExitCodes.Failure;
6969
}
7070
}
7171
}

src/aggregator-cli/Instances/ListInstancesCommand.cs

+7-5
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ private async Task<int> ListByLocationAsync(CommandContext context, AggregatorIn
5050
{
5151
context.Logger.WriteInfo($"No aggregator instances found in {Location} Region.");
5252
}
53-
return 0;
53+
return ExitCodes.Success;
5454
}
5555

5656
private async Task<int> ListInResourceGroupAsync(CommandContext context, AggregatorInstances instances, CancellationToken cancellationToken)
@@ -68,7 +68,7 @@ private async Task<int> ListInResourceGroupAsync(CommandContext context, Aggrega
6868
context.Logger.WriteInfo($"No aggregator instances found in {ResourceGroup} Resource Group.");
6969
}
7070

71-
return 0;
71+
return ExitCodes.Success;
7272
}
7373

7474
private static async Task<int> ListAllAsync(CommandContext context, AggregatorInstances instances, CancellationToken cancellationToken)
@@ -80,13 +80,15 @@ private static async Task<int> ListAllAsync(CommandContext context, AggregatorIn
8080
context.Logger.WriteOutput(dataObject);
8181
any = true;
8282
}
83-
8483
if (!any)
8584
{
8685
context.Logger.WriteInfo("No aggregator instances found.");
86+
return ExitCodes.NotFound;
87+
}
88+
else
89+
{
90+
return ExitCodes.Success;
8791
}
88-
89-
return 0;
9092
}
9193
}
9294
}

src/aggregator-cli/Instances/StreamLogsCommand.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ internal override async Task<int> RunAsync(CancellationToken cancellationToken)
2424
var instance = context.Naming.Instance(Instance, ResourceGroup);
2525
var instances = new AggregatorInstances(context.Azure, context.Logger, context.Naming);
2626
bool ok = await instances.StreamLogsAsync(instance, cancellationToken);
27-
return ok ? 0 : 1;
27+
return ok ? ExitCodes.Success : ExitCodes.Failure;
2828
}
2929
}
3030
}

src/aggregator-cli/Instances/UninstallInstanceCommand.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ internal override async Task<int> RunAsync(CancellationToken cancellationToken)
3939

4040
var instances = new AggregatorInstances(context.Azure, context.Logger, context.Naming);
4141
var ok = await instances.RemoveAsync(instance, Location);
42-
return ok ? 0 : 1;
42+
return ok ? ExitCodes.Success : ExitCodes.Failure;
4343
}
4444
}
4545
}

src/aggregator-cli/Instances/UpdateInstance.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ internal override async Task<int> RunAsync(CancellationToken cancellationToken)
3232
var instance = context.Naming.Instance(Instance, ResourceGroup);
3333

3434
bool ok = await instances.UpdateAsync(instance, RequiredVersion, SourceUrl, cancellationToken);
35-
return ok ? 0 : 1;
35+
return ok ? ExitCodes.Success : ExitCodes.Failure;
3636
}
3737
}
3838
}

src/aggregator-cli/Logon/LogonAzureCommand.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ internal override async Task<int> RunAsync(CancellationToken cancellationToken)
3636
if (azure == null)
3737
{
3838
context.Logger.WriteError("Invalid azure credentials");
39-
return 2;
39+
return ExitCodes.InvalidArguments;
4040
}
4141
// FIX #60: call some read API to validate parameters
4242
try
@@ -48,9 +48,9 @@ internal override async Task<int> RunAsync(CancellationToken cancellationToken)
4848
int nl = ex.Message.IndexOf(Environment.NewLine);
4949
string m = nl != -1 ? ex.Message.Remove(nl) : ex.Message;
5050
context.Logger.WriteError("Invalid azure credentials: " + m);
51-
return 2;
51+
return ExitCodes.InvalidArguments;
5252
}
53-
return 0;
53+
return ExitCodes.Success;
5454
}
5555
}
5656
}

src/aggregator-cli/Logon/LogonDevOpsCommand.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ internal override async Task<int> RunAsync(CancellationToken cancellationToken)
3636
if (devops == null)
3737
{
3838
context.Logger.WriteError("Invalid Azure DevOps credentials");
39-
return 2;
39+
return ExitCodes.InvalidArguments;
4040
}
4141

42-
return 0;
42+
return ExitCodes.Success;
4343
}
4444
}
4545
}

src/aggregator-cli/Mappings/AggregatorMappings.cs

+14-5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212

1313
namespace aggregator.cli
1414
{
15+
internal enum RemoveOutcome
16+
{
17+
Succeeded = 0,
18+
NotFound = 2,
19+
Failed = 1
20+
}
21+
1522
internal class AggregatorMappings
1623
{
1724
private readonly VssConnection devops;
@@ -157,17 +164,17 @@ internal async Task<Guid> AddAsync(string projectName, string @event, EventFilte
157164
return newSubscription.Id;
158165
}
159166

160-
internal async Task<bool> RemoveInstanceAsync(InstanceName instance)
167+
internal async Task<RemoveOutcome> RemoveInstanceAsync(InstanceName instance)
161168
{
162169
return await RemoveRuleEventAsync("*", instance, "*", "*");
163170
}
164171

165-
internal async Task<bool> RemoveRuleAsync(InstanceName instance, string rule)
172+
internal async Task<RemoveOutcome> RemoveRuleAsync(InstanceName instance, string rule)
166173
{
167174
return await RemoveRuleEventAsync("*", instance, "*", rule);
168175
}
169176

170-
internal async Task<bool> RemoveRuleEventAsync(string @event, InstanceName instance, string projectName, string rule)
177+
internal async Task<RemoveOutcome> RemoveRuleEventAsync(string @event, InstanceName instance, string projectName, string rule)
171178
{
172179
logger.WriteInfo($"Querying the Azure DevOps subscriptions for rule(s) {instance.PlainName}/{rule}");
173180
var serviceHooksClient = devops.GetClient<ServiceHooksPublisherHttpClient>();
@@ -200,14 +207,16 @@ internal async Task<bool> RemoveRuleEventAsync(string @event, InstanceName insta
200207
.StartsWith(invocationUrl, StringComparison.OrdinalIgnoreCase));
201208
}
202209

210+
uint count = 0;
203211
foreach (var ruleSub in ruleSubs)
204212
{
205213
logger.WriteVerbose($"Deleting subscription {ruleSub.EventDescription} {ruleSub.EventType}...");
206214
await serviceHooksClient.DeleteSubscriptionAsync(ruleSub.Id);
207215
logger.WriteInfo($"Subscription {ruleSub.EventDescription} {ruleSub.EventType} deleted.");
216+
count++;
208217
}
209218

210-
return true;
219+
return count > 0 ? RemoveOutcome.Succeeded : RemoveOutcome.NotFound;
211220
}
212221
}
213222

@@ -261,4 +270,4 @@ public static IEnumerable<InputFilterCondition> ToFilterConditions(this EventFil
261270
});
262271
}
263272
}
264-
}
273+
}

src/aggregator-cli/Mappings/ListMappingsCommand.cs

+6-2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ internal override async Task<int> RunAsync(CancellationToken cancellationToken)
2929
&& string.IsNullOrEmpty(Project))
3030
{
3131
context.Logger.WriteError("Specify at least one filtering parameter.");
32-
return 2;
32+
return ExitCodes.InvalidArguments;
3333
}
3434
var instance = string.IsNullOrEmpty(Instance) ? null : context.Naming.Instance(Instance, ResourceGroup);
3535
// HACK we pass null as the next calls do not use the Azure connection
@@ -44,8 +44,12 @@ internal override async Task<int> RunAsync(CancellationToken cancellationToken)
4444
if (!any)
4545
{
4646
context.Logger.WriteInfo("No rule mappings found.");
47+
return ExitCodes.NotFound;
48+
}
49+
else
50+
{
51+
return ExitCodes.Success;
4752
}
48-
return 0;
4953
}
5054
}
5155
}

src/aggregator-cli/Mappings/MapRuleCommand.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ internal override async Task<int> RunAsync(CancellationToken cancellationToken)
4949
if (!ok)
5050
{
5151
context.Logger.WriteError($"Invalid event type.");
52-
return 2;
52+
return ExitCodes.InvalidArguments;
5353
}
5454

5555
var filters = new EventFilters
@@ -62,7 +62,7 @@ internal override async Task<int> RunAsync(CancellationToken cancellationToken)
6262

6363
var instance = context.Naming.Instance(Instance, ResourceGroup);
6464
var id = await mappings.AddAsync(Project, Event, filters, instance, Rule, ImpersonateExecution, cancellationToken);
65-
return id.Equals(Guid.Empty) ? 1 : 0;
65+
return id.Equals(Guid.Empty) ? ExitCodes.Failure : ExitCodes.Success;
6666
}
6767
}
6868
}

src/aggregator-cli/Mappings/UnmapRuleCommand.cs

+15-4
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,27 @@ internal override async Task<int> RunAsync(CancellationToken cancellationToken)
3131
.WithAzureLogon()
3232
.WithDevOpsLogon()
3333
.BuildAsync(cancellationToken);
34-
bool ok = DevOpsEvents.IsValidEvent(Event);
34+
bool ok = DevOpsEvents.IsValidEvent(Event) || Event == "*";
3535
if (!ok)
3636
{
3737
context.Logger.WriteError($"Invalid event type.");
38-
return 2;
38+
return ExitCodes.InvalidArguments;
3939
}
4040
var instance = context.Naming.Instance(Instance, ResourceGroup);
4141
var mappings = new AggregatorMappings(context.Devops, context.Azure, context.Logger, context.Naming);
42-
ok = await mappings.RemoveRuleEventAsync(Event, instance, Project, Rule);
43-
return ok ? 0 : 1;
42+
var outcome = await mappings.RemoveRuleEventAsync(Event, instance, Project, Rule);
43+
switch (outcome)
44+
{
45+
case RemoveOutcome.Succeeded:
46+
return ExitCodes.Success;
47+
case RemoveOutcome.NotFound:
48+
context.Logger.WriteWarning($"No mapping(s) found for rule(s) {instance.PlainName}/{Rule}");
49+
return ExitCodes.NotFound;
50+
case RemoveOutcome.Failed:
51+
return ExitCodes.Failure;
52+
default:
53+
return ExitCodes.Unexpected;
54+
}
4455
}
4556
}
4657
}

src/aggregator-cli/Program.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ void cancelEventHandler(object sender, ConsoleCancelEventArgs e)
7070
typeof(ListMappingsCommand), typeof(MapRuleCommand), typeof(UnmapRuleCommand)
7171
};
7272
var parserResult = parser.ParseArguments(args, types);
73-
int rc = -1;
73+
int rc = ExitCodes.Unexpected;
7474
var cancellationToken = cancellationTokenSource.Token;
7575
parserResult
7676
.WithParsed<CreateTestCommand>(cmd => rc = cmd.Run(cancellationToken))
@@ -96,7 +96,7 @@ void cancelEventHandler(object sender, ConsoleCancelEventArgs e)
9696
{
9797
var helpText = HelpText.AutoBuild(parserResult);
9898
Console.Error.Write(helpText);
99-
rc = 1;
99+
rc = ExitCodes.InvalidArguments;
100100
});
101101

102102
Console.ForegroundColor = save;

src/aggregator-cli/Rules/AddRuleCommand.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ internal override async Task<int> RunAsync(CancellationToken cancellationToken)
3030
var instance = context.Naming.Instance(Instance, ResourceGroup);
3131
var rules = new AggregatorRules(context.Azure, context.Logger);
3232
bool ok = await rules.AddAsync(instance, Name, File, cancellationToken);
33-
return ok ? 0 : 1;
33+
return ok ? ExitCodes.Success : ExitCodes.Failure;
3434
}
3535
}
3636
}

src/aggregator-cli/Rules/ConfigureRuleCommand.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ internal override async Task<int> RunAsync(CancellationToken cancellationToken)
4242
var impersonate = GetEnableStatus(DisableImpersonateExecution, EnableImpersonateExecution);
4343

4444
var ok = await rules.ConfigureAsync(instance, Name, disable, impersonate, cancellationToken);
45-
return ok ? 0 : 1;
45+
return ok ? ExitCodes.Success : ExitCodes.Failure;
4646
}
4747

4848

src/aggregator-cli/Rules/InvokeRuleCommand.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,14 @@ internal override async Task<int> RunAsync(CancellationToken cancellationToken)
5858
if (Local)
5959
{
6060
bool ok = await rules.InvokeLocalAsync(Project, Event, WorkItemId, Source, DryRun, SaveMode, ImpersonateExecution, cancellationToken);
61-
return ok ? 0 : 1;
61+
return ok ? ExitCodes.Success : ExitCodes.Failure;
6262
}
6363
else
6464
{
6565
var instance = context.Naming.Instance(Instance, ResourceGroup);
6666
context.Logger.WriteWarning("Untested feature!");
6767
bool ok = await rules.InvokeRemoteAsync(Account, Project, Event, WorkItemId, instance, Name, DryRun, SaveMode, ImpersonateExecution, cancellationToken);
68-
return ok ? 0 : 1;
68+
return ok ? ExitCodes.Success : ExitCodes.Failure;
6969
}
7070
}
7171
}

src/aggregator-cli/Rules/ListRulesCommand.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,12 @@ internal override async Task<int> RunAsync(CancellationToken cancellationToken)
3434
if (!any)
3535
{
3636
context.Logger.WriteInfo($"No rules found in aggregator instance {instance.PlainName}.");
37+
return ExitCodes.NotFound;
38+
}
39+
else
40+
{
41+
return ExitCodes.Success;
3742
}
38-
39-
return 0;
4043
}
4144
}
4245
}

0 commit comments

Comments
 (0)