Skip to content

fix: CloudFormation string numbers, template directories, block devices; Lambda arm64, Azure VM sizes, Cloud SQL regions - #103

Merged
andreacappelletti97 merged 1 commit into
mainfrom
fix/demo-found-bugs
Sep 29, 2026
Merged

andreacappelletti97 merged 1 commit into
mainfrom
fix/demo-found-bugs

Conversation

@andreacappelletti97

Copy link
Copy Markdown
Contributor

Bugs found while building the c3x demo repo. Each has a regression test that fails on main and passes here.

Depends on c3xdev/c3x-pricing-api#72. The catalog changes are identical in both repos, so the catalog-sync check here fails until that PR merges. Released CLIs get the catalog fixes from the pricing API. The catalog uses only expression functions that exist in released CLIs (toJSON, contains, map literals, ??, let).

CloudFormation

  1. Numbers and booleans written as strings (AllocatedStorage: "250", MultiAZ: "false", Type: Number parameters, --var overrides) failed the whole estimate with expected number, got string or reflect: Call using string as type bool. The parser now converts strings that read back identically ("250", "16.4", "true"), recursively. "8.0" and "007" stay strings, so versions and identifiers keep their spelling. RunNumber also parses a numeric string, which its doc comment already claimed it did.
  2. --path <dir> with templates failed with no .tf, .tf.json, .tofu or .tofu.json files found. A directory with no Terraform/OpenTofu files now estimates every .yaml/.yml/.json/.template file directly inside it that has a non-empty Resources section plus AWSTemplateFormatVersion or an AWS:: type. c3x-usage.yml, buildspecs and similar files are skipped. Terraform directories are unchanged: .tf files take precedence.
  3. BlockDeviceMappings EBS volumes were unpriced. The mapping on the root device (/dev/xvda, /dev/sda1, ...) becomes root_block_device and the others become ebs_block_device entries, which is the shape the aws_instance catalog prices. Instance-store and NoDevice mappings are skipped. Lambda Architectures and EphemeralStorage.Size are also translated now.

Before / after, template with m7i.xlarge + 50 GB root + "200" GB extra gp3 and RDS AllocatedStorage: "250", MultiAZ: "false":

  • before: error (Call using string as type bool, then expected number, got string). With the strings removed: $350.39/mo with no EBS lines.
  • after: $370.39/mo (root 50 GB x $0.08 = $4, extra 200 GB x $0.08 = $16, storage 250 GB x $0.115 = $28.75).
  • c3x estimate --path c3x-demo/cloudformation: before no .tf ... files found; after $362.79/mo, the same as --path .../template.yaml.

Catalog / engine

  1. Lambda arm64: architectures is a list, so == "arm64" never matched. The check is now toJSON(default(architectures, "")) contains "arm64", and provisioned concurrency uses the same test. With 10M requests x 200 ms x 1024 MB in us-east-1: $35.33 -> $28.67/mo (Arm $0.0000133334 vs x86 $0.0000166667 per GB-s, https://aws.amazon.com/lambda/pricing/).
  2. Azure B-series no_price: the catalog filtered on skuName, which Azure spells B2s, B2s v2, D2 v3 for most series. Only some series use Standard_D2s_v5. In westeurope, 1,256 of 1,716 sizes matched nothing. VM-sized kinds now filter on armSkuName. Before -> after in westeurope (Linux, per hour): B2s $0 -> $0.048, B2ms $0 -> $0.096, B2s_v2 $0 -> $0.105, B2als_v2 $0 -> $0.0524. The last two, and D2as_v5 ($0.196), are Cloud Services rows the scraper labels Linux. The scraper fix in c3x-pricing-api#72 brings them to $0.096 / $0.0432 / $0.104 on the next Azure scrape.
  3. Cloud SQL regions: SKU descriptions end in a per-region location (in Frankfurt, in EMEA, in Singapore, in Northern Virginia). The mapping always asked for in Americas, so other regions fell back to us-central1 prices. A region -> location table now picks the right SKU; unknown regions keep the old behaviour. The engine also exposes the region a resource is priced in as region when the resource does not set it, so a provider-level region works too. db-custom-2-7680 PostgreSQL zonal, 100 GB: europe-west3 $115.62 (fallback) -> $138.81; asia-southeast1 $115.62 -> $161.84; MySQL regional with the region from the provider block (europe-west3) $231.25 -> $277.47. All 920 region x engine x availability x SKU descriptions were checked against the live API.

Checks

  • go test ./... -count=1, golangci-lint v2.13.2 (0 issues), gofumpt clean.
  • go run ./cmd/verify_catalog: 164 live, 109 static, 1067 free, 0 zero, 0 drift, 0 nofix, 0 stale, 0 errored.
  • c3x-demo aws/azure/gcp/opentofu totals are unchanged. The gcp scenario loses its 3 region-fallback caveats: europe-west1 "EMEA" happens to cost the same as us-central1.

…vices; Lambda arm64, Azure VM sizes, Cloud SQL regions

CloudFormation:
- Numeric and boolean strings ("250", "true"), which CloudFormation
  accepts and which every parameter reference produces, are converted
  when they read back identically, so catalog arithmetic and pick()
  no longer fail with "expected number, got string". RunNumber also
  parses a numeric string, as its doc comment already claimed.
- A --path directory with templates and no .tf/.tofu files estimates
  the templates (YAML/JSON with Resources plus AWSTemplateFormatVersion
  or AWS:: types). Terraform directories are unchanged.
- EC2 BlockDeviceMappings map to root_block_device / ebs_block_device,
  so their EBS volumes are priced. Lambda Architectures and
  EphemeralStorage.Size are translated.

Catalog (mirrors the pricing API catalog):
- aws_lambda_function: arm64 detection works on the architectures list.
- Azure VM-sized kinds filter on armSkuName, not skuName.
- google_sql_database_instance: SKU location follows the region.

Engine: expressions see the region a resource is priced in as `region`
when the resource does not set that attribute.
@andreacappelletti97
andreacappelletti97 merged commit 34f3a34 into main Sep 29, 2026
7 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant