Skip to content

Commit

Permalink
[3.1.0] Azure Support (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
vasiliyskysql authored Jul 15, 2024
1 parent 7312cda commit 5e98c0a
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 8 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## [3.1.0] - 2024-07-15
### Features
- `azure` provider is now supported.

## [3.0.0] - 2024-06-05
### Breaking Change
- New API Key Access, `TF_SKYSQL_API_KEY` [API Access](https://app.skysql.com/user-profile/api-keys)
Expand Down
35 changes: 27 additions & 8 deletions internal/provider/service_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import (
"encoding/json"
"errors"
"fmt"
"reflect"
"regexp"
"time"

"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
Expand All @@ -24,9 +28,6 @@ import (
sdkresource "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/skysqlinc/terraform-provider-skysql/internal/skysql"
"github.com/skysqlinc/terraform-provider-skysql/internal/skysql/provisioning"
"reflect"
"regexp"
"time"
)

const defaultCreateTimeout = 60 * time.Minute
Expand Down Expand Up @@ -461,6 +462,12 @@ func (r *ServiceResource) Create(ctx context.Context, req resource.CreateRequest
AvailabilityZone: state.AvailabilityZone.ValueString(),
}

if !Contains[string]([]string{"gcp", "aws", "azure"}, createServiceRequest.Provider) {
resp.Diagnostics.AddAttributeError(path.Root("provider"),
"Invalid provider value",
fmt.Sprintf("The %q is an invalid value. Allowed values: aws, gcp, azure", createServiceRequest.Provider))
}

if !state.MaxscaleSize.IsUnknown() && !state.MaxscaleSize.IsNull() && len(state.MaxscaleSize.ValueString()) > 0 {
createServiceRequest.MaxscaleSize = toPtr[string](state.MaxscaleSize.ValueString())
} else {
Expand Down Expand Up @@ -1134,10 +1141,10 @@ func (r *ServiceResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
return
}

if !Contains[string]([]string{"gcp", "aws"}, plan.Provider.ValueString()) {
if !Contains[string]([]string{"gcp", "aws", "azure"}, plan.Provider.ValueString()) {
resp.Diagnostics.AddAttributeError(path.Root("provider"),
"Invalid provider value",
fmt.Sprintf("The %q is an invalid value. Allowed values: aws or gcp", plan.Provider.ValueString()))
fmt.Sprintf("The %q is an invalid value. Allowed values: aws, gcp, or azure", plan.Provider.ValueString()))
}

if plan.Provider.ValueString() == "aws" {
Expand All @@ -1148,14 +1155,13 @@ func (r *ServiceResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
"Use: io1 for volume_type if volume_iops is set")
return
}
if !plan.VolumeIOPS.IsNull() &&
plan.VolumeType.ValueString() != "io1" {
if !plan.VolumeIOPS.IsNull() && plan.VolumeType.ValueString() != "io1" {
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")
return
}
} else {
} else if plan.Provider.ValueString() == "gcp" {
if !(plan.VolumeType.ValueString() == "" || plan.VolumeType.IsNull() || plan.VolumeType.ValueString() == "pd-ssd") {
resp.Diagnostics.AddAttributeError(
path.Root("volume_type"),
Expand All @@ -1177,6 +1183,19 @@ func (r *ServiceResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
fmt.Sprintf("Volume IOPS are not supported for %q provider", plan.Provider.ValueString()))
return
}
} else if plan.Provider.ValueString() == "azure" {
if plan.VolumeType.ValueString() != "" && plan.VolumeType.ValueString() != "StandardSSD_LRS" {
resp.Diagnostics.AddAttributeError(path.Root("volume_type"),
"volume_type is not supported for azure provider",
"Volume type is not supported for azure provider")
return
}
if !plan.VolumeIOPS.IsNull() {
resp.Diagnostics.AddAttributeError(path.Root("volume_iops"),
"volume_iops is not supported for azure provider",
"Volume IOPS are not supported for azure provider")
return
}
}

if !Contains[string]([]string{"lakehouse", "sa"}, plan.Topology.ValueString()) {
Expand Down
146 changes: 146 additions & 0 deletions internal/provider/service_resource_volume_type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,3 +453,149 @@ func TestServiceResourceAWSIO1VolumeType(t *testing.T) {
},
})
}

func TestServiceResourceAzureVolumeType(t *testing.T) {
const serviceID = "dbdgf42002418"

testURL, expectRequest, closeAPI := mockSkySQLAPI(t)
defer closeAPI()
os.Setenv("TF_SKYSQL_API_KEY", "[api-key]")
os.Setenv("TF_SKYSQL_API_BASE_URL", testURL)

r := require.New(t)

configureOnce.Reset()
var service *provisioning.Service
// Check API connectivity
expectRequest(func(w http.ResponseWriter, req *http.Request) {
r.Equal("/provisioning/v1/versions", req.URL.Path)
r.Equal("page_size=1", req.URL.RawQuery)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode([]provisioning.Version{})
})
// Create service
expectRequest(func(w http.ResponseWriter, req *http.Request) {
r.Equal(http.MethodPost, req.Method)
r.Equal("/provisioning/v1/services", req.URL.Path)
w.Header().Set("Content-Type", "application/json")
payload := provisioning.CreateServiceRequest{}
err := json.NewDecoder(req.Body).Decode(&payload)
r.NoError(err)
service = &provisioning.Service{
ID: serviceID,
Name: payload.Name,
Region: payload.Region,
Provider: payload.Provider,
Tier: "foundation",
Topology: payload.Topology,
Version: payload.Version,
Architecture: payload.Architecture,
Size: payload.Size,
Nodes: int(payload.Nodes),
SSLEnabled: payload.SSLEnabled,
NosqlEnabled: payload.NoSQLEnabled,
FQDN: "",
Status: "pending_create",
CreatedOn: int(time.Now().Unix()),
UpdatedOn: int(time.Now().Unix()),
CreatedBy: uuid.New().String(),
UpdatedBy: uuid.New().String(),
Endpoints: []provisioning.Endpoint{
{
Name: "primary",
Ports: []provisioning.Port{
{
Name: "readwrite",
Port: 3306,
Purpose: "readwrite",
},
},
Mechanism: payload.Mechanism,
AllowedAccounts: payload.AllowedAccounts,
},
},
StorageVolume: struct {
Size int `json:"size"`
VolumeType string `json:"volume_type"`
IOPS int `json:"iops"`
}{
Size: int(payload.Storage),
VolumeType: payload.VolumeType,
IOPS: int(payload.VolumeIOPS),
},
OutboundIps: nil,
IsActive: true,
ServiceType: payload.ServiceType,
ReplicationEnabled: false,
PrimaryHost: "",
MaxscaleSize: &(payload.Size),
MaxscaleNodes: 0,
}
r.NoError(json.NewEncoder(w).Encode(service))
w.WriteHeader(http.StatusCreated)
})
for i := 0; i < 3; i++ {
// Get service status
expectRequest(func(w http.ResponseWriter, req *http.Request) {
r.Equal(http.MethodGet, req.Method)
r.Equal("/provisioning/v1/services/"+serviceID, req.URL.Path)
w.Header().Set("Content-Type", "application/json")
service.Status = "ready"
json.NewEncoder(w).Encode(service)
w.WriteHeader(http.StatusOK)
})
}
expectRequest(func(w http.ResponseWriter, req *http.Request) {
r.Equal(
fmt.Sprintf("%s %s/%s", http.MethodDelete, "/provisioning/v1/services", serviceID),
fmt.Sprintf("%s %s", req.Method, req.URL.Path))
w.Header().Set("Content-Type", "application/json")
service.Status = "ready"
json.NewEncoder(w).Encode(service)
w.WriteHeader(http.StatusOK)
})

expectRequest(func(w http.ResponseWriter, req *http.Request) {
r.Equal(
fmt.Sprintf("%s %s/%s", http.MethodGet, "/provisioning/v1/services", serviceID),
fmt.Sprintf("%s %s", req.Method, req.URL.Path))
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusNotFound)
json.NewEncoder(w).Encode(&skysql.ErrorResponse{
Code: http.StatusNotFound,
})
})

resource.Test(t, resource.TestCase{
IsUnitTest: true,
ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){
"skysql": providerserver.NewProtocol6WithError(New("")()),
},
Steps: []resource.TestStep{
{
Config: `
resource "skysql_service" default {
service_type = "transactional"
topology = "es-single"
cloud_provider = "azure"
region = "us-central1"
name = "test-gcp"
architecture = "amd64"
nodes = 1
size = "sky-2x8"
storage = 100
ssl_enabled = true
version = "10.6.11-6-1"
volume_type = "StandardSSD_LRS"
deletion_protection = "false"
}
`,
Check: resource.ComposeAggregateTestCheckFunc([]resource.TestCheckFunc{
resource.TestCheckResourceAttr("skysql_service.default", "id", serviceID),
resource.TestCheckResourceAttr("skysql_service.default", "volume_type", "StandardSSD_LRS"),
}...),
},
},
})
}

0 comments on commit 5e98c0a

Please sign in to comment.