Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Provide better usability in terms of startup and configuration insights #1474

Merged
merged 6 commits into from
Feb 3, 2021
Merged
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
2 changes: 2 additions & 0 deletions config/promitor/scraper/metrics.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ metrics:
- name: promitor_demo_appplan_percentage_cpu
description: "Average percentage of memory usage on an Azure App Plan"
resourceType: AppPlan
labels:
app: promitor
azureMetricConfiguration:
metricName: MemoryPercentage
aggregation:
Expand Down
4 changes: 2 additions & 2 deletions config/promitor/scraper/runtime.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ telemetry:
verbosity: warning
containerLogs:
isEnabled: true
verbosity: trace
defaultVerbosity: trace
verbosity: information
defaultVerbosity: information
azureMonitor:
logging:
informationLevel: Headers
Expand Down
4 changes: 4 additions & 0 deletions docs/thank-you.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ Here is an overview of the NuGet packages that we rely on:
- Swagger tools for documenting API's built on ASP.NET Core
- [Prometheus.Client](https://github.com/PrometheusClientNet/Prometheus.Client)
- .NET client for prometheus.io
- [spectre.console](https://github.com/spectresystems/spectre.console) - A library that makes it easier to create
beautiful console applications.
- [Humanizer](https://github.com/Humanizr/Humanizer) - Humanizer meets all your .NET needs for manipulating and
displaying strings, enums, dates, times, timespans, numbers and quantities
- [YamlDotNet](https://github.com/aaubry/YamlDotNet) - .NET library for YAML
- [Guard.NET](https://github.com/george-pancescu/Guard) - Library that facilitates
runtime checks of code and allows to define preconditions and invariants within
Expand Down
3 changes: 3 additions & 0 deletions src/Promitor.Agents.Core/Promitor.Agents.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="2.1.0" />
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Spectre.Console" Version="0.37.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="5.6.3" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="6.0.1" />
<PackageReference Include="Humanizer" Version="2.8.26" />
<PackageReference Include="Spectre.Console" Version="0.37.0" />
</ItemGroup>

<ItemGroup>
Expand Down
22 changes: 22 additions & 0 deletions src/Promitor.Agents.Core/Usability/AsciiTableGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Spectre.Console;

namespace Promitor.Agents.Core.Usability
{
public class AsciiTableGenerator
{
protected Table CreateAsciiTable(string caption = null)
{
var asciiTable = new Table
{
Border = TableBorder.Rounded
};

if(string.IsNullOrWhiteSpace(caption) == false)
{
asciiTable.Caption(caption);
}

return asciiTable;
}
}
}
30 changes: 25 additions & 5 deletions src/Promitor.Agents.Core/Validation/RuntimeValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Linq;
using Microsoft.Extensions.Logging;
using Promitor.Agents.Core.Validation.Interfaces;
using Spectre.Console;

#pragma warning disable 618
namespace Promitor.Agents.Core.Validation
Expand Down Expand Up @@ -43,29 +44,48 @@ private List<ValidationResult> RunValidationSteps()

var totalValidationSteps = _validationSteps.Count;
var validationResults = new List<ValidationResult>();

// Create a table
var asciiTable = CreateAsciiTable();

for (var currentValidationStep = 1; currentValidationStep <= totalValidationSteps; currentValidationStep++)
{
var validationStep = _validationSteps[currentValidationStep - 1];
var validationResult = RunValidationStep(validationStep, currentValidationStep, totalValidationSteps);
var validationResult = RunValidationStep(validationStep, asciiTable);
validationResults.Add(validationResult);
}

AnsiConsole.Render(asciiTable);

return validationResults;
}

private ValidationResult RunValidationStep(IValidationStep validationStep, int currentStep, int totalSteps)
private static Table CreateAsciiTable()
{
_validationLogger.LogInformation("Start Validation step {currentStep}/{totalSteps}: {validationStepName}", currentStep, totalSteps, validationStep.ComponentName);
var asciiTable = new Table
{
Border = TableBorder.HeavyEdge
};

// Add some columns
asciiTable.AddColumn("Name");
asciiTable.AddColumn("Outcome");
asciiTable.AddColumn("Details");
asciiTable.Caption("Validation");

return asciiTable;
}

private ValidationResult RunValidationStep(IValidationStep validationStep, Table asciiTable)
{
var validationResult = validationStep.Run();
if (validationResult.IsSuccessful)
{
_validationLogger.LogInformation("Validation step {currentStep}/{totalSteps} succeeded", currentStep, totalSteps);
asciiTable.AddRow(validationStep.ComponentName, "Success", "Everything is well-configured.");
}
else
{
_validationLogger.LogWarning("Validation step {currentStep}/{totalSteps} failed. Error(s): {validationMessage}", currentStep, totalSteps, validationResult.Message);
asciiTable.AddRow(validationStep.ComponentName, "Failed", $"Validation failed:\r\n{validationResult.Message}");
}

return validationResult;
Expand Down
10 changes: 10 additions & 0 deletions src/Promitor.Agents.ResourceDiscovery/Docs/Open-Api.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Promitor.Agents.ResourceDiscovery.Graph.Interfaces;
using Promitor.Agents.ResourceDiscovery.Repositories;
using Promitor.Agents.ResourceDiscovery.Repositories.Interfaces;
using Promitor.Agents.ResourceDiscovery.Usability;
using Promitor.Agents.ResourceDiscovery.Validation.Steps;

namespace Promitor.Agents.ResourceDiscovery.Extensions
Expand Down Expand Up @@ -71,5 +72,15 @@ public static IServiceCollection AddValidationRules(this IServiceCollection serv

return services;
}

/// <summary>
/// Add usability
/// </summary>
public static IServiceCollection AddUsability(this IServiceCollection services)
{
services.AddTransient<DiscoveryGroupTableGenerator>();

return services;
}
}
}
9 changes: 9 additions & 0 deletions src/Promitor.Agents.ResourceDiscovery/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Promitor.Agents.Core.Configuration.Server;
using Promitor.Agents.Core.Extensions;
using Promitor.Agents.Core.Validation;
using Promitor.Agents.ResourceDiscovery.Usability;
using Promitor.Core;
using Serilog;

Expand Down Expand Up @@ -43,6 +44,7 @@ public static int Main(string[] args)
}

Log.Logger.Information("Promitor configuration is valid, we are good to go.");
PlotConfiguredDiscoveryGroups(scope);
}

host.Run();
Expand Down Expand Up @@ -93,5 +95,12 @@ private static IConfiguration BuildConfiguration(string[] args, string configura

return configuration;
}

private static void PlotConfiguredDiscoveryGroups(IServiceScope scope)
{
var metricsTableGenerator = scope.ServiceProvider.GetRequiredService<DiscoveryGroupTableGenerator>();
Log.Logger.Information("Here's an overview of what was configured:");
metricsTableGenerator.PlotOverviewInAsciiTable();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
<ItemGroup>
<ProjectReference Include="..\Promitor.Agents.Core\Promitor.Agents.Core.csproj" />
<ProjectReference Include="..\Promitor.Core.Contracts\Promitor.Core.Contracts.csproj" />
<ProjectReference Include="..\Promitor.Core.Scraping\Promitor.Core.Scraping.csproj" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions src/Promitor.Agents.ResourceDiscovery/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public void ConfigureServices(IServiceCollection services)
.AddMemoryCache()
.AddRuntimeConfiguration(Configuration)
.AddAzureResourceGraph(Configuration)
.AddUsability()
.UseOpenApiSpecifications($"{ApiName} v1", ApiDescription, 1)
.AddValidationRules()
.AddHttpCorrelation()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using GuardNet;
using Humanizer;
using Microsoft.Extensions.Options;
using Promitor.Agents.Core.Usability;
using Promitor.Agents.ResourceDiscovery.Configuration;
using Spectre.Console;

namespace Promitor.Agents.ResourceDiscovery.Usability
{
public class DiscoveryGroupTableGenerator : AsciiTableGenerator
{
private readonly IOptionsMonitor<ResourceDeclaration> _resourceDeclarationMonitor;

public DiscoveryGroupTableGenerator(IOptionsMonitor<ResourceDeclaration> resourceDeclarationMonitor)
{
Guard.NotNull(resourceDeclarationMonitor, nameof(resourceDeclarationMonitor));

_resourceDeclarationMonitor = resourceDeclarationMonitor;
}

/// <summary>
/// Plots all configured information into an ASCII table
/// </summary>
public void PlotOverviewInAsciiTable()
{
var resourceDeclaration = _resourceDeclarationMonitor.CurrentValue;
PlotAzureMetadataInAsciiTable(resourceDeclaration.AzureLandscape);
PlotResourceDiscoveryGroupsInAsciiTable(resourceDeclaration.ResourceDiscoveryGroups);
}

private void PlotResourceDiscoveryGroupsInAsciiTable(List<ResourceDiscoveryGroup> resourceDiscoveryGroups)
{
var asciiTable = CreateResourceDiscoveryGroupsAsciiTable();

foreach (var resourceDiscoveryGroup in resourceDiscoveryGroups)
{
var isInclusionCriteriaConfigured = resourceDiscoveryGroup.Criteria.Include != null ? "Yes" : "No";
asciiTable.AddRow(resourceDiscoveryGroup.Name, resourceDiscoveryGroup.Type.Humanize(LetterCasing.Title), isInclusionCriteriaConfigured);
}

AnsiConsole.Render(asciiTable);
}

private void PlotAzureMetadataInAsciiTable(AzureLandscape azureLandscape)
{
var asciiTable = CreateAzureMetadataAsciiTable();

var rawSubscriptions = "- " + string.Join($"{Environment.NewLine} - ", azureLandscape.Subscriptions);

asciiTable.AddRow(azureLandscape.TenantId, azureLandscape.Cloud.Humanize(LetterCasing.Title), rawSubscriptions);

AnsiConsole.Render(asciiTable);
}

private Table CreateResourceDiscoveryGroupsAsciiTable()
{
var asciiTable = CreateAsciiTable("Resource Discovery Groups");

// Add some columns
asciiTable.AddColumn("Name");
asciiTable.AddColumn("Resource Type");
asciiTable.AddColumn("Is Include Criteria Configured?");

return asciiTable;
}

private Table CreateAzureMetadataAsciiTable()
{
var asciiTable = CreateAsciiTable("Azure Landscape");

// Add some columns
asciiTable.AddColumn("Tenant Id");
asciiTable.AddColumn("Azure Cloud");
asciiTable.AddColumn("Subscriptions");

return asciiTable;
}
}
}
11 changes: 11 additions & 0 deletions src/Promitor.Agents.Scraper/Docs/Open-Api.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Promitor.Agents.Scraper.Configuration;
using Promitor.Agents.Scraper.Configuration.Sinks;
using Promitor.Agents.Scraper.Discovery;
using Promitor.Agents.Scraper.Usability;
using Promitor.Agents.Scraper.Validation.Steps;
using Promitor.Agents.Scraper.Validation.Steps.Sinks;
using Promitor.Core;
Expand Down Expand Up @@ -113,6 +114,17 @@ public static IServiceCollection DefineDependencies(this IServiceCollection serv
return services;
}

/// <summary>
/// Adds usability
/// </summary>
/// <param name="services">Collections of services in application</param>
public static IServiceCollection AddUsability(this IServiceCollection services)
{
services.AddTransient<MetricsTableGenerator>();

return services;
}

/// <summary>
/// Defines the validation for when Promitor starts up
/// </summary>
Expand Down
12 changes: 11 additions & 1 deletion src/Promitor.Agents.Scraper/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Promitor.Agents.Core.Configuration.Server;
using Promitor.Agents.Core.Extensions;
using Promitor.Agents.Core.Validation;
using Promitor.Agents.Scraper.Usability;
using Promitor.Core;
using Serilog;

Expand Down Expand Up @@ -43,7 +44,9 @@ public static int Main(string[] args)
return (int)ExitStatus.ValidationFailed;
}

Log.Logger.Information("Promitor configuration is valid, we are good to go.");
Log.Logger.Information("Promitor configuration is valid, we are good to go.");

PlotConfiguredMetrics(scope);
}

host.Run();
Expand Down Expand Up @@ -93,5 +96,12 @@ private static IConfigurationRoot BuildConfiguration(string configurationFolder)

return configuration;
}

private static void PlotConfiguredMetrics(IServiceScope scope)
{
var metricsTableGenerator = scope.ServiceProvider.GetRequiredService<MetricsTableGenerator>();
Log.Logger.Information("Here's an overview of what was configured:");
metricsTableGenerator.PlotOverviewInAsciiTable();
}
}
}
1 change: 1 addition & 0 deletions src/Promitor.Agents.Scraper/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public void ConfigureServices(IServiceCollection services)
services.UseWebApi()
.AddResourceDiscoveryClient(promitorUserAgent)
.AddAtlassianStatuspageClient(promitorUserAgent, Configuration)
.AddUsability()
.AddHttpCorrelation()
.AddAutoMapper(typeof(V1MappingProfile).Assembly)
.DefineDependencies()
Expand Down
Loading