github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/google/resource_sql_database_instance.go (about)

     1  package google
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  
     8  	"github.com/hashicorp/terraform/helper/resource"
     9  	"github.com/hashicorp/terraform/helper/schema"
    10  
    11  	"google.golang.org/api/googleapi"
    12  	"google.golang.org/api/sqladmin/v1beta4"
    13  )
    14  
    15  func resourceSqlDatabaseInstance() *schema.Resource {
    16  	return &schema.Resource{
    17  		Create: resourceSqlDatabaseInstanceCreate,
    18  		Read:   resourceSqlDatabaseInstanceRead,
    19  		Update: resourceSqlDatabaseInstanceUpdate,
    20  		Delete: resourceSqlDatabaseInstanceDelete,
    21  
    22  		Schema: map[string]*schema.Schema{
    23  			"region": &schema.Schema{
    24  				Type:     schema.TypeString,
    25  				Required: true,
    26  				ForceNew: true,
    27  			},
    28  
    29  			"settings": &schema.Schema{
    30  				Type:     schema.TypeList,
    31  				Required: true,
    32  				Elem: &schema.Resource{
    33  					Schema: map[string]*schema.Schema{
    34  						"version": &schema.Schema{
    35  							Type:     schema.TypeInt,
    36  							Computed: true,
    37  						},
    38  						"tier": &schema.Schema{
    39  							Type:     schema.TypeString,
    40  							Required: true,
    41  						},
    42  						"activation_policy": &schema.Schema{
    43  							Type:     schema.TypeString,
    44  							Optional: true,
    45  						},
    46  						"authorized_gae_applications": &schema.Schema{
    47  							Type:     schema.TypeList,
    48  							Optional: true,
    49  							Elem:     &schema.Schema{Type: schema.TypeString},
    50  						},
    51  						"backup_configuration": &schema.Schema{
    52  							Type:     schema.TypeList,
    53  							Optional: true,
    54  							Elem: &schema.Resource{
    55  								Schema: map[string]*schema.Schema{
    56  									"binary_log_enabled": &schema.Schema{
    57  										Type:     schema.TypeBool,
    58  										Optional: true,
    59  									},
    60  									"enabled": &schema.Schema{
    61  										Type:     schema.TypeBool,
    62  										Optional: true,
    63  									},
    64  									"start_time": &schema.Schema{
    65  										Type:     schema.TypeString,
    66  										Optional: true,
    67  									},
    68  								},
    69  							},
    70  						},
    71  						"crash_safe_replication": &schema.Schema{
    72  							Type:     schema.TypeBool,
    73  							Optional: true,
    74  							Computed: true,
    75  						},
    76  						"database_flags": &schema.Schema{
    77  							Type:     schema.TypeList,
    78  							Optional: true,
    79  							Elem: &schema.Resource{
    80  								Schema: map[string]*schema.Schema{
    81  									"value": &schema.Schema{
    82  										Type:     schema.TypeString,
    83  										Optional: true,
    84  									},
    85  									"name": &schema.Schema{
    86  										Type:     schema.TypeString,
    87  										Optional: true,
    88  									},
    89  								},
    90  							},
    91  						},
    92  						"disk_autoresize": &schema.Schema{
    93  							Type:     schema.TypeBool,
    94  							Optional: true,
    95  						},
    96  						"disk_size": &schema.Schema{
    97  							Type:     schema.TypeInt,
    98  							Optional: true,
    99  						},
   100  						"disk_type": &schema.Schema{
   101  							Type:     schema.TypeString,
   102  							Optional: true,
   103  						},
   104  						"ip_configuration": &schema.Schema{
   105  							Type:     schema.TypeList,
   106  							Optional: true,
   107  							Elem: &schema.Resource{
   108  								Schema: map[string]*schema.Schema{
   109  									"authorized_networks": &schema.Schema{
   110  										Type:     schema.TypeList,
   111  										Optional: true,
   112  										Elem: &schema.Resource{
   113  											Schema: map[string]*schema.Schema{
   114  												"expiration_time": &schema.Schema{
   115  													Type:     schema.TypeString,
   116  													Optional: true,
   117  												},
   118  												"name": &schema.Schema{
   119  													Type:     schema.TypeString,
   120  													Optional: true,
   121  												},
   122  												"value": &schema.Schema{
   123  													Type:     schema.TypeString,
   124  													Optional: true,
   125  												},
   126  											},
   127  										},
   128  									},
   129  									"ipv4_enabled": &schema.Schema{
   130  										Type:     schema.TypeBool,
   131  										Optional: true,
   132  									},
   133  									"require_ssl": &schema.Schema{
   134  										Type:     schema.TypeBool,
   135  										Optional: true,
   136  									},
   137  								},
   138  							},
   139  						},
   140  						"location_preference": &schema.Schema{
   141  							Type:     schema.TypeList,
   142  							Optional: true,
   143  							Elem: &schema.Resource{
   144  								Schema: map[string]*schema.Schema{
   145  									"follow_gae_application": &schema.Schema{
   146  										Type:     schema.TypeString,
   147  										Optional: true,
   148  									},
   149  									"zone": &schema.Schema{
   150  										Type:     schema.TypeString,
   151  										Optional: true,
   152  									},
   153  								},
   154  							},
   155  						},
   156  						"maintenance_window": &schema.Schema{
   157  							Type:     schema.TypeList,
   158  							Optional: true,
   159  							MaxItems: 1,
   160  							Elem: &schema.Resource{
   161  								Schema: map[string]*schema.Schema{
   162  									"day": &schema.Schema{
   163  										Type:     schema.TypeInt,
   164  										Optional: true,
   165  										ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
   166  											return validateNumericRange(v, k, 1, 7)
   167  										},
   168  									},
   169  									"hour": &schema.Schema{
   170  										Type:     schema.TypeInt,
   171  										Optional: true,
   172  										ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
   173  											return validateNumericRange(v, k, 0, 23)
   174  										},
   175  									},
   176  									"update_track": &schema.Schema{
   177  										Type:     schema.TypeString,
   178  										Optional: true,
   179  									},
   180  								},
   181  							},
   182  						},
   183  						"pricing_plan": &schema.Schema{
   184  							Type:     schema.TypeString,
   185  							Optional: true,
   186  						},
   187  						"replication_type": &schema.Schema{
   188  							Type:     schema.TypeString,
   189  							Optional: true,
   190  						},
   191  					},
   192  				},
   193  			},
   194  
   195  			"database_version": &schema.Schema{
   196  				Type:     schema.TypeString,
   197  				Optional: true,
   198  				Default:  "MYSQL_5_6",
   199  				ForceNew: true,
   200  			},
   201  
   202  			"ip_address": &schema.Schema{
   203  				Type:     schema.TypeList,
   204  				Computed: true,
   205  				Elem: &schema.Resource{
   206  					Schema: map[string]*schema.Schema{
   207  						"ip_address": &schema.Schema{
   208  							Type:     schema.TypeString,
   209  							Computed: true,
   210  						},
   211  						"time_to_retire": &schema.Schema{
   212  							Type:     schema.TypeString,
   213  							Optional: true,
   214  							Computed: true,
   215  						},
   216  					},
   217  				},
   218  			},
   219  
   220  			"name": &schema.Schema{
   221  				Type:     schema.TypeString,
   222  				Optional: true,
   223  				Computed: true,
   224  				ForceNew: true,
   225  			},
   226  
   227  			"master_instance_name": &schema.Schema{
   228  				Type:     schema.TypeString,
   229  				Optional: true,
   230  				ForceNew: true,
   231  			},
   232  
   233  			"project": &schema.Schema{
   234  				Type:     schema.TypeString,
   235  				Optional: true,
   236  				ForceNew: true,
   237  			},
   238  
   239  			"replica_configuration": &schema.Schema{
   240  				Type:     schema.TypeList,
   241  				Optional: true,
   242  				Elem: &schema.Resource{
   243  					Schema: map[string]*schema.Schema{
   244  						"ca_certificate": &schema.Schema{
   245  							Type:     schema.TypeString,
   246  							Optional: true,
   247  							ForceNew: true,
   248  						},
   249  						"client_certificate": &schema.Schema{
   250  							Type:     schema.TypeString,
   251  							Optional: true,
   252  							ForceNew: true,
   253  						},
   254  						"client_key": &schema.Schema{
   255  							Type:     schema.TypeString,
   256  							Optional: true,
   257  							ForceNew: true,
   258  						},
   259  						"connect_retry_interval": &schema.Schema{
   260  							Type:     schema.TypeInt,
   261  							Optional: true,
   262  							ForceNew: true,
   263  						},
   264  						"dump_file_path": &schema.Schema{
   265  							Type:     schema.TypeString,
   266  							Optional: true,
   267  							ForceNew: true,
   268  						},
   269  						"master_heartbeat_period": &schema.Schema{
   270  							Type:     schema.TypeInt,
   271  							Optional: true,
   272  							ForceNew: true,
   273  						},
   274  						"password": &schema.Schema{
   275  							Type:     schema.TypeString,
   276  							Optional: true,
   277  							ForceNew: true,
   278  						},
   279  						"ssl_cipher": &schema.Schema{
   280  							Type:     schema.TypeString,
   281  							Optional: true,
   282  							ForceNew: true,
   283  						},
   284  						"username": &schema.Schema{
   285  							Type:     schema.TypeString,
   286  							Optional: true,
   287  							ForceNew: true,
   288  						},
   289  						"verify_server_certificate": &schema.Schema{
   290  							Type:     schema.TypeBool,
   291  							Optional: true,
   292  							ForceNew: true,
   293  						},
   294  					},
   295  				},
   296  			},
   297  
   298  			"self_link": &schema.Schema{
   299  				Type:     schema.TypeString,
   300  				Computed: true,
   301  			},
   302  		},
   303  	}
   304  }
   305  
   306  func resourceSqlDatabaseInstanceCreate(d *schema.ResourceData, meta interface{}) error {
   307  	config := meta.(*Config)
   308  
   309  	project, err := getProject(d, config)
   310  	if err != nil {
   311  		return err
   312  	}
   313  
   314  	region := d.Get("region").(string)
   315  	databaseVersion := d.Get("database_version").(string)
   316  
   317  	_settingsList := d.Get("settings").([]interface{})
   318  	if len(_settingsList) > 1 {
   319  		return fmt.Errorf("At most one settings block is allowed")
   320  	}
   321  
   322  	_settings := _settingsList[0].(map[string]interface{})
   323  	settings := &sqladmin.Settings{
   324  		Tier: _settings["tier"].(string),
   325  	}
   326  
   327  	if v, ok := _settings["activation_policy"]; ok {
   328  		settings.ActivationPolicy = v.(string)
   329  	}
   330  
   331  	if v, ok := _settings["authorized_gae_applications"]; ok {
   332  		settings.AuthorizedGaeApplications = make([]string, 0)
   333  		for _, app := range v.([]interface{}) {
   334  			settings.AuthorizedGaeApplications = append(settings.AuthorizedGaeApplications,
   335  				app.(string))
   336  		}
   337  	}
   338  
   339  	if v, ok := _settings["backup_configuration"]; ok {
   340  		_backupConfigurationList := v.([]interface{})
   341  		if len(_backupConfigurationList) > 1 {
   342  			return fmt.Errorf("At most one backup_configuration block is allowed")
   343  		}
   344  
   345  		if len(_backupConfigurationList) == 1 && _backupConfigurationList[0] != nil {
   346  			settings.BackupConfiguration = &sqladmin.BackupConfiguration{}
   347  			_backupConfiguration := _backupConfigurationList[0].(map[string]interface{})
   348  
   349  			if vp, okp := _backupConfiguration["binary_log_enabled"]; okp {
   350  				settings.BackupConfiguration.BinaryLogEnabled = vp.(bool)
   351  			}
   352  
   353  			if vp, okp := _backupConfiguration["enabled"]; okp {
   354  				settings.BackupConfiguration.Enabled = vp.(bool)
   355  			}
   356  
   357  			if vp, okp := _backupConfiguration["start_time"]; okp {
   358  				settings.BackupConfiguration.StartTime = vp.(string)
   359  			}
   360  		}
   361  	}
   362  
   363  	if v, ok := _settings["crash_safe_replication"]; ok {
   364  		settings.CrashSafeReplicationEnabled = v.(bool)
   365  	}
   366  
   367  	if v, ok := _settings["disk_autoresize"]; ok && v.(bool) {
   368  		settings.StorageAutoResize = v.(bool)
   369  	}
   370  
   371  	if v, ok := _settings["disk_size"]; ok && v.(int) > 0 {
   372  		settings.DataDiskSizeGb = int64(v.(int))
   373  	}
   374  
   375  	if v, ok := _settings["disk_type"]; ok && len(v.(string)) > 0 {
   376  		settings.DataDiskType = v.(string)
   377  	}
   378  
   379  	if v, ok := _settings["database_flags"]; ok {
   380  		settings.DatabaseFlags = make([]*sqladmin.DatabaseFlags, 0)
   381  		_databaseFlagsList := v.([]interface{})
   382  		for _, _flag := range _databaseFlagsList {
   383  			_entry := _flag.(map[string]interface{})
   384  			flag := &sqladmin.DatabaseFlags{}
   385  			if vp, okp := _entry["name"]; okp {
   386  				flag.Name = vp.(string)
   387  			}
   388  
   389  			if vp, okp := _entry["value"]; okp {
   390  				flag.Value = vp.(string)
   391  			}
   392  
   393  			settings.DatabaseFlags = append(settings.DatabaseFlags, flag)
   394  		}
   395  	}
   396  
   397  	if v, ok := _settings["ip_configuration"]; ok {
   398  		_ipConfigurationList := v.([]interface{})
   399  		if len(_ipConfigurationList) > 1 {
   400  			return fmt.Errorf("At most one ip_configuration block is allowed")
   401  		}
   402  
   403  		if len(_ipConfigurationList) == 1 && _ipConfigurationList[0] != nil {
   404  			settings.IpConfiguration = &sqladmin.IpConfiguration{}
   405  			_ipConfiguration := _ipConfigurationList[0].(map[string]interface{})
   406  
   407  			if vp, okp := _ipConfiguration["ipv4_enabled"]; okp {
   408  				settings.IpConfiguration.Ipv4Enabled = vp.(bool)
   409  			}
   410  
   411  			if vp, okp := _ipConfiguration["require_ssl"]; okp {
   412  				settings.IpConfiguration.RequireSsl = vp.(bool)
   413  			}
   414  
   415  			if vp, okp := _ipConfiguration["authorized_networks"]; okp {
   416  				settings.IpConfiguration.AuthorizedNetworks = make([]*sqladmin.AclEntry, 0)
   417  				_authorizedNetworksList := vp.([]interface{})
   418  				for _, _acl := range _authorizedNetworksList {
   419  					_entry := _acl.(map[string]interface{})
   420  					entry := &sqladmin.AclEntry{}
   421  
   422  					if vpp, okpp := _entry["expiration_time"]; okpp {
   423  						entry.ExpirationTime = vpp.(string)
   424  					}
   425  
   426  					if vpp, okpp := _entry["name"]; okpp {
   427  						entry.Name = vpp.(string)
   428  					}
   429  
   430  					if vpp, okpp := _entry["value"]; okpp {
   431  						entry.Value = vpp.(string)
   432  					}
   433  
   434  					settings.IpConfiguration.AuthorizedNetworks = append(
   435  						settings.IpConfiguration.AuthorizedNetworks, entry)
   436  				}
   437  			}
   438  		}
   439  	}
   440  
   441  	if v, ok := _settings["location_preference"]; ok {
   442  		_locationPreferenceList := v.([]interface{})
   443  		if len(_locationPreferenceList) > 1 {
   444  			return fmt.Errorf("At most one location_preference block is allowed")
   445  		}
   446  
   447  		if len(_locationPreferenceList) == 1 && _locationPreferenceList[0] != nil {
   448  			settings.LocationPreference = &sqladmin.LocationPreference{}
   449  			_locationPreference := _locationPreferenceList[0].(map[string]interface{})
   450  
   451  			if vp, okp := _locationPreference["follow_gae_application"]; okp {
   452  				settings.LocationPreference.FollowGaeApplication = vp.(string)
   453  			}
   454  
   455  			if vp, okp := _locationPreference["zone"]; okp {
   456  				settings.LocationPreference.Zone = vp.(string)
   457  			}
   458  		}
   459  	}
   460  
   461  	if v, ok := _settings["maintenance_window"]; ok && len(v.([]interface{})) > 0 {
   462  		settings.MaintenanceWindow = &sqladmin.MaintenanceWindow{}
   463  		_maintenanceWindow := v.([]interface{})[0].(map[string]interface{})
   464  
   465  		if vp, okp := _maintenanceWindow["day"]; okp {
   466  			settings.MaintenanceWindow.Day = int64(vp.(int))
   467  		}
   468  
   469  		if vp, okp := _maintenanceWindow["hour"]; okp {
   470  			settings.MaintenanceWindow.Hour = int64(vp.(int))
   471  		}
   472  
   473  		if vp, ok := _maintenanceWindow["update_track"]; ok {
   474  			if len(vp.(string)) > 0 {
   475  				settings.MaintenanceWindow.UpdateTrack = vp.(string)
   476  			}
   477  		}
   478  	}
   479  
   480  	if v, ok := _settings["pricing_plan"]; ok {
   481  		settings.PricingPlan = v.(string)
   482  	}
   483  
   484  	if v, ok := _settings["replication_type"]; ok {
   485  		settings.ReplicationType = v.(string)
   486  	}
   487  
   488  	instance := &sqladmin.DatabaseInstance{
   489  		Region:          region,
   490  		Settings:        settings,
   491  		DatabaseVersion: databaseVersion,
   492  	}
   493  
   494  	if v, ok := d.GetOk("name"); ok {
   495  		instance.Name = v.(string)
   496  	} else {
   497  		instance.Name = resource.UniqueId()
   498  		d.Set("name", instance.Name)
   499  	}
   500  
   501  	if v, ok := d.GetOk("replica_configuration"); ok {
   502  		_replicaConfigurationList := v.([]interface{})
   503  		if len(_replicaConfigurationList) > 1 {
   504  			return fmt.Errorf("Only one replica_configuration block may be defined")
   505  		}
   506  
   507  		if len(_replicaConfigurationList) == 1 && _replicaConfigurationList[0] != nil {
   508  			replicaConfiguration := &sqladmin.ReplicaConfiguration{}
   509  			mySqlReplicaConfiguration := &sqladmin.MySqlReplicaConfiguration{}
   510  			_replicaConfiguration := _replicaConfigurationList[0].(map[string]interface{})
   511  
   512  			if vp, okp := _replicaConfiguration["ca_certificate"]; okp {
   513  				mySqlReplicaConfiguration.CaCertificate = vp.(string)
   514  			}
   515  
   516  			if vp, okp := _replicaConfiguration["client_certificate"]; okp {
   517  				mySqlReplicaConfiguration.ClientCertificate = vp.(string)
   518  			}
   519  
   520  			if vp, okp := _replicaConfiguration["client_key"]; okp {
   521  				mySqlReplicaConfiguration.ClientKey = vp.(string)
   522  			}
   523  
   524  			if vp, okp := _replicaConfiguration["connect_retry_interval"]; okp {
   525  				mySqlReplicaConfiguration.ConnectRetryInterval = int64(vp.(int))
   526  			}
   527  
   528  			if vp, okp := _replicaConfiguration["dump_file_path"]; okp {
   529  				mySqlReplicaConfiguration.DumpFilePath = vp.(string)
   530  			}
   531  
   532  			if vp, okp := _replicaConfiguration["master_heartbeat_period"]; okp {
   533  				mySqlReplicaConfiguration.MasterHeartbeatPeriod = int64(vp.(int))
   534  			}
   535  
   536  			if vp, okp := _replicaConfiguration["password"]; okp {
   537  				mySqlReplicaConfiguration.Password = vp.(string)
   538  			}
   539  
   540  			if vp, okp := _replicaConfiguration["ssl_cipher"]; okp {
   541  				mySqlReplicaConfiguration.SslCipher = vp.(string)
   542  			}
   543  
   544  			if vp, okp := _replicaConfiguration["username"]; okp {
   545  				mySqlReplicaConfiguration.Username = vp.(string)
   546  			}
   547  
   548  			if vp, okp := _replicaConfiguration["verify_server_certificate"]; okp {
   549  				mySqlReplicaConfiguration.VerifyServerCertificate = vp.(bool)
   550  			}
   551  
   552  			replicaConfiguration.MysqlReplicaConfiguration = mySqlReplicaConfiguration
   553  			instance.ReplicaConfiguration = replicaConfiguration
   554  		}
   555  	}
   556  
   557  	if v, ok := d.GetOk("master_instance_name"); ok {
   558  		instance.MasterInstanceName = v.(string)
   559  	}
   560  
   561  	op, err := config.clientSqlAdmin.Instances.Insert(project, instance).Do()
   562  	if err != nil {
   563  		if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 409 {
   564  			return fmt.Errorf("Error, the name %s is unavailable because it was used recently", instance.Name)
   565  		} else {
   566  			return fmt.Errorf("Error, failed to create instance %s: %s", instance.Name, err)
   567  		}
   568  	}
   569  
   570  	err = sqladminOperationWait(config, op, "Create Instance")
   571  	if err != nil {
   572  		return err
   573  	}
   574  
   575  	err = resourceSqlDatabaseInstanceRead(d, meta)
   576  	if err != nil {
   577  		return err
   578  	}
   579  
   580  	// If a root user exists with a wildcard ('%') hostname, delete it.
   581  	users, err := config.clientSqlAdmin.Users.List(project, instance.Name).Do()
   582  	if err != nil {
   583  		return fmt.Errorf("Error, attempting to list users associated with instance %s: %s", instance.Name, err)
   584  	}
   585  	for _, u := range users.Items {
   586  		if u.Name == "root" && u.Host == "%" {
   587  			op, err = config.clientSqlAdmin.Users.Delete(project, instance.Name, u.Host, u.Name).Do()
   588  			if err != nil {
   589  				return fmt.Errorf("Error, failed to delete default 'root'@'*' user, but the database was created successfully: %s", err)
   590  			}
   591  			err = sqladminOperationWait(config, op, "Delete default root User")
   592  			if err != nil {
   593  				return err
   594  			}
   595  		}
   596  	}
   597  
   598  	return nil
   599  }
   600  
   601  func resourceSqlDatabaseInstanceRead(d *schema.ResourceData, meta interface{}) error {
   602  	config := meta.(*Config)
   603  
   604  	project, err := getProject(d, config)
   605  	if err != nil {
   606  		return err
   607  	}
   608  
   609  	instance, err := config.clientSqlAdmin.Instances.Get(project,
   610  		d.Get("name").(string)).Do()
   611  
   612  	if err != nil {
   613  		if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
   614  			log.Printf("[WARN] Removing SQL Database %q because it's gone", d.Get("name").(string))
   615  			// The resource doesn't exist anymore
   616  			d.SetId("")
   617  
   618  			return nil
   619  		}
   620  
   621  		return fmt.Errorf("Error retrieving instance %s: %s",
   622  			d.Get("name").(string), err)
   623  	}
   624  
   625  	_settingsList := d.Get("settings").([]interface{})
   626  	_settings := _settingsList[0].(map[string]interface{})
   627  
   628  	settings := instance.Settings
   629  	_settings["version"] = settings.SettingsVersion
   630  	_settings["tier"] = settings.Tier
   631  
   632  	// Take care to only update attributes that the user has defined explicitly
   633  	if v, ok := _settings["activation_policy"]; ok && len(v.(string)) > 0 {
   634  		_settings["activation_policy"] = settings.ActivationPolicy
   635  	}
   636  
   637  	if v, ok := _settings["authorized_gae_applications"]; ok && len(v.([]interface{})) > 0 {
   638  		_authorized_gae_applications := make([]interface{}, 0)
   639  		for _, app := range settings.AuthorizedGaeApplications {
   640  			_authorized_gae_applications = append(_authorized_gae_applications, app)
   641  		}
   642  		_settings["authorized_gae_applications"] = _authorized_gae_applications
   643  	}
   644  
   645  	if v, ok := _settings["backup_configuration"]; ok {
   646  		_backupConfigurationList := v.([]interface{})
   647  		if len(_backupConfigurationList) > 1 {
   648  			return fmt.Errorf("At most one backup_configuration block is allowed")
   649  		}
   650  
   651  		if len(_backupConfigurationList) == 1 && _backupConfigurationList[0] != nil {
   652  			_backupConfiguration := _backupConfigurationList[0].(map[string]interface{})
   653  
   654  			if vp, okp := _backupConfiguration["binary_log_enabled"]; okp && vp != nil {
   655  				_backupConfiguration["binary_log_enabled"] = settings.BackupConfiguration.BinaryLogEnabled
   656  			}
   657  
   658  			if vp, okp := _backupConfiguration["enabled"]; okp && vp != nil {
   659  				_backupConfiguration["enabled"] = settings.BackupConfiguration.Enabled
   660  			}
   661  
   662  			if vp, okp := _backupConfiguration["start_time"]; okp && len(vp.(string)) > 0 {
   663  				_backupConfiguration["start_time"] = settings.BackupConfiguration.StartTime
   664  			}
   665  
   666  			_backupConfigurationList[0] = _backupConfiguration
   667  			_settings["backup_configuration"] = _backupConfigurationList
   668  		}
   669  	}
   670  
   671  	if v, ok := _settings["crash_safe_replication"]; ok && v != nil {
   672  		_settings["crash_safe_replication"] = settings.CrashSafeReplicationEnabled
   673  	}
   674  
   675  	if v, ok := _settings["disk_autoresize"]; ok && v != nil {
   676  		if v.(bool) {
   677  			_settings["disk_autoresize"] = settings.StorageAutoResize
   678  		}
   679  	}
   680  
   681  	if v, ok := _settings["disk_size"]; ok && v != nil {
   682  		if v.(int) > 0 && settings.DataDiskSizeGb < int64(v.(int)) {
   683  			_settings["disk_size"] = settings.DataDiskSizeGb
   684  		}
   685  	}
   686  
   687  	if v, ok := _settings["disk_type"]; ok && v != nil {
   688  		if len(v.(string)) > 0 {
   689  			_settings["disk_type"] = settings.DataDiskType
   690  		}
   691  	}
   692  
   693  	if v, ok := _settings["database_flags"]; ok && len(v.([]interface{})) > 0 {
   694  		_flag_map := make(map[string]string)
   695  		// First keep track of localy defined flag pairs
   696  		for _, _flag := range _settings["database_flags"].([]interface{}) {
   697  			_entry := _flag.(map[string]interface{})
   698  			_flag_map[_entry["name"].(string)] = _entry["value"].(string)
   699  		}
   700  
   701  		_database_flags := make([]interface{}, 0)
   702  		// Next read the flag pairs from the server, and reinsert those that
   703  		// correspond to ones defined locally
   704  		for _, entry := range settings.DatabaseFlags {
   705  			if _, okp := _flag_map[entry.Name]; okp {
   706  				_entry := make(map[string]interface{})
   707  				_entry["name"] = entry.Name
   708  				_entry["value"] = entry.Value
   709  				_database_flags = append(_database_flags, _entry)
   710  			}
   711  		}
   712  		_settings["database_flags"] = _database_flags
   713  	}
   714  
   715  	if v, ok := _settings["ip_configuration"]; ok {
   716  		_ipConfigurationList := v.([]interface{})
   717  		if len(_ipConfigurationList) > 1 {
   718  			return fmt.Errorf("At most one ip_configuration block is allowed")
   719  		}
   720  
   721  		if len(_ipConfigurationList) == 1 && _ipConfigurationList[0] != nil {
   722  			_ipConfiguration := _ipConfigurationList[0].(map[string]interface{})
   723  
   724  			if vp, okp := _ipConfiguration["ipv4_enabled"]; okp && vp != nil {
   725  				_ipConfiguration["ipv4_enabled"] = settings.IpConfiguration.Ipv4Enabled
   726  			}
   727  
   728  			if vp, okp := _ipConfiguration["require_ssl"]; okp && vp != nil {
   729  				_ipConfiguration["require_ssl"] = settings.IpConfiguration.RequireSsl
   730  			}
   731  
   732  			if vp, okp := _ipConfiguration["authorized_networks"]; okp && vp != nil {
   733  				_authorizedNetworksList := vp.([]interface{})
   734  				_ipc_map := make(map[string]interface{})
   735  				// First keep track of locally defined ip configurations
   736  				for _, _ipc := range _authorizedNetworksList {
   737  					if _ipc == nil {
   738  						continue
   739  					}
   740  					_entry := _ipc.(map[string]interface{})
   741  					if _entry["value"] == nil {
   742  						continue
   743  					}
   744  					_value := make(map[string]interface{})
   745  					_value["name"] = _entry["name"]
   746  					_value["expiration_time"] = _entry["expiration_time"]
   747  					// We key on value, since that is the only required part of
   748  					// this 3-tuple
   749  					_ipc_map[_entry["value"].(string)] = _value
   750  				}
   751  				_authorized_networks := make([]interface{}, 0)
   752  				// Next read the network tuples from the server, and reinsert those that
   753  				// correspond to ones defined locally
   754  				for _, entry := range settings.IpConfiguration.AuthorizedNetworks {
   755  					if _, okp := _ipc_map[entry.Value]; okp {
   756  						_entry := make(map[string]interface{})
   757  						_entry["value"] = entry.Value
   758  						_entry["name"] = entry.Name
   759  						_entry["expiration_time"] = entry.ExpirationTime
   760  						_authorized_networks = append(_authorized_networks, _entry)
   761  					}
   762  				}
   763  				_ipConfiguration["authorized_networks"] = _authorized_networks
   764  			}
   765  			_ipConfigurationList[0] = _ipConfiguration
   766  			_settings["ip_configuration"] = _ipConfigurationList
   767  		}
   768  	}
   769  
   770  	if v, ok := _settings["location_preference"]; ok && len(v.([]interface{})) > 0 {
   771  		_locationPreferenceList := v.([]interface{})
   772  		if len(_locationPreferenceList) > 1 {
   773  			return fmt.Errorf("At most one location_preference block is allowed")
   774  		}
   775  
   776  		if len(_locationPreferenceList) == 1 && _locationPreferenceList[0] != nil &&
   777  			settings.LocationPreference != nil {
   778  			_locationPreference := _locationPreferenceList[0].(map[string]interface{})
   779  
   780  			if vp, okp := _locationPreference["follow_gae_application"]; okp && vp != nil {
   781  				_locationPreference["follow_gae_application"] =
   782  					settings.LocationPreference.FollowGaeApplication
   783  			}
   784  
   785  			if vp, okp := _locationPreference["zone"]; okp && vp != nil {
   786  				_locationPreference["zone"] = settings.LocationPreference.Zone
   787  			}
   788  
   789  			_locationPreferenceList[0] = _locationPreference
   790  			_settings["location_preference"] = _locationPreferenceList[0]
   791  		}
   792  	}
   793  
   794  	if v, ok := _settings["maintenance_window"]; ok && len(v.([]interface{})) > 0 &&
   795  		settings.MaintenanceWindow != nil {
   796  		_maintenanceWindow := v.([]interface{})[0].(map[string]interface{})
   797  
   798  		if vp, okp := _maintenanceWindow["day"]; okp && vp != nil {
   799  			_maintenanceWindow["day"] = settings.MaintenanceWindow.Day
   800  		}
   801  
   802  		if vp, okp := _maintenanceWindow["hour"]; okp && vp != nil {
   803  			_maintenanceWindow["hour"] = settings.MaintenanceWindow.Hour
   804  		}
   805  
   806  		if vp, ok := _maintenanceWindow["update_track"]; ok && vp != nil {
   807  			if len(vp.(string)) > 0 {
   808  				_maintenanceWindow["update_track"] = settings.MaintenanceWindow.UpdateTrack
   809  			}
   810  		}
   811  	}
   812  
   813  	if v, ok := _settings["pricing_plan"]; ok && len(v.(string)) > 0 {
   814  		_settings["pricing_plan"] = settings.PricingPlan
   815  	}
   816  
   817  	if v, ok := _settings["replication_type"]; ok && len(v.(string)) > 0 {
   818  		_settings["replication_type"] = settings.ReplicationType
   819  	}
   820  
   821  	_settingsList[0] = _settings
   822  	d.Set("settings", _settingsList)
   823  
   824  	if v, ok := d.GetOk("replica_configuration"); ok && v != nil {
   825  		_replicaConfigurationList := v.([]interface{})
   826  		if len(_replicaConfigurationList) > 1 {
   827  			return fmt.Errorf("Only one replica_configuration block may be defined")
   828  		}
   829  
   830  		if len(_replicaConfigurationList) == 1 && _replicaConfigurationList[0] != nil {
   831  			mySqlReplicaConfiguration := instance.ReplicaConfiguration.MysqlReplicaConfiguration
   832  			_replicaConfiguration := _replicaConfigurationList[0].(map[string]interface{})
   833  
   834  			if vp, okp := _replicaConfiguration["ca_certificate"]; okp && vp != nil {
   835  				_replicaConfiguration["ca_certificate"] = mySqlReplicaConfiguration.CaCertificate
   836  			}
   837  
   838  			if vp, okp := _replicaConfiguration["client_certificate"]; okp && vp != nil {
   839  				_replicaConfiguration["client_certificate"] = mySqlReplicaConfiguration.ClientCertificate
   840  			}
   841  
   842  			if vp, okp := _replicaConfiguration["client_key"]; okp && vp != nil {
   843  				_replicaConfiguration["client_key"] = mySqlReplicaConfiguration.ClientKey
   844  			}
   845  
   846  			if vp, okp := _replicaConfiguration["connect_retry_interval"]; okp && vp != nil {
   847  				_replicaConfiguration["connect_retry_interval"] = mySqlReplicaConfiguration.ConnectRetryInterval
   848  			}
   849  
   850  			if vp, okp := _replicaConfiguration["dump_file_path"]; okp && vp != nil {
   851  				_replicaConfiguration["dump_file_path"] = mySqlReplicaConfiguration.DumpFilePath
   852  			}
   853  
   854  			if vp, okp := _replicaConfiguration["master_heartbeat_period"]; okp && vp != nil {
   855  				_replicaConfiguration["master_heartbeat_period"] = mySqlReplicaConfiguration.MasterHeartbeatPeriod
   856  			}
   857  
   858  			if vp, okp := _replicaConfiguration["password"]; okp && vp != nil {
   859  				_replicaConfiguration["password"] = mySqlReplicaConfiguration.Password
   860  			}
   861  
   862  			if vp, okp := _replicaConfiguration["ssl_cipher"]; okp && vp != nil {
   863  				_replicaConfiguration["ssl_cipher"] = mySqlReplicaConfiguration.SslCipher
   864  			}
   865  
   866  			if vp, okp := _replicaConfiguration["username"]; okp && vp != nil {
   867  				_replicaConfiguration["username"] = mySqlReplicaConfiguration.Username
   868  			}
   869  
   870  			if vp, okp := _replicaConfiguration["verify_server_certificate"]; okp && vp != nil {
   871  				_replicaConfiguration["verify_server_certificate"] = mySqlReplicaConfiguration.VerifyServerCertificate
   872  			}
   873  
   874  			_replicaConfigurationList[0] = _replicaConfiguration
   875  			d.Set("replica_configuration", _replicaConfigurationList)
   876  		}
   877  	}
   878  
   879  	_ipAddresses := make([]interface{}, len(instance.IpAddresses))
   880  
   881  	for i, ip := range instance.IpAddresses {
   882  		_ipAddress := make(map[string]interface{})
   883  
   884  		_ipAddress["ip_address"] = ip.IpAddress
   885  		_ipAddress["time_to_retire"] = ip.TimeToRetire
   886  
   887  		_ipAddresses[i] = _ipAddress
   888  	}
   889  
   890  	d.Set("ip_address", _ipAddresses)
   891  
   892  	if v, ok := d.GetOk("master_instance_name"); ok && v != nil {
   893  		d.Set("master_instance_name", strings.TrimPrefix(instance.MasterInstanceName, project+":"))
   894  	}
   895  
   896  	d.Set("self_link", instance.SelfLink)
   897  	d.SetId(instance.Name)
   898  
   899  	return nil
   900  }
   901  
   902  func resourceSqlDatabaseInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
   903  	config := meta.(*Config)
   904  
   905  	project, err := getProject(d, config)
   906  	if err != nil {
   907  		return err
   908  	}
   909  
   910  	d.Partial(true)
   911  
   912  	instance, err := config.clientSqlAdmin.Instances.Get(project,
   913  		d.Get("name").(string)).Do()
   914  
   915  	if err != nil {
   916  		return fmt.Errorf("Error retrieving instance %s: %s",
   917  			d.Get("name").(string), err)
   918  	}
   919  
   920  	if d.HasChange("settings") {
   921  		_oListCast, _settingsListCast := d.GetChange("settings")
   922  		_oList := _oListCast.([]interface{})
   923  		_o := _oList[0].(map[string]interface{})
   924  		_settingsList := _settingsListCast.([]interface{})
   925  		if len(_settingsList) > 1 {
   926  			return fmt.Errorf("At most one settings block is allowed")
   927  		}
   928  
   929  		_settings := _settingsList[0].(map[string]interface{})
   930  		settings := &sqladmin.Settings{
   931  			Tier:            _settings["tier"].(string),
   932  			SettingsVersion: instance.Settings.SettingsVersion,
   933  		}
   934  
   935  		if v, ok := _settings["activation_policy"]; ok {
   936  			settings.ActivationPolicy = v.(string)
   937  		}
   938  
   939  		if v, ok := _settings["authorized_gae_applications"]; ok {
   940  			settings.AuthorizedGaeApplications = make([]string, 0)
   941  			for _, app := range v.([]interface{}) {
   942  				settings.AuthorizedGaeApplications = append(settings.AuthorizedGaeApplications,
   943  					app.(string))
   944  			}
   945  		}
   946  
   947  		if v, ok := _settings["backup_configuration"]; ok {
   948  			_backupConfigurationList := v.([]interface{})
   949  			if len(_backupConfigurationList) > 1 {
   950  				return fmt.Errorf("At most one backup_configuration block is allowed")
   951  			}
   952  
   953  			if len(_backupConfigurationList) == 1 && _backupConfigurationList[0] != nil {
   954  				settings.BackupConfiguration = &sqladmin.BackupConfiguration{}
   955  				_backupConfiguration := _backupConfigurationList[0].(map[string]interface{})
   956  
   957  				if vp, okp := _backupConfiguration["binary_log_enabled"]; okp {
   958  					settings.BackupConfiguration.BinaryLogEnabled = vp.(bool)
   959  				}
   960  
   961  				if vp, okp := _backupConfiguration["enabled"]; okp {
   962  					settings.BackupConfiguration.Enabled = vp.(bool)
   963  				}
   964  
   965  				if vp, okp := _backupConfiguration["start_time"]; okp {
   966  					settings.BackupConfiguration.StartTime = vp.(string)
   967  				}
   968  			}
   969  		}
   970  
   971  		if v, ok := _settings["crash_safe_replication"]; ok {
   972  			settings.CrashSafeReplicationEnabled = v.(bool)
   973  		}
   974  
   975  		if v, ok := _settings["disk_autoresize"]; ok && v.(bool) {
   976  			settings.StorageAutoResize = v.(bool)
   977  		}
   978  
   979  		if v, ok := _settings["disk_size"]; ok {
   980  			if v.(int) > 0 && int64(v.(int)) > instance.Settings.DataDiskSizeGb {
   981  				settings.DataDiskSizeGb = int64(v.(int))
   982  			}
   983  		}
   984  
   985  		if v, ok := _settings["disk_type"]; ok && len(v.(string)) > 0 {
   986  			settings.DataDiskType = v.(string)
   987  		}
   988  
   989  		_oldDatabaseFlags := make([]interface{}, 0)
   990  		if ov, ook := _o["database_flags"]; ook {
   991  			_oldDatabaseFlags = ov.([]interface{})
   992  		}
   993  
   994  		if v, ok := _settings["database_flags"]; ok || len(_oldDatabaseFlags) > 0 {
   995  			oldDatabaseFlags := settings.DatabaseFlags
   996  			settings.DatabaseFlags = make([]*sqladmin.DatabaseFlags, 0)
   997  			_databaseFlagsList := make([]interface{}, 0)
   998  			if v != nil {
   999  				_databaseFlagsList = v.([]interface{})
  1000  			}
  1001  
  1002  			_odbf_map := make(map[string]interface{})
  1003  			for _, _dbf := range _oldDatabaseFlags {
  1004  				_entry := _dbf.(map[string]interface{})
  1005  				_odbf_map[_entry["name"].(string)] = true
  1006  			}
  1007  
  1008  			// First read the flags from the server, and reinsert those that
  1009  			// were not previously defined
  1010  			for _, entry := range oldDatabaseFlags {
  1011  				_, ok_old := _odbf_map[entry.Name]
  1012  				if !ok_old {
  1013  					settings.DatabaseFlags = append(
  1014  						settings.DatabaseFlags, entry)
  1015  				}
  1016  			}
  1017  			// finally, insert only those that were previously defined
  1018  			// and are still defined.
  1019  			for _, _flag := range _databaseFlagsList {
  1020  				_entry := _flag.(map[string]interface{})
  1021  				flag := &sqladmin.DatabaseFlags{}
  1022  				if vp, okp := _entry["name"]; okp {
  1023  					flag.Name = vp.(string)
  1024  				}
  1025  
  1026  				if vp, okp := _entry["value"]; okp {
  1027  					flag.Value = vp.(string)
  1028  				}
  1029  
  1030  				settings.DatabaseFlags = append(settings.DatabaseFlags, flag)
  1031  			}
  1032  		}
  1033  
  1034  		if v, ok := _settings["ip_configuration"]; ok {
  1035  			_ipConfigurationList := v.([]interface{})
  1036  			if len(_ipConfigurationList) > 1 {
  1037  				return fmt.Errorf("At most one ip_configuration block is allowed")
  1038  			}
  1039  
  1040  			if len(_ipConfigurationList) == 1 && _ipConfigurationList[0] != nil {
  1041  				settings.IpConfiguration = &sqladmin.IpConfiguration{}
  1042  				_ipConfiguration := _ipConfigurationList[0].(map[string]interface{})
  1043  
  1044  				if vp, okp := _ipConfiguration["ipv4_enabled"]; okp {
  1045  					settings.IpConfiguration.Ipv4Enabled = vp.(bool)
  1046  				}
  1047  
  1048  				if vp, okp := _ipConfiguration["require_ssl"]; okp {
  1049  					settings.IpConfiguration.RequireSsl = vp.(bool)
  1050  				}
  1051  
  1052  				_oldAuthorizedNetworkList := make([]interface{}, 0)
  1053  				if ov, ook := _o["ip_configuration"]; ook {
  1054  					_oldIpConfList := ov.([]interface{})
  1055  					if len(_oldIpConfList) > 0 {
  1056  						_oldIpConf := _oldIpConfList[0].(map[string]interface{})
  1057  						if ovp, ookp := _oldIpConf["authorized_networks"]; ookp {
  1058  							_oldAuthorizedNetworkList = ovp.([]interface{})
  1059  						}
  1060  					}
  1061  				}
  1062  
  1063  				if vp, okp := _ipConfiguration["authorized_networks"]; okp || len(_oldAuthorizedNetworkList) > 0 {
  1064  					oldAuthorizedNetworks := instance.Settings.IpConfiguration.AuthorizedNetworks
  1065  					settings.IpConfiguration.AuthorizedNetworks = make([]*sqladmin.AclEntry, 0)
  1066  
  1067  					_authorizedNetworksList := make([]interface{}, 0)
  1068  					if vp != nil {
  1069  						_authorizedNetworksList = vp.([]interface{})
  1070  					}
  1071  					_oipc_map := make(map[string]interface{})
  1072  					for _, _ipc := range _oldAuthorizedNetworkList {
  1073  						_entry := _ipc.(map[string]interface{})
  1074  						_oipc_map[_entry["value"].(string)] = true
  1075  					}
  1076  					// Next read the network tuples from the server, and reinsert those that
  1077  					// were not previously defined
  1078  					for _, entry := range oldAuthorizedNetworks {
  1079  						_, ok_old := _oipc_map[entry.Value]
  1080  						if !ok_old {
  1081  							settings.IpConfiguration.AuthorizedNetworks = append(
  1082  								settings.IpConfiguration.AuthorizedNetworks, entry)
  1083  						}
  1084  					}
  1085  					// finally, update old entries and insert new ones
  1086  					// and are still defined.
  1087  					for _, _ipc := range _authorizedNetworksList {
  1088  						_entry := _ipc.(map[string]interface{})
  1089  						entry := &sqladmin.AclEntry{}
  1090  
  1091  						if vpp, okpp := _entry["expiration_time"]; okpp {
  1092  							entry.ExpirationTime = vpp.(string)
  1093  						}
  1094  
  1095  						if vpp, okpp := _entry["name"]; okpp {
  1096  							entry.Name = vpp.(string)
  1097  						}
  1098  
  1099  						if vpp, okpp := _entry["value"]; okpp {
  1100  							entry.Value = vpp.(string)
  1101  						}
  1102  
  1103  						settings.IpConfiguration.AuthorizedNetworks = append(
  1104  							settings.IpConfiguration.AuthorizedNetworks, entry)
  1105  					}
  1106  				}
  1107  			}
  1108  		}
  1109  
  1110  		if v, ok := _settings["location_preference"]; ok {
  1111  			_locationPreferenceList := v.([]interface{})
  1112  			if len(_locationPreferenceList) > 1 {
  1113  				return fmt.Errorf("At most one location_preference block is allowed")
  1114  			}
  1115  
  1116  			if len(_locationPreferenceList) == 1 && _locationPreferenceList[0] != nil {
  1117  				settings.LocationPreference = &sqladmin.LocationPreference{}
  1118  				_locationPreference := _locationPreferenceList[0].(map[string]interface{})
  1119  
  1120  				if vp, okp := _locationPreference["follow_gae_application"]; okp {
  1121  					settings.LocationPreference.FollowGaeApplication = vp.(string)
  1122  				}
  1123  
  1124  				if vp, okp := _locationPreference["zone"]; okp {
  1125  					settings.LocationPreference.Zone = vp.(string)
  1126  				}
  1127  			}
  1128  		}
  1129  
  1130  		if v, ok := _settings["maintenance_window"]; ok && len(v.([]interface{})) > 0 {
  1131  			settings.MaintenanceWindow = &sqladmin.MaintenanceWindow{}
  1132  			_maintenanceWindow := v.([]interface{})[0].(map[string]interface{})
  1133  
  1134  			if vp, okp := _maintenanceWindow["day"]; okp {
  1135  				settings.MaintenanceWindow.Day = int64(vp.(int))
  1136  			}
  1137  
  1138  			if vp, okp := _maintenanceWindow["hour"]; okp {
  1139  				settings.MaintenanceWindow.Hour = int64(vp.(int))
  1140  			}
  1141  
  1142  			if vp, ok := _maintenanceWindow["update_track"]; ok {
  1143  				if len(vp.(string)) > 0 {
  1144  					settings.MaintenanceWindow.UpdateTrack = vp.(string)
  1145  				}
  1146  			}
  1147  		}
  1148  
  1149  		if v, ok := _settings["pricing_plan"]; ok {
  1150  			settings.PricingPlan = v.(string)
  1151  		}
  1152  
  1153  		if v, ok := _settings["replication_type"]; ok {
  1154  			settings.ReplicationType = v.(string)
  1155  		}
  1156  
  1157  		instance.Settings = settings
  1158  	}
  1159  
  1160  	d.Partial(false)
  1161  
  1162  	op, err := config.clientSqlAdmin.Instances.Update(project, instance.Name, instance).Do()
  1163  	if err != nil {
  1164  		return fmt.Errorf("Error, failed to update instance %s: %s", instance.Name, err)
  1165  	}
  1166  
  1167  	err = sqladminOperationWait(config, op, "Create Instance")
  1168  	if err != nil {
  1169  		return err
  1170  	}
  1171  
  1172  	return resourceSqlDatabaseInstanceRead(d, meta)
  1173  }
  1174  
  1175  func resourceSqlDatabaseInstanceDelete(d *schema.ResourceData, meta interface{}) error {
  1176  	config := meta.(*Config)
  1177  
  1178  	project, err := getProject(d, config)
  1179  	if err != nil {
  1180  		return err
  1181  	}
  1182  
  1183  	op, err := config.clientSqlAdmin.Instances.Delete(project, d.Get("name").(string)).Do()
  1184  
  1185  	if err != nil {
  1186  		return fmt.Errorf("Error, failed to delete instance %s: %s", d.Get("name").(string), err)
  1187  	}
  1188  
  1189  	err = sqladminOperationWait(config, op, "Delete Instance")
  1190  	if err != nil {
  1191  		return err
  1192  	}
  1193  
  1194  	return nil
  1195  }
  1196  
  1197  func validateNumericRange(v interface{}, k string, min int, max int) (ws []string, errors []error) {
  1198  	value := v.(int)
  1199  	if min > value || value > max {
  1200  		errors = append(errors, fmt.Errorf(
  1201  			"%q outside range %d-%d.", k, min, max))
  1202  	}
  1203  	return
  1204  }