Skip to content

Commit

Permalink
Add Redis cache (#3843)
Browse files Browse the repository at this point in the history
  • Loading branch information
oleksandr-didyk authored Aug 20, 2024
1 parent da0fc35 commit 92cd690
Show file tree
Hide file tree
Showing 11 changed files with 88 additions and 11 deletions.
5 changes: 4 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<PackageVersion Include="Aspire.Hosting.AppHost" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting.Azure.Storage" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting.Azure" Version="$(AspireVersion)" />
<PackageVersion Include="Aspire.Hosting.Redis" Version="$(AspireVersion)" />
<PackageVersion Include="Azure.Core" Version="1.41.0" />
<PackageVersion Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.3.0" />
<PackageVersion Include="Azure.Extensions.AspNetCore.DataProtection.Blobs" Version="1.2.3" />
Expand Down Expand Up @@ -48,6 +49,7 @@
<PackageVersion Include="Microsoft.AspNetCore.Server.HttpSys" Version="2.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.WebHooks.Receivers.GitHub" Version="1.0.0-preview2-final" />
<PackageVersion Include="Microsoft.AspNetCore.WebHooks.Receivers" Version="1.0.0-preview2-final" />
<PackageVersion Include="Microsoft.Azure.StackExchangeRedis" Version="3.1.0" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
<PackageVersion Include="Microsoft.Build.Framework" Version="17.3.2" />
<PackageVersion Include="Microsoft.Build.Tasks.Core" Version="17.3.2" />
Expand Down Expand Up @@ -118,6 +120,7 @@
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.14" />
<PackageVersion Include="Moq" Version="4.18.4" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="NRedisStack" Version="0.12.0" />
<PackageVersion Include="NuGet.Configuration" Version="6.9.1" />
<PackageVersion Include="NuGet.Packaging" Version="6.9.1" />
<PackageVersion Include="NuGet.Protocol" Version="6.9.1" />
Expand Down Expand Up @@ -149,4 +152,4 @@
<PackageVersion Include="Verify.NUnit" Version="19.6" />
<PackageVersion Include="YamlDotNet" Version="15.1.2" />
</ItemGroup>
</Project>
</Project>
32 changes: 32 additions & 0 deletions eng/service-templates/ProductConstructionService/provision.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ param keyVaultName string = 'ProductConstructionInt'
@description('Dev Key Vault name')
param devKeyVaultName string = 'ProductConstructionDev'

@description('Azure Cache for Redis name')
param azureCacheRedisName string = 'prodconstaging'

@description('Log analytics workspace name')
param logAnalyticsName string = 'product-construction-service-workspace-int'

Expand Down Expand Up @@ -681,6 +684,24 @@ resource devKeyVault 'Microsoft.KeyVault/vaults@2022-07-01' = if (aspnetcoreEnvi
}
}

resource redisCache 'Microsoft.Cache/redis@2024-03-01' = {
name: azureCacheRedisName
location: location
properties: {
enableNonSslPort: false
minimumTlsVersion: '1.2'
sku: {
capacity: 0
family: 'C'
name: 'Basic'
}
redisConfiguration: {
'aad-enabled': 'true'
}
disableAccessKeyAuthentication: true
}
}

// allow secret access to the identity used for the aca's
resource secretAccess 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
scope: keyVault // Use when specifying a scope that is different than the deployment scope
Expand Down Expand Up @@ -741,6 +762,17 @@ resource subscriptionTriggererStorageQueueAccess 'Microsoft.Authorization/roleAs
}
}

// allow redis cache read / write access to the service's identity
resource redisCacheBuiltInAccessPolicyAssignment 'Microsoft.Cache/redis/accessPolicyAssignments@2024-03-01' = {
name: guid(subscription().id, resourceGroup().id, 'pcsDataContributor')
parent: redisCache
properties: {
accessPolicyName: 'Data Contributor'
objectId: pcsIdentity.properties.principalId
objectIdAlias: 'PCS Managed Identity'
}
}

