Skip to content

Commit

Permalink
Use Generic Host and logging
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielweyer committed Oct 26, 2024
1 parent a02e31f commit 6e3ef17
Show file tree
Hide file tree
Showing 19 changed files with 367 additions and 146 deletions.
4 changes: 4 additions & 0 deletions coster/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,10 @@ dotnet_diagnostic.MA0004.severity = none
# This is a personal preference
dotnet_diagnostic.MA0007.severity = none

# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1848
# This is not high-performance code (quite the contrary)
dotnet_diagnostic.CA1848.severity = none

# CSharp code style settings:
[*.cs]
# Newline settings
Expand Down
2 changes: 2 additions & 0 deletions coster/AzureVmCoster.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/Environment/Filtering/ExcludeCoverageFilters/=AzureVmCosterTests_003B_002A_003B_002A_003B_002A/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/Filtering/ExcludeCoverageFilters/=_005Fbuild_003B_002A_003B_002A_003B_002A/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Coster/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
1 change: 1 addition & 0 deletions coster/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageVersion Include="xunit.analyzers" Version="1.16.0" />
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<GlobalPackageReference Include="Meziantou.Analyzer" Version="2.0.176" />
<GlobalPackageReference Include="AsyncFixer" Version="1.6.0" />
<GlobalPackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.11.20" />
Expand Down
1 change: 1 addition & 0 deletions coster/src/AzureVmCoster/AzureVmCoster.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

<ItemGroup>
<PackageReference Include="CsvHelper" />
<PackageReference Include="Microsoft.Extensions.Hosting" />
</ItemGroup>

<ItemGroup>
Expand Down
11 changes: 11 additions & 0 deletions coster/src/AzureVmCoster/Models/PriceDirectory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace AzureVmCoster.Models;

internal class PriceDirectory
{
public string Directory { get; }

public PriceDirectory(string priceDirectory)
{
Directory = priceDirectory;
}
}
53 changes: 29 additions & 24 deletions coster/src/AzureVmCoster/Program.cs
Original file line number Diff line number Diff line change
@@ -1,54 +1,59 @@
using AzureVmCoster.Services;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace AzureVmCoster;

