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

Provide validation rules for resource discovery agent #1249

Merged
merged 12 commits into from
Aug 31, 2020
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ steps:
displayName: 'Determine if a network is required'
- script: |
echo Mounting volumes: ${{ parameters.volumes }}
docker run -d -p ${{ parameters.containerPort }}:99 --name ${{ parameters.containerName }} $(networkArgument) --env DISCOVERY_APPID=${{ parameters.activeDirectoryAppId }} --env DISCOVERY_APPSECRET=${{ parameters.activeDirectoryAppSecret }} --volume ${{ parameters.volumes }} ${{ parameters.imageName }}
docker run -d -p ${{ parameters.containerPort }}:99 --name ${{ parameters.containerName }} $(networkArgument) --env PROMITOR_AUTH_APPID=${{ parameters.activeDirectoryAppId }} --env PROMITOR_AUTH_APPKEY=${{ parameters.activeDirectoryAppSecret }} --volume ${{ parameters.volumes }} ${{ parameters.imageName }}
sleep 10
docker logs ${{ parameters.containerName }}
displayName: Run Resource Discovery image as ${{ parameters.containerName }} container on ${{ parameters.os }}
Expand Down
2 changes: 1 addition & 1 deletion docs/deployment/resource-discovery/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Here is an overview of how you can deploy Promitor Resource Discovery on your in

```shell
❯ docker run -d -p 9999:80 --name promitor-agent-resource-discovery \
--env PROMITOR_DISCOVERY_APPID='<azure-ad-app-id>' \
--env PROMITOR_AUTH_APPID='<azure-ad-app-id>' \
--env-file C:/Promitor/promitor-discovery-auth.creds \
--volume C:/Promitor/resource-discovery-declaration.yaml:/config/resource-discovery-declaration.yaml \
--volume C:/Promitor/resource-discovery-runtime.yaml:/config/runtime.yaml \
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
using Microsoft.Extensions.Configuration;
using GuardNet;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Promitor.Agents.Core.Validation;
using Promitor.Agents.Core.Validation.Interfaces;
using Promitor.Agents.Core.Validation.Steps;
using Promitor.Core;

namespace Promitor.Agents.Scraper.Validation.Steps
namespace Promitor.Agents.Core.Validation.Steps
{
public class AzureAuthenticationValidationStep : ValidationStep, IValidationStep
{
private readonly IConfiguration _configuration;

public string ComponentName { get; } = "Azure Authentication";

public AzureAuthenticationValidationStep(IConfiguration configuration, ILogger<AzureAuthenticationValidationStep> logger) : base(logger)
{
Guard.NotNull(configuration, nameof(configuration));

_configuration = configuration;
}

public string ComponentName { get; } = "Azure Authentication";

public ValidationResult Run()
{
var applicationId = _configuration.GetValue<string>(EnvironmentVariables.Authentication.ApplicationId);
Expand Down
5 changes: 5 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
@@ -1,12 +1,17 @@
using Microsoft.Extensions.Configuration;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Promitor.Agents.Core.Configuration;
using Promitor.Agents.Core.Configuration.Server;
using Promitor.Agents.Core.Configuration.Telemetry;
using Promitor.Agents.Core.Configuration.Telemetry.Sinks;
using Promitor.Agents.Core.Validation;
using Promitor.Agents.Core.Validation.Interfaces;
using Promitor.Agents.Core.Validation.Steps;
using Promitor.Agents.ResourceDiscovery.Configuration;
using Promitor.Agents.ResourceDiscovery.Graph;
using Promitor.Agents.ResourceDiscovery.Repositories;
using Promitor.Agents.ResourceDiscovery.Validation.Steps;

// ReSharper disable once CheckNamespace
namespace Promitor.Agents.Scraper.Extensions
Expand Down Expand Up @@ -34,10 +39,25 @@ public static IServiceCollection AddRuntimeConfiguration(this IServiceCollection
public static IServiceCollection AddAzureResourceGraph(this IServiceCollection services, IConfiguration configuration)
{
services.Configure<ResourceDeclaration>(configuration);
services.Configure<AzureLandscape>(configuration.GetSection("azureLandscape"));
services.Configure<List<ResourceDiscoveryGroup>>(configuration.GetSection("resourceDiscoveryGroups"));
services.AddTransient<AzureResourceGraph>();
services.AddTransient<ResourceRepository>();

return services;
}

/// <summary>
/// Add validation rules
/// </summary>
public static IServiceCollection AddValidationRules(this IServiceCollection services)
{
services.AddTransient<IValidationStep, AzureLandscapeValidationStep>();
services.AddTransient<IValidationStep, ResourceDiscoveryGroupValidationStep>();
services.AddTransient<IValidationStep, AzureAuthenticationValidationStep>();
services.AddTransient<RuntimeValidator>();

return services;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ public AzureResourceGraph(IOptionsMonitor<ResourceDeclaration> resourceDeclarati
_logger = logger;
_resourceDeclarationMonitor = resourceDeclarationMonitor;

QueryApplicationId = configuration["DISCOVERY_APPID"];
_queryApplicationSecret = configuration["DISCOVERY_APPSECRET"];
QueryApplicationId = configuration[EnvironmentVariables.Authentication.ApplicationId];
_queryApplicationSecret = configuration[EnvironmentVariables.Authentication.ApplicationKey];
}

public async Task<JObject> QueryAsync(string query)
Expand Down
21 changes: 18 additions & 3 deletions src/Promitor.Agents.ResourceDiscovery/Program.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System;
using System.IO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Promitor.Agents.Core;
using Promitor.Agents.Core.Configuration.Server;
using Promitor.Agents.Core.Extensions;
using Promitor.Agents.Core.Validation;
using Promitor.Core;
using Serilog;

Expand All @@ -28,9 +30,22 @@ public static int Main(string[] args)
return (int)ExitStatus.ConfigurationFolderNotSpecified;
}

CreateHostBuilder(args, configurationFolder)
.Build()
.Run();
var host = CreateHostBuilder(args, configurationFolder)
.Build();

using (var scope = host.Services.CreateScope())
{
var validator = scope.ServiceProvider.GetRequiredService<RuntimeValidator>();
if (!validator.Validate())
{
Log.Logger.Fatal("Promitor is not configured correctly. Please fix validation issues and re-run.");
return (int)ExitStatus.ValidationFailed;
}

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

host.Run();

return (int)ExitStatus.Success;
}
Expand Down
1 change: 1 addition & 0 deletions src/Promitor.Agents.ResourceDiscovery/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public void ConfigureServices(IServiceCollection services)
.AddRuntimeConfiguration(Configuration)
.AddAzureResourceGraph(Configuration)
.UseOpenApiSpecifications($"{ApiName} v1", ApiDescription, 1)
.AddValidationRules()
.AddHttpCorrelation()
.AddHealthChecks()
.AddCheck<AzureResourceGraphHealthCheck>("azure-resource-graph", failureStatus: HealthStatus.Unhealthy);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Promitor.Agents.Core.Validation;
using Promitor.Agents.Core.Validation.Interfaces;
using Promitor.Agents.Core.Validation.Steps;
using Promitor.Agents.ResourceDiscovery.Configuration;
using Promitor.Core.Serialization.Enum;

namespace Promitor.Agents.ResourceDiscovery.Validation.Steps
{
public class AzureLandscapeValidationStep : ValidationStep,
IValidationStep
{
private readonly AzureLandscape _azureLandscape;

public AzureLandscapeValidationStep(IOptions<AzureLandscape> azureLandscape, ILogger<AzureLandscapeValidationStep> logger) : base(logger)
{
_azureLandscape = azureLandscape.Value;
}

public string ComponentName { get; } = "Azure Landscape";

public ValidationResult Run()
{
var errorMessages = new List<string>();
if (string.IsNullOrWhiteSpace(_azureLandscape.TenantId))
{
errorMessages.Add("No tenant id was configured");
}

if (_azureLandscape.Cloud == AzureCloud.Unspecified)
{
errorMessages.Add("No Azure cloud was configured");
}

if (_azureLandscape.Subscriptions == null || _azureLandscape.Subscriptions.Any() == false)
{
errorMessages.Add("No subscription id(s) were configured to query");
}
else
{
if (_azureLandscape.Subscriptions.Distinct().Count() != _azureLandscape.Subscriptions.Count)
{
errorMessages.Add("Duplicate subscription ids were configured to query");
}

if (_azureLandscape.Subscriptions.Any(string.IsNullOrWhiteSpace))
{
errorMessages.Add("Empty subscription is configured to query");
}
}

return errorMessages.Any() ? ValidationResult.Failure(ComponentName, errorMessages) : ValidationResult.Successful(ComponentName);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Promitor.Agents.Core.Validation;
using Promitor.Agents.Core.Validation.Interfaces;
using Promitor.Agents.Core.Validation.Steps;
using Promitor.Agents.ResourceDiscovery.Configuration;

namespace Promitor.Agents.ResourceDiscovery.Validation.Steps
{
public class ResourceDiscoveryGroupValidationStep : ValidationStep,
IValidationStep
{
private readonly List<ResourceDiscoveryGroup> _resourceDiscoveryGroups;

public ResourceDiscoveryGroupValidationStep(IOptions<List<ResourceDiscoveryGroup>> resourceDiscoveryGroups, ILogger<ResourceDiscoveryGroupValidationStep> logger) : base(logger)
{
_resourceDiscoveryGroups = resourceDiscoveryGroups.Value;
}

public string ComponentName { get; } = "Resource Discovery Groups";

public ValidationResult Run()
{
var errorMessages = new List<string>();
if (_resourceDiscoveryGroups == null || _resourceDiscoveryGroups.Any() == false)
{
errorMessages.Add("No resource discovery groups were configured");
return ValidationResult.Failure(ComponentName, errorMessages);
}

var uniqueDiscoveryGroups = _resourceDiscoveryGroups.Select(grp=>grp.Name).Distinct().ToList();
if (_resourceDiscoveryGroups.Count != uniqueDiscoveryGroups.Count)
{
foreach (var discoveryGroup in uniqueDiscoveryGroups)
{
var duplicateEntryCount = _resourceDiscoveryGroups.Count(grp => grp.Name.Equals(discoveryGroup, StringComparison.InvariantCultureIgnoreCase));
if (duplicateEntryCount > 1)
{
errorMessages.Add($"Duplicate resource discovery groups were configured with name '{discoveryGroup}'. ({duplicateEntryCount} groups with same name found)");
return ValidationResult.Failure(ComponentName, errorMessages);
}
}
}

return ValidationResult.Successful(ComponentName);
}
}
}
2 changes: 1 addition & 1 deletion 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 @@ -9,6 +9,7 @@
using Promitor.Agents.Core.Configuration.Telemetry.Sinks;
using Promitor.Agents.Core.Validation;
using Promitor.Agents.Core.Validation.Interfaces;
using Promitor.Agents.Core.Validation.Steps;
using Promitor.Agents.Scraper;
using Promitor.Agents.Scraper.Configuration;
using Promitor.Agents.Scraper.Configuration.Sinks;
Expand Down Expand Up @@ -116,7 +117,7 @@ public static IServiceCollection DefineDependencies(this IServiceCollection serv
/// Defines the validation for when Promitor starts up
/// </summary>
/// <param name="services">Collections of services in application</param>
public static IServiceCollection ConfigureValidation(this IServiceCollection services)
public static IServiceCollection AddValidationRules(this IServiceCollection services)
{
services.AddTransient<IValidationStep, ConfigurationPathValidationStep>();
services.AddTransient<IValidationStep, AzureAuthenticationValidationStep>();
Expand Down
2 changes: 1 addition & 1 deletion src/Promitor.Agents.Scraper/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void ConfigureServices(IServiceCollection services)
.AddHttpCorrelation()
.AddAutoMapper(typeof(V1MappingProfile).Assembly)
.DefineDependencies()
.ConfigureValidation()
.AddValidationRules()
.ConfigureYamlConfiguration(Configuration)
.UseOpenApiSpecifications("Promitor - Scraper API v1", openApiDescription, 1);

Expand Down
9 changes: 9 additions & 0 deletions src/Promitor.Tests.Unit/PromitorAssert.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.Azure.Management.ResourceManager.Fluent;
using Promitor.Agents.Core.Validation;
using Xunit;

namespace Promitor.Tests.Unit
Expand All @@ -19,5 +20,13 @@ public static void ContainsSameAzureEnvironmentInfo(AzureEnvironment expectedEnv
Equal(expectedEnvironment.ResourceManagerEndpoint, azureEnvironment.ResourceManagerEndpoint);
Equal(expectedEnvironment.StorageEndpointSuffix, azureEnvironment.StorageEndpointSuffix);
}

/// <summary>
/// Verifies validation was successful
/// </summary>
public static void ValidationIsSuccessful(ValidationResult validationResult)
{
True(validationResult.IsSuccessful, $"Validation was not successful. Reason: {validationResult.Message}");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
using System.ComponentModel;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging.Abstractions;
using Promitor.Agents.Core.Validation.Steps;
using Promitor.Core;
using Promitor.Agents.Scraper.Validation.Steps;
using Xunit;

namespace Promitor.Tests.Unit.Validation.Authentication
Expand Down
Loading