Skip to content
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.8.6
3.8.7
1 change: 1 addition & 0 deletions docs/data-sources/instance.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ The instance data source.
- `id` (String) The ID of this resource.
- `maximum_connections` (Number) The maximum number of connections. The default value is 10.
- `name` (String) The instance full name in instances/{resource id} format.
- `sync_databases` (Set of String) Enable sync for following databases. Default empty, means sync all schemas & databases.
- `sync_interval` (Number) How often the instance is synced in seconds. Default 0, means never sync.
- `title` (String) The instance title.

Expand Down
1 change: 1 addition & 0 deletions docs/data-sources/instance_list.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Read-Only:
- `maximum_connections` (Number)
- `name` (String)
- `resource_id` (String)
- `sync_databases` (Set of String)
- `sync_interval` (Number)
- `title` (String)

Expand Down
1 change: 1 addition & 0 deletions docs/resources/instance.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ The instance resource.
- `engine_version` (String) The engine version.
- `id` (String) The ID of this resource.
- `name` (String) The instance full name in instances/{resource id} format.
- `sync_databases` (Set of String) Enable sync for following databases. Default empty, means sync all schemas & databases.

<a id="nestedblock--data_sources"></a>
### Nested Schema for `data_sources`
Expand Down
56 changes: 56 additions & 0 deletions examples/setup/instance.tf
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,59 @@ resource "bytebase_instance" "prod" {
port = "54321"
}
}

# Instance with external_secret
#
# Option 1, use external_secret (recommend)
# resource "bytebase_instance" "aws" {
# resource_id = "instance-with-aws"
# environment = "environments/test"
# title = "Instance with AWS"
# engine = "POSTGRES"
# activation = true # activation is required to be true to enable the external_secret feature

# data_sources {
# id = "admin data source"
# type = "ADMIN"
# host = "127.0.0.1"
# port = "54321"
# username = "bytebase"
# external_secret {
# aws_secrets_manager {
# secret_name = "bytebase-external-secret"
# password_key_name = "db_password"
# }
# }
# }
# }
#
# Option 2, work with aws provider
# terraform {
# required_providers {
# aws = {
# source = "hashicorp/aws"
# version = "6.4.0"
# }
# }
# }
#
# Retrieve the secret
# data "aws_secretsmanager_secret_version" "bytebase" {
# secret_id = "bytebase-external-secret"
# }

# resource "bytebase_instance" "aws" {
# resource_id = "instance-with-aws"
# environment = "environments/test"
# title = "Instance with AWS"
# engine = "POSTGRES"

# data_sources {
# id = "admin data source"
# type = "ADMIN"
# host = "127.0.0.1"
# port = "54321"
# username = "bytebase"
# password = jsondecode(data.aws_secretsmanager_secret_version.bytebase.secret_string)["db_password"] # db_password is the secret key name in AWS Secret Manager
# }
# }
2 changes: 1 addition & 1 deletion examples/setup/main.tf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
terraform {
required_providers {
bytebase = {
version = "3.8.2"
version = "3.8.7"
# For local development, please use "terraform.local/bytebase/bytebase" instead
source = "registry.terraform.io/bytebase/bytebase"
}
Expand Down
3 changes: 2 additions & 1 deletion provider/data_source_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@ func dataSourceInstance() *schema.Resource {
Default: false,
Description: "List all databases in this instance. If false, will only list 500 databases.",
},
"databases": getDatabasesSchema(true),
"databases": getDatabasesSchema(true),
"sync_databases": getSyncDatabasesSchema(true),
},
}
}
Expand Down
6 changes: 5 additions & 1 deletion provider/data_source_instance_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ func dataSourceInstanceList() *schema.Resource {
Computed: true,
Description: "The maximum number of connections. The default value is 10.",
},
"sync_databases": getSyncDatabasesSchema(true),
"data_sources": {
Type: schema.TypeSet,
Computed: true,
Expand Down Expand Up @@ -251,8 +252,11 @@ func dataSourceInstanceListRead(ctx context.Context, d *schema.ResourceData, m i
ins["engine_version"] = instance.EngineVersion
ins["external_link"] = instance.ExternalLink
ins["environment"] = instance.Environment
ins["sync_interval"] = instance.GetSyncInterval().GetSeconds()
if v := instance.GetSyncInterval(); v != nil {
ins["sync_interval"] = v.GetSeconds()
}
ins["maximum_connections"] = instance.GetMaximumConnections()
ins["sync_databases"] = instance.SyncDatabases

dataSources, err := flattenDataSourceList(d, instance.DataSources)
if err != nil {
Expand Down
33 changes: 1 addition & 32 deletions provider/data_source_policy.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package provider

import (
"bytes"
"context"
"fmt"
"strings"
Expand Down Expand Up @@ -143,7 +142,6 @@ func getMaskingExceptionPolicySchema(computed bool) *schema.Schema {
},
},
},
Set: exceptionHash,
},
},
},
Expand Down Expand Up @@ -485,36 +483,7 @@ func flattenMaskingExceptionPolicy(p *v1pb.MaskingExceptionPolicy) ([]interface{
exceptionList = append(exceptionList, raw)
}
policy := map[string]interface{}{
"exceptions": schema.NewSet(exceptionHash, exceptionList),
"exceptions": exceptionList,
}
return []interface{}{policy}, nil
}

