forked from GoogleCloudPlatform/magic-modules
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Split handwritten / mmv1 howto guides into separate pages (GoogleClou…
- Loading branch information
Showing
14 changed files
with
678 additions
and
699 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
--- | ||
title: "" | ||
title: "Home" | ||
weight: 0 | ||
type: "docs" | ||
date: 2022-11-14T09:50:49-08:00 | ||
--- | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
title: "How To" | ||
weight: 10 | ||
isSection: true | ||
--- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
--- | ||
title: "Add a handwritten datasource" | ||
weight: 22 | ||
--- | ||
|
||
# Add a handwritten datasource | ||
|
||
**Note** : only handwritten datasources are currently supported | ||
|
||
Datasources are like terraform resources except they don't *create* anything. | ||
They are simply read-only operations that will expose some sort of values needed | ||
for subsequent resource operations. If you're adding a field to an existing | ||
datasource, check the [Resource](#resource) section. Everything there will | ||
be mostly consistent with the type of change you'll need to make. For adding | ||
a new datasource there are 5 steps to doing so. | ||
|
||
1. Create a new datasource declaration file and a corresponding test file | ||
1. Add Schema and Read operation implementation | ||
1. Add the datasource to the `provider.go.erb` index | ||
1. Implement a test which will create and resources and read the corresponding | ||
datasource. | ||
1. Add documentation. | ||
|
||
For creating a datasource based off an existing resource you can [make use of the | ||
schema directly](https://github.com/GoogleCloudPlatform/magic-modules/blob/1d293f7bfadacaa20580874c8e8634827fb99a14/mmv1/third_party/terraform/data_sources/data_source_cloud_run_service.go). | ||
Otherwise [implementing the schema directly](https://github.com/GoogleCloudPlatform/magic-modules/blob/1d293f7bfadacaa20580874c8e8634827fb99a14/mmv1/third_party/terraform/data_sources/data_source_google_compute_address.go), | ||
similar to normal resource creation, is the desired path. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
--- | ||
title: "Add handwritten IAM resources" | ||
weight: 23 | ||
--- | ||
|
||
# Add handwritten IAM resources | ||
|
||
Handwritten IAM support is only recommended for resources that cannot be managed | ||
using [MMv1](/magic-modules/docs/how-to/add-mmv1-iam), | ||
including for handwritten resources, due to the need to manage tests and | ||
documentation by hand. This guidance goes through the motions of adding support | ||
for new handwritten IAM resources, but does not go into the details of the | ||
implementation as any new handwritten IAM resources are expected to be | ||
exceptional. | ||
|
||
IAM resources are implemented using an IAM framework, where you implement an | ||
interface for each parent resource supporting `getIamPolicy`/`setIamPolicy` and | ||
the associated IAM resources that target that parent resource- `_member`, | ||
`_binding`, and `_policy`- are created by the framework. | ||
|
||
To add support for a new target, create a new file in | ||
`mmv1/third_party/terraform/utils` called `iam_{{resource}}.go`, and implement | ||
the `ResourceIamUpdater`, `newResourceIamUpdaterFunc`, `iamPolicyModifyFunc`, | ||
`resourceIdParserFunc` interfaces from | ||
https://github.com/GoogleCloudPlatform/magic-modules/blob/main/mmv1/third_party/terraform/utils/iam.go.erb | ||
in public types, alongside a public `map[string]*schema.Schema` containing all | ||
fields referenced in the resource. | ||
|
||
Once your implementation is complete, add the IAM resources to `provider.go` | ||
inside the `START non-generated IAM resources` block, creating the concrete | ||
resource types using the `ResourceIamMember`, `ResourceIamBinding`, and | ||
`ResourceIamPolicy` functions. For example: | ||
|
||
```go | ||
"google_bigtable_instance_iam_binding": ResourceIamBinding(IamBigtableInstanceSchema, NewBigtableInstanceUpdater, BigtableInstanceIdParseFunc), | ||
"google_bigtable_instance_iam_member": ResourceIamMember(IamBigtableInstanceSchema, NewBigtableInstanceUpdater, BigtableInstanceIdParseFunc), | ||
"google_bigtable_instance_iam_policy": ResourceIamPolicy(IamBigtableInstanceSchema, NewBigtableInstanceUpdater, BigtableInstanceIdParseFunc), | ||
``` | ||
|
||
Following that, write a test for each resource exercising create and update for | ||
both `_policy` and `_binding`, and create for `_member`. No special | ||
accommodations are needed for the IAM test compared to a normal Terraform | ||
resource test. | ||
|
||
Documentation for IAM resources is done using single page per target resource, | ||
rather than a distinct page for each IAM resource level. As most of the page is | ||
standard, you can generally copy and edit an existing handwritten page such as | ||
https://github.com/GoogleCloudPlatform/magic-modules/blob/main/mmv1/third_party/terraform/website/docs/r/bigtable_instance_iam.html.markdown | ||
to write the documentation. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
--- | ||
title: "Add a handwritten test" | ||
weight: 21 | ||
--- | ||
|
||
|
||
# Add a handwritten test | ||
|
||
For handwritten resources and generated resources that need to test update, | ||
handwritten tests must be added. | ||
|
||
Tests are made up of a templated Terraform configuration where unique values | ||
like GCE names are passed in as arguments, and boilerplate to exercise that | ||
configuration. | ||
|
||
The test boilerplate effectively does the following: | ||
|
||
1. Run `terraform apply` on the configuration, waiting for it to succeed and | ||
recording the results in Terraform state | ||
2. Run `terraform plan`, and fail if Terraform detects any drift | ||
3. Clear the resource from state and run `terraform import` on it | ||
4. Deeply compare the original state from `terraform apply` and the `terraform | ||
import` results, returning an error if any values are not identical | ||
5. Destroy all resources in the configuration using `terraform destroy`, | ||
waiting for the destroy command to succeed | ||
6. Call `GET` on the resource, and fail the test if it is still present | ||
|
||
## Simple Tests | ||
|
||
Terraform configurations are stored as string constants wrapped in Go functions | ||
like the following: | ||
|
||
```go | ||
func testAccComputeFirewall_basic(network, firewall string) string { | ||
return fmt.Sprintf(` | ||
resource "google_compute_network" "foobar" { | ||
name = "%s" | ||
auto_create_subnetworks = false | ||
} | ||
resource "google_compute_firewall" "foobar" { | ||
name = "%s" | ||
description = "Resource created for Terraform acceptance testing" | ||
network = google_compute_network.foobar.name | ||
source_tags = ["foo"] | ||
allow { | ||
protocol = "icmp" | ||
} | ||
} | ||
`, network, firewall) | ||
} | ||
``` | ||
|
||
For the most part, you can copy and paste a preexisting test case and modify it. | ||
For example, the following test case is a good reference: | ||
|
||
```go | ||
func TestAccComputeFirewall_noSource(t *testing.T) { | ||
t.Parallel() | ||
|
||
networkName := fmt.Sprintf("tf-test-firewall-%s", randString(t, 10)) | ||
firewallName := fmt.Sprintf("tf-test-firewall-%s", randString(t, 10)) | ||
|
||
vcrTest(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccCheckComputeFirewallDestroyProducer(t), | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccComputeFirewall_noSource(networkName, firewallName), | ||
}, | ||
{ | ||
ResourceName: "google_compute_firewall.foobar", | ||
ImportState: true, | ||
ImportStateVerify: true, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccComputeFirewall_noSource(network, firewall string) string { | ||
return fmt.Sprintf(` | ||
resource "google_compute_network" "foobar" { | ||
name = "%s" | ||
auto_create_subnetworks = false | ||
} | ||
resource "google_compute_firewall" "foobar" { | ||
name = "%s" | ||
description = "Resource created for Terraform acceptance testing" | ||
network = google_compute_network.foobar.name | ||
allow { | ||
protocol = "tcp" | ||
ports = [22] | ||
} | ||
} | ||
`, network, firewall) | ||
} | ||
``` | ||
|
||
## Update tests | ||
|
||
Inside of a test, additional steps can be added in order to transition between | ||
Terraform configurations, updating the stored state as it progresses. This | ||
allows you to exercise update behaviour. This modifies the flow from before: | ||
|
||
1. Start with an empty Terraform state | ||
1. For each `Config` and `ImportState` pair: | ||
1. Run `terraform apply` on the configuration, waiting for it to succeed | ||
and recording the results in Terraform state | ||
1. Run `terraform plan`, and fail if Terraform detects any drift | ||
1. Clear the resource from state and run `terraform import` on it | ||
1. Deeply compare the original state from `terraform apply` and the | ||
`terraform import` results, returning an error if any values are not | ||
identical | ||
1. Destroy all resources in the configuration using `terraform destroy`, | ||
waiting for the destroy command to succeed | ||
1. Call `GET` on the resource, and fail the test if it is still present | ||
|
||
For example: | ||
|
||
```go | ||
func TestAccComputeFirewall_disabled(t *testing.T) { | ||
t.Parallel() | ||
|
||
networkName := fmt.Sprintf("tf-test-firewall-%s", randString(t, 10)) | ||
firewallName := fmt.Sprintf("tf-test-firewall-%s", randString(t, 10)) | ||
|
||
vcrTest(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccCheckComputeFirewallDestroyProducer(t), | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccComputeFirewall_disabled(networkName, firewallName), | ||
}, | ||
{ | ||
ResourceName: "google_compute_firewall.foobar", | ||
ImportState: true, | ||
ImportStateVerify: true, | ||
}, | ||
{ | ||
Config: testAccComputeFirewall_basic(networkName, firewallName), | ||
}, | ||
{ | ||
ResourceName: "google_compute_firewall.foobar", | ||
ImportState: true, | ||
ImportStateVerify: true, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccComputeFirewall_basic(network, firewall string) string { | ||
return fmt.Sprintf(` | ||
resource "google_compute_network" "foobar" { | ||
name = "%s" | ||
auto_create_subnetworks = false | ||
} | ||
resource "google_compute_firewall" "foobar" { | ||
name = "%s" | ||
description = "Resource created for Terraform acceptance testing" | ||
network = google_compute_network.foobar.name | ||
source_tags = ["foo"] | ||
allow { | ||
protocol = "icmp" | ||
} | ||
} | ||
`, network, firewall) | ||
} | ||
|
||
func testAccComputeFirewall_disabled(network, firewall string) string { | ||
return fmt.Sprintf(` | ||
resource "google_compute_network" "foobar" { | ||
name = "%s" | ||
auto_create_subnetworks = false | ||
} | ||
resource "google_compute_firewall" "foobar" { | ||
name = "%s" | ||
description = "Resource created for Terraform acceptance testing" | ||
network = google_compute_network.foobar.name | ||
source_tags = ["foo"] | ||
allow { | ||
protocol = "icmp" | ||
} | ||
disabled = true | ||
} | ||
`, network, firewall) | ||
} | ||
``` | ||
|
||
## Testing Beta Features | ||
|
||
If you worked with a beta feature and had to use beta version guards in a | ||
handwritten resource or set `min_version: beta` in a generated resource, you'll | ||
want to version guard both the test case and configuration by enclosing them in | ||
ERB tags like below. Additionally, if the filename ends in `.go`, rename it to | ||
end in `.go.erb`. | ||
|
||
``` | ||
<% unless version == 'ga' -%> | ||
// test case + config here | ||
<% end -%> | ||
``` | ||
|
||
Otherwise, tests using a beta feature are written exactly the same as tests | ||
using a GA one. Normally to use the beta provider, it's necessary to specify | ||
`provider = google-beta`, as Terraform maps any resources prefixed with | ||
`google_` to the `google` provider by default. However, inside the test | ||
framework, the `google-beta` provider has been aliased as the `google` provider | ||
and that is not necessary. | ||
|
||
Note: You _may_ use version guards to test different configurations between the | ||
GA and beta provider tests, but it's strongly recommended that you write | ||
different test cases instead, even if they're slightly duplicative. |
Oops, something went wrong.