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

Datastream Stream resource #6806

Merged
merged 42 commits into from
Jan 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
9cb02e9
Added initial datastream Stream structure
melinath Sep 19, 2022
940de80
Added basic test for datastream
melinath Sep 28, 2022
848267b
Removed oracle support since it's hard to test
melinath Nov 9, 2022
0e83051
Added some additional fields that could not previously be handled
melinath Nov 9, 2022
434463e
Added full example for datastream stream with MySQL -> GCS
melinath Nov 9, 2022
05729da
Removed additional references to oracle on datastream stream
melinath Nov 10, 2022
14b7832
cleaned up api.yaml and terraform.yaml
melinath Nov 10, 2022
8e5b56d
Revert "Removed additional references to oracle on datastream stream"
melinath Nov 10, 2022
d6579e3
Revert "Removed oracle support since it's hard to test"
melinath Nov 10, 2022
a8881fe
Added oracleSourceConfig.dropLargeObjects and oracleSourceConfig.stre…
melinath Nov 10, 2022
b0601ed
Require a value inside jsonFileFormat
melinath Nov 10, 2022
24df2f0
Require a value inside bigqueryDestinationConfig
melinath Nov 10, 2022
3f2f3a9
Corrected type of fileRotationMb
melinath Nov 14, 2022
8ee1290
Added state field and added default_from_api to a few fields
melinath Nov 14, 2022
165e985
Fixed typo
melinath Nov 14, 2022
aae92f7
Corrected name of default_value property
melinath Nov 14, 2022
cd31c21
Removed support for oracle and postgres from Datastream Stream
melinath Nov 22, 2022
bb6a18f
Converted status field to be computed
melinath Nov 22, 2022
6d760d6
Added desired_state field
melinath Nov 22, 2022
f11c78b
removed input: true from stream
melinath Nov 22, 2022
a8fb622
ignore read on stream connection profiles
melinath Nov 29, 2022
c3ede7f
Added update test
melinath Nov 29, 2022
67be969
Added unit test for customizediff
melinath Nov 29, 2022
b7a5f79
Added unit tests for customize diff
melinath Nov 29, 2022
c05993e
Added external providers
melinath Nov 29, 2022
c5ca4e5
gofmt
melinath Nov 29, 2022
722c17e
Corrected deletion_protection usage
melinath Nov 29, 2022
bd636d5
Updates due to code review
melinath Nov 30, 2022
10695bb
Cleaned up test issues
melinath Nov 30, 2022
5a54936
Corrections to tests
melinath Nov 30, 2022
3757749
Fixed test issues
melinath Dec 1, 2022
583406c
Added gcs file format to fix tests
melinath Dec 1, 2022
b81ca7d
Added projectNumberDiffSuppress
melinath Dec 8, 2022
d65050f
Added default_from_api for fileRotationMb
melinath Dec 9, 2022
d1b99a8
Corrected labels usage in tests
melinath Dec 9, 2022
d471ab6
Corrected stream update verb
melinath Dec 9, 2022
43a05bb
Cleaned up references to google_storage_bucket.bucket.name
melinath Dec 9, 2022
374fc58
Source / destination profile are not updatable
melinath Dec 12, 2022
8867dd8
Marked column length fields as output-only
melinath Dec 20, 2022
95e9175
Made update test use checks instead of importstateverify for state / …
melinath Dec 20, 2022
75ae319
gofmt
melinath Dec 20, 2022
b2719d8
Add state to update mask if desired_state is changed
melinath Jan 4, 2023
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
433 changes: 429 additions & 4 deletions mmv1/products/datastream/api.yaml

Large diffs are not rendered by default.

