|
| 1 | +package azure |
| 2 | + |
| 3 | +import ( |
| 4 | + "fmt" |
| 5 | + |
| 6 | + "github.com/infracost/infracost/internal/resources" |
| 7 | + "github.com/infracost/infracost/internal/schema" |
| 8 | + "github.com/shopspring/decimal" |
| 9 | +) |
| 10 | + |
| 11 | +// MySQLFlexibleServer struct represents Azure MySQL Flexible Server resource. |
| 12 | +// |
| 13 | +// Resource information: https://docs.microsoft.com/en-gb/azure/mysql/flexible-server/ |
| 14 | +// Pricing information: https://azure.microsoft.com/en-gb/pricing/details/mysql/flexible-server/ |
| 15 | +type MySQLFlexibleServer struct { |
| 16 | + Address string |
| 17 | + Region string |
| 18 | + |
| 19 | + SKU string |
| 20 | + Tier string |
| 21 | + InstanceType string |
| 22 | + InstanceVersion string |
| 23 | + Storage int64 |
| 24 | + IOPS int64 |
| 25 | + |
| 26 | + // "usage" args |
| 27 | + AdditionalBackupStorageGB *float64 `infracost_usage:"additional_backup_storage_gb"` |
| 28 | +} |
| 29 | + |
| 30 | +// MySQLFlexibleServerUsageSchema defines a list which represents the usage schema of MySQLFlexibleServer. |
| 31 | +var MySQLFlexibleServerUsageSchema = []*schema.UsageItem{ |
| 32 | + {Key: "additional_backup_storage_gb", DefaultValue: 0, ValueType: schema.Float64}, |
| 33 | +} |
| 34 | + |
| 35 | +// PopulateUsage parses the u schema.UsageData into the MySQLFlexibleServer. |
| 36 | +// It uses the `infracost_usage` struct tags to populate data into the MySQLFlexibleServer. |
| 37 | +func (r *MySQLFlexibleServer) PopulateUsage(u *schema.UsageData) { |
| 38 | + resources.PopulateArgsWithUsage(r, u) |
| 39 | +} |
| 40 | + |
| 41 | +// BuildResource builds a schema.Resource from a valid MySQLFlexibleServer struct. |
| 42 | +// This method is called after the resource is initialised by an IaC provider. |
| 43 | +// See providers folder for more information. |
| 44 | +func (r *MySQLFlexibleServer) BuildResource() *schema.Resource { |
| 45 | + costComponents := []*schema.CostComponent{ |
| 46 | + r.computeCostComponent(), |
| 47 | + r.storageCostComponent(), |
| 48 | + } |
| 49 | + |
| 50 | + if iopsCostComponent := r.iopsCostComponent(); iopsCostComponent != nil { |
| 51 | + costComponents = append(costComponents, iopsCostComponent) |
| 52 | + } |
| 53 | + |
| 54 | + costComponents = append(costComponents, r.backupCostComponent()) |
| 55 | + |
| 56 | + return &schema.Resource{ |
| 57 | + Name: r.Address, |
| 58 | + UsageSchema: MySQLFlexibleServerUsageSchema, |
| 59 | + CostComponents: costComponents, |
| 60 | + } |
| 61 | +} |
| 62 | + |
| 63 | +// computeCostComponent returns a cost component for server compute requirements. |
| 64 | +func (r *MySQLFlexibleServer) computeCostComponent() *schema.CostComponent { |
| 65 | + attrs := getFlexibleServerFilterAttributes(r.Tier, r.InstanceType, r.InstanceVersion) |
| 66 | + |
| 67 | + // Eds series has two spaces in CPAPI hence '\s+' |
| 68 | + productNameRegex := fmt.Sprintf("^Azure Database for MySQL Flexible Server %s\\s+%s", attrs.TierName, attrs.Series) |
| 69 | + |
| 70 | + return &schema.CostComponent{ |
| 71 | + Name: fmt.Sprintf("Compute (%s)", r.SKU), |
| 72 | + Unit: "hours", |
| 73 | + UnitMultiplier: decimal.NewFromInt(1), |
| 74 | + HourlyQuantity: decimalPtr(decimal.NewFromInt(1)), |
| 75 | + ProductFilter: &schema.ProductFilter{ |
| 76 | + VendorName: strPtr("azure"), |
| 77 | + Region: strPtr(r.Region), |
| 78 | + Service: strPtr("Azure Database for MySQL"), |
| 79 | + ProductFamily: strPtr("Databases"), |
| 80 | + AttributeFilters: []*schema.AttributeFilter{ |
| 81 | + {Key: "productName", ValueRegex: regexPtr(productNameRegex)}, |
| 82 | + {Key: "skuName", ValueRegex: regexPtr(fmt.Sprintf("^%s$", attrs.SKUName))}, |
| 83 | + {Key: "meterName", ValueRegex: regexPtr(fmt.Sprintf("^%s$", attrs.MeterName))}, |
| 84 | + }, |
| 85 | + }, |
| 86 | + PriceFilter: &schema.PriceFilter{ |
| 87 | + PurchaseOption: strPtr("Consumption"), |
| 88 | + }, |
| 89 | + } |
| 90 | +} |
| 91 | + |
| 92 | +// storageCostComponent returns a cost component for server's storage. If |
| 93 | +// storage is not defined, it is assumed it is a minimum default of 20GB. |
| 94 | +func (r *MySQLFlexibleServer) storageCostComponent() *schema.CostComponent { |
| 95 | + storage := r.Storage |
| 96 | + if storage == 0 { |
| 97 | + storage = 20 // minimum default |
| 98 | + } |
| 99 | + |
| 100 | + return &schema.CostComponent{ |
| 101 | + Name: "Storage", |
| 102 | + Unit: "GB", |
| 103 | + UnitMultiplier: decimal.NewFromInt(1), |
| 104 | + MonthlyQuantity: decimalPtr(decimal.NewFromInt(storage)), |
| 105 | + ProductFilter: &schema.ProductFilter{ |
| 106 | + VendorName: strPtr("azure"), |
| 107 | + Region: strPtr(r.Region), |
| 108 | + Service: strPtr("Azure Database for MySQL"), |
| 109 | + ProductFamily: strPtr("Databases"), |
| 110 | + AttributeFilters: []*schema.AttributeFilter{ |
| 111 | + {Key: "productName", Value: strPtr("Azure Database for MySQL Flexible Server Storage")}, |
| 112 | + {Key: "meterName", Value: strPtr("Storage Data Stored")}, |
| 113 | + }, |
| 114 | + }, |
| 115 | + } |
| 116 | +} |
| 117 | + |
| 118 | +// iopsCostComponent returns a cost component for additional IOPS. Each server |
| 119 | +// includes free 300 IOPS and 3 IOPS per each storage GB. As minimum storage is |
| 120 | +// 20GB, the total free IOPS is 360. If no IOPS is defined it's assumed it is |
| 121 | +// the minimum of 360. |
| 122 | +func (r *MySQLFlexibleServer) iopsCostComponent() *schema.CostComponent { |
| 123 | + var freeIOPS int64 = 360 |
| 124 | + |
| 125 | + iops := r.IOPS |
| 126 | + if iops == 0 { |
| 127 | + iops = freeIOPS |
| 128 | + } |
| 129 | + |
| 130 | + additionalIOPS := iops - freeIOPS |
| 131 | + |
| 132 | + if additionalIOPS <= 0 { |
| 133 | + return nil |
| 134 | + } |
| 135 | + |
| 136 | + return &schema.CostComponent{ |
| 137 | + Name: "Additional IOPS", |
| 138 | + Unit: "IOPS", |
| 139 | + UnitMultiplier: decimal.NewFromInt(1), |
| 140 | + MonthlyQuantity: decimalPtr(decimal.NewFromInt(additionalIOPS)), |
| 141 | + ProductFilter: &schema.ProductFilter{ |
| 142 | + VendorName: strPtr("azure"), |
| 143 | + Region: strPtr(r.Region), |
| 144 | + Service: strPtr("Azure Database for MySQL"), |
| 145 | + ProductFamily: strPtr("Databases"), |
| 146 | + AttributeFilters: []*schema.AttributeFilter{ |
| 147 | + {Key: "productName", Value: strPtr("Azure Database for MySQL Flexible Server Storage")}, |
| 148 | + {Key: "skuName", Value: strPtr("Additional IOPS")}, |
| 149 | + }, |
| 150 | + }, |
| 151 | + } |
| 152 | +} |
| 153 | + |
| 154 | +// backupCostComponent returns a cost component for additional backup storage. |
| 155 | +func (r *MySQLFlexibleServer) backupCostComponent() *schema.CostComponent { |
| 156 | + var quantity *decimal.Decimal |
| 157 | + if r.AdditionalBackupStorageGB != nil { |
| 158 | + quantity = decimalPtr(decimal.NewFromFloat(*r.AdditionalBackupStorageGB)) |
| 159 | + } |
| 160 | + |
| 161 | + return &schema.CostComponent{ |
| 162 | + Name: "Additional backup storage", |
| 163 | + Unit: "GB", |
| 164 | + UnitMultiplier: decimal.NewFromInt(1), |
| 165 | + MonthlyQuantity: quantity, |
| 166 | + ProductFilter: &schema.ProductFilter{ |
| 167 | + VendorName: strPtr("azure"), |
| 168 | + Region: strPtr(r.Region), |
| 169 | + Service: strPtr("Azure Database for MySQL"), |
| 170 | + ProductFamily: strPtr("Databases"), |
| 171 | + AttributeFilters: []*schema.AttributeFilter{ |
| 172 | + {Key: "productName", Value: strPtr("Azure Database for MySQL Flexible Server Backup Storage")}, |
| 173 | + {Key: "meterName", Value: strPtr("Backup Storage Data Stored")}, |
| 174 | + }, |
| 175 | + }, |
| 176 | + } |
| 177 | +} |
0 commit comments