Skip to content

Commit

Permalink
Add point_in_time_recovery_enablement and corresponding output fields…
Browse files Browse the repository at this point in the history
… to firestore_database (#8863) (#15795)

Signed-off-by: Modular Magician <magic-modules@google.com>
  • Loading branch information
modular-magician committed Sep 11, 2023
1 parent 8d40cc7 commit fe4ff6f
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 32 deletions.
3 changes: 3 additions & 0 deletions .changelog/8863.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
firestore: added `point_in_time_recovery_enablement` field to `google_firestore_database` resource
```
67 changes: 67 additions & 0 deletions google/services/firestore/resource_firestore_database.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,29 @@ for information about how to choose. Possible values: ["FIRESTORE_NATIVE", "DATA
ValidateFunc: verify.ValidateEnum([]string{"OPTIMISTIC", "PESSIMISTIC", "OPTIMISTIC_WITH_ENTITY_GROUPS", ""}),
Description: `The concurrency control mode to use for this database. Possible values: ["OPTIMISTIC", "PESSIMISTIC", "OPTIMISTIC_WITH_ENTITY_GROUPS"]`,
},
"point_in_time_recovery_enablement": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: verify.ValidateEnum([]string{"POINT_IN_TIME_RECOVERY_ENABLED", "POINT_IN_TIME_RECOVERY_DISABLED", ""}),
Description: `Whether to enable the PITR feature on this database.
If 'POINT_IN_TIME_RECOVERY_ENABLED' is selected, reads are supported on selected versions of the data from within the past 7 days.
versionRetentionPeriod and earliestVersionTime can be used to determine the supported versions. These include reads against any timestamp within the past hour
and reads against 1-minute snapshots beyond 1 hour and within 7 days.
If 'POINT_IN_TIME_RECOVERY_DISABLED' is selected, reads are supported on any version of the data from within the past 1 hour. Default value: "POINT_IN_TIME_RECOVERY_DISABLED" Possible values: ["POINT_IN_TIME_RECOVERY_ENABLED", "POINT_IN_TIME_RECOVERY_DISABLED"]`,
Default: "POINT_IN_TIME_RECOVERY_DISABLED",
},
"create_time": {
Type: schema.TypeString,
Computed: true,
Description: `The timestamp at which this database was created.`,
},
"earliest_version_time": {
Type: schema.TypeString,
Computed: true,
Description: `Output only. The earliest timestamp at which older versions of the data can be read from the database. See versionRetentionPeriod above; this field is populated with now - versionRetentionPeriod.
This value is continuously updated, and becomes stale the moment it is queried. If you are using this value to recover data, make sure to account for the time from the moment when the value is queried to the moment when you initiate the recovery.
A timestamp in RFC3339 UTC "Zulu" format, with nanosecond resolution and up to nine fractional digits. Examples: "2014-10-02T15:01:23Z" and "2014-10-02T15:01:23.045123456Z".`,
},
"etag": {
Type: schema.TypeString,
Computed: true,
Expand All @@ -108,6 +126,14 @@ up-to-date value before proceeding.`,
This keyPrefix is used, in combination with the project id ("~") to construct the application id
that is returned from the Cloud Datastore APIs in Google App Engine first generation runtimes.
This value may be empty in which case the appid to use for URL-encoded keys is the project_id (eg: foo instead of v~foo).`,
},
"version_retention_period": {
Type: schema.TypeString,
Computed: true,
Description: `Output only. The period during which past versions of data are retained in the database.
Any read or query can specify a readTime within this window, and will read the state of the database at that time.
If the PITR feature is enabled, the retention period is 7 days. Otherwise, the retention period is 1 hour.
A duration in seconds with up to nine fractional digits, ending with 's'. Example: "3.5s".`,
},
"project": {
Type: schema.TypeString,
Expand Down Expand Up @@ -158,6 +184,12 @@ func resourceFirestoreDatabaseCreate(d *schema.ResourceData, meta interface{}) e
} else if v, ok := d.GetOkExists("app_engine_integration_mode"); !tpgresource.IsEmptyValue(reflect.ValueOf(appEngineIntegrationModeProp)) && (ok || !reflect.DeepEqual(v, appEngineIntegrationModeProp)) {
obj["appEngineIntegrationMode"] = appEngineIntegrationModeProp
}
pointInTimeRecoveryEnablementProp, err := expandFirestoreDatabasePointInTimeRecoveryEnablement(d.Get("point_in_time_recovery_enablement"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("point_in_time_recovery_enablement"); !tpgresource.IsEmptyValue(reflect.ValueOf(pointInTimeRecoveryEnablementProp)) && (ok || !reflect.DeepEqual(v, pointInTimeRecoveryEnablementProp)) {
obj["pointInTimeRecoveryEnablement"] = pointInTimeRecoveryEnablementProp
}
etagProp, err := expandFirestoreDatabaseEtag(d.Get("etag"), d, config)
if err != nil {
return err
Expand Down Expand Up @@ -288,6 +320,9 @@ func resourceFirestoreDatabaseRead(d *schema.ResourceData, meta interface{}) err
if err := d.Set("app_engine_integration_mode", flattenFirestoreDatabaseAppEngineIntegrationMode(res["appEngineIntegrationMode"], d, config)); err != nil {
return fmt.Errorf("Error reading Database: %s", err)
}
if err := d.Set("point_in_time_recovery_enablement", flattenFirestoreDatabasePointInTimeRecoveryEnablement(res["pointInTimeRecoveryEnablement"], d, config)); err != nil {
return fmt.Errorf("Error reading Database: %s", err)
}
if err := d.Set("key_prefix", flattenFirestoreDatabaseKeyPrefix(res["key_prefix"], d, config)); err != nil {
return fmt.Errorf("Error reading Database: %s", err)
}
Expand All @@ -297,6 +332,12 @@ func resourceFirestoreDatabaseRead(d *schema.ResourceData, meta interface{}) err
if err := d.Set("create_time", flattenFirestoreDatabaseCreateTime(res["create_time"], d, config)); err != nil {
return fmt.Errorf("Error reading Database: %s", err)
}
if err := d.Set("version_retention_period", flattenFirestoreDatabaseVersionRetentionPeriod(res["versionRetentionPeriod"], d, config)); err != nil {
return fmt.Errorf("Error reading Database: %s", err)
}
if err := d.Set("earliest_version_time", flattenFirestoreDatabaseEarliestVersionTime(res["earliestVersionTime"], d, config)); err != nil {
return fmt.Errorf("Error reading Database: %s", err)
}

return nil
}
Expand Down Expand Up @@ -335,6 +376,12 @@ func resourceFirestoreDatabaseUpdate(d *schema.ResourceData, meta interface{}) e
} else if v, ok := d.GetOkExists("app_engine_integration_mode"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, appEngineIntegrationModeProp)) {
obj["appEngineIntegrationMode"] = appEngineIntegrationModeProp
}
pointInTimeRecoveryEnablementProp, err := expandFirestoreDatabasePointInTimeRecoveryEnablement(d.Get("point_in_time_recovery_enablement"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("point_in_time_recovery_enablement"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, pointInTimeRecoveryEnablementProp)) {
obj["pointInTimeRecoveryEnablement"] = pointInTimeRecoveryEnablementProp
}
etagProp, err := expandFirestoreDatabaseEtag(d.Get("etag"), d, config)
if err != nil {
return err
Expand Down Expand Up @@ -362,6 +409,10 @@ func resourceFirestoreDatabaseUpdate(d *schema.ResourceData, meta interface{}) e
updateMask = append(updateMask, "appEngineIntegrationMode")
}

if d.HasChange("point_in_time_recovery_enablement") {
updateMask = append(updateMask, "pointInTimeRecoveryEnablement")
}

if d.HasChange("etag") {
updateMask = append(updateMask, "etag")
}
Expand Down Expand Up @@ -456,6 +507,10 @@ func flattenFirestoreDatabaseAppEngineIntegrationMode(v interface{}, d *schema.R
return v
}

func flattenFirestoreDatabasePointInTimeRecoveryEnablement(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
return v
}

func flattenFirestoreDatabaseKeyPrefix(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
return v
}
Expand All @@ -468,6 +523,14 @@ func flattenFirestoreDatabaseCreateTime(v interface{}, d *schema.ResourceData, c
return v
}

func flattenFirestoreDatabaseVersionRetentionPeriod(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
return v
}

func flattenFirestoreDatabaseEarliestVersionTime(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
return v
}

func expandFirestoreDatabaseName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return tpgresource.ReplaceVars(d, config, "projects/{{project}}/databases/{{name}}")
}
Expand All @@ -488,6 +551,10 @@ func expandFirestoreDatabaseAppEngineIntegrationMode(v interface{}, d tpgresourc
return v, nil
}

func expandFirestoreDatabasePointInTimeRecoveryEnablement(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}

func expandFirestoreDatabaseEtag(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,13 @@ resource "google_project_service" "firestore" {
}
resource "google_firestore_database" "database" {
project = google_project.project.project_id
name = "my-database"
location_id = "nam5"
type = "FIRESTORE_NATIVE"
concurrency_mode = "OPTIMISTIC"
app_engine_integration_mode = "DISABLED"
project = google_project.project.project_id
name = "my-database"
location_id = "nam5"
type = "FIRESTORE_NATIVE"
concurrency_mode = "OPTIMISTIC"
app_engine_integration_mode = "DISABLED"
point_in_time_recovery_enablement = "POINT_IN_TIME_RECOVERY_ENABLED"
depends_on = [google_project_service.firestore]
}
Expand Down Expand Up @@ -268,12 +269,13 @@ resource "google_project_service" "firestore" {
}
resource "google_firestore_database" "database" {
project = google_project.project.project_id
name = "datastore-mode-database"
location_id = "nam5"
type = "DATASTORE_MODE"
concurrency_mode = "OPTIMISTIC"
app_engine_integration_mode = "DISABLED"
project = google_project.project.project_id
name = "datastore-mode-database"
location_id = "nam5"
type = "DATASTORE_MODE"
concurrency_mode = "OPTIMISTIC"
app_engine_integration_mode = "DISABLED"
point_in_time_recovery_enablement = "POINT_IN_TIME_RECOVERY_ENABLED"
depends_on = [google_project_service.firestore]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccFirestoreDatabase_update(t *testing.T) {
func TestAccFirestoreDatabase_updateConcurrencyMode(t *testing.T) {
t.Parallel()

orgId := envvar.GetTestOrgFromEnv(t)
billingAccount := envvar.GetTestBillingAccountFromEnv(t)
randomSuffix := acctest.RandString(t, 10)

acctest.VcrTest(t, resource.TestCase{
Expand All @@ -25,7 +26,7 @@ func TestAccFirestoreDatabase_update(t *testing.T) {
},
Steps: []resource.TestStep{
{
Config: testAccFirestoreDatabase_concurrencyMode(orgId, randomSuffix, "OPTIMISTIC"),
Config: testAccFirestoreDatabase_concurrencyMode(orgId, billingAccount, randomSuffix, "OPTIMISTIC"),
},
{
ResourceName: "google_firestore_database.default",
Expand All @@ -34,7 +35,7 @@ func TestAccFirestoreDatabase_update(t *testing.T) {
ImportStateVerifyIgnore: []string{"etag", "project"},
},
{
Config: testAccFirestoreDatabase_concurrencyMode(orgId, randomSuffix, "PESSIMISTIC"),
Config: testAccFirestoreDatabase_concurrencyMode(orgId, billingAccount, randomSuffix, "PESSIMISTIC"),
},
{
ResourceName: "google_firestore_database.default",
Expand All @@ -46,12 +47,49 @@ func TestAccFirestoreDatabase_update(t *testing.T) {
})
}

func testAccFirestoreDatabase_concurrencyMode(orgId string, randomSuffix string, concurrencyMode string) string {
func TestAccFirestoreDatabase_updatePitrEnablement(t *testing.T) {
t.Parallel()

orgId := envvar.GetTestOrgFromEnv(t)
billingAccount := envvar.GetTestBillingAccountFromEnv(t)
randomSuffix := acctest.RandString(t, 10)

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
ExternalProviders: map[string]resource.ExternalProvider{
"time": {},
},
Steps: []resource.TestStep{
{
Config: testAccFirestoreDatabase_pitrEnablement(orgId, billingAccount, randomSuffix, "POINT_IN_TIME_RECOVERY_ENABLED"),
},
{
ResourceName: "google_firestore_database.default",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"etag", "project"},
},
{
Config: testAccFirestoreDatabase_pitrEnablement(orgId, billingAccount, randomSuffix, "POINT_IN_TIME_RECOVERY_DISABLED"),
},
{
ResourceName: "google_firestore_database.default",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"etag", "project"},
},
},
})
}

func testAccFirestoreDatabase_basicDependencies(orgId, billingAccount string, randomSuffix string) string {
return fmt.Sprintf(`
resource "google_project" "default" {
project_id = "tf-test%s"
name = "tf-test%s"
org_id = "%s"
project_id = "tf-test%s"
name = "tf-test%s"
org_id = "%s"
billing_account = "%s"
}
resource "time_sleep" "wait_60_seconds" {
Expand All @@ -67,6 +105,11 @@ resource "google_project_service" "firestore" {
# Needed for CI tests for permissions to propagate, should not be needed for actual usage
depends_on = [time_sleep.wait_60_seconds]
}
`, randomSuffix, randomSuffix, orgId, billingAccount)
}

func testAccFirestoreDatabase_concurrencyMode(orgId, billingAccount string, randomSuffix string, concurrencyMode string) string {
return testAccFirestoreDatabase_basicDependencies(orgId, billingAccount, randomSuffix) + fmt.Sprintf(`
resource "google_firestore_database" "default" {
name = "(default)"
Expand All @@ -78,5 +121,21 @@ resource "google_firestore_database" "default" {
depends_on = [google_project_service.firestore]
}
`, randomSuffix, randomSuffix, orgId, concurrencyMode)
`, concurrencyMode)
}

func testAccFirestoreDatabase_pitrEnablement(orgId, billingAccount string, randomSuffix string, pointInTimeRecoveryEnablement string) string {
return testAccFirestoreDatabase_basicDependencies(orgId, billingAccount, randomSuffix) + fmt.Sprintf(`
resource "google_firestore_database" "default" {
name = "(default)"
type = "DATASTORE_MODE"
location_id = "nam5"
point_in_time_recovery_enablement = "%s"
project = google_project.default.project_id
depends_on = [google_project_service.firestore]
}
`, pointInTimeRecoveryEnablement)
}
47 changes: 35 additions & 12 deletions website/docs/r/firestore_database.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,13 @@ resource "google_project_service" "firestore" {
}
resource "google_firestore_database" "database" {
project = google_project.project.project_id
name = "my-database"
location_id = "nam5"
type = "FIRESTORE_NATIVE"
concurrency_mode = "OPTIMISTIC"
app_engine_integration_mode = "DISABLED"
project = google_project.project.project_id
name = "my-database"
location_id = "nam5"
type = "FIRESTORE_NATIVE"
concurrency_mode = "OPTIMISTIC"
app_engine_integration_mode = "DISABLED"
point_in_time_recovery_enablement = "POINT_IN_TIME_RECOVERY_ENABLED"
depends_on = [google_project_service.firestore]
}
Expand Down Expand Up @@ -158,12 +159,13 @@ resource "google_project_service" "firestore" {
}
resource "google_firestore_database" "database" {
project = google_project.project.project_id
name = "datastore-mode-database"
location_id = "nam5"
type = "DATASTORE_MODE"
concurrency_mode = "OPTIMISTIC"
app_engine_integration_mode = "DISABLED"
project = google_project.project.project_id
name = "datastore-mode-database"
location_id = "nam5"
type = "DATASTORE_MODE"
concurrency_mode = "OPTIMISTIC"
app_engine_integration_mode = "DISABLED"
point_in_time_recovery_enablement = "POINT_IN_TIME_RECOVERY_ENABLED"
depends_on = [google_project_service.firestore]
}
Expand Down Expand Up @@ -209,6 +211,16 @@ The following arguments are supported:
The App Engine integration mode to use for this database.
Possible values are: `ENABLED`, `DISABLED`.

* `point_in_time_recovery_enablement` -
(Optional)
Whether to enable the PITR feature on this database.
If `POINT_IN_TIME_RECOVERY_ENABLED` is selected, reads are supported on selected versions of the data from within the past 7 days.
versionRetentionPeriod and earliestVersionTime can be used to determine the supported versions. These include reads against any timestamp within the past hour
and reads against 1-minute snapshots beyond 1 hour and within 7 days.
If `POINT_IN_TIME_RECOVERY_DISABLED` is selected, reads are supported on any version of the data from within the past 1 hour.
Default value is `POINT_IN_TIME_RECOVERY_DISABLED`.
Possible values are: `POINT_IN_TIME_RECOVERY_ENABLED`, `POINT_IN_TIME_RECOVERY_DISABLED`.

* `project` - (Optional) The ID of the project in which the resource belongs.
If it is not provided, the provider project is used.

Expand All @@ -233,6 +245,17 @@ In addition to the arguments listed above, the following computed attributes are
* `create_time` -
The timestamp at which this database was created.

* `version_retention_period` -
Output only. The period during which past versions of data are retained in the database.
Any read or query can specify a readTime within this window, and will read the state of the database at that time.
If the PITR feature is enabled, the retention period is 7 days. Otherwise, the retention period is 1 hour.
A duration in seconds with up to nine fractional digits, ending with 's'. Example: "3.5s".

* `earliest_version_time` -
Output only. The earliest timestamp at which older versions of the data can be read from the database. See versionRetentionPeriod above; this field is populated with now - versionRetentionPeriod.
This value is continuously updated, and becomes stale the moment it is queried. If you are using this value to recover data, make sure to account for the time from the moment when the value is queried to the moment when you initiate the recovery.
A timestamp in RFC3339 UTC "Zulu" format, with nanosecond resolution and up to nine fractional digits. Examples: "2014-10-02T15:01:23Z" and "2014-10-02T15:01:23.045123456Z".


## Timeouts

Expand Down

0 comments on commit fe4ff6f

Please sign in to comment.