Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

providers/google: Add support for Internal Load Balancing #10453

Merged
merged 16 commits into from
Dec 12, 2016

Conversation

danawillow
Copy link
Contributor

This change enables Internal Load Balancing with the introduction of two new resources: google_compute_region_backend_service and google_compute_health_check.

For all intents and purposes, this is ready for review. I still need to add documentation.

@evandbrown

@cblecker
Copy link
Contributor

cblecker commented Dec 1, 2016

@danawillow: Looks like it needs to be rebased on master, due to vendor updates :)

@danawillow
Copy link
Contributor Author

All right, now it's ready.

Copy link
Contributor

@paddycarver paddycarver left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi! Thanks for the PR! It brings a lot of good stuff, and it's really exciting to be able to add all this to Terraform! My apologies for taking so long to get to it--it's been hard finding a large enough chunk of time for it. I think smaller PRs are easier to review, but sometimes they don't split up easily, so it's always a judgement call. :)

Anyways this looks great! There were a few things I noted, hopefully not too nitpicky, that I could totally be wrong about. But I just wanted to draw your attention to them.

Thanks!

@@ -93,13 +127,24 @@ func resourceComputeForwardingRuleCreate(d *schema.ResourceData, meta interface{
return err
}

ps := d.Get("ports").(*schema.Set).List()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an optional attribute. What if it's not specified--wouldn't this panic?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're totally right, and I learned some new stuff about *ResourceData.Get and *schema.Set.List today. Thanks!

Name: d.Get("name").(string),
PortRange: d.Get("port_range").(string),
Target: d.Get("target").(string),
BackendService: d.Get("backend_service").(string),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here--isn't this nil if it's not set? It's an optional attribute, I think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only required attribute in the whole list is Name, so it seems there's a precedent for doing it this way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're totally right, I misunderstood the situations in which d.Get would return nil. Whoops! Thanks for correcting me. :D

Description: d.Get("description").(string),
LoadBalancingScheme: d.Get("load_balancing_scheme").(string),
Name: d.Get("name").(string),
Network: d.Get("network").(string),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, what if this isn't set?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, you're totally right on this.

PortRange: d.Get("port_range").(string),
Ports: ports,
Subnetwork: d.Get("subnetwork").(string),
Target: d.Get("target").(string),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if this isn't set?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise

Network: d.Get("network").(string),
PortRange: d.Get("port_range").(string),
Ports: ports,
Subnetwork: d.Get("subnetwork").(string),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if this isn't set?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise

},
},

