Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Datashare add schema tables #65

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/resources/datashare.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ resource "redshift_datashare" "my_datashare" {
"public",
"other",
]
# Optional. Specifies which schema tables to expose to the datashare.
schema_tables = [
"schema1.table1",
"schema1.table2",
"schema2.table1",
"schema2.table2",
]
}
```

Expand All @@ -50,6 +57,7 @@ resource "redshift_datashare" "my_datashare" {
- **id** (String) The ID of this resource.
- **owner** (String) The user who owns the datashare.
- **publicly_accessible** (Boolean) Specifies whether the datashare can be shared to clusters that are publicly accessible. Default is `false`.
- **schema_tables** (Set of String) Defines which schema tables are exposed to the data share.
- **schemas** (Set of String) Defines which schemas are exposed to the data share.

### Read-Only
Expand Down
7 changes: 7 additions & 0 deletions examples/resources/redshift_datashare/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,11 @@ resource "redshift_datashare" "my_datashare" {
"public",
"other",
]
# Optional. Specifies which schema tables to expose to the datashare.
schema_tables = [
"schema1.table1",
"schema1.table2",
"schema2.table1",
"schema2.table2",
]
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ require (
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 // indirect
github.com/aws/aws-sdk-go v1.25.3 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.3.0 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.3.0
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.2.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.1.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.2.0 // indirect
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki
github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE=
github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/andybalholm/crlf v0.0.0-20171020200849-670099aa064f/go.mod h1:k8feO4+kXDxro6ErPXBRTJ/ro2mf0SsFG8s7doP9kJE=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/apparentlymart/go-cidr v1.0.1 h1:NmIwLZ/KdsjIUlhf+/Np40atNXm/+lZ5txfTJ/SpF+U=
github.com/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
Expand All @@ -64,6 +65,7 @@ github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
github.com/aws/aws-sdk-go v1.25.3 h1:uM16hIw9BotjZKMZlX05SN2EFtaWfi/NonPKIARiBLQ=
Expand Down Expand Up @@ -111,13 +113,16 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34=
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8=
github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4=
github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
Expand Down Expand Up @@ -277,6 +282,7 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
Expand Down Expand Up @@ -315,6 +321,7 @@ github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce/go.mod h1:uFMI8w+ref4
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down
9 changes: 9 additions & 0 deletions redshift/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,12 @@ func setToPgIdentList(identifiers *schema.Set, prefix string) string {

return strings.Join(quoted, ",")
}

func contains(s []string, e string) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}
147 changes: 147 additions & 0 deletions redshift/resource_redshift_datashare.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const (
dataShareProducerNamespaceAttr = "producer_namespace"
dataShareCreatedAttr = "created"
dataShareSchemasAttr = "schemas"
dataShareSchemaTablesAttr = "schema_tables"
)

func redshiftDatashare() *schema.Resource {
Expand Down Expand Up @@ -92,6 +93,18 @@ such as RA3.
},
},
},
dataShareSchemaTablesAttr: {
Type: schema.TypeSet,
Optional: true,
Description: "Defines which schema tables are exposed to the data share.",
Set: schema.HashString,
Elem: &schema.Schema{
Type: schema.TypeString,
StateFunc: func(val interface{}) string {
return strings.ToLower(val.(string))
},
},
},
},
}
}
Expand Down Expand Up @@ -152,6 +165,20 @@ func resourceRedshiftDatashareCreate(db *DBConnection, d *schema.ResourceData) e
}
}

var schemas []string
var tables []string
for _, schemaTable := range d.Get(dataShareSchemaTablesAttr).(*schema.Set).List() {
schemaName := strings.Split(schemaTable.(string), ".")[0]
appendIfTrue(contains(schemas, schemaName), schemaName, &schemas)
tableName := schemaTable.(string)
appendIfTrue(contains(tables, tableName), tableName, &tables)
}

err = addSchemaTablesToDatashare(tx, shareName, schemas, tables)
if err != nil {
return err
}

if err = tx.Commit(); err != nil {
return fmt.Errorf("could not commit transaction: %w", err)
}
Expand All @@ -172,6 +199,19 @@ func addSchemaToDatashare(tx *sql.Tx, shareName string, schemaName string) error
return err
}

func addSchemaTablesToDatashare(tx *sql.Tx, shareName string, schemas []string, tables []string) error {
schemasString := strings.Join(schemas, ",")
tablesString := strings.Join(tables, ",")
err := resourceRedshiftDatashareAddTablesInSchema(tx, shareName, schemasString, tablesString)
return err
}

func removeSchemaTablesFromDatashare(tx *sql.Tx, shareName string, tables []string) error {
tablesString := strings.Join(tables, ",")
err := resourceRedshiftDatashareRemoveTablesInSchema(tx, shareName, tablesString)
return err
}

func resourceRedshiftDatashareAddSchema(tx *sql.Tx, shareName string, schemaName string) error {
query := fmt.Sprintf("ALTER DATASHARE %s ADD SCHEMA %s", pq.QuoteIdentifier(shareName), pq.QuoteIdentifier(schemaName))
log.Printf("[DEBUG] %s\n", query)
Expand Down Expand Up @@ -208,6 +248,35 @@ func resourceRedshiftDatashareAddAllTables(tx *sql.Tx, shareName string, schemaN
return err
}

func resourceRedshiftDatashareAddTablesInSchema(tx *sql.Tx, shareName string, schemas string, tables string) error {
query := fmt.Sprintf("ALTER DATASHARE %s ADD SCHEMA %s", pq.QuoteIdentifier(shareName), pq.QuoteIdentifier(schemas))
log.Printf("[DEBUG] %s\n", query)
_, err := tx.Exec(query)
if err != nil {
// if the schema is already in the datashare we get a "duplicate schema" error code. This is fine.
if pqErr, ok := err.(*pq.Error); ok {
if string(pqErr.Code) == pqErrorCodeDuplicateSchema {
log.Printf("[WARN] Schema %s already exists in datashare %s\n", schemas, shareName)
} else {
return err
}
} else {
return err
}
}
query = fmt.Sprintf("ALTER DATASHARE %s ADD TABLE %s", pq.QuoteIdentifier(shareName), pq.QuoteIdentifier(tables))
log.Printf("[DEBUG] %s\n", query)
_, err = tx.Exec(query)
return err
}

func resourceRedshiftDatashareRemoveTablesInSchema(tx *sql.Tx, shareName string, tables string) error {
query := fmt.Sprintf("ALTER DATASHARE %s REMOVE TABLE %s", pq.QuoteIdentifier(shareName), pq.QuoteIdentifier(tables))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tested that it will actually work for more than one table in the list? To my knowledge, this will produce an SQL like ALTER DATASHARE "xyz" REMOVE TABLE "schema.table1,schema.table2" which will try to remove the table named "schema.table1,schema.table2" as one name rather than 2 separate tables (due to double quotes). The same would apply to ADD TABLE query.

I'm asking, because I've faced a similar issue with removing users from groups and had to quote each name individually before joining. See https://github.com/brainly/terraform-provider-redshift/blob/master/redshift/resource_redshift_group.go#L228-L232

log.Printf("[DEBUG] %s\n", query)
_, err := tx.Exec(query)
return err
}

func removeSchemaFromDatashare(tx *sql.Tx, shareName string, schemaName string) error {
err := resourceRedshiftDatashareRemoveAllFunctions(tx, shareName, schemaName)
if err != nil {
Expand Down Expand Up @@ -293,6 +362,10 @@ func resourceRedshiftDatashareRead(db *DBConnection, d *schema.ResourceData) err
return err
}

if err = readDatashareSchemaTables(tx, shareName, d); err != nil {
return err
}

if err = tx.Commit(); err != nil {
return err
}
Expand Down Expand Up @@ -328,6 +401,34 @@ func readDatashareSchemas(tx *sql.Tx, shareName string, d *schema.ResourceData)
return nil
}

func readDatashareSchemaTables(tx *sql.Tx, shareName string, d *schema.ResourceData) error {
query := `
SELECT
object_name
FROM svv_datashare_objects
WHERE share_type = 'OUTBOUND'
AND object_type = 'table'
AND share_name = $1
`
log.Printf("[DEBUG] %s, $1=%s\n", query, shareName)
rows, err := tx.Query(query, shareName)
if err != nil {
return err
}
defer rows.Close()

schemaTables := schema.NewSet(schema.HashString, nil)
for rows.Next() {
var schemaTableName string
if err = rows.Scan(&schemaTableName); err != nil {
return err
}
schemaTables.Add(schemaTableName)
}
d.Set(dataShareSchemaTablesAttr, schemaTables)
return nil
}

func resourceRedshiftDatashareUpdate(db *DBConnection, d *schema.ResourceData) error {
tx, err := startTransaction(db.client, "")
if err != nil {
Expand All @@ -347,6 +448,10 @@ func resourceRedshiftDatashareUpdate(db *DBConnection, d *schema.ResourceData) e
return err
}

if err := setDatashareTableSchemas(tx, d); err != nil {
return err
}

if err = tx.Commit(); err != nil {
return fmt.Errorf("could not commit transaction: %w", err)
}
Expand Down Expand Up @@ -420,6 +525,48 @@ func setDatashareSchemas(tx *sql.Tx, d *schema.ResourceData) error {
return nil
}

func setDatashareTableSchemas(tx *sql.Tx, d *schema.ResourceData) error {
if !d.HasChange(dataShareSchemaTablesAttr) {
return nil
}
before, after := d.GetChange(dataShareSchemaTablesAttr)
if before == nil {
before = schema.NewSet(schema.HashString, nil)
}
if after == nil {
after = schema.NewSet(schema.HashString, nil)
}

add := after.(*schema.Set).Difference(before.(*schema.Set))
remove := before.(*schema.Set).Difference(after.(*schema.Set))

shareName := d.Get(dataShareSchemaTablesAttr).(string)

var schemas []string
var tables []string
for _, schemaTable := range add.List() {
schemaName := strings.Split(schemaTable.(string), ".")[0]
appendIfTrue(contains(schemas, schemaName), schemaName, &schemas)
tableName := schemaTable.(string)
appendIfTrue(contains(tables, tableName), tableName, &tables)
}
err := addSchemaTablesToDatashare(tx, shareName, schemas, tables)
if err != nil {
return err
}

var tablesToBeRemoved []string
for _, schemaTable := range remove.List() {
tableName := schemaTable.(string)
appendIfTrue(contains(tablesToBeRemoved, tableName), tableName, &tablesToBeRemoved)
}
if err := removeSchemaTablesFromDatashare(tx, shareName, tablesToBeRemoved); err != nil {
return err
}

return nil
}

func resourceRedshiftDatashareDelete(db *DBConnection, d *schema.ResourceData) error {
tx, err := startTransaction(db.client, "")
if err != nil {
Expand Down
12 changes: 10 additions & 2 deletions redshift/resource_redshift_datashare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@ resource "redshift_user" "user" {

resource "redshift_datashare" "basic" {
%[5]s = %[2]q
%[6]s = ["schema1.table1"]
depends_on = [
redshift_user.user,
]
}
`, schemaNameAttr, shareName, schemaCascadeOnDeleteAttr, userNameAttr, dataShareNameAttr)
`, schemaNameAttr, shareName, schemaCascadeOnDeleteAttr, userNameAttr, dataShareNameAttr, dataShareSchemaTablesAttr)

configUpdate := fmt.Sprintf(`
resource "redshift_schema" "schema" {
Expand All @@ -51,8 +52,9 @@ resource "redshift_datashare" "basic" {
%[8]s = [
redshift_schema.schema.%[1]s,
]
%[9]s = ["schema1.table1", "schema1.table2", "schema2.table1", "schema2.table2"]
}
`, schemaNameAttr, shareName, schemaCascadeOnDeleteAttr, userNameAttr, dataShareNameAttr, dataShareOwnerAttr, dataSharePublicAccessibleAttr, dataShareSchemasAttr)
`, schemaNameAttr, shareName, schemaCascadeOnDeleteAttr, userNameAttr, dataShareNameAttr, dataShareOwnerAttr, dataSharePublicAccessibleAttr, dataShareSchemasAttr, dataShareSchemaTablesAttr)
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Expand All @@ -69,6 +71,8 @@ resource "redshift_datashare" "basic" {
resource.TestCheckResourceAttrSet("redshift_datashare.basic", dataShareProducerNamespaceAttr),
resource.TestCheckResourceAttrSet("redshift_datashare.basic", dataShareCreatedAttr),
resource.TestCheckResourceAttr("redshift_datashare.basic", fmt.Sprintf("%s.#", dataShareSchemasAttr), "0"),
resource.TestCheckResourceAttr("redshift_datashare.basic", fmt.Sprintf("%s.#", dataShareSchemaTablesAttr), "1"),
resource.TestCheckTypeSetElemAttr("redshift_datashare.basic", fmt.Sprintf("%s.*", dataShareSchemaTablesAttr), shareName),
),
},
{
Expand All @@ -83,6 +87,8 @@ resource "redshift_datashare" "basic" {
resource.TestCheckResourceAttrSet("redshift_datashare.basic", dataShareCreatedAttr),
resource.TestCheckResourceAttr("redshift_datashare.basic", fmt.Sprintf("%s.#", dataShareSchemasAttr), "1"),
resource.TestCheckTypeSetElemAttr("redshift_datashare.basic", fmt.Sprintf("%s.*", dataShareSchemasAttr), shareName),
resource.TestCheckResourceAttr("redshift_datashare.basic", fmt.Sprintf("%s.#", dataShareSchemaTablesAttr), "4"),
resource.TestCheckTypeSetElemAttr("redshift_datashare.basic", fmt.Sprintf("%s.*", dataShareSchemaTablesAttr), shareName),
),
},
{
Expand All @@ -96,6 +102,8 @@ resource "redshift_datashare" "basic" {
resource.TestCheckResourceAttrSet("redshift_datashare.basic", dataShareProducerNamespaceAttr),
resource.TestCheckResourceAttrSet("redshift_datashare.basic", dataShareCreatedAttr),
resource.TestCheckResourceAttr("redshift_datashare.basic", fmt.Sprintf("%s.#", dataShareSchemasAttr), "0"),
resource.TestCheckResourceAttr("redshift_datashare.basic", fmt.Sprintf("%s.#", dataShareSchemaTablesAttr), "1"),
resource.TestCheckTypeSetElemAttr("redshift_datashare.basic", fmt.Sprintf("%s.*", dataShareSchemaTablesAttr), shareName),
),
},
{
Expand Down