66 changes: 66 additions & 0 deletions mmv1/products/datastream/terraform.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,72 @@ overrides: !ruby/object:Overrides::ResourceOverrides
constants: templates/terraform/constants/private_connection.go.erb
post_create: templates/terraform/post_create/private_connection.go.erb
post_import: templates/terraform/post_import/private_connection.go.erb
Stream: !ruby/object:Overrides::Terraform::ResourceOverride
id_format: projects/{{project}}/locations/{{location}}/streams/{{stream_id}}
import_format: ["projects/{{project}}/locations/{{location}}/streams/{{stream_id}}"]
properties:
destinationConfig.gcsDestinationConfig.fileRotationMb: !ruby/object:Overrides::Terraform::PropertyOverride
default_from_api: true
destinationConfig.gcsDestinationConfig.fileRotationInterval: !ruby/object:Overrides::Terraform::PropertyOverride
default_from_api: true
destinationConfig.destinationConnectionProfile: !ruby/object:Overrides::Terraform::PropertyOverride
melinath marked this conversation as resolved.
Show resolved Hide resolved
diff_suppress_func: "projectNumberDiffSuppress"
sourceConfig.sourceConnectionProfile: !ruby/object:Overrides::Terraform::PropertyOverride
diff_suppress_func: "projectNumberDiffSuppress"
sourceConfig.mysqlSourceConfig.maxConcurrentCdcTasks: !ruby/object:Overrides::Terraform::PropertyOverride
default_from_api: true
validation: !ruby/object:Provider::Terraform::Validation
function: 'validation.IntAtLeast(0)'
virtual_fields:
- !ruby/object:Api::Type::Enum
name: 'desired_state'
melinath marked this conversation as resolved.
Show resolved Hide resolved
description: |
Desired state of the Stream. Set this field to `RUNNING` to start the stream, and `PAUSED` to pause the stream.
values:
- :NOT_STARTED
- :RUNNING
- :PAUSED
default_value: :NOT_STARTED
custom_code: !ruby/object:Provider::Terraform::CustomCode
constants: 'templates/terraform/constants/datastream_stream.go.erb'
post_create: 'templates/terraform/post_create/datastream_stream.go.erb'
post_import: 'templates/terraform/post_import/datastream_stream.go.erb'
pre_update: 'templates/terraform/pre_update/datastream_stream.go.erb'
post_update: 'templates/terraform/post_update/datastream_stream.go.erb'
encoder: 'templates/terraform/encoders/datastream_stream.go.erb'
resource_definition: 'templates/terraform/resource_definition/datastream_stream.go.erb'
examples:
- !ruby/object:Provider::Terraform::Examples
name: "datastream_stream_basic"
pull_external: true
primary_resource_id: "default"
skip_docs: true
vars:
stream_id: "my-stream"
private_connection_id: "my-connection"
network_name: "my-network"
source_connection_profile_id: "source-profile"
database_instance_name: "my-instance"
deletion_protection: "true"
bucket_name: "my-bucket"
destination_connection_profile_id: "destination-profile"
test_vars_overrides:
deletion_protection: "false"
- !ruby/object:Provider::Terraform::Examples
name: "datastream_stream_full"
pull_external: true
primary_resource_id: "default"
vars:
stream_id: "my-stream"
private_connection_id: "my-connection"
network_name: "my-network"
source_connection_profile_id: "source-profile"
database_instance_name: "my-instance"
deletion_protection: "true"
bucket_name: "my-bucket"
destination_connection_profile_id: "destination-profile"
test_vars_overrides:
deletion_protection: "false"

# This is for copying files over
files: !ruby/object:Provider::Config::Files
Expand Down
66 changes: 66 additions & 0 deletions mmv1/templates/terraform/constants/datastream_stream.go.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<%- # the license inside this block applies to this file
# Copyright 2022 Google Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-%>
func resourceDatastreamStreamCustomDiffFunc(diff TerraformResourceDiff) error {
if diff.HasChange("desired_state") {
old, new := diff.GetChange("desired_state")
oldState := old.(string)
newState := new.(string)

if isNewResource(diff) {
if newState != "NOT_STARTED" && newState != "RUNNING" {
return fmt.Errorf("`desired_state` can only be set to `NOT_STARTED` or `RUNNING` when creating a new Stream")
}
} else {
if newState == "NOT_STARTED" && oldState != newState {
return fmt.Errorf("Field `desired_state` cannot be changed to `NOT_STARTED` for an already-created Stream")
}

if oldState == "NOT_STARTED" && newState != "RUNNING" {
return fmt.Errorf("Field `desired_state` can only be set to `RUNNING` from `NOT_STARTED`")
}

if newState != "RUNNING" && newState != "PAUSED" {
return fmt.Errorf("`desired_state` can only be set to `RUNNING` or `PAUSED` when updating a Stream")
}
}
}
return nil
}
func resourceDatastreamStreamCustomDiff(_ context.Context, diff *schema.ResourceDiff, meta interface{}) error {
// separate func to allow unit testing
return resourceDatastreamStreamCustomDiffFunc(diff)
}

