Skip to content

Commit

Permalink
[3.1.1] AWS GP3 and Throughput support
Browse files Browse the repository at this point in the history
  • Loading branch information
vasiliyskysql committed Jul 17, 2024
1 parent 5e98c0a commit b66fa77
Show file tree
Hide file tree
Showing 17 changed files with 91 additions and 23 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## [3.1.1] - 2024-07-16
### Fixed
- `gp3` volume type support for AWS.
- `volume_throughput` support for AWS GP3 storage volume type.

## [3.1.0] - 2024-07-15
### Features
- `azure` provider is now supported.
Expand Down
1 change: 1 addition & 0 deletions docs/data-sources/skysql_service.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Read-Only:
Optional:

- `iops` (Number) The number of IOPS for the storage volume. This is only applicable for io1 volumes.
- `throughput` (Number) The Throughput for the storage volume. This is only applicable for io1 volumes.

Read-Only:

Expand Down
5 changes: 3 additions & 2 deletions docs/resources/skysql_service.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ resource "skysql_service" "default" {
storage = 100
ssl_enabled = true
version = data.skysql_versions.default.versions[0].name
volume_type = "gp2"
volume_type = "gp3"
# The service create is an asynchronous operation.
# if you want to wait for the service to be created set wait_for_creation to true
wait_for_creation = true
Expand Down Expand Up @@ -66,7 +66,8 @@ resource "skysql_service" "default" {
- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts))
- `version` (String) The software version
- `volume_iops` (Number) The volume IOPS. This is only applicable for AWS
- `volume_type` (String) The volume type. Valid values are: gp2 and io1. This is only applicable for AWS
- `volume_throughput` (Number) The volume Throughput. This is only applicable for AWS
- `volume_type` (String) The volume type. Valid values are: gp3, gp2, and io1. This is only applicable for AWS
- `wait_for_creation` (Boolean) Whether to wait for the service to be created. Valid values are: true or false
- `wait_for_deletion` (Boolean) Whether to wait for the service to be deleted. Valid values are: true or false
- `wait_for_update` (Boolean) Whether to wait for the service to be updated. Valid values are: true or false
Expand Down
2 changes: 1 addition & 1 deletion examples/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ resource "skysql_service" "default" {
storage = 100
ssl_enabled = true
version = data.skysql_versions.default.versions[0].name
volume_type = "gp2"
volume_type = "gp3"
allow_list = [
{
"ip" : "127.0.0.1/32",
Expand Down
2 changes: 1 addition & 1 deletion examples/resources/skysql_service.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ resource "skysql_service" "default" {
storage = 100
ssl_enabled = true
version = data.skysql_versions.default.versions[0].name
volume_type = "gp2"
volume_type = "gp3"
# The service create is an asynchronous operation.
# if you want to wait for the service to be created set wait_for_creation to true
wait_for_creation = true
Expand Down
7 changes: 7 additions & 0 deletions internal/provider/service_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ type StorageVolumeDataSourceModel struct {
Size types.Int64 `tfsdk:"size"`
VolumeType types.String `tfsdk:"volume_type"`
IOPS types.Int64 `tfsdk:"iops"`
Throughput types.Int64 `tfsdk:"throughput"`
}

func (d *ServiceDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
Expand Down Expand Up @@ -207,6 +208,11 @@ func (d *ServiceDataSource) Schema(ctx context.Context, req datasource.SchemaReq
Optional: true,
Description: "The number of IOPS for the storage volume. This is only applicable for io1 volumes.",
},
"throughput": schema.Int64Attribute{
Computed: true,
Optional: true,
Description: "The Throughput for the storage volume. This is only applicable for io1 volumes.",
},
},
},
"outbound_ips": schema.ListAttribute{
Expand Down Expand Up @@ -318,6 +324,7 @@ func (d *ServiceDataSource) Read(ctx context.Context, req datasource.ReadRequest
Size: types.Int64Value(int64(service.StorageVolume.Size)),
VolumeType: types.StringValue(service.StorageVolume.VolumeType),
IOPS: types.Int64Value(int64(service.StorageVolume.IOPS)),
Throughput: types.Int64Value(int64(service.StorageVolume.Throughput)),
}

data.OutboundIps = make([]types.String, len(service.OutboundIps))
Expand Down
56 changes: 42 additions & 14 deletions internal/provider/service_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ type ServiceResourceModel struct {
Topology types.String `tfsdk:"topology"`
Storage types.Int64 `tfsdk:"storage"`
VolumeIOPS types.Int64 `tfsdk:"volume_iops"`
VolumeThroughput types.Int64 `tfsdk:"volume_throughput"`
SSLEnabled types.Bool `tfsdk:"ssl_enabled"`
NoSQLEnabled types.Bool `tfsdk:"nosql_enabled"`
VolumeType types.String `tfsdk:"volume_type"`
Expand Down Expand Up @@ -221,6 +222,13 @@ var serviceResourceSchemaV0 = schema.Schema{
int64planmodifier.UseStateForUnknown(),
},
},
"volume_throughput": schema.Int64Attribute{
Optional: true,
Description: "The volume Throughput. This is only applicable for AWS",
PlanModifiers: []planmodifier.Int64{
int64planmodifier.UseStateForUnknown(),
},
},
"ssl_enabled": schema.BoolAttribute{
Optional: true,
Computed: true,
Expand All @@ -241,7 +249,7 @@ var serviceResourceSchemaV0 = schema.Schema{
"volume_type": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "The volume type. Valid values are: gp2 and io1. This is only applicable for AWS",
Description: "The volume type. Valid values are: gp3, gp2, and io1. This is only applicable for AWS",
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
stringplanmodifier.RequiresReplaceIf(
Expand Down Expand Up @@ -452,6 +460,7 @@ func (r *ServiceResource) Create(ctx context.Context, req resource.CreateRequest
Topology: state.Topology.ValueString(),
Storage: uint(state.Storage.ValueInt64()),
VolumeIOPS: uint(state.VolumeIOPS.ValueInt64()),
VolumeThroughput: uint(state.VolumeThroughput.ValueInt64()),
SSLEnabled: state.SSLEnabled.ValueBool(),
NoSQLEnabled: state.NoSQLEnabled.ValueBool(),
VolumeType: state.VolumeType.ValueString(),
Expand Down Expand Up @@ -523,6 +532,11 @@ func (r *ServiceResource) Create(ctx context.Context, req resource.CreateRequest
} else {
state.VolumeIOPS = types.Int64Null()
}
if service.StorageVolume.Throughput > 0 {
state.VolumeThroughput = types.Int64Value(int64(service.StorageVolume.Throughput))
} else {
state.VolumeThroughput = types.Int64Null()
}
if service.StorageVolume.VolumeType != "" {
state.VolumeType = types.StringValue(service.StorageVolume.VolumeType)
} else {
Expand Down Expand Up @@ -679,6 +693,11 @@ func (r *ServiceResource) readServiceState(ctx context.Context, data *ServiceRes
} else {
data.VolumeIOPS = types.Int64Null()
}
if !data.VolumeThroughput.IsNull() && service.StorageVolume.Throughput > 0 {
data.VolumeThroughput = types.Int64Value(int64(service.StorageVolume.Throughput))
} else {
data.VolumeThroughput = types.Int64Null()
}
data.VolumeType = types.StringValue(service.StorageVolume.VolumeType)
if !data.ReplicationEnabled.IsNull() {
data.ReplicationEnabled = types.BoolValue(service.ReplicationEnabled)
Expand Down Expand Up @@ -805,16 +824,18 @@ func (r *ServiceResource) updateAllowListState(plan *ServiceResourceModel, state
}

func (r *ServiceResource) updateServiceStorage(ctx context.Context, plan *ServiceResourceModel, state *ServiceResourceModel, resp *resource.UpdateResponse) {
if plan.Storage.ValueInt64() != state.Storage.ValueInt64() || plan.VolumeIOPS.ValueInt64() != state.VolumeIOPS.ValueInt64() {
if plan.Storage.ValueInt64() != state.Storage.ValueInt64() || plan.VolumeIOPS.ValueInt64() != state.VolumeIOPS.ValueInt64() || plan.VolumeThroughput.ValueInt64() != state.VolumeThroughput.ValueInt64() {
tflog.Info(ctx, "Updating storage size for the service", map[string]interface{}{
"id": state.ID.ValueString(),
"from": state.Storage.ValueInt64(),
"to": plan.Storage.ValueInt64(),
"iops_from": state.VolumeIOPS.ValueInt64(),
"iops_to": plan.VolumeIOPS.ValueInt64(),
"id": state.ID.ValueString(),
"from": state.Storage.ValueInt64(),
"to": plan.Storage.ValueInt64(),
"iops_from": state.VolumeIOPS.ValueInt64(),
"iops_to": plan.VolumeIOPS.ValueInt64(),
"throughput_from": state.VolumeThroughput.ValueInt64(),
"throughput_to": plan.VolumeThroughput.ValueInt64(),
})

err := r.client.ModifyServiceStorage(ctx, state.ID.ValueString(), plan.Storage.ValueInt64(), plan.VolumeIOPS.ValueInt64())
err := r.client.ModifyServiceStorage(ctx, state.ID.ValueString(), plan.Storage.ValueInt64(), plan.VolumeIOPS.ValueInt64(), plan.VolumeThroughput.ValueInt64())
if err != nil {
resp.Diagnostics.AddError("Error updating a storage for the service",
fmt.Sprintf("Unable to update a storage size for the service, got error: %s", err))
Expand Down Expand Up @@ -1150,15 +1171,22 @@ func (r *ServiceResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
if plan.Provider.ValueString() == "aws" {
if !plan.VolumeIOPS.IsNull() && plan.VolumeType.IsNull() {
resp.Diagnostics.AddAttributeError(path.Root("volume_type"),
"volume_type is require",
"volume_type is required when volume_iops is set. "+
"Use: io1 for volume_type if volume_iops is set")
"volume_type is required",
"volume_typed is required when volume_iops is set. "+
"Use: io1|gp2|gp3 for volume_type if volume_iops is set")
return
}
if !plan.VolumeThroughput.IsNull() && plan.VolumeType.IsNull() {
resp.Diagnostics.AddAttributeError(path.Root("volume_type"),
"volume_type is required",
"volume_typed is required when volume_throughput is set. "+
"Use: io1|gp2|gp3 for volume_type if volume_throughput is set")
return
}
if !plan.VolumeIOPS.IsNull() && plan.VolumeType.ValueString() != "io1" {
if !plan.VolumeIOPS.IsNull() && (plan.VolumeType.ValueString() != "io1" && plan.VolumeType.ValueString() != "gp2" && plan.VolumeType.ValueString() != "gp3") {
resp.Diagnostics.AddAttributeError(path.Root("volume_type"),
"volume_type must be io1 when you want to set IOPS",
"Use: io1 for volume_type if volume_iops is set")
"volume_type must be io1|gp2|gp3 when you want to set IOPS",
"Use: io1|gp2|gp3 for volume_type if volume_iops is set")
return
}
} else if plan.Provider.ValueString() == "gcp" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,12 @@ func TestServiceResourceDeletionProtection(t *testing.T) {
Size int `json:"size"`
VolumeType string `json:"volume_type"`
IOPS int `json:"iops"`
Throughput int `json:"throughput"`
}{
Size: int(payload.Storage),
VolumeType: payload.VolumeType,
IOPS: int(payload.VolumeIOPS),
Throughput: int(payload.VolumeThroughput),
},
OutboundIps: nil,
IsActive: true,
Expand Down
4 changes: 4 additions & 0 deletions internal/provider/service_resource_privatlink_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,12 @@ func TestServiceResourcePrivateLink(t *testing.T) {
Size int `json:"size"`
VolumeType string `json:"volume_type"`
IOPS int `json:"iops"`
Throughput int `json:"throughput"`
}{
Size: int(payload.Storage),
VolumeType: payload.VolumeType,
IOPS: int(payload.VolumeIOPS),
Throughput: int(payload.VolumeThroughput),
},
OutboundIps: nil,
IsActive: true,
Expand Down Expand Up @@ -393,10 +395,12 @@ func TestServiceResourcePrivateConnectWhenAllowedAccountsEmpty(t *testing.T) {
Size int `json:"size"`
VolumeType string `json:"volume_type"`
IOPS int `json:"iops"`
Throughput int `json:"throughput"`
}{
Size: int(payload.Storage),
VolumeType: payload.VolumeType,
IOPS: int(payload.VolumeIOPS),
Throughput: int(payload.VolumeThroughput),
},
OutboundIps: nil,
IsActive: true,
Expand Down
1 change: 1 addition & 0 deletions internal/provider/service_resource_scale_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ func TestServiceResourceScaleTest(t *testing.T) {
Size int `json:"size"`
VolumeType string `json:"volume_type"`
IOPS int `json:"iops"`
Throughput int `json:"throughput"`
}{
Size: int(payload.Storage),
VolumeType: payload.VolumeType,
Expand Down
8 changes: 7 additions & 1 deletion internal/provider/service_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,12 @@ resource "skysql_service" default {
Size int `json:"size"`
VolumeType string `json:"volume_type"`
IOPS int `json:"iops"`
Throughput int `json:"throughput"`
}{
Size: int(payload.Storage),
VolumeType: payload.VolumeType,
IOPS: int(payload.VolumeIOPS),
Throughput: int(payload.VolumeThroughput),
},
OutboundIps: nil,
IsActive: true,
Expand Down Expand Up @@ -375,10 +377,12 @@ resource "skysql_service" default {
Size int `json:"size"`
VolumeType string `json:"volume_type"`
IOPS int `json:"iops"`
Throughput int `json:"throughput"`
}{
Size: int(payload.Storage),
VolumeType: payload.VolumeType,
IOPS: int(payload.VolumeIOPS),
Throughput: int(payload.VolumeThroughput),
},
OutboundIps: nil,
IsActive: false,
Expand Down Expand Up @@ -522,10 +526,12 @@ resource "skysql_service" default {
Size int `json:"size"`
VolumeType string `json:"volume_type"`
IOPS int `json:"iops"`
Throughput int `json:"throughput"`
}{
Size: int(payload.Storage),
VolumeType: payload.VolumeType,
IOPS: int(payload.VolumeIOPS),
Throughput: int(payload.VolumeThroughput),
},
OutboundIps: nil,
IsActive: true,
Expand Down Expand Up @@ -631,7 +637,7 @@ resource "skysql_service" default {
w.WriteHeader(http.StatusOK)
})
},
expectError: regexp.MustCompile(`volume_type must be io1 when you want to set IOPS`),
expectError: regexp.MustCompile(`volume_type must be io1|gp2|gp3 when you want to set IOPS`),
},
}

Expand Down
2 changes: 2 additions & 0 deletions internal/provider/service_resource_update_allowlist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,12 @@ func TestServiceResourceAllowlistUpdate(t *testing.T) {
Size int `json:"size"`
VolumeType string `json:"volume_type"`
IOPS int `json:"iops"`
Throughput int `json:"throughput"`
}{
Size: int(payload.Storage),
VolumeType: payload.VolumeType,
IOPS: int(payload.VolumeIOPS),
Throughput: int(payload.VolumeThroughput),
},
OutboundIps: nil,
IsActive: true,
Expand Down
8 changes: 8 additions & 0 deletions internal/provider/service_resource_volume_type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,12 @@ func TestServiceResourceGCPVolumeType(t *testing.T) {
Size int `json:"size"`
VolumeType string `json:"volume_type"`
IOPS int `json:"iops"`
Throughput int `json:"throughput"`
}{
Size: int(payload.Storage),
VolumeType: payload.VolumeType,
IOPS: int(payload.VolumeIOPS),
Throughput: int(payload.VolumeThroughput),
},
OutboundIps: nil,
IsActive: true,
Expand Down Expand Up @@ -227,10 +229,12 @@ func TestServiceResourceAWSGP2VolumeType(t *testing.T) {
Size int `json:"size"`
VolumeType string `json:"volume_type"`
IOPS int `json:"iops"`
Throughput int `json:"throughput"`
}{
Size: int(payload.Storage),
VolumeType: payload.VolumeType,
IOPS: int(payload.VolumeIOPS),
Throughput: int(payload.VolumeThroughput),
},
OutboundIps: nil,
IsActive: true,
Expand Down Expand Up @@ -373,10 +377,12 @@ func TestServiceResourceAWSIO1VolumeType(t *testing.T) {
Size int `json:"size"`
VolumeType string `json:"volume_type"`
IOPS int `json:"iops"`
Throughput int `json:"throughput"`
}{
Size: int(payload.Storage),
VolumeType: payload.VolumeType,
IOPS: int(payload.VolumeIOPS),
Throughput: int(payload.VolumeThroughput),
},
OutboundIps: nil,
IsActive: true,
Expand Down Expand Up @@ -519,10 +525,12 @@ func TestServiceResourceAzureVolumeType(t *testing.T) {
Size int `json:"size"`
VolumeType string `json:"volume_type"`
IOPS int `json:"iops"`
Throughput int `json:"throughput"`
}{
Size: int(payload.Storage),
VolumeType: payload.VolumeType,
IOPS: int(payload.VolumeIOPS),
Throughput: int(payload.VolumeThroughput),
},
OutboundIps: nil,
IsActive: true,
Expand Down
4 changes: 2 additions & 2 deletions internal/skysql/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,11 +300,11 @@ func (c *Client) ModifyServiceNodeNumber(ctx context.Context, serviceID string,
return err
}

func (c *Client) ModifyServiceStorage(ctx context.Context, serviceID string, size int64, iops int64) error {
func (c *Client) ModifyServiceStorage(ctx context.Context, serviceID string, size int64, iops int64, throughput int64) error {
resp, err := c.HTTPClient.R().
SetHeader("Accept", "application/json").
SetContext(ctx).
SetBody(&provisioning.UpdateStorageRequest{Size: size, IOPS: iops}).
SetBody(&provisioning.UpdateStorageRequest{Size: size, IOPS: iops, Throughput: throughput}).
SetError(&ErrorResponse{}).
Patch("/provisioning/v1/services/" + serviceID + "/storage")
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions internal/skysql/provisioning/create_service_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type CreateServiceRequest struct {
Topology string `json:"topology"`
Storage uint `json:"storage"`
VolumeIOPS uint `json:"volume_iops"`
VolumeThroughput uint `json:"volume_throughput"`
SSLEnabled bool `json:"ssl_enabled"`
NoSQLEnabled bool `json:"nosql_enabled"`
VolumeType string `json:"volume_type,omitempty"`
Expand Down
1 change: 1 addition & 0 deletions internal/skysql/provisioning/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type Service struct {
Size int `json:"size"`
VolumeType string `json:"volume_type"`
IOPS int `json:"iops"`
Throughput int `json:"throughput"`
} `json:"storage_volume"`
OutboundIps []string `json:"outbound_ips"`
IsActive bool `json:"is_active"`
Expand Down
Loading

0 comments on commit b66fa77

Please sign in to comment.