From d16e7e0ad6fefe0bbb2ce9501d1acdb48b5394e7 Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Tue, 28 Jul 2020 20:07:18 +0000 Subject: [PATCH] Add support for cloudrun ports (#3748) * Add support for cloudrun ports * Typo in comments * Add link to api docs to explain optional port parameters * Use existing description convention * Default protcol is TCP, not http * Set default_from_api to true for ports * Make container_port required * Update products/cloudrun/api.yaml Co-authored-by: Sam Levenick Co-authored-by: Sam Levenick Signed-off-by: Modular Magician --- .changelog/3748.txt | 3 + google/resource_cloud_run_service.go | 128 ++++++++++++++++++ google/resource_cloud_run_service_test.go | 3 + .../docs/r/cloud_run_service.html.markdown | 20 +++ 4 files changed, 154 insertions(+) create mode 100644 .changelog/3748.txt diff --git a/.changelog/3748.txt b/.changelog/3748.txt new file mode 100644 index 00000000000..462bc6e9f2b --- /dev/null +++ b/.changelog/3748.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +cloudrun: added `ports` field to `google_cloud_run_service` `templates.spec.containers` +``` diff --git a/google/resource_cloud_run_service.go b/google/resource_cloud_run_service.go index b7e1b5b0c73..b0127df8485 100644 --- a/google/resource_cloud_run_service.go +++ b/google/resource_cloud_run_service.go @@ -252,6 +252,33 @@ https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names`, }, }, }, + "ports": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Description: `List of open ports in the container. +More Info: +https://cloud.google.com/run/docs/reference/rest/v1/RevisionSpec#ContainerPort`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "container_port": { + Type: schema.TypeInt, + Required: true, + Description: `Port number.`, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Description: `Name of the port.`, + }, + "protocol": { + Type: schema.TypeString, + Optional: true, + Description: `Protocol used on port. Defaults to TCP.`, + }, + }, + }, + }, "resources": { Type: schema.TypeList, Computed: true, @@ -1028,6 +1055,7 @@ func flattenCloudRunServiceSpecTemplateSpecContainers(v interface{}, d *schema.R "image": flattenCloudRunServiceSpecTemplateSpecContainersImage(original["image"], d, config), "command": flattenCloudRunServiceSpecTemplateSpecContainersCommand(original["command"], d, config), "env": flattenCloudRunServiceSpecTemplateSpecContainersEnv(original["env"], d, config), + "ports": flattenCloudRunServiceSpecTemplateSpecContainersPorts(original["ports"], d, config), "resources": flattenCloudRunServiceSpecTemplateSpecContainersResources(original["resources"], d, config), }) } @@ -1172,6 +1200,51 @@ func flattenCloudRunServiceSpecTemplateSpecContainersEnvValue(v interface{}, d * return v } +func flattenCloudRunServiceSpecTemplateSpecContainersPorts(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "name": flattenCloudRunServiceSpecTemplateSpecContainersPortsName(original["name"], d, config), + "protocol": flattenCloudRunServiceSpecTemplateSpecContainersPortsProtocol(original["protocol"], d, config), + "container_port": flattenCloudRunServiceSpecTemplateSpecContainersPortsContainerPort(original["containerPort"], d, config), + }) + } + return transformed +} +func flattenCloudRunServiceSpecTemplateSpecContainersPortsName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunServiceSpecTemplateSpecContainersPortsProtocol(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunServiceSpecTemplateSpecContainersPortsContainerPort(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + func flattenCloudRunServiceSpecTemplateSpecContainersResources(v interface{}, d *schema.ResourceData, config *Config) interface{} { if v == nil { return nil @@ -1706,6 +1779,13 @@ func expandCloudRunServiceSpecTemplateSpecContainers(v interface{}, d TerraformR transformed["env"] = transformedEnv } + transformedPorts, err := expandCloudRunServiceSpecTemplateSpecContainersPorts(original["ports"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPorts); val.IsValid() && !isEmptyValue(val) { + transformed["ports"] = transformedPorts + } + transformedResources, err := expandCloudRunServiceSpecTemplateSpecContainersResources(original["resources"], d, config) if err != nil { return nil, err @@ -1917,6 +1997,54 @@ func expandCloudRunServiceSpecTemplateSpecContainersEnvValue(v interface{}, d Te return v, nil } +func expandCloudRunServiceSpecTemplateSpecContainersPorts(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedName, err := expandCloudRunServiceSpecTemplateSpecContainersPortsName(original["name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedName); val.IsValid() && !isEmptyValue(val) { + transformed["name"] = transformedName + } + + transformedProtocol, err := expandCloudRunServiceSpecTemplateSpecContainersPortsProtocol(original["protocol"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedProtocol); val.IsValid() && !isEmptyValue(val) { + transformed["protocol"] = transformedProtocol + } + + transformedContainerPort, err := expandCloudRunServiceSpecTemplateSpecContainersPortsContainerPort(original["container_port"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedContainerPort); val.IsValid() && !isEmptyValue(val) { + transformed["containerPort"] = transformedContainerPort + } + + req = append(req, transformed) + } + return req, nil +} + +func expandCloudRunServiceSpecTemplateSpecContainersPortsName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunServiceSpecTemplateSpecContainersPortsProtocol(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunServiceSpecTemplateSpecContainersPortsContainerPort(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func expandCloudRunServiceSpecTemplateSpecContainersResources(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { l := v.([]interface{}) if len(l) == 0 || l[0] == nil { diff --git a/google/resource_cloud_run_service_test.go b/google/resource_cloud_run_service_test.go index 8781993adce..58835313aeb 100644 --- a/google/resource_cloud_run_service_test.go +++ b/google/resource_cloud_run_service_test.go @@ -54,6 +54,9 @@ resource "google_cloud_run_service" "default" { containers { image = "gcr.io/cloudrun/hello" args = ["arrgs"] + ports { + container_port = 8080 + } } container_concurrency = %s timeout_seconds = %s diff --git a/website/docs/r/cloud_run_service.html.markdown b/website/docs/r/cloud_run_service.html.markdown index 6f0dd31b765..865511e6990 100644 --- a/website/docs/r/cloud_run_service.html.markdown +++ b/website/docs/r/cloud_run_service.html.markdown @@ -403,6 +403,12 @@ The `containers` block supports: (Optional) List of environment variables to set in the container. Structure is documented below. +* `ports` - + (Optional) + List of open ports in the container. + More Info: + https://cloud.google.com/run/docs/reference/rest/v1/RevisionSpec#ContainerPort Structure is documented below. + * `resources` - (Optional) Compute Resources required by this container. Used to set values such as max memory @@ -480,6 +486,20 @@ The `env` block supports: exists or not. Defaults to "". +The `ports` block supports: + +* `name` - + (Optional) + Name of the port. + +* `protocol` - + (Optional) + Protocol used on port. Defaults to TCP. + +* `container_port` - + (Required) + Port number. + The `resources` block supports: * `limits` -