Skip to content

Commit 5562534

Browse files
fix(estimation): 🩹 Fixed calculation of Burstable SKU in PostgreSQL
1 parent b4bd608 commit 5562534

File tree

8 files changed

+217
-19
lines changed

8 files changed

+217
-19
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77
**/dist/**
88
**/.terraform
99
**/.terraform.lock.hcl
10-
**/tfplan
10+
**/tfplan
11+
**/.mono
12+
**.csv

ace-tests/Reworked/KeyVault/KeyVaultUsagePatternsTests.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
using System.Text.Json;
2-
using ACE;
3-
41
namespace ACE_Tests.Reworked.KeyVault;
52

63
[Parallelizable(ParallelScope.Self)]
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
namespace ACE_Tests.Reworked.PostgreSQL;
2+
3+
[Parallelizable(ParallelScope.Self)]
4+
public class PostgreSQLTests
5+
{
6+
[Test]
7+
public void PostgreSQL_Basic_WhenThereIsBurstableSKU_ItShouldNotThrowExceptionAndGiveCorrectEstimation()
8+
{
9+
var outputFilename = $"ace_test_{DateTime.Now.Ticks}";
10+
var exitCode = Program.Main([
11+
"templates/reworked/postgresql/fix-261.bicep",
12+
"cf70b558-b930-45e4-9048-ebcefb926adf",
13+
"arm-estimator-tests-rg",
14+
"--generateJsonOutput",
15+
"--jsonOutputFilename",
16+
outputFilename,
17+
"--debug",
18+
"--mocked-retail-api-response-path",
19+
"mocked-responses/retail-api/postgresql/burstable.json"
20+
]);
21+
22+
Assert.That(exitCode, Is.EqualTo(0));
23+
24+
var outputFile = File.ReadAllText($"{outputFilename}.json");
25+
var output = JsonSerializer.Deserialize<EstimationOutput>(outputFile, Shared.JsonSerializerOptions);
26+
27+
Assert.That(output, Is.Not.Null);
28+
Assert.Multiple(() =>
29+
{
30+
Assert.That(output.TotalCost.OriginalValue, Is.EqualTo(14.990300000000001d));
31+
Assert.That(output.TotalResourceCount, Is.EqualTo(1));
32+
});
33+
}
34+
}

ace-tests/Usings.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
global using NUnit.Framework;
2-
global using ACE_Tests;
2+
global using ACE_Tests;
3+
global using ACE;
4+
global using System.Text.Json;

ace-tests/azure-cost-estimator-tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
<None Remove="mocked-responses\retail-api\asr\inferred-3.json" />
4949
<None Remove="mocked-responses\retail-api\automation-account\usage-patterns.json" />
5050
<None Remove="templates/reworked/key-vault/usage-patterns-1.bicep" />
51+
<None Remove="templates/reworked/postgresql/fix-261.bicep" />
5152
<None Remove="templates/reworked/static-web-app/static-web-app-free.bicep" />
5253
<None Remove="templates/reworked/static-web-app/static-web-app-standard.bicep" />
5354
<None Remove="templates/reworked/virtual-network/peering-1-2.bicep" />
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
{
2+
"Url": "https://prices.azure.com/api/retail/prices?currencyCode='USD'&$filter=priceType eq 'Consumption' and serviceId eq 'DZH3199QPQTD' and ((armRegionName eq 'westeurope' and skuName eq 'B1MS' and productName eq 'Azure Database for PostgreSQL Flexible Server Burstable BS Series Compute') or (armRegionName eq 'westeurope' and productName eq 'Az DB for PostgreSQL Flexible Server Storage'))",
3+
"BillingCurrency": "USD",
4+
"CustomerEntityId": "Default",
5+
"CustomerEntityType": "Retail",
6+
"Items": [
7+
{
8+
"currencyCode": "USD",
9+
"tierMinimumUnits": 0.0,
10+
"retailPrice": 0.104,
11+
"unitPrice": 0.104,
12+
"armRegionName": "westeurope",
13+
"location": "EU West",
14+
"effectiveStartDate": "2023-10-01T00:00:00Z",
15+
"meterId": "1c8e77ad-b61c-5291-be61-cf764b588c92",
16+
"meterName": "PMD V2 Throughput Prov Throughput (MBps)",
17+
"productId": "DZH318Z0DCRX",
18+
"skuId": "DZH318Z0DCRX/00LF",
19+
"productName": "Az DB for PostgreSQL Flexible Server Storage",
20+
"skuName": "PMD V2 Throughput",
21+
"serviceName": "Azure Database for PostgreSQL",
22+
"serviceId": "DZH3199QPQTD",
23+
"serviceFamily": "Databases",
24+
"unitOfMeasure": "1/Month",
25+
"type": "Consumption",
26+
"isPrimaryMeterRegion": true,
27+
"armSkuName": "V2 throughput"
28+
},
29+
{
30+
"currencyCode": "USD",
31+
"tierMinimumUnits": 0.0,
32+
"retailPrice": 0.1369,
33+
"unitPrice": 0.1369,
34+
"armRegionName": "westeurope",
35+
"location": "EU West",
36+
"effectiveStartDate": "2021-06-01T00:00:00Z",
37+
"meterId": "36845e2a-736f-5a9d-af4e-b2dc7547a06f",
38+
"meterName": "Storage Data Stored",
39+
"productId": "DZH318Z0DCRX",
40+
"skuId": "DZH318Z0DCRX/001G",
41+
"productName": "Az DB for PostgreSQL Flexible Server Storage",
42+
"skuName": "Storage",
43+
"serviceName": "Azure Database for PostgreSQL",
44+
"serviceId": "DZH3199QPQTD",
45+
"serviceFamily": "Databases",
46+
"unitOfMeasure": "1 GB/Month",
47+
"type": "Consumption",
48+
"isPrimaryMeterRegion": true,
49+
"armSkuName": "Storage"
50+
},
51+
{
52+
"currencyCode": "USD",
53+
"tierMinimumUnits": 0.0,
54+
"retailPrice": 0.1369,
55+
"unitPrice": 0.1369,
56+
"armRegionName": "westeurope",
57+
"location": "EU West",
58+
"effectiveStartDate": "2023-10-01T00:00:00Z",
59+
"meterId": "50d9a511-fb6e-5113-8003-5a1ef7abca11",
60+
"meterName": "Premium Managed Disk V2 Data Stored",
61+
"productId": "DZH318Z0DCRX",
62+
"skuId": "DZH318Z0DCRX/00C6",
63+
"productName": "Az DB for PostgreSQL Flexible Server Storage",
64+
"skuName": "Premium Managed Disk V2",
65+
"serviceName": "Azure Database for PostgreSQL",
66+
"serviceId": "DZH3199QPQTD",
67+
"serviceFamily": "Databases",
68+
"unitOfMeasure": "1 GiB/Month",
69+
"type": "Consumption",
70+
"isPrimaryMeterRegion": true,
71+
"armSkuName": "Premium Managed Disk"
72+
},
73+
{
74+
"currencyCode": "USD",
75+
"tierMinimumUnits": 0.0,
76+
"retailPrice": 0.0199,
77+
"unitPrice": 0.0199,
78+
"armRegionName": "westeurope",
79+
"location": "EU West",
80+
"effectiveStartDate": "2021-12-01T00:00:00Z",
81+
"meterId": "5e31969e-0b91-5186-81a1-7475fabe1262",
82+
"meterName": "B1MS",
83+
"productId": "DZH318Z0DCRL",
84+
"skuId": "DZH318Z0DCRL/003J",
85+
"productName": "Azure Database for PostgreSQL Flexible Server Burstable BS Series Compute",
86+
"skuName": "B1MS",
87+
"serviceName": "Azure Database for PostgreSQL",
88+
"serviceId": "DZH3199QPQTD",
89+
"serviceFamily": "Databases",
90+
"unitOfMeasure": "1 Hour",
91+
"type": "Consumption",
92+
"isPrimaryMeterRegion": true,
93+
"armSkuName": "B1MS"
94+
},
95+
{
96+
"currencyCode": "USD",
97+
"tierMinimumUnits": 0.0,
98+
"retailPrice": 0.0595,
99+
"unitPrice": 0.0595,
100+
"armRegionName": "westeurope",
101+
"location": "EU West",
102+
"effectiveStartDate": "2023-03-01T00:00:00Z",
103+
"meterId": "75ef37c1-50db-5bc2-8bf5-ba9b97da69d2",
104+
"meterName": "IOPS Scaling Provisioned IOPS",
105+
"productId": "DZH318Z0DCRX",
106+
"skuId": "DZH318Z0DCRX/00CG",
107+
"productName": "Az DB for PostgreSQL Flexible Server Storage",
108+
"skuName": "IOPS Scaling",
109+
"serviceName": "Azure Database for PostgreSQL",
110+
"serviceId": "DZH3199QPQTD",
111+
"serviceFamily": "Databases",
112+
"unitOfMeasure": "1/Month",
113+
"type": "Consumption",
114+
"isPrimaryMeterRegion": true,
115+
"armSkuName": "IOPS Scaling"
116+
},
117+
{
118+
"currencyCode": "USD",
119+
"tierMinimumUnits": 0.0,
120+
"retailPrice": 0.026,
121+
"unitPrice": 0.026,
122+
"armRegionName": "westeurope",
123+
"location": "EU West",
124+
"effectiveStartDate": "2023-10-01T00:00:00Z",
125+
"meterId": "a3b9a7f9-8335-5ff1-b3bb-b8b82c26ac2a",
126+
"meterName": "PMD V2 IOPS Provisioned IOPS",
127+
"productId": "DZH318Z0DCRX",
128+
"skuId": "DZH318Z0DCRX/00LJ",
129+
"productName": "Az DB for PostgreSQL Flexible Server Storage",
130+
"skuName": "PMD V2 IOPS",
131+
"serviceName": "Azure Database for PostgreSQL",
132+
"serviceId": "DZH3199QPQTD",
133+
"serviceFamily": "Databases",
134+
"unitOfMeasure": "1/Month",
135+
"type": "Consumption",
136+
"isPrimaryMeterRegion": true,
137+
"armSkuName": "V2 IOPS"
138+
}
139+
],
140+
"NextPageLink": null,
141+
"Count": 6
142+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
param parSuffix string = utcNow('yyyyMMddhhmmss')
2+
param parLocation string = resourceGroup().location
3+
4+
resource db1 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = {
5+
#disable-next-line use-stable-resource-identifiers
6+
name: 'postgresqlserver1-${parSuffix}'
7+
location: parLocation
8+
sku: {
9+
name: 'Standard_B1ms'
10+
tier: 'Burstable'
11+
}
12+
properties: {
13+
createMode: 'Default'
14+
administratorLogin: 'login'
15+
administratorLoginPassword: 'password'
16+
storage: {
17+
storageSizeGB: 64
18+
}
19+
}
20+
}

ace/Products/PostgreSQL/PostgreSQLFlexibleQueryFilter.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,13 @@
11
using ACE.WhatIf;
22
using Microsoft.Extensions.Logging;
3-
using System;
43
using System.Text.Json;
54

6-
internal class PostgreSQLFlexibleQueryFilter : IQueryFilter
5+
internal class PostgreSQLFlexibleQueryFilter(WhatIfAfterBeforeChange afterState, ILogger logger) : IQueryFilter
76
{
87
private const string ServiceId = "DZH3199QPQTD";
98

10-
private readonly WhatIfAfterBeforeChange afterState;
11-
private readonly ILogger logger;
12-
13-
public PostgreSQLFlexibleQueryFilter(WhatIfAfterBeforeChange afterState, ILogger logger)
14-
{
15-
this.afterState = afterState;
16-
this.logger = logger;
17-
}
9+
private readonly WhatIfAfterBeforeChange afterState = afterState;
10+
private readonly ILogger logger = logger;
1811

1912
public string? GetFiltersBasedOnDesiredState(string location)
2013
{
@@ -28,29 +21,36 @@ public PostgreSQLFlexibleQueryFilter(WhatIfAfterBeforeChange afterState, ILogger
2821
var tierId = this.afterState.sku?.tier;
2922
var skuParts = sku.Split("_");
3023
var familyId = skuParts[1];
31-
var cores = skuParts[2];
32-
var skuName = $"{cores} vCore";
24+
3325
string? skuProductName;
3426
string? storageProductName;
3527
string? productName;
28+
string? skuName;
3629

3730
// Note that for some reasons some product names for PostgreSQL contain non-breaking space \u00A0
3831
// meaning query won't work if we miss them. We may use armSkuName in the future
3932
if (tierId == "Burstable")
4033
{
34+
// Always fun when there're magical edge cases :F
35+
if(familyId.Equals("B1ms", StringComparison.CurrentCultureIgnoreCase) || familyId.Equals("B2s", StringComparison.CurrentCultureIgnoreCase))
36+
{
37+
familyId = familyId.ToUpper();
38+
}
39+
4140
skuName = familyId;
42-
skuProductName = "Burstable BS";
4341
storageProductName = "Az DB for PostgreSQL Flexible Server Storage";
44-
productName = $"Azure Database for PostgreSQL Flexible Server {skuProductName} Series Compute";
42+
productName = $"Azure Database for PostgreSQL Flexible Server Burstable BS Series Compute";
4543
}
4644
else if (tierId == "GeneralPurpose")
4745
{
46+
skuName = $"{skuParts[2]} vCore";
4847
skuProductName = $"General Purpose\u00A0{familyId}\u00A0";
4948
storageProductName = "Az DB for PostgreSQL Flexible Server Storage";
5049
productName = $"Azure Database for PostgreSQL Flexible Server {skuProductName}Series Compute";
5150
}
5251
else
5352
{
53+
skuName = $"{skuParts[2]} vCore";
5454
skuProductName = $"Memory Optimized\u00A0{familyId}\u00A0";
5555
storageProductName = "Az DB for PostgreSQL Flexible Server Storage";
5656

0 commit comments

Comments
 (0)