Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Dynamic Type/Attributes/Parameters #147

Closed
paddycarver opened this issue Sep 8, 2021 · 12 comments · Fixed by #931
Closed

Support Dynamic Type/Attributes/Parameters #147

paddycarver opened this issue Sep 8, 2021 · 12 comments · Fixed by #931
Assignees
Labels
enhancement New feature or request tf-devex-triage Terraform DevEx project tracking types Issues and pull requests about our types abstraction and implementations.
Milestone

Comments

@paddycarver
Copy link
Contributor

Module version

v0.3.0

Use-cases

There are sometimes occasions in which provider developers want to use unstructured output or input. See, for example, the kubernetes_manifest implementation, or the tfe_outputs data source. See also hashicorp/terraform-provider-external#76. Allowing people to do this from the framework offers a friendlier interface for this functionality than plugin-go.

Attempted Solutions

Traditionally, this has been achieved through muxing in plugin-go.

Proposal

If we add a types.DynamicType and types.Dynamic implementation, we can natively support this:

type DynamicType struct{}

func (d DynamicType) TerraformType(_ context.Context) tftypes.Type {
  return tftypes.DynamicPseudoType
}

func (d DynamicType) ValueFromTerraform(_ context.Context, val tftypes.Value) (attr.Value, error) {
  return Dynamic{Value: val}, nil
}

// ... etc.

type Dynamic struct{
  Value tftypes.Value
}

// ... etc
@paddycarver paddycarver added the enhancement New feature or request label Sep 8, 2021
@apparentlymart
Copy link
Member

The design you've shown here does seem like it would neatly sidestep one of the shortcomings of the cty design: you've declared "dynamic" as a real type which has a value of some other concrete (but dynamically-chosen) type inside of it, whereas cty.DynamicPseudoType is only a type constraint and there are never any concrete values of that type. (type constraint vs. type confusion is a common hazard in practical use of cty.)

The downside of such a design is that producers and consumers of such values will presumably need to explicitly wrap and unwrap the values in the "dynamic" container, which is less convenient but more explicit than just being given a value and inspecting it directly to find out what type it has.

Overall I think your approach here is a net win in that the explicitness resolves an ambiguity in the cty design which commonly leads to misunderstandings and therefore bugs, and thus the extra wrapping/unwrapping pays for itself.

@paddycarver paddycarver added the types Issues and pull requests about our types abstraction and implementations. label Sep 21, 2021
@m13t
Copy link

m13t commented Nov 11, 2021

I’m interested to utilise dynamic types for an internal provider we have. If I try to create a custom type using dynamic pseudo type at the moment however, the runtime plan fails as it states types don’t match. Is there additional inner workings that need to be updated to support dynamic pseudo type constraint?

@paddycarver
Copy link
Contributor Author

There very well may be assumptions that don't hold up in the face of DPT that we'll need to fix. We haven't exercised that code path yet. If you post your code and the error you're getting, we may be able to tell you more.

@m13t
Copy link

m13t commented Nov 11, 2021

Sure I’ll get a concise example together but it’s mostly based on the above proposal.

@sebhoss
Copy link

sebhoss commented Oct 26, 2022

I have some error & workaround to share. The error message looks like this:

!!!!!!!!!!!!!!!!!!!!!!!!!!! TERRAFORM CRASH !!!!!!!!!!!!!!!!!!!!!!!!!!!!

Terraform crashed! This is always indicative of a bug within Terraform.
Please report the crash with Terraform[1] so that we can fix this.

When reporting bugs, please include your terraform version, the stack trace
shown below, and any additional information which may help replicate the issue.

[1]: https://github.com/hashicorp/terraform/issues

!!!!!!!!!!!!!!!!!!!!!!!!!!! TERRAFORM CRASH !!!!!!!!!!!!!!!!!!!!!!!!!!!!

inconsistent list element types (cty.Object(map[string]cty.Type{"args":cty.List(cty.String), "command":cty.List(cty.String), "env":cty.List(cty.Object(map[string]cty.Type{"name":cty.String, "value":cty.String, "value_from":cty.Object(map[string]cty.Type{"config_map_key_ref":cty.Object(map[string]cty.Type{"key":cty.String, "name":cty.String, "optional":cty.Bool}), "field_ref":cty.Object(map[string]cty.Type{"api_version":cty.String, "field_path":cty.String}), "resource_field_ref":cty.Object(map[string]cty.Type{"container_name":cty.String, "divisor":cty.String, "resource":cty.String}), "secret_key_ref":cty.Object(map[string]cty.Type{"key":cty.String, "name":cty.String, "optional":cty.Bool})})})), "env_from":cty.List(cty.Object(map[string]cty.Type{"config_map_ref":cty.Object(map[string]cty.Type{"name":cty.String, "optional":cty.Bool}), "prefix":cty.String, "secret_ref":cty.Object(map[string]cty.Type{"name":cty.String, "optional":cty.Bool})})), "image":cty.String, "image_pull_policy":cty.String, "lifecycle":cty.Object(map[string]cty.Type{"post_start":cty.Object(map[string]cty.Type{"exec":cty.Object(map[string]cty.Type{"command":cty.List(cty.String)}), "http_get":cty.Object(map[string]cty.Type{"host":cty.String, "http_headers":cty.List(cty.Object(map[string]cty.Type{"name":cty.String, "value":cty.String})), "path":cty.String, "port":cty.DynamicPseudoType, "scheme":cty.String}), "tcp_socket":cty.Object(map[string]cty.Type{"host":cty.String, "port":cty.DynamicPseudoType})}), "pre_stop":cty.Object(map[string]cty.Type{"exec":cty.Object(map[string]cty.Type{"command":cty.List(cty.String)}), "http_get":cty.Object(map[string]cty.Type{"host":cty.String, "http_headers":cty.List(cty.Object(map[string]cty.Type{"name":cty.String, "value":cty.String})), "path":cty.String, "port":cty.DynamicPseudoType, "scheme":cty.String}), "tcp_socket":cty.Object(map[string]cty.Type{"host":cty.String, "port":cty.DynamicPseudoType})})}), "liveness_probe":cty.Object(map[string]cty.Type{"exec":cty.Object(map[string]cty.Type{"command":cty.List(cty.String)}), "failure_threshold":cty.Number, "grpc":cty.Object(map[string]cty.Type{"port":cty.Number, "service":cty.String}), "http_get":cty.Object(map[string]cty.Type{"host":cty.String, "http_headers":cty.List(cty.Object(map[string]cty.Type{"name":cty.String, "value":cty.String})), "path":cty.String, "port":cty.Number, "scheme":cty.String}), "initial_delay_seconds":cty.Number, "period_seconds":cty.Number, "success_threshold":cty.Number, "tcp_socket":cty.Object(map[string]cty.Type{"host":cty.String, "port":cty.DynamicPseudoType}), "termination_grace_period_seconds":cty.Number, "timeout_seconds":cty.Number}), "name":cty.String, "ports":cty.List(cty.Object(map[string]cty.Type{"container_port":cty.Number, "host_ip":cty.String, "host_port":cty.Number, "name":cty.String, "protocol":cty.String})), "readiness_probe":cty.Object(map[string]cty.Type{"exec":cty.Object(map[string]cty.Type{"command":cty.List(cty.String)}), "failure_threshold":cty.Number, "grpc":cty.Object(map[string]cty.Type{"port":cty.Number, "service":cty.String}), "http_get":cty.Object(map[string]cty.Type{"host":cty.String, "http_headers":cty.List(cty.Object(map[string]cty.Type{"name":cty.String, "value":cty.String})), "path":cty.String, "port":cty.Number, "scheme":cty.String}), "initial_delay_seconds":cty.Number, "period_seconds":cty.Number, "success_threshold":cty.Number, "tcp_socket":cty.Object(map[string]cty.Type{"host":cty.String, "port":cty.DynamicPseudoType}), "termination_grace_period_seconds":cty.Number, "timeout_seconds":cty.Number}), "resources":cty.Object(map[string]cty.Type{"limits":cty.Map(cty.String), "requests":cty.Map(cty.String)}), "security_context":cty.Object(map[string]cty.Type{"allow_privilege_escalation":cty.Bool, "capabilities":cty.Object(map[string]cty.Type{"add":cty.List(cty.String), "drop":cty.List(cty.String)}), "privileged":cty.Bool, "proc_mount":cty.String, "read_only_root_filesystem":cty.Bool, "run_as_group":cty.Number, "run_as_non_root":cty.Bool, "run_as_user":cty.Number, "se_linux_options":cty.Object(map[string]cty.Type{"level":cty.String, "role":cty.String, "type":cty.String, "user":cty.String}), "seccomp_profile":cty.Object(map[string]cty.Type{"localhost_profile":cty.String, "type":cty.String}), "windows_options":cty.Object(map[string]cty.Type{"gmsa_credential_spec":cty.String, "gmsa_credential_spec_name":cty.String, "host_process":cty.Bool, "run_as_user_name":cty.String})}), "startup_probe":cty.Object(map[string]cty.Type{"exec":cty.Object(map[string]cty.Type{"command":cty.List(cty.String)}), "failure_threshold":cty.Number, "grpc":cty.Object(map[string]cty.Type{"port":cty.Number, "service":cty.String}), "http_get":cty.Object(map[string]cty.Type{"host":cty.String, "http_headers":cty.List(cty.Object(map[string]cty.Type{"name":cty.String, "value":cty.String})), "path":cty.String, "port":cty.DynamicPseudoType, "scheme":cty.String}), "initial_delay_seconds":cty.Number, "period_seconds":cty.Number, "success_threshold":cty.Number, "tcp_socket":cty.Object(map[string]cty.Type{"host":cty.String, "port":cty.DynamicPseudoType}), "termination_grace_period_seconds":cty.Number, "timeout_seconds":cty.Number}), "stdin":cty.Bool, "stdin_once":cty.Bool, "termination_message_path":cty.String, "termination_message_policy":cty.String, "tty":cty.Bool, "volume_devices":cty.List(cty.Object(map[string]cty.Type{"device_path":cty.String, "name":cty.String})), "volume_mounts":cty.List(cty.Object(map[string]cty.Type{"mount_path":cty.String, "mount_propagation":cty.String, "name":cty.String, "read_only":cty.Bool, "sub_path":cty.String, "sub_path_expr":cty.String})), "working_dir":cty.String}) then cty.Object(map[string]cty.Type{"args":cty.List(cty.String), "command":cty.List(cty.String), "env":cty.List(cty.Object(map[string]cty.Type{"name":cty.String, "value":cty.String, "value_from":cty.Object(map[string]cty.Type{"config_map_key_ref":cty.Object(map[string]cty.Type{"key":cty.String, "name":cty.String, "optional":cty.Bool}), "field_ref":cty.Object(map[string]cty.Type{"api_version":cty.String, "field_path":cty.String}), "resource_field_ref":cty.Object(map[string]cty.Type{"container_name":cty.String, "divisor":cty.String, "resource":cty.String}), "secret_key_ref":cty.Object(map[string]cty.Type{"key":cty.String, "name":cty.String, "optional":cty.Bool})})})), "env_from":cty.List(cty.Object(map[string]cty.Type{"config_map_ref":cty.Object(map[string]cty.Type{"name":cty.String, "optional":cty.Bool}), "prefix":cty.String, "secret_ref":cty.Object(map[string]cty.Type{"name":cty.String, "optional":cty.Bool})})), "image":cty.String, "image_pull_policy":cty.String, "lifecycle":cty.Object(map[string]cty.Type{"post_start":cty.Object(map[string]cty.Type{"exec":cty.Object(map[string]cty.Type{"command":cty.List(cty.String)}), "http_get":cty.Object(map[string]cty.Type{"host":cty.String, "http_headers":cty.List(cty.Object(map[string]cty.Type{"name":cty.String, "value":cty.String})), "path":cty.String, "port":cty.DynamicPseudoType, "scheme":cty.String}), "tcp_socket":cty.Object(map[string]cty.Type{"host":cty.String, "port":cty.DynamicPseudoType})}), "pre_stop":cty.Object(map[string]cty.Type{"exec":cty.Object(map[string]cty.Type{"command":cty.List(cty.String)}), "http_get":cty.Object(map[string]cty.Type{"host":cty.String, "http_headers":cty.List(cty.Object(map[string]cty.Type{"name":cty.String, "value":cty.String})), "path":cty.String, "port":cty.DynamicPseudoType, "scheme":cty.String}), "tcp_socket":cty.Object(map[string]cty.Type{"host":cty.String, "port":cty.DynamicPseudoType})})}), "liveness_probe":cty.Object(map[string]cty.Type{"exec":cty.Object(map[string]cty.Type{"command":cty.List(cty.String)}), "failure_threshold":cty.Number, "grpc":cty.Object(map[string]cty.Type{"port":cty.Number, "service":cty.String}), "http_get":cty.Object(map[string]cty.Type{"host":cty.String, "http_headers":cty.List(cty.Object(map[string]cty.Type{"name":cty.String, "value":cty.String})), "path":cty.String, "port":cty.DynamicPseudoType, "scheme":cty.String}), "initial_delay_seconds":cty.Number, "period_seconds":cty.Number, "success_threshold":cty.Number, "tcp_socket":cty.Object(map[string]cty.Type{"host":cty.String, "port":cty.DynamicPseudoType}), "termination_grace_period_seconds":cty.Number, "timeout_seconds":cty.Number}), "name":cty.String, "ports":cty.List(cty.Object(map[string]cty.Type{"container_port":cty.Number, "host_ip":cty.String, "host_port":cty.Number, "name":cty.String, "protocol":cty.String})), "readiness_probe":cty.Object(map[string]cty.Type{"exec":cty.Object(map[string]cty.Type{"command":cty.List(cty.String)}), "failure_threshold":cty.Number, "grpc":cty.Object(map[string]cty.Type{"port":cty.Number, "service":cty.String}), "http_get":cty.Object(map[string]cty.Type{"host":cty.String, "http_headers":cty.List(cty.Object(map[string]cty.Type{"name":cty.String, "value":cty.String})), "path":cty.String, "port":cty.Number, "scheme":cty.String}), "initial_delay_seconds":cty.Number, "period_seconds":cty.Number, "success_threshold":cty.Number, "tcp_socket":cty.Object(map[string]cty.Type{"host":cty.String, "port":cty.DynamicPseudoType}), "termination_grace_period_seconds":cty.Number, "timeout_seconds":cty.Number}), "resources":cty.Object(map[string]cty.Type{"limits":cty.Map(cty.String), "requests":cty.Map(cty.String)}), "security_context":cty.Object(map[string]cty.Type{"allow_privilege_escalation":cty.Bool, "capabilities":cty.Object(map[string]cty.Type{"add":cty.List(cty.String), "drop":cty.List(cty.String)}), "privileged":cty.Bool, "proc_mount":cty.String, "read_only_root_filesystem":cty.Bool, "run_as_group":cty.Number, "run_as_non_root":cty.Bool, "run_as_user":cty.Number, "se_linux_options":cty.Object(map[string]cty.Type{"level":cty.String, "role":cty.String, "type":cty.String, "user":cty.String}), "seccomp_profile":cty.Object(map[string]cty.Type{"localhost_profile":cty.String, "type":cty.String}), "windows_options":cty.Object(map[string]cty.Type{"gmsa_credential_spec":cty.String, "gmsa_credential_spec_name":cty.String, "host_process":cty.Bool, "run_as_user_name":cty.String})}), "startup_probe":cty.Object(map[string]cty.Type{"exec":cty.Object(map[string]cty.Type{"command":cty.List(cty.String)}), "failure_threshold":cty.Number, "grpc":cty.Object(map[string]cty.Type{"port":cty.Number, "service":cty.String}), "http_get":cty.Object(map[string]cty.Type{"host":cty.String, "http_headers":cty.List(cty.Object(map[string]cty.Type{"name":cty.String, "value":cty.String})), "path":cty.String, "port":cty.DynamicPseudoType, "scheme":cty.String}), "initial_delay_seconds":cty.Number, "period_seconds":cty.Number, "success_threshold":cty.Number, "tcp_socket":cty.Object(map[string]cty.Type{"host":cty.String, "port":cty.DynamicPseudoType}), "termination_grace_period_seconds":cty.Number, "timeout_seconds":cty.Number}), "stdin":cty.Bool, "stdin_once":cty.Bool, "termination_message_path":cty.String, "termination_message_policy":cty.String, "tty":cty.Bool, "volume_devices":cty.List(cty.Object(map[string]cty.Type{"device_path":cty.String, "name":cty.String})), "volume_mounts":cty.List(cty.Object(map[string]cty.Type{"mount_path":cty.String, "mount_propagation":cty.String, "name":cty.String, "read_only":cty.Bool, "sub_path":cty.String, "sub_path_expr":cty.String})), "working_dir":cty.String}))
goroutine 74 [running]:
runtime/debug.Stack()
	/usr/local/go/src/runtime/debug/stack.go:24 +0x65
runtime/debug.PrintStack()
	/usr/local/go/src/runtime/debug/stack.go:16 +0x19
github.com/hashicorp/terraform/internal/logging.PanicHandler()
	/go/pkg/mod/github.com/hashicorp/terraform@v1.3.0-dev.0.20221020194023-730756eca23f/internal/logging/panic.go:55 +0x153
panic({0x21e7200, 0xc003e71bd0})
	/usr/local/go/src/runtime/panic.go:890 +0x262
github.com/zclconf/go-cty/cty.ListVal({0xc0086e4700, 0x2, 0xc0036d8c80?})
	/go/pkg/mod/github.com/zclconf/go-cty@v1.11.1/cty/value_init.go:166 +0x42e
github.com/zclconf/go-cty/cty/msgpack.unmarshalList(0x2b6f590?, {{0x2b6f600?, 0xc0036d8c80?}}, {0xc0086e46c0?, 0x4, 0x4})
	/go/pkg/mod/github.com/zclconf/go-cty@v1.11.1/cty/msgpack/unmarshal.go:161 +0x4bc
github.com/zclconf/go-cty/cty/msgpack.unmarshal(0x221ec00?, {{0x2b6f590?, 0xc0036d8c90?}}, {0xc0086e46c0?, 0x4, 0x4})
	/go/pkg/mod/github.com/zclconf/go-cty@v1.11.1/cty/msgpack/unmarshal.go:52 +0x41a
github.com/zclconf/go-cty/cty/msgpack.unmarshalObject(0xc0062f7f88?, 0xc0064355c0, {0xc0086e46c0?, 0x3, 0x4})
	/go/pkg/mod/github.com/zclconf/go-cty@v1.11.1/cty/msgpack/unmarshal.go:297 +0x4b6
github.com/zclconf/go-cty/cty/msgpack.unmarshal(0x221ec00?, {{0x2b6f600?, 0xc0036d9c60?}}, {0xc0086e46c0?, 0x3, 0x4})
	/go/pkg/mod/github.com/zclconf/go-cty@v1.11.1/cty/msgpack/unmarshal.go:60 +0x555
github.com/zclconf/go-cty/cty/msgpack.unmarshalObject(0xc0062f7f88?, 0xc0064355f0, {0xc000627e80?, 0x2, 0x2})
	/go/pkg/mod/github.com/zclconf/go-cty@v1.11.1/cty/msgpack/unmarshal.go:297 +0x4b6
github.com/zclconf/go-cty/cty/msgpack.unmarshal(0x221ec00?, {{0x2b6f600?, 0xc0036d9c70?}}, {0xc000627e80?, 0x2, 0x2})
	/go/pkg/mod/github.com/zclconf/go-cty@v1.11.1/cty/msgpack/unmarshal.go:60 +0x555
github.com/zclconf/go-cty/cty/msgpack.unmarshalObject(0xc0062f7f88?, 0xc006435650, {0xc0036d9ca0?, 0x1, 0x1})
	/go/pkg/mod/github.com/zclconf/go-cty@v1.11.1/cty/msgpack/unmarshal.go:297 +0x4b6
github.com/zclconf/go-cty/cty/msgpack.unmarshal(0x221ec00?, {{0x2b6f600?, 0xc0036d9c80?}}, {0xc0036d9ca0?, 0x1, 0x1})
	/go/pkg/mod/github.com/zclconf/go-cty@v1.11.1/cty/msgpack/unmarshal.go:60 +0x555
github.com/zclconf/go-cty/cty/msgpack.unmarshalObject(0xc0062f7f88?, 0xc0064356b0, {0x0?, 0x0, 0x0})
	/go/pkg/mod/github.com/zclconf/go-cty@v1.11.1/cty/msgpack/unmarshal.go:297 +0x4b6
github.com/zclconf/go-cty/cty/msgpack.unmarshal(0xc0062f7f88?, {{0x2b6f600?, 0xc0036d9c90?}}, {0x0?, 0x0, 0x0})
	/go/pkg/mod/github.com/zclconf/go-cty@v1.11.1/cty/msgpack/unmarshal.go:60 +0x555
github.com/zclconf/go-cty/cty/msgpack.Unmarshal({0xc003e33300, 0x12fa, 0x1300}, {{0x2b6f600?, 0xc0036d9c90?}})
	/go/pkg/mod/github.com/zclconf/go-cty@v1.11.1/cty/msgpack/unmarshal.go:22 +0x105
github.com/hashicorp/terraform/internal/plugin6.decodeDynamicValue(0x0?, {{0x2b6f600?, 0xc0036d9c90?}})
	/go/pkg/mod/github.com/hashicorp/terraform@v1.3.0-dev.0.20221020194023-730756eca23f/internal/plugin6/grpc_provider.go:688 +0x97
github.com/hashicorp/terraform/internal/plugin6.(*GRPCProvider).PlanResourceChange(_, {{0xc000056db0, 0x18}, {{{0x2b6f600, 0xc007dd2380}}, {0x0, 0x0}}, {{{0x2b6f600, 0xc00893fa10}}, {0x221f740, ...}}, ...})
	/go/pkg/mod/github.com/hashicorp/terraform@v1.3.0-dev.0.20221020194023-730756eca23f/internal/plugin6/grpc_provider.go:466 +0x859
github.com/hashicorp/terraform/internal/terraform.(*NodeAbstractResourceInstance).plan(0xc004c683c0, {0x2b85ad8, 0xc004754380}, 0x0, 0x0, 0x0, {0x0, 0x0, 0x0?})
	/go/pkg/mod/github.com/hashicorp/terraform@v1.3.0-dev.0.20221020194023-730756eca23f/internal/terraform/node_resource_abstract_instance.go:802 +0x12a3
github.com/hashicorp/terraform/internal/terraform.(*NodePlannableResourceInstance).managedResourceExecute(0xc0089de080, {0x2b85ad8, 0xc004754380})
	/go/pkg/mod/github.com/hashicorp/terraform@v1.3.0-dev.0.20221020194023-730756eca23f/internal/terraform/node_resource_plan_instance.go:218 +0xd6e
github.com/hashicorp/terraform/internal/terraform.(*NodePlannableResourceInstance).Execute(0x0?, {0x2b85ad8?, 0xc004754380?}, 0x0?)
	/go/pkg/mod/github.com/hashicorp/terraform@v1.3.0-dev.0.20221020194023-730756eca23f/internal/terraform/node_resource_plan_instance.go:60 +0x90
github.com/hashicorp/terraform/internal/terraform.(*ContextGraphWalker).Execute(0xc004c660f0, {0x2b85ad8, 0xc004754380}, {0x7f08101c3640, 0xc0089de080})
	/go/pkg/mod/github.com/hashicorp/terraform@v1.3.0-dev.0.20221020194023-730756eca23f/internal/terraform/graph_walk_context.go:136 +0xc2
github.com/hashicorp/terraform/internal/terraform.(*Graph).walk.func1({0x25706a0, 0xc0089de080})
	/go/pkg/mod/github.com/hashicorp/terraform@v1.3.0-dev.0.20221020194023-730756eca23f/internal/terraform/graph.go:75 +0x315
github.com/hashicorp/terraform/internal/dag.(*Walker).walkVertex(0xc0089e0180, {0x25706a0, 0xc0089de080}, 0xc0089de1c0)
	/go/pkg/mod/github.com/hashicorp/terraform@v1.3.0-dev.0.20221020194023-730756eca23f/internal/dag/walk.go:381 +0x2f6
created by github.com/hashicorp/terraform/internal/dag.(*Walker).Update
	/go/pkg/mod/github.com/hashicorp/terraform@v1.3.0-dev.0.20221020194023-730756eca23f/internal/dag/walk.go:304 +0xf65}

This is happening because of the following example terraform config (simplified):

resource "example_resource" "testcase" {
  list_of_objects = [
    {
      name = "first"
      dynamic_attribute = 123
    },
    {
      name = "second"
    },
  ]
}

dynamic_attribute in this example is using tftypes.DynamicPseudoType and the long-ish error message basically says that there is a type inconsistency between the first object in the list (cty.Number) and the second one (cty.DynamicPseudoType). The problem disapears once I specify dynamic_attribute for the second object as well.

