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

compute_url_map - dynamic route_rule block(s) #9415

Closed
drandell opened this issue Jun 22, 2021 · 5 comments
Closed

compute_url_map - dynamic route_rule block(s) #9415

drandell opened this issue Jun 22, 2021 · 5 comments
Assignees
Labels

Comments

@drandell
Copy link
Contributor

Community Note

  • Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritize this request.
  • Please do not leave +1 or me too comments, they generate extra noise for issue followers and do not help prioritize the request.
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment.
  • If an issue is assigned to the modular-magician user, it is either in the process of being autogenerated, or is planned to be autogenerated soon. If an issue is assigned to a user, that user is claiming responsibility for the issue. If an issue is assigned to hashibot, a community member has claimed the issue already.

Terraform Version

v1.0.0

Affected Resource(s)

  • google_compute_url_map

Terraform Configuration Files

resource "google_compute_url_map" "url_map" { 
  name            = "load-balancer"
  project         = var.project_id
  default_service = google_compute_backend_service.live_backend_service.id

  host_rule {
    hosts        = [var.domain_name]
    path_matcher = "live"
  }

  path_matcher {
    name            = "live"
    default_service = google_compute_backend_service.live_backend_service.id

    route_rules {
      priority = 1
      service  = google_compute_backend_service.self_service_backend_service.id
      match_rules {
        prefix_match = "/ss"
        ignore_case  = false
      }
    }

    route_rules {
      priority = 2
      match_rules {
        prefix_match = "/geo-admin"
        ignore_case  = false
      }
      url_redirect {
        https_redirect = true
        prefix_redirect = "/ravegeo/admin"
      }
    }


    dynamic "route_rules" {
      for_each = { for service in google_compute_backend_service.geo_backend_service : service.id => service }
      content {
        priority = 3
        service  = route_rules.value.id
        match_rules {
          prefix_match = "/ravegeo"
          ignore_case  = false
        }
      }
    }

    # dynamic "route_rules" {
    #   for_each = { for web_service in google_compute_backend_service.connector_backend_service : web_service.id => web_service }
    #   content {
    #     priority = 4
    #     service  = route_rules.value.id
    #     match_rules {
    #       prefix_match = "/web"
    #       ignore_case  = false
    #     }
    #   }
    # }

    # the above fails, put using this works
    # route_rules {
    #  priority = 4
    #  service  = google_compute_backend_service.connector_backend_service[0].id
    #  match_rules {
    #    prefix_match = "/web"
    #    ignore_case  = false
    #  }
    #}
  }
}

Expected Behavior

The 3 route_rules already exist, the fourth route_rule should be created with the dynamic block.

Actual Behavior

If we comment in the extra dynamic block, then in the resource only the /ss rule is shown as being created (the other 2 rules are marked as being deleted in a plan), the fourth rule (in the dynamic block) does not even appear in the plan.
If we comment in the hard-coded route_rule then it works as expected.

I also tried to merge all 3 services into one, since they are similar;

## variables.tf
variable "routes" {
  type        = list(object({
    name = string
    priority = number
    prefix_match = string
    ignore_case = bool
  }))
  default     = [
    {
      name = "lb-self-service-backend-service"
      priority = 1,
      prefix_match = "/ss",
      ignore_case = false
    },
    {
      name = "lb-geo-backend-service",
      priority = 3,
      prefix_match = "/ravegeo",
      ignore_case = false
    },
    {
      name = "lb-connector-backend-service",
      priority = 4,
      prefix_match = "/web",
      ignore_case = false
    }
  ]
  description = "(Optional) A list of objects containing the routes"
}

## main.tf
locals {
  routes = { for service in var.routes : service.name => service }
}

### inside compute_url_map

route_rules {
      priority = 2
      match_rules {
        prefix_match = "/geo-admin"
        ignore_case  = false
      }
      url_redirect {
        https_redirect = true
        prefix_redirect = "/ravegeo/admin"
      }
    }

### priortiy 1/3/4 supplied 
# dynamic "route_rules" {
    #   for_each = { for service in concat(google_compute_backend_service.self_service_backend_service, google_compute_backend_service.geo-backend_service, google_compute_backend_service.connector_backend_service) : service.id => service } 
    #   content {
    #     priority = local.routes[route_rules.value.name].priority
    #     service  = route_rules.value.id
    #     match_rules {
    #       prefix_match = local.routes[route_rules.value.name].prefix_match
    #       ignore_case  = local.routes[route_rules.value.name].ignore_case
    #     }
    #   }
    # }

Steps to Reproduce

  1. Create 3 backend services
  2. Add the above config and try and create the initial 3 routes using 2 of the services
  3. terraform apply
  4. add in the dynamic block containing the fourth route_rule, it should hopefully break.

Important Factoids

n/a

@drandell drandell added the bug label Jun 22, 2021
@edwardmedia edwardmedia self-assigned this Jun 22, 2021
@edwardmedia
Copy link
Contributor

@drandell what error did you receive? Can you post the full debug log so I can take a close look?

@drandell
Copy link
Contributor Author

drandell commented Jun 23, 2021

Sorry, i didnt get an error as such, but the plan just did not look correct. So if i try and include the 2nd dynamic block, this is the plan output;

