@suss/contract-terraform 0.23.0 → 0.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +6 -67
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @suss/contract-terraform
2
2
 
3
+ Part of [suss](https://github.com/nimbuscloud-ai/suss), which reads both sides of every call in a repository and says where the two disagree.
4
+
3
5
  Reads the boundaries a Terraform configuration declares, so code and other resources can be checked against them.
4
6
 
5
7
  ## What this package is
@@ -50,71 +52,8 @@ A table declares its keys and lets every other attribute vary, so the contract s
50
52
 
51
53
  Depends on `@suss/behavioral-ir` for the summaries it produces and `hcl2-parser` for reading HCL. The storage pass in `@suss/checker` pairs what code touches against what this declares.
52
54
 
53
- ## A way in that copies part of an item
54
-
55
- A DynamoDB index does not contain the whole item. `projection_type = "INCLUDE"` copies the attributes it lists, `KEYS_ONLY` copies none, and both copy the index's keys and the table's. A reader asking that index for anything else gets nothing back for it, and the store raises no error, so the caller sees an item with fields missing and nothing says why.
56
-
57
- So an index like that declares every field it will ever have, and its contract says `exhaustive` where the table's says `partial`. A pack states which attributes have that, under `serves`, and a store without the idea leaves it out.
58
-
59
- ## A resource that reads what another one declares
60
-
61
- Some resources refer to another resource by the string the deployment gives it. An alert refers to a metric that way, and a metric declares itself:
62
-
63
- ```hcl
64
- resource "signals_counter" "refusals" {
65
- name = "${var.environment}-refusals"
66
-
67
- shape {
68
- value_type = "SPREAD"
69
- }
70
- }
71
-
72
- resource "signals_watch" "refusals_climbing" {
73
- rules {
74
- over_threshold {
75
- selector = "signal.id=\"signals.example/counters/${var.environment}-refusals\" AND region=\"west\""
76
- limit = 5
77
- }
78
- }
79
- }
80
- ```
81
-
82
- An entry with `kind: "metric"` says which attribute is the name and how the deployed identity is spelled around it, as `metricTypeTemplate`, so the reader can build the string the other side spells. An entry with `kind: "metric-reading"` says which blocks one reading is written inside, which attribute is the selector, and which key inside that selector is the metric. Each reading becomes its own consumer summary, since one resource usually states several, each about a different metric.
83
-
84
- The selector is a small query language rather than a name, so `parseFilterQuery` reads it: comparisons joined by `AND` and `OR`, with parentheses, `NOT`, quoted or bare values, and terms written next to each other for AND. A key may quote one of its own segments, `metric.label."response_code_class"`, and that comes back as one key with the quotes off. A call may stand where a comparison would, which is how Cloud Monitoring writes an SLO burn-rate condition, and `filterCalls` gives those back. The parser is exported, so a pack that has to read the same string for something else does not write a second one. A selector it cannot read leaves the reading with no metric on it, which pairs with nothing.
85
-
86
- Both kinds also say what the resource's own words mean in suss's terms, so the checker never learns a provider's vocabulary. A pack writes `values: { attribute: "shape.value_type", means: { SPREAD: "histogram", SCALAR: "number" } }`, the reader takes the value at that attribute, dotted paths stepping into nested blocks, looks it up in the table, and writes the answer to `metricContract` on the summary. `accumulates` works the same way. A value the table does not list says nothing, the same as an attribute the configuration never set.
87
-
88
- A reading translates two things the same way. `comparesTo: { attribute: "limit", whenSet: "number" }` says the reading compares the series against a single number when it states a limit at all, since a threshold is a number by being written down. `reducesTo` maps an aligner or reducer to what it leaves behind. Both land on the summary under `metricReading`, along with the setting and the values that would reduce the series, so a finding can name the fix without the checker knowing which provider it came from.
89
-
90
- Comparing the two sides is `checkMetric` in `@suss/checker`, and it runs off summaries alone. No pack is loaded at check time.
91
-
92
- ## A reference to a resource in the same configuration
93
-
94
- The side that reads a metric often spells the name through a Terraform reference rather than by copying the string:
95
-
96
- ```hcl
97
- resource "signals_counter" "refused" {
98
- name = "edp-sweep-refused"
99
- }
100
-
101
- resource "signals_watch" "refused_sustained" {
102
- rules {
103
- over_threshold {
104
- selector = "signal.id=\"signals.example/counters/${signals_counter.refused.name}\""
105
- }
106
- }
107
- }
108
- ```
109
-
110
- Both resources deploy the same string, and the configuration already says what it is, so the reference resolves to `signals.example/counters/edp-sweep-refused` on both summaries and the two pair. Left as the hole `{signals_counter.refused.name}`, the two sides of one configuration spell the same metric differently and pair with nothing.
111
-
112
- Only `<resource_type>.<label>.<attribute>` resolves, and only when that resource writes the attribute as a literal string. An attribute the provider fills in at apply time, an `id` or an `arn` or a `self_link`, is not written anywhere in the file, so nothing is found and the hole stays. `var.` and `local.` stay holes as well: a variable's default is not what production runs with, and a name built from a stage prefix has to go on pairing with whatever stage the code that meets it was written for.
113
-
114
- A reference whose attribute is itself built from another reference is followed four hops, and then the hole stays. Two resources that refer to each other leave the value exactly as it was written.
115
-
116
- The scope is every file being read together, so a reference finds a resource another file in the module states, the way Terraform reads a module.
117
-
118
- ## A name a locals block states
55
+ ## More
119
56
 
120
- A configuration that writes `local.table_name = "orders-v1"` and refers to it from every resource has stated that name as plainly as a resource would, so a reference to it resolves. One built from a variable, `"${var.environment}-orders-v1"`, expands to a value that still has a variable in it, which becomes a hole again, so a name built from a stage prefix reads the way it always did.
57
+ - [How it resolves a reference](./DESIGN.md)
58
+ - [Documentation](https://nimbuscloud-ai.github.io/suss/)
59
+ - [Source and issues](https://github.com/nimbuscloud-ai/suss)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/contract-terraform",
3
- "version": "0.23.0",
3
+ "version": "0.24.1",
4
4
  "type": "module",
5
5
  "description": "Read the storage a Terraform configuration declares and check it against the code that reads and writes those tables.",
6
6
  "keywords": [
@@ -47,7 +47,7 @@
47
47
  "typecheck": "tsc --noEmit"
48
48
  },
49
49
  "dependencies": {
50
- "@suss/behavioral-ir": "0.23.0",
50
+ "@suss/behavioral-ir": "0.24.1",
51
51
  "hcl2-parser": "^1.0.3",
52
52
  "semver": "^7.6.0"
53
53
  },