Skip to content

Commit

Permalink
[3.1.1] AWS GP3 and Throughput support (#11)
Browse files Browse the repository at this point in the history
* [3.1.1] AWS GP3 and Throughput support
  • Loading branch information
vasiliyskysql authored Jul 18, 2024
1 parent 5e98c0a commit bb4b0b8
Show file tree
Hide file tree
Showing 19 changed files with 336 additions and 69 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
5 changes: 3 additions & 2 deletions docs/data-sources/skysql_service.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,11 @@ Read-Only:

Optional:

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

Read-Only:

- `size` (Number) The size of the storage volume in GB.
- `volume_type` (String) The type of the storage volume. Possible values are: gp2, io1 etc
- `volume_type` (String) The type of the storage volume. Possible values are: gp3, io1 etc

31 changes: 17 additions & 14 deletions docs/resources/skysql_service.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,21 @@ Creates and manages a service in SkySQL
```terraform
# Create a service
resource "skysql_service" "default" {
project_id = data.skysql_projects.default.projects[0].id
service_type = "transactional"
topology = "es-single"
cloud_provider = "aws"
region = "us-east-1"
name = "myservice"
architecture = "amd64"
nodes = 1
size = "sky-2x8"
storage = 100
ssl_enabled = true
version = data.skysql_versions.default.versions[0].name
volume_type = "gp2"
project_id = data.skysql_projects.default.projects[0].id
service_type = "transactional"
topology = "es-single"
cloud_provider = "aws"
region = "us-east-1"
name = "myservice"
architecture = "amd64"
nodes = 1
size = "sky-2x8"
storage = 100
ssl_enabled = true
version = data.skysql_versions.default.versions[0].name
volume_type = "gp3"
volume_iops = 3000
volume_throughput = 125
# 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 +68,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 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
26 changes: 14 additions & 12 deletions examples/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,20 @@ data "skysql_versions" "default" {


resource "skysql_service" "default" {
service_type = "transactional"
topology = "es-single"
cloud_provider = "aws"
region = "us-east-2"
name = "myservice"
architecture = "amd64"
nodes = 1
size = "sky-2x8"
storage = 100
ssl_enabled = true
version = data.skysql_versions.default.versions[0].name
volume_type = "gp2"
service_type = "transactional"
topology = "es-single"
cloud_provider = "aws"
region = "us-east-2"
name = "myservice"
architecture = "amd64"
nodes = 1
size = "sky-2x8"
storage = 100
ssl_enabled = true
version = data.skysql_versions.default.versions[0].name
volume_type = "gp3"
volume_iops = 3000
volume_throughput = 125
allow_list = [
{
"ip" : "127.0.0.1/32",
Expand Down
3 changes: 3 additions & 0 deletions examples/privateconnect/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ resource "skysql_service" "this" {
endpoint_mechanism = "privateconnect"
endpoint_allowed_accounts = [data.aws_caller_identity.this.account_id]
wait_for_creation = true
volume_type = "gp3"
volume_iops = 3000
volume_throughput = 125
# The following line will be required when tearing down the skysql service
# deletion_protection = false
}
Expand Down
28 changes: 15 additions & 13 deletions examples/resources/skysql_service.tf
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
# Create a service
resource "skysql_service" "default" {
project_id = data.skysql_projects.default.projects[0].id
service_type = "transactional"
topology = "es-single"
cloud_provider = "aws"
region = "us-east-1"
name = "myservice"
architecture = "amd64"
nodes = 1
size = "sky-2x8"
storage = 100
ssl_enabled = true
version = data.skysql_versions.default.versions[0].name
volume_type = "gp2"
project_id = data.skysql_projects.default.projects[0].id
service_type = "transactional"
topology = "es-single"
cloud_provider = "aws"
region = "us-east-1"
name = "myservice"
architecture = "amd64"
nodes = 1
size = "sky-2x8"
storage = 100
ssl_enabled = true
version = data.skysql_versions.default.versions[0].name
volume_type = "gp3"
volume_iops = 3000
volume_throughput = 125
# 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
11 changes: 9 additions & 2 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 @@ -200,12 +201,17 @@ func (d *ServiceDataSource) Schema(ctx context.Context, req datasource.SchemaReq
},
"volume_type": schema.StringAttribute{
Computed: true,
Description: "The type of the storage volume. Possible values are: gp2, io1 etc",
Description: "The type of the storage volume. Possible values are: gp3, io1 etc",
},
"iops": schema.Int64Attribute{
Computed: true,
Optional: true,
Description: "The number of IOPS for the storage volume. This is only applicable for io1 volumes.",
Description: "The number of IOPS for the storage volume. This is only applicable for io1 and gp3 volumes.",
},
"throughput": schema.Int64Attribute{
Computed: true,
Optional: true,
Description: "The Throughput for the storage volume. This is only applicable for gp3 volumes.",
},
},
},
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
74 changes: 59 additions & 15 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 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 @@ -1148,19 +1169,42 @@ func (r *ServiceResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
}

if plan.Provider.ValueString() == "aws" {
if !plan.VolumeIOPS.IsNull() && plan.VolumeType.IsNull() {

if 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_type is required for AWS. Use: io1 or gp3 for volume_type.")
return
}
if !plan.VolumeIOPS.IsNull() && plan.VolumeType.ValueString() != "io1" {

if plan.VolumeType.ValueString() != "io1" && 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 is not supported",
"volume_type provided is not supported. Use: io1 or gp3 for volume_type.")
return
}

if plan.VolumeIOPS.IsNull() {
resp.Diagnostics.AddAttributeError(path.Root("volume_iops"),
"volume_iops are required",
"volume_iops are required for AWS")
return
}

if plan.VolumeType.ValueString() == "io1" && !plan.VolumeThroughput.IsNull() {
resp.Diagnostics.AddAttributeError(path.Root("volume_throughput"),
"volume_throughput is not supported for io1",
"volume_throughput is supported only for gp3 volume_type for AWS")
return
}

if plan.VolumeType.ValueString() == "gp3" && plan.VolumeThroughput.IsNull() {
resp.Diagnostics.AddAttributeError(path.Root("volume_throughput"),
"volume_throughput is required",
"volume_throughput is required for gp3 volume_type for AWS")
return
}

} else if plan.Provider.ValueString() == "gcp" {
if !(plan.VolumeType.ValueString() == "" || plan.VolumeType.IsNull() || plan.VolumeType.ValueString() == "pd-ssd") {
resp.Diagnostics.AddAttributeError(
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
Loading

0 comments on commit bb4b0b8

Please sign in to comment.