Resources¶
A resource implements provider operations with
runtime.TypedResource[In, Out, Config]. Lifecycle policy belongs in one
runtime.ResourceDefinition passed during registration.
type File struct {
Path string
Content string
}
type FileOutput struct {
Path string
Size int64
Exists bool
}
func fileDefinition() runtime.ResourceDefinition[
File,
*FileOutput,
runtime.NoConfig,
] {
path := runtime.InputField(func(input *File) *string {
return &input.Path
})
return runtime.ResourceDefinition[File, *FileOutput, runtime.NoConfig]{
SchemaVersion: 1,
Replace: runtime.Replacement[File, *FileOutput, runtime.NoConfig]{
Fields: []runtime.AnyInputField[File]{path},
},
}
}
func (f *File) Create(
ctx context.Context,
config runtime.NoConfig,
) (*FileOutput, error) {
return writeFile(f.Path, f.Content)
}
func (f *File) Read(
ctx context.Context,
config runtime.NoConfig,
prior runtime.Prior[File, *FileOutput, runtime.NoConfig],
) (*FileOutput, error) {
return readFile(prior.Inputs.Path, prior.Outputs)
}
func (f *File) Update(
ctx context.Context,
config runtime.NoConfig,
prior runtime.Prior[File, *FileOutput, runtime.NoConfig],
) (*FileOutput, error) {
return writeFile(f.Path, f.Content)
}
func (f *File) Delete(
ctx context.Context,
config runtime.NoConfig,
prior runtime.Prior[File, *FileOutput, runtime.NoConfig],
) error {
return removeFile(prior.Inputs.Path, prior.Observed)
}
Register the resource with its definition:
Resources: map[string]runtime.ResourceRegistration{
"file": runtime.MakeResource[File, *FileOutput, runtime.NoConfig](
fileDefinition(),
),
}
Use MakeResourceWith when each receiver needs a constructed client, fake, or
other external state. Pass the definition first and the constructor second.
Prior target information¶
runtime.Prior[In, Out, Config] contains:
Inputs, the recorded inputs for the managed target.Outputs, the outputs saved after the previous apply.Observed, the latest read result. It is zero duringRead, the saved plan-time observation duringUpdate, and the immediate observation duringDelete.Configuration, the configuration recorded for the managed target.
The separate Config method argument is the current configuration. Use it for
credentials, endpoints, timeouts, and other settings needed to perform the
operation. Use Prior.Configuration, Prior.Inputs, and Prior.Outputs to
identify the previously managed target.
Return runtime.ErrNotFound from Read only when the recorded target is
absent. The runtime then plans Create.
Definition¶
Every resource definition sets a positive SchemaVersion. It can also declare:
Migratefor older state entries.Validatefor checks that require decoded inputs or live configuration.Equalityfor field-specific semantic equality.Replacefor input, configuration, and drift replacement rules.StableIDfor an immutable provider object identifier.
Validate runs before create, update, or replacement mutations. For a
replacement it runs before the old target is deleted.
Validate: func(
ctx context.Context,
input File,
config runtime.NoConfig,
) error {
if input.Path == "" {
return errors.New("path is required")
}
return nil
},
Prefer schemas, defaults, and constraints for checks known from source.
Semantic equality¶
Use runtime.EqualBy when different input values mean the same thing to the
provider:
functionName := runtime.InputField(func(input *Alias) *string {
return &input.FunctionName
})
Equality: []runtime.InputEqualityRule[Alias]{
runtime.EqualBy(functionName, equivalentFunctionNameOrARN),
},
Equivalent desired values do not request update or replacement. A successful no-op stores the accepted value.
Replacement¶
Replacement.Fields lists input fields whose semantic changes always require
replacement. Replacement.Rules applies a predicate after a selected input
field changes.
capacity := runtime.InputField(func(input *Volume) *int64 {
return &input.Capacity
})
Replace: runtime.Replacement[Volume, *VolumeOutput, *Config]{
Rules: []runtime.ReplacementRule[Volume]{
runtime.ReplaceWhen(capacity, func(prior, desired int64) bool {
return desired < prior
}),
},
},
ConfigurationFields selects configuration changes that require replacement
for this resource. Credential changes otherwise produce a no-op when inputs and
remote state are unchanged.
Drift selects output changes that require replacement. A resource with drift
replacement rules must declare StableID:
generation := runtime.OutputField(func(output *BucketOutput) *int64 {
return &output.Generation
})
Replace: runtime.Replacement[Bucket, *BucketOutput, *Config]{
Drift: []runtime.DriftRule[*BucketOutput]{
runtime.ReplaceOnDrift(generation, func(recorded, observed int64) bool {
return recorded != observed
}),
},
},
StableID: func(_ Bucket, output *BucketOutput) (string, error) {
return output.ProviderID, nil
},
A stable ID must be non-empty, immutable for one provider object, and different for a later object that reuses the same name. The runtime checks it during planning and immediately before deletion.
Selectors are typed field references. Registration rejects invalid, duplicate, or overlapping selectors and nil callbacks.
When an input depends on an upstream output that may change, its comparisons wait until that output resolves. Equality and replacement callbacks receive concrete values. An unknown member makes its atomic collection pending; an explicit null is a concrete value.
The saved plan records the permitted operations and pending replacement checks. Apply resolves these after dependencies finish and executes the selected operation within the same apply. An unchanged upstream ID can therefore leave its dependent unchanged, or let a separate mutable change update it. A changed replacement field still replaces it. Confirmed inputs, replacement reasons, validation, and prior-target identity checks remain enforced.