-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Add support for Google Cloud IoT Core registry #970
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for Google Cloud IoT Core registry #970
Conversation
google/provider.go
Outdated
@@ -88,6 +88,7 @@ func Provider() terraform.ResourceProvider { | |||
"google_bigtable_instance": resourceBigtableInstance(), | |||
"google_bigtable_table": resourceBigtableTable(), | |||
"google_cloudfunctions_function": resourceCloudFunctionsFunction(), | |||
"google_cloudiot_registry": resourceCloudiotRegistry(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer CloudIoT
for capitalization consistency - I think most monospace fonts render the I
sufficiently differently from an l
that it probably won't be an issue, and this is what https://github.com/golang/go/wiki/CodeReviewComments#initialisms requests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks much better 👍 Done.
{ | ||
"public_key_certificate" = { | ||
format = "X509_CERTIFICATE_PEM" | ||
certificate = "-----BEGIN CERTIFICATE-----\nMIICnjCCAYYCCQC/5gx7LgJFqTANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZ1\nbnVzZWQwHhcNMTgwMTEyMjAxMzQzWhcNMjMwMTExMjAxMzQzWjARMQ8wDQYDVQQD\nDAZ1bnVzZWQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDStfQvJzmN\nCYLSWpwvTmyCKn8t19cfWZ69wFaB3OSglxXgYe3w9An0QHybDpKITt61PpfsKov3\nEcnzH5IA+Ox+4jUppBL1mSkO/BWig+sd1dG7pQMbGi4nMxW704A0PRUaNIOarOlR\nrNUJZQrsghkMjLayCTJ2HISBBiPnKKB3f3KCc9sDhj2Z7zy7HfeW0apZ1m6xAQCC\neSZNW0IyGIYKTd9F7HEJFzOWg9JHvabbciBEcFWKGVzM8nQr1q8KU8Xi3iN2mpNK\nJkbRLNnqKhvjPyIZ4s4cDSEZN1/OaGQ4XP2mvU03+4UAoMPoJ8IczBKTB0mFxfX8\nlDZZa5IWU9sNAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAHnkTIghRj/cerR9ctji\nkancnjlsdNEuPiVpMj+SOtOH8cvlgl0oWG6segYTVzk4VEHlq3POB67Yjoz829XM\nCEgUxSqGvDrQ7IaPLPryYy8o5azMLnEZDr+Yd6CUKr/pUZzJoZxHj7z3iqeQZnMW\nS6kb6HYvG5PKlJ7+JUIKLou0RQmaM9BQ0Nln/YDRRIerD0MY9k7No2ZEDbywZqQK\nGRIqT+BlN84oHOR44h2RqWhn9O50tkbcmAIKgmeg/mxwmeAm/6hQ8VrOhDHqsFdT\nzh2l6IeCl8EF8MjNrFRcQx21TTqeU6vGIPgM3E0k8PQUc+s+lir8UFsIzKaOFsIh\nuKU=\n-----END CERTIFICATE-----\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider committing this as a file to google/test-fixtures instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
} | ||
} | ||
|
||
func testAccCloudiotRegistryUpdate(n string) resource.TestCheckFunc { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems elaborate - I think it only tests that the state matches the config, which is implicitly part of a test that has config. If the state doesn't match the config post-update, the test will fail even without this method, and so removing this method reduces the difficulty of maintenance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Have removed this function. I was not aware of this internal test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I apologize but I was wrong about this - this is true only when there's an importstate step on the test. If you can add one of those in, these tests will be easier to maintain, but if that's a hassle for unforeseen reasons you can definitely go back to this method. My bad!
{ | ||
public_key_certificate = { | ||
format = "X509_CERTIFICATE_PEM" | ||
certificate = "<CERTIFICATE>" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't hurt to do, maybe, "${file("foo.pem")}" or something like that so the example is more valid?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep! Sounds much better!
google/resource_cloudiot_registry.go
Outdated
Type: schema.TypeList, | ||
Optional: true, | ||
ForceNew: false, | ||
MaxItems: 10, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where does the max of 10 come from?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found the max 10 in the documentation of DeviceRegistry for the credentials
field: "No more than 10 credentials can be bound to a single registry at a time.". Hope I got it right.
google/resource_cloudiot_registry.go
Outdated
|
||
parent := fmt.Sprintf("projects/%s/locations/%s", project, region) | ||
|
||
deviceRegistry := expandDeviceRegistry(d) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, for instance, createDeviceRegistry would be more accurate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
google/resource_cloudiot_registry.go
Outdated
} | ||
d.Set("event_notification_configs", eventConfigs) | ||
} | ||
// to keep the data model lean only changes are pushed if not the default is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you clean up the wording in this comment a bit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
google/resource_cloudiot_registry.go
Outdated
|
||
d.Set("name", res.Id) | ||
|
||
if res.EventNotificationConfigs != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If they are nil, is it right to clear them from the ResourceData object? To stay up to date with, for instance, a deleted config from the console?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I missed that. Now I reset first everything to nil.
google/resource_cloudiot_registry.go
Outdated
return nil | ||
} | ||
|
||
func resourceCloudiotRegistryStateImporter(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to see an import test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
google/resource_cloudiot_registry.go
Outdated
return nil, err | ||
} | ||
|
||
id := fmt.Sprintf("projects/%s/locations/%s/registries/%s", project, region, d.Id()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Problem - in this mode, the user must configure the provider such that the default region and project are the right ones - d.Id() is the only thing you'll have going into the import.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not thought about that. I now ask for full path projects/{project}/locations/{region}/registries/{name}
as id. (see also registry get API).
- Removed default parameters from schema. - Added import test. - Refactored naming of methods (expand->build) - Before read set all parameters of model to nil. - Moved cert data to external file.
…fied regex in import.
Thank you very much for your feedback @ndmckinley! Hope I got now everything right. How does it look now? PS: I added a validation function for the registry id. I hope the function name is ok. This can be reuses later for a device id (see: Creating or editing a device). |
return handleNotFoundError(err, d, fmt.Sprintf("Registry %q", name)) | ||
} | ||
|
||
d.Set("name", res.Id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A comment here which explains that you're speculatively setting all attributes to nil and updating the ones you find later might help.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After some testing I saw that all the nil checks are not necessary. Hope my changes now makes it more readable.
Just to make sure we have the same thing in mind when we read the code:
This is
resource "google_cloudiot_registry" "hello" {
name = "hello"
}
comparable with
{
"id": "hello",
"name": "projects/<...>/locations/<...>/registries/hello",
"mqttConfig": {
"mqttEnabledState": "MQTT_ENABLED"
},
"stateNotificationConfig": {
},
"httpConfig": {
"httpEnabledState": "HTTP_ENABLED"
}
}
because the code ignores default state, mqtt, http config:
google/validation.go
Outdated
@@ -107,3 +109,16 @@ func validateRFC1035Name(min, max int) schema.SchemaValidateFunc { | |||
|
|||
return validateRegexp(fmt.Sprintf("^"+RFC1035NameTemplate+"$", min-2, max-2)) | |||
} | |||
|
|||
func validateCloudIoTId(v interface{}, k string) (warnings []string, errors []error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: "ID" is capitalized when it's short for "identifier"
https://github.com/golang/go/wiki/CodeReviewComments#initialisms
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
google/validation.go
Outdated
@@ -18,6 +19,7 @@ const ( | |||
SubnetworkLinkRegex = "projects/(" + ProjectRegex + ")/regions/(" + RegionRegex + ")/subnetworks/(" + SubnetworkRegex + ")$" | |||
|
|||
RFC1035NameTemplate = "[a-z](?:[-a-z0-9]{%d,%d}[a-z0-9])" | |||
CloudIoTIdRegex = "[a-zA-Z][-a-zA-Z0-9._+~%]{2,254}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this well-tested? The formatter is trying to complain about the % as if it's a format-string specifier and I'm pretty sure it's a formatter bug and not a bug in your code. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unit test is now included. Found bug and yep this seems to be a formatter bug :-)
google/resource_cloudiot_registry.go
Outdated
if err != nil { | ||
return err | ||
} | ||
d.SetId(res.Name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be helpful to do d.SetId(name) before the create call, then do d.SetId(nil) in the event of an error - that way, if the process is killed during the request, the state file will include the resource, and we'll be able to do a Read() on refresh to determine if the call ever actually succeeded.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
It's looking pretty good to me - I made a mistake in the earlier review, sorry for the back-and-forth, but other than that, and these small changes, I'm happy with it. |
- Removed unnecessary nil checks in read function. - Added unit test for cloudiot id validation func. - Added start/end delimiter to cloud iot id regex. - Fixed bug were certficates were missing in read. - Set id to empty if creating a registry fails.
Have implemented the requested changes. Found a bug where the certificates were not properly read into the state, because I forgot the |
Great - I'm running the tests now and if they pass, I'll merge it. |
Hey there, looks like we're getting errors from the tests, which seem to assume a particular setup. Specifically, for
|
The important thing is that the test passes without any special configuration on our side ahead of time - ideally we wouldn't need to manually create a service account and grant it permissions. The best case would be for you to put that service account creation and permission grants into the test terraform config! But if that doesn't work out, let me know how to set it up and I'll see what I can do. :) |
…ervice to publish to pubsub.
Thanks! I'll run the tests again. |
Ouch! This was a RTFM moment. Got it fixed now :-) |
Hey, wonderful, it passes! :) Last thing - the import test is obsolete since your other tests do an import now, and you can remove it without decreasing coverage. After that, I'll merge it. :) |
Do you mean |
Yes, you can remove the entire import_*_test.go file. |
Done. |
Thank you! |
I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues. If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. If you feel I made an error 🤖 🙉 , please reach out to my human friends 👉 [email protected]. Thanks! |
This pull request adds support for the Google Cloud IoT Core registry. The config structure reflects the structure of the registry API. The downside is a lot of nesting in the config. But hopefully it allows a easy extension if the API evolves.