public static class Program
#pragma warning disable CA1052 // Used as a parameter type
public class Program
#pragma warning restore CA1052
{
private const string PricingDirectory = @"Pricing\";

public static async Task<int> Main(string[] args)
{
var builder = Host.CreateDefaultBuilder(args);
builder.ConfigureServices(s => s
.AddSingleton(new PriceDirectory(@"Pricing\"))
.AddSingleton<Pricer>()
.AddSingleton<ArgumentReader>()
.AddSingleton<VmPricingParser>()
.AddSingleton<PricedVmWriter>()
.AddSingleton<PriceService>());
var host = builder.Build();

var logger = host.Services.GetRequiredService<ILogger<Program>>();
var priceService = host.Services.GetRequiredService<PriceService>();
var argumentReader = host.Services.GetRequiredService<ArgumentReader>();

try
{
ArgumentNullException.ThrowIfNull(args);

var (inputFilePath, configurationFilePath, culture) = ParseConfiguration(args);

var inputFile = InputFileValidator.Validate(inputFilePath);
var inputVms = InputVmParser.Parse(inputFile, culture);

var pricer = new Pricer(PricingDirectory);
pricer.EnsurePricingExists(inputVms);

var configuration = await CosterConfiguration.FromPathAsync(configurationFilePath);

var pricings = await new VmPricingParser(PricingDirectory).ParseAsync();
pricings = Pricer.FilterPricing(pricings, configuration.ExcludedVms);
var (inputFilePath, configurationFilePath, culture) = ParseConfiguration(args, argumentReader);

var pricedVms = Pricer.Price(inputVms, pricings);
PricedVmWriter.Write(inputFile.Name, pricedVms, culture);
await priceService.PriceAsync(inputFilePath, configurationFilePath, culture);

return 0;
}
#pragma warning disable CA1031 // This is a catch-all so that we can log the exception
catch (Exception e)
#pragma warning restore CA1031
{
Console.WriteLine(e);
logger.LogError(e, "Failed to cost VMs");
return -1;
}
}

private static (string? inputFilePath, string? configurationFilePath, CultureInfo culture) ParseConfiguration(string[] args)
private static (string? inputVmFilePath, string? configurationFilePath, CultureInfo culture) ParseConfiguration(string[] args, ArgumentReader argumentReader)
{
string? inputFilePath = null;
string? inputVmFilePath = null;
string? configurationFilePath = null;
var culture = Thread.CurrentThread.CurrentCulture;

#if DEBUG
Console.Write("Input file path: ");
inputFilePath = Console.ReadLine();
ArgumentReader.StripSurroundingDoubleQuotes(ref inputFilePath);
inputVmFilePath = Console.ReadLine();
ArgumentReader.StripSurroundingDoubleQuotes(ref inputVmFilePath);

Console.Write("Configuration file path (leave blank if not used): ");
configurationFilePath = Console.ReadLine();
Expand All @@ -62,9 +67,9 @@ private static (string? inputFilePath, string? configurationFilePath, CultureInf
culture = new CultureInfo(cultureInput);
}
#else
(inputFilePath, configurationFilePath, culture) = ArgumentReader.Read(args);
(inputVmFilePath, configurationFilePath, culture) = argumentReader.Read(args);
#endif

return (inputFilePath, configurationFilePath, culture);
return (inputVmFilePath, configurationFilePath, culture);
}
}
23 changes: 16 additions & 7 deletions coster/src/AzureVmCoster/Services/ArgumentReader.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
using Microsoft.Extensions.Logging;

namespace AzureVmCoster.Services;

internal static class ArgumentReader
internal class ArgumentReader
{
public static (string? inputFilePath, string? configurationFilePath, CultureInfo culture) Read(string[] args)
private readonly ILogger<ArgumentReader> _logger;

public ArgumentReader(ILogger<ArgumentReader> logger)
{
_logger = logger;
}

public (string? inputVmFilePath, string? configurationFilePath, CultureInfo culture) Read(string[] args)
{
string? inputFilePath = null;
string? inputVmFilePath = null;
string? configurationFilePath = null;
var culture = Thread.CurrentThread.CurrentCulture;

Expand All @@ -18,21 +27,21 @@ public static (string? inputFilePath, string? configurationFilePath, CultureInfo
break;
case "-i":
case "--input":
inputFilePath = args[offset + 1];
StripSurroundingDoubleQuotes(ref inputFilePath);
inputVmFilePath = args[offset + 1];
StripSurroundingDoubleQuotes(ref inputVmFilePath);
break;
case "-c":
case "--configuration":
configurationFilePath = args[offset + 1];
StripSurroundingDoubleQuotes(ref configurationFilePath);
break;
default:
Console.WriteLine($"'{args[offset]}' is not a known switch, supported values are: '-l', '--culture', '-i', '--input', '-c', '--configuration'");
_logger.LogWarning("'{UnsupportedArgument}' is not a known switch, supported values are: '-l', '--culture', '-i', '--input', '-c', '--configuration'", args[offset]);
break;
}
}

return (inputFilePath, configurationFilePath, culture);
return (inputVmFilePath, configurationFilePath, culture);
}

/// <summary>
Expand Down
28 changes: 28 additions & 0 deletions coster/src/AzureVmCoster/Services/PriceService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace AzureVmCoster.Services;

internal class PriceService
{
private readonly Pricer _pricer;
private readonly VmPricingParser _vmPricingParser;
private readonly PricedVmWriter _pricedVmWriter;

public PriceService(Pricer pricer, VmPricingParser vmPricingParser, PricedVmWriter pricedVmWriter)
{
_pricer = pricer;
_vmPricingParser = vmPricingParser;
_pricedVmWriter = pricedVmWriter;
}

public async Task PriceAsync(string? inputFilePath, string? configurationFilePath, CultureInfo culture)
{
var inputFile = InputFileValidator.Validate(inputFilePath);
var inputVms = InputVmParser.Parse(inputFile, culture);

var vmPrices = await _vmPricingParser.ParseAsync();

var configuration = await CosterConfiguration.FromPathAsync(configurationFilePath);

var pricedVms = _pricer.Price(inputVms, vmPrices, configuration);
_pricedVmWriter.Write(inputFile.Name, pricedVms, culture);
}
}
18 changes: 15 additions & 3 deletions coster/src/AzureVmCoster/Services/PricedVmWriter.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
using AzureVmCoster.Models.Csv;
using CsvHelper;
using Microsoft.Extensions.Logging;

namespace AzureVmCoster.Services;

internal static class PricedVmWriter
internal class PricedVmWriter
{
public static void Write(string filename, List<PricedVm> pricedVms, CultureInfo culture)
private readonly ILogger<PricedVmWriter> _logger;

public PricedVmWriter(ILogger<PricedVmWriter> logger)
{
_logger = logger;
}

public void Write(string filename, IList<PricedVm> pricedVms, CultureInfo culture)
{
var csvConfiguration = new CsvConfiguration(culture)
{
Delimiter = ","
};

using var writer = new StreamWriter($@"Out\{filename}");
var fileInfo = new FileInfo($@"Out\{filename}");

using var writer = new StreamWriter(fileInfo.FullName);
using var csv = new CsvWriter(writer, csvConfiguration);
csv.Context.RegisterClassMap<PricedVmMap>();
csv.WriteRecords(pricedVms);

_logger.LogInformation("Wrote priced VM to '{OutputFile}'", fileInfo);
}
}
Loading

0 comments on commit 6e3ef17

Please sign in to comment.