Skip to content
Closed
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"log"
"reflect"
"regexp"
"strings"
"time"

Expand Down Expand Up @@ -695,14 +696,25 @@ func gcsDataSchema() *schema.Resource {
},
"path": {
Optional: true,
Computed: true,
Type: schema.TypeString,
Description: `Google Cloud Storage path in bucket to transfer`,
ValidateFunc: validatePath,
},
},
}
}

func validatePath(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
value = strings.TrimSpace(value)
// checks if path not started with "/"
regex, err := regexp.Compile("^/+")
if err == nil && len(value) > 0 && regex.Match([]byte(value)) {
errors = append(errors, fmt.Errorf("%q cannot start with /", k))
}
return
}

func awsS3DataSchema() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,32 @@ func TestAccStorageTransferJob_hdfsSource(t *testing.T) {
})
}

func TestAccStorageTransferJob_transferUpdateToEmptyString(t *testing.T) {
t.Parallel()

testDataSourceBucketName := acctest.RandString(t, 10)
testDataSinkName := acctest.RandString(t, 10)
testTransferJobDescription := acctest.RandString(t, 10)
testTransferJobName := fmt.Sprintf("tf-test-transfer-job-%s", acctest.RandString(t, 10))

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccStorageTransferJobDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccStorageTransferJob_transferJobGcsPath(envvar.GetTestProjectFromEnv(), testDataSourceBucketName, testDataSinkName, testTransferJobDescription, testTransferJobName, "bar/"),
},
{
Config: testAccStorageTransferJob_transferJobGcsPath(envvar.GetTestProjectFromEnv(), testDataSourceBucketName, testDataSinkName, testTransferJobDescription, testTransferJobName, ""),
},
{
Config: testAccStorageTransferJob_transferJobGcsPath(envvar.GetTestProjectFromEnv(), testDataSourceBucketName, testDataSinkName, testTransferJobDescription, testTransferJobName, "bar/"),
},
},
})
}

func testAccStorageTransferJobDestroyProducer(t *testing.T) func(s *terraform.State) error {
return func(s *terraform.State) error {
config := acctest.GoogleProviderConfig(t)
Expand Down Expand Up @@ -2399,3 +2425,81 @@ resource "google_storage_transfer_job" "transfer_job" {
}
`, project, dataSourceBucketName, project, dataSinkBucketName, project, transferJobDescription, project)
}

func testAccStorageTransferJob_transferJobGcsPath(project string, dataSourceBucketName string, dataSinkBucketName string, transferJobDescription string, testTransferJobName string, gcsPath string) string {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Where is this being used?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

updated test case

return fmt.Sprintf(`
data "google_storage_transfer_project_service_account" "default" {
project = "%s"
}

resource "google_storage_bucket" "data_source" {
name = "%s"
project = "%s"
location = "US"
force_destroy = true
uniform_bucket_level_access = true
}

resource "google_storage_bucket_iam_member" "data_source" {
bucket = google_storage_bucket.data_source.name
role = "roles/storage.admin"
member = "serviceAccount:${data.google_storage_transfer_project_service_account.default.email}"
}

resource "google_storage_bucket" "data_sink" {
name = "%s"
project = "%s"
location = "US"
force_destroy = true
uniform_bucket_level_access = true
}

resource "google_storage_bucket_iam_member" "data_sink" {
bucket = google_storage_bucket.data_sink.name
role = "roles/storage.admin"
member = "serviceAccount:${data.google_storage_transfer_project_service_account.default.email}"
}

resource "google_storage_transfer_job" "transfer_job" {
name = "transferJobs/%s"
description = "%s"
project = "%s"

transfer_spec {
gcs_data_source {
bucket_name = google_storage_bucket.data_source.name
path = "foo/"
}
gcs_data_sink {
bucket_name = google_storage_bucket.data_sink.name
path = "%s"
}
}

schedule {
schedule_start_date {
year = 2018
month = 10
day = 1
}
schedule_end_date {
year = 2019
month = 10
day = 1
}
start_time_of_day {
hours = 0
minutes = 30
seconds = 0
nanos = 0
}
repeat_interval = "604800s"
}

depends_on = [
google_storage_bucket_iam_member.data_source,
google_storage_bucket_iam_member.data_sink,
]
}
`, project, dataSourceBucketName, project, dataSinkBucketName, project, testTransferJobName, transferJobDescription, project, gcsPath)
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ Description of the change and how users should adjust their configuration (if ne

## Resources

## Resource: `google_product_resource`
## Resource: `google_storage_transfer_job`

### Resource-level change example header
### `path` does not allow strings starting with /

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should add full field name here including parent block names otherwise it will mislead users. Only specifying path will be interpreted as top level field within the google_storage_transfer_job resource.

Reference: https://github.com/GoogleCloudPlatform/magic-modules/blob/main/mmv1/third_party/terraform/website/docs/guides/version_6_upgrade.html.markdown#advanced_datapath_observability_configrelay_mode-is-now-removed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

updated description


Description of the change and how users should adjust their configuration (if needed).
`path` does not allow strings starting with /.
Loading