func exceptionHash(rawException interface{}) int {
var buf bytes.Buffer
exception := rawException.(map[string]interface{})

if v, ok := exception["database"].(string); ok {
_, _ = buf.WriteString(fmt.Sprintf("%s-", v))
}
if v, ok := exception["schema"].(string); ok {
_, _ = buf.WriteString(fmt.Sprintf("%s-", v))
}
if v, ok := exception["table"].(string); ok {
_, _ = buf.WriteString(fmt.Sprintf("%s-", v))
}
if v, ok := exception["column"].(string); ok {
_, _ = buf.WriteString(fmt.Sprintf("%s-", v))
}
if v, ok := exception["member"].(string); ok {
_, _ = buf.WriteString(fmt.Sprintf("%s-", v))
}
if v, ok := exception["action"].(string); ok {
_, _ = buf.WriteString(fmt.Sprintf("%s-", v))
}
if v, ok := exception["expire_timestamp"].(string); ok {
_, _ = buf.WriteString(fmt.Sprintf("%s-", v))
}

return internal.ToHashcodeInt(buf.String())
}
5 changes: 4 additions & 1 deletion provider/internal/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ func ResourceDelete(ctx context.Context, d *schema.ResourceData, m interface{})
var diags diag.Diagnostics

if err := c.DeleteResource(ctx, fullName); err != nil {
return diag.FromErr(err)
// Check if the resource was deleted outside of Terraform
if !isNotFoundError(err) {
return diag.FromErr(err)
}
}

d.SetId("")
Expand Down
53 changes: 46 additions & 7 deletions provider/resource_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ import (

func resourceInstance() *schema.Resource {
return &schema.Resource{
Description: "The instance resource.",
CreateContext: resourceInstanceCreate,
ReadWithoutTimeout: internal.ResourceRead(resourceInstanceRead),
UpdateContext: resourceInstanceUpdate,
DeleteContext: internal.ResourceDelete,
Description: "The instance resource.",
CreateWithoutTimeout: resourceInstanceCreate,
ReadWithoutTimeout: internal.ResourceRead(resourceInstanceRead),
UpdateContext: resourceInstanceUpdate,
DeleteContext: internal.ResourceDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Expand Down Expand Up @@ -90,6 +90,7 @@ func resourceInstance() *schema.Resource {
Computed: true,
Description: "The maximum number of connections.",
},
"sync_databases": getSyncDatabasesSchema(true),
"data_sources": {
Type: schema.TypeSet,
Required: true,
Expand Down Expand Up @@ -300,6 +301,18 @@ func resourceInstance() *schema.Resource {
}
}

func getSyncDatabasesSchema(computed bool) *schema.Schema {
return &schema.Schema{
Type: schema.TypeSet,
Computed: computed,
Optional: !computed,
Description: "Enable sync for following databases. Default empty, means sync all schemas & databases.",
Elem: &schema.Schema{
Type: schema.TypeString,
},
}
}

// suppressSensitiveFieldDiff suppresses diffs for write-only sensitive fields.
func suppressSensitiveFieldDiff(_, oldValue, newValue string, _ *schema.ResourceData) bool {
// If the field was previously set (exists in state) and the new value is empty,
Expand Down Expand Up @@ -336,6 +349,7 @@ func resourceInstanceCreate(ctx context.Context, d *schema.ResourceData, m inter
State: v1pb.State_ACTIVE,
MaximumConnections: int32(d.Get("maximum_connections").(int)),
Engine: v1pb.Engine(v1pb.Engine_value[d.Get("engine").(string)]),
SyncDatabases: getSyncDatabases(d),
}
rawConfig := d.GetRawConfig()
if config := rawConfig.GetAttr("sync_interval"); !config.IsNull() {
Expand Down Expand Up @@ -396,6 +410,9 @@ func resourceInstanceCreate(ctx context.Context, d *schema.ResourceData, m inter
if config := rawConfig.GetAttr("maximum_connections"); !config.IsNull() && instance.MaximumConnections != existedInstance.GetMaximumConnections() {
updateMasks = append(updateMasks, "maximum_connections")
}
if config := rawConfig.GetAttr("sync_databases"); !config.IsNull() {
updateMasks = append(updateMasks, "sync_databases")
}
if len(dataSourceList) > 0 {
updateMasks = append(updateMasks, "data_sources")
}
Expand Down Expand Up @@ -461,6 +478,18 @@ func resourceInstanceRead(ctx context.Context, d *schema.ResourceData, m interfa
return resp
}

func getSyncDatabases(d *schema.ResourceData) []string {
rawSet, ok := d.Get("sync_databases").(*schema.Set)
if !ok {
return nil
}
dbList := []string{}
for _, raw := range rawSet.List() {
dbList = append(dbList, raw.(string))
}
return dbList
}

func resourceInstanceUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
if d.HasChange("resource_id") {
return diag.Errorf("cannot change the resource id")
Expand Down Expand Up @@ -518,6 +547,9 @@ func resourceInstanceUpdate(ctx context.Context, d *schema.ResourceData, m inter
if d.HasChange("sync_interval") {
paths = append(paths, "sync_interval")
}
if d.HasChange("sync_databases") {
paths = append(paths, "sync_databases")
}
if d.HasChange("maximum_connections") {
paths = append(paths, "maximum_connections")
}
Expand All @@ -535,6 +567,7 @@ func resourceInstanceUpdate(ctx context.Context, d *schema.ResourceData, m inter
Seconds: int64(d.Get("sync_interval").(int)),
},
MaximumConnections: int32(d.Get("maximum_connections").(int)),
SyncDatabases: getSyncDatabases(d),
}, paths); err != nil {
return diag.FromErr(err)
}
Expand Down Expand Up @@ -593,8 +626,10 @@ func setInstanceMessage(
if err := d.Set("external_link", instance.ExternalLink); err != nil {
return diag.Errorf("cannot set external_link for instance: %s", err.Error())
}
if err := d.Set("sync_interval", instance.GetSyncInterval().GetSeconds()); err != nil {
return diag.Errorf("cannot set sync_interval for instance: %s", err.Error())
if v := instance.GetSyncInterval(); v != nil {
if err := d.Set("sync_interval", v.GetSeconds()); err != nil {
return diag.Errorf("cannot set sync_interval for instance: %s", err.Error())
}
}
if err := d.Set("maximum_connections", instance.GetMaximumConnections()); err != nil {
return diag.Errorf("cannot set maximum_connections for instance: %s", err.Error())
Expand Down Expand Up @@ -622,6 +657,10 @@ func setInstanceMessage(
return diag.Errorf("cannot set databases for instance: %s", err.Error())
}

if err := d.Set("sync_databases", instance.SyncDatabases); err != nil {
return diag.Errorf("cannot set sync_databases for instance: %s", err.Error())
}

return nil
}

Expand Down
2 changes: 1 addition & 1 deletion provider/resource_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ func convertToMaskingExceptionPolicy(d *schema.ResourceData) (*v1pb.MaskingExcep
return nil, err
}
policy.MaskingExceptions = append(policy.MaskingExceptions, &v1pb.MaskingExceptionPolicy_MaskingException{
Member: rawException["member"].(string),
Member: member,
Action: v1pb.MaskingExceptionPolicy_MaskingException_Action(
v1pb.MaskingExceptionPolicy_MaskingException_Action_value[rawException["action"].(string)],
),
Expand Down