addrs: ConfigCheckable type

Our existing addrs.Checkable represents a particular (possibly-dynamic)
object that can have checks associated with it.

This new addrs.ConfigCheckable represents static configuration objects
that can potentially generate addrs.Checkable objects.

The idea here is to allow us to predict from the configuration a set of
potential checkable object containers and then dynamically associate
the dynamic checkable objects with them as we make progress with planning.

This is intended for our integration of checks into the "terraform test"
testing harness, to be used instead of the weirdo builtin provider we were
using as a placeholder before we had first-class syntax for checks.
Test reporting tools find it helpful for there to be a consistent set of
test cases from one run to the next so that they can report on trends over
multiple runs, and so our ConfigCheckable addresses will serve as the
relatively-static "test case" that we'll then associate the dynamic checks
with, so that we can still talk about objects in the test result report
even if we end up not reaching them due to an upstream conditution failure.
This commit is contained in:
Martin Atkins 2022-06-14 16:18:56 -07:00
parent 746bd49723
commit d06cbfe6c8
3 changed files with 119 additions and 10 deletions

View File

@ -39,11 +39,25 @@ func (c Check) String() string {
// Checkable is an interface implemented by all address types that can contain
// condition blocks.
type Checkable interface {
UniqueKeyer
checkableSigil()
// Check returns the address of an individual check rule of a specified
// type and index within this checkable container.
Check(CheckType, int) Check
// ConfigCheckable returns the address of the configuration construct that
// this Checkable belongs to.
//
// Checkable objects can potentially be dynamically declared during a
// plan operation using constructs like resource for_each, and so
// ConfigCheckable gives us a way to talk about the static containers
// those dynamic objects belong to, in case we wish to group together
// dynamic checkable objects into their static checkable for reporting
// purposes.
ConfigCheckable() ConfigCheckable
String() string
}
@ -52,12 +66,6 @@ var (
_ Checkable = AbsOutputValue{}
)
type checkable struct {
}
func (c checkable) checkableSigil() {
}
// CheckType describes the category of check.
type CheckType int
@ -85,3 +93,24 @@ func (c CheckType) Description() string {
return "Condition"
}
}
// ConfigCheckable is an interfaces implemented by address types that represent
// configuration constructs that can have Checkable addresses associated with
// them.
//
// This address type therefore in a sense represents a container for zero or
// more checkable objects all declared by the same configuration construct,
// so that we can talk about these groups of checkable objects before we're
// ready to decide how many checkable objects belong to each one.
type ConfigCheckable interface {
UniqueKeyer
configCheckableSigil()
String() string
}
var (
_ ConfigCheckable = ConfigResource{}
_ ConfigCheckable = ConfigOutputValue{}
)

View File

@ -38,7 +38,6 @@ func (v OutputValue) Absolute(m ModuleInstance) AbsOutputValue {
// configuration. It is related to but separate from ModuleCallOutput, which
// represents a module output from the perspective of its parent module.
type AbsOutputValue struct {
checkable
Module ModuleInstance
OutputValue OutputValue
}
@ -73,6 +72,31 @@ func (v AbsOutputValue) Equal(o AbsOutputValue) bool {
return v.OutputValue == o.OutputValue && v.Module.Equal(o.Module)
}
func (v AbsOutputValue) ConfigOutputValue() ConfigOutputValue {
return ConfigOutputValue{
Module: v.Module.Module(),
OutputValue: v.OutputValue,
}
}
func (v AbsOutputValue) checkableSigil() {
// Output values are checkable
}
func (v AbsOutputValue) ConfigCheckable() ConfigCheckable {
// Output values are declared by "output" blocks in the configuration,
// represented as ConfigOutputValue.
return v.ConfigOutputValue()
}
func (v AbsOutputValue) UniqueKey() UniqueKey {
return absOutputValueUniqueKey(v.String())
}
type absOutputValueUniqueKey string
func (k absOutputValueUniqueKey) uniqueKeySigil() {}
func ParseAbsOutputValue(traversal hcl.Traversal) (AbsOutputValue, tfdiags.Diagnostics) {
path, remain, diags := parseModuleInstancePrefix(traversal)
if diags.HasErrors() {
@ -152,3 +176,31 @@ func (v AbsOutputValue) ModuleCallOutput() (ModuleInstance, ModuleCallInstanceOu
Name: v.OutputValue.Name,
}
}
// ConfigOutputValue represents a particular "output" block in the
// configuration, which might have many AbsOutputValue addresses associated
// with it at runtime if it belongs to a module that was called using
// "count" or "for_each".
type ConfigOutputValue struct {
Module Module
OutputValue OutputValue
}
func (v ConfigOutputValue) String() string {
if v.Module.IsRoot() {
return v.OutputValue.String()
}
return fmt.Sprintf("%s.%s", v.Module.String(), v.OutputValue.String())
}
func (v ConfigOutputValue) configCheckableSigil() {
// ConfigOutputValue is the ConfigCheckable for AbsOutputValue.
}
func (v ConfigOutputValue) UniqueKey() UniqueKey {
return configOutputValueUniqueKey(v.String())
}
type configOutputValueUniqueKey string
func (k configOutputValueUniqueKey) uniqueKeySigil() {}

View File

@ -210,7 +210,6 @@ func (r AbsResource) UniqueKey() UniqueKey {
// AbsResourceInstance is an absolute address for a resource instance under a
// given module path.
type AbsResourceInstance struct {
checkable
targetable
Module ModuleInstance
Resource ResourceInstance
@ -241,6 +240,15 @@ func (r AbsResourceInstance) ContainingResource() AbsResource {
}
}
// ConfigResource returns the address of the configuration block that declared
// this instance.
func (r AbsResourceInstance) ConfigResource() ConfigResource {
return ConfigResource{
Module: r.Module.Module(),
Resource: r.Resource.Resource,
}
}
// TargetContains implements Targetable by returning true if the given other
// address is equal to the receiver.
func (r AbsResourceInstance) TargetContains(other Targetable) bool {
@ -322,6 +330,14 @@ func (r AbsResourceInstance) Less(o AbsResourceInstance) bool {
}
}
// AbsResourceInstance is a Checkable
func (r AbsResourceInstance) checkableSigil() {}
func (r AbsResourceInstance) ConfigCheckable() ConfigCheckable {
// The ConfigCheckable for an AbsResourceInstance is its ConfigResource.
return r.ConfigResource()
}
type absResourceInstanceKey string
func (r AbsResourceInstance) UniqueKey() UniqueKey {
@ -393,10 +409,22 @@ func (r ConfigResource) Equal(o ConfigResource) bool {
return r.Module.Equal(o.Module) && r.Resource.Equal(o.Resource)
}
func (r ConfigResource) configMoveableSigil() {
// AbsResource is moveable
func (r ConfigResource) UniqueKey() UniqueKey {
return configResourceKey(r.String())
}
func (r ConfigResource) configMoveableSigil() {
// ConfigResource is moveable
}
func (r ConfigResource) configCheckableSigil() {
// ConfigResource represents a configuration object that declares checkable objects
}
type configResourceKey string
func (k configResourceKey) uniqueKeySigil() {}
// ResourceMode defines which lifecycle applies to a given resource. Each
// resource lifecycle has a slightly different address format.
type ResourceMode rune