"https_health_check": &schema.Schema{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming nit: do we really need _health_check. as a suffix to all of these, inside of the health_check resource?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the name in the GCP resource (https://cloud.google.com/compute/docs/reference/latest/healthChecks), but I don't mind changing it. I don't think just calling it http or https is a good naming choice though, since to me that feels more like a boolean. it could be http_details, but then it's not much shorter than the current version. What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, these are some good points. My instincts are based on the Go philosophy of not repeating things, but that may be a bad instinct here. Looking at what the aws_route53_health_check resource does (as a close equivalent), it uses a single property for all its health checks, and uses "type" to distinguish them, as you previously had. Though that seems to be a reflection of the underlying API.

Thinking this through, I think you're right. It's probably better to stick closer to the API than to try to abstract the repetition out. Good call. :)

if hchk.Type != "HTTP" {
return fmt.Errorf("HTTP health check declared but type is listed as %s", hchk.Type)
}
httpcheck := v.([]interface{})[0].(map[string]interface{})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may prevent a future crash to check that

  • v.([]interface{}) has at least one item
  • that item isn't nil

before we try to cast it to a map[string]interface{}.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't totally understand this comment, can you give me an example configuration that could cause this issue?

if hchk.Type != "TCP" {
return fmt.Errorf("TCP health check declared but type is listed as %s", hchk.Type)
}
tcpcheck := v.([]interface{})[0].(map[string]interface{})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing in update as in create, re: potentially preventing crashes down the road.

_, err := config.clientCompute.HealthChecks.Get(
config.Project, rs.Primary.ID).Do()
if err == nil {
return fmt.Errorf("HealthCheck still exists")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be nice to log the ID of the health check that still exists, to make manual clean-up and debugging easier. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


# google\_compute\_region\_backend\_service

A Region Backend Service defines a regionally-scoped group of virtual machines that will serve traffic for load balancing.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A link to the docs would be awesome here!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor Author

@danawillow danawillow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review!

@@ -93,13 +127,24 @@ func resourceComputeForwardingRuleCreate(d *schema.ResourceData, meta interface{
return err
}

ps := d.Get("ports").(*schema.Set).List()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name: d.Get("name").(string),
PortRange: d.Get("port_range").(string),
Target: d.Get("target").(string),
BackendService: d.Get("backend_service").(string),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only required attribute in the whole list is Name, so it seems there's a precedent for doing it this way.

Network: d.Get("network").(string),
PortRange: d.Get("port_range").(string),
Ports: ports,
Subnetwork: d.Get("subnetwork").(string),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise

PortRange: d.Get("port_range").(string),
Ports: ports,
Subnetwork: d.Get("subnetwork").(string),
Target: d.Get("target").(string),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise

_, err := config.clientCompute.HealthChecks.Get(
config.Project, rs.Primary.ID).Do()
if err == nil {
return fmt.Errorf("HealthCheck still exists")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


# google\_compute\_region\_backend\_service

A Region Backend Service defines a regionally-scoped group of virtual machines that will serve traffic for load balancing.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Description: d.Get("description").(string),
LoadBalancingScheme: d.Get("load_balancing_scheme").(string),
Name: d.Get("name").(string),
Network: d.Get("network").(string),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise

},
},

"https_health_check": &schema.Schema{
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the name in the GCP resource (https://cloud.google.com/compute/docs/reference/latest/healthChecks), but I don't mind changing it. I don't think just calling it http or https is a good naming choice though, since to me that feels more like a boolean. it could be http_details, but then it's not much shorter than the current version. What do you think?

}
if v, ok := d.GetOk("tcp_health_check"); ok {
if hchk.Type != "TCP" {
return fmt.Errorf("TCP health check declared but type is listed as %s", hchk.Type)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh neat, great idea! Done.

if hchk.Type != "HTTP" {
return fmt.Errorf("HTTP health check declared but type is listed as %s", hchk.Type)
}
httpcheck := v.([]interface{})[0].(map[string]interface{})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't totally understand this comment, can you give me an example configuration that could cause this issue?


## Example Usage

```js
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use tf here (I was surprised, too!):

resource "google_compute_health_check" "default" {
  name = "test"
}

I won't comment on all the others, but they can all be updated :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


timeout_sec = 1
check_interval_sec = 1
type = "TCP"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that this isn't a thing, we should remove it from the example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

* `timeout_sec` - (Optional) The number of seconds to wait before declaring
failure (default 5).

* `type` - (Optional) The type of HealthCheck, either TCP, SSL, HTTP or HTTPS
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably remove this, as it no longer exists. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, good call. Done.

@paddycarver
Copy link
Contributor

Thanks for all the changes and comments. Taught me a few things. :) I added a second, shorter round of reviews, mostly about documentation and examples. Other than that, I couldn't see any problems. :)

I do notice that we can't run TestAccComputeForwardingRule_internalLoadBalancing on our project, probably because it's in beta and we need to be whitelisted. I know @stack72 is working with @evandbrown and others to change the way our GCP tests are run, to take advantage of features like this. Unfortunately, I don't think we can merge this until that happens, as it'll break our tests.

@paddycarver
Copy link
Contributor

Ah, you updated the docs, thanks! Took me too long to investigate. :) Running the tests locally, I'm getting this from make testacc TEST=./builtin/providers/google TESTARGS='-run=TestAccComputeRegionBackendService_*'

==> Checking that code complies with gofmt requirements...
go generate $(go list ./... | grep -v /terraform/vendor/)
2016/12/08 18:08:09 Generated command/internal_plugin_list.go
TF_ACC=1 go test ./builtin/providers/google -v -run=TestAccComputeRegionBackendService_* -timeout 120m
=== RUN   TestAccComputeRegionBackendService_basic
--- PASS: TestAccComputeRegionBackendService_basic (66.68s)
=== RUN   TestAccComputeRegionBackendService_withBackend
--- FAIL: TestAccComputeRegionBackendService_withBackend (0.01s)
	testing.go:265: Step 0 error: Configuration is invalid.

		Warnings: []string(nil)

		Errors: []string{"google_compute_health_check.default: : invalid or unknown key: type"}
	resource_compute_region_backend_service_test.go:66: Expected TimeoutSec == 10, got 0
	resource_compute_region_backend_service_test.go:69: Expected Protocol to be TCP, got ""
	resource_compute_region_backend_service_test.go:72: Expected 1 backend, got 0
=== RUN   TestAccComputeRegionBackendService_withBackendAndUpdate
--- FAIL: TestAccComputeRegionBackendService_withBackendAndUpdate (0.01s)
	testing.go:265: Step 0 error: Configuration is invalid.

		Warnings: []string(nil)

		Errors: []string{"google_compute_health_check.default: : invalid or unknown key: type"}
	resource_compute_region_backend_service_test.go:107: Expected TimeoutSec == 20, got 0
	resource_compute_region_backend_service_test.go:110: Expected Protocol to be TCP, got ""
	resource_compute_region_backend_service_test.go:113: Expected 1 backend, got 0
FAIL
exit status 1
FAIL	github.com/hashicorp/terraform/builtin/providers/google	66.719s
make: *** [testacc] Error 1

Looks like we need to update the tests, too. :( Sorry for the piecemeal review!

@danawillow
Copy link
Contributor Author

danawillow commented Dec 9, 2016

Actually, I'm pretty sure the reason you can't run TestAccComputeForwardingRule_internalLoadBalancing is because I accidentally broke the test. Give it another try :)

(for what it's worth, ILB is in GA now. Even if it were in beta though, I don't think you would need a whitelist- that's just for alpha [citation needed though].)

@paddycarver
Copy link
Contributor

Cool! Sorry for the confusion, I couldn't find solid info on that, and when the test failed with ILB stuff, I just assumed. Whoops. I'll re-run the tests and do a final check. :)

@danawillow
Copy link
Contributor Author

No worries!

@evandbrown
Copy link
Contributor

Thanks @danawillow and @paddyforan for the hard work on this! Excited to see it in the next release!

@paddycarver
Copy link
Contributor

Tests all pass for me. Thanks for sticking with the lengthy review process, and for contributing the features!

@paddycarver paddycarver merged commit 8d046c7 into hashicorp:master Dec 12, 2016
@ghost
Copy link

ghost commented Apr 18, 2020

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 have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@ghost ghost locked and limited conversation to collaborators Apr 18, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants