From b279ba7d818509d6463c9f17f8d729f33097a1f5 Mon Sep 17 00:00:00 2001 From: The Magician Date: Thu, 22 Feb 2024 09:41:48 -0800 Subject: [PATCH] Sync main branch into FEATURE-BRANCH-provider-functions (#10032) (#7005) * fixes: permadiff issue if event trigger region is not specified (#9989) * fixes: permadiff issue if event trigger region is not specified - fixes https://github.com/hashicorp/terraform-provider-google/issues/17161 * chore: Updated cloudfunction2 test with no explicit trigger_region * Fix GKE front matter, bad field docs (#10018) * Fix GKE front matter, bad field docs * Update container_cluster.html.markdown * added default value for minPortsPerVM field at "google_compute_router_nat" (#9712) * added default value for statis allocation * setting the default from API * add newline removal bash command in guidelines (#9734) * Update hashicorp/terraform-plugin-framework, hashicorp/terraform-plugin-mux, hashicorp/terraform-plugin-sdk/v2 (#10009) * Adding Data Source Forwarding Rules (#10004) * Fix broken terraform datasource google_compute_machine_types example (#10020) * Add check for Environment proto field before accessing in Dataflow provider (#10016) * Add comments to skipped sweeper (#10023) * Add comments for skipped sweepers (#10024) * C3 version schema (#9986) * add support for build number in composerEnvironmentVersionRegexp and composerImageVersionDiffSuppress * make build number optional * regroup regex, cleaner comparison of versions * correction * Add Resource Manager Tags support to 'google_container_cluster' (#9531) * resourceManagerTags added to Cluster Node Config schema * update beta tag * add cluster and node proto tests * add expand and flatten proto * removed beta tag * added to documentation * added resource manager tags to auto pilot * migrating resourceManagerTags tests * migrating node_pools test * migrating additional tests * minor fixes * fixing tests * add in-place update support * fixed tests * fixed annotations * validated clusters and node pools tests. Isolated node pool auto config * isolated resource manager tags from docs * fixed permission issue * fixed spaces * fixed non determinism on tag keys * removed auto_pilot rmts * fixed time_sleep * add depends_on to IAM policies * Add volume replication support for Google Cloud NetApp Volumes (#9816) * Initial replication commit * Cleanup work - Renamed a lot of files to make clear which resource the belong to - Updated documentation for resource fields - Renamed a few resource fields and changed some types - Disabled the custom code for now. Needs to be discussed first * Update example file * Updated example file * Major updates - Reorganisation of block - Reorganisation of fields to match API documentation - Updated example parameters - Added missing API fields - Improved descriptions - * For replication deletion, stop replication first * Add support for deleting destination volume on replication delete * Make volumes deletable in presence of snapshots. This change will be PRed for volume resource independently. Adding it here while it is not in main. * Improving debug error message * yaml check and format fix * Add wait for mirror to initialize. Required to run destroy shortly after create. * Wait on destroy, not on create * Make deleting a replication more robust - doc improvements - started to implement stop/resume. More work required. - renamed a few files to better reflect what they are good for * adding support for stop/resume * yamlformat and lint * Add force delete to delete volumes with nested snapshots * resource test first version * More changes to make tests solid - Introduced new parameter to wait for mirror_status==MIRRORED - more mirror state reconciliation * Test updates * few cleanups * Make virtual field verifies happy * Minor test improvements * More fine tuning - Remove merge conflict in volume.yaml - make generated test work - make output field work - ignore_read for virtual fields * Resource name change as suggested by @slevenick * Remove snapshot code block and fix typo * Detect manual stop/resume actions * Remove ignore_read for deletion_policy * - Made destinationVolumeParameters immutable. It still requires ignore_read. - removed ignore_read from virtual_fields * destinationVolumeParameters are only evaluated at create. Make the immutable. * Name cleanups and comment improvements * removed comment * tabs to spaces in resource block * Updates to address review comments - make wait_for_mirror also work for stop/resume, additionally to create - convert tabs in test resource blocks to spaces - fix typos * Rewording of comments --------- * Ensured that beta runs in TeamCity use only beta paths (#10025) * Ensured that beta runs in TeamCity use only beta paths * Added tests for sweeper package path --------- [upstream:566f9489b8186aa106c77573bc6c08d63047523d] Signed-off-by: Modular Magician --- .changelog/10032.txt | 3 + go.mod | 40 +- go.sum | 202 ++- .../provider/provider_mmv1_resources.go | 6 +- .../resource_cloudfunctions2_function.go | 1 + ...cloudfunctions2_function_generated_test.go | 1 - .../composer/resource_composer_environment.go | 20 +- ..._source_google_compute_forwarding_rules.go | 104 ++ ...ce_google_compute_forwarding_rules_test.go | 55 + .../compute/resource_compute_instance.go | 2 +- .../compute/resource_compute_router_nat.go | 3 +- google-beta/services/container/node_config.go | 39 +- .../container/resource_container_cluster.go | 2 + .../resource_container_cluster_test.go | 128 +- .../container/resource_container_node_pool.go | 42 + .../resource_container_node_pool_test.go | 404 +++++- .../dataflow/resource_dataflow_job.go | 3 + .../resource_netapp_volume_replication.go | 1119 +++++++++++++++++ ...etapp_volume_replication_generated_test.go | 155 +++ ...resource_netapp_volume_replication_test.go | 308 +++++ .../d/compute_forwarding_rules.html.markdown | 42 + .../d/compute_machine_types.html.markdown | 2 +- .../docs/guides/getting_started.html.markdown | 2 +- .../r/cloudfunctions2_function.html.markdown | 1 - .../docs/r/compute_router_nat.html.markdown | 2 +- .../docs/r/container_cluster.html.markdown | 35 +- .../r/netapp_volume_replication.html.markdown | 317 +++++ 27 files changed, 2858 insertions(+), 180 deletions(-) create mode 100644 .changelog/10032.txt create mode 100644 google-beta/services/compute/data_source_google_compute_forwarding_rules.go create mode 100644 google-beta/services/compute/data_source_google_compute_forwarding_rules_test.go create mode 100644 google-beta/services/netapp/resource_netapp_volume_replication.go create mode 100644 google-beta/services/netapp/resource_netapp_volume_replication_generated_test.go create mode 100644 google-beta/services/netapp/resource_netapp_volume_replication_test.go create mode 100644 website/docs/d/compute_forwarding_rules.html.markdown create mode 100644 website/docs/r/netapp_volume_replication.html.markdown diff --git a/.changelog/10032.txt b/.changelog/10032.txt new file mode 100644 index 0000000000..42b910df15 --- /dev/null +++ b/.changelog/10032.txt @@ -0,0 +1,3 @@ +```release-note:none + +``` \ No newline at end of file diff --git a/go.mod b/go.mod index df9d2560c4..c42de4839b 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,5 @@ module github.com/hashicorp/terraform-provider-google-beta + go 1.20 require ( @@ -14,12 +15,12 @@ require ( github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-version v1.6.0 - github.com/hashicorp/terraform-plugin-framework v1.1.1 + github.com/hashicorp/terraform-plugin-framework v1.5.0 github.com/hashicorp/terraform-plugin-framework-validators v0.9.0 - github.com/hashicorp/terraform-plugin-go v0.14.3 - github.com/hashicorp/terraform-plugin-log v0.7.0 - github.com/hashicorp/terraform-plugin-mux v0.8.0 - github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.0 + github.com/hashicorp/terraform-plugin-go v0.20.0 + github.com/hashicorp/terraform-plugin-log v0.9.0 + github.com/hashicorp/terraform-plugin-mux v0.13.0 + github.com/hashicorp/terraform-plugin-sdk/v2 v2.31.0 github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/hashstructure v1.1.0 github.com/sirupsen/logrus v1.8.1 @@ -38,11 +39,13 @@ require ( cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.5 // indirect cloud.google.com/go/longrunning v0.5.4 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect github.com/agext/levenshtein v1.2.2 // indirect - github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect + github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cloudflare/circl v1.3.3 // indirect github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe // indirect github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 // indirect github.com/envoyproxy/go-control-plane v0.11.1 // indirect @@ -62,17 +65,17 @@ require ( github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect - github.com/hashicorp/go-hclog v1.2.1 // indirect - github.com/hashicorp/go-plugin v1.4.8 // indirect + github.com/hashicorp/go-hclog v1.5.0 // indirect + github.com/hashicorp/go-plugin v1.6.0 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/hashicorp/hc-install v0.4.0 // indirect - github.com/hashicorp/hcl/v2 v2.14.1 // indirect + github.com/hashicorp/hc-install v0.6.2 // indirect + github.com/hashicorp/hcl/v2 v2.19.1 // indirect github.com/hashicorp/logutils v1.0.0 // indirect - github.com/hashicorp/terraform-exec v0.17.3 // indirect - github.com/hashicorp/terraform-json v0.14.0 // indirect - github.com/hashicorp/terraform-registry-address v0.1.0 // indirect - github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect - github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect + github.com/hashicorp/terraform-exec v0.19.0 // indirect + github.com/hashicorp/terraform-json v0.18.0 // indirect + github.com/hashicorp/terraform-registry-address v0.2.3 // indirect + github.com/hashicorp/terraform-svchost v0.1.1 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect @@ -83,9 +86,9 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/oklog/run v1.0.0 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect - github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect - github.com/vmihailenco/tagparser v0.1.2 // indirect - github.com/zclconf/go-cty v1.11.0 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + github.com/zclconf/go-cty v1.14.1 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect @@ -93,6 +96,7 @@ require ( go.opentelemetry.io/otel/metric v1.21.0 // indirect go.opentelemetry.io/otel/trace v1.21.0 // indirect golang.org/x/crypto v0.18.0 // indirect + golang.org/x/mod v0.14.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect diff --git a/go.sum b/go.sum index ab792bb42c..b265f1fc35 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,6 @@ bitbucket.org/creachadair/stringset v0.0.8 h1:gQqe4vs8XWgMyijfyKE6K8o4TcyGGrRXe0JvHgx5H+M= bitbucket.org/creachadair/stringset v0.0.8/go.mod h1:AgthVMyMxC/6FK1KBJ2ALdqkZObGN8hOetgpwXyMn34= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.111.0 h1:YHLKNupSD1KqjDbQ3+LVdQ81h/UJbJyZG203cEfnQgM= cloud.google.com/go v0.111.0/go.mod h1:0mibmpKP1TyOOFYQY5izo0LnT+ecvOQ0Sg3OdmMiNRU= cloud.google.com/go/bigtable v1.19.0 h1:wiq9LT0kukfInzvy1joMDijCw/OD1UChpSbORXYn0LI= @@ -14,27 +13,22 @@ cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= cloud.google.com/go/longrunning v0.5.4 h1:w8xEcbZodnA2BbW6sVirkkoC+1gP8wS57EUUgGS0GVg= cloud.google.com/go/longrunning v0.5.4/go.mod h1:zqNVncI0BOP8ST6XQD1+VcvuShMmq7+xFSzOL++V0dI= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/GoogleCloudPlatform/declarative-resource-client-library v1.62.0 h1:s4Y6r6RrYLBnqosGXLwR0h1Gqr0VT3wgd6rqvHsD9OE= github.com/GoogleCloudPlatform/declarative-resource-client-library v1.62.0/go.mod h1:pL2Qt5HT+x6xrTd806oMiM3awW6kNIXB/iiuClz6m6k= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= -github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= -github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= +github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= 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/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU= github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= -github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFUye+ZcSR6opIgz9Co7WcDx6ZcY+RjfFHoA0I= -github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= -github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= -github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= +github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= +github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= +github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -43,6 +37,8 @@ github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91 github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= +github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= @@ -50,14 +46,13 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/creachadair/staticfile v0.1.2/go.mod h1:a3qySzCIXEprDGxk6tSxSI+dBBdLzqeBOMhZ+o2d3pM= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= -github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -70,20 +65,13 @@ github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/gammazero/deque v0.0.0-20180920172122-f6adf94963e4 h1:R+19WKQClnfMXS60cP5BmMe1wjZ4u0evY2p2Ar0ZTXo= github.com/gammazero/deque v0.0.0-20180920172122-f6adf94963e4/go.mod h1:GeIq9qoE43YdGnDXURnmKTnGg15pQz4mYkXSTChbneI= github.com/gammazero/workerpool v0.0.0-20181230203049-86a96b5d5d92 h1:EipXK6U05IQ2wtuFRn4k3h0+2lXypzItoXGVyf4r9Io= github.com/gammazero/workerpool v0.0.0-20181230203049-86a96b5d5d92/go.mod h1:w9RqFVO2BM3xwWEcAB8Fwp0OviTBBEiRmSBDfbXnd3w= -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/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= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= +github.com/go-git/go-git/v5 v5.10.1 h1:tu8/D8i+TWxgKpzQ3Vc43e+kkhXqtsZCKI/egajKnxk= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -104,10 +92,8 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4er github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -127,8 +113,6 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cpy v0.0.0-20211218193943-a9c933c06932 h1:5/4TSDzpDnHQ8rKEEQBjRlYx77mHOvXu08oGchxej7o= @@ -149,75 +133,63 @@ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI= github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= -github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8YmtEw= -github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.4.8 h1:CHGwpxYDOttQOY7HOWgETU9dyVjOXzniXDqJcYJE1zM= -github.com/hashicorp/go-plugin v1.4.8/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= +github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A= +github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/hc-install v0.4.0 h1:cZkRFr1WVa0Ty6x5fTvL1TuO1flul231rWkGH92oYYk= -github.com/hashicorp/hc-install v0.4.0/go.mod h1:5d155H8EC5ewegao9A4PUTMNPZaq+TbOzkJJZ4vrXeI= -github.com/hashicorp/hcl/v2 v2.14.1 h1:x0BpjfZ+CYdbiz+8yZTQ+gdLO7IXvOut7Da+XJayx34= -github.com/hashicorp/hcl/v2 v2.14.1/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0= +github.com/hashicorp/hc-install v0.6.2 h1:V1k+Vraqz4olgZ9UzKiAcbman9i9scg9GgSt/U3mw/M= +github.com/hashicorp/hc-install v0.6.2/go.mod h1:2JBpd+NCFKiHiu/yYCGaPyPHhZLxXTpz8oreHa/a3Ps= +github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI= +github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/terraform-exec v0.17.3 h1:MX14Kvnka/oWGmIkyuyvL6POx25ZmKrjlaclkx3eErU= -github.com/hashicorp/terraform-exec v0.17.3/go.mod h1:+NELG0EqQekJzhvikkeQsOAZpsw0cv/03rbeQJqscAI= -github.com/hashicorp/terraform-json v0.14.0 h1:sh9iZ1Y8IFJLx+xQiKHGud6/TSUCM0N8e17dKDpqV7s= -github.com/hashicorp/terraform-json v0.14.0/go.mod h1:5A9HIWPkk4e5aeeXIBbkcOvaZbIYnAIkEyqP2pNSckM= -github.com/hashicorp/terraform-plugin-framework v1.1.1 h1:PbnEKHsIU8KTTzoztHQGgjZUWx7Kk8uGtpGMMc1p+oI= -github.com/hashicorp/terraform-plugin-framework v1.1.1/go.mod h1:DyZPxQA+4OKK5ELxFIIcqggcszqdWWUpTLPHAhS/tkY= +github.com/hashicorp/terraform-exec v0.19.0 h1:FpqZ6n50Tk95mItTSS9BjeOVUb4eg81SpgVtZNNtFSM= +github.com/hashicorp/terraform-exec v0.19.0/go.mod h1:tbxUpe3JKruE9Cuf65mycSIT8KiNPZ0FkuTE3H4urQg= +github.com/hashicorp/terraform-json v0.18.0 h1:pCjgJEqqDESv4y0Tzdqfxr/edOIGkjs8keY42xfNBwU= +github.com/hashicorp/terraform-json v0.18.0/go.mod h1:qdeBs11ovMzo5puhrRibdD6d2Dq6TyE/28JiU4tIQxk= +github.com/hashicorp/terraform-plugin-framework v1.5.0 h1:8kcvqJs/x6QyOFSdeAyEgsenVOUeC/IyKpi2ul4fjTg= +github.com/hashicorp/terraform-plugin-framework v1.5.0/go.mod h1:6waavirukIlFpVpthbGd2PUNYaFedB0RwW3MDzJ/rtc= github.com/hashicorp/terraform-plugin-framework-validators v0.9.0 h1:LYz4bXh3t7bTEydXOmPDPupRRnA480B/9+jV8yZvxBA= github.com/hashicorp/terraform-plugin-framework-validators v0.9.0/go.mod h1:+BVERsnfdlhYR2YkXMBtPnmn9UsL19U3qUtSZ+Y/5MY= -github.com/hashicorp/terraform-plugin-go v0.14.3 h1:nlnJ1GXKdMwsC8g1Nh05tK2wsC3+3BL/DBBxFEki+j0= -github.com/hashicorp/terraform-plugin-go v0.14.3/go.mod h1:7ees7DMZ263q8wQ6E4RdIdR6nHHJtrdt4ogX5lPkX1A= -github.com/hashicorp/terraform-plugin-log v0.7.0 h1:SDxJUyT8TwN4l5b5/VkiTIaQgY6R+Y2BQ0sRZftGKQs= -github.com/hashicorp/terraform-plugin-log v0.7.0/go.mod h1:p4R1jWBXRTvL4odmEkFfDdhUjHf9zcs/BCoNHAc7IK4= -github.com/hashicorp/terraform-plugin-mux v0.8.0 h1:WCTP66mZ+iIaIrCNJnjPEYnVjawTshnDJu12BcXK1EI= -github.com/hashicorp/terraform-plugin-mux v0.8.0/go.mod h1:vdW0daEi8Kd4RFJmet5Ot+SIVB/B8SwQVJiYKQwdCy8= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.0 h1:FtCLTiTcykdsURXPt/ku7fYXm3y19nbzbZcUxHx9RbI= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.0/go.mod h1:80wf5oad1tW+oLnbXS4UTYmDCrl7BuN1Q+IA91X1a4Y= -github.com/hashicorp/terraform-registry-address v0.1.0 h1:W6JkV9wbum+m516rCl5/NjKxCyTVaaUBbzYcMzBDO3U= -github.com/hashicorp/terraform-registry-address v0.1.0/go.mod h1:EnyO2jYO6j29DTHbJcm00E5nQTFeTtyZH3H5ycydQ5A= -github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 h1:HKLsbzeOsfXmKNpr3GiT18XAblV0BjCbzL8KQAMZGa0= -github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg= -github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= -github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/hashicorp/terraform-plugin-go v0.20.0 h1:oqvoUlL+2EUbKNsJbIt3zqqZ7wi6lzn4ufkn/UA51xQ= +github.com/hashicorp/terraform-plugin-go v0.20.0/go.mod h1:Rr8LBdMlY53a3Z/HpP+ZU3/xCDqtKNCkeI9qOyT10QE= +github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= +github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= +github.com/hashicorp/terraform-plugin-mux v0.13.0 h1:79U401/3nd8CWwDGtTHc8F3miSCAS9XGtVarxSTDgwA= +github.com/hashicorp/terraform-plugin-mux v0.13.0/go.mod h1:Ndv0FtwDG2ogzH59y64f2NYimFJ6I0smRgFUKfm6dyQ= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.31.0 h1:Bl3e2ei2j/Z3Hc2HIS15Gal2KMKyLAZ2om1HCEvK6es= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.31.0/go.mod h1:i2C41tszDjiWfziPQDL5R/f3Zp0gahXe5No/MIO9rCE= +github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= +github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM= +github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= +github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= -github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= -github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= -github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= @@ -238,30 +210,25 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -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= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -270,22 +237,16 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= -github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U= -github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= -github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= -github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc= -github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= -github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= -github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= -github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= -github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= -github.com/zclconf/go-cty v1.11.0 h1:726SxLdi2SDnjY+BStqB9J1hNp4+2WlzyXLuimibIe0= -github.com/zclconf/go-cty v1.11.0/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA= -github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= +github.com/zclconf/go-cty v1.14.1 h1:t9fyA35fwjjUMcmL5hLER+e/rEPqrbCK1/OSE4SI9KA= +github.com/zclconf/go-cty v1.14.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= @@ -302,14 +263,12 @@ go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -319,72 +278,72 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= @@ -398,6 +357,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -406,7 +367,6 @@ google.golang.org/api v0.156.0 h1:yloYcGbBtVYjLKQe4enCunxvwn3s2w/XPrrhVf6MsvQ= google.golang.org/api v0.156.0/go.mod h1:bUSmn4KFO0Q+69zo9CNIDp4Psi6BqM0np0CbzKRSiSY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -441,16 +401,10 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/google-beta/provider/provider_mmv1_resources.go b/google-beta/provider/provider_mmv1_resources.go index 2dd38e327b..373a86e895 100644 --- a/google-beta/provider/provider_mmv1_resources.go +++ b/google-beta/provider/provider_mmv1_resources.go @@ -179,6 +179,7 @@ var handwrittenDatasources = map[string]*schema.Resource{ "google_compute_default_service_account": compute.DataSourceGoogleComputeDefaultServiceAccount(), "google_compute_disk": compute.DataSourceGoogleComputeDisk(), "google_compute_forwarding_rule": compute.DataSourceGoogleComputeForwardingRule(), + "google_compute_forwarding_rules": compute.DataSourceGoogleComputeForwardingRules(), "google_compute_global_address": compute.DataSourceGoogleComputeGlobalAddress(), "google_compute_global_forwarding_rule": compute.DataSourceGoogleComputeGlobalForwardingRule(), "google_compute_ha_vpn_gateway": compute.DataSourceGoogleComputeHaVpnGateway(), @@ -429,9 +430,9 @@ var handwrittenIAMDatasources = map[string]*schema.Resource{ } // Resources -// Generated resources: 443 +// Generated resources: 444 // Generated IAM resources: 258 -// Total generated resources: 701 +// Total generated resources: 702 var generatedResources = map[string]*schema.Resource{ "google_folder_access_approval_settings": accessapproval.ResourceAccessApprovalFolderSettings(), "google_organization_access_approval_settings": accessapproval.ResourceAccessApprovalOrganizationSettings(), @@ -947,6 +948,7 @@ var generatedResources = map[string]*schema.Resource{ "google_monitoring_slo": monitoring.ResourceMonitoringSlo(), "google_monitoring_uptime_check_config": monitoring.ResourceMonitoringUptimeCheckConfig(), "google_netapp_volume": netapp.ResourceNetappVolume(), + "google_netapp_volume_replication": netapp.ResourceNetappVolumeReplication(), "google_netapp_volume_snapshot": netapp.ResourceNetappVolumeSnapshot(), "google_netapp_active_directory": netapp.ResourceNetappactiveDirectory(), "google_netapp_backup_policy": netapp.ResourceNetappbackupPolicy(), diff --git a/google-beta/services/cloudfunctions2/resource_cloudfunctions2_function.go b/google-beta/services/cloudfunctions2/resource_cloudfunctions2_function.go index 9d1375e9f5..2932ce67fb 100644 --- a/google-beta/services/cloudfunctions2/resource_cloudfunctions2_function.go +++ b/google-beta/services/cloudfunctions2/resource_cloudfunctions2_function.go @@ -257,6 +257,7 @@ Compute Engine default service account: {project_number}-compute@developer.gserv }, "trigger_region": { Type: schema.TypeString, + Computed: true, Optional: true, Description: `The region that the trigger will be in. The trigger will only receive events originating in this region. It can be the same diff --git a/google-beta/services/cloudfunctions2/resource_cloudfunctions2_function_generated_test.go b/google-beta/services/cloudfunctions2/resource_cloudfunctions2_function_generated_test.go index fcb59f9182..ca3ae63c06 100644 --- a/google-beta/services/cloudfunctions2/resource_cloudfunctions2_function_generated_test.go +++ b/google-beta/services/cloudfunctions2/resource_cloudfunctions2_function_generated_test.go @@ -330,7 +330,6 @@ resource "google_cloudfunctions2_function" "function" { } event_trigger { - trigger_region = "us-central1" # The trigger must be in the same location as the bucket event_type = "google.cloud.storage.object.v1.finalized" retry_policy = "RETRY_POLICY_RETRY" service_account_email = google_service_account.account.email diff --git a/google-beta/services/composer/resource_composer_environment.go b/google-beta/services/composer/resource_composer_environment.go index cfce2feb04..1679b8a14f 100644 --- a/google-beta/services/composer/resource_composer_environment.go +++ b/google-beta/services/composer/resource_composer_environment.go @@ -24,7 +24,7 @@ import ( const ( composerEnvironmentEnvVariablesRegexp = "[a-zA-Z_][a-zA-Z0-9_]*." composerEnvironmentReservedAirflowEnvVarRegexp = "AIRFLOW__[A-Z0-9_]+__[A-Z0-9_]+" - composerEnvironmentVersionRegexp = `composer-(([0-9]+)(\.[0-9]+\.[0-9]+(-preview\.[0-9]+)?)?|latest)-airflow-(([0-9]+)((\.[0-9]+)(\.[0-9]+)?)?)` + composerEnvironmentVersionRegexp = `composer-(([0-9]+)(\.[0-9]+\.[0-9]+(-preview\.[0-9]+)?)?|latest)-airflow-(([0-9]+)((\.[0-9]+)(\.[0-9]+)?)?(-build\.[0-9]+)?)` ) var composerEnvironmentReservedEnvVar = map[string]struct{}{ @@ -2766,7 +2766,7 @@ func composerImageVersionDiffSuppress(_, old, new string, _ *schema.ResourceData versionRe := regexp.MustCompile(composerEnvironmentVersionRegexp) oldVersions := versionRe.FindStringSubmatch(old) newVersions := versionRe.FindStringSubmatch(new) - if oldVersions == nil || len(oldVersions) < 10 { + if oldVersions == nil || len(oldVersions) < 11 { // Somehow one of the versions didn't match the regexp or didn't // have values in the capturing groups. In that case, fall back to // an equality check. @@ -2775,7 +2775,7 @@ func composerImageVersionDiffSuppress(_, old, new string, _ *schema.ResourceData } return old == new } - if newVersions == nil || len(newVersions) < 10 { + if newVersions == nil || len(newVersions) < 11 { // Somehow one of the versions didn't match the regexp or didn't // have values in the capturing groups. In that case, fall back to // an equality check. @@ -2788,9 +2788,11 @@ func composerImageVersionDiffSuppress(_, old, new string, _ *schema.ResourceData oldAirflow := oldVersions[5] oldAirflowMajor := oldVersions[6] oldAirflowMajorMinor := oldVersions[6] + oldVersions[8] + oldAirflowMajorMinorPatch := oldVersions[6] + oldVersions[8] + oldVersions[9] newAirflow := newVersions[5] newAirflowMajor := newVersions[6] newAirflowMajorMinor := newVersions[6] + newVersions[8] + newAirflowMajorMinorPatch := newVersions[6] + newVersions[8] + newVersions[9] // Check Airflow versions. if oldAirflow == oldAirflowMajor || newAirflow == newAirflowMajor { // If one of the Airflow versions specifies only major version @@ -2812,8 +2814,18 @@ func composerImageVersionDiffSuppress(_, old, new string, _ *schema.ResourceData if !eq { return false } + } else if oldAirflow == oldAirflowMajorMinorPatch || newAirflow == newAirflowMajorMinorPatch { + // If one of the Airflow versions specifies only major, minor and patch version + // (like 1.10.15), we can only compare major, minor and patch versions. + eq, err := versionsEqual(oldAirflowMajorMinorPatch, newAirflowMajorMinorPatch) + if err != nil { + log.Printf("[WARN] Could not parse airflow version, %s", err) + } + if !eq { + return false + } } else { - // Otherwise, we compare the full Airflow versions (like 1.10.15). + // Otherwise, we compare the full Airflow versions (like 1.10.15-build.5). eq, err := versionsEqual(oldAirflow, newAirflow) if err != nil { log.Printf("[WARN] Could not parse airflow version, %s", err) diff --git a/google-beta/services/compute/data_source_google_compute_forwarding_rules.go b/google-beta/services/compute/data_source_google_compute_forwarding_rules.go new file mode 100644 index 0000000000..e1687f22a9 --- /dev/null +++ b/google-beta/services/compute/data_source_google_compute_forwarding_rules.go @@ -0,0 +1,104 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package compute + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport" +) + +func DataSourceGoogleComputeForwardingRules() *schema.Resource { + return &schema.Resource{ + Read: dataSourceGoogleComputeForwardingRulesRead, + + Schema: map[string]*schema.Schema{ + + "project": { + Type: schema.TypeString, + Optional: true, + }, + + "region": { + Type: schema.TypeString, + Optional: true, + }, + + "rules": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: tpgresource.DatasourceSchemaFromResourceSchema(ResourceComputeForwardingRule().Schema), + }, + }, + }, + } +} + +func dataSourceGoogleComputeForwardingRulesRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return err + } + + region, err := tpgresource.GetRegion(d, config) + if err != nil { + return err + } + + id := fmt.Sprintf("projects/%s/regions/%s/forwardingRules", project, region) + d.SetId(id) + + forwardingRulesAggregatedList, err := config.NewComputeClient(userAgent).ForwardingRules.List(project, region).Do() + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("Forwarding Rules Not Found : %s", project)) + } + + forwardingRules := make([]map[string]interface{}, 0, len(forwardingRulesAggregatedList.Items)) + + for i := 0; i < len(forwardingRulesAggregatedList.Items); i++ { + rule := forwardingRulesAggregatedList.Items[i] + mappedData := map[string]interface{}{ + "name": rule.Name, + "network": rule.Network, + "subnetwork": rule.Subnetwork, + "backend_service": rule.BackendService, + "ip_address": rule.IPAddress, + "service_name": rule.ServiceName, + "service_label": rule.ServiceLabel, + "description": rule.Description, + "self_link": rule.SelfLink, + "labels": rule.Labels, + "ports": rule.Ports, + "region": rule.Region, + "target": rule.Target, + "ip_version": rule.IpVersion, + "network_tier": rule.NetworkTier, + "base_forwarding_rule": rule.BaseForwardingRule, + "port_range": rule.PortRange, + } + forwardingRules = append(forwardingRules, mappedData) + } + + if err := d.Set("rules", forwardingRules); err != nil { + return fmt.Errorf("Error setting the forwarding rules names: %s", err) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error setting the network names: %s", err) + } + + if err := d.Set("region", region); err != nil { + return fmt.Errorf("Error setting the region: %s", err) + } + + return nil +} diff --git a/google-beta/services/compute/data_source_google_compute_forwarding_rules_test.go b/google-beta/services/compute/data_source_google_compute_forwarding_rules_test.go new file mode 100644 index 0000000000..031917f178 --- /dev/null +++ b/google-beta/services/compute/data_source_google_compute_forwarding_rules_test.go @@ -0,0 +1,55 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package compute_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-google-beta/google-beta/acctest" +) + +func TestAccDataSourceGoogleForwardingRules(t *testing.T) { + t.Parallel() + + poolName := fmt.Sprintf("tf-%s", acctest.RandString(t, 10)) + ruleName := fmt.Sprintf("tf-%s", acctest.RandString(t, 10)) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccDataSourceGoogleForwardingRuleConfig(poolName, ruleName), + Check: acctest.CheckDataSourceStateMatchesResourceState("data.google_compute_forwarding_rule.my_forwarding_rule", "google_compute_forwarding_rule.foobar-fr"), + }, + }, + }) +} + +func testAccDataSourceGoogleForwardingRulesConfig(poolName, ruleName string) string { + return fmt.Sprintf(` +resource "google_compute_target_pool" "foobar-tp" { + description = "Resource created for Terraform acceptance testing" + instances = ["us-central1-a/foo", "us-central1-b/bar"] + name = "%s" +} + +resource "google_compute_forwarding_rule" "foobar-fr" { + description = "Resource created for Terraform acceptance testing" + ip_protocol = "UDP" + name = "%s" + port_range = "80-81" + target = google_compute_target_pool.foobar-tp.self_link + labels = { + my-label = "my-label-value" + } +} + +data "google_compute_forwarding_rules" "my_forwarding_rule" { + project = google_compute_forwarding_rule.foobar-fr.project + region = google_compute_forwarding_rule.foobar-fr.region +} +`, poolName, ruleName) +} diff --git a/google-beta/services/compute/resource_compute_instance.go b/google-beta/services/compute/resource_compute_instance.go index c9e818fb23..6223a6463b 100644 --- a/google-beta/services/compute/resource_compute_instance.go +++ b/google-beta/services/compute/resource_compute_instance.go @@ -660,7 +660,7 @@ func ResourceComputeInstance() *schema.Resource { Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: `A set of key/value label pairs assigned to the instance. - + **Note**: This field is non-authoritative, and will only manage the labels present in your configuration. Please refer to the field 'effective_labels' for all of the labels present on the resource.`, }, diff --git a/google-beta/services/compute/resource_compute_router_nat.go b/google-beta/services/compute/resource_compute_router_nat.go index 71f4966a7a..77fec1e262 100644 --- a/google-beta/services/compute/resource_compute_router_nat.go +++ b/google-beta/services/compute/resource_compute_router_nat.go @@ -308,8 +308,9 @@ This field can only be set when enableDynamicPortAllocation is enabled.`, }, "min_ports_per_vm": { Type: schema.TypeInt, + Computed: true, Optional: true, - Description: `Minimum number of ports allocated to a VM from this NAT.`, + Description: `Minimum number of ports allocated to a VM from this NAT. Defaults to 64 for static port allocation and 32 dynamic port allocation if not set.`, }, "nat_ip_allocate_option": { Type: schema.TypeString, diff --git a/google-beta/services/container/node_config.go b/google-beta/services/container/node_config.go index dede34c40b..39e0e54e3d 100644 --- a/google-beta/services/container/node_config.go +++ b/google-beta/services/container/node_config.go @@ -664,6 +664,11 @@ func schemaNodeConfig() *schema.Schema { }, }, }, + "resource_manager_tags": { + Type: schema.TypeMap, + Optional: true, + Description: `A map of resource manager tags. Resource manager tag keys and values have the same definition as resource manager tags. Keys must be in the format tagKeys/{tag_key_id}, and values are in the format tagValues/456. The field is ignored (both PUT & PATCH) when empty.`, + }, "enable_confidential_storage": { Type: schema.TypeBool, Optional: true, @@ -867,6 +872,10 @@ func expandNodeConfig(v interface{}) *container.NodeConfig { nc.ResourceLabels = m } + if v, ok := nodeConfig["resource_manager_tags"]; ok && len(v.(map[string]interface{})) > 0 { + nc.ResourceManagerTags = expandResourceManagerTags(v) + } + if v, ok := nodeConfig["tags"]; ok { tagsList := v.([]interface{}) tags := []string{} @@ -965,6 +974,19 @@ func expandNodeConfig(v interface{}) *container.NodeConfig { return nc } +func expandResourceManagerTags(v interface{}) *container.ResourceManagerTags { + rmts := make(map[string]string) + + if v != nil { + rmts = tpgresource.ConvertStringMap(v.(map[string]interface{})) + } + + return &container.ResourceManagerTags{ + Tags: rmts, + ForceSendFields: []string{"Tags"}, + } +} + func expandWorkloadMetadataConfig(v interface{}) *container.WorkloadMetadataConfig { if v == nil { return nil @@ -1182,8 +1204,8 @@ func flattenNodeConfig(c *container.NodeConfig, v interface{}) []map[string]inte "advanced_machine_features": flattenAdvancedMachineFeaturesConfig(c.AdvancedMachineFeatures), "sole_tenant_config": flattenSoleTenantConfig(c.SoleTenantConfig), "fast_socket": flattenFastSocket(c.FastSocket), - - "enable_confidential_storage": c.EnableConfidentialStorage, + "resource_manager_tags": flattenResourceManagerTags(c.ResourceManagerTags), + "enable_confidential_storage": c.EnableConfidentialStorage, }) if len(c.OauthScopes) > 0 { @@ -1193,6 +1215,19 @@ func flattenNodeConfig(c *container.NodeConfig, v interface{}) []map[string]inte return config } +func flattenResourceManagerTags(c *container.ResourceManagerTags) map[string]interface{} { + rmt := make(map[string]interface{}) + + if c != nil { + for k, v := range c.Tags { + rmt[k] = v + } + + } + + return rmt +} + func flattenAdvancedMachineFeaturesConfig(c *container.AdvancedMachineFeatures) []map[string]interface{} { result := []map[string]interface{}{} if c != nil { diff --git a/google-beta/services/container/resource_container_cluster.go b/google-beta/services/container/resource_container_cluster.go index 598b4078a9..ff0789574f 100644 --- a/google-beta/services/container/resource_container_cluster.go +++ b/google-beta/services/container/resource_container_cluster.go @@ -92,6 +92,7 @@ var ( forceNewClusterNodeConfigFields = []string{ "labels", "workload_metadata_config", + "resource_manager_tags", } suppressDiffForAutopilot = schema.SchemaDiffSuppressFunc(func(k, oldValue, newValue string, d *schema.ResourceData) bool { @@ -5349,6 +5350,7 @@ func expandNodePoolAutoConfig(configured interface{}) *container.NodePoolAutoCon if v, ok := config["network_tags"]; ok && len(v.([]interface{})) > 0 { npac.NetworkTags = expandNodePoolAutoConfigNetworkTags(v) } + return npac } diff --git a/google-beta/services/container/resource_container_cluster_test.go b/google-beta/services/container/resource_container_cluster_test.go index b47f7163dd..f141f0ae60 100644 --- a/google-beta/services/container/resource_container_cluster_test.go +++ b/google-beta/services/container/resource_container_cluster_test.go @@ -57,6 +57,43 @@ func TestAccContainerCluster_basic(t *testing.T) { }) } +func TestAccContainerCluster_resourceManagerTags(t *testing.T) { + t.Parallel() + + pid := envvar.GetTestProjectFromEnv() + + randomSuffix := acctest.RandString(t, 10) + clusterName := fmt.Sprintf("tf-test-cluster-%s", randomSuffix) + + networkName := acctest.BootstrapSharedTestNetwork(t, "gke-cluster") + subnetworkName := acctest.BootstrapSubnet(t, "gke-cluster", networkName) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + ExternalProviders: map[string]resource.ExternalProvider{ + "time": {}, + }, + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccContainerCluster_resourceManagerTags(pid, clusterName, networkName, subnetworkName, randomSuffix), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("google_container_cluster.primary", "self_link"), + resource.TestCheckResourceAttrSet("google_container_cluster.primary", "node_config.0.resource_manager_tags.%"), + ), + }, + { + ResourceName: "google_container_cluster.primary", + ImportStateId: fmt.Sprintf("us-central1-a/%s", clusterName), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version", "deletion_protection"}, + }, + }, + }) +} + func TestAccContainerCluster_networkingModeRoutes(t *testing.T) { t.Parallel() @@ -4418,11 +4455,11 @@ func testAccContainerCluster_withIncompatibleMasterVersionNodeVersion(name strin resource "google_container_cluster" "gke_cluster" { name = "%s" location = "us-central1" - + min_master_version = "1.10.9-gke.5" node_version = "1.10.6-gke.11" initial_node_count = 1 - + } `, name) } @@ -6631,7 +6668,7 @@ resource "google_container_cluster" "with_autoprovisioning" { min_master_version = data.google_container_engine_versions.central1a.latest_master_version initial_node_count = 1 deletion_protection = false - + network = "%s" subnetwork = "%s" @@ -9197,7 +9234,7 @@ resource "google_compute_resource_policy" "policy" { resource "google_container_cluster" "cluster" { name = "%s" location = "us-central1-a" - + node_pool { name = "%s" initial_node_count = 2 @@ -9504,3 +9541,86 @@ func testAccContainerCluster_withWorkloadALTSConfig(projectID, name, networkName } `, projectID, networkName, subnetworkName, name, enable) } + +func testAccContainerCluster_resourceManagerTags(projectID, clusterName, networkName, subnetworkName, randomSuffix string) string { + return fmt.Sprintf(` +data "google_project" "project" { + project_id = "%[1]s" +} + +resource "google_project_iam_binding" "tagHoldAdmin" { + project = "%[1]s" + role = "roles/resourcemanager.tagHoldAdmin" + members = [ + "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com", + ] +} + +resource "google_project_iam_binding" "tagUser" { + project = "%[1]s" + role = "roles/resourcemanager.tagUser" + members = [ + "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com", + "serviceAccount:${data.google_project.project.number}@cloudservices.gserviceaccount.com", + ] + + depends_on = [google_project_iam_binding.tagHoldAdmin] +} + +resource "time_sleep" "wait_120_seconds" { + create_duration = "120s" + + depends_on = [ + google_project_iam_binding.tagHoldAdmin, + google_project_iam_binding.tagUser + ] +} + +resource "google_tags_tag_key" "key" { + parent = "projects/%[1]s" + short_name = "foobarbaz-%[2]s" + description = "For foo/bar resources" + purpose = "GCE_FIREWALL" + purpose_data = { + network = "%[1]s/%[4]s" + } +} + +resource "google_tags_tag_value" "value" { + parent = "tagKeys/${google_tags_tag_key.key.name}" + short_name = "foo-%[2]s" + description = "For foo resources" +} + +data "google_container_engine_versions" "uscentral1a" { + location = "us-central1-a" +} + +resource "google_container_cluster" "primary" { + name = "%[3]s" + location = "us-central1-a" + min_master_version = data.google_container_engine_versions.uscentral1a.release_channel_latest_version["STABLE"] + initial_node_count = 1 + + node_config { + machine_type = "n1-standard-1" // can't be e2 because of local-ssd + disk_size_gb = 15 + + resource_manager_tags = { + "tagKeys/${google_tags_tag_key.key.name}" = "tagValues/${google_tags_tag_value.value.name}" + } + } + + deletion_protection = false + network = "%[4]s" + subnetwork = "%[5]s" + + timeouts { + create = "30m" + update = "40m" + } + + depends_on = [time_sleep.wait_120_seconds] +} +`, projectID, randomSuffix, clusterName, networkName, subnetworkName) +} diff --git a/google-beta/services/container/resource_container_node_pool.go b/google-beta/services/container/resource_container_node_pool.go index 71279e3063..e4cb910789 100644 --- a/google-beta/services/container/resource_container_node_pool.go +++ b/google-beta/services/container/resource_container_node_pool.go @@ -1586,6 +1586,48 @@ func nodePoolUpdate(d *schema.ResourceData, meta interface{}, nodePoolInfo *Node log.Printf("[INFO] Updated tags for node pool %s", name) } + if d.HasChange(prefix + "node_config.0.resource_manager_tags") { + req := &container.UpdateNodePoolRequest{ + Name: name, + } + if v, ok := d.GetOk(prefix + "node_config.0.resource_manager_tags"); ok { + req.ResourceManagerTags = expandResourceManagerTags(v) + } + + // sets resource manager tags to the empty list when user removes a previously defined list of tags entriely + // aka the node pool goes from having tags to no longer having any + if req.ResourceManagerTags == nil { + tags := make(map[string]string) + rmTags := &container.ResourceManagerTags{ + Tags: tags, + } + req.ResourceManagerTags = rmTags + } + + updateF := func() error { + clusterNodePoolsUpdateCall := config.NewContainerClient(userAgent).Projects.Locations.Clusters.NodePools.Update(nodePoolInfo.fullyQualifiedName(name), req) + if config.UserProjectOverride { + clusterNodePoolsUpdateCall.Header().Add("X-Goog-User-Project", nodePoolInfo.project) + } + op, err := clusterNodePoolsUpdateCall.Do() + if err != nil { + return err + } + + // Wait until it's updated + return ContainerOperationWait(config, op, + nodePoolInfo.project, + nodePoolInfo.location, + "updating GKE node pool resource manager tags", userAgent, + timeout) + } + + if err := retryWhileIncompatibleOperation(timeout, npLockKey, updateF); err != nil { + return err + } + log.Printf("[INFO] Updated resource manager tags for node pool %s", name) + } + if d.HasChange(prefix + "node_config.0.resource_labels") { req := &container.UpdateNodePoolRequest{ Name: name, diff --git a/google-beta/services/container/resource_container_node_pool_test.go b/google-beta/services/container/resource_container_node_pool_test.go index 74fcb1e96b..a60a760509 100644 --- a/google-beta/services/container/resource_container_node_pool_test.go +++ b/google-beta/services/container/resource_container_node_pool_test.go @@ -38,6 +38,61 @@ func TestAccContainerNodePool_basic(t *testing.T) { }) } +func TestAccContainerNodePool_resourceManagerTags(t *testing.T) { + t.Parallel() + pid := envvar.GetTestProjectFromEnv() + + randomSuffix := acctest.RandString(t, 10) + clusterName := fmt.Sprintf("tf-test-cluster-%s", randomSuffix) + + networkName := acctest.BootstrapSharedTestNetwork(t, "gke-cluster") + subnetworkName := acctest.BootstrapSubnet(t, "gke-cluster", networkName) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + ExternalProviders: map[string]resource.ExternalProvider{ + "time": {}, + }, + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccContainerNodePool_resourceManagerTags(pid, clusterName, networkName, subnetworkName, randomSuffix), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("google_container_node_pool.primary_nodes", "node_config.0.resource_manager_tags.%"), + ), + }, + { + ResourceName: "google_container_node_pool.primary_nodes", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version", "cluster"}, + }, + { + Config: testAccContainerNodePool_resourceManagerTagsUpdate1(pid, clusterName, networkName, subnetworkName, randomSuffix), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("google_container_node_pool.primary_nodes", "node_config.0.resource_manager_tags.%"), + ), + }, + { + ResourceName: "google_container_node_pool.primary_nodes", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version", "cluster"}, + }, + { + Config: testAccContainerNodePool_resourceManagerTagsUpdate2(pid, clusterName, networkName, subnetworkName, randomSuffix), + }, + { + ResourceName: "google_container_node_pool.primary_nodes", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version", "cluster"}, + }, + }, + }) +} + func TestAccContainerNodePool_basicWithClusterId(t *testing.T) { t.Parallel() @@ -4107,7 +4162,7 @@ resource "google_container_node_pool" "with_confidential_boot_disk" { name = "%s" location = "us-central1-a" cluster = google_container_cluster.cluster.name - + node_config { image_type = "COS_CONTAINERD" boot_disk_kms_key = "%s" @@ -4168,7 +4223,7 @@ resource "google_container_node_pool" "without_confidential_boot_disk" { name = "%s" location = "us-central1-a" cluster = google_container_cluster.cluster.name - + node_config { image_type = "COS_CONTAINERD" oauth_scopes = [ @@ -4182,3 +4237,348 @@ resource "google_container_node_pool" "without_confidential_boot_disk" { } `, cluster, networkName, subnetworkName, np) } + +func testAccContainerNodePool_resourceManagerTags(projectID, clusterName, networkName, subnetworkName, randomSuffix string) string { + return fmt.Sprintf(` +data "google_project" "project" { + project_id = "%[1]s" +} + +resource "google_project_iam_binding" "tagHoldAdmin" { + project = "%[1]s" + role = "roles/resourcemanager.tagHoldAdmin" + members = [ + "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com", + ] +} + +resource "google_project_iam_binding" "tagUser" { + project = "%[1]s" + role = "roles/resourcemanager.tagUser" + members = [ + "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com", + "serviceAccount:${data.google_project.project.number}@cloudservices.gserviceaccount.com", + ] + + depends_on = [google_project_iam_binding.tagHoldAdmin] +} + +resource "time_sleep" "wait_120_seconds" { + create_duration = "120s" + + depends_on = [ + google_project_iam_binding.tagHoldAdmin, + google_project_iam_binding.tagUser + ] +} + +resource "google_tags_tag_key" "key1" { + parent = "projects/%[1]s" + short_name = "foobarbaz1-%[2]s" + description = "For foo/bar1 resources" + purpose = "GCE_FIREWALL" + purpose_data = { + network = "%[1]s/%[4]s" + } +} + +resource "google_tags_tag_value" "value1" { + parent = "tagKeys/${google_tags_tag_key.key1.name}" + short_name = "foo1-%[2]s" + description = "For foo1 resources" +} + +resource "google_tags_tag_key" "key2" { + parent = "projects/%[1]s" + short_name = "foobarbaz2-%[2]s" + description = "For foo/bar2 resources" + purpose = "GCE_FIREWALL" + purpose_data = { + network = "%[1]s/%[4]s" + } + + depends_on = [google_tags_tag_key.key1] +} + +resource "google_tags_tag_value" "value2" { + parent = "tagKeys/${google_tags_tag_key.key2.name}" + short_name = "foo2-%[2]s" + description = "For foo2 resources" +} + +data "google_container_engine_versions" "uscentral1a" { + location = "us-central1-a" +} + +resource "google_container_cluster" "primary" { + name = "%[3]s" + location = "us-central1-a" + min_master_version = data.google_container_engine_versions.uscentral1a.release_channel_latest_version["STABLE"] + + # We can't create a cluster with no node pool defined, but we want to only use + # separately managed node pools. So we create the smallest possible default + # node pool and immediately delete it. + remove_default_node_pool = true + initial_node_count = 1 + + deletion_protection = false + network = "%[4]s" + subnetwork = "%[5]s" + + timeouts { + create = "30m" + update = "40m" + } + + depends_on = [time_sleep.wait_120_seconds] +} + +# Separately Managed Node Pool +resource "google_container_node_pool" "primary_nodes" { + name = google_container_cluster.primary.name + location = "us-central1-a" + cluster = google_container_cluster.primary.name + + version = data.google_container_engine_versions.uscentral1a.release_channel_latest_version["STABLE"] + node_count = 1 + + node_config { + machine_type = "n1-standard-1" // can't be e2 because of local-ssd + disk_size_gb = 15 + + resource_manager_tags = { + "tagKeys/${google_tags_tag_key.key1.name}" = "tagValues/${google_tags_tag_value.value1.name}" + } + } +} +`, projectID, randomSuffix, clusterName, networkName, subnetworkName) +} + +func testAccContainerNodePool_resourceManagerTagsUpdate1(projectID, clusterName, networkName, subnetworkName, randomSuffix string) string { + return fmt.Sprintf(` +data "google_project" "project" { + project_id = "%[1]s" +} + +resource "google_project_iam_binding" "tagHoldAdmin" { + project = "%[1]s" + role = "roles/resourcemanager.tagHoldAdmin" + members = [ + "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com", + ] +} + +resource "google_project_iam_binding" "tagUser" { + project = "%[1]s" + role = "roles/resourcemanager.tagUser" + members = [ + "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com", + "serviceAccount:${data.google_project.project.number}@cloudservices.gserviceaccount.com", + ] + + depends_on = [google_project_iam_binding.tagHoldAdmin] +} + +resource "time_sleep" "wait_120_seconds" { + create_duration = "120s" + + depends_on = [ + google_project_iam_binding.tagHoldAdmin, + google_project_iam_binding.tagUser + ] +} + +resource "google_tags_tag_key" "key1" { + parent = "projects/%[1]s" + short_name = "foobarbaz1-%[2]s" + description = "For foo/bar1 resources" + purpose = "GCE_FIREWALL" + purpose_data = { + network = "%[1]s/%[4]s" + } +} + +resource "google_tags_tag_value" "value1" { + parent = "tagKeys/${google_tags_tag_key.key1.name}" + short_name = "foo1-%[2]s" + description = "For foo1 resources" +} + +resource "google_tags_tag_key" "key2" { + parent = "projects/%[1]s" + short_name = "foobarbaz2-%[2]s" + description = "For foo/bar2 resources" + purpose = "GCE_FIREWALL" + purpose_data = { + network = "%[1]s/%[4]s" + } + + depends_on = [google_tags_tag_key.key1] +} + +resource "google_tags_tag_value" "value2" { + parent = "tagKeys/${google_tags_tag_key.key2.name}" + short_name = "foo2-%[2]s" + description = "For foo2 resources" +} + +data "google_container_engine_versions" "uscentral1a" { + location = "us-central1-a" +} + +resource "google_container_cluster" "primary" { + name = "%[3]s" + location = "us-central1-a" + min_master_version = data.google_container_engine_versions.uscentral1a.release_channel_latest_version["STABLE"] + + # We can't create a cluster with no node pool defined, but we want to only use + # separately managed node pools. So we create the smallest possible default + # node pool and immediately delete it. + remove_default_node_pool = true + initial_node_count = 1 + + deletion_protection = false + network = "%[4]s" + subnetwork = "%[5]s" + + timeouts { + create = "30m" + update = "40m" + } + + depends_on = [time_sleep.wait_120_seconds] +} + +# Separately Managed Node Pool +resource "google_container_node_pool" "primary_nodes" { + name = google_container_cluster.primary.name + location = "us-central1-a" + cluster = google_container_cluster.primary.name + + version = data.google_container_engine_versions.uscentral1a.release_channel_latest_version["STABLE"] + node_count = 1 + + node_config { + machine_type = "n1-standard-1" // can't be e2 because of local-ssd + disk_size_gb = 15 + + resource_manager_tags = { + "tagKeys/${google_tags_tag_key.key1.name}" = "tagValues/${google_tags_tag_value.value1.name}" + "tagKeys/${google_tags_tag_key.key2.name}" = "tagValues/${google_tags_tag_value.value2.name}" + } + } +} +`, projectID, randomSuffix, clusterName, networkName, subnetworkName) +} + +func testAccContainerNodePool_resourceManagerTagsUpdate2(projectID, clusterName, networkName, subnetworkName, randomSuffix string) string { + return fmt.Sprintf(` +data "google_project" "project" { + project_id = "%[1]s" +} + +resource "google_project_iam_binding" "tagHoldAdmin" { + project = "%[1]s" + role = "roles/resourcemanager.tagHoldAdmin" + members = [ + "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com", + ] +} + +resource "google_project_iam_binding" "tagUser" { + project = "%[1]s" + role = "roles/resourcemanager.tagUser" + members = [ + "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com", + "serviceAccount:${data.google_project.project.number}@cloudservices.gserviceaccount.com", + ] + + depends_on = [google_project_iam_binding.tagHoldAdmin] +} + +resource "time_sleep" "wait_120_seconds" { + create_duration = "120s" + + depends_on = [ + google_project_iam_binding.tagHoldAdmin, + google_project_iam_binding.tagUser + ] +} + +resource "google_tags_tag_key" "key1" { + parent = "projects/%[1]s" + short_name = "foobarbaz1-%[2]s" + description = "For foo/bar1 resources" + purpose = "GCE_FIREWALL" + purpose_data = { + network = "%[1]s/%[4]s" + } +} + +resource "google_tags_tag_value" "value1" { + parent = "tagKeys/${google_tags_tag_key.key1.name}" + short_name = "foo1-%[2]s" + description = "For foo1 resources" +} + +resource "google_tags_tag_key" "key2" { + parent = "projects/%[1]s" + short_name = "foobarbaz2-%[2]s" + description = "For foo/bar2 resources" + purpose = "GCE_FIREWALL" + purpose_data = { + network = "%[1]s/%[4]s" + } + + depends_on = [google_tags_tag_key.key1] +} + +resource "google_tags_tag_value" "value2" { + parent = "tagKeys/${google_tags_tag_key.key2.name}" + short_name = "foo2-%[2]s" + description = "For foo2 resources" +} + +data "google_container_engine_versions" "uscentral1a" { + location = "us-central1-a" +} + +resource "google_container_cluster" "primary" { + name = "%[3]s" + location = "us-central1-a" + min_master_version = data.google_container_engine_versions.uscentral1a.release_channel_latest_version["STABLE"] + + # We can't create a cluster with no node pool defined, but we want to only use + # separately managed node pools. So we create the smallest possible default + # node pool and immediately delete it. + remove_default_node_pool = true + initial_node_count = 1 + + deletion_protection = false + network = "%[4]s" + subnetwork = "%[5]s" + + timeouts { + create = "30m" + update = "40m" + } + + depends_on = [time_sleep.wait_120_seconds] +} + +# Separately Managed Node Pool +resource "google_container_node_pool" "primary_nodes" { + name = google_container_cluster.primary.name + location = "us-central1-a" + cluster = google_container_cluster.primary.name + + version = data.google_container_engine_versions.uscentral1a.release_channel_latest_version["STABLE"] + node_count = 1 + + node_config { + machine_type = "n1-standard-1" // can't be e2 because of local-ssd + disk_size_gb = 15 + } +} +`, projectID, randomSuffix, clusterName, networkName, subnetworkName) +} diff --git a/google-beta/services/dataflow/resource_dataflow_job.go b/google-beta/services/dataflow/resource_dataflow_job.go index 36316ecb76..421308835b 100644 --- a/google-beta/services/dataflow/resource_dataflow_job.go +++ b/google-beta/services/dataflow/resource_dataflow_job.go @@ -372,6 +372,9 @@ func resourceDataflowJobRead(d *schema.ResourceData, meta interface{}) error { if err := d.Set("effective_labels", job.Labels); err != nil { return fmt.Errorf("Error setting effective_labels: %s", err) } + if job.Environment == nil { + return fmt.Errorf("Error accessing Environment proto: proto is nil") + } if err := d.Set("kms_key_name", job.Environment.ServiceKmsKeyName); err != nil { return fmt.Errorf("Error setting kms_key_name: %s", err) } diff --git a/google-beta/services/netapp/resource_netapp_volume_replication.go b/google-beta/services/netapp/resource_netapp_volume_replication.go new file mode 100644 index 0000000000..5116a67738 --- /dev/null +++ b/google-beta/services/netapp/resource_netapp_volume_replication.go @@ -0,0 +1,1119 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package netapp + +import ( + "fmt" + "log" + "reflect" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport" + "github.com/hashicorp/terraform-provider-google-beta/google-beta/verify" +) + +// Custom function to wait for mirrorState target states +func NetAppVolumeReplicationWaitForMirror(d *schema.ResourceData, meta interface{}, targetState string) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + url, err := tpgresource.ReplaceVars(d, config, "{{NetappBasePath}}projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for volume replication: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + for { + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + }) + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("NetappVolumeReplication %q", d.Id())) + } + + log.Printf("[DEBUG] waiting for mirrorState. actual: %v, target: %v", res["mirrorState"], targetState) + + if res["mirrorState"] == targetState { + break + } + + time.Sleep(30 * time.Second) + // This method can potentially run for days, e.g. when setting up a replication for a source volume + // with dozens of TiB of data. Timeout handling yes/no? + } + + return nil +} + +func ResourceNetappVolumeReplication() *schema.Resource { + return &schema.Resource{ + Create: resourceNetappVolumeReplicationCreate, + Read: resourceNetappVolumeReplicationRead, + Update: resourceNetappVolumeReplicationUpdate, + Delete: resourceNetappVolumeReplicationDelete, + + Importer: &schema.ResourceImporter{ + State: resourceNetappVolumeReplicationImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(20 * time.Minute), + Update: schema.DefaultTimeout(20 * time.Minute), + Delete: schema.DefaultTimeout(20 * time.Minute), + }, + + CustomizeDiff: customdiff.All( + tpgresource.SetLabelsDiff, + tpgresource.DefaultProviderProject, + ), + + Schema: map[string]*schema.Schema{ + "location": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `Name of region for this resource. The resource needs to be created in the region of the destination volume.`, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The name of the replication. Needs to be unique per location.`, + }, + "replication_schedule": { + Type: schema.TypeString, + Required: true, + ValidateFunc: verify.ValidateEnum([]string{"EVERY_10_MINUTES", "HOURLY", "DAILY"}), + Description: `Specifies the replication interval. Possible values: ["EVERY_10_MINUTES", "HOURLY", "DAILY"]`, + }, + "volume_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The name of the existing source volume.`, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: `An description of this resource.`, + }, + "destination_volume_parameters": { + Type: schema.TypeList, + Optional: true, + Description: `Destination volume parameters.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "storage_pool": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `Name of an existing storage pool for the destination volume with format: 'projects/{{project}}/locations/{{location}}/storagePools/{{poolId}}'`, + }, + "description": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: `Description for the destination volume.`, + }, + "share_name": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + Description: `Share name for destination volume. If not specified, name of source volume's share name will be used.`, + }, + "volume_id": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + Description: `Name for the destination volume to be created. If not specified, the name of the source volume will be used.`, + }, + }, + }, + }, + "labels": { + Type: schema.TypeMap, + Optional: true, + Description: `Labels as key value pairs. Example: '{ "owner": "Bob", "department": "finance", "purpose": "testing" }' + + +**Note**: This field is non-authoritative, and will only manage the labels present in your configuration. +Please refer to the field 'effective_labels' for all of the labels present on the resource.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: `Create time of the active directory. A timestamp in RFC3339 UTC "Zulu" format. Examples: "2023-06-22T09:13:01.617Z".`, + }, + "destination_volume": { + Type: schema.TypeString, + Computed: true, + Description: `Full resource name of destination volume with format: 'projects/{{project}}/locations/{{location}}/volumes/{{volumeId}}'`, + }, + "effective_labels": { + Type: schema.TypeMap, + Computed: true, + Description: `All of labels (key/value pairs) present on the resource in GCP, including the labels configured through Terraform, other clients and services.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "healthy": { + Type: schema.TypeBool, + Computed: true, + Description: `Condition of the relationship. Can be one of the following: + - true: The replication relationship is healthy. It has not missed the most recent scheduled transfer. + - false: The replication relationship is not healthy. It has missed the most recent scheduled transfer.`, + }, + "mirror_state": { + Type: schema.TypeString, + Computed: true, + Description: `Indicates the state of the mirror between source and destination volumes. Depending on the amount of data +in your source volume, PREPARING phase can take hours or days. mirrorState = MIRRORED indicates your baseline +transfer ended and destination volume became accessible read-only. TRANSFERRING means a MIRRORED volume +currently receives an update. Updated every 5 minutes.`, + }, + "role": { + Type: schema.TypeString, + Computed: true, + Description: `Reverting a replication can swap source and destination volume roles. This field indicates if the 'location' hosts +the source or destination volume. For resume and revert and resume operations it is critical to understand +which volume is the source volume, since it will overwrite changes done to the destination volume.`, + }, + "source_volume": { + Type: schema.TypeString, + Computed: true, + Description: `Full resource name of source volume with format: 'projects/{{project}}/locations/{{location}}/volumes/{{volumeId}}'`, + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: `Indicates the state of replication resource. State of the mirror itself is indicated in mirrorState.`, + }, + "state_details": { + Type: schema.TypeString, + Computed: true, + Description: `State details of the replication resource.`, + }, + "terraform_labels": { + Type: schema.TypeMap, + Computed: true, + Description: `The combination of labels configured directly on the resource + and default labels configured on the provider.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "transfer_stats": { + Type: schema.TypeList, + Computed: true, + Description: `Replication transfer statistics. All statistics are updated every 5 minutes.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "lag_duration": { + Type: schema.TypeString, + Computed: true, + Description: `The elapsed time since the creation of the snapshot on the source volume that was last replicated +to the destination volume. Lag time represents the difference in age of the destination volume +data in relation to the source volume data.`, + }, + "last_transfer_bytes": { + Type: schema.TypeString, + Computed: true, + Description: `Size of last completed transfer in bytes.`, + }, + "last_transfer_duration": { + Type: schema.TypeString, + Computed: true, + Description: `Time taken during last completed transfer.`, + }, + "last_transfer_end_time": { + Type: schema.TypeString, + Computed: true, + Description: `Time when last transfer completed. A timestamp in RFC3339 UTC "Zulu" format. Examples: "2023-06-22T09:13:01.617Z".`, + }, + "last_transfer_error": { + Type: schema.TypeString, + Computed: true, + Description: `A message describing the cause of the last transfer failure.`, + }, + "total_transfer_duration": { + Type: schema.TypeString, + Computed: true, + Description: `Total time taken so far during current transfer.`, + }, + "transfer_bytes": { + Type: schema.TypeString, + Computed: true, + Description: `Number of bytes transferred so far in current transfer.`, + }, + "update_time": { + Type: schema.TypeString, + Computed: true, + Description: `Time when progress was updated last. A timestamp in RFC3339 UTC "Zulu" format. Examples: "2023-06-22T09:13:01.617Z".`, + }, + }, + }, + }, + "delete_destination_volume": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: `A destination volume is created as part of replication creation. The destination volume will not became +under Terraform management unless you import it manually. If you delete the replication, this volume +will remain. +Setting this parameter to true will delete the *current* destination volume when destroying the +replication. If you reversed the replication direction, this will be your former source volume! +For production use, it is recommended to keep this parameter false to avoid accidental volume +deletion. Handle with care. Default is false.`, + }, + "replication_enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: `Set to false to stop/break the mirror. Stopping the mirror makes the destination volume read-write +and act independently from the source volume. +Set to true to enable/resume the mirror. WARNING: Resuming a mirror overwrites any changes +done to the destination volume with the content of the source volume.`, + }, + "force_stopping": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: `Only replications with mirror_state=MIRRORED can be stopped. A replication in mirror_state=TRANSFERRING +currently receives an update and stopping the update might be undesirable. Set this parameter to true +to stop anyway. All data transferred to the destination will be discarded and content of destination +volume will remain at the state of the last successful update. Default is false.`, + }, + "wait_for_mirror": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: `Replication resource state is independent of mirror_state. With enough data, it can take many hours +for mirror_state to reach MIRRORED. If you want Terraform to wait for the mirror to finish on +create/stop/resume operations, set this parameter to true. Default is false.`, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + UseJSONNumber: true, + } +} + +func resourceNetappVolumeReplicationCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + replicationScheduleProp, err := expandNetappVolumeReplicationReplicationSchedule(d.Get("replication_schedule"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("replication_schedule"); !tpgresource.IsEmptyValue(reflect.ValueOf(replicationScheduleProp)) && (ok || !reflect.DeepEqual(v, replicationScheduleProp)) { + obj["replicationSchedule"] = replicationScheduleProp + } + destinationVolumeParametersProp, err := expandNetappVolumeReplicationDestinationVolumeParameters(d.Get("destination_volume_parameters"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("destination_volume_parameters"); !tpgresource.IsEmptyValue(reflect.ValueOf(destinationVolumeParametersProp)) && (ok || !reflect.DeepEqual(v, destinationVolumeParametersProp)) { + obj["destinationVolumeParameters"] = destinationVolumeParametersProp + } + descriptionProp, err := expandNetappVolumeReplicationDescription(d.Get("description"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("description"); !tpgresource.IsEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { + obj["description"] = descriptionProp + } + labelsProp, err := expandNetappVolumeReplicationEffectiveLabels(d.Get("effective_labels"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("effective_labels"); !tpgresource.IsEmptyValue(reflect.ValueOf(labelsProp)) && (ok || !reflect.DeepEqual(v, labelsProp)) { + obj["labels"] = labelsProp + } + + url, err := tpgresource.ReplaceVars(d, config, "{{NetappBasePath}}projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications?replicationId={{name}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new VolumeReplication: %#v", obj) + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for VolumeReplication: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutCreate), + }) + if err != nil { + return fmt.Errorf("Error creating VolumeReplication: %s", err) + } + + // Store the ID now + id, err := tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications/{{name}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + err = NetappOperationWaitTime( + config, res, project, "Creating VolumeReplication", userAgent, + d.Timeout(schema.TimeoutCreate)) + + if err != nil { + // The resource didn't actually create + d.SetId("") + return fmt.Errorf("Error waiting to create VolumeReplication: %s", err) + } + + if d.Get("wait_for_mirror").(bool) == true { + // Wait for mirrorState=MIRRORED before treating the resource as created + err = NetAppVolumeReplicationWaitForMirror(d, meta, "MIRRORED") + if err != nil { + return fmt.Errorf("Error waiting for volume replication to reach mirror_state==MIRRORED: %s", err) + } + } + + log.Printf("[DEBUG] Finished creating VolumeReplication %q: %#v", d.Id(), res) + + return resourceNetappVolumeReplicationRead(d, meta) +} + +func resourceNetappVolumeReplicationRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + url, err := tpgresource.ReplaceVars(d, config, "{{NetappBasePath}}projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for VolumeReplication: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + }) + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("NetappVolumeReplication %q", d.Id())) + } + + // Explicitly set virtual fields to default values if unset + if _, ok := d.GetOkExists("delete_destination_volume"); !ok { + if err := d.Set("delete_destination_volume", false); err != nil { + return fmt.Errorf("Error setting delete_destination_volume: %s", err) + } + } + if _, ok := d.GetOkExists("replication_enabled"); !ok { + if err := d.Set("replication_enabled", true); err != nil { + return fmt.Errorf("Error setting replication_enabled: %s", err) + } + } + if _, ok := d.GetOkExists("force_stopping"); !ok { + if err := d.Set("force_stopping", false); err != nil { + return fmt.Errorf("Error setting force_stopping: %s", err) + } + } + if _, ok := d.GetOkExists("wait_for_mirror"); !ok { + if err := d.Set("wait_for_mirror", false); err != nil { + return fmt.Errorf("Error setting wait_for_mirror: %s", err) + } + } + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading VolumeReplication: %s", err) + } + + if err := d.Set("state", flattenNetappVolumeReplicationState(res["state"], d, config)); err != nil { + return fmt.Errorf("Error reading VolumeReplication: %s", err) + } + if err := d.Set("state_details", flattenNetappVolumeReplicationStateDetails(res["stateDetails"], d, config)); err != nil { + return fmt.Errorf("Error reading VolumeReplication: %s", err) + } + if err := d.Set("role", flattenNetappVolumeReplicationRole(res["role"], d, config)); err != nil { + return fmt.Errorf("Error reading VolumeReplication: %s", err) + } + if err := d.Set("replication_schedule", flattenNetappVolumeReplicationReplicationSchedule(res["replicationSchedule"], d, config)); err != nil { + return fmt.Errorf("Error reading VolumeReplication: %s", err) + } + if err := d.Set("mirror_state", flattenNetappVolumeReplicationMirrorState(res["mirrorState"], d, config)); err != nil { + return fmt.Errorf("Error reading VolumeReplication: %s", err) + } + if err := d.Set("create_time", flattenNetappVolumeReplicationCreateTime(res["createTime"], d, config)); err != nil { + return fmt.Errorf("Error reading VolumeReplication: %s", err) + } + if err := d.Set("destination_volume", flattenNetappVolumeReplicationDestinationVolume(res["destinationVolume"], d, config)); err != nil { + return fmt.Errorf("Error reading VolumeReplication: %s", err) + } + if err := d.Set("transfer_stats", flattenNetappVolumeReplicationTransferStats(res["transferStats"], d, config)); err != nil { + return fmt.Errorf("Error reading VolumeReplication: %s", err) + } + if err := d.Set("labels", flattenNetappVolumeReplicationLabels(res["labels"], d, config)); err != nil { + return fmt.Errorf("Error reading VolumeReplication: %s", err) + } + if err := d.Set("source_volume", flattenNetappVolumeReplicationSourceVolume(res["sourceVolume"], d, config)); err != nil { + return fmt.Errorf("Error reading VolumeReplication: %s", err) + } + if err := d.Set("healthy", flattenNetappVolumeReplicationHealthy(res["healthy"], d, config)); err != nil { + return fmt.Errorf("Error reading VolumeReplication: %s", err) + } + if err := d.Set("description", flattenNetappVolumeReplicationDescription(res["description"], d, config)); err != nil { + return fmt.Errorf("Error reading VolumeReplication: %s", err) + } + if err := d.Set("terraform_labels", flattenNetappVolumeReplicationTerraformLabels(res["labels"], d, config)); err != nil { + return fmt.Errorf("Error reading VolumeReplication: %s", err) + } + if err := d.Set("effective_labels", flattenNetappVolumeReplicationEffectiveLabels(res["labels"], d, config)); err != nil { + return fmt.Errorf("Error reading VolumeReplication: %s", err) + } + + return nil +} + +func resourceNetappVolumeReplicationUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for VolumeReplication: %s", err) + } + billingProject = project + + obj := make(map[string]interface{}) + replicationScheduleProp, err := expandNetappVolumeReplicationReplicationSchedule(d.Get("replication_schedule"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("replication_schedule"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, replicationScheduleProp)) { + obj["replicationSchedule"] = replicationScheduleProp + } + destinationVolumeParametersProp, err := expandNetappVolumeReplicationDestinationVolumeParameters(d.Get("destination_volume_parameters"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("destination_volume_parameters"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, destinationVolumeParametersProp)) { + obj["destinationVolumeParameters"] = destinationVolumeParametersProp + } + descriptionProp, err := expandNetappVolumeReplicationDescription(d.Get("description"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("description"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { + obj["description"] = descriptionProp + } + labelsProp, err := expandNetappVolumeReplicationEffectiveLabels(d.Get("effective_labels"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("effective_labels"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, labelsProp)) { + obj["labels"] = labelsProp + } + + url, err := tpgresource.ReplaceVars(d, config, "{{NetappBasePath}}projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications/{{name}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating VolumeReplication %q: %#v", d.Id(), obj) + updateMask := []string{} + + if d.HasChange("replication_schedule") { + updateMask = append(updateMask, "replicationSchedule") + } + + if d.HasChange("destination_volume_parameters") { + updateMask = append(updateMask, "destinationVolumeParameters") + } + + if d.HasChange("description") { + updateMask = append(updateMask, "description") + } + + if d.HasChange("effective_labels") { + updateMask = append(updateMask, "labels") + } + // updateMask is a URL parameter but not present in the schema, so ReplaceVars + // won't set it + url, err = transport_tpg.AddQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")}) + if err != nil { + return err + } + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + // if updateMask is empty we are not updating anything so skip the post + if len(updateMask) > 0 { + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PATCH", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutUpdate), + }) + + if err != nil { + return fmt.Errorf("Error updating VolumeReplication %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating VolumeReplication %q: %#v", d.Id(), res) + } + + err = NetappOperationWaitTime( + config, res, project, "Updating VolumeReplication", userAgent, + d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return err + } + } + + // Manage stopping and resuming a mirror + + var obj2 map[string]interface{} + do_change := false + var action string + var targetState string + // state transitions + // there can be a glitch is a transfer starts/ends between reading mirrorState + // and sending the action. This will be very rare. No workaround. + if d.Get("replication_enabled").(bool) == true { + switch d.Get("mirror_state").(string) { + case "STOPPED": + // replication_enabled==true, mirrorState==STOPPED -> resume + action = "resume" + targetState = "MIRRORED" + do_change = true + default: + // replication_enabled==true, mirrorState!=STOPPED -> NOOP + do_change = false + } + } else { + switch d.Get("mirror_state").(string) { + case "MIRRORED": + // replication_enabled==false, mirrorState==MIRRORED -> stop + action = "stop" + targetState = "STOPPED" + do_change = true + case "TRANSFERRING": + // replication_enabled==false, mirrorState==TRANSFERRING -> force stop + // User needs to add force_stopping = true, otherwise will receive error + action = "stop" + targetState = "STOPPED" + do_change = true + case "PREPARING": + // replication_enabled==false, mirrorState==PREPARING -> stop + // Currently cannot be stopped. User will receive following error: + // Error code 3, message: invalid request error: "Replication in preparing state. Please wait until replication is in 'READY' STATE and try again later.". + // User needs to wait until mirrorState=MIRRORED + action = "stop" + targetState = "STOPPED" + do_change = true + default: + // replication_enabled==false, mirrorState==STOPPED -> NOOP + do_change = false + } + + if do_change == true && d.Get("force_stopping").(bool) == true { + obj2 = map[string]interface{}{ + "force": true, + } + } + } + + if do_change { + // We need to send STOP/RESUME API calls + rawurl, err := tpgresource.ReplaceVars(d, config, "{{NetappBasePath}}projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications/{{name}}:"+action) + if err != nil { + return err + } + + res2, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: billingProject, + RawURL: rawurl, + UserAgent: userAgent, + Body: obj2, + Timeout: d.Timeout(schema.TimeoutUpdate), + }) + if err != nil { + return fmt.Errorf("Error stopping/resuming replication %q: %s", d.Id(), err) + } + + err = NetappOperationWaitTime( + config, res2, project, "volume replication "+action, userAgent, + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + + // If user specified to wait for mirror operations, wait to reach target state + if d.Get("wait_for_mirror").(bool) == true { + err = NetAppVolumeReplicationWaitForMirror(d, meta, targetState) + if err != nil { + return fmt.Errorf("Error waiting for volume replication to reach mirror_state==%s: %s", targetState, err) + } + } + } + return resourceNetappVolumeReplicationRead(d, meta) +} + +func resourceNetappVolumeReplicationDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for VolumeReplication: %s", err) + } + billingProject = project + + url, err := tpgresource.ReplaceVars(d, config, "{{NetappBasePath}}projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications/{{name}}") + if err != nil { + return err + } + + var obj map[string]interface{} + // A replication can only be deleted if mirrorState==STOPPED + // We are about to delete the replication and need to stop the mirror before. + // FYI: Stopping a PREPARING mirror currently doesn't work. User have to wait until + // mirror reaches MIRRORED. + if d.Get("mirror_state") != "STOPPED" { + rawurl, err := tpgresource.ReplaceVars(d, config, "{{NetappBasePath}}projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications/{{name}}:stop") + if err != nil { + return err + } + + reso, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: billingProject, + RawURL: rawurl, + UserAgent: userAgent, + // We delete anyway, so lets always use force stop + Body: map[string]interface{}{ + "force": true, + }, + Timeout: d.Timeout(schema.TimeoutUpdate), + }) + if err != nil { + return fmt.Errorf("Error stopping volume replication %q before deleting it: %s", d.Id(), err) + } + + err = NetappOperationWaitTime( + config, reso, project, "Deleting volume replication", userAgent, + d.Timeout(schema.TimeoutDelete)) + if err != nil { + return err + } + } + log.Printf("[DEBUG] Deleting VolumeReplication %q", d.Id()) + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "DELETE", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutDelete), + }) + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, "VolumeReplication") + } + + err = NetappOperationWaitTime( + config, res, project, "Deleting VolumeReplication", userAgent, + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + // A replication CREATE also created a destination volume + // A user can chooses to delete the destination volume after deleting the replication + if d.Get("delete_destination_volume").(bool) == true { + log.Printf("[DEBUG] delete_destination_volume is true. Deleting destination volume %v", d.Get("destination_volume")) + destination_volume := d.Get("destination_volume").(string) + del_url, err := tpgresource.ReplaceVars(d, config, "{{NetappBasePath}}"+destination_volume+"?force=true") + if err != nil { + return err + } + + var obj map[string]interface{} + res_del, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "DELETE", + Project: billingProject, + RawURL: del_url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutDelete), + }) + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, "Volume") + } + + err = NetappOperationWaitTime( + config, res_del, project, "Deleting destination volume", userAgent, + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Finished deleting destination Volume %q: %#v", destination_volume, res_del) + } + + log.Printf("[DEBUG] Finished deleting VolumeReplication %q: %#v", d.Id(), res) + return nil +} + +func resourceNetappVolumeReplicationImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*transport_tpg.Config) + if err := tpgresource.ParseImportId([]string{ + "^projects/(?P[^/]+)/locations/(?P[^/]+)/volumes/(?P[^/]+)/replications/(?P[^/]+)$", + "^(?P[^/]+)/(?P[^/]+)/(?P[^/]+)/(?P[^/]+)$", + "^(?P[^/]+)/(?P[^/]+)/(?P[^/]+)$", + }, d, config); err != nil { + return nil, err + } + + // Replace import id for the resource id + id, err := tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications/{{name}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + // Explicitly set virtual fields to default values on import + if err := d.Set("delete_destination_volume", false); err != nil { + return nil, fmt.Errorf("Error setting delete_destination_volume: %s", err) + } + if err := d.Set("replication_enabled", true); err != nil { + return nil, fmt.Errorf("Error setting replication_enabled: %s", err) + } + if err := d.Set("force_stopping", false); err != nil { + return nil, fmt.Errorf("Error setting force_stopping: %s", err) + } + if err := d.Set("wait_for_mirror", false); err != nil { + return nil, fmt.Errorf("Error setting wait_for_mirror: %s", err) + } + + return []*schema.ResourceData{d}, nil +} + +func flattenNetappVolumeReplicationState(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenNetappVolumeReplicationStateDetails(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenNetappVolumeReplicationRole(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenNetappVolumeReplicationReplicationSchedule(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenNetappVolumeReplicationMirrorState(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + // Actual state of replication_enabled depends on mirrorState. let's update it. + // This is to pickup manual user STOP/RESUME operations on the replication. + if v == nil { + return v + } + + if v.(string) == "STOPPED" { + if err := d.Set("replication_enabled", false); err != nil { + return fmt.Errorf("Error setting replication_enabled: %s", err) + } + } else { + if err := d.Set("replication_enabled", true); err != nil { + return fmt.Errorf("Error setting replication_enabled: %s", err) + } + } + log.Printf("[DEBUG] value of replication_state : %v", d.Get("replication_enabled")) + + return v +} + +func flattenNetappVolumeReplicationCreateTime(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenNetappVolumeReplicationDestinationVolume(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenNetappVolumeReplicationTransferStats(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["transfer_bytes"] = + flattenNetappVolumeReplicationTransferStatsTransferBytes(original["transferBytes"], d, config) + transformed["total_transfer_duration"] = + flattenNetappVolumeReplicationTransferStatsTotalTransferDuration(original["totalTransferDuration"], d, config) + transformed["last_transfer_bytes"] = + flattenNetappVolumeReplicationTransferStatsLastTransferBytes(original["lastTransferBytes"], d, config) + transformed["last_transfer_duration"] = + flattenNetappVolumeReplicationTransferStatsLastTransferDuration(original["lastTransferDuration"], d, config) + transformed["lag_duration"] = + flattenNetappVolumeReplicationTransferStatsLagDuration(original["lagDuration"], d, config) + transformed["update_time"] = + flattenNetappVolumeReplicationTransferStatsUpdateTime(original["updateTime"], d, config) + transformed["last_transfer_end_time"] = + flattenNetappVolumeReplicationTransferStatsLastTransferEndTime(original["lastTransferEndTime"], d, config) + transformed["last_transfer_error"] = + flattenNetappVolumeReplicationTransferStatsLastTransferError(original["lastTransferError"], d, config) + return []interface{}{transformed} +} +func flattenNetappVolumeReplicationTransferStatsTransferBytes(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenNetappVolumeReplicationTransferStatsTotalTransferDuration(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenNetappVolumeReplicationTransferStatsLastTransferBytes(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenNetappVolumeReplicationTransferStatsLastTransferDuration(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenNetappVolumeReplicationTransferStatsLagDuration(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenNetappVolumeReplicationTransferStatsUpdateTime(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenNetappVolumeReplicationTransferStatsLastTransferEndTime(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenNetappVolumeReplicationTransferStatsLastTransferError(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenNetappVolumeReplicationLabels(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return v + } + + transformed := make(map[string]interface{}) + if l, ok := d.GetOkExists("labels"); ok { + for k := range l.(map[string]interface{}) { + transformed[k] = v.(map[string]interface{})[k] + } + } + + return transformed +} + +func flattenNetappVolumeReplicationSourceVolume(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenNetappVolumeReplicationHealthy(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenNetappVolumeReplicationDescription(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenNetappVolumeReplicationTerraformLabels(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return v + } + + transformed := make(map[string]interface{}) + if l, ok := d.GetOkExists("terraform_labels"); ok { + for k := range l.(map[string]interface{}) { + transformed[k] = v.(map[string]interface{})[k] + } + } + + return transformed +} + +func flattenNetappVolumeReplicationEffectiveLabels(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func expandNetappVolumeReplicationReplicationSchedule(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandNetappVolumeReplicationDestinationVolumeParameters(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedStoragePool, err := expandNetappVolumeReplicationDestinationVolumeParametersStoragePool(original["storage_pool"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedStoragePool); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["storagePool"] = transformedStoragePool + } + + transformedVolumeId, err := expandNetappVolumeReplicationDestinationVolumeParametersVolumeId(original["volume_id"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedVolumeId); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["volumeId"] = transformedVolumeId + } + + transformedShareName, err := expandNetappVolumeReplicationDestinationVolumeParametersShareName(original["share_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedShareName); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["shareName"] = transformedShareName + } + + transformedDescription, err := expandNetappVolumeReplicationDestinationVolumeParametersDescription(original["description"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDescription); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["description"] = transformedDescription + } + + return transformed, nil +} + +func expandNetappVolumeReplicationDestinationVolumeParametersStoragePool(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandNetappVolumeReplicationDestinationVolumeParametersVolumeId(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandNetappVolumeReplicationDestinationVolumeParametersShareName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandNetappVolumeReplicationDestinationVolumeParametersDescription(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandNetappVolumeReplicationDescription(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandNetappVolumeReplicationEffectiveLabels(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]string, error) { + if v == nil { + return map[string]string{}, nil + } + m := make(map[string]string) + for k, val := range v.(map[string]interface{}) { + m[k] = val.(string) + } + return m, nil +} diff --git a/google-beta/services/netapp/resource_netapp_volume_replication_generated_test.go b/google-beta/services/netapp/resource_netapp_volume_replication_generated_test.go new file mode 100644 index 0000000000..bfa4e30d36 --- /dev/null +++ b/google-beta/services/netapp/resource_netapp_volume_replication_generated_test.go @@ -0,0 +1,155 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package netapp_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + + "github.com/hashicorp/terraform-provider-google-beta/google-beta/acctest" + "github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport" +) + +func TestAccNetappVolumeReplication_netappVolumeReplicationCreateExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "network_name": acctest.BootstrapSharedServiceNetworkingConnection(t, "gcnv-network-config-1", acctest.ServiceNetworkWithParentService("netapp.servicenetworking.goog")), + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckNetappVolumeReplicationDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccNetappVolumeReplication_netappVolumeReplicationCreateExample(context), + }, + { + ResourceName: "google_netapp_volume_replication.test_replication", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"destination_volume_parameters", "location", "volume_name", "name", "delete_destination_volume", "replication_enabled", "force_stopping", "wait_for_mirror", "labels", "terraform_labels"}, + }, + }, + }) +} + +func testAccNetappVolumeReplication_netappVolumeReplicationCreateExample(context map[string]interface{}) string { + return acctest.Nprintf(` + +data "google_compute_network" "default" { + name = "%{network_name}" +} + +resource "google_netapp_storage_pool" "source_pool" { + name = "tf-test-source-pool%{random_suffix}" + location = "us-central1" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_storage_pool" "destination_pool" { + name = "tf-test-destination-pool%{random_suffix}" + location = "us-west2" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_volume" "source_volume" { + location = google_netapp_storage_pool.source_pool.location + name = "tf-test-source-volume%{random_suffix}" + capacity_gib = 100 + share_name = "tf-test-source-volume%{random_suffix}" + storage_pool = google_netapp_storage_pool.source_pool.name + protocols = [ + "NFSV3" + ] + deletion_policy = "FORCE" +} + +resource "google_netapp_volume_replication" "test_replication" { + depends_on = [google_netapp_volume.source_volume] + location = google_netapp_volume.source_volume.location + volume_name = google_netapp_volume.source_volume.name + name = "tf-test-test-replication%{random_suffix}" + replication_schedule = "EVERY_10_MINUTES" + description = "This is a replication resource" + destination_volume_parameters { + storage_pool = google_netapp_storage_pool.destination_pool.id + volume_id = "tf-test-destination-volume%{random_suffix}" + # Keeping the share_name of source and destination the same + # simplifies implementing client failover concepts + share_name = "tf-test-source-volume%{random_suffix}" + description = "This is a replicated volume" + } + # WARNING: Setting delete_destination_volume to true, will delete the + # CURRENT destination volume if the replication is deleted. Omit the field + # or set delete_destination_volume=false to avoid accidental volume deletion. + delete_destination_volume = true + wait_for_mirror = true +} +`, context) +} + +func testAccCheckNetappVolumeReplicationDestroyProducer(t *testing.T) func(s *terraform.State) error { + return func(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "google_netapp_volume_replication" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := acctest.GoogleProviderConfig(t) + + url, err := tpgresource.ReplaceVarsForTest(config, rs, "{{NetappBasePath}}projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + if config.BillingProject != "" { + billingProject = config.BillingProject + } + + _, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: billingProject, + RawURL: url, + UserAgent: config.UserAgent, + }) + if err == nil { + return fmt.Errorf("NetappVolumeReplication still exists at %s", url) + } + } + + return nil + } +} diff --git a/google-beta/services/netapp/resource_netapp_volume_replication_test.go b/google-beta/services/netapp/resource_netapp_volume_replication_test.go new file mode 100644 index 0000000000..4e1859c94b --- /dev/null +++ b/google-beta/services/netapp/resource_netapp_volume_replication_test.go @@ -0,0 +1,308 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package netapp_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/hashicorp/terraform-provider-google-beta/google-beta/acctest" +) + +func TestAccNetappVolumeReplication_netappVolumeReplicationCreateExample_update(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "network_name": acctest.BootstrapSharedServiceNetworkingConnection(t, "gcnv-network-config-1", acctest.ServiceNetworkWithParentService("netapp.servicenetworking.goog")), + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckNetappVolumeReplicationDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccNetappVolumeReplication_netappVolumeReplicationCreateExample_basic(context), + }, + { + ResourceName: "google_netapp_volume_replication.test_replication", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"destination_volume_parameters", "location", "volume_name", "name", "delete_destination_volume", "replication_enabled", "force_stopping", "wait_for_mirror", "labels", "terraform_labels"}, + }, + { + Config: testAccNetappVolumeReplication_netappVolumeReplicationCreateExample_stop(context), + }, + { + ResourceName: "google_netapp_volume_replication.test_replication", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"destination_volume_parameters", "location", "volume_name", "name", "delete_destination_volume", "replication_enabled", "force_stopping", "wait_for_mirror", "labels", "terraform_labels"}, + }, + { + Config: testAccNetappVolumeReplication_netappVolumeReplicationCreateExample_resume(context), + }, + { + ResourceName: "google_netapp_volume_replication.test_replication", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"destination_volume_parameters", "location", "volume_name", "name", "delete_destination_volume", "replication_enabled", "force_stopping", "wait_for_mirror", "labels", "terraform_labels"}, + }, + { + Config: testAccNetappVolumeReplication_netappVolumeReplicationCreateExample_update(context), + }, + { + ResourceName: "google_netapp_volume_replication.test_replication", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"destination_volume_parameters", "location", "volume_name", "name", "delete_destination_volume", "replication_enabled", "force_stopping", "wait_for_mirror", "labels", "terraform_labels"}, + }, + }, + }) +} + +// Basic replication +func testAccNetappVolumeReplication_netappVolumeReplicationCreateExample_basic(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_compute_network" "default" { + name = "%{network_name}" +} + +resource "google_netapp_storage_pool" "source_pool" { + name = "tf-test-source-pool%{random_suffix}" + location = "us-central1" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_storage_pool" "destination_pool" { + name = "tf-test-destination-pool%{random_suffix}" + location = "us-west2" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_volume" "source_volume" { + location = google_netapp_storage_pool.source_pool.location + name = "tf-test-source-volume%{random_suffix}" + capacity_gib = 100 + share_name = "tf-test-source-volume%{random_suffix}" + storage_pool = google_netapp_storage_pool.source_pool.name + protocols = [ + "NFSV3" + ] + deletion_policy = "FORCE" +} + +resource "google_netapp_volume_replication" "test_replication" { + depends_on = [google_netapp_volume.source_volume] + location = google_netapp_volume.source_volume.location + volume_name = google_netapp_volume.source_volume.name + name = "tf-test-test-replication%{random_suffix}" + replication_schedule = "EVERY_10_MINUTES" + destination_volume_parameters { + storage_pool = google_netapp_storage_pool.destination_pool.id + volume_id = "tf-test-destination-volume%{random_suffix}" + # Keeping the share_name of source and destination the same + # simplifies implementing client failover concepts + share_name = "tf-test-source-volume%{random_suffix}" + description = "This is a replicated volume" + } + delete_destination_volume = true + wait_for_mirror = true +} +`, context) +} + +// Update parameters +func testAccNetappVolumeReplication_netappVolumeReplicationCreateExample_update(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_compute_network" "default" { + name = "%{network_name}" +} + +resource "google_netapp_storage_pool" "source_pool" { + name = "tf-test-source-pool%{random_suffix}" + location = "us-central1" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_storage_pool" "destination_pool" { + name = "tf-test-destination-pool%{random_suffix}" + location = "us-west2" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_volume" "source_volume" { + location = google_netapp_storage_pool.source_pool.location + name = "tf-test-source-volume%{random_suffix}" + capacity_gib = 100 + share_name = "tf-test-source-volume%{random_suffix}" + storage_pool = google_netapp_storage_pool.source_pool.name + protocols = [ + "NFSV3" + ] + deletion_policy = "FORCE" +} + +resource "google_netapp_volume_replication" "test_replication" { + depends_on = [google_netapp_volume.source_volume] + location = google_netapp_volume.source_volume.location + volume_name = google_netapp_volume.source_volume.name + name = "tf-test-test-replication%{random_suffix}" + replication_schedule = "EVERY_10_MINUTES" + description = "This is a replication resource" + labels = { + key = "test" + value = "replication" + } + destination_volume_parameters { + storage_pool = google_netapp_storage_pool.destination_pool.id + volume_id = "tf-test-destination-volume%{random_suffix}" + # Keeping the share_name of source and destination the same + # simplifies implementing client failover concepts + share_name = "tf-test-source-volume%{random_suffix}" + description = "This is a replicated volume" + } + replication_enabled = true + delete_destination_volume = true + force_stopping = true + wait_for_mirror = true +} +`, context) +} + +// Stop replication +func testAccNetappVolumeReplication_netappVolumeReplicationCreateExample_stop(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_compute_network" "default" { + name = "%{network_name}" +} + +resource "google_netapp_storage_pool" "source_pool" { + name = "tf-test-source-pool%{random_suffix}" + location = "us-central1" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_storage_pool" "destination_pool" { + name = "tf-test-destination-pool%{random_suffix}" + location = "us-west2" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_volume" "source_volume" { + location = google_netapp_storage_pool.source_pool.location + name = "tf-test-source-volume%{random_suffix}" + capacity_gib = 100 + share_name = "tf-test-source-volume%{random_suffix}" + storage_pool = google_netapp_storage_pool.source_pool.name + protocols = [ + "NFSV3" + ] + deletion_policy = "FORCE" +} + +resource "google_netapp_volume_replication" "test_replication" { + depends_on = [google_netapp_volume.source_volume] + location = google_netapp_volume.source_volume.location + volume_name = google_netapp_volume.source_volume.name + name = "tf-test-test-replication%{random_suffix}" + replication_schedule = "EVERY_10_MINUTES" + description = "This is a replication resource" + labels = { + key = "test" + value = "replication2" + } + destination_volume_parameters { + storage_pool = google_netapp_storage_pool.destination_pool.id + volume_id = "tf-test-destination-volume%{random_suffix}" + # Keeping the share_name of source and destination the same + # simplifies implementing client failover concepts + share_name = "tf-test-source-volume%{random_suffix}" + description = "This is a replicated volume" + } + replication_enabled = false + delete_destination_volume = true + force_stopping = true + wait_for_mirror = true +} +`, context) +} + +// resume replication +func testAccNetappVolumeReplication_netappVolumeReplicationCreateExample_resume(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_compute_network" "default" { + name = "%{network_name}" +} + +resource "google_netapp_storage_pool" "source_pool" { + name = "tf-test-source-pool%{random_suffix}" + location = "us-central1" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_storage_pool" "destination_pool" { + name = "tf-test-destination-pool%{random_suffix}" + location = "us-west2" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_volume" "source_volume" { + location = google_netapp_storage_pool.source_pool.location + name = "tf-test-source-volume%{random_suffix}" + capacity_gib = 100 + share_name = "tf-test-source-volume%{random_suffix}" + storage_pool = google_netapp_storage_pool.source_pool.name + protocols = [ + "NFSV3" + ] + deletion_policy = "FORCE" +} + +resource "google_netapp_volume_replication" "test_replication" { + depends_on = [google_netapp_volume.source_volume] + location = google_netapp_volume.source_volume.location + volume_name = google_netapp_volume.source_volume.name + name = "tf-test-test-replication%{random_suffix}" + replication_schedule = "HOURLY" + description = "This is a replication resource" + labels = { + key = "test" + value = "replication2" + } + destination_volume_parameters { + storage_pool = google_netapp_storage_pool.destination_pool.id + volume_id = "tf-test-destination-volume%{random_suffix}" + # Keeping the share_name of source and destination the same + # simplifies implementing client failover concepts + share_name = "tf-test-source-volume%{random_suffix}" + description = "This is a replicated volume" + } + replication_enabled = true + delete_destination_volume = true + force_stopping = true + wait_for_mirror = true +} +`, context) +} diff --git a/website/docs/d/compute_forwarding_rules.html.markdown b/website/docs/d/compute_forwarding_rules.html.markdown new file mode 100644 index 0000000000..7c930a68a1 --- /dev/null +++ b/website/docs/d/compute_forwarding_rules.html.markdown @@ -0,0 +1,42 @@ +--- +subcategory: "Compute Engine" +description: |- + List forwarding rules in a region of a Google Cloud project. +--- + +# google\_compute\_forwarding\_rules + +List all networks in a specified Google Cloud project. + +## Example Usage + +```tf +data "google_compute_forwarding_rules" "my-forwarding-rules" { + project = "my-cloud-project" + region = "us-central1" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `project` - (Optional) The name of the project. + +* `region` - (Optional) The region you want to get the forwarding rules from. + +These arguments must be set in either the provider or the resouce in order for the information to be queried. + +## Attributes Reference + +In addition to the arguments listed above, the following attributes are exported: + +* `id` - an identifier for the resource with format projects/{{project}}/region/{{region}}/forwardingRules + +* `project` - The project name being queried. + +* `region` - The region being queried. + +* `rules` - This is a list of the forwarding rules in the project. Each forwarding rule will list the backend, description, ip address. name, network, self link, service label, service name, and subnet. + +* `self_link` - The URI of the resource. diff --git a/website/docs/d/compute_machine_types.html.markdown b/website/docs/d/compute_machine_types.html.markdown index b0cc478409..eb42c50544 100644 --- a/website/docs/d/compute_machine_types.html.markdown +++ b/website/docs/d/compute_machine_types.html.markdown @@ -20,7 +20,7 @@ Configure a Google Kubernetes Engine (GKE) cluster with node auto-provisioning, ```hcl data "google_compute_machine_types" "example" { - filter = "name = 'n1-standard-1'" + filter = "name = \"n1-standard-1\"" zone = "us-central1-a" } diff --git a/website/docs/guides/getting_started.html.markdown b/website/docs/guides/getting_started.html.markdown index 4559a54f22..a329a71258 100644 --- a/website/docs/guides/getting_started.html.markdown +++ b/website/docs/guides/getting_started.html.markdown @@ -182,7 +182,7 @@ quota or billing issues which don't seem to apply to you, you may want to set ### Using Terraform Cloud as the Backend You need to use a different [environment variable](https://www.terraform.io/docs/cloud/workspaces/variables.html) name to store your credentials in Terraform Cloud. 1. Create an environment variable called `GOOGLE_CREDENTIALS` in your Terraform Cloud workspace. -2. Remove the newline characters from your JSON key file and then paste the credentials into the environment variable value field. +2. Remove the newline characters from your JSON key file and then paste the credentials into the environment variable value field. (Running `cat CREDENTIALS.json | tr -s '\n' ' '` will remove newline characters from your JSON key file) 3. Mark the variable as **Sensitive** and click **Save variable**. All runs within the workspace will use the `GOOGLE_CREDENTIALS` variable to authenticate with Google Cloud Platform. diff --git a/website/docs/r/cloudfunctions2_function.html.markdown b/website/docs/r/cloudfunctions2_function.html.markdown index 77d139e1a8..cbac3ad893 100644 --- a/website/docs/r/cloudfunctions2_function.html.markdown +++ b/website/docs/r/cloudfunctions2_function.html.markdown @@ -323,7 +323,6 @@ resource "google_cloudfunctions2_function" "function" { } event_trigger { - trigger_region = "us-central1" # The trigger must be in the same location as the bucket event_type = "google.cloud.storage.object.v1.finalized" retry_policy = "RETRY_POLICY_RETRY" service_account_email = google_service_account.account.email diff --git a/website/docs/r/compute_router_nat.html.markdown b/website/docs/r/compute_router_nat.html.markdown index 126eb9557f..7a549f1e1c 100644 --- a/website/docs/r/compute_router_nat.html.markdown +++ b/website/docs/r/compute_router_nat.html.markdown @@ -307,7 +307,7 @@ The following arguments are supported: * `min_ports_per_vm` - (Optional) - Minimum number of ports allocated to a VM from this NAT. + Minimum number of ports allocated to a VM from this NAT. Defaults to 64 for static port allocation and 32 dynamic port allocation if not set. * `max_ports_per_vm` - (Optional) diff --git a/website/docs/r/container_cluster.html.markdown b/website/docs/r/container_cluster.html.markdown index 46fafb9df4..3c72beba2d 100644 --- a/website/docs/r/container_cluster.html.markdown +++ b/website/docs/r/container_cluster.html.markdown @@ -6,22 +6,21 @@ description: |- # google\_container\_cluster --> Visit the [Provision a GKE Cluster (Google Cloud)](https://learn.hashicorp.com/tutorials/terraform/gke?in=terraform/kubernetes&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) Learn tutorial to learn how to provision and interact -with a GKE cluster. +Manages a Google Kubernetes Engine (GKE) cluster. --> See the [Using GKE with Terraform](/docs/providers/google/guides/using_gke_with_terraform.html) -guide for more information about using GKE with Terraform. +To get more information about GKE clusters, see: + * [The API reference](https://cloud.google.com/kubernetes-engine/docs/reference/rest/v1beta1/projects.locations.clusters) + * How-to guides + * [GKE overview](https://cloud.google.com/kubernetes-engine/docs/concepts/kubernetes-engine-overview) + * [About cluster configuration choices](https://cloud.google.com/kubernetes-engine/docs/concepts/types-of-clusters) + * Terraform guidance + * [Using GKE with Terraform](/docs/providers/google/guides/using_gke_with_terraform.html) + * [Provision a GKE Cluster (Google Cloud) Learn tutorial](https://learn.hashicorp.com/tutorials/terraform/gke?in=terraform/kubernetes&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) -Manages a Google Kubernetes Engine (GKE) cluster. For more information see -[the official documentation](https://cloud.google.com/container-engine/docs/clusters) -and [the API reference](https://cloud.google.com/kubernetes-engine/docs/reference/rest/v1beta1/projects.locations.clusters). +-> On version 5.0.0+ of the provider, you must explicitly set `deletion_protection = false` +and run `terraform apply` to write the field to state in order to destroy a cluster. --> **Note**: On version 5.0.0+ of the provider, you must explicitly set `deletion_protection=false` -(and run `terraform apply` to write the field to state) in order to destroy a cluster. -It is recommended to not set this field (or set it to true) until you're ready to destroy. - -~> **Warning:** All arguments and attributes, including basic auth username and -passwords as well as certificate outputs will be stored in the raw state as +~> All arguments and attributes (including certificate outputs) will be stored in the raw state as plaintext. [Read more about sensitive data in state](https://www.terraform.io/language/state/sensitive-data). ## Example Usage - with a separately managed node pool (recommended) @@ -906,6 +905,8 @@ gvnic { * `tags` - (Optional) The list of instance tags applied to all nodes. Tags are used to identify valid sources or targets for network firewalls. +* `resource_manager_tags` - (Optional) A map of resource manager tag keys and values to be attached to the nodes for managing Compute Engine firewalls using Network Firewall Policies. Tags must be according to specifications found [here](https://cloud.google.com/vpc/docs/tags-firewalls-overview#specifications). A maximum of 5 tag key-value pairs can be specified. Existing tags will be replaced with new values. Tags must be in one of the following formats ([KEY]=[VALUE]) 1. `tagKeys/{tag_key_id}=tagValues/{tag_value_id}` 2. `{org_id}/{tag_key_name}={tag_value_name}` 3. `{project_id}/{tag_key_name}={tag_value_name}`. + * `taint` - (Optional) A list of [Kubernetes taints](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) to apply to nodes. This field will only report drift on taint keys that are @@ -999,7 +1000,7 @@ sole_tenant_config { * `count` (Required) - The number of the guest accelerator cards exposed to this instance. * `gpu_driver_installation_config` (Optional) - Configuration for auto installation of GPU driver. Structure is [documented below](#nested_gpu_driver_installation_config). - + * `gpu_partition_size` (Optional) - Size of partitions to create on the GPU. Valid values are described in the NVIDIA mig [user guide](https://docs.nvidia.com/datacenter/tesla/mig-user-guide/#partitioning). * `gpu_sharing_config` (Optional) - Configuration for GPU sharing. Structure is [documented below](#nested_gpu_sharing_config). @@ -1111,6 +1112,8 @@ subnet. See [Private Cluster Limitations](https://cloud.google.com/kubernetes-en for more details. This field only applies to private clusters, when `enable_private_nodes` is `true`. +* `private_endpoint_subnetwork` - (Optional) Subnetwork in cluster's network where master's endpoint will be provisioned. + * `master_global_access_config` (Optional) - Controls cluster master global access settings. If unset, Terraform will no longer manage this field and will not modify the previously-set value. Structure is [documented below](#nested_master_global_access_config). @@ -1121,8 +1124,6 @@ In addition, the `private_cluster_config` allows access to the following read-on * `private_endpoint` - The internal IP address of this cluster's master endpoint. -* `private_endpoint_subnetwork` - Subnetwork in cluster's network where master's endpoint will be provisioned. - * `public_endpoint` - The external IP address of this cluster's master endpoint. !> The Google provider is unable to validate certain configurations of @@ -1346,7 +1347,7 @@ exported: * `node_config.0.effective_taints` - List of kubernetes taints applied to each node. Structure is [documented above](#nested_taint). -* `fleet.0.membership` - The resource name of the fleet Membership resource associated to this cluster with format `//gkehub.googleapis.com/projects/{{project}}/locations/{{location}}/memberships/{{name}}`. See the official doc for [fleet management](https://cloud.google.com/kubernetes-engine/docs/fleets-overview). +* `fleet.0.membership` - The resource name of the fleet Membership resource associated to this cluster with format `//gkehub.googleapis.com/projects/{{project}}/locations/{{location}}/memberships/{{name}}`. See the official doc for [fleet management](https://cloud.google.com/kubernetes-engine/docs/fleets-overview). * `fleet.0.membership_id` - The short name of the fleet membership, extracted from `fleet.0.membership`. You can use this field to configure `membership_id` under [google_gkehub_feature_membership](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/gke_hub_feature_membership). diff --git a/website/docs/r/netapp_volume_replication.html.markdown b/website/docs/r/netapp_volume_replication.html.markdown new file mode 100644 index 0000000000..e3e92820b6 --- /dev/null +++ b/website/docs/r/netapp_volume_replication.html.markdown @@ -0,0 +1,317 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** Type: MMv1 *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in +# .github/CONTRIBUTING.md. +# +# ---------------------------------------------------------------------------- +subcategory: "Google Cloud NetApp Volumes" +description: |- + Volume replication creates an asynchronous mirror of a volume in a different location. +--- + +# google\_netapp\_volume\_replication + +Volume replication creates an asynchronous mirror of a volume in a different location. This capability +lets you use the replicated volume for critical application activity in case of a location-wide outage +or disaster. + +A new destination volume is created as part of the replication resource. It's content is updated on a +schedule with content of the source volume. It can be used as a read-only copy while the mirror is +enabled, or as an independent read-write volume while the mirror is stopped. A destination volume will +also contain the snapshots of the source volume. Resuming a mirror will overwrite all changes on the +destination volume with the content of the source volume. While is mirror is enabled, all configuration +changes done to source or destination volumes are automatically done to both. Please note that the +destination volume is not a resource managed by Terraform. + +Reversing the replication direction is not supported through the provider. + + +To get more information about VolumeReplication, see: + +* [API documentation](https://cloud.google.com/netapp/volumes/docs/reference/rest/v1/projects.locations.volumes.replications) +* How-to Guides + * [Documentation](https://cloud.google.com/netapp/volumes/docs/protect-data/about-volume-replication) + + +## Example Usage - Netapp Volume Replication Create + + +```hcl + +data "google_compute_network" "default" { + name = "test-network" +} + +resource "google_netapp_storage_pool" "source_pool" { + name = "source-pool" + location = "us-central1" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_storage_pool" "destination_pool" { + name = "destination-pool" + location = "us-west2" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_volume" "source_volume" { + location = google_netapp_storage_pool.source_pool.location + name = "source-volume" + capacity_gib = 100 + share_name = "source-volume" + storage_pool = google_netapp_storage_pool.source_pool.name + protocols = [ + "NFSV3" + ] + deletion_policy = "FORCE" +} + +resource "google_netapp_volume_replication" "test_replication" { + depends_on = [google_netapp_volume.source_volume] + location = google_netapp_volume.source_volume.location + volume_name = google_netapp_volume.source_volume.name + name = "test-replication" + replication_schedule = "EVERY_10_MINUTES" + description = "This is a replication resource" + destination_volume_parameters { + storage_pool = google_netapp_storage_pool.destination_pool.id + volume_id = "destination-volume" + # Keeping the share_name of source and destination the same + # simplifies implementing client failover concepts + share_name = "source-volume" + description = "This is a replicated volume" + } + # WARNING: Setting delete_destination_volume to true, will delete the + # CURRENT destination volume if the replication is deleted. Omit the field + # or set delete_destination_volume=false to avoid accidental volume deletion. + delete_destination_volume = true + wait_for_mirror = true +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `replication_schedule` - + (Required) + Specifies the replication interval. + Possible values are: `EVERY_10_MINUTES`, `HOURLY`, `DAILY`. + +* `location` - + (Required) + Name of region for this resource. The resource needs to be created in the region of the destination volume. + +* `volume_name` - + (Required) + The name of the existing source volume. + +* `name` - + (Required) + The name of the replication. Needs to be unique per location. + + +- - - + + +* `labels` - + (Optional) + Labels as key value pairs. Example: `{ "owner": "Bob", "department": "finance", "purpose": "testing" }` + + **Note**: This field is non-authoritative, and will only manage the labels present in your configuration. + Please refer to the field `effective_labels` for all of the labels present on the resource. + +* `destination_volume_parameters` - + (Optional) + Destination volume parameters. + Structure is [documented below](#nested_destination_volume_parameters). + +* `description` - + (Optional) + An description of this resource. + +* `project` - (Optional) The ID of the project in which the resource belongs. + If it is not provided, the provider project is used. + +* `delete_destination_volume` - (Optional) A destination volume is created as part of replication creation. The destination volume will not became +under Terraform management unless you import it manually. If you delete the replication, this volume +will remain. +Setting this parameter to true will delete the *current* destination volume when destroying the +replication. If you reversed the replication direction, this will be your former source volume! +For production use, it is recommended to keep this parameter false to avoid accidental volume +deletion. Handle with care. Default is false. + +* `replication_enabled` - (Optional) Set to false to stop/break the mirror. Stopping the mirror makes the destination volume read-write +and act independently from the source volume. +Set to true to enable/resume the mirror. WARNING: Resuming a mirror overwrites any changes +done to the destination volume with the content of the source volume. + +* `force_stopping` - (Optional) Only replications with mirror_state=MIRRORED can be stopped. A replication in mirror_state=TRANSFERRING +currently receives an update and stopping the update might be undesirable. Set this parameter to true +to stop anyway. All data transferred to the destination will be discarded and content of destination +volume will remain at the state of the last successful update. Default is false. + +* `wait_for_mirror` - (Optional) Replication resource state is independent of mirror_state. With enough data, it can take many hours +for mirror_state to reach MIRRORED. If you want Terraform to wait for the mirror to finish on +create/stop/resume operations, set this parameter to true. Default is false. + + +The `destination_volume_parameters` block supports: + +* `storage_pool` - + (Required) + Name of an existing storage pool for the destination volume with format: `projects/{{project}}/locations/{{location}}/storagePools/{{poolId}}` + +* `volume_id` - + (Optional) + Name for the destination volume to be created. If not specified, the name of the source volume will be used. + +* `share_name` - + (Optional) + Share name for destination volume. If not specified, name of source volume's share name will be used. + +* `description` - + (Optional) + Description for the destination volume. + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are exported: + +* `id` - an identifier for the resource with format `projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications/{{name}}` + +* `state` - + Indicates the state of replication resource. State of the mirror itself is indicated in mirrorState. + +* `state_details` - + State details of the replication resource. + +* `role` - + Reverting a replication can swap source and destination volume roles. This field indicates if the `location` hosts + the source or destination volume. For resume and revert and resume operations it is critical to understand + which volume is the source volume, since it will overwrite changes done to the destination volume. + +* `mirror_state` - + Indicates the state of the mirror between source and destination volumes. Depending on the amount of data + in your source volume, PREPARING phase can take hours or days. mirrorState = MIRRORED indicates your baseline + transfer ended and destination volume became accessible read-only. TRANSFERRING means a MIRRORED volume + currently receives an update. Updated every 5 minutes. + +* `create_time` - + Create time of the active directory. A timestamp in RFC3339 UTC "Zulu" format. Examples: "2023-06-22T09:13:01.617Z". + +* `destination_volume` - + Full resource name of destination volume with format: `projects/{{project}}/locations/{{location}}/volumes/{{volumeId}}` + +* `transfer_stats` - + Replication transfer statistics. All statistics are updated every 5 minutes. + Structure is [documented below](#nested_transfer_stats). + +* `source_volume` - + Full resource name of source volume with format: `projects/{{project}}/locations/{{location}}/volumes/{{volumeId}}` + +* `healthy` - + Condition of the relationship. Can be one of the following: + - true: The replication relationship is healthy. It has not missed the most recent scheduled transfer. + - false: The replication relationship is not healthy. It has missed the most recent scheduled transfer. + +* `terraform_labels` - + The combination of labels configured directly on the resource + and default labels configured on the provider. + +* `effective_labels` - + All of labels (key/value pairs) present on the resource in GCP, including the labels configured through Terraform, other clients and services. + + +The `transfer_stats` block contains: + +* `transfer_bytes` - + (Output) + Number of bytes transferred so far in current transfer. + +* `total_transfer_duration` - + (Output) + Total time taken so far during current transfer. + +* `last_transfer_bytes` - + (Output) + Size of last completed transfer in bytes. + +* `last_transfer_duration` - + (Output) + Time taken during last completed transfer. + +* `lag_duration` - + (Output) + The elapsed time since the creation of the snapshot on the source volume that was last replicated + to the destination volume. Lag time represents the difference in age of the destination volume + data in relation to the source volume data. + +* `update_time` - + (Output) + Time when progress was updated last. A timestamp in RFC3339 UTC "Zulu" format. Examples: "2023-06-22T09:13:01.617Z". + +* `last_transfer_end_time` - + (Output) + Time when last transfer completed. A timestamp in RFC3339 UTC "Zulu" format. Examples: "2023-06-22T09:13:01.617Z". + +* `last_transfer_error` - + (Output) + A message describing the cause of the last transfer failure. + +## Timeouts + +This resource provides the following +[Timeouts](https://developer.hashicorp.com/terraform/plugin/sdkv2/resources/retries-and-customizable-timeouts) configuration options: + +- `create` - Default is 20 minutes. +- `update` - Default is 20 minutes. +- `delete` - Default is 20 minutes. + +## Import + + +VolumeReplication can be imported using any of these accepted formats: + +* `projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications/{{name}}` +* `{{project}}/{{location}}/{{volume_name}}/{{name}}` +* `{{location}}/{{volume_name}}/{{name}}` + + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import VolumeReplication using one of the formats above. For example: + +```tf +import { + id = "projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications/{{name}}" + to = google_netapp_volume_replication.default +} +``` + +When using the [`terraform import` command](https://developer.hashicorp.com/terraform/cli/commands/import), VolumeReplication can be imported using one of the formats above. For example: + +``` +$ terraform import google_netapp_volume_replication.default projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications/{{name}} +$ terraform import google_netapp_volume_replication.default {{project}}/{{location}}/{{volume_name}}/{{name}} +$ terraform import google_netapp_volume_replication.default {{location}}/{{volume_name}}/{{name}} +``` + +## User Project Overrides + +This resource supports [User Project Overrides](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/provider_reference#user_project_override).