Documentation
¶
Overview ¶
Package registry loads the two committed join artifacts - live/mapping.json (issue #43, the TF-to-CFN type join) and live/registry.json (issue #42, the CloudFormation Registry's per-type roster) - and answers the one question discovery's Cloud Control fallback (#47) needs of them: given a TF resource type with no native provider list resource, is there a mapped CFN type that Cloud Control can enumerate on its own, with no per-call parsing of either file?
Load parses both files once into a Roster; every lookup after that is a map read. A caller that discovers many types in one run - a whole estate's admission table, in the sweep case - is expected to build one Roster and share it, not reopen the files per type.
What counts as mapped ¶
live/mapping.json's via column carries five values (see tools/mapping-gen): "name" and "alias" are a real CFN counterpart, curated or found by the name heuristic; "service-alias" is the same claim from mapping-gen's heuristic v2 (a service-level join whose ambiguous hits are never written into the artifact, so a row that carries it is an unambiguous match); "fold" says the TF type's live representation is folded into a parent CFN resource's own type, so there is no CFN type of its own to enumerate; "none" is no counterpart at all. Only the first three produce a Roster.CloudControlType hit - a fold parent is a different resource, and enumerating it would return the wrong type's population. (service-alias was missing from the accepted set until #124's aps cohort failed replan on aws_prometheus_workspace, a service-alias row the roster silently dropped.)
What counts as listable ¶
live/registry.json's handlers.list is CloudFormation's own claim that a ListResources call for the type will do something; handlers.list's list_required_input is what tools/registry-gen calls EnumerabilityParentInput - a list call that needs a parent's identifier this package's callers have no way to supply from a bare type name. Roster.Listable is therefore true only for handlers.list with an empty required-input list - CloudFormation's "free" enumerability - which is the same bar admission-path-2 discovery already clears for a provider's native list resource: no argument beyond an optional region.
A required-input type is not unlistable, only unlistable without a parent's value in hand. Roster.EnumerationSourceScoped answers the complementary question Roster.EnumerationSource refuses: given a TF type mapped to a CFN type whose list handler names required input, what are those input property names, so a caller that does have a parent value (internal/live/discovery's parent-scoped Cloud Control leg) can build the ResourceModel the scoped call needs (pkg/cloudcontrol.Client.ListResourcesScoped). The two methods are mutually exclusive for any one CFN type: a type reports ok from at most one of them, since Listable's own condition is required-input being empty.
Index ¶
- type Roster
- func (r *Roster) CloudControlType(tfType string) (cfnType string, ok bool)
- func (r *Roster) CloudControlTypeOrService(tfType string) (string, bool)
- func (r *Roster) EnumerationSource(tfType string) (cfnType string, ok bool)
- func (r *Roster) EnumerationSourceScoped(tfType string) (cfnType string, requiredInput []string, ok bool)
- func (r *Roster) IdentifierArity(cfnType string) int
- func (r *Roster) Listable(cfnType string) bool
- func (r *Roster) ListableKnown(cfnType string) (listable, known bool)
- func (r *Roster) PrimaryIdentifier(cfnType string) []string
- func (r *Roster) RelatedTypes(tfType string) map[string]bool
- func (r *Roster) RequiredListInput(cfnType string) (properties []string, ok bool)
- func (r *Roster) ServiceOf(tfType string) (string, bool)
- func (r *Roster) TFTypesForCFNType(cfnType string) []string
- func (r *Roster) TFTypesForCFNTypeAnyProvenance(cfnType string) []string
- func (r *Roster) TagOnCreateKnown(cfnType string) (tagOnCreate, known bool)
- func (r *Roster) Taggable(cfnType string) bool
- func (r *Roster) TaggableKnown(cfnType string) (taggable, known bool)
- func (r *Roster) TaggingDeclared(cfnType string) (declared, known bool)
- func (r *Roster) TagsAfterCreate(cfnType string) bool
- func (r *Roster) TagsCreateOnly(cfnType string) (createOnly, known bool)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Roster ¶
type Roster struct {
// contains filtered or unexported fields
}
Roster is live/mapping.json joined against live/registry.json, held in memory after one parse of each. The zero value is not useful; build one with Load or Parse.
func Embedded ¶
Embedded returns the roster built from the artifacts compiled into the binary, parsed once. This is what production commands use (Load and Parse serve tests and tools that point at a checkout's live/ copies).
func Parse ¶
Parse builds a Roster from the two artifacts' JSON bytes directly, for a caller that already has them (a test fixture, an embedded copy, bytes fetched some other way) without going through the filesystem.
func (*Roster) CloudControlType ¶
CloudControlType returns the CFN type live/mapping.json joins tfType to, and whether it named one at all (a "name" or "alias" row - see the package doc). A "fold" or "none" row, or a tfType the mapping artifact never saw, reports ok=false.
func (*Roster) CloudControlTypeOrService ¶
CloudControlTypeOrService returns the CFN type tfType maps to under the wider join Roster.ServiceOf uses - every mapping row naming a CFN type, including the former2-sourced ones Roster.CloudControlType excludes because they are not Cloud Control enumerable.
Named for what it is: the CFN type, for a caller that wants identity or relationship facts rather than enumerability.
func (*Roster) EnumerationSource ¶
EnumerationSource is the combined join a caller actually wants: the CFN type Cloud Control should be asked to list tfType through, when the mapping names one and the registry roster says it is listable. ok is false for anything short of both - unmapped, folded, or mapped to a type that turns out to require input to list - which is exactly the "neither" leg of discovery's enumeration-source selection (issue #47): the caller falls back to its existing refusal.
func (*Roster) EnumerationSourceScoped ¶
func (r *Roster) EnumerationSourceScoped(tfType string) (cfnType string, requiredInput []string, ok bool)
EnumerationSourceScoped is Roster.EnumerationSource's counterpart for a TF type mapped to a CFN type whose list handler requires scoping input: where EnumerationSource reports ok=false and leaves the caller with a refusal, this reports the CFN type and the required property names, for a caller that has a parent value in hand to scope with (internal/live/discovery's parent-scoped Cloud Control leg, pkg/cloudcontrol.Client.ListResourcesScoped).
ok is false for anything short of a mapped CFN type whose registry row sets handlers.list with a non-empty required-input list - which, by construction, is exactly the set Roster.EnumerationSource excludes for requiring input. A tfType is therefore never ok from both methods.
func (*Roster) IdentifierArity ¶
IdentifierArity is the number of "|"-joined segments a Cloud Control identifier for cfnType carries - live/registry.json's len(primary_identifier). Zero for a type live/registry.json never saw, which is indistinguishable here from a (nonexistent) zero-part identifier; callers that need to tell those apart should check Roster.Listable first.
func (*Roster) Listable ¶
Listable reports whether cfnType can be enumerated with Cloud Control's ListResources and no required input (see the package doc's "What counts as listable"). False for a type live/registry.json never saw.
func (*Roster) ListableKnown ¶
ListableKnown is Roster.Listable with the same found/not-found split Roster.TaggableKnown carries, and for the same reason.
func (*Roster) PrimaryIdentifier ¶
PrimaryIdentifier returns live/registry.json's own primary_identifier for cfnType, in order - the CFN property name each "|"-joined segment of a ListResources/GetResource Identifier carries, positionally (see the [Roster.primaryIdentifier] field doc for why a caller needs the position rather than assuming one). Nil for a type live/registry.json never saw.
func (*Roster) RelatedTypes ¶
RelatedTypes returns the set of CloudFormation types the schema for tfType's own CFN type declares a relationshipRef to (issue #151), keyed by CFN type name. Empty when the type is unmapped or its schema declares none, which is the overwhelming majority: 26 of 1,683 schemas carry the annotation at the pin this was written against.
It is a set rather than a per-property map on purpose. A consumer asking "is this derived parent one AWS declares a relationship to" does not need to know which property carries it, and pairing a Terraform argument to a CloudFormation property name would be a second inference on top of the one being checked.
func (*Roster) RequiredListInput ¶
RequiredListInput returns the CFN property names cfnType's list handler requires as scoping input (live/registry.json's handlers.list_required_input), in registry order, and whether it has any. False for a type live/registry.json never saw, one whose handlers.list is unset, or one whose list needs no input at all - Roster.Listable is true for that last case instead, and the two never overlap.
func (*Roster) ServiceOf ¶
ServiceOf reports the CloudFormation service segment of the type tfType maps to - "EC2" for aws_volume_attachment, via AWS::EC2::VolumeAttachment - and whether it is mapped at all.
It exists for [identity.ServiceOf] (issue #129), whose parent derivation needs to know whether two Terraform types belong to the same AWS service. The Terraform name cannot answer that: aws_volume_attachment and aws_ebs_volume share no prefix and are both AWS::EC2. Only the mapping knows.
Wider than Roster.CloudControlType on purpose. That one answers "can Cloud Control enumerate this", so it keeps only name/alias/service-alias rows; this one answers "which service is this", so a former2-sourced row counts too - aws_volume_attachment is one, and excluding it is what sent it to the residue in the first draft of this change.
A type this roster has no CFN mapping for reports ok=false rather than an empty service, so a caller can tell "unmapped" from "mapped to something with no service segment", and fall back rather than treat two unmapped types as sharing one.
func (*Roster) TFTypesForCFNType ¶
TFTypesForCFNType returns every TF type live/mapping.json's "name" or "alias" rows map to cfnType, sorted - the reverse of Roster.CloudControlType, for a caller that has a CFN type in hand and wants the TF type it names (the ARN join, issue #51: an ARN's service and resource segments join to a CFN type first, and only then to a TF type). nil for a CFN type nothing maps to. A result with more than one entry means the CFN type's TF mapping is not unique: in the committed pkg/registry/mapping.json 19 CFN types map to more than one TF type (AWS::ElasticLoadBalancingV2::LoadBalancer to aws_alb and aws_lb, AWS::FSx::FileSystem to four, GitHub issue #2286). A caller doing a reverse join treats such a result as ambiguous rather than picking the first; an adoption file names the engine type for those (pkg/adoption, Selection.EngineType).
func (*Roster) TFTypesForCFNTypeAnyProvenance ¶
TFTypesForCFNTypeAnyProvenance is Roster.TFTypesForCFNType over every mapping row that names a CFN type, whatever its via - see tfTypeForAnyProvenance's own field comment for why the reverse join is allowed a wider index than the enumeration join, and for the measurement behind it. Sorted, nil for a CFN type no row names at all. More than one entry is ambiguity the caller must resolve or refuse, exactly as with the narrow method.
func (*Roster) TagOnCreateKnown ¶
TagOnCreateKnown reports whether cfnType's create call accepts tags - live/registry.json's tagging.tag_on_create - and whether that answer came from upstream at all. known is false for a type the registry never saw, for a row whose schema declared no tagging block (the Roster.TaggingDeclared silence, which registry-gen records as the block's zero value, tag_on_create included), and for a row that is not taggable in the first place, where the flag has nothing to govern.
GitHub issue #1084 asked which of three things this field is, and the answer is the create path (the issue's case 2): a type whose row reads taggable=true and tag_on_create=false can carry this fork's ownership marker but cannot be handed it in the call that creates it, so the node writer withholds the marker from that call and the live path writes it immediately after, in the same apply, before the instance is reported complete (internal/live/projection's AdjustCreateConfigValue and WriteAppliedMarkers, keyed on this accessor and nothing else). Ten of the artifact's 1,035 taggable rows read false at the pinned bundle, AWS::Route53::HostedZone among them; the rest read true and take the ordinary path, where the marker rides the create call in the provider's own tags argument.
A false with known=true is therefore the only value a caller acts on. A caller that treated the bare bool as an answer would withhold markers from every silent and every untaggable row, which is 648 types the flag says nothing about.
func (*Roster) Taggable ¶
Taggable reports live/registry.json's tagging.taggable for cfnType. False for a type live/registry.json never saw.
func (*Roster) TaggableKnown ¶
TaggableKnown is Roster.Taggable with the question the bare bool cannot answer: whether live/registry.json has a row for this CFN type at all.
The two are different facts and the plain form collapses them, which is issue #168. A caller that turns a false into an operator-facing sentence naming the artifact ("live/registry.json records X as untaggable") is claiming the artifact said something; when there is no row, it said nothing, and what the operator needs to hear is that two artifacts have drifted rather than that a resource type cannot carry a tag.
Zero CFN types the roster maps are missing from the registry today. That is a property of two artifacts regenerated from different upstreams at different times, not a guarantee.
func (*Roster) TaggingDeclared ¶
TaggingDeclared reports whether the CloudFormation schema behind cfnType's registry row said anything about tagging at all, and whether live/registry.json has a row for the type in the first place (the same known split Roster.TaggableKnown carries, for the same reason).
It is the question Roster.Taggable cannot answer and issue #1327's whole subject. registry-gen writes a tagging block for every row; a schema with no "tagging" key gets that block's zero value, so tagging.taggable reads false for it exactly as it does for a schema that denied tagging outright. 216 of the artifact's 1,683 rows are silence recorded as denial.
The distinction is not cosmetic. Over the pinned bundle, 89 of those 216 silent schemas declare a Tags property of their own against 3 of the 432 explicit denials, so the two populations disagree with the schema's own properties at completely different rates - reading one as the other is wrong about roughly two fifths of the silent set.
What a caller should do with declared=false: not turn tagging.taggable into a sentence that attributes a claim to CloudFormation. Whether the object can carry a marker is a different question with a different authority - the provider's own resource schema, which internal/live/markers.Taggable reads and internal/live/stamp writes through.
func (*Roster) TagsAfterCreate ¶
TagsAfterCreate reports whether cfnType is a taggable type whose create call cannot carry tags, so its marker must be written after the create - Roster.TagOnCreateKnown with the three-way answer collapsed to the one case a writer acts on. False for every type the registry cannot vouch for, which is the ordinary path.
func (*Roster) TagsCreateOnly ¶
TagsCreateOnly reports whether cfnType is a taggable type whose tags can be set only at create (tagging.tag_updatable false, or Tags among its create_only_properties), and whether the registry can answer at all: known is false for a type it never saw, one whose schema declared no tagging, and one that is not taggable. A marker written onto an existing object of such a type would replace it, so adoption refuses the type by name before any provider call (GitHub issue #2287; 67 of the pinned registry's 1035 taggable types, AWS::EC2::KeyPair and AWS::AppRunner::Service among them).