# module.load_balancer.module.load_balancer_https[0].google_compute_url_map.url_map will be updated in-place
  ~ resource "google_compute_url_map" "url_map" {
        id                 = "projects/[REDACTED]/global/urlMaps/load-balancer"
        name               = "load-balancer"
        # (6 unchanged attributes hidden)
      ~ path_matcher {
            name            = "live"
            # (1 unchanged attribute hidden)
          ~ route_rules {
              ~ priority = 1 -> (known after apply)
              ~ service  = "[REDACTED]" -> (known after apply)
              + header_action {
                  + request_headers_to_remove  = (known after apply)
                  + response_headers_to_remove = (known after apply)
                  + request_headers_to_add {
                      + header_name  = (known after apply)
                      + header_value = (known after apply)
                      + replace      = (known after apply)
                    }
                  + response_headers_to_add {
                      + header_name  = (known after apply)
                      + header_value = (known after apply)
                      + replace      = (known after apply)
                    }
                }
              ~ match_rules {
                  + full_path_match = (known after apply)
                  ~ ignore_case     = false -> (known after apply)
                  ~ prefix_match    = "/ss" -> (known after apply)
                  + regex_match     = (known after apply)
                  + header_matches {
                      + exact_match   = (known after apply)
                      + header_name   = (known after apply)
                      + invert_match  = (known after apply)
                      + prefix_match  = (known after apply)
                      + present_match = (known after apply)
                      + regex_match   = (known after apply)
                      + suffix_match  = (known after apply)
                      + range_match {
                          + range_end   = (known after apply)
                          + range_start = (known after apply)
                        }
                    }
                  + metadata_filters {
                      + filter_match_criteria = (known after apply)
                      + filter_labels {
                          + name  = (known after apply)
                          + value = (known after apply)
                        }
                    }
                  + query_parameter_matches {
                      + exact_match   = (known after apply)
                      + name          = (known after apply)
                      + present_match = (known after apply)
                      + regex_match   = (known after apply)
                    }
                }
              + route_action {
                  + cors_policy {
                      + allow_credentials    = (known after apply)
                      + allow_headers        = (known after apply)
                      + allow_methods        = (known after apply)
                      + allow_origin_regexes = (known after apply)
                      + allow_origins        = (known after apply)
                      + disabled             = (known after apply)
                      + expose_headers       = (known after apply)
                      + max_age              = (known after apply)
                    }
                  + fault_injection_policy {
                      + abort {
                          + http_status = (known after apply)
                          + percentage  = (known after apply)
                        }
                      + delay {
                          + percentage = (known after apply)
                          + fixed_delay {
                              + nanos   = (known after apply)
                              + seconds = (known after apply)
                            }
                        }
                    }
                  + request_mirror_policy {
                      + backend_service = (known after apply)
                    }
                  + retry_policy {
                      + num_retries      = (known after apply)
                      + retry_conditions = (known after apply)
                      + per_try_timeout {
                          + nanos   = (known after apply)
                          + seconds = (known after apply)
                        }
                    }
                  + timeout {
                      + nanos   = (known after apply)
                      + seconds = (known after apply)
                    }
                  + url_rewrite {
                      + host_rewrite        = (known after apply)
                      + path_prefix_rewrite = (known after apply)
                    }
                  + weighted_backend_services {
                      + backend_service = (known after apply)
                      + weight          = (known after apply)
                      + header_action {
                          + request_headers_to_remove  = (known after apply)
                          + response_headers_to_remove = (known after apply)
                          + request_headers_to_add {
                              + header_name  = (known after apply)
                              + header_value = (known after apply)
                              + replace      = (known after apply)
                            }
                          + response_headers_to_add {
                              + header_name  = (known after apply)
                              + header_value = (known after apply)
                              + replace      = (known after apply)
                            }
                        }
                    }
                }
              + url_redirect {
                  + host_redirect          = (known after apply)
                  + https_redirect         = (known after apply)
                  + path_redirect          = (known after apply)
                  + prefix_redirect        = (known after apply)
                  + redirect_response_code = (known after apply)
                  + strip_query            = (known after apply)
                }
            }
          - route_rules {
              - priority = 2 -> null
              - service  = "[REDACTED]" -> null
              - match_rules {
                  - ignore_case  = false -> null
                  - prefix_match = "/ravegeo" -> null
                }
            }
          - route_rules {
              - priority = 3 -> null
              - match_rules {
                  - ignore_case  = false -> null
                  - prefix_match = "/geo-admin" -> null
                }
              - url_redirect {
                  - https_redirect  = true -> null
                  - prefix_redirect = "/ravegeo/admin" -> null
                  - strip_query     = false -> null
                }
            }
        }
        # (1 unchanged block hidden)
    }

note: i've redacted some of the service urls and project-id.

This doesnt look right to me (so we never applied).
If we 'hardcode' the service in as the fourth route rule then it works as intended.

Also the second example, with the dynamic handling 3 services and the 1 hardcoded route rule. That also produces the same plan output.

@edwardmedia
Copy link
Contributor

@drandell dynamic handling is part of Terraform core. Shouldn't you file an issue with them to see if they can help?

Since we only have the partial code, it is hard to repro the issue. Can you create a complete test config that can be used to repro the issue?

@drandell
Copy link
Contributor Author

Will come back when i get time to make a repro repo

@github-actions
Copy link

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.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 24, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants