From 9262f6e743c98cea566cba6983a778c7dee06be0 Mon Sep 17 00:00:00 2001 From: Paul Tyng Date: Fri, 9 Apr 2021 16:12:14 -0400 Subject: [PATCH] Add sql_driver data source Fixes #14 --- docs/data-sources/driver.md | 23 ++++++++++ docs/data-sources/query.md | 4 +- docs/resources/migrate.md | 6 +-- docs/resources/migrate_directory.md | 6 +-- internal/provider/data_driver.go | 61 +++++++++++++++++++++++++++ internal/provider/data_driver_test.go | 54 ++++++++++++++++++++++++ internal/provider/db.go | 3 +- internal/provider/db_test.go | 11 +++++ internal/provider/provider.go | 6 ++- 9 files changed, 164 insertions(+), 10 deletions(-) create mode 100644 docs/data-sources/driver.md create mode 100644 internal/provider/data_driver.go create mode 100644 internal/provider/data_driver_test.go diff --git a/docs/data-sources/driver.md b/docs/data-sources/driver.md new file mode 100644 index 0000000..003cf03 --- /dev/null +++ b/docs/data-sources/driver.md @@ -0,0 +1,23 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "sql_driver Data Source - terraform-provider-sql" +subcategory: "" +description: |- + The sql_driver datasource allows you to determine which driver is in use by the provider. This is mostly useful for module development when you may communicate with multiple types of databases. +--- + +# sql_driver (Data Source) + +The `sql_driver` datasource allows you to determine which driver is in use by the provider. This is mostly useful for module development when you may communicate with multiple types of databases. + + + + +## Schema + +### Read-Only + +- **id** (String, Deprecated) This attribute is only present for some compatibility issues and should not be used. It will be removed in a future version. +- **name** (String) The name of the driver, currently this will be one of `pgx`, `mysql`, or `sqlserver`. + + diff --git a/docs/data-sources/query.md b/docs/data-sources/query.md index 46986b7..d8cb864 100644 --- a/docs/data-sources/query.md +++ b/docs/data-sources/query.md @@ -6,7 +6,7 @@ description: |- The sql_query datasource allows you to execute a SQL query against the database of your choice. --- -# Data Source `sql_query` +# sql_query (Data Source) The `sql_query` datasource allows you to execute a SQL query against the database of your choice. @@ -35,7 +35,7 @@ output "math" { - **query** (String) The query to execute. The types in this query will be reflected in the typing of the `result` attribute. -### Read-only +### Read-Only - **id** (String, Deprecated) This attribute is only present for some compatibility issues and should not be used. It will be removed in a future version. - **result** (List of Dynamic) The result of the query. This will be a list of objects. Each object will have attributes with names that match column names and types that match column types. The exact translation of types is dependent upon the database driver. diff --git a/docs/resources/migrate.md b/docs/resources/migrate.md index 0667d56..d0ddd35 100644 --- a/docs/resources/migrate.md +++ b/docs/resources/migrate.md @@ -6,7 +6,7 @@ description: |- --- -# Resource `sql_migrate` +# sql_migrate (Resource) @@ -51,7 +51,7 @@ output "rowcount" { - **migration** (Block List) (see [below for nested schema](#nestedblock--migration)) -### Read-only +### Read-Only - **complete_migrations** (List of Object) The completed migrations that have been run against your database. This list is used as storage to migrate down or as a trigger for downstream dependencies. (see [below for nested schema](#nestedatt--complete_migrations)) - **id** (String, Deprecated) This attribute is only present for some compatibility issues and should not be used. It will be removed in a future version. @@ -69,7 +69,7 @@ Required: ### Nested Schema for `complete_migrations` -Read-only: +Read-Only: - **down** (String) - **id** (String) diff --git a/docs/resources/migrate_directory.md b/docs/resources/migrate_directory.md index 363ab6c..4362e40 100644 --- a/docs/resources/migrate_directory.md +++ b/docs/resources/migrate_directory.md @@ -6,7 +6,7 @@ description: |- --- -# Resource `sql_migrate_directory` +# sql_migrate_directory (Resource) @@ -41,7 +41,7 @@ output "rowcount" { - **single_file_split** (String) Set this to a value if your migration up and down are in a single file, split on some constant string (ie. in the case of [shmig](https://github.com/mbucc/shmig) you would use `-- ==== DOWN ====`). -### Read-only +### Read-Only - **complete_migrations** (List of Object) The completed migrations that have been run against your database. This list is used as storage to migrate down or as a trigger for downstream dependencies. (see [below for nested schema](#nestedatt--complete_migrations)) - **id** (String, Deprecated) This attribute is only present for some compatibility issues and should not be used. It will be removed in a future version. @@ -49,7 +49,7 @@ output "rowcount" { ### Nested Schema for `complete_migrations` -Read-only: +Read-Only: - **down** (String) - **id** (String) diff --git a/internal/provider/data_driver.go b/internal/provider/data_driver.go new file mode 100644 index 0000000..65708ee --- /dev/null +++ b/internal/provider/data_driver.go @@ -0,0 +1,61 @@ +package provider + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tfprotov5/tftypes" + "github.com/paultyng/terraform-provider-sql/internal/server" +) + +type dataDriver struct { + driver string +} + +var _ server.DataSource = (*dataDriver)(nil) + +func newDataDriver(driver driverName) (*dataDriver, error) { + return &dataDriver{ + driver: string(driver), + }, nil +} + +func (d *dataDriver) Schema(context.Context) *tfprotov5.Schema { + return &tfprotov5.Schema{ + Block: &tfprotov5.SchemaBlock{ + Description: "The `sql_driver` datasource allows you to determine which driver is in use by the provider. This " + + "is mostly useful for module development when you may communicate with multiple types of databases.", + DescriptionKind: tfprotov5.StringKindMarkdown, + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "name", + Computed: true, + Description: "The name of the driver, currently this will be one of `pgx`, `mysql`, or `sqlserver`.", + DescriptionKind: tfprotov5.StringKindMarkdown, + Type: tftypes.String, + }, + + deprecatedIDAttribute(), + }, + }, + } +} + +func (d *dataDriver) Validate(ctx context.Context, config map[string]tftypes.Value) ([]*tfprotov5.Diagnostic, error) { + return nil, nil +} + +func (d *dataDriver) Read(ctx context.Context, config map[string]tftypes.Value) (map[string]tftypes.Value, []*tfprotov5.Diagnostic, error) { + return map[string]tftypes.Value{ + "name": tftypes.NewValue( + tftypes.String, + d.driver, + ), + + // just a placeholder, see deprecatedIDAttribute + "id": tftypes.NewValue( + tftypes.String, + "", + ), + }, nil, nil +} diff --git a/internal/provider/data_driver_test.go b/internal/provider/data_driver_test.go new file mode 100644 index 0000000..fa4e3df --- /dev/null +++ b/internal/provider/data_driver_test.go @@ -0,0 +1,54 @@ +package provider + +import ( + "fmt" + "testing" + + helperresource "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestDataDriver(t *testing.T) { + if testing.Short() { + t.Skip("skipping long test") + } + + for _, server := range testServers { + // TODO: check nulls for all these + t.Run(server.ServerType, func(t *testing.T) { + url, _, err := server.URL() + if err != nil { + t.Fatal(err) + } + + helperresource.UnitTest(t, helperresource.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories, + Steps: []helperresource.TestStep{ + { + + Config: fmt.Sprintf(` +provider "sql" { +url = %q + +max_idle_conns = 0 +} + +data "sql_driver" "test" { +} + `, url), + Check: helperresource.ComposeTestCheckFunc( + func(s *terraform.State) error { + rs := s.RootModule().Resources["data.sql_driver.test"] + att := rs.Primary.Attributes["name"] + if att != server.ExpectedDriver { + return fmt.Errorf("expected %q, got %q", server.ExpectedDriver, att) + } + return nil + }, + ), + }, + }, + }) + }) + } +} diff --git a/internal/provider/db.go b/internal/provider/db.go index f65cd15..5cd6e93 100644 --- a/internal/provider/db.go +++ b/internal/provider/db.go @@ -37,6 +37,7 @@ func (p *provider) connect(dsn string) error { switch scheme { case "postgres", "postgresql": + // TODO: use consts for these driver names? p.Driver = "pgx" case "mysql": p.Driver = "mysql" @@ -51,7 +52,7 @@ func (p *provider) connect(dsn string) error { return fmt.Errorf("unexpected datasource name scheme: %q", scheme) } - p.DB, err = sql.Open(p.Driver, dsn) + p.DB, err = sql.Open(string(p.Driver), dsn) if err != nil { return fmt.Errorf("unable to open database: %w", err) } diff --git a/internal/provider/db_test.go b/internal/provider/db_test.go index 1fac72e..9772fd8 100644 --- a/internal/provider/db_test.go +++ b/internal/provider/db_test.go @@ -33,6 +33,8 @@ var testServers = []*testServer{ return resource, url, nil }, + + ExpectedDriver: "mysql", }, { ServerType: "postgres", @@ -51,6 +53,8 @@ var testServers = []*testServer{ return resource, url, nil }, + + ExpectedDriver: "pgx", }, { ServerType: "cockroach", @@ -78,6 +82,8 @@ var testServers = []*testServer{ } return nil }, + + ExpectedDriver: "pgx", }, { ServerType: "sqlserver", @@ -95,6 +101,8 @@ var testServers = []*testServer{ return resource, url, nil }, + + ExpectedDriver: "sqlserver", }, } @@ -167,6 +175,9 @@ type testServer struct { StartContainer func() (*dockertest.Resource, string, error) OnReady func(*sql.DB) error + // This is the driver determination expected by the URL scheme + ExpectedDriver string + // these are all governed by the sync.Once // TODO: support multiple instances, so one test doesn't break // another, etc. or maybe just multiple databases in a single server? diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 298eb21..2fcc954 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -20,6 +20,7 @@ func New(version string) func() tfprotov5.ProviderServer { }) // data sources + s.MustRegisterDataSource("sql_driver", newDataDriver) s.MustRegisterDataSource("sql_query", newDataQuery) // resources @@ -30,10 +31,13 @@ func New(version string) func() tfprotov5.ProviderServer { } } +// TODO: use consts for driver names? +type driverName string + type provider struct { DB *sql.DB `argmapper:",typeOnly"` - Driver string + Driver driverName } var _ server.Provider = (*provider)(nil)