I think the fundamental problem here is that TerraformType(_ context.Context) always returns tftypes.DynamicPseudoType whereas ToTerraformValue(_ context.Context) returns the actual value & type that is used in the configuration.

@apparentlymart
Copy link
Member

Thanks for sharing that, @sebhoss!

I expect there is an improvement to make to the framework to avoid a value like this getting so far through before it is detected, but what you showed here also indicates a bug in Terraform Core: it seems to be possible for a provider to crash Terraform Core by returning a value that doesn't conform to the schema, whereas that's supposed to be handled as a proper error message so that Terraform Core will at least get a chance to exit cleanly and tidy up any other concurrent operations in progress.

Would you mind reporting this in the hashicorp/terraform repository too, so that the Terraform Core team can look into that part of the problem more closely? If you do, it'd be helpful to share the relevant part of the schema of the provider (as produced by terraform providers schema -json) so we can see exactly what type constraint the provider is effectively declaring (after stripping away the framework abstractions). Thanks!

@bflad
Copy link
Member

bflad commented Oct 26, 2022

Please note that the framework does not officially support tftypes.DynamicPseudoType currently. It may be possible to create custom types with tftypes.DynamicPseudoType via the attr.Type and attr.Value interfaces, but there has not been any effort in testing it, nor building any validation or other potential features in the framework to natively support that special type. The framework type system is currently tailored for 1:1 mapping of "schema" type to value type, so there would need to be some investigation and design effort to handle how dynamic typing can interact in that model.

@sebhoss
Copy link

sebhoss commented Oct 27, 2022

@apparentlymart ok created hashicorp/terraform#32092 for that

@bflad yeah I understand. I was aware of this ticket here before deciding to give it a try and was actually surprised how well it all went. In most cases it just works(tm) and I only shared the log output as input for future design work in case you guys ever decide to implement a dynamic type. If there is anything I can do to help or try out, please let me know!

@bflad bflad changed the title Add a types.Dynamic Support Dynamic Type/Attributes Jan 6, 2023
@tyler-dunkel
Copy link

@bflad thank you for pointing me to this issue. Just trying to catch up here, it looks like using DPT might work (although hasnt been verified from what I can see) for now but is there any anticipation on when this might be better supported natively in the provider framework?

@bflad bflad added the tf-devex-triage Terraform DevEx project tracking label Aug 29, 2023
jamonation pushed a commit to jamonation/terraform-plugin-images-readme that referenced this issue Sep 18, 2023
…hashicorp#147)

* Bump github.com/hashicorp/terraform-plugin-docs from 0.14.1 to 0.15.0

Bumps [github.com/hashicorp/terraform-plugin-docs](https://github.com/hashicorp/terraform-plugin-docs) from 0.14.1 to 0.15.0.
- [Release notes](https://github.com/hashicorp/terraform-plugin-docs/releases)
- [Changelog](https://github.com/hashicorp/terraform-plugin-docs/blob/main/CHANGELOG.md)
- [Commits](hashicorp/terraform-plugin-docs@v0.14.1...v0.15.0)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/terraform-plugin-docs
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Regenerate documentation using updated tfplugindocs

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Brian Flad <bflad417@gmail.com>
@bflad bflad changed the title Support Dynamic Type/Attributes Support Dynamic Type/Attributes/Parameters Jan 12, 2024
@bflad
Copy link
Member

bflad commented Jan 12, 2024

Please note that we will also use this issue as a catching point for dynamic function parameter support. This functionality is being actively considered for implementation in the coming weeks (but not fully committed yet).

@austinvalle
Copy link
Member

If you've been following along in this issue, Dynamic type support has just landed in terraform-plugin-framework v1.7.0 🥳. This includes dynamic attributes (resource, data source, and provider schemas) and dynamic function parameters/returns.

We have written some initial documentation to capture some of the scenarios you'll need to account for when using a dynamic type: https://developer.hashicorp.com/terraform/plugin/framework/handling-data/dynamic-data (may need to let our CDN catch up, since it's just been released).

As always, if you run into any problems, please submit a new issue!
https://github.com/hashicorp/terraform-plugin-framework/issues

Copy link

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.
If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 22, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request tf-devex-triage Terraform DevEx project tracking types Issues and pull requests about our types abstraction and implementations.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants