From 5ac476e201e96a52a9d3f45b97e6c7906ddfeb11 Mon Sep 17 00:00:00 2001 From: Andrej Friesen Date: Tue, 12 Dec 2023 15:13:50 +0100 Subject: [PATCH 01/15] Create mysql8_0 resources from mysql --- gridscale/resource_gridscale_mysql8_0.go | 625 ++++++++++++++++++ gridscale/resource_gridscale_mysql_test8_0.go | 63 ++ 2 files changed, 688 insertions(+) create mode 100644 gridscale/resource_gridscale_mysql8_0.go create mode 100644 gridscale/resource_gridscale_mysql_test8_0.go diff --git a/gridscale/resource_gridscale_mysql8_0.go b/gridscale/resource_gridscale_mysql8_0.go new file mode 100644 index 000000000..7bd508df0 --- /dev/null +++ b/gridscale/resource_gridscale_mysql8_0.go @@ -0,0 +1,625 @@ +package gridscale + +import ( + "context" + "errors" + "fmt" + "net/http" + "regexp" + "strings" + "time" + + "github.com/gridscale/gsclient-go/v3" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + errHandler "github.com/terraform-providers/terraform-provider-gridscale/gridscale/error-handler" + + "log" +) + +const mysqlTemplateFlavourName = "mysql" + +func resourceGridscaleMySQL() *schema.Resource { + return &schema.Resource{ + Create: resourceGridscaleMySQLCreate, + Read: resourceGridscaleMySQLRead, + Delete: resourceGridscaleMySQLDelete, + Update: resourceGridscaleMySQLUpdate, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + CustomizeDiff: func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error { + client := meta.(*gsclient.Client) + paasTemplates, err := client.GetPaaSTemplateList(ctx) + if err != nil { + return err + } + + releaseVal := d.Get("release").(string) + perfClassVal := d.Get("performance_class").(string) + var chosenTemplate gsclient.PaaSTemplate + var isReleasePerfClassValid bool + releaseWPerfClasess := make(map[string][]string) + for _, template := range paasTemplates { + if template.Properties.Flavour == mysqlTemplateFlavourName { + perfClasses := releaseWPerfClasess[template.Properties.Release] + releaseWPerfClasess[template.Properties.Release] = append(perfClasses, template.Properties.PerformanceClass) + if template.Properties.Release == releaseVal && template.Properties.PerformanceClass == perfClassVal { + isReleasePerfClassValid = true + chosenTemplate = template + } + } + } + if !isReleasePerfClassValid { + errMess := fmt.Sprintf("release %v with performance class %s is not a valid MySQL release/performance class. Valid releases with corresponding performance classes are:\n\t", releaseVal, perfClassVal) + for release, perfClasses := range releaseWPerfClasess { + errMess += fmt.Sprintf("release %s has following perfomance classes: %s\n\t", release, strings.Join(perfClasses, ", ")) + } + return errors.New(errMess) + } + return validateMySQLParameters(d, chosenTemplate) + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Description: "The human-readable name of the object. It supports the full UTF-8 character set, with a maximum of 64 characters.", + Required: true, + ValidateFunc: validation.NoZeroValues, + }, + "release": { + Type: schema.TypeString, + Description: `The MySQL release of this instance.\n + For convenience, please use gscloud https://github.com/gridscale/gscloud to get the list of available MySQL service releases.`, + Required: true, + ValidateFunc: validation.NoZeroValues, + }, + "performance_class": { + Type: schema.TypeString, + Description: "Performance class of MySQL service.", + Required: true, + ValidateFunc: validation.NoZeroValues, + }, + "mysql_log_bin": { + Type: schema.TypeBool, + Description: "Binary Logging.", + Optional: true, + Default: false, + }, + "mysql_sql_mode": { + Type: schema.TypeString, + Description: "SQL Mode.", + Optional: true, + Default: "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION", + }, + "mysql_server_id": { + Type: schema.TypeInt, + Description: "Server Id.", + Optional: true, + Default: 1, + }, + "mysql_query_cache": { + Type: schema.TypeBool, + Description: "Enable query cache.", + Optional: true, + Default: true, + }, + "mysql_binlog_format": { + Type: schema.TypeString, + Description: "Binary Logging Format.", + Optional: true, + Default: "ROW", + }, + "mysql_max_connections": { + Type: schema.TypeInt, + Description: "Max Connections.", + Optional: true, + Default: 4000, + }, + "mysql_query_cache_size": { + Type: schema.TypeString, + Description: "Query Cache Size. Format: xM (where x is an integer, M stands for unit: k(kB), M(MB), G(GB)).", + Optional: true, + Default: "128M", + }, + "mysql_default_time_zone": { + Type: schema.TypeString, + Description: "Server Timezone.", + Optional: true, + Default: "UTC", + }, + "mysql_query_cache_limit": { + Type: schema.TypeString, + Description: "Query Cache Limit. Format: xM (where x is an integer, M stands for unit: k(kB), M(MB), G(GB)).", + Optional: true, + Default: "1M", + }, + "mysql_max_allowed_packet": { + Type: schema.TypeString, + Description: "Max Allowed Packet Size. Format: xM (where x is an integer, M stands for unit: k(kB), M(MB), G(GB)).", + Optional: true, + Default: "64M", + }, + "username": { + Type: schema.TypeString, + Description: "Username for MySQL service. It is used to connect to the MySQL instance.", + Computed: true, + Sensitive: true, + }, + "password": { + Type: schema.TypeString, + Description: "Password for MySQL service. It is used to connect to the MySQL instance.", + Computed: true, + Sensitive: true, + }, + "listen_port": { + Type: schema.TypeSet, + Description: "The port numbers where this MySQL service accepts connections.", + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "host": { + Type: schema.TypeString, + Computed: true, + }, + "port": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "security_zone_uuid": { + Type: schema.TypeString, + Description: "Security zone UUID linked to MySQL service.", + Deprecated: "Security zone is deprecated for gridSQL, gridStore, and gridFs. Please consider to use private network instead.", + Optional: true, + ForceNew: true, + Computed: true, + }, + "network_uuid": { + Type: schema.TypeString, + Description: "The UUID of the network that the service is attached to.", + Optional: true, + Computed: true, + }, + "service_template_uuid": { + Type: schema.TypeString, + Description: "PaaS service template that MySQL service uses.", + Computed: true, + }, + "service_template_category": { + Type: schema.TypeString, + Computed: true, + Description: "The template service's category used to create the service.", + }, + "usage_in_minutes": { + Type: schema.TypeInt, + Description: "Number of minutes that MySQL service is in use.", + Computed: true, + }, + "change_time": { + Type: schema.TypeString, + Description: "Time of the last change.", + Computed: true, + }, + "create_time": { + Type: schema.TypeString, + Description: "Date time this service has been created.", + Computed: true, + }, + "status": { + Type: schema.TypeString, + Description: "Current status of MySQL service.", + Computed: true, + }, + "max_core_count": { + Type: schema.TypeInt, + Description: "Maximum CPU core count. The MySQL instance's CPU core count will be autoscaled based on the workload. The number of cores stays between 1 and `max_core_count`.", + Optional: true, + Computed: true, + ValidateFunc: validation.NoZeroValues, + }, + "labels": { + Type: schema.TypeSet, + Description: "List of labels.", + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(15 * time.Minute), + Update: schema.DefaultTimeout(15 * time.Minute), + Delete: schema.DefaultTimeout(15 * time.Minute), + }, + } +} + +func resourceGridscaleMySQLRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*gsclient.Client) + errorPrefix := fmt.Sprintf("read mysql (%s) resource -", d.Id()) + paas, err := client.GetPaaSService(context.Background(), d.Id()) + if err != nil { + if requestError, ok := err.(gsclient.RequestError); ok { + if requestError.StatusCode == 404 { + d.SetId("") + return nil + } + } + return fmt.Errorf("%s error: %v", errorPrefix, err) + } + props := paas.Properties + creds := props.Credentials + if err = d.Set("name", props.Name); err != nil { + return fmt.Errorf("%s error setting name: %v", errorPrefix, err) + } + if len(creds) > 0 { + if err = d.Set("username", creds[0].Username); err != nil { + return fmt.Errorf("%s error setting username: %v", errorPrefix, err) + } + if err = d.Set("password", creds[0].Password); err != nil { + return fmt.Errorf("%s error setting password: %v", errorPrefix, err) + } + } + if err = d.Set("network_uuid", props.NetworkUUID); err != nil { + return fmt.Errorf("%s error setting network_uuid: %v", errorPrefix, err) + } + if err = d.Set("security_zone_uuid", props.SecurityZoneUUID); err != nil { + return fmt.Errorf("%s error setting security_zone_uuid: %v", errorPrefix, err) + } + if err = d.Set("service_template_category", props.ServiceTemplateCategory); err != nil { + return fmt.Errorf("%s error setting service_template_category: %v", errorPrefix, err) + } + if err = d.Set("service_template_uuid", props.ServiceTemplateUUID); err != nil { + return fmt.Errorf("%s error setting service_template_uuid: %v", errorPrefix, err) + } + if err = d.Set("usage_in_minutes", props.UsageInMinutes); err != nil { + return fmt.Errorf("%s error setting usage_in_minutes: %v", errorPrefix, err) + } + if err = d.Set("change_time", props.ChangeTime.String()); err != nil { + return fmt.Errorf("%s error setting change_time: %v", errorPrefix, err) + } + if err = d.Set("create_time", props.CreateTime.String()); err != nil { + return fmt.Errorf("%s error setting create_time: %v", errorPrefix, err) + } + if err = d.Set("status", props.Status); err != nil { + return fmt.Errorf("%s error setting status: %v", errorPrefix, err) + } + + // Set MySQL parameters + if err = d.Set("mysql_log_bin", props.Parameters["mysql_log_bin"]); err != nil { + return fmt.Errorf("%s error setting mysql_log_bin: %v", errorPrefix, err) + } + if err = d.Set("mysql_sql_mode", props.Parameters["mysql_sql_mode"]); err != nil { + return fmt.Errorf("%s error setting mysql_sql_mode: %v", errorPrefix, err) + } + if err = d.Set("mysql_server_id", props.Parameters["mysql_server_id"]); err != nil { + return fmt.Errorf("%s error setting mysql_server_id: %v", errorPrefix, err) + } + if err = d.Set("mysql_query_cache", props.Parameters["mysql_query_cache"]); err != nil { + return fmt.Errorf("%s error setting mysql_query_cache: %v", errorPrefix, err) + } + if err = d.Set("mysql_binlog_format", props.Parameters["mysql_binlog_format"]); err != nil { + return fmt.Errorf("%s error setting mysql_binlog_format: %v", errorPrefix, err) + } + if err = d.Set("mysql_max_connections", props.Parameters["mysql_max_connections"]); err != nil { + return fmt.Errorf("%s error setting mysql_max_connections: %v", errorPrefix, err) + } + if err = d.Set("mysql_query_cache_size", props.Parameters["mysql_query_cache_size"]); err != nil { + return fmt.Errorf("%s error setting mysql_query_cache_size: %v", errorPrefix, err) + } + if err = d.Set("mysql_default_time_zone", props.Parameters["mysql_default_time_zone"]); err != nil { + return fmt.Errorf("%s error setting mysql_default_time_zone: %v", errorPrefix, err) + } + if err = d.Set("mysql_query_cache_limit", props.Parameters["mysql_query_cache_limit"]); err != nil { + return fmt.Errorf("%s error setting mysql_query_cache_limit: %v", errorPrefix, err) + } + if err = d.Set("mysql_max_allowed_packet", props.Parameters["mysql_max_allowed_packet"]); err != nil { + return fmt.Errorf("%s error setting mysql_max_allowed_packet: %v", errorPrefix, err) + } + //Get listen ports + listenPorts := make([]interface{}, 0) + for host, value := range props.ListenPorts { + for k, portValue := range value { + port := map[string]interface{}{ + "name": k, + "host": host, + "port": portValue, + } + listenPorts = append(listenPorts, port) + } + } + if err = d.Set("listen_port", listenPorts); err != nil { + return fmt.Errorf("%s error setting listen ports: %v", errorPrefix, err) + } + + //Get core count's limit + for _, value := range props.ResourceLimits { + if value.Resource == "cores" { + if err = d.Set("max_core_count", value.Limit); err != nil { + return fmt.Errorf("%s error setting max_core_count: %v", errorPrefix, err) + } + } + } + + //Set labels + if err = d.Set("labels", props.Labels); err != nil { + return fmt.Errorf("%s error setting labels: %v", errorPrefix, err) + } + + // Look for security zone's network that the PaaS service is connected to + // (if the paas is connected to security zone. O.w skip) + if props.SecurityZoneUUID == "" { + return nil + } + networks, err := client.GetNetworkList(context.Background()) + if err != nil { + return fmt.Errorf("%s error getting networks: %v", errorPrefix, err) + } + //look for a network that the MySQL service is in + for _, network := range networks { + securityZones := network.Properties.Relations.PaaSSecurityZones + //Each network can contain only one security zone + if len(securityZones) >= 1 { + if securityZones[0].ObjectUUID == props.SecurityZoneUUID { + if err = d.Set("network_uuid", network.Properties.ObjectUUID); err != nil { + return fmt.Errorf("%s error setting network_uuid: %v", errorPrefix, err) + } + } + } + } + return nil +} + +func resourceGridscaleMySQLCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*gsclient.Client) + errorPrefix := fmt.Sprintf("create mysql (%s) resource -", d.Id()) + + // Get mysql template UUID + release := d.Get("release").(string) + performanceClass := d.Get("performance_class").(string) + templateUUID, err := getMySQLTemplateUUID(client, release, performanceClass) + if err != nil { + return fmt.Errorf("%s error: %v", errorPrefix, err) + } + + requestBody := gsclient.PaaSServiceCreateRequest{ + Name: d.Get("name").(string), + PaaSServiceTemplateUUID: templateUUID, + Labels: convSOStrings(d.Get("labels").(*schema.Set).List()), + } + networkUUIDInf, isNetworkSet := d.GetOk("network_uuid") + if isNetworkSet { + requestBody.NetworkUUID = networkUUIDInf.(string) + } + // If network_uuid is set, skip setting security_zone_uuid. + if secZoneUUIDInf, ok := d.GetOk("security_zone_uuid"); ok && !isNetworkSet { + requestBody.PaaSSecurityZoneUUID = secZoneUUIDInf.(string) + } + if val, ok := d.GetOk("max_core_count"); ok { + limits := []gsclient.ResourceLimit{ + { + Resource: "cores", + Limit: val.(int), + }, + } + requestBody.ResourceLimits = limits + } + + params := make(map[string]interface{}) + params["mysql_log_bin"] = d.Get("mysql_log_bin") + params["mysql_sql_mode"] = d.Get("mysql_sql_mode") + params["mysql_server_id"] = d.Get("mysql_server_id") + params["mysql_query_cache"] = d.Get("mysql_query_cache") + params["mysql_binlog_format"] = d.Get("mysql_binlog_format") + params["mysql_max_connections"] = d.Get("mysql_max_connections") + params["mysql_query_cache_size"] = d.Get("mysql_query_cache_size") + params["mysql_default_time_zone"] = d.Get("mysql_default_time_zone") + params["mysql_query_cache_limit"] = d.Get("mysql_query_cache_limit") + params["mysql_max_allowed_packet"] = d.Get("mysql_max_allowed_packet") + requestBody.Parameters = params + + ctx, cancel := context.WithTimeout(context.Background(), d.Timeout(schema.TimeoutCreate)) + defer cancel() + response, err := client.CreatePaaSService(ctx, requestBody) + if err != nil { + return err + } + d.SetId(response.ObjectUUID) + log.Printf("The id for MySQL service %s has been set to %v", requestBody.Name, response.ObjectUUID) + return resourceGridscaleMySQLRead(d, meta) +} + +func resourceGridscaleMySQLUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*gsclient.Client) + errorPrefix := fmt.Sprintf("update mysql (%s) resource -", d.Id()) + + labels := convSOStrings(d.Get("labels").(*schema.Set).List()) + requestBody := gsclient.PaaSServiceUpdateRequest{ + Name: d.Get("name").(string), + Labels: &labels, + } + if d.HasChange("network_uuid") { + requestBody.NetworkUUID = d.Get("network_uuid").(string) + } + // Only update templateUUID, when `release` or `performance_class` is changed + if d.HasChange("release") || d.HasChange("performance_class") { + // Get mysql template UUID + release := d.Get("release").(string) + performanceClass := d.Get("performance_class").(string) + templateUUID, err := getMySQLTemplateUUID(client, release, performanceClass) + if err != nil { + return fmt.Errorf("%s error: %v", errorPrefix, err) + } + requestBody.PaaSServiceTemplateUUID = templateUUID + } + + if val, ok := d.GetOk("max_core_count"); ok { + limits := []gsclient.ResourceLimit{ + { + Resource: "cores", + Limit: val.(int), + }, + } + requestBody.ResourceLimits = limits + } + + params := make(map[string]interface{}) + params["mysql_log_bin"] = d.Get("mysql_log_bin") + params["mysql_sql_mode"] = d.Get("mysql_sql_mode") + params["mysql_server_id"] = d.Get("mysql_server_id") + params["mysql_query_cache"] = d.Get("mysql_query_cache") + params["mysql_binlog_format"] = d.Get("mysql_binlog_format") + params["mysql_max_connections"] = d.Get("mysql_max_connections") + params["mysql_query_cache_size"] = d.Get("mysql_query_cache_size") + params["mysql_default_time_zone"] = d.Get("mysql_default_time_zone") + params["mysql_query_cache_limit"] = d.Get("mysql_query_cache_limit") + params["mysql_max_allowed_packet"] = d.Get("mysql_max_allowed_packet") + requestBody.Parameters = params + + ctx, cancel := context.WithTimeout(context.Background(), d.Timeout(schema.TimeoutUpdate)) + defer cancel() + err := client.UpdatePaaSService(ctx, d.Id(), requestBody) + if err != nil { + return fmt.Errorf("%s error: %v", errorPrefix, err) + } + return resourceGridscaleMySQLRead(d, meta) +} + +func resourceGridscaleMySQLDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*gsclient.Client) + errorPrefix := fmt.Sprintf("delete mysql (%s) resource -", d.Id()) + + ctx, cancel := context.WithTimeout(context.Background(), d.Timeout(schema.TimeoutDelete)) + defer cancel() + err := errHandler.SuppressHTTPErrorCodes( + client.DeletePaaSService(ctx, d.Id()), + http.StatusNotFound, + ) + if err != nil { + return fmt.Errorf("%s error: %v", errorPrefix, err) + } + return nil +} + +// getMySQLTemplateUUID returns the UUID of the mysql service template. +func getMySQLTemplateUUID(client *gsclient.Client, release, performanceClass string) (string, error) { + paasTemplates, err := client.GetPaaSTemplateList(context.Background()) + if err != nil { + return "", err + } + var isReleaseValid bool + var releases []string + var uTemplate gsclient.PaaSTemplate + for _, template := range paasTemplates { + if template.Properties.Flavour == mysqlTemplateFlavourName { + releases = append(releases, template.Properties.Release) + if template.Properties.Release == release && template.Properties.PerformanceClass == performanceClass { + isReleaseValid = true + uTemplate = template + } + } + } + if !isReleaseValid { + return "", fmt.Errorf("%v is not a valid MySQL release. Valid releases are: %v\n", release, strings.Join(releases, ", ")) + } + + return uTemplate.Properties.ObjectUUID, nil +} + +func validateMySQLParameters(d *schema.ResourceDiff, template gsclient.PaaSTemplate) error { + var errorMessages []string + if sqlMode, ok := d.GetOk("mysql_sql_mode"); ok { + if scheme, ok := template.Properties.ParametersSchema["mysql_sql_mode"]; ok { + validMode := regexp.MustCompile(scheme.Regex) + if !validMode.MatchString(sqlMode.(string)) { + errorMessages = append(errorMessages, fmt.Sprintf("Invalid 'mysql_sql_mode' value. Example value: '%s'\n", scheme.Default)) + } + } + } + if serverID, ok := d.GetOk("mysql_server_id"); ok { + if scheme, ok := template.Properties.ParametersSchema["mysql_server_id"]; ok { + if scheme.Min > serverID.(int) || serverID.(int) > scheme.Max { + errorMessages = append(errorMessages, fmt.Sprintf("Invalid 'mysql_server_id' value. Value must stays between %d and %d\n", scheme.Min, scheme.Max)) + } + } + } + if binLogFormat, ok := d.GetOk("mysql_binlog_format"); ok { + if scheme, ok := template.Properties.ParametersSchema["mysql_binlog_format"]; ok { + var isValidFormat bool + for _, allowedValue := range scheme.Allowed { + if binLogFormat.(string) == allowedValue { + isValidFormat = true + } + } + if !isValidFormat { + errorMessages = append(errorMessages, + fmt.Sprintf("Invalid 'mysql_binlog_format' value. Value must be one of these:\n\t%s\n", + strings.Join(scheme.Allowed, "\n\t"), + ), + ) + } + } + } + if maxNConn, ok := d.GetOk("mysql_max_connections"); ok { + if scheme, ok := template.Properties.ParametersSchema["mysql_max_connections"]; ok { + if scheme.Min > maxNConn.(int) || maxNConn.(int) > scheme.Max { + errorMessages = append(errorMessages, fmt.Sprintf("Invalid 'mysql_max_connections' value. Value must stays between %d and %d\n", scheme.Min, scheme.Max)) + } + } + } + if cacheSize, ok := d.GetOk("mysql_query_cache_size"); ok { + if scheme, ok := template.Properties.ParametersSchema["mysql_query_cache_size"]; ok { + validMode := regexp.MustCompile(scheme.Regex) + if !validMode.MatchString(cacheSize.(string)) { + errorMessages = append(errorMessages, fmt.Sprintf("Invalid 'mysql_query_cache_size' value. Example value: '%s'\n", scheme.Default)) + } + } + } + if defaultTimeZone, ok := d.GetOk("mysql_default_time_zone"); ok { + if scheme, ok := template.Properties.ParametersSchema["mysql_default_time_zone"]; ok { + var isValidFormat bool + for _, allowedValue := range scheme.Allowed { + if defaultTimeZone.(string) == allowedValue { + isValidFormat = true + } + } + if !isValidFormat { + errorMessages = append(errorMessages, + fmt.Sprintf("Invalid 'mysql_default_time_zone' value. Value must be one of these:\n\t%s", + strings.Join(scheme.Allowed, "\n\t"), + ), + ) + } + } + } + if cacheLimit, ok := d.GetOk("mysql_query_cache_limit"); ok { + if scheme, ok := template.Properties.ParametersSchema["mysql_query_cache_limit"]; ok { + validMode := regexp.MustCompile(scheme.Regex) + if !validMode.MatchString(cacheLimit.(string)) { + errorMessages = append(errorMessages, fmt.Sprintf("Invalid 'mysql_query_cache_limit' value. Example value: '%s'\n", scheme.Default)) + } + } + } + if maxAllowedPacket, ok := d.GetOk("mysql_max_allowed_packet"); ok { + if scheme, ok := template.Properties.ParametersSchema["mysql_max_allowed_packet"]; ok { + validMode := regexp.MustCompile(scheme.Regex) + if !validMode.MatchString(maxAllowedPacket.(string)) { + errorMessages = append(errorMessages, fmt.Sprintf("Invalid 'mysql_max_allowed_packet' value. Example value: '%s'\n", scheme.Default)) + } + } + } + if maxNCore, ok := d.GetOk("max_core_count"); ok { + autoscalingNCore := template.Properties.Autoscaling.Cores + if autoscalingNCore.Min > maxNCore.(int) || maxNCore.(int) > autoscalingNCore.Max { + errorMessages = append(errorMessages, fmt.Sprintf("Invalid 'max_core_count' value. Value must stays between %d and %d\n", autoscalingNCore.Min, autoscalingNCore.Max)) + } + } + if len(errorMessages) != 0 { + return errors.New(strings.Join(errorMessages, "")) + } + return nil +} diff --git a/gridscale/resource_gridscale_mysql_test8_0.go b/gridscale/resource_gridscale_mysql_test8_0.go new file mode 100644 index 000000000..ca92c05e3 --- /dev/null +++ b/gridscale/resource_gridscale_mysql_test8_0.go @@ -0,0 +1,63 @@ +package gridscale + +import ( + "fmt" + + "github.com/gridscale/gsclient-go/v3" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "testing" +) + +func TestAccResourceGridscaleMySQL_Basic(t *testing.T) { + var object gsclient.PaaSService + name := fmt.Sprintf("MySQL-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckResourceGridscalePaaSDestroyCheck, + Steps: []resource.TestStep{ + { + Config: testAccCheckResourceGridscaleMySQLConfig_basic(name), + Check: resource.ComposeTestCheckFunc( + testAccCheckResourceGridscalePaaSExists("gridscale_mysql.test", &object), + resource.TestCheckResourceAttr( + "gridscale_mysql.test", "name", name), + ), + }, + { + Config: testAccCheckResourceGridscaleMySQLConfig_basic_update(), + Check: resource.ComposeTestCheckFunc( + testAccCheckResourceGridscalePaaSExists("gridscale_mysql.test", &object), + resource.TestCheckResourceAttr( + "gridscale_mysql.test", "name", "newname"), + ), + }, + }, + }) +} + +func testAccCheckResourceGridscaleMySQLConfig_basic(name string) string { + return fmt.Sprintf(` +resource "gridscale_mysql" "test" { + name = "%s" + release = "5.7" + performance_class = "standard" +} +`, name) +} + +func testAccCheckResourceGridscaleMySQLConfig_basic_update() string { + return fmt.Sprintf(` +resource "gridscale_mysql" "test" { + name = "newname" + release = "5.7" + performance_class = "standard" + max_core_count = 20 + mysql_max_connections = 2000 + labels = ["test"] +} +`) +} From 86c92ec722fd3eeefc2d6577eb3dcf1c1c7a0e70 Mon Sep 17 00:00:00 2001 From: Andrej Friesen Date: Tue, 12 Dec 2023 15:20:31 +0100 Subject: [PATCH 02/15] Remove deprecated parameters for mysql8_0 We deprecated server id, query cache and bin log related fields in the parameters for mysql 8.0 --- gridscale/resource_gridscale_mysql8_0.go | 110 +---------------------- 1 file changed, 2 insertions(+), 108 deletions(-) diff --git a/gridscale/resource_gridscale_mysql8_0.go b/gridscale/resource_gridscale_mysql8_0.go index 7bd508df0..9f9bb55d3 100644 --- a/gridscale/resource_gridscale_mysql8_0.go +++ b/gridscale/resource_gridscale_mysql8_0.go @@ -79,35 +79,11 @@ func resourceGridscaleMySQL() *schema.Resource { Required: true, ValidateFunc: validation.NoZeroValues, }, - "mysql_log_bin": { - Type: schema.TypeBool, - Description: "Binary Logging.", - Optional: true, - Default: false, - }, "mysql_sql_mode": { Type: schema.TypeString, Description: "SQL Mode.", Optional: true, - Default: "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION", - }, - "mysql_server_id": { - Type: schema.TypeInt, - Description: "Server Id.", - Optional: true, - Default: 1, - }, - "mysql_query_cache": { - Type: schema.TypeBool, - Description: "Enable query cache.", - Optional: true, - Default: true, - }, - "mysql_binlog_format": { - Type: schema.TypeString, - Description: "Binary Logging Format.", - Optional: true, - Default: "ROW", + Default: "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION", }, "mysql_max_connections": { Type: schema.TypeInt, @@ -115,24 +91,12 @@ func resourceGridscaleMySQL() *schema.Resource { Optional: true, Default: 4000, }, - "mysql_query_cache_size": { - Type: schema.TypeString, - Description: "Query Cache Size. Format: xM (where x is an integer, M stands for unit: k(kB), M(MB), G(GB)).", - Optional: true, - Default: "128M", - }, "mysql_default_time_zone": { Type: schema.TypeString, Description: "Server Timezone.", Optional: true, Default: "UTC", }, - "mysql_query_cache_limit": { - Type: schema.TypeString, - Description: "Query Cache Limit. Format: xM (where x is an integer, M stands for unit: k(kB), M(MB), G(GB)).", - Optional: true, - Default: "1M", - }, "mysql_max_allowed_packet": { Type: schema.TypeString, Description: "Max Allowed Packet Size. Format: xM (where x is an integer, M stands for unit: k(kB), M(MB), G(GB)).", @@ -290,33 +254,15 @@ func resourceGridscaleMySQLRead(d *schema.ResourceData, meta interface{}) error } // Set MySQL parameters - if err = d.Set("mysql_log_bin", props.Parameters["mysql_log_bin"]); err != nil { - return fmt.Errorf("%s error setting mysql_log_bin: %v", errorPrefix, err) - } if err = d.Set("mysql_sql_mode", props.Parameters["mysql_sql_mode"]); err != nil { return fmt.Errorf("%s error setting mysql_sql_mode: %v", errorPrefix, err) } - if err = d.Set("mysql_server_id", props.Parameters["mysql_server_id"]); err != nil { - return fmt.Errorf("%s error setting mysql_server_id: %v", errorPrefix, err) - } - if err = d.Set("mysql_query_cache", props.Parameters["mysql_query_cache"]); err != nil { - return fmt.Errorf("%s error setting mysql_query_cache: %v", errorPrefix, err) - } - if err = d.Set("mysql_binlog_format", props.Parameters["mysql_binlog_format"]); err != nil { - return fmt.Errorf("%s error setting mysql_binlog_format: %v", errorPrefix, err) - } if err = d.Set("mysql_max_connections", props.Parameters["mysql_max_connections"]); err != nil { return fmt.Errorf("%s error setting mysql_max_connections: %v", errorPrefix, err) } - if err = d.Set("mysql_query_cache_size", props.Parameters["mysql_query_cache_size"]); err != nil { - return fmt.Errorf("%s error setting mysql_query_cache_size: %v", errorPrefix, err) - } - if err = d.Set("mysql_default_time_zone", props.Parameters["mysql_default_time_zone"]); err != nil { + if err = d.Set("mysql_default_time_zone", props.Parameters["mysql_default_time_zone"]); err != nil { return fmt.Errorf("%s error setting mysql_default_time_zone: %v", errorPrefix, err) } - if err = d.Set("mysql_query_cache_limit", props.Parameters["mysql_query_cache_limit"]); err != nil { - return fmt.Errorf("%s error setting mysql_query_cache_limit: %v", errorPrefix, err) - } if err = d.Set("mysql_max_allowed_packet", props.Parameters["mysql_max_allowed_packet"]); err != nil { return fmt.Errorf("%s error setting mysql_max_allowed_packet: %v", errorPrefix, err) } @@ -410,15 +356,9 @@ func resourceGridscaleMySQLCreate(d *schema.ResourceData, meta interface{}) erro } params := make(map[string]interface{}) - params["mysql_log_bin"] = d.Get("mysql_log_bin") params["mysql_sql_mode"] = d.Get("mysql_sql_mode") - params["mysql_server_id"] = d.Get("mysql_server_id") - params["mysql_query_cache"] = d.Get("mysql_query_cache") - params["mysql_binlog_format"] = d.Get("mysql_binlog_format") params["mysql_max_connections"] = d.Get("mysql_max_connections") - params["mysql_query_cache_size"] = d.Get("mysql_query_cache_size") params["mysql_default_time_zone"] = d.Get("mysql_default_time_zone") - params["mysql_query_cache_limit"] = d.Get("mysql_query_cache_limit") params["mysql_max_allowed_packet"] = d.Get("mysql_max_allowed_packet") requestBody.Parameters = params @@ -468,15 +408,9 @@ func resourceGridscaleMySQLUpdate(d *schema.ResourceData, meta interface{}) erro } params := make(map[string]interface{}) - params["mysql_log_bin"] = d.Get("mysql_log_bin") params["mysql_sql_mode"] = d.Get("mysql_sql_mode") - params["mysql_server_id"] = d.Get("mysql_server_id") - params["mysql_query_cache"] = d.Get("mysql_query_cache") - params["mysql_binlog_format"] = d.Get("mysql_binlog_format") params["mysql_max_connections"] = d.Get("mysql_max_connections") - params["mysql_query_cache_size"] = d.Get("mysql_query_cache_size") params["mysql_default_time_zone"] = d.Get("mysql_default_time_zone") - params["mysql_query_cache_limit"] = d.Get("mysql_query_cache_limit") params["mysql_max_allowed_packet"] = d.Get("mysql_max_allowed_packet") requestBody.Parameters = params @@ -540,30 +474,6 @@ func validateMySQLParameters(d *schema.ResourceDiff, template gsclient.PaaSTempl } } } - if serverID, ok := d.GetOk("mysql_server_id"); ok { - if scheme, ok := template.Properties.ParametersSchema["mysql_server_id"]; ok { - if scheme.Min > serverID.(int) || serverID.(int) > scheme.Max { - errorMessages = append(errorMessages, fmt.Sprintf("Invalid 'mysql_server_id' value. Value must stays between %d and %d\n", scheme.Min, scheme.Max)) - } - } - } - if binLogFormat, ok := d.GetOk("mysql_binlog_format"); ok { - if scheme, ok := template.Properties.ParametersSchema["mysql_binlog_format"]; ok { - var isValidFormat bool - for _, allowedValue := range scheme.Allowed { - if binLogFormat.(string) == allowedValue { - isValidFormat = true - } - } - if !isValidFormat { - errorMessages = append(errorMessages, - fmt.Sprintf("Invalid 'mysql_binlog_format' value. Value must be one of these:\n\t%s\n", - strings.Join(scheme.Allowed, "\n\t"), - ), - ) - } - } - } if maxNConn, ok := d.GetOk("mysql_max_connections"); ok { if scheme, ok := template.Properties.ParametersSchema["mysql_max_connections"]; ok { if scheme.Min > maxNConn.(int) || maxNConn.(int) > scheme.Max { @@ -571,14 +481,6 @@ func validateMySQLParameters(d *schema.ResourceDiff, template gsclient.PaaSTempl } } } - if cacheSize, ok := d.GetOk("mysql_query_cache_size"); ok { - if scheme, ok := template.Properties.ParametersSchema["mysql_query_cache_size"]; ok { - validMode := regexp.MustCompile(scheme.Regex) - if !validMode.MatchString(cacheSize.(string)) { - errorMessages = append(errorMessages, fmt.Sprintf("Invalid 'mysql_query_cache_size' value. Example value: '%s'\n", scheme.Default)) - } - } - } if defaultTimeZone, ok := d.GetOk("mysql_default_time_zone"); ok { if scheme, ok := template.Properties.ParametersSchema["mysql_default_time_zone"]; ok { var isValidFormat bool @@ -596,14 +498,6 @@ func validateMySQLParameters(d *schema.ResourceDiff, template gsclient.PaaSTempl } } } - if cacheLimit, ok := d.GetOk("mysql_query_cache_limit"); ok { - if scheme, ok := template.Properties.ParametersSchema["mysql_query_cache_limit"]; ok { - validMode := regexp.MustCompile(scheme.Regex) - if !validMode.MatchString(cacheLimit.(string)) { - errorMessages = append(errorMessages, fmt.Sprintf("Invalid 'mysql_query_cache_limit' value. Example value: '%s'\n", scheme.Default)) - } - } - } if maxAllowedPacket, ok := d.GetOk("mysql_max_allowed_packet"); ok { if scheme, ok := template.Properties.ParametersSchema["mysql_max_allowed_packet"]; ok { validMode := regexp.MustCompile(scheme.Regex) From 872a1e09c22e99c97c76da2863d7f17494566c89 Mon Sep 17 00:00:00 2001 From: Andrej Friesen Date: Tue, 12 Dec 2023 15:40:20 +0100 Subject: [PATCH 03/15] Rename mysql8_0 test file --- ...t8_0.go => resource_gridscale_mysql8_0_test.go} | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) rename gridscale/{resource_gridscale_mysql_test8_0.go => resource_gridscale_mysql8_0_test.go} (76%) diff --git a/gridscale/resource_gridscale_mysql_test8_0.go b/gridscale/resource_gridscale_mysql8_0_test.go similarity index 76% rename from gridscale/resource_gridscale_mysql_test8_0.go rename to gridscale/resource_gridscale_mysql8_0_test.go index ca92c05e3..2cbcf6419 100644 --- a/gridscale/resource_gridscale_mysql_test8_0.go +++ b/gridscale/resource_gridscale_mysql8_0_test.go @@ -10,7 +10,7 @@ import ( "testing" ) -func TestAccResourceGridscaleMySQL_Basic(t *testing.T) { +func TestAccResourceGridscaleMySQL8_0_Basic(t *testing.T) { var object gsclient.PaaSService name := fmt.Sprintf("MySQL-%s", acctest.RandString(10)) @@ -20,7 +20,7 @@ func TestAccResourceGridscaleMySQL_Basic(t *testing.T) { CheckDestroy: testAccCheckResourceGridscalePaaSDestroyCheck, Steps: []resource.TestStep{ { - Config: testAccCheckResourceGridscaleMySQLConfig_basic(name), + Config: testAccCheckResourceGridscaleMySQL8_0Config_basic(name), Check: resource.ComposeTestCheckFunc( testAccCheckResourceGridscalePaaSExists("gridscale_mysql.test", &object), resource.TestCheckResourceAttr( @@ -28,7 +28,7 @@ func TestAccResourceGridscaleMySQL_Basic(t *testing.T) { ), }, { - Config: testAccCheckResourceGridscaleMySQLConfig_basic_update(), + Config: testAccCheckResourceGridscaleMySQL8_0Config_basic_update(), Check: resource.ComposeTestCheckFunc( testAccCheckResourceGridscalePaaSExists("gridscale_mysql.test", &object), resource.TestCheckResourceAttr( @@ -39,21 +39,21 @@ func TestAccResourceGridscaleMySQL_Basic(t *testing.T) { }) } -func testAccCheckResourceGridscaleMySQLConfig_basic(name string) string { +func testAccCheckResourceGridscaleMySQL8_0Config_basic(name string) string { return fmt.Sprintf(` resource "gridscale_mysql" "test" { name = "%s" - release = "5.7" + release = "8.0" performance_class = "standard" } `, name) } -func testAccCheckResourceGridscaleMySQLConfig_basic_update() string { +func testAccCheckResourceGridscaleMySQL8_0Config_basic_update() string { return fmt.Sprintf(` resource "gridscale_mysql" "test" { name = "newname" - release = "5.7" + release = "8.0" performance_class = "standard" max_core_count = 20 mysql_max_connections = 2000 From 9199598c0bc358e4f7b881cdb1f36a905d69201e Mon Sep 17 00:00:00 2001 From: Andrej Friesen Date: Tue, 12 Dec 2023 15:40:38 +0100 Subject: [PATCH 04/15] Add mysql8_0 provider --- gridscale/provider.go | 1 + 1 file changed, 1 insertion(+) diff --git a/gridscale/provider.go b/gridscale/provider.go index 2eb4d8173..af1b48872 100644 --- a/gridscale/provider.go +++ b/gridscale/provider.go @@ -96,6 +96,7 @@ func Provider() *schema.Provider { "gridscale_postgresql": resourceGridscalePostgreSQL(), "gridscale_sqlserver": resourceGridscaleMSSQLServer(), "gridscale_mysql": resourceGridscaleMySQL(), + "gridscale_mysql8_0": resourceGridscaleMySQL8_0(), "gridscale_mariadb": resourceGridscaleMariaDB(), "gridscale_memcached": resourceGridscaleMemcached(), "gridscale_filesystem": resourceGridscaleFilesystem(), From 5adfbf91a481847e1a0aabbdef45278dd1d37e6d Mon Sep 17 00:00:00 2001 From: Andrej Friesen Date: Tue, 12 Dec 2023 15:41:38 +0100 Subject: [PATCH 05/15] Create new constant for mysql8 --- gridscale/resource_gridscale_mysql8_0.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gridscale/resource_gridscale_mysql8_0.go b/gridscale/resource_gridscale_mysql8_0.go index 9f9bb55d3..5dbe97923 100644 --- a/gridscale/resource_gridscale_mysql8_0.go +++ b/gridscale/resource_gridscale_mysql8_0.go @@ -17,7 +17,7 @@ import ( "log" ) -const mysqlTemplateFlavourName = "mysql" +const mysql8TemplateFlavourName = "mysql" func resourceGridscaleMySQL() *schema.Resource { return &schema.Resource{ @@ -41,7 +41,7 @@ func resourceGridscaleMySQL() *schema.Resource { var isReleasePerfClassValid bool releaseWPerfClasess := make(map[string][]string) for _, template := range paasTemplates { - if template.Properties.Flavour == mysqlTemplateFlavourName { + if template.Properties.Flavour == mysql8TemplateFlavourName { perfClasses := releaseWPerfClasess[template.Properties.Release] releaseWPerfClasess[template.Properties.Release] = append(perfClasses, template.Properties.PerformanceClass) if template.Properties.Release == releaseVal && template.Properties.PerformanceClass == perfClassVal { @@ -449,7 +449,7 @@ func getMySQLTemplateUUID(client *gsclient.Client, release, performanceClass str var releases []string var uTemplate gsclient.PaaSTemplate for _, template := range paasTemplates { - if template.Properties.Flavour == mysqlTemplateFlavourName { + if template.Properties.Flavour == mysql8TemplateFlavourName { releases = append(releases, template.Properties.Release) if template.Properties.Release == release && template.Properties.PerformanceClass == performanceClass { isReleaseValid = true From f3ca799bd5045d35b4a4a1e4d9cee5f23d06c239 Mon Sep 17 00:00:00 2001 From: Andrej Friesen Date: Tue, 12 Dec 2023 15:42:09 +0100 Subject: [PATCH 06/15] Rename mysql8_0 functions --- gridscale/resource_gridscale_mysql8_0.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/gridscale/resource_gridscale_mysql8_0.go b/gridscale/resource_gridscale_mysql8_0.go index 5dbe97923..47a31398c 100644 --- a/gridscale/resource_gridscale_mysql8_0.go +++ b/gridscale/resource_gridscale_mysql8_0.go @@ -19,7 +19,7 @@ import ( const mysql8TemplateFlavourName = "mysql" -func resourceGridscaleMySQL() *schema.Resource { +func resourceGridscaleMySQL8_0() *schema.Resource { return &schema.Resource{ Create: resourceGridscaleMySQLCreate, Read: resourceGridscaleMySQLRead, @@ -202,7 +202,7 @@ func resourceGridscaleMySQL() *schema.Resource { } } -func resourceGridscaleMySQLRead(d *schema.ResourceData, meta interface{}) error { +func resourceGridscaleMySQL8_0Read(d *schema.ResourceData, meta interface{}) error { client := meta.(*gsclient.Client) errorPrefix := fmt.Sprintf("read mysql (%s) resource -", d.Id()) paas, err := client.GetPaaSService(context.Background(), d.Id()) @@ -320,7 +320,7 @@ func resourceGridscaleMySQLRead(d *schema.ResourceData, meta interface{}) error return nil } -func resourceGridscaleMySQLCreate(d *schema.ResourceData, meta interface{}) error { +func resourceGridscaleMySQL8_0Create(d *schema.ResourceData, meta interface{}) error { client := meta.(*gsclient.Client) errorPrefix := fmt.Sprintf("create mysql (%s) resource -", d.Id()) @@ -373,7 +373,7 @@ func resourceGridscaleMySQLCreate(d *schema.ResourceData, meta interface{}) erro return resourceGridscaleMySQLRead(d, meta) } -func resourceGridscaleMySQLUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceGridscaleMySQL8_0Update(d *schema.ResourceData, meta interface{}) error { client := meta.(*gsclient.Client) errorPrefix := fmt.Sprintf("update mysql (%s) resource -", d.Id()) @@ -423,7 +423,7 @@ func resourceGridscaleMySQLUpdate(d *schema.ResourceData, meta interface{}) erro return resourceGridscaleMySQLRead(d, meta) } -func resourceGridscaleMySQLDelete(d *schema.ResourceData, meta interface{}) error { +func resourceGridscaleMySQL8_0Delete(d *schema.ResourceData, meta interface{}) error { client := meta.(*gsclient.Client) errorPrefix := fmt.Sprintf("delete mysql (%s) resource -", d.Id()) @@ -440,7 +440,7 @@ func resourceGridscaleMySQLDelete(d *schema.ResourceData, meta interface{}) erro } // getMySQLTemplateUUID returns the UUID of the mysql service template. -func getMySQLTemplateUUID(client *gsclient.Client, release, performanceClass string) (string, error) { +func getMySQL8_0TemplateUUID(client *gsclient.Client, release, performanceClass string) (string, error) { paasTemplates, err := client.GetPaaSTemplateList(context.Background()) if err != nil { return "", err @@ -464,7 +464,7 @@ func getMySQLTemplateUUID(client *gsclient.Client, release, performanceClass str return uTemplate.Properties.ObjectUUID, nil } -func validateMySQLParameters(d *schema.ResourceDiff, template gsclient.PaaSTemplate) error { +func validateMySQL8_0Parameters(d *schema.ResourceDiff, template gsclient.PaaSTemplate) error { var errorMessages []string if sqlMode, ok := d.GetOk("mysql_sql_mode"); ok { if scheme, ok := template.Properties.ParametersSchema["mysql_sql_mode"]; ok { From 0185d5c262967c094d70990c3c8a2f8e205ab419 Mon Sep 17 00:00:00 2001 From: Andrej Friesen Date: Tue, 12 Dec 2023 15:51:33 +0100 Subject: [PATCH 07/15] Add instructions for easier local development --- README.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/README.md b/README.md index c82866f1d..2ec25c4bf 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,45 @@ To run a specific acceptance test, use `TESTARGS`. TEST=./gridscale \ TESTARGS='-run=TestAccResourceGridscaleLoadBalancerBasic' +# Override local terraform provider for development + +Create `local-dev.tfrc` with this content and change YOUR_USERNAME: + +``` +provider_installation { + # Use /Users/YOUR_USERNAME/github/terraform-provider-gridscale as an overridden package directory + # for the hashicorp/null provider. This disables the version and checksum + # verifications for this provider and forces Terraform to look for the + # null provider plugin in the given directory. + dev_overrides { + "gridscale/gridscale" = "/Users/YOUR_USERNAME/github/terraform-provider-gridscale/" + } + # For all other providers, install them directly from their origin provider + # registries as normal. If you omit this, Terraform will _only_ use + # the dev_overrides block, and so no other providers will be available. + direct {} +} +``` + +`export TF_CLI_CONFIG_FILE=./local-dev.tfrc` + +Build the binary: `go build -o terraform-provider-gridscale` + +Now you can run `terraform plan/apply` and terraform will tell you that you are using a local version of that provider: + +``` +> terraform apply +╷ +│ Warning: Provider development overrides are in effect +│ +│ The following provider development overrides are set in the CLI configuration: +│ - gridscale/gridscale in /Users/YOUR_USERNAME/github/terraform-provider-gridscale +│ +│ The behavior may therefore not match any released version of the provider and applying changes may cause the state to become incompatible with published +│ releases. +╵ +``` + ## Releasing the Provider A [GoReleaser](https://goreleaser.com/) configuration is provided that produces build artifacts matching the [layout required](https://www.terraform.io/docs/registry/providers/publishing.html#manually-preparing-a-release) to publish the provider in the Terraform Registry. From 06b4f9fc03f1613176f0c5711c28203311a38bbf Mon Sep 17 00:00:00 2001 From: Andrej Friesen Date: Tue, 12 Dec 2023 15:54:30 +0100 Subject: [PATCH 08/15] Use MYSQL8_0 related functions --- gridscale/resource_gridscale_mysql8_0.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/gridscale/resource_gridscale_mysql8_0.go b/gridscale/resource_gridscale_mysql8_0.go index 47a31398c..dd5f9f6e0 100644 --- a/gridscale/resource_gridscale_mysql8_0.go +++ b/gridscale/resource_gridscale_mysql8_0.go @@ -21,10 +21,10 @@ const mysql8TemplateFlavourName = "mysql" func resourceGridscaleMySQL8_0() *schema.Resource { return &schema.Resource{ - Create: resourceGridscaleMySQLCreate, - Read: resourceGridscaleMySQLRead, - Delete: resourceGridscaleMySQLDelete, - Update: resourceGridscaleMySQLUpdate, + Create: resourceGridscaleMySQL8_0Create, + Read: resourceGridscaleMySQL8_0Read, + Delete: resourceGridscaleMySQL8_0Delete, + Update: resourceGridscaleMySQL8_0Update, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, @@ -57,7 +57,7 @@ func resourceGridscaleMySQL8_0() *schema.Resource { } return errors.New(errMess) } - return validateMySQLParameters(d, chosenTemplate) + return validateMySQL8_0Parameters(d, chosenTemplate) }, Schema: map[string]*schema.Schema{ "name": { @@ -327,7 +327,7 @@ func resourceGridscaleMySQL8_0Create(d *schema.ResourceData, meta interface{}) e // Get mysql template UUID release := d.Get("release").(string) performanceClass := d.Get("performance_class").(string) - templateUUID, err := getMySQLTemplateUUID(client, release, performanceClass) + templateUUID, err := getMySQL8_0TemplateUUID(client, release, performanceClass) if err != nil { return fmt.Errorf("%s error: %v", errorPrefix, err) } @@ -370,7 +370,7 @@ func resourceGridscaleMySQL8_0Create(d *schema.ResourceData, meta interface{}) e } d.SetId(response.ObjectUUID) log.Printf("The id for MySQL service %s has been set to %v", requestBody.Name, response.ObjectUUID) - return resourceGridscaleMySQLRead(d, meta) + return resourceGridscaleMySQL8_0Read(d, meta) } func resourceGridscaleMySQL8_0Update(d *schema.ResourceData, meta interface{}) error { @@ -390,7 +390,7 @@ func resourceGridscaleMySQL8_0Update(d *schema.ResourceData, meta interface{}) e // Get mysql template UUID release := d.Get("release").(string) performanceClass := d.Get("performance_class").(string) - templateUUID, err := getMySQLTemplateUUID(client, release, performanceClass) + templateUUID, err := getMySQL8_0TemplateUUID(client, release, performanceClass) if err != nil { return fmt.Errorf("%s error: %v", errorPrefix, err) } @@ -420,7 +420,7 @@ func resourceGridscaleMySQL8_0Update(d *schema.ResourceData, meta interface{}) e if err != nil { return fmt.Errorf("%s error: %v", errorPrefix, err) } - return resourceGridscaleMySQLRead(d, meta) + return resourceGridscaleMySQL8_0Read(d, meta) } func resourceGridscaleMySQL8_0Delete(d *schema.ResourceData, meta interface{}) error { @@ -439,7 +439,7 @@ func resourceGridscaleMySQL8_0Delete(d *schema.ResourceData, meta interface{}) e return nil } -// getMySQLTemplateUUID returns the UUID of the mysql service template. +// getMySQL8_0TemplateUUID returns the UUID of the mysql service template. func getMySQL8_0TemplateUUID(client *gsclient.Client, release, performanceClass string) (string, error) { paasTemplates, err := client.GetPaaSTemplateList(context.Background()) if err != nil { From 92682cd5d6761f544fc6c2cab1e6059d843f2f42 Mon Sep 17 00:00:00 2001 From: Andrej Friesen Date: Tue, 12 Dec 2023 15:56:50 +0100 Subject: [PATCH 09/15] Add deprecation notice to mysql 5.7 resource --- website/docs/r/mysql.html.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/docs/r/mysql.html.md b/website/docs/r/mysql.html.md index a7cd8ba2b..689618955 100644 --- a/website/docs/r/mysql.html.md +++ b/website/docs/r/mysql.html.md @@ -8,6 +8,8 @@ description: |- # gridscale_mysql +*DEPRECATED* - We keep this for existing customers who still use MySQL 5.7. Please migrate to MySQL 8.0. + Provides a MySQL resource. This can be used to create, modify, and delete MySQL instances. ## Example From 63cbcd9c0c8b5bf9877fae57df2259e9ad919481 Mon Sep 17 00:00:00 2001 From: Andrej Friesen Date: Tue, 12 Dec 2023 15:57:13 +0100 Subject: [PATCH 10/15] Add docs dor MySQL8_0 resource --- website/docs/r/mysql8_0.html.md | 89 +++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 website/docs/r/mysql8_0.html.md diff --git a/website/docs/r/mysql8_0.html.md b/website/docs/r/mysql8_0.html.md new file mode 100644 index 000000000..ded473dbf --- /dev/null +++ b/website/docs/r/mysql8_0.html.md @@ -0,0 +1,89 @@ +--- +layout: "gridscale" +page_title: "gridscale: gridscale_mysql" +sidebar_current: "docs-gridscale-resource-mysql" +description: |- + Manage a MySQL service in gridscale. +--- + +# gridscale_mysql + + +Provides a MySQL resource. This can be used to create, modify, and delete MySQL instances. + +## Example + +The following example shows how one might use this resource to add a MySQL service to gridscale: + +```terraform +resource "gridscale_mysql8_0" "terra-mysql-test" { + name = "my mysql" + release = "8.0" + performance_class = "insane" + max_core_count = 20 + mysql_default_time_zone = "Europe/Berlin" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The human-readable name of the object. It supports the full UTF-8 character set, with a maximum of 64 characters. + +* `release` - (Required) The mysql release of this instance. For convenience, please use [gscloud](https://github.com/gridscale/gscloud) to get the list of available mysql service releases. + +* `performance_class` - (Required) Performance class of mysql service. Available performance classes at the time of writing: `standard`, `high`, `insane`, `ultra`. + +* `mysql_sql_mode` - (Optional) mysql parameter: SQL Mode. Default: "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION". + +* `mysql_max_connections` - (Optional) mysql parameter: Max Connections. Default: 4000. + +* `mysql_default_time_zone` - (Optional) mysql parameter: Server Timezone. Default: UTC. + +* `mysql_max_allowed_packet` - (Optional) mysql parameter: Max Allowed Packet Size. Format: xM (where x is an integer, M stands for unit: k(kB), M(MB), G(GB)). Default: 64M. + +* `labels` - (Optional) List of labels in the format [ "label1", "label2" ]. + +* `network_uuid` - (Optional) The UUID of the network that the service is attached to. + +* `security_zone_uuid` - *DEPRECATED* (Optional, Forcenew) The UUID of the security zone that the service is attached to. + +* `max_core_count` - (Optional) Maximum CPU core count. The mysql instance's CPU core count will be autoscaled based on the workload. The number of cores stays between 1 and `max_core_count`. + +## Timeouts + +Timeouts configuration options (in seconds): +More info: [terraform.io/docs/configuration/resources.html#operation-timeouts](https://www.terraform.io/docs/configuration/resources.html#operation-timeouts) + +* `create` - (Default value is "15m" - 15 minutes) Used for creating a resource. +* `update` - (Default value is "15m" - 15 minutes) Used for updating a resource. +* `delete` - (Default value is "15m" - 15 minutes) Used for deleting a resource. + +## Attributes + +This resource exports the following attributes: + +* `name` - See Argument Reference above. +* `release` - See Argument Reference above. +* `performance_class` - See Argument Reference above. +* `mysql_sql_mode` - See Argument Reference above. +* `mysql_max_connections` - See Argument Reference above. +* `mysql_default_time_zone` - See Argument Reference above. +* `mysql_max_allowed_packet` - See Argument Reference above. +* `username` - Username for PaaS service. It is used to connect to the mysql instance. +* `password` - Password for PaaS service. It is used to connect to the mysql instance. +* `listen_port` - The port numbers where this mysql service accepts connections. + * `name` - Name of a port. + * `host` - Host address. + * `listen_port` - Port number. +* `security_zone_uuid` - See Argument Reference above. +* `network_uuid` - The UUID of the network that the service is attached to or network UUID containing security zone. +* `service_template_uuid` - PaaS service template that mysql service uses. +* `service_template_category` - The template service's category used to create the service. +* `usage_in_minutes` - Number of minutes that PaaS service is in use. +* `change_time` - Time of the last change. +* `create_time` - Date time this service has been created. +* `status` - Current status of PaaS service. +* `max_core_count` - See Argument Reference above. +* `labels` - See Argument Reference above. From 0ed09e93f9334b222316b0f7950405117243e642 Mon Sep 17 00:00:00 2001 From: Andrej Friesen Date: Tue, 12 Dec 2023 15:57:58 +0100 Subject: [PATCH 11/15] Run go fmt --- gridscale/resource_gridscale_mysql8_0.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gridscale/resource_gridscale_mysql8_0.go b/gridscale/resource_gridscale_mysql8_0.go index dd5f9f6e0..64a3d60ed 100644 --- a/gridscale/resource_gridscale_mysql8_0.go +++ b/gridscale/resource_gridscale_mysql8_0.go @@ -260,7 +260,7 @@ func resourceGridscaleMySQL8_0Read(d *schema.ResourceData, meta interface{}) err if err = d.Set("mysql_max_connections", props.Parameters["mysql_max_connections"]); err != nil { return fmt.Errorf("%s error setting mysql_max_connections: %v", errorPrefix, err) } - if err = d.Set("mysql_default_time_zone", props.Parameters["mysql_default_time_zone"]); err != nil { + if err = d.Set("mysql_default_time_zone", props.Parameters["mysql_default_time_zone"]); err != nil { return fmt.Errorf("%s error setting mysql_default_time_zone: %v", errorPrefix, err) } if err = d.Set("mysql_max_allowed_packet", props.Parameters["mysql_max_allowed_packet"]); err != nil { From 01d92c21557c75964fda884e94950bf745e04b55 Mon Sep 17 00:00:00 2001 From: Andrej Friesen Date: Tue, 12 Dec 2023 16:31:04 +0100 Subject: [PATCH 12/15] Rename mysql test to mysql8_0 test We can not run the test for mysql 5.7 anymore since we can not create that via api. --- .github/workflows/{mysql.yml => mysql8_0.yml} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename .github/workflows/{mysql.yml => mysql8_0.yml} (89%) diff --git a/.github/workflows/mysql.yml b/.github/workflows/mysql8_0.yml similarity index 89% rename from .github/workflows/mysql.yml rename to .github/workflows/mysql8_0.yml index 604bb9c9c..d3b2f96bb 100644 --- a/.github/workflows/mysql.yml +++ b/.github/workflows/mysql8_0.yml @@ -1,4 +1,4 @@ -name: Test MySQL rs +name: Test MySQL8_0 rs on: workflow_dispatch: @@ -39,5 +39,5 @@ jobs: - name: Check out code into the Go module directory uses: actions/checkout@v2 - - name: Run TestAccResourceGridscaleMySQL_Basic - run: make testacc TEST=./gridscale TESTARGS='-run=TestAccResourceGridscaleMySQL_Basic' + - name: Run TestAccResourceGridscaleMySQL8_0_Basic + run: make testacc TEST=./gridscale TESTARGS='-run=TestAccResourceGridscaleMySQL8_0_Basic' From a9ed223c537f18d5bc5fb884b42b995bf95619b0 Mon Sep 17 00:00:00 2001 From: Andrej Friesen Date: Tue, 12 Dec 2023 16:43:20 +0100 Subject: [PATCH 13/15] Fix test for mysql8_0 resource --- gridscale/resource_gridscale_mysql8_0_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gridscale/resource_gridscale_mysql8_0_test.go b/gridscale/resource_gridscale_mysql8_0_test.go index 2cbcf6419..8f57f4e03 100644 --- a/gridscale/resource_gridscale_mysql8_0_test.go +++ b/gridscale/resource_gridscale_mysql8_0_test.go @@ -22,17 +22,17 @@ func TestAccResourceGridscaleMySQL8_0_Basic(t *testing.T) { { Config: testAccCheckResourceGridscaleMySQL8_0Config_basic(name), Check: resource.ComposeTestCheckFunc( - testAccCheckResourceGridscalePaaSExists("gridscale_mysql.test", &object), + testAccCheckResourceGridscalePaaSExists("gridscale_mysql8_0.test", &object), resource.TestCheckResourceAttr( - "gridscale_mysql.test", "name", name), + "gridscale_mysql8_0.test", "name", name), ), }, { Config: testAccCheckResourceGridscaleMySQL8_0Config_basic_update(), Check: resource.ComposeTestCheckFunc( - testAccCheckResourceGridscalePaaSExists("gridscale_mysql.test", &object), + testAccCheckResourceGridscalePaaSExists("gridscale_mysql8_0.test", &object), resource.TestCheckResourceAttr( - "gridscale_mysql.test", "name", "newname"), + "gridscale_mysql8_0.test", "name", "newname"), ), }, }, @@ -41,7 +41,7 @@ func TestAccResourceGridscaleMySQL8_0_Basic(t *testing.T) { func testAccCheckResourceGridscaleMySQL8_0Config_basic(name string) string { return fmt.Sprintf(` -resource "gridscale_mysql" "test" { +resource "gridscale_mysql8_0" "test" { name = "%s" release = "8.0" performance_class = "standard" @@ -51,7 +51,7 @@ resource "gridscale_mysql" "test" { func testAccCheckResourceGridscaleMySQL8_0Config_basic_update() string { return fmt.Sprintf(` -resource "gridscale_mysql" "test" { +resource "gridscale_mysql8_0" "test" { name = "newname" release = "8.0" performance_class = "standard" From 9b075743aac516d259afed570af369efa6328c12 Mon Sep 17 00:00:00 2001 From: Andrej Friesen Date: Tue, 12 Dec 2023 16:47:56 +0100 Subject: [PATCH 14/15] Adjust the docs for mysql8_0 --- website/docs/r/mysql8_0.html.md | 12 ++++++------ website/gridscale.erb | 3 +++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/website/docs/r/mysql8_0.html.md b/website/docs/r/mysql8_0.html.md index ded473dbf..7bf537690 100644 --- a/website/docs/r/mysql8_0.html.md +++ b/website/docs/r/mysql8_0.html.md @@ -1,19 +1,19 @@ --- layout: "gridscale" -page_title: "gridscale: gridscale_mysql" -sidebar_current: "docs-gridscale-resource-mysql" +page_title: "gridscale: gridscale_mysql8_0" +sidebar_current: "docs-gridscale-resource-mysql8_0" description: |- - Manage a MySQL service in gridscale. + Manage a MySQL 8.0 service in gridscale. --- -# gridscale_mysql +# gridscale_mysql8_0 -Provides a MySQL resource. This can be used to create, modify, and delete MySQL instances. +Provides a MySQL 8.0 resource. This can be used to create, modify, and delete MySQL 8.0 instances. ## Example -The following example shows how one might use this resource to add a MySQL service to gridscale: +The following example shows how one might use this resource to add a MySQL 8.0 service to gridscale: ```terraform resource "gridscale_mysql8_0" "terra-mysql-test" { diff --git a/website/gridscale.erb b/website/gridscale.erb index 841c31ef5..cb73ea882 100644 --- a/website/gridscale.erb +++ b/website/gridscale.erb @@ -118,6 +118,9 @@ > gridscale_mysql + + > + gridscale_mysql8_0 > gridscale_filesystem From 928e6a7b671bcf78f021ef0bbb59837d174e978b Mon Sep 17 00:00:00 2001 From: Andrej Friesen Date: Tue, 12 Dec 2023 16:59:33 +0100 Subject: [PATCH 15/15] Update changelog --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index baa6dc4a8..63c521117 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog + +## 1.23.0 (Dec 12, 2023) + +FEATURES: +- Add `griscale_mysql8_0` resource [PR #246](https://github.com/gridscale/terraform-provider-gridscale/issues/246) + +IMPROVEMENTS: +- Add deprecation notice for `griscale_mysql` (MySQL 5.7) + ## 1.22.0 (Sept 26, 2023) FEATURES: