-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Moving secondary_ranges to a key under subnets. #69
Moving secondary_ranges to a key under subnets. #69
Conversation
This commit removes the secondary_ranges map and makes it a required key in the subnets data structure. Although you now have to pass an empty list if your don't want to create a secondary range, this does allow all the configuration of a subnet to be in the same block.
Overall I like this change and think it provides a much better API! Assuming you can get everything to work properly (and pass tests), I'd be happy to approve. |
I've been working on getting tests running locally with |
@tfhartmann The recommended approach is actually to set them in a tfvars file: https://github.com/terraform-google-modules/terraform-google-network/blob/master/CONTRIBUTING.md#terraform-integration-tests You'll have to symlink that tfvars file: #67 Apologies for the painful process here, we're working on improving it. |
🤣 No worries! Thanks! At least now I know I'm not going mad! |
I wasn't able to make these optional, the lookup function when used in the count.index loop, for whatever reason always returned the default value, even when the keys/value was passed as an input to the module. There may be another way to test for this, but I wasn't able to get any of them working. I tried differnt variations of "if has_key, then.." but wasn't able to get it working. Making these parameters required changes the API, making it more explicit (and longer) but has the advantage of actually working, and passing tests 😄
Changing subnets to a object seems to change the behavior of things like the lookup function, the tldr is that I had to make a number of other parameters required as opposed to optional. I also want to add a couple more tests, at least one for description, which I don't think is working quite right in my branch, because of the change to an object. Also a test to verify that a subnets in different regions can have the same name. |
That's unfortunate, I'd really like to avoid making optional params required. Could we see if it's possible to keep the existing map-based approach? Apologies if you stated that up front, but if we're making all optional params required it might be too big of a change. |
@morgante Agreed, it's not ideal. Heres what I'm seeing. I changed the
😠 Maybe I need to loop through it differently then I have been? Honestly I'd love to be able to keep it a list of maps until we are able to pass in defaults to objects. Here's the issue opened to add the ability to add defaults to Object, which frankly would make the whole object VS map thing a non-issue for this module. There are two reasons I'd still like to propose this as a change even if the optional parameters become required. Firstly, currently the module doesn't support having subnets in different regions with the same name. Because Secondly, IMO, the improved API is still worthwhile, especially for sites that have lots of subnets to manage. For example we have ~43 most with at least two secondary networks 😭 I'll keep at it and see if I can get the module working with this config: variable "subnets" {
type = list(object(
{
subnet_name = string
subnet_ip = string
subnet_region = string
subnet_flow_logs = string
subnet_private_access = string
secondary_ranges = list(map(string))
#secondary_ranges = list(object({ range_name = string, ip_cidr_range = string }))
}
))
description = "The list of subnets being created"
} if I can get this working, it would be ideal. Thanks for the reviews and feedback! |
Doh, I think I'm wrong, it's not the inner object type thats causing this result, it's the outer. and that can't be a map because variable "subnets" {
type = list(object( |
Thanks for iterating on this. Does the You could also try switching to the block for_each syntax so we directly construct the secondary_ranges block by iterating through the secondary_ranges value. |
Also, one note:
This is not a desired goal of the module, actually. Our prescriptive guidance is that subnets in different regions should have unique names, so it's probably not going to be supported for a variety of reasons. |
Apparently not with an object? The behavior I'm seeing when setting the variable like this variable "subnets" {
type = list(object(
{
subnet_name = string
subnet_ip = string
subnet_region = string
secondary_ranges = list(object({ range_name = string, ip_cidr_range = string }))
}
))
description = "The list of subnets being created"
} then set my lookups: private_ip_google_access = lookup(var.subnets[count.index], "subnet_private_access", "false")
enable_flow_logs = lookup(var.subnets[count.index], "subnet_flow_logs", "false") The result is that regardless of what is passed into the module, the lookup default always takes precedence. So both are set "false", even if "true" is passed in.
Actually changing |
Updated the subnetwork resource to use a dynamic block, and aslo changed the `secondary_ranges` to be a list of maps VS a list of objects
Adding in a test to confirm that you can create a network with a duplicate name in a differnet region, and that the secondary subnets are correct
I think this is a bit clearer
@morgante So here's where i've gotten to. I wasn't able to make keys in the subnet object optional, or set defaults. It may perhaps be possible, however I wasn't able to figure out how to do it. 😢 I do want to propose the changes in this PR as a contribution to this module though. I totally understand the reservations, and hope you'll consider them, even though it's a pretty significant change to the API, and a major release. I think the API changes, even with the addition of the required keys in the subnets data structure still has some nice value. IMO, having all the subnet and secondary configuration in the same place in the manifest makes managing the networks, and secondary networks clearer especially in installations with more than few networks. In addition, as hashicorp/terraform#22449 progresses, we'll be able to set sane defaults again in that object. I totally agree it's not ideal, as I also prefer modules with opinions and sane defaults. Somewhat related, my observation of how this module as it's currently working, is that it will allow the user to create subnets in different regions with the same name, but currently creates the secondary subnets wrong - when the module does the lookup it sets the secondary networks to whichever subnet name key it finds first in |
I'm definitely willing to consider this change, but it does introduce a significantly different API for users. For now, my inclination is to hold off for the time being and hope Terraform improves objects to allow defaults sometime soon. In particular, what I want to avoid is a scenario where we release this module, force everyone to start being explicit, then have defaults available in the future. So for now I think we're going to hold off and see if core can get some traction.
I'm fine with either, but my concern with allowing them in different regions is that we then have to include the region in the key—making moving subnets between regions more complicated. |
Cool, I'll keep an eye on that issue too.
I'm not sure I understand this concern. Can subnets be moved between regions currently? When I tried it looks like the networks will be destroyed and recreated in a different region. Thanks for taking a look at this! This has been a fun exercise! I'm looking forward to the glorious future when defaults get added to objects! 😃 |
True, I guess the difference is that if we key based on it it will show up as a delete + create rather than a create/delete. Essentially Terraform state location will move. Not a big deal though. |
While looking up something for #70 in the terraform changelog, I noticed that in 0.12.7 the lookup function was enhanced to work on objects! I may bump the TF version in the tests and give it a try 😄 https://github.com/hashicorp/terraform/blob/master/CHANGELOG.md#0127-august-22-2019 |
I didn't realise this PR + for each was already open and worked on, FWIW i ended up doing a fork that does both of those changes using a One thing to point out from that is we can have a slightly cleaner interface (imo) for secondary ranges e.g. subnets = {
belgium-gke = {
subnet_name = "belgium-gke"
subnet_ip = "10.228.0.0/20"
subnet_region = "europe-west1"
subnet_private_access = "true"
subnet_flow_logs = "false"
secondary_ranges = {
test = "10.240.0.0/14"
belgium-gke-services = "10.250.0.0/20"
}
},
} Where secondary_ip_range is generated via: secondary_ip_range = [
for range_name, ip_cidr_range in lookup(each.value, "secondary_ranges", {}) : {
range_name = range_name
ip_cidr_range = ip_cidr_range
}
]
# Alternativety we could also do
dynamic "secondary_ip_range" {
for_each = lookup(each.value, "secondary_ranges", {})
content {
range_name = secondary_ip_range.key
ip_cidr_range = secondary_ip_range.value
}
} |
@Dev25 Yeah, using a map instead of a list of maps would totally sidestep the problem I ran into with objects. I really like this simplification I'm totally going to change it!
|
Hey @tfhartmann! Were you able to try this out by any chance?
@morgante I actually don't have an alternative for what @tfhartmann ran into with the lack of defaults for object values. I opted to still use the object representation, but understand your hesitation. |
Oh boy, it's been so long since I've worked on this branch, I don't actually recall. I put this on hold a bit until #73 and #86 got added. I think changing the secondary networks from a list of maps, to a direct map is just cleaner and I liked it, I don't however recall if it solved the problem I ran into with setting defaults on an object. I suspect not, since (if I recall correct) the reason I needed to use an object in the first place was to be able to have a data structure with a mix of types, So I think you'd still need to use an object to represent a map that included values of both string and map type, but honestly I'm not sure 😢 |
Can we look at getting this interface updated/merged in now?, 2.0.0 was released without it. For reference i have used this specific implementation for more then a couple of months now. secondary_ip_range = [
for range_name, ip_cidr_range in lookup(each.value, "secondary_ranges", {}) : {
range_name = range_name
ip_cidr_range = ip_cidr_range
}
] |
@Dev25 Does that mean you have to define every value for subnets? From my recollection, we never got around the issue of objects lacking defaults. If your map-based approach works without changing the interface for the rest of the module, I'd be happy to review a PR including it. |
Would you like me to refactor this now that 2.0.0 is released? |
@tfhartmann I don't think we ever found a solution to being forced to fill in all values (because objects lack defaults)? |
@morgante I still think this is a worthy of doing and cutting a 3.0.0 release? |
FWIW, the behavior without defaults would track to the UI on other modules in this GitHub org, I was just using terraform-google-modules/terraform-google-cloud-nat and noticed that I had to pass in the object with a |
Especially given the addition of the various subnet_flow_log inputs, I'm still concerned about the verbosity of forcing all values to be configured for all subnets. Especially since the only meaningful gain is not having to keep secondary_ranges in a separate map. |
This PR is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days |
This PR removes secondary_ranges as an input variable and moves it to the subnets list. I wasn't able to use dynamics blocs which was my original intent, the https://www.terraform.io/docs/providers/google/r/compute_subnetwork.html#secondary_ip_range parameter uses attributes as blocks rather then dynamic blocks for backwards compatibility.
I also changed the
subnets
to be a list of objects rather than a list of maps, since I needed to mix and match types. Because I wanted thesecondary_ranges
entry in the object be a list of maps, I had to define it's type, which has the result of making the secondary_ranges key required. Which is a bummer because I know that was just made optional recently.I think this PR will require a bit of clean up, examples and test, but I wanted to open it to highlight my proposed change.
The subnets parameter would look something like this :