From 5ba0ed3c6a987eeef6c13ed14e3859e17399c1b2 Mon Sep 17 00:00:00 2001 From: Yuu Ito Date: Thu, 20 Feb 2025 09:28:50 +0900 Subject: [PATCH] Revert "Revert "add connection_type: PostgreSQL"" (#56) * Revert "Revert "add connection_type: PostgreSQL (#45)" (#55)" This reverts commit dc40fddc350f81736b7777d3991717bf9102e564. * remove computed * fix * nesting ssl_mode as a child element or attribute under ssl * run go generate ./... * remove validation * fix * go generate --------- Co-authored-by: mizuho.otake Co-authored-by: kkatamot --- docs/resources/connection.md | 80 ++++++++++++++---- .../resources/trocco_connection/postgresql.tf | 41 +++++++++ internal/client/connection.go | 12 +++ internal/provider/connection_resource.go | 83 +++++++++++++++---- internal/provider/model/connection/ssl.go | 7 +- templates/resources/connection.md.tmpl | 6 +- 6 files changed, 193 insertions(+), 36 deletions(-) create mode 100644 examples/resources/trocco_connection/postgresql.tf diff --git a/docs/resources/connection.md b/docs/resources/connection.md index d4eba4be..45a216ed 100644 --- a/docs/resources/connection.md +++ b/docs/resources/connection.md @@ -32,7 +32,7 @@ resource "trocco_connection" "bigquery" { } ``` -### Snowflake +### Snowflake ```terraform resource "trocco_connection" "snowflake" { @@ -198,10 +198,11 @@ 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. -- `gateway` (Attributes) MySQL: Whether to connect via SSH (see [below for nested schema](#nestedatt--gateway)) -- `host` (String) Snowflake: The host of a Snowflake account. -- `password` (String, Sensitive) Snowflake: The password for the Snowflake user. -- `port` (Number) MySQL: The port of the MySQL server. +- `driver` (String) PostgreSQL: The name of a PostgreSQL driver. +- `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. +- `port` (Number) MySQL, PostgreSQL: The port of the (MySQL, PostgreSQL) server. - `private_key` (String, Sensitive) Snowflake: A private key for the Snowflake user. - `project_id` (String) BigQuery, GCS: A GCP project ID. - `resource_group_id` (Number) The ID of the resource group the connection belongs to. @@ -209,8 +210,8 @@ resource "trocco_connection" "s3_with_assume_role" { - `security_token` (String, Sensitive) Salesforce: Security token. - `service_account_email` (String, Sensitive) GCS: A GCP service account email. - `service_account_json_key` (String, Sensitive) BigQuery, Google Sheets: A GCP service account key. -- `ssl` (Attributes) MySQL: SSL configuration. (see [below for nested schema](#nestedatt--ssl)) -- `user_name` (String) Snowflake: The name of a Snowflake user. +- `ssl` (Attributes) MySQL, PostgreSQL: SSL configuration. (see [below for nested schema](#nestedatt--ssl)) +- `user_name` (String) Snowflake, PostgreSQL: The name of a (Snowflake, PostgreSQL) user. ### Read-Only @@ -239,12 +240,12 @@ Optional: Optional: -- `host` (String, Sensitive) MySQL: SSH Host -- `key` (String, Sensitive) MySQL: SSH Private Key -- `key_passphrase` (String, Sensitive) MySQL: SSH Private Key Passphrase -- `password` (String, Sensitive) MySQL: SSH Password -- `port` (Number, Sensitive) MySQL: SSH Port -- `user_name` (String, Sensitive) MySQL: SSH User +- `host` (String, Sensitive) MySQL, PostgreSQL: SSH Host +- `key` (String, Sensitive) MySQL, PostgreSQL: SSH Private Key +- `key_passphrase` (String, Sensitive) MySQL, PostgreSQL: SSH Private Key Passphrase +- `password` (String, Sensitive) MySQL, PostgreSQL: SSH Password +- `port` (Number, Sensitive) MySQL, PostgreSQL: SSH Port +- `user_name` (String, Sensitive) MySQL, PostgreSQL: SSH User @@ -252,13 +253,60 @@ Optional: Optional: -- `ca` (String, Sensitive) MySQL: CA certificate -- `cert` (String, Sensitive) MySQL: Certificate (CRT file) -- `key` (String, Sensitive) MySQL: Key (KEY file) +- `ca` (String, Sensitive) MySQL, PostgreSQL: CA certificate +- `cert` (String, Sensitive) MySQL, PostgreSQL: Certificate (CRT file) +- `key` (String, Sensitive) MySQL, PostgreSQL: Key (KEY file) +- `ssl_mode` (String) PostgreSQL: SSL connection mode. +### PostgreSQL + +```terraform +resource "trocco_connection" "postgresql" { + connection_type = "postgresql" + name = "PostgreSQL Example" + description = "This is a PostgreSQL connection example" + host = "db.example.com" + port = 5432 + user_name = "root" + password = "password" + driver = "postgresql_42_5_1" + ssl = { + ca = <<-SSL_CA + -----BEGIN PRIVATE KEY----- + ...SSL CA... + -----END PRIVATE KEY----- + SSL_CA + cert = <<-SSL_CERT + -----BEGIN CERTIFICATE----- + ...SSL CRT... + -----END CERTIFICATE----- + SSL_CERT + key = <<-SSL_KEY + -----BEGIN PRIVATE KEY----- + ...SSL KEY... + -----END PRIVATE KEY----- + SSL_KEY + ssl_mode = "require" + } + gateway = { + host = "gateway.example.com" + port = 1234 + user_name = "gateway-joe" + password = "gateway-joepass" + key = <<-GATEWAY_KEY + -----BEGIN PRIVATE KEY----- + ... GATEWAY KEY... + -----END PRIVATE KEY----- + GATEWAY_KEY + key_passphrase = "sample_passphrase" + } + resource_group_id = 1 +} +``` + ## Import Import is supported using the following syntax: diff --git a/examples/resources/trocco_connection/postgresql.tf b/examples/resources/trocco_connection/postgresql.tf new file mode 100644 index 00000000..1a2e2d5d --- /dev/null +++ b/examples/resources/trocco_connection/postgresql.tf @@ -0,0 +1,41 @@ +resource "trocco_connection" "postgresql" { + connection_type = "postgresql" + name = "PostgreSQL Example" + description = "This is a PostgreSQL connection example" + host = "db.example.com" + port = 5432 + user_name = "root" + password = "password" + driver = "postgresql_42_5_1" + ssl = { + ca = <<-SSL_CA + -----BEGIN PRIVATE KEY----- + ...SSL CA... + -----END PRIVATE KEY----- + SSL_CA + cert = <<-SSL_CERT + -----BEGIN CERTIFICATE----- + ...SSL CRT... + -----END CERTIFICATE----- + SSL_CERT + key = <<-SSL_KEY + -----BEGIN PRIVATE KEY----- + ...SSL KEY... + -----END PRIVATE KEY----- + SSL_KEY + ssl_mode = "require" + } + gateway = { + host = "gateway.example.com" + port = 1234 + user_name = "gateway-joe" + password = "gateway-joepass" + key = <<-GATEWAY_KEY + -----BEGIN PRIVATE KEY----- + ... GATEWAY KEY... + -----END PRIVATE KEY----- + GATEWAY_KEY + key_passphrase = "sample_passphrase" + } + resource_group_id = 1 +} diff --git a/internal/client/connection.go b/internal/client/connection.go index 3865bb39..62defafb 100644 --- a/internal/client/connection.go +++ b/internal/client/connection.go @@ -104,6 +104,12 @@ type CreateConnectionInput struct { AWSSecretAccessKey *string `json:"aws_secret_access_key,omitempty"` AWSAssumeRoleAccountID *string `json:"aws_assume_role_account_id,omitempty"` AWSAssumeRoleName *string `json:"aws_assume_role_name,omitempty"` + + // PostgreSQL Fields + 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"` } type UpdateConnectionInput struct { @@ -152,6 +158,12 @@ type UpdateConnectionInput struct { AWSSecretAccessKey *string `json:"aws_secret_access_key,omitempty"` AWSAssumeRoleAccountID *string `json:"aws_assume_role_account_id,omitempty"` AWSAssumeRoleName *string `json:"aws_assume_role_name,omitempty"` + + // PostgreSQL Fields + 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"` } func (c *TroccoClient) GetConnections(connectionType string, in *GetConnectionsInput) (*ConnectionList, error) { diff --git a/internal/provider/connection_resource.go b/internal/provider/connection_resource.go index 52618526..26028e0b 100644 --- a/internal/provider/connection_resource.go +++ b/internal/provider/connection_resource.go @@ -57,6 +57,9 @@ type connectionResourceModel struct { SSL *connection.SSL `tfsdk:"ssl"` Gateway *connection.Gateway `tfsdk:"gateway"` + // PostgreSQL Fields + Driver types.String `tfsdk:"driver"` + // S3 Fields AWSAuthType types.String `tfsdk:"aws_auth_type"` AWSIAMUser *connection.AWSIAMUser `tfsdk:"aws_iam_user"` @@ -99,6 +102,9 @@ func (m *connectionResourceModel) ToCreateConnectionInput() *client.CreateConnec // S3 Fields AWSAuthType: m.AWSAuthType.ValueStringPointer(), + + // PostgreSQL Fields + Driver: m.Driver.ValueStringPointer(), } // SSL Fields @@ -106,7 +112,10 @@ func (m *connectionResourceModel) ToCreateConnectionInput() *client.CreateConnec input.SSL = model.NewNullableBool(types.BoolValue(true)) input.SSLCA = m.SSL.CA.ValueStringPointer() input.SSLCert = m.SSL.Cert.ValueStringPointer() + input.SSLClientCa = m.SSL.Cert.ValueStringPointer() input.SSLKey = m.SSL.Key.ValueStringPointer() + input.SSLClientKey = m.SSL.Key.ValueStringPointer() + input.SSLMode = model.NewNullableString(m.SSL.SSLMode) } else { input.SSL = model.NewNullableBool(types.BoolValue(false)) } @@ -171,6 +180,9 @@ func (m *connectionResourceModel) ToUpdateConnectionInput() *client.UpdateConnec // S3 Fields AWSAuthType: m.AWSAuthType.ValueStringPointer(), + + // PostgreSQL Fields + Driver: m.Driver.ValueStringPointer(), } // SSL Fields @@ -179,6 +191,9 @@ func (m *connectionResourceModel) ToUpdateConnectionInput() *client.UpdateConnec input.SSLCA = m.SSL.CA.ValueStringPointer() input.SSLCert = m.SSL.Cert.ValueStringPointer() input.SSLKey = m.SSL.Key.ValueStringPointer() + input.SSLClientCa = m.SSL.Cert.ValueStringPointer() + input.SSLClientKey = m.SSL.Key.ValueStringPointer() + input.SSLMode = model.NewNullableString(m.SSL.SSLMode) } else { input.SSL = model.NewNullableBool(types.BoolValue(false)) } @@ -264,7 +279,7 @@ func (r *connectionResource) Schema( stringplanmodifier.RequiresReplace(), }, Validators: []validator.String{ - stringvalidator.OneOf("bigquery", "snowflake", "gcs", "google_spreadsheets", "mysql", "salesforce", "s3"), + stringvalidator.OneOf("bigquery", "snowflake", "gcs", "google_spreadsheets", "mysql", "salesforce", "s3", "postgresql"), }, }, "id": schema.Int64Attribute{ @@ -318,14 +333,14 @@ func (r *connectionResource) Schema( // Snowflake Fields "host": schema.StringAttribute{ - MarkdownDescription: "Snowflake: The host of a Snowflake account.", + MarkdownDescription: "Snowflake, PostgreSQL: The host of a (Snowflake, PostgreSQL) account.", Optional: true, Validators: []validator.String{ stringvalidator.UTF8LengthAtLeast(1), }, }, "user_name": schema.StringAttribute{ - MarkdownDescription: "Snowflake: The name of a Snowflake user.", + MarkdownDescription: "Snowflake, PostgreSQL: The name of a (Snowflake, PostgreSQL) user.", Optional: true, Validators: []validator.String{ stringvalidator.UTF8LengthAtLeast(1), @@ -346,7 +361,7 @@ func (r *connectionResource) Schema( }, }, "password": schema.StringAttribute{ - MarkdownDescription: "Snowflake: The password for the Snowflake user.", + MarkdownDescription: "Snowflake, PostgreSQL: The password for the (Snowflake, PostgreSQL) user.", Optional: true, Sensitive: true, Validators: []validator.String{ @@ -381,19 +396,20 @@ func (r *connectionResource) Schema( // MySQL Fields "port": schema.Int64Attribute{ - MarkdownDescription: "MySQL: The port of the MySQL server.", + MarkdownDescription: "MySQL, PostgreSQL: The port of the (MySQL, PostgreSQL) server.", Optional: true, Validators: []validator.Int64{ int64validator.AtLeast(1), int64validator.AtMost(65535), }, }, + "ssl": schema.SingleNestedAttribute{ - MarkdownDescription: "MySQL: SSL configuration.", + MarkdownDescription: "MySQL, PostgreSQL: SSL configuration.", Optional: true, Attributes: map[string]schema.Attribute{ "ca": schema.StringAttribute{ - MarkdownDescription: "MySQL: CA certificate", + MarkdownDescription: "MySQL, PostgreSQL: CA certificate", Optional: true, Sensitive: true, Validators: []validator.String{ @@ -403,7 +419,7 @@ func (r *connectionResource) Schema( Default: stringdefault.StaticString(""), }, "cert": schema.StringAttribute{ - MarkdownDescription: "MySQL: Certificate (CRT file)", + MarkdownDescription: "MySQL, PostgreSQL: Certificate (CRT file)", Optional: true, Sensitive: true, Validators: []validator.String{ @@ -413,7 +429,7 @@ func (r *connectionResource) Schema( Default: stringdefault.StaticString(""), }, "key": schema.StringAttribute{ - MarkdownDescription: "MySQL: Key (KEY file)", + MarkdownDescription: "MySQL, PostgreSQL: Key (KEY file)", Optional: true, Sensitive: true, Validators: []validator.String{ @@ -422,14 +438,21 @@ func (r *connectionResource) Schema( Computed: true, Default: stringdefault.StaticString(""), }, + "ssl_mode": schema.StringAttribute{ + MarkdownDescription: "PostgreSQL: SSL connection mode.", + Optional: true, + Validators: []validator.String{ + stringvalidator.OneOf("require", "verify-ca"), + }, + }, }, }, "gateway": schema.SingleNestedAttribute{ - MarkdownDescription: "MySQL: Whether to connect via SSH", + MarkdownDescription: "MySQL, PostgreSQL: Whether to connect via SSH", Optional: true, Attributes: map[string]schema.Attribute{ "host": schema.StringAttribute{ - MarkdownDescription: "MySQL: SSH Host", + MarkdownDescription: "MySQL, PostgreSQL: SSH Host", Optional: true, Sensitive: true, Validators: []validator.String{ @@ -437,7 +460,7 @@ func (r *connectionResource) Schema( }, }, "port": schema.Int64Attribute{ - MarkdownDescription: "MySQL: SSH Port", + MarkdownDescription: "MySQL, PostgreSQL: SSH Port", Optional: true, Sensitive: true, Validators: []validator.Int64{ @@ -446,7 +469,7 @@ func (r *connectionResource) Schema( }, }, "user_name": schema.StringAttribute{ - MarkdownDescription: "MySQL: SSH User", + MarkdownDescription: "MySQL, PostgreSQL: SSH User", Optional: true, Sensitive: true, Validators: []validator.String{ @@ -454,21 +477,21 @@ func (r *connectionResource) Schema( }, }, "password": schema.StringAttribute{ - MarkdownDescription: "MySQL: SSH Password", + MarkdownDescription: "MySQL, PostgreSQL: SSH Password", Optional: true, Computed: true, Sensitive: true, Default: stringdefault.StaticString(""), }, "key": schema.StringAttribute{ - MarkdownDescription: "MySQL: SSH Private Key", + MarkdownDescription: "MySQL, PostgreSQL: SSH Private Key", Optional: true, Computed: true, Sensitive: true, Default: stringdefault.StaticString(""), }, "key_passphrase": schema.StringAttribute{ - MarkdownDescription: "MySQL: SSH Private Key Passphrase", + MarkdownDescription: "MySQL, PostgreSQL: SSH Private Key Passphrase", Optional: true, Computed: true, Sensitive: true, @@ -543,6 +566,15 @@ func (r *connectionResource) Schema( }, }, }, + + // PostgreSQL Fields + "driver": schema.StringAttribute{ + MarkdownDescription: "PostgreSQL: The name of a PostgreSQL driver.", + Optional: true, + Validators: []validator.String{ + stringvalidator.OneOf("postgresql_42_5_1", "postgresql_9_4_1205_jdbc41"), + }, + }, }, } } @@ -611,6 +643,9 @@ func (r *connectionResource) Create( AWSAuthType: types.StringPointerValue(conn.AWSAuthType), AWSIAMUser: plan.AWSIAMUser, AWSAssumeRole: connection.NewAWSAssumeRole(conn), + + // PostgreSQL Fields + Driver: types.StringPointerValue(conn.Driver), } resp.Diagnostics.Append(resp.State.Set(ctx, newState)...) } @@ -696,6 +731,9 @@ func (r *connectionResource) Update( AWSAuthType: types.StringPointerValue(connection.AWSAuthType), AWSIAMUser: plan.AWSIAMUser, AWSAssumeRole: plan.AWSAssumeRole, + + // PostgreSQL Fields + Driver: types.StringPointerValue(connection.Driver), } resp.Diagnostics.Append(resp.State.Set(ctx, newState)...) } @@ -760,6 +798,9 @@ func (r *connectionResource) Read( AWSAuthType: types.StringPointerValue(conn.AWSAuthType), AWSIAMUser: state.AWSIAMUser, AWSAssumeRole: connection.NewAWSAssumeRole(conn), + + // PostgreSQL Fields + Driver: types.StringPointerValue(conn.Driver), } resp.Diagnostics.Append(resp.State.Set(ctx, newState)...) } @@ -902,6 +943,16 @@ func (r *connectionResource) ValidateConfig( validateRequiredString(plan.AWSAssumeRole.AccountRoleName, "aws_assume_role.account_role_name", "S3", resp) } } + case "postgresql": + validateRequiredString(plan.Host, "host", "PostgreSQL", resp) + validateRequiredInt(plan.Port, "port", "PostgreSQL", resp) + validateRequiredString(plan.UserName, "user_name", "PostgreSQL", resp) + validateRequiredString(plan.Driver, "driver", "PostgreSQL", resp) + if plan.Gateway != nil { + validateRequiredString(plan.Gateway.Host, "gateway.host", "PostgreSQL", resp) + validateRequiredInt(plan.Gateway.Port, "gateway.port", "PostgreSQL", resp) + validateRequiredString(plan.Gateway.UserName, "gateway.user_name", "PostgreSQL", resp) + } } } diff --git a/internal/provider/model/connection/ssl.go b/internal/provider/model/connection/ssl.go index ea732ae6..91e35923 100644 --- a/internal/provider/model/connection/ssl.go +++ b/internal/provider/model/connection/ssl.go @@ -3,7 +3,8 @@ package connection import "github.com/hashicorp/terraform-plugin-framework/types" type SSL struct { - CA types.String `tfsdk:"ca"` - Cert types.String `tfsdk:"cert"` - Key types.String `tfsdk:"key"` + CA types.String `tfsdk:"ca"` + Cert types.String `tfsdk:"cert"` + Key types.String `tfsdk:"key"` + SSLMode types.String `tfsdk:"ssl_mode"` } diff --git a/templates/resources/connection.md.tmpl b/templates/resources/connection.md.tmpl index 9366d8d4..dfb57451 100644 --- a/templates/resources/connection.md.tmpl +++ b/templates/resources/connection.md.tmpl @@ -15,7 +15,7 @@ description: |- {{codefile "terraform" "examples/resources/trocco_connection/bigquery.tf"}} -### Snowflake +### Snowflake {{codefile "terraform" "examples/resources/trocco_connection/snowflake.tf"}} @@ -41,6 +41,10 @@ description: |- {{.SchemaMarkdown}} +### PostgreSQL + +{{codefile "terraform" "examples/resources/trocco_connection/postgresql.tf"}} + ## Import Import is supported using the following syntax: