Problem with getting information from schema. Custom provider

Hello

Today I faced with issue that I cant get all information from schema.
I have resource

resource "custom_configuration" "new" {
 restart                        = 0
 dashboard_api_access_token     = "7099454d2064f8d9c43d316e4ff32f3"
 password_enforce_complexity    = false
 password_lockout_attempts      = 0
 storage_enabled                = true
 prefer_local_reads             = false
 # cloud_boot_enabled             = true
 cloud_boot_domain_name_servers = var.cp_mgmt
 cloud_boot_target              = var.cp_mgmt
 snmptrap_addresses             = var.cp_mgmt
 nfs_root_ip                    = var.cp_mgmt
 enforce_redundancy             = false
 disable_hypervisor_failover    = true
}

Also I have function to get all fields from schema to the map. This is test function because previous method also not work.

func createSettings(d *schema.ResourceData) *map[string]interface{} {
  res := make(map[string]interface{})
  t := reflect.TypeOf(&custom.Configuration{}).Elem()
    for i := 0; i < t.NumField(); i++ {
      fieldName := jsonTag(t.Field(i))
	  if attr, ok := d.GetOk(fieldName); ok {
		res[fieldName] = attr
		log.Printf("CHANGED +++++++++++++++ %s = %+v", fieldName, attr)
	  }
  }
  log.Printf("[DEBUG] createSettings +++++ %#v", res)
  return &res
}

This function return next output:

CHANGED +++++++++++++++ cloud_boot_domain_name_servers = 192.168.16.5
CHANGED +++++++++++++++ cloud_boot_target = 192.168.16.5
CHANGED +++++++++++++++ dashboard_api_access_token = 7099454d20619b07b943d316e4ff32f3
CHANGED +++++++++++++++ disable_hypervisor_failover = true
CHANGED +++++++++++++++ nfs_root_ip = 192.168.16.5
CHANGED +++++++++++++++ snmptrap_addresses = 192.168.16.5
CHANGED +++++++++++++++ storage_enabled = true
[DEBUG] createSettings map[string]interface {}{"cloud_boot_domain_name_servers":"192.168.16.5", "cloud_boot_target":"192.168.16.5", "dashboard_api_access_token":"7099454d20619b07b943d316e4ff32f3", "disable_hypervisor_failover":true, "nfs_root_ip":"192.168.16.5", "snmptrap_addresses":"192.168.16.5", "storage_enabled":true}

So as we could see, there are not some values from terraform configuration,

password_enforce_complexity    = false
password_lockout_attempts      = 0

As I understand this is because GetOk function return only non zero, non empty value from resource schema. If I use Get function I of course get all data from resource schema, but I can’t send this data because I overwrite correct values on the resource…

The question is how I can resolve this problem?

Any ideas? For me it is very strange situation… I think that option which declared in configuration file should be returned by Get, GetOk even if they have ‘false’, ‘0’, “” values?

@apparentlymart what do you think?

Really, no one here don’t have the same situation?
How can I get “zero” values from resource???

Hi there,

Is there a reason why you’re using loop through the configuration and retrieving values using d.GetOk()?

The typical way to retrieve values from a resource is by defining the resource schema, then retrieving the value using d.Get(). This will allow you to retrieve the values even if they’re zero or empty.

We recently published a collection of tutorials that guide you through writing a custom Terraform provider. This may help you in your journey to create a custom provider.

Maybe I described the situation badly.

There is a endpoint that has a default configuration after installation. There is a resource for editing this configuration, I’m interested in passing parameters that are clearly described in the resource. I can only do it through GetOk, because when I use Get, all default settings will be passed, which is wrong in my situation, because they pass empty value where the default configuration has a value that I should not change. Next example I get by Get function but I should not pass these parameters because they are wrong…

CHANGED +++++++++++++++ action_global_lock_expiration_timeout = 0
CHANGED +++++++++++++++ action_global_lock_retry_delay = 0

If I specify default values in the resource schema, they will all be passed in the same way when editing only some configuration parameters …

In my situation the following parameters are correct even though they are zero or empty

password_enforce_complexity = false
password_lockout_attempts   = 0

What can I do in this situation? Import default configuration to teraform state? Next, edit the necessary parameters through the resource?

If I understand your situation correctly, GetOkExist is the function you’re looking for.

This is similar to GetOk but the exists boolean value does not verify that the value is a zero-value. You can look at the source here.

Hope this helps!

Maybe yes, but as I know this is deprecated function…

Yep, it’s a deprecated function but I can’t think of an alternative for your situation.

appilon mentioned that it won’t be actually be removed without a suitable replacement, so it could still serve as a temporarily stopgap if you need this functionality immediately.

Okay thank you, at this moment I will use this function.

Hello

Today I have the same situation but now I can’t get default values from schema for Set

"timeout": {
  Type:     schema.TypeSet,
  Optional: true,
  MaxItems: 1,
  Elem: &schema.Resource{
    Schema: map[string]*schema.Schema{
      "delay": {
        Type:        schema.TypeInt,
        Optional:    true,
        Default:     30, // seconds
      },
      "timeout": {
        Type:        schema.TypeInt,
        Optional:    true,
        Default:     120, //seconds
      },
      "min_timeout": {
        Type:        schema.TypeInt,
        Optional:    true,
        Default:     120, //seconds
      },
    },
  },
},

How I get data from schema:

t := expandTimeoutOpts(d.Get("timeout").(*schema.Set).List())
log.Printf("[DEBUG] expandTimeoutOpts: %# v\n", pretty.Formatter(t))

Log output show this:

[DEBUG] expandTimeoutOpts: &custom.timeout{}

As we can see I got empty object, how I can get default values from schema and return initialized object? I don’t want make this as Required field in schema.

func expandTimeoutOpts(in []interface{}) *timeout {
	out := &timeout{}

	for _, v := range in {
		val := v.(map[string]interface{})
		if res, ok := val["delay"]; ok {
			out.Delay = res.(time.Duration)
		}
		if res, ok := val["timeout"]; ok {
			out.Timeout = res.(time.Duration)
		}
		if res, ok := val["min_timeout"]; ok {
			out.MinTimeout = res.(time.Duration)
		}
	}

	return out
}

Okay, as I understand I can’t get object with default values, if it not defined in configuration file? So I should define any complex object in config even they aren’t needed?

I don’t believe you’ll be able to retrieve the values if they’re not defined in the schema.