-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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 IAP support for backend services #471
Conversation
I think this is more or less good to go, though I might be missing something. @danawillow, whatcha think? |
sorry, one last change, wrote a reciprocal function for flatten to keep in line with previous work. now it should be good! |
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.
Thanks @Cidan! Can you add tests in resource_compute_backend_service_test.go
? I think a test with one step that sets up the backend service with IAP enabled and another that disables IAP would be good.
@@ -37,6 +37,32 @@ func resourceComputeBackendService() *schema.Resource { | |||
MaxItems: 1, | |||
}, | |||
|
|||
"iap": &schema.Schema{ | |||
Type: schema.TypeSet, |
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.
Since it's MaxItems: 1 anyway, I'd recommend just making this a list. Then you can also get rid of the Set fn on line 63
@@ -37,6 +37,32 @@ func resourceComputeBackendService() *schema.Resource { | |||
MaxItems: 1, | |||
}, | |||
|
|||
"iap": &schema.Schema{ | |||
Type: schema.TypeSet, | |||
Elem: &schema.Resource{ |
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.
Most of our other resources have Elem as the last item in the struct. I'm not sure why this resource does it the other way but it would be awesome if you could move this one and change some of the others in this file too!
Schema: map[string]*schema.Schema{ | ||
"enabled": &schema.Schema{ | ||
Type: schema.TypeBool, | ||
Optional: true, |
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 wonder whether we should make this Required, that way people don't accidentally try to enable it by putting in the block but forgetting this field
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 considered this as well. I think about how I work, and commenting out IAP enabled would "disable" it, without having to edit the line and set it as false. What's the standard here?
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.
Something else to consider here, should we even have the enabled flag? Should it be implicit by providing the keys?
func expandIap(configured []interface{}) *compute.BackendServiceIAP { | ||
data := configured[0].(map[string]interface{}) | ||
iap := &compute.BackendServiceIAP{ | ||
Enabled: data["enabled"].(bool), |
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.
Without also setting ForceSendFields
, these three fields won't appear in the request if they're set to empty (or false). It might still work since the outer block will still be there, but if you could check that you can disable iap using this code that would be great.
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 will double check and also add tests.
@@ -203,6 +229,7 @@ func resourceComputeBackendServiceRead(d *schema.ResourceData, meta interface{}) | |||
d.Set("self_link", service.SelfLink) | |||
d.Set("backend", flattenBackends(service.Backends)) | |||
d.Set("connection_draining_timeout_sec", service.ConnectionDraining.DrainingTimeoutSec) | |||
d.Set("iap", flattenIap(service.Iap)) |
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.
will service.Iap ever be 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.
Only in-so-far as the service object doesn't initialize it a few lines up from here. I'll add a check in the flatten function.
Removed the enabled flag for IAP IAP is now enabled when the client id and secret are set IAP now correctly disables when IAP stanza is removed Client secret is now correctly hashed against the secret hash stored on the server
Alright, had to make quite a few changes here. I'll try to explain my reasoning. There seems to be a bug with the IAP API it self where if you disable IAP and attempt to zero out the secret (ie, remove the entire stanza), only the Enable -> false flag is set and the client secret is not cleared. As a result, the next time an apply is called, the state doesn't match and the apply will happen a second time, correctly clearing the key. So, what I did was removed the notion of enable as an option. When IAP is added to a backend, you must provide a key and secret to enable it. Dropping the stanza for IAP disables it -- this is somewhat cleaner since the API it self requires the id and secret when enabling anyway. I've also made the service send up a default empty IAP object via ForceSendFields when the IAP stanza is missing, thus disabling the service if the previous state was enabled. The IAP API never returns the secret once it's sent, only its' SHA256 sum, so I've added a function for comparing the SHA256 sum we get from the API vs the sum of the locally defined key. Tests still to come. |
Tests written and pass acceptance tests. @danawillow, how does it look now? |
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 like this got a bit out-of-date, mind merging master in? Also it looks like the test failed:
$ make testacc TEST=./google TESTARGS='-run=TestAccComputeBackendService_withBackendAndIAP'
==> Checking that code complies with gofmt requirements...
TF_ACC=1 go test ./google -v -run=TestAccComputeBackendService_withBackendAndIAP -timeout 120m
=== RUN TestAccComputeBackendService_withBackendAndIAP
--- FAIL: TestAccComputeBackendService_withBackendAndIAP (92.66s)
testing.go:435: Step 0 error: After applying this step, the plan was not empty:
DIFF:
UPDATE: google_compute_instance_group_manager.foobar
instance_template: "https://www.googleapis.com/compute/v1/projects/graphite-test-danahoffman-tf/global/instanceTemplates/tf-test-louv6hr7uj" => "<computed>"
DESTROY/CREATE: google_compute_instance_template.foobar
can_ip_forward: "false" => "false"
disk.#: "1" => "1"
disk.0.auto_delete: "true" => "true"
disk.0.boot: "true" => "true"
disk.0.device_name: "persistent-disk-0" => "" (forces new resource)
disk.0.disk_type: "pd-standard" => "<computed>"
disk.0.interface: "SCSI" => "<computed>"
disk.0.mode: "READ_WRITE" => "<computed>"
disk.0.source_image: "debian-8-jessie-v20160803" => "debian-8-jessie-v20160803"
disk.0.type: "PERSISTENT" => "<computed>"
machine_type: "n1-standard-1" => "n1-standard-1"
metadata_fingerprint: "zxKvLKF-n4o=" => "<computed>"
name: "tf-test-louv6hr7uj" => "tf-test-louv6hr7uj"
network_interface.#: "1" => "1"
network_interface.0.network: "default" => "default"
network_interface.0.subnetwork_project: "" => "<computed>"
project: "graphite-test-danahoffman-tf" => "<computed>"
scheduling.#: "1" => "<computed>"
self_link: "https://www.googleapis.com/compute/v1/projects/graphite-test-danahoffman-tf/global/instanceTemplates/tf-test-louv6hr7uj" => "<computed>"
tags_fingerprint: "" => "<computed>"
STATE:
google_compute_backend_service.lipsum:
ID = tf-test-0cr9fil17g
backend.# = 1
backend.2480256626.balancing_mode = UTILIZATION
backend.2480256626.capacity_scaler = 1
backend.2480256626.description =
backend.2480256626.group = https://www.googleapis.com/compute/v1/projects/graphite-test-danahoffman-tf/zones/us-central1-f/instanceGroups/tf-test-j0hrqsyeol
backend.2480256626.max_rate = 0
backend.2480256626.max_rate_per_instance = 0
backend.2480256626.max_utilization = 0.8
connection_draining_timeout_sec = 300
description = Hello World 1234
enable_cdn = false
fingerprint = ogC68Pi2rHI=
health_checks.# = 1
health_checks.4210293762 = https://www.googleapis.com/compute/v1/projects/graphite-test-danahoffman-tf/global/httpHealthChecks/tf-test-dx7gfqocw2
iap.# = 1
iap.0.oauth2_client_id = test
iap.0.oauth2_client_secret = test
name = tf-test-0cr9fil17g
port_name = http
protocol = HTTP
self_link = https://www.googleapis.com/compute/v1/projects/graphite-test-danahoffman-tf/global/backendServices/tf-test-0cr9fil17g
session_affinity = NONE
timeout_sec = 10
Dependencies:
google_compute_http_health_check.default
google_compute_instance_group_manager.foobar
google_compute_http_health_check.default:
ID = tf-test-dx7gfqocw2
check_interval_sec = 1
description =
healthy_threshold = 2
host =
name = tf-test-dx7gfqocw2
port = 80
project = graphite-test-danahoffman-tf
request_path = /
self_link = https://www.googleapis.com/compute/v1/projects/graphite-test-danahoffman-tf/global/httpHealthChecks/tf-test-dx7gfqocw2
timeout_sec = 1
unhealthy_threshold = 2
google_compute_instance_group_manager.foobar:
ID = tf-test-j0hrqsyeol
auto_healing_policies.# = 0
base_instance_name = foobar
description =
fingerprint = 8ibhjaSOFBE=
instance_group = https://www.googleapis.com/compute/v1/projects/graphite-test-danahoffman-tf/zones/us-central1-f/instanceGroups/tf-test-j0hrqsyeol
instance_template = https://www.googleapis.com/compute/v1/projects/graphite-test-danahoffman-tf/global/instanceTemplates/tf-test-louv6hr7uj
name = tf-test-j0hrqsyeol
named_port.# = 0
project = graphite-test-danahoffman-tf
self_link = https://www.googleapis.com/compute/v1/projects/graphite-test-danahoffman-tf/zones/us-central1-f/instanceGroupManagers/tf-test-j0hrqsyeol
target_pools.# = 0
target_size = 1
update_strategy = RESTART
zone = us-central1-f
Dependencies:
google_compute_instance_template.foobar
google_compute_instance_template.foobar:
ID = tf-test-louv6hr7uj
can_ip_forward = false
description =
disk.# = 1
disk.0.auto_delete = true
disk.0.boot = true
disk.0.device_name = persistent-disk-0
disk.0.disk_name =
disk.0.disk_size_gb = 0
disk.0.disk_type = pd-standard
disk.0.interface = SCSI
disk.0.mode = READ_WRITE
disk.0.source =
disk.0.source_image = debian-8-jessie-v20160803
disk.0.type = PERSISTENT
instance_description =
machine_type = n1-standard-1
metadata.% = 0
metadata_fingerprint = zxKvLKF-n4o=
name = tf-test-louv6hr7uj
network_interface.# = 1
network_interface.0.access_config.# = 0
network_interface.0.network = default
network_interface.0.network_ip =
network_interface.0.subnetwork =
network_interface.0.subnetwork_project =
project = graphite-test-danahoffman-tf
scheduling.# = 1
scheduling.0.automatic_restart = true
scheduling.0.on_host_maintenance = MIGRATE
scheduling.0.preemptible = false
self_link = https://www.googleapis.com/compute/v1/projects/graphite-test-danahoffman-tf/global/instanceTemplates/tf-test-louv6hr7uj
FAIL
exit status 1
FAIL github.com/terraform-providers/terraform-provider-google/google 92.894s
make: *** [testacc] Error 1
|
||
func flattenIap(iap *compute.BackendServiceIAP) []map[string]interface{} { | ||
if iap == nil { | ||
return make([]map[string]interface{}, 1, 1) |
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.
just curious, why 1, 1?
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.
Because this is called directly on the ResourceData object via .Set, which will always have at most one IAP object. I'm not sure why the IAP API has this as a list -- perhaps future expansion of IAP options and credentials?
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.
Sorry, my question wasn't "why is this a list?", but "why is it being initialized with 1, 1 instead of 0, 1?"
0, 1 will have the list not have anything in it, whereas 1, 1 will have the list have an empty map in it (see https://play.golang.org/p/SFCt6l5Fsw). The flatten
functions for all other resources are currently using 0, 1.
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.
Ah, my apologies, I misread your question. Will adjust, :)
"oauth2_client_id": iap.Oauth2ClientId, | ||
"oauth2_client_secret": iap.Oauth2ClientSecretSha256, | ||
} | ||
result = append(result, iapMap) |
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.
Totally up to you, but a shorter way to do this would be:
iapMap := map[string]interface{}{
// properties
}
return []map[string]interface{}{iapMap}
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 hadn't considered that -- I agree, much cleaner.
@@ -326,6 +379,13 @@ func expandBackendService(d *schema.ResourceData) compute.BackendService { | |||
service := compute.BackendService{ | |||
Name: d.Get("name").(string), | |||
HealthChecks: healthChecks, | |||
Iap: &compute.BackendServiceIAP{ | |||
ForceSendFields: []string{"Enabled", "Oauth2ClientId", "Oauth2ClientSecret"}, |
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.
Can you add a comment as to why Iap
has to be set (and why the three fields are force)?
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!
}, | ||
}) | ||
|
||
if svc.TimeoutSec != 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.
Eh, I don't necessarily mind having these checks here but they're not really what the test is testing.
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.
Other tests do this too, even though it's not relevant (and I agree, I found it odd), so I decided to leave in for this test as well.
@@ -273,6 +314,38 @@ func testAccCheckComputeBackendServiceExists(n string, svc *compute.BackendServi | |||
} | |||
} | |||
|
|||
func testAccCheckComputeBackendServiceExistsWithIAP(n string, svc *compute.BackendService) 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.
Instead of a full testcheck here you could just use the existing one and then do checks on the returned svc like you're already doing.
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 actually prefer the testcheck personally versus the svc object test, as it feels more complete -- I actually would rather rip the svc checks out since they seem like they aren't really needed.
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.
In what way is it more complete? Both ways would do the exact same thing, except one is duplicating a lot of code between functions and the other is calling an existing function.
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 see your point. Will adjust.
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.
Actually, I take it back. I started to implement the svc object check when I remembered why I don't like it, particularly for multi-stage tests. The svc object is ambiguous in both timing, and in scope. When is the svc object actually set? After destruction? Before? What about when you have multiple stages, which stage are we checking, IAP being set, or not? The answer may seem obvious, and it is (it's after all tests run), but it's not explicit and self contained. What we end up with is two different ways to validate a test run, and picking which way to test now follows a branching pattern (single stage? test svc. multi stage? test all but last stage with svc, and other stages with a check function).
It makes more sense to me to follow a single pattern -- check each stage with a function and be explicit about intent.
timeout_sec = %v | ||
|
||
backend { | ||
group = "${google_compute_instance_group_manager.foobar.instance_group}" |
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.
can you fix the formatting here? I think it's a tabs vs spaces issue
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!
@danawillow That's interesting on the test failure -- I can't reproduce it. Any guidance on your setup?
|
|
||
func flattenIap(iap *compute.BackendServiceIAP) []map[string]interface{} { | ||
if iap == nil { | ||
return make([]map[string]interface{}, 1, 1) |
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.
Sorry, my question wasn't "why is this a list?", but "why is it being initialized with 1, 1 instead of 0, 1?"
0, 1 will have the list not have anything in it, whereas 1, 1 will have the list have an empty map in it (see https://play.golang.org/p/SFCt6l5Fsw). The flatten
functions for all other resources are currently using 0, 1.
Config: testAccComputeBackendService_withBackend( | ||
serviceName, igName, itName, checkName, 10), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckComputeBackendServiceExists( |
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 checks that the backend service exists, but it doesn't check that IAP actually got disabled (I'd highly recommend just adding that check in below with the other checks on svc
)
@@ -273,6 +314,38 @@ func testAccCheckComputeBackendServiceExists(n string, svc *compute.BackendServi | |||
} | |||
} | |||
|
|||
func testAccCheckComputeBackendServiceExistsWithIAP(n string, svc *compute.BackendService) 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.
In what way is it more complete? Both ways would do the exact same thing, except one is duplicating a lot of code between functions and the other is calling an existing function.
@danawillow Alright, let's try this one more time! Made the recommended changes, and testing now checks for IAP enabled and disabled properly. |
Thanks @Cidan! |
Signed-off-by: Modular Magician <[email protected]>
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! |
WIP PR, do not merge yet
Adds IAP support for #444