diff --git a/src/lib/azure/common/construct.ts b/src/lib/azure/common/construct.ts index 4597fbfe..6796f95d 100644 --- a/src/lib/azure/common/construct.ts +++ b/src/lib/azure/common/construct.ts @@ -20,6 +20,7 @@ import { AzureDnsManager, AzureLogAnalyticsWorkspaceManager, AzureMonitorManager, + AzureRedisManager, } from '../services' import { CommonAzureStackProps } from './types' import { AzureRemoteBackend } from './constants' @@ -40,6 +41,7 @@ export class CommonAzureConstruct extends TerraformStack { keyVaultManager: AzureKeyVaultManager logAnalyticsWorkspaceManager: AzureLogAnalyticsWorkspaceManager monitorManager: AzureMonitorManager + redisManager: AzureRedisManager resourceGroupManager: AzureResourceGroupManager resourceNameFormatter: AzureResourceNameFormatter servicebusManager: AzureServicebusManager @@ -62,6 +64,7 @@ export class CommonAzureConstruct extends TerraformStack { this.keyVaultManager = new AzureKeyVaultManager() this.logAnalyticsWorkspaceManager = new AzureLogAnalyticsWorkspaceManager() this.monitorManager = new AzureMonitorManager() + this.redisManager = new AzureRedisManager() this.resourceGroupManager = new AzureResourceGroupManager() this.resourceNameFormatter = new AzureResourceNameFormatter(this, `${id}-rnf`, props) this.servicebusManager = new AzureServicebusManager() diff --git a/src/lib/azure/services/api-management/main.ts b/src/lib/azure/services/api-management/main.ts index 463a6ad7..ae7fc6b9 100644 --- a/src/lib/azure/services/api-management/main.ts +++ b/src/lib/azure/services/api-management/main.ts @@ -12,6 +12,8 @@ import { ApiManagementLogger, ApiManagementLoggerApplicationInsights, } from '@cdktf/provider-azurerm/lib/api-management-logger' +import { ApiManagementRedisCache } from '@cdktf/provider-azurerm/lib/api-management-redis-cache' +import { RedisCache } from '@cdktf/provider-azurerm/lib/redis-cache' import { Resource } from '../../.gen/providers/azapi/resource' import { CommonAzureConstruct } from '../../common' import { createAzureTfOutput } from '../../utils' @@ -20,6 +22,7 @@ import { ApiManagementBackendProps, ApiManagementApiProps, ApiManagementCustomDomainProps, + ApiManagementRedisCacheProps, } from './types' import _ from 'lodash' @@ -52,7 +55,8 @@ export class AzureApiManagementManager { id: string, scope: CommonAzureConstruct, props: ApiManagementProps, - applicationInsightsKey?: ApiManagementLoggerApplicationInsights['instrumentationKey'] + applicationInsightsKey?: ApiManagementLoggerApplicationInsights['instrumentationKey'], + externalRedisCache?: RedisCache ) { if (!props) throw `Props undefined for ${id}` @@ -84,6 +88,16 @@ export class AzureApiManagementManager { }) } + if (externalRedisCache) { + new ApiManagementRedisCache(scope, `${id}-am-redis-cache`, { + name: scope.resourceNameFormatter.format(props.name, scope.props.resourceNameOptions?.apiManagementRedisCache), + apiManagementId: apiManagement.id, + connectionString: externalRedisCache.primaryConnectionString, + cacheLocation: externalRedisCache.location, + redisCacheId: externalRedisCache.id, + }) + } + createAzureTfOutput(`${id}-apiManagementName`, scope, apiManagement.name) createAzureTfOutput(`${id}-apiManagementFriendlyUniqueId`, scope, apiManagement.friendlyUniqueId) createAzureTfOutput(`${id}-apiManagementId`, scope, apiManagement.id) diff --git a/src/lib/azure/services/api-management/types.ts b/src/lib/azure/services/api-management/types.ts index c57d6991..1f9a3f21 100644 --- a/src/lib/azure/services/api-management/types.ts +++ b/src/lib/azure/services/api-management/types.ts @@ -3,6 +3,7 @@ import { ApiManagementBackendConfig } from '@cdktf/provider-azurerm/lib/api-mana import { ApiManagementCustomDomainConfig } from '@cdktf/provider-azurerm/lib/api-management-custom-domain' import { ApiManagementApiConfig } from '@cdktf/provider-azurerm/lib/api-management-api' import { ApiManagementApiOperationConfig } from '@cdktf/provider-azurerm/lib/api-management-api-operation' +import { ApiManagementRedisCacheConfig } from '@cdktf/provider-azurerm/lib/api-management-redis-cache' export interface ApiManagementProps extends ApiManagementConfig {} @@ -35,3 +36,5 @@ export interface ApiManagementApiRateLimit { calls: number renewalPeriodInSecs: number } + +export interface ApiManagementRedisCacheProps extends ApiManagementRedisCacheConfig {} diff --git a/src/lib/azure/services/index.ts b/src/lib/azure/services/index.ts index a4305542..d8adf072 100644 --- a/src/lib/azure/services/index.ts +++ b/src/lib/azure/services/index.ts @@ -9,6 +9,7 @@ export * from './function' export * from './key-vault' export * from './log-analytics-workspace' export * from './monitor' +export * from './redis' export * from './resource-group' export * from './servicebus' export * from './storage' diff --git a/src/lib/azure/services/redis/index.ts b/src/lib/azure/services/redis/index.ts new file mode 100644 index 00000000..77385458 --- /dev/null +++ b/src/lib/azure/services/redis/index.ts @@ -0,0 +1,2 @@ +export * from './main' +export * from './types' diff --git a/src/lib/azure/services/redis/main.ts b/src/lib/azure/services/redis/main.ts new file mode 100644 index 00000000..1e6948a8 --- /dev/null +++ b/src/lib/azure/services/redis/main.ts @@ -0,0 +1,59 @@ +import { DataAzurermResourceGroup } from '@cdktf/provider-azurerm/lib/data-azurerm-resource-group' +import { RedisCache } from '@cdktf/provider-azurerm/lib/redis-cache' +import { CommonAzureConstruct } from '../../common' +import { createAzureTfOutput } from '../../utils' +import { RedisCacheProps } from './types' + +/** + * @classdesc Provides operations on Azure Redis + * - A new instance of this class is injected into {@link CommonAzureConstruct} constructor. + * - If a custom construct extends {@link CommonAzureConstruct}, an instance is available within the context. + * @example + * ``` + * import { CommonAzureConstruct, CommonAzureStackProps } from '@gradientedge/cdk-utils' + * + * class CustomConstruct extends CommonAzureConstruct { + * constructor(parent: Construct, id: string, props: CommonAzureStackProps) { + * super(parent, id, props) + * this.props = props + * this.redisManager.createRedis('MyRedisCache', this, props) + * } + * } + * ``` + */ +export class AzureRedisManager { + /** + * @summary Method to create a new redis cache + * @param id scoped id of the resource + * @param scope scope in which this resource is defined + * @param props redis cache properties + * @see [CDKTF Redis Cache Module]{@link https://github.com/cdktf/cdktf-provider-azurerm/blob/main/docs/redisCache.typescript.md} + */ + public createRedisCache(id: string, scope: CommonAzureConstruct, props: RedisCacheProps) { + if (!props) throw `Props undefined for ${id}` + + const resourceGroup = new DataAzurermResourceGroup(scope, `${id}-rc-rg`, { + name: scope.props.resourceGroupName + ? scope.resourceNameFormatter.format(scope.props.resourceGroupName) + : `${props.resourceGroupName}`, + }) + + if (!resourceGroup) throw `Resource group undefined for ${id}` + + const redisCache = new RedisCache(scope, `${id}-rc`, { + ...props, + name: scope.resourceNameFormatter.format(props.name, scope.props.resourceNameOptions?.redisCache), + location: resourceGroup.location, + resourceGroupName: resourceGroup.name, + tags: props.tags ?? { + environment: scope.props.stage, + }, + }) + + createAzureTfOutput(`${id}-redisCacheName`, scope, redisCache.name) + createAzureTfOutput(`${id}-redisCacheFriendlyUniqueId`, scope, redisCache.friendlyUniqueId) + createAzureTfOutput(`${id}-redisCacheId`, scope, redisCache.id) + + return redisCache + } +} diff --git a/src/lib/azure/services/redis/types.ts b/src/lib/azure/services/redis/types.ts new file mode 100644 index 00000000..efb63ce3 --- /dev/null +++ b/src/lib/azure/services/redis/types.ts @@ -0,0 +1,3 @@ +import { RedisCacheConfig } from '@cdktf/provider-azurerm/lib/redis-cache' + +export interface RedisCacheProps extends RedisCacheConfig {} diff --git a/src/test/azure/common/cdkConfig/redis.json b/src/test/azure/common/cdkConfig/redis.json new file mode 100644 index 00000000..e9d2cfaf --- /dev/null +++ b/src/test/azure/common/cdkConfig/redis.json @@ -0,0 +1,10 @@ +{ + "testRedisCache": { + "name": "test-redis-cache", + "capacity": 2, + "family": "C", + "skuName": "Basic", + "nonSslPortEnabled": false, + "minimumTlsVersion": "1.2" + } +} diff --git a/src/test/azure/services/redis-manager.test.ts b/src/test/azure/services/redis-manager.test.ts new file mode 100644 index 00000000..700bd1d2 --- /dev/null +++ b/src/test/azure/services/redis-manager.test.ts @@ -0,0 +1,121 @@ +import { RedisCache } from '@cdktf/provider-azurerm/lib/redis-cache' +import { App, Testing } from 'cdktf' +import 'cdktf/lib/testing/adapters/jest' +import { Construct } from 'constructs' +import { CommonAzureConstruct, CommonAzureStack, CommonAzureStackProps, RedisCacheProps } from '../../../lib' + +interface TestAzureStackProps extends CommonAzureStackProps { + testRedisCache: RedisCacheProps + testAttribute?: string +} + +const testStackProps: any = { + domainName: 'gradientedge.io', + extraContexts: ['src/test/azure/common/cdkConfig/dummy.json', 'src/test/azure/common/cdkConfig/redis.json'], + features: {}, + name: 'test-common-stack', + resourceGroupName: 'test-rg', + skipStageForARecords: false, + stage: 'dev', + stageContextPath: 'src/test/aws/common/cdkEnv', +} + +class TestCommonStack extends CommonAzureStack { + declare props: TestAzureStackProps + + constructor(parent: Construct, name: string, props: TestAzureStackProps) { + super(parent, name, testStackProps) + this.construct = new TestCommonConstruct(this, props.name, this.props) + } + + protected determineConstructProps(props: CommonAzureStackProps) { + return { + ...super.determineConstructProps(props), + testAttribute: this.node.tryGetContext('testAttribute'), + testRedisCache: this.node.tryGetContext('testRedisCache'), + } + } +} + +class TestInvalidCommonStack extends CommonAzureStack { + declare props: TestAzureStackProps + + constructor(parent: Construct, name: string, props: TestAzureStackProps) { + super(parent, name, props) + + this.construct = new TestCommonConstruct(this, testStackProps.name, this.props) + } +} + +class TestCommonConstruct extends CommonAzureConstruct { + declare props: TestAzureStackProps + + constructor(parent: Construct, name: string, props: TestAzureStackProps) { + super(parent, name, props) + this.redisManager.createRedisCache(`test-redis-cache-${this.props.stage}`, this, this.props.testRedisCache) + } +} + +const app = new App({ context: testStackProps }) +const testingApp = Testing.fakeCdktfJsonPath(app) +const commonStack = new TestCommonStack(testingApp, 'test-common-stack', testStackProps) +const stack = Testing.fullSynth(commonStack) +const construct = Testing.synth(commonStack.construct) + +describe('TestAzureRedisConstruct', () => { + test('handles mis-configurations as expected', () => { + const error = () => new TestInvalidCommonStack(app, 'test-invalid-stack', testStackProps) + expect(error).toThrow('Props undefined for test-redis-cache-dev') + }) +}) + +describe('TestAzureRedisConstruct', () => { + test('is initialised as expected', () => { + /* test if the created stack have the right properties injected */ + expect(commonStack.props).toHaveProperty('testAttribute') + expect(commonStack.props.testAttribute).toEqual('success') + }) +}) + +describe('TestAzureRedisConstruct', () => { + test('synthesises as expected', () => { + expect(stack).toBeDefined() + expect(construct).toBeDefined() + expect(stack).toBeValidTerraform() + expect(stack).toPlanSuccessfully() + }) +}) + +describe('TestAzureRedisConstruct', () => { + test('provisions outputs as expected', () => { + expect(JSON.parse(construct).output).toMatchObject({ + testRedisCacheDevRedisCacheFriendlyUniqueId: { + value: 'test-redis-cache-dev-rc', + }, + testRedisCacheDevRedisCacheId: { + value: '${azurerm_redis_cache.test-redis-cache-dev-rc.id}', + }, + testRedisCacheDevRedisCacheName: { + value: '${azurerm_redis_cache.test-redis-cache-dev-rc.name}', + }, + }) + }) +}) + +describe('TestAzureRedisConstruct', () => { + test('provisions redis cache as expected', () => { + expect(construct).toHaveResourceWithProperties(RedisCache, { + capacity: 2, + family: 'C', + location: '${data.azurerm_resource_group.test-redis-cache-dev-rc-rg.location}', + minimum_tls_version: '1.2', + name: 'test-redis-cache-dev', + non_ssl_port_enabled: false, + resource_group_name: '${data.azurerm_resource_group.test-redis-cache-dev-rc-rg.name}', + sku_name: 'Basic', + tags: { + environment: 'dev', + }, + }) + }) +})