Skip to content

Commit

Permalink
add driver for snowflake/mysql (#66)
Browse files Browse the repository at this point in the history
* add driver for snowflake/mysql

* docs

* add validator

* add tests

* add example for make testacc

* use string syntax for markdown

* rename
  • Loading branch information
u110 authored Feb 27, 2025
1 parent 05511c5 commit 28dda91
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 8 deletions.
5 changes: 5 additions & 0 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@ default: testacc
.PHONY: testacc
testacc:
TF_ACC=1 go test ./... -v $(TESTARGS) -timeout 120m

# example)
# $ TROCCO_TEST_URL=https://localhost:4000 \
# TROCCO_API_KEY=**** \
# make testacc TESTARGS="-run TestAccConnectionResource"
5 changes: 4 additions & 1 deletion docs/resources/connection.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,10 @@ resource "trocco_connection" "s3_with_assume_role" {
- `aws_auth_type` (String) S3: The authentication type for the S3 connection. It must be one of `iam_user` or `assume_role`.
- `aws_iam_user` (Attributes) S3: IAM User configuration. (see [below for nested schema](#nestedatt--aws_iam_user))
- `description` (String) The description of the connection.
- `driver` (String) PostgreSQL: The name of a PostgreSQL driver.
- `driver` (String) Snowflake, MySQL, PostgreSQL: The name of a Database driver.
- MySQL: null, mysql_connector_java_5_1_49
- Snowflake: null, snowflake_jdbc_3_14_2, snowflake_jdbc_3_17_0,
- PostgreSQL: postgresql_42_5_1, postgresql_9_4_1205_jdbc41
- `gateway` (Attributes) MySQL, PostgreSQL: Whether to connect via SSH (see [below for nested schema](#nestedatt--gateway))
- `host` (String) Snowflake, PostgreSQL: The host of a (Snowflake, PostgreSQL) account.
- `password` (String, Sensitive) Snowflake, PostgreSQL: The password for the (Snowflake, PostgreSQL) user.
Expand Down
4 changes: 2 additions & 2 deletions internal/client/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ type CreateConnectionInput struct {
SSLClientCa *string `json:"ssl_client_ca,omitempty"`
SSLClientKey *string `json:"ssl_client_key,omitempty"`
SSLMode *parameter.NullableString `json:"ssl_mode,omitempty"`
Driver *string `json:"driver,omitempty"`
Driver *parameter.NullableString `json:"driver,omitempty"`
}

type UpdateConnectionInput struct {
Expand Down Expand Up @@ -163,7 +163,7 @@ type UpdateConnectionInput struct {
SSLClientCa *string `json:"ssl_client_ca,omitempty"`
SSLClientKey *string `json:"ssl_client_key,omitempty"`
SSLMode *parameter.NullableString `json:"ssl_mode,omitempty"`
Driver *string `json:"driver,omitempty"`
Driver *parameter.NullableString `json:"driver,omitempty"`
}

func (c *TroccoClient) GetConnections(connectionType string, in *GetConnectionsInput) (*ConnectionList, error) {
Expand Down
48 changes: 43 additions & 5 deletions internal/provider/connection_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func (m *connectionResourceModel) ToCreateConnectionInput() *client.CreateConnec
AWSAuthType: m.AWSAuthType.ValueStringPointer(),

// PostgreSQL Fields
Driver: m.Driver.ValueStringPointer(),
Driver: model.NewNullableString(m.Driver),
}

// SSL Fields
Expand Down Expand Up @@ -182,7 +182,7 @@ func (m *connectionResourceModel) ToUpdateConnectionInput() *client.UpdateConnec
AWSAuthType: m.AWSAuthType.ValueStringPointer(),

// PostgreSQL Fields
Driver: m.Driver.ValueStringPointer(),
Driver: model.NewNullableString(m.Driver),
}

// SSL Fields
Expand Down Expand Up @@ -569,10 +569,23 @@ func (r *connectionResource) Schema(

// PostgreSQL Fields
"driver": schema.StringAttribute{
MarkdownDescription: "PostgreSQL: The name of a PostgreSQL driver.",
Optional: true,
MarkdownDescription: `Snowflake, MySQL, PostgreSQL: The name of a Database driver.
- MySQL: null, mysql_connector_java_5_1_49
- Snowflake: null, snowflake_jdbc_3_14_2, snowflake_jdbc_3_17_0,
- PostgreSQL: postgresql_42_5_1, postgresql_9_4_1205_jdbc41
`,
Optional: true,
Validators: []validator.String{
stringvalidator.OneOf("postgresql_42_5_1", "postgresql_9_4_1205_jdbc41"),
stringvalidator.OneOf(
// MySQL
"mysql_connector_java_5_1_49",
// Snowflake
"snowflake_jdbc_3_14_2",
"snowflake_jdbc_3_17_0",
// PostgreSQL
"postgresql_42_5_1",
"postgresql_9_4_1205_jdbc41",
),
},
},
},
Expand Down Expand Up @@ -885,6 +898,7 @@ func (r *connectionResource) ValidateConfig(
if plan.AuthMethod.ValueString() == "user_password" {
validateRequiredString(plan.Password, "password", "Snowflake", resp)
}
validateStringAgainstPatterns(plan.Driver, "driver", "Snowflake", resp, "snowflake_jdbc_3_14_2", "snowflake_jdbc_3_17_0")
case "gcs":
validateRequiredString(plan.ApplicationName, "application_name", "GCS", resp)
validateRequiredString(plan.ServiceAccountEmail, "service_account_email", "GCS", resp)
Expand All @@ -896,6 +910,7 @@ func (r *connectionResource) ValidateConfig(
validateRequiredInt(plan.Port, "port", "MySQL", resp)
validateRequiredString(plan.UserName, "user_name", "MySQL", resp)
validateRequiredString(plan.Password, "password", "MySQL", resp)
validateStringAgainstPatterns(plan.Driver, "driver", "MySQL", resp, "mysql_connector_java_5_1_49")
if plan.Gateway != nil {
validateRequiredString(plan.Gateway.Host, "gateway.host", "MySQL", resp)
validateRequiredInt(plan.Gateway.Port, "gateway.port", "MySQL", resp)
Expand Down Expand Up @@ -948,6 +963,7 @@ func (r *connectionResource) ValidateConfig(
validateRequiredInt(plan.Port, "port", "PostgreSQL", resp)
validateRequiredString(plan.UserName, "user_name", "PostgreSQL", resp)
validateRequiredString(plan.Driver, "driver", "PostgreSQL", resp)
validateStringAgainstPatterns(plan.Driver, "driver", "PostgreSQL", resp, "postgresql_42_5_1", "postgresql_9_4_1205_jdbc41")
if plan.Gateway != nil {
validateRequiredString(plan.Gateway.Host, "gateway.host", "PostgreSQL", resp)
validateRequiredInt(plan.Gateway.Port, "gateway.port", "PostgreSQL", resp)
Expand All @@ -956,6 +972,28 @@ func (r *connectionResource) ValidateConfig(
}
}

func validateStringAgainstPatterns(field types.String, fieldName, connectionType string, resp *resource.ValidateConfigResponse, patterns ...string) {
if field.IsNull() {
return
}

for _, pattern := range patterns {
if field.ValueString() == pattern {
return
}
}

resp.Diagnostics.AddError(
fieldName,
fmt.Sprintf("%s: `%s` is invalid for %s connection. Valid values are: %s",
fieldName,
field.ValueString(),
connectionType,
strings.Join(patterns, ", "),
),
)
}

func validateRequiredString(field types.String, fieldName, connectionType string, resp *resource.ValidateConfigResponse) {
if field.IsNull() {
resp.Diagnostics.AddError(
Expand Down
123 changes: 123 additions & 0 deletions internal/provider/connection_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package provider

import (
"fmt"
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-testing/helper/resource"
Expand All @@ -19,6 +20,7 @@ func TestAccConnectionResource(t *testing.T) {
name = "test"
description = "The quick brown fox jumps over the lazy dog."
project_id = "test"
service_account_json_key = "{\"type\":\"service_account\",\"project_id\":\"\",\"private_key_id\":\"\",\"private_key\":\"\"}"
}
Expand All @@ -42,6 +44,127 @@ func TestAccConnectionResource(t *testing.T) {
return fmt.Sprintf("bigquery,%s", connectionID), nil
},
},
// Snowflake
{
Config: providerConfig + `
resource "trocco_connection" "snowflake_test" {
connection_type = "snowflake"
auth_method = "user_password"
name = "snowflake test"
host = "example.snowflakecomputing.com"
user_name = "root"
password = "password"
}
`,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("trocco_connection.snowflake_test", "connection_type", "snowflake"),
resource.TestCheckResourceAttr("trocco_connection.snowflake_test", "name", "snowflake test"),
resource.TestCheckResourceAttrSet("trocco_connection.snowflake_test", "id"),
),
},
// MySQL
{
Config: providerConfig + `
resource "trocco_connection" "mysql_test" {
connection_type = "mysql"
name = "mysql test"
host = "localhost"
user_name = "root"
password = "password"
port = 3306
}
`,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("trocco_connection.mysql_test", "connection_type", "mysql"),
resource.TestCheckResourceAttr("trocco_connection.mysql_test", "name", "mysql test"),
resource.TestCheckResourceAttrSet("trocco_connection.mysql_test", "id"),
),
},
// PostgreSQL
{
Config: providerConfig + `
resource "trocco_connection" "postgresql_test" {
connection_type = "postgresql"
name = "postgresql test"
host = "localhost"
user_name = "root"
password = "password"
port = 5432
driver = "postgresql_42_5_1"
}
`,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("trocco_connection.postgresql_test", "connection_type", "postgresql"),
resource.TestCheckResourceAttrSet("trocco_connection.postgresql_test", "id"),
),
},
},
})
}

func TestInvalidDriver(t *testing.T) {
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: providerConfig + `
resource "trocco_connection" "invalid_driver_test" {
connection_type = "postgresql"
name = "invalid driver test"
host = "localhost"
user_name = "root"
password = "password"
port = 5432
driver = "invalid_driver"
}
`,
ExpectError: regexp.MustCompile("driver: `invalid_driver` is invalid for PostgreSQL connection. "),
},
{
Config: providerConfig + `
resource "trocco_connection" "mismatch_driver_test_postgresql" {
connection_type = "postgresql"
name = "invalid driver test"
host = "localhost"
user_name = "root"
password = "password"
port = 5432
driver = "mysql_connector_java_5_1_49"
}
`,
ExpectError: regexp.MustCompile("are: postgresql_42_5_1, postgresql_9_4_1205_jdbc41"),
},
{
Config: providerConfig + `
resource "trocco_connection" "mismatch_driver_test_mysql" {
connection_type = "mysql"
name = "invalid driver test"
host = "localhost"
user_name = "root"
password = "password"
port = 3306
driver = "snowflake_jdbc_3_14_2"
}
`,
ExpectError: regexp.MustCompile("are: mysql_connector_java_5_1_49"),
},
{
Config: providerConfig + `
resource "trocco_connection" "mismatch_driver_test_snowflake" {
connection_type = "snowflake"
name = "invalid driver test"
auth_method = "user_password"
host = "example.snowflakecomputing.com"
user_name = "root"
password = "password"
driver = "mysql_connector_java_5_1_49"
}
`,
ExpectError: regexp.MustCompile("are: snowflake_jdbc_3_14_2, snowflake_jdbc_3_17_0"),
},
},
})
}

0 comments on commit 28dda91

Please sign in to comment.