Documentation
¶
Overview ¶
Package substrate is the contract GitHub issue #1118 asks for between this fork's live path and a provider family: one Substrate value per family (AWS, Kubernetes) that answers which marker surface a schema carries, how the marker is read off a live or planned object, how it is written, and which sweep client the family's provider block builds.
Before it, those answers were three dispatches that each asked the internal/live/markers predicates on their own: the projection's ownership read (markerSurfaceOf and markersOf), live-mv's surfaceOf, and live-import's ratifyOne carriers. Each was found missing a surface after the unit that should have covered it had merged (#1108, #1104, #1109). They now ask this package, and this package is the one place a new surface has to be taught. The completeness guard in internal/live/markers/seams_test.go measures the functions here the same way it measures every other seam, so a fourth surface fails every dispatch below until it is handled.
It is an extraction: every answer here is the answer the dispatch it replaced gave. GitHub issue #1589: the projection's ownership read used to ask a looser tag question than SurfaceOf (markers.HasTagsAttribute (since deleted)), kept apart in case the two ever disagreed on a real AWS or Kubernetes type. The 2026-09-26 decision package measured the disagreement empty at every pinned provider version, so the ownership read now asks SurfaceOf like every other caller and the second question is gone.
Index ¶
- Constants
- Variables
- func AddressInMarkers(surface markers.Surface) bool
- func CarrierPhrase(surface markers.Surface) string
- func CarriesAddress(surface markers.Surface) bool
- func CreateCollidesOnKey(surface markers.Surface) bool
- func CreatedObject(surface markers.Surface, created Created) string
- func KnownMarkersOf(surface markers.Surface, obj cty.Value) (map[string]string, bool)
- func KubernetesSweepAttrs(val cty.Value, ok bool) kubesweep.Attrs
- func ManualMarkFix(surface markers.Surface, created Created, want map[string]string, facts Facts) string
- func MarkerNoun(surface markers.Surface) string
- func MarkersOf(surface markers.Surface, obj cty.Value) (map[string]string, bool)
- func NotACarrier(providerType string, block *configschema.Block, typeName string) string
- func ObjectMetaShape(block *configschema.Block) (namespaced bool, ok bool)
- func PostCreateNeeded(surface markers.Surface, created Created, facts Facts) (string, bool)
- func SurfaceOf(block *configschema.Block) (markers.Surface, bool)
- func Sweeps(providerType string) bool
- type AWSCreateTagFacts
- type Created
- type Facts
- type Hold
- type HoldEvidence
- type HoldRecognition
- type IdentityComponent
- type LabelListSweeper
- type Substrate
- type Sweep
- type Sweeper
- type SynthesizedIdentity
- type Write
- type Writes
Constants ¶
const ManifestImportSyntax = "apiVersion=APIVERSION,kind=KIND,[namespace=NAMESPACE,]name=NAME"
ManifestImportSyntax is the provider's documented import id for a manifest object, the namespace segment present for a namespaced kind only.
Variables ¶
var All = []Substrate{Kubernetes, AWS}
All is every family, in the order a surface question asks them.
AWS is last, and that is load-bearing for Substrate.SynthesizeIdentity (GitHub issue #1586): the AWS answer is the identity-schema route, which claims every type, so a family with a convention of its own has to be asked before it. The surface questions are disjoint and do not care.
Functions ¶
func AddressInMarkers ¶
AddressInMarkers reports whether surface's marker map holds the tofu-address key, which is its family's [Substrate.AddressInMarkers]. False for the zero Surface.
func CarrierPhrase ¶
CarrierPhrase names where surface's marker map lives ("tags attribute", "metadata.labels map"), for the one refusal that has to tell an operator the provider returned no such map. Empty for the zero Surface, which has no map to be missing: a caller asks this only of a surface it read.
func CarriesAddress ¶
CarriesAddress reports whether an object on surface carries its block address, which is its family's Substrate.CarriesAddress. Both families do: AWS in the tofu-address tag, Kubernetes in the address annotation beside the estate label (GitHub issue #1641, step 3 of the ruling on #1605). False for the zero Surface.
On Kubernetes an object can still lack the annotation - one an older build created, or one migrated from stock state before live-import stamped it - so a reader that needs the address of one particular object asks that object, not this.
func CreateCollidesOnKey ¶
CreateCollidesOnKey reports whether, for a declared resource on surface, an object read at its identity means the resource's own create would be refused by the server as a duplicate: the first half of GitHub issue #1546's ruling, "its identity is a server-enforced unique key". False for the zero Surface.
True for the Kubernetes label surface only, and each family's answer says why its other surfaces are excluded ([kubernetes.CreateCollidesOnKey], [aws.CreateCollidesOnKey]).
func CreatedObject ¶
CreatedObject is [Substrate.CreatedObject] asked of surface's family. A surface with no family gets the object's id, the one attribute every provider's object has.
func KnownMarkersOf ¶
KnownMarkersOf is MarkersOf for a caller that must not read an unknown marker map as an empty one. It reports false when the object is null or unknown, when any carrier path reaches an unknown value (the map, or anything on the way to it), or when the surface reader itself reports false. A value beside the carrier being unknown, such as a planned object's metadata.resource_version, does not hide a marker that is known.
A carrier path that ends early at a null, or indexes past an empty list, is not unknown: the reader decides what that object carries.
func KubernetesSweepAttrs ¶
KubernetesSweepAttrs reads the connection arguments the Kubernetes sweep understands off the evaluated provider block. A marked value is left unread rather than unmarked - the same rule internal/command's statelessProviders.region applies to a sensitive region - EXCEPT for the three arguments that are themselves the credential, which are unmarked and read: see secret below, and GitHub issue #1527 for what leaving them unread cost. It moved here from internal/command with the sweep-client choice (GitHub issue #1118).
func ManualMarkFix ¶
func ManualMarkFix(surface markers.Surface, created Created, want map[string]string, facts Facts) string
ManualMarkFix is [Substrate.ManualMarkFix] asked of surface's family. A surface with no family - the zero Surface, or one For does not recognise - gets the generic sentence naming only the markers, since there is no family to name a command for.
func MarkerNoun ¶
MarkerNoun is what one entry of surface's marker map is called ("tag", "label"), or "marker" for the zero Surface.
func MarkersOf ¶
MarkersOf reads the marker map off an object from wherever surface keeps it. The second return is the surface reader's own: false means the object has no such map at all, which on a type whose schema declares one is a provider bug and never a licence to adopt. False for the zero Surface.
func NotACarrier ¶
func NotACarrier(providerType string, block *configschema.Block, typeName string) string
NotACarrier explains why a resource type of provider providerType has nowhere to carry an ownership marker, in its family's own words: the AWS "no tags map this configuration can set" (or the tags map the marker vocabulary cannot round-trip, markers.NotAMarkerSurface), and the Kubernetes "no metadata.labels map". A provider with no family gets a sentence that names no carrier rather than the AWS one. The caller has already established the type carries no surface (SurfaceOf false).
func ObjectMetaShape ¶
func ObjectMetaShape(block *configschema.Block) (namespaced bool, ok bool)
ObjectMetaShape reports whether block carries Kubernetes object metadata, and whether the kind it describes is namespaced. Read from the schema, never from a type-name list, for the same reason markers.Taggable is.
It is the shape internal/live/identity's metadata.go documents: a "metadata" nested block of list nesting with at most one item, holding a settable "name", a computed "uid", a settable "labels" map, and - for a namespaced kind - a settable "namespace".
func PostCreateNeeded ¶
PostCreateNeeded is [Substrate.PostCreateNeeded] asked of surface's family. False for the zero Surface.
func SurfaceOf ¶
func SurfaceOf(block *configschema.Block) (markers.Surface, bool)
SurfaceOf is the marker surface a resource type's schema carries, or false when it has none. The families' predicates are disjoint by construction (markers.LabelSurface and markers.ManifestSurface each refuse a markers.Taggable type, and the manifest shape refuses a metadata block), so the order they are asked in cannot decide an answer.
This is the question live-mv's surface switch and live-import's carrier choice asked.
func Sweeps ¶
Sweeps reports whether providerType names a family whose own sweep leg finds its objects independently of internal/live/identity's admission table (GitHub issue #1581): a type belonging to such a family needs no row there to be found again once its last block is removed.
Only Kubernetes qualifies today (SweepLabelList): its leg lists every kind the cluster serves and joins the result against the estate's objects, drawing its universe from the provider and the cluster rather than from the table. AWS's own sweep (SweepTaggingIndex) is that same admission table read a different way, so a type with no row gets nothing extra from it, and neither does an unregistered provider ForProvider does not recognise at all.
This is the question internal/live/identity's no-orphan-recovery warning needs, and it is asked by provider - the resource's own resolved provider configuration, never by a type's schema shape. A type can share a Kubernetes-shaped schema (an object-metadata block, say) with an unrelated provider's type by coincidence; only the provider says which sweep leg, if any, will actually look for it again.
Types ¶
type AWSCreateTagFacts ¶
type AWSCreateTagFacts interface {
CloudControlTypeOrService(tfType string) (string, bool)
TagsAfterCreate(cfnType string) bool
}
AWSCreateTagFacts is the AWS family's entry in Facts (GitHub issue #1708): live/mapping.json's Terraform-to-CloudFormation join and live/registry.json's tagging.tag_on_create. *internal/live/registry.Roster implements it, nil included (every answer false); internal/command puts the embedded roster under AWS's name. An interface so this package stays below the registry.
type Created ¶
type Created struct {
Addr addrs.AbsResourceInstance
Provider addrs.AbsProviderConfig
// Object is the object ApplyResourceChange returned; cty.NilVal where
// the question is asked before the create.
Object cty.Value
}
Created is the instance a post-create question or write is about: its address, the provider configuration it was applied under, and the object the provider returned. internal/live/projection's CreatedInstance, which the post-create writer is handed, is this type.
type Facts ¶
Facts is what the run knows about types beyond their schemas, keyed by family Substrate.Name and built once per run by the command layer (for AWS, the registry roster: see AWSCreateTagFacts). Each family reads its own entry and asserts it to the shape it owns; an absent entry, a nil one, or one of another shape reads as a family with no facts, which is an ordinary state (a run whose embedded artifacts did not parse).
type Hold ¶
type Hold struct {
// Controller is the controller that holds it: ACK, Crossplane, Helm.
Controller string
// HeldBy names the controller and the object of its that holds this
// one, in one line an operator can act on: "Helm release web/web",
// "ACK s3 controller (s3-v1.0.14), custom resource in namespace team-a".
HeldBy string
}
Hold is a family's answer for a held object.
func ControllerHeld ¶
func ControllerHeld(ev HoldEvidence) (Hold, bool)
ControllerHeld asks every family in All's order whether the object ev was read from is held by a controller, and the first to answer decides. The families read disjoint fields of ev, so the order cannot decide an answer today.
type HoldEvidence ¶
type HoldEvidence struct {
// Tags are an AWS resource's tags.
Tags map[string]string
// Annotations are a Kubernetes object's metadata.annotations.
Annotations map[string]string
}
HoldEvidence is what a sweep leg read off one live object that a family may recognise a controller's hold in. A leg fills what its object carries and leaves the rest empty; each family reads only its own field.
type HoldRecognition ¶
HoldRecognition describes how a family recognises a held object, for the capability matrix (live/substrates.json). Keys empty means the family recognises no controller at all.
type IdentityComponent ¶
type IdentityComponent struct {
Literal string
Attrs []string
Block string
Path []string
OmitIfAbsent bool
SameNameIdentity bool
}
IdentityComponent is the subset of identity.Component a family's synthesis sets. Each field means what the identity package's field of the same name means, except SameNameIdentity, which stands for identity.SameNameIdentity in IdentityAttr.
type LabelListSweeper ¶
LabelListSweeper is the Kubernetes family's client: the cluster client built from the provider block, whose methods it carries (kubesweep.Sweeper, kubesweep.LabelPatcher).
func (LabelListSweeper) SweepKind ¶
func (LabelListSweeper) SweepKind() Sweep
SweepKind is SweepLabelList.
type Substrate ¶
type Substrate interface {
// Name is the family's name, which is also the provider type name
// [ForProvider] matches.
Name() string
// Surfaces are the marker surfaces this family's types carry. No
// surface belongs to two families.
Surfaces() []markers.Surface
// SurfaceOf is the surface a resource type's schema carries when it is
// one of this family's, read off the schema and never off the type
// name.
SurfaceOf(block *configschema.Block) (markers.Surface, bool)
// MarkersOf reads the marker map off an object from wherever surface,
// one of this family's, keeps it.
MarkersOf(surface markers.Surface, obj cty.Value) (map[string]string, bool)
// Writes is how surface's marker, one of this family's, is written.
Writes(surface markers.Surface) Writes
// CarriesAddress is whether this family's objects carry the block
// address beside tofu-estate, so that the sweep can bind a live object
// back to the block that made it: the AWS tofu-address tag, or the
// Kubernetes address annotation (GitHub issues #1639 to #1641). Where
// the address sits is [Substrate.AddressInMarkers]'s question.
CarriesAddress() bool
// Sweep is which sweep client the family's provider block builds.
Sweep() Sweep
// NewSweeper builds the family's estate-sweep client from its provider
// block's evaluated configuration (ok false when the run holds none):
// nil with no error for a family whose sweep runs through the
// configured provider itself ([SweepTaggingIndex]), and the error for a
// block the client cannot be built from. The client is a [Sweeper],
// never a family's concrete type (GitHub issue #1580).
NewSweeper(providerConfig cty.Value, ok bool) (Sweeper, error)
// SynthesizeIdentity is how this family's schema identifies an
// instance of a type the ratified table does not cover, or false when
// the schema is not one of this family's shapes. Asked in [All]'s
// order by internal/live/identity's synthesizeTypeIdentity, and the
// first family to answer decides.
SynthesizeIdentity(typeName string, schema providers.Schema) (SynthesizedIdentity, bool)
// contains filtered or unexported methods
}
Substrate is one provider family's answers. There is one value per family (AWS, Kubernetes), each in its own file, and the package-level functions below dispatch over All. A family answers only for its own surfaces; the dispatch is what answers for a schema or a surface whose family is not yet known.
The split is also what keeps the completeness guard (internal/live/markers/seams_test.go) able to see the callers. The guard credits a caller in another package with the surfaces a callee references itself, and the dispatch functions here reference none: they reach the markers predicates only through the family methods. So a caller that asks SurfaceOf and then acts per surface handles exactly the markers.Surface constants it names, and one that forgets a surface is red.
var AWS Substrate = aws{}
AWS is the hashicorp/aws family: tofu-estate and tofu-address in a settable top-level tags map.
var Kubernetes Substrate = kubernetes{}
Kubernetes is the hashicorp/kubernetes family: the tofu-estate label, in metadata[0].labels or, for kubernetes_manifest, in manifest.metadata.labels, with the block address in the markers.AddressAnnotation annotation beside it (#1639).
func ForProvider ¶
ForProvider is the family a provider type name belongs to ("aws", "kubernetes"). It matches the type name alone, which is what every provider-family check it replaced asked.
type Sweep ¶
type Sweep string
Sweep is which estate-sweep client a family's provider block builds.
const ( // SweepTaggingIndex is the AWS sweep: the discovery legs (the // Resource Groups Tagging API index, the provider's own list // resources, Cloud Control, direct reads), all driven through the // configured provider itself, so no client is built from the block. SweepTaggingIndex Sweep = "tagging-index" // SweepLabelList is the Kubernetes sweep: one label-selected list per // served kind, through a cluster client built from the provider // block's own connection arguments (Substrate.NewSweeper). SweepLabelList Sweep = "label-list" )
type Sweeper ¶
type Sweeper interface {
SweepKind() Sweep
}
Sweeper is a family's estate-sweep client as Substrate.NewSweeper builds it from the provider block. SweepKind is the sweep it serves, its family's own Substrate.Sweep: internal/live/discovery pairs a client with the leg that lists through it by that property, never by the family's name, so a third family's client plugs in by naming a sweep and a leg serving it.
type SynthesizedIdentity ¶
type SynthesizedIdentity struct {
// FromIdentitySchema: the provider's own resource identity schema
// decides, through internal/live/identity's identity-schema route (which
// reads the full schema map and the configuration signal, neither of
// which a family sees). Every other field is empty when it is set.
FromIdentitySchema bool
// NonAWSProvider is identity.TypeIdentity's field of the same name.
NonAWSProvider bool
// Components, ImportSyntax and IdentityAttrs are the entry's, in
// identity.TypeIdentity's terms.
Components []IdentityComponent
ImportSyntax string
IdentityAttrs []string
}
SynthesizedIdentity is a family's reading of one type's identity from its schema.
type Write ¶
type Write string
Write is how a marker reaches a live object.
const ( // WriteInCreate: the marker rides in the create call's own arguments, // put there by the node-path stamp (internal/live/projection). WriteInCreate Write = "in-create" // WriteTagsPlan: a plan-then-apply through the provider that changes // the tags map and nothing else, refused if it would change anything // more (internal/live/liveimport's tags.go, internal/live/mv's // rewrite.go). WriteTagsPlan Write = "tags-only-plan" // WriteLabelsPlan: the same, confined to metadata[0].labels // ([markers.WithLabels]; liveimport's labels.go, mv's label.go). WriteLabelsPlan Write = "labels-only-plan" // WriteAPIPatch: one merge patch of the label straight to the API // server under the caller's own credential, sent first with // dryRun=All and refused if the answer changes anything beyond the // labels (kubesweep.LabelPatcher; liveimport's manifest.go, ruled on // #1109 and #1104). WriteAPIPatch Write = "api-patch" )
const ( // WriteTaggingAPI: the Resource Groups Tagging API's TagResources, // addressed by the arn the provider returned, issued after the create // of a type whose create call cannot carry tags (live/registry.json's // tag_on_create false, GitHub issue #1084). WriteTaggingAPI Write = "tagging-api" // WriteNeverNeeded: the create call always carries the marker, so no // write follows it. Named rather than left empty so that a family that // has not answered is distinguishable from one that answered "never". WriteNeverNeeded Write = "never-needed" )
type Writes ¶
type Writes struct {
// Create is a resource this run creates.
Create Write
// Adopt is an existing object a migration (live-import -approve) or a
// move between estates (live-mv -from-estate) marks.
Adopt Write
// PostCreate is a resource this run creates whose create call cannot
// carry the marker, so it is written onto the object once the create
// returns (GitHub issue #1587, [WriteTaggingAPI], [WriteNeverNeeded]).
PostCreate Write
}
Writes is how one surface's marker is written, by occasion.