// Give the PCS Deployment MI the Contributor role in the containerapp to allow it to deploy
resource deploymentContainerAppContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
scope: containerapp // Use when specifying a scope that is different than the deployment scope
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,18 @@ static PcsStartup()
/// Registers all necessary services for the Product Construction Service
/// </summary>
/// <param name="builder"></param>
/// <param name="initializeService">Run service initialization? Currently this just means cloning the VMR</param>
/// <param name="addKeyVault">Use KeyVault for secrets?</param>
/// <param name="addRedis">Use Redis for caching?</param>
/// <param name="addSwagger">Add Swagger UI?</param>
internal static void ConfigurePcs(
internal static async Task ConfigurePcs(
this WebApplicationBuilder builder,
bool initializeService,
bool addKeyVault,
bool addRedis,
bool addSwagger)
{
bool isDevelopment = builder.Environment.IsDevelopment();
bool initializeService = !isDevelopment;

// Read configuration
string? managedIdentityId = builder.Configuration[ConfigurationKeys.ManagedIdentityId];
string databaseConnectionString = builder.Configuration.GetRequiredValue(ConfigurationKeys.DatabaseConnectionString)
Expand Down Expand Up @@ -159,6 +162,11 @@ internal static void ConfigurePcs(
builder.Services.AddMergePolicies();
builder.Services.Configure<SlaOptions>(builder.Configuration.GetSection(ConfigurationKeys.DependencyFlowSLAs));

if (addRedis)
{
await builder.Services.AddRedis(builder.Configuration, isDevelopment);
}

if (initializeService)
{
builder.AddVmrInitialization();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<PackageReference Include="Swashbuckle.AspNetCore" />
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" />
<PackageReference Include="NRedisStack" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
bool isDevelopment = builder.Environment.IsDevelopment();
bool useSwagger = isDevelopment;

builder.ConfigurePcs(
await builder.ConfigurePcs(
addKeyVault: true,
initializeService: !isDevelopment,
addRedis: true,
addSwagger: useSwagger);

var app = builder.Build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"KeyVaultName": "ProductConstructionInt",
"ConnectionStrings": {
"queues": "https://productconstructionint.queue.core.windows.net"
"queues": "https://productconstructionint.queue.core.windows.net",
"redis": "prodconstaging.redis.cache.windows.net:6380"
},
"ManagedIdentityClientId": "1d43ba8a-c2a6-4fad-b064-6d8c16fc0745",
"VmrUri": "https://github.com/maestro-auth-test/dnceng-vmr",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<PackageReference Include="Aspire.Hosting.Azure" />
<PackageReference Include="Aspire.Hosting.Azure.Storage" />
<PackageReference Include="Aspire.Azure.Storage.Queues" />
<PackageReference Include="Aspire.Hosting.Redis" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@

var builder = DistributedApplication.CreateBuilder(args);

var redisCache = builder.AddRedis("redis");
var queues = builder.AddAzureStorage("storage")
.RunAsEmulator(emulator => emulator.WithImageTag("3.31.0")) // Workaround for https://github.com/dotnet/aspire/issues/5078
.AddQueues("queues");

builder.AddProject<Projects.ProductConstructionService_Api>("productConstructionServiceApi")
.WithReference(queues);
.WithReference(queues)
.WithReference(redisCache);

builder.Build().Run();
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
<PackageReference Include="Microsoft.Extensions.Configuration" />
<PackageReference Include="Microsoft.Extensions.Logging.ApplicationInsights" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
<PackageReference Include="Microsoft.Azure.StackExchangeRedis" />
<PackageReference Include="NRedisStack" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.EntityFrameworkCore;
using StackExchange.Redis;
using Microsoft.Azure.StackExchangeRedis;

namespace ProductConstructionService.Common;

public static class ProductConstructionServiceExtension
{
private const string QueueConnectionString = "QueueConnectionString";
private const string RedisConnectionString = "redis";
private const string ManagedIdentityClientId = "ManagedIdentityClientId";
private const string SqlConnectionStringUserIdPlaceholder = "USER_ID_PLACEHOLDER";
private const string DatabaseConnectionString = "BuildAssetRegistrySqlConnectionString";
Expand Down Expand Up @@ -73,4 +76,28 @@ public static void RegisterBuildAssetRegistry(this IServiceCollection services,
services.AddKustoClientProvider("Kusto");
services.AddSingleton<IInstallationLookup, BuildAssetRegistryInstallationLookup>(); ;
}

public static async Task AddRedis(
this IServiceCollection services,
IConfiguration configuration,
bool isDevelopment)
{
var redisConfig = ConfigurationOptions.Parse(
configuration.GetSection("ConnectionStrings").GetRequiredValue(RedisConnectionString));
var managedIdentityId = configuration[ManagedIdentityClientId];

// Local redis instance should not need authentication
if (!isDevelopment)
{
AzureCacheOptions azureOptions = new();
if (managedIdentityId != "system")
{
azureOptions.ClientId = managedIdentityId;
}

await redisConfig.ConfigureForAzureAsync(azureOptions);
}

services.AddSingleton<IConnectionMultiplexer>(_ => ConnectionMultiplexer.Connect(redisConfig));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace ProductConstructionService.Api.Tests;
public class DependencyRegistrationTests
{
[Test]
public void AreDependenciesRegistered()
public async Task AreDependenciesRegistered()
{
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", Environments.Staging);

Expand All @@ -28,9 +28,9 @@ public void AreDependenciesRegistered()
builder.Configuration["DataProtection:DataProtectionKeyUri"] = "https://keyvault.azure.com/secret/key";
builder.Configuration["DataProtection:KeyBlobUri"] = "https://blobs.azure.com/secret/key";

builder.ConfigurePcs(
initializeService: true,
await builder.ConfigurePcs(
addKeyVault: false,
addRedis: false,
addSwagger: true);

DependencyInjectionValidation.IsDependencyResolutionCoherent(
Expand Down

0 comments on commit 92cd690

Please sign in to comment.