<% unless compiler == "terraformvalidator-codegen" -%>
// waitForDatastreamStreamReady waits for an agent pool to reach a stable state to indicate that it's ready.
func waitForDatastreamStreamReady(d *schema.ResourceData, config *Config, timeout time.Duration) error {
return resource.Retry(timeout, func() *resource.RetryError {
if err := resourceDatastreamStreamRead(d, config); err != nil {
return resource.NonRetryableError(err)
}

name := d.Get("name").(string)
state := d.Get("state").(string)
if state == "STARTING" || state == "DRAINING" {
return resource.RetryableError(fmt.Errorf("Stream %q has state %q.", name, state))
} else if state == "NOT_STARTED" || state == "RUNNING" || state == "PAUSED" {
log.Printf("[DEBUG] Stream %q has state %q.", name, state)
return nil
} else {
return resource.NonRetryableError(fmt.Errorf("Stream %q has state %q.", name, state))
}
})
}
<% end -%>
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func resourcePrivateCaCACustomDiff(_ context.Context, diff *schema.ResourceDiff,
return nil
}

func isNewResource(diff *schema.ResourceDiff) bool {
func isNewResource(diff TerraformResourceDiff) bool {
name := diff.Get("name")
return name.(string) == ""
}
18 changes: 18 additions & 0 deletions mmv1/templates/terraform/encoders/datastream_stream.go.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<%- # the license inside this block applies to this file
# Copyright 2022 Google Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-%>
if d.HasChange("desired_state") {
obj["state"] = d.Get("desired_state")
}
return obj, nil
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ resource "google_datastream_connection_profile" "<%= ctx[:primary_resource_id] %
port = 8022
password = "swordfish"
}
labels = {
key = "value"
}
}
124 changes: 124 additions & 0 deletions mmv1/templates/terraform/examples/datastream_stream_basic.tf.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
data "google_project" "project" {
}

resource "google_sql_database_instance" "instance" {
name = "<%= ctx[:vars]['database_instance_name'] %>"
database_version = "MYSQL_8_0"
region = "us-central1"
settings {
tier = "db-f1-micro"
backup_configuration {
enabled = true
binary_log_enabled = true
}

ip_configuration {

// Datastream IPs will vary by region.
authorized_networks {
value = "34.71.242.81"
}

authorized_networks {
value = "34.72.28.29"
}

authorized_networks {
value = "34.67.6.157"
}

authorized_networks {
value = "34.67.234.134"
}

authorized_networks {
value = "34.72.239.218"
}
}
}

deletion_protection = <%= ctx[:vars]['deletion_protection'] %>
}

resource "google_sql_database" "db" {
instance = google_sql_database_instance.instance.name
name = "db"
}

resource "random_password" "pwd" {
length = 16
special = false
}

resource "google_sql_user" "user" {
name = "user"
instance = google_sql_database_instance.instance.name
host = "%"
password = random_password.pwd.result
}

resource "google_datastream_connection_profile" "source_connection_profile" {
display_name = "Source connection profile"
location = "us-central1"
connection_profile_id = "<%= ctx[:vars]['source_connection_profile_id'] %>"

mysql_profile {
hostname = google_sql_database_instance.instance.public_ip_address
username = google_sql_user.user.name
password = google_sql_user.user.password
}
}

resource "google_storage_bucket" "bucket" {
name = "<%= ctx[:vars]['bucket_name'] %>"
location = "US"
uniform_bucket_level_access = true
}

resource "google_storage_bucket_iam_member" "viewer" {
bucket = google_storage_bucket.bucket.name
role = "roles/storage.objectViewer"
member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-datastream.iam.gserviceaccount.com"
}

resource "google_storage_bucket_iam_member" "creator" {
bucket = google_storage_bucket.bucket.name
role = "roles/storage.objectCreator"
member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-datastream.iam.gserviceaccount.com"
}

resource "google_storage_bucket_iam_member" "reader" {
bucket = google_storage_bucket.bucket.name
role = "roles/storage.legacyBucketReader"
member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-datastream.iam.gserviceaccount.com"
}

resource "google_datastream_connection_profile" "destination_connection_profile" {
display_name = "Connection profile"
location = "us-central1"
connection_profile_id = "<%= ctx[:vars]['destination_connection_profile_id'] %>"

gcs_profile {
bucket = google_storage_bucket.bucket.name
root_path = "/path"
}
}

resource "google_datastream_stream" "<%= ctx[:primary_resource_id] %>" {
stream_id = "<%= ctx[:vars]['stream_id'] %>"
location = "us-central1"
display_name = "my stream"
source_config {
source_connection_profile = google_datastream_connection_profile.source_connection_profile.id
mysql_source_config {}
}
destination_config {
destination_connection_profile = google_datastream_connection_profile.destination_connection_profile.id
gcs_destination_config {
avro_file_format {}
}
}

backfill_none {
}
}
Loading