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

Proposal: helper/schema TypeEnum type #79

Open
rileykarson opened this issue Jul 28, 2017 · 3 comments
Open

Proposal: helper/schema TypeEnum type #79

rileykarson opened this issue Jul 28, 2017 · 3 comments
Labels
enhancement New feature or request subsystem/types Issues and feature requests related to the type system of Terraform and our shims around it. terraform-plugin-framework Resolved in terraform-plugin-framework

Comments

@rileykarson
Copy link
Contributor

This proposal is for a new type in helper/schema to represent an enum, schema.TypeEnum, as well as highlighting two possible extensions/use cases for the type.

To other provider contributors reading this; if you have use cases or ideas that you can add, feel free to add them below as comments.


It's a common pattern in the Google provider for us to want to consume an enum
value from a Terraform configuration. Using google_bigtable_instance's
storage_type field as an example, we specify it like this right now:

"storage_type": {
    Type:         schema.TypeString,
    Optional:     true,
    ForceNew:     true,
    Default:      "SSD",
    ValidateFunc: validation.StringInSlice([]string{"SSD", "HDD"}, false),
},

A schema.TypeEnum would be represented as a string; that means that it would
not be an object type, and could not have child elements like a TypeList or
TypeSet. It would have a Values attribute which is a slice of strings.

Terraform will automatically run validation.StringInSlice with ignoreCase: false on the value, and also run any additional ValidateFunc that is set, if
any, displaying the set of errors if there are any.

Terraform will validate that the Default value is present in Values when
performing internal validation.

This means that we would have roughly the same behaviour, as well as validation
that our Default is correct. We could specify storage_type like:

"storage_type": {
    Type:         schema.TypeEnum,
    Optional:     true,
    ForceNew:     true,
    Default:      "SSD",
    Values: []string{"SSD", "HDD"},
},

This functionality alone is what this issue is asking for; like this,
schema.TypeEnum is a nice alias over schema.TypeString that attaches some
more semantic significance. There are two ways we could extend it, though;


This extension would be a nice-to-have, and would provide a lot of benefit for
us. It's not necessary for schema.TypeEnum as a whole, but is something I
would like to see as a provider developer.

Past Google Go clients have used string to represent enums in the API. Newer
Google clients under the cloud.google.com/go/ like bigtable use constants
to represent enum values
,
and we need to perform awkward mappings from string -> enum.

Instead of Values accepting a slice of strings, it would take in a
map[string]interface{} of config-side keys to arbitrary values. The
responsibility of casting to the correct type would be on the client code. The
default value specified in schema would be the string key. If the enum is
represented as a string, like Google's older clients such as for Compute, you
would map from string to string.

For context for this example, Bigtable's enums are bigtable.HDD and
bigtable.SSD and have the type bigtable.StorageType; that means we would
then write storage_type as

"storage_type": {
    Type:         schema.TypeEnum,
    Optional:     true,
    ForceNew:     true,
    Default:      "SSD",
    Values: map[string]interface{}{
        "SSD": bigtable.SSD,
        "HDD": bigtable.HDD
    },
},

If a user specified

storage_type = "HDD"

then we would get it in code in the correct type like:

storageType := d.Get("storage_type").(bigtable.StorageType)

This feature is just as much asking if it's possible as it is a nice-to-have; it
is completely unnecessary for the rest of the proposal, but it lets us handle a
gross edge case that comes up a few times in schema instead of code.

Having a constrained set of values would also let us perform validations based
on what the user has specified in their config file. As a specific example,
google_sql_database_instance has a field database_version that could be used
as an enum. It looks like this right now:

"database_version": &schema.Schema{
    Type:     schema.TypeString,
    Optional: true,
    Default:  "MYSQL_5_6",
    ForceNew: true,
},

Even though we currently allow free-form input, and rely on the API for
validation, it has a constrained set of potential values. They are: MYSQL_5_5,
MYSQL_5_6, MYSQL_5_7, and POSTGRES_9_6. So, it would look like:

"database_version": &schema.Schema{
    Type:     schema.TypeEnum,
    Optional: true,
    Default:  "MYSQL_5_6",
    ForceNew: true,
    Values: []string{"MYSQL_5_5", "MYSQL_5_6", "MYSQL_5_7", "POSTGRES_9_6"},
},

Because we know exhaustively the set of potential values and schema.TypeEnum
cannot have children, we should be able to use ConflictsWith with specific
enum values. This would give plan-time errors with invalid configurations.

For google_sql_database_instance, we support a replica_configuration object
which represents the API-side
mysqlReplicaConfiguration
.
This isn't valid when using a POSTGRES_9_6 instance.

We would specify it like so;

"replica_configuration": &schema.Schema{
    Type:     schema.TypeList,
    Optional: true,
    MaxItems: 1,
    ConflictsWith: []string{"database_version.POSTGRES_9_6"},
    Elem: &schema.Resource{ /* omitted */ },
}

So if we specified this in our google_sql_database_instance body:

database_version = "POSTGRES_9_6"
replica_configuration {
}

We would receive an error at plan time like:

Cannot specify `replica_configuration` when `database_version` has value "POSTGRES_9_6"
@joestump
Copy link

joestump commented Jun 4, 2018

Really could have used this today for the Heroku data provider.

@hashibot hashibot transferred this issue from hashicorp/terraform Sep 26, 2019
@hashibot hashibot added the enhancement New feature or request label Oct 2, 2019
@paddycarver paddycarver added the subsystem/types Issues and feature requests related to the type system of Terraform and our shims around it. label Jan 6, 2021
@bflad bflad added the terraform-plugin-framework Resolved in terraform-plugin-framework label Mar 30, 2022
@jasondamour
Copy link

Seems like this would be massively useful

@joekendal
Copy link

+1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request subsystem/types Issues and feature requests related to the type system of Terraform and our shims around it. terraform-plugin-framework Resolved in terraform-plugin-framework
Projects
None yet
Development

No branches or pull requests

7 participants