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

Add support for GPU fields in Cloud Run Service v1 and v2 api #8216

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changelog/11597.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
```release-note:enhancement
cloudrun: added `nvidia.com/gpu` to `resources` and `node_selector` to `google_cloud_run_service` resource (beta)
```
```release-note:enhancement
cloudrunv2: added `nvidia.com/gpu` to `resources` and `node_selector` to `google_cloud_run_v2_service` resource (beta)
```
32 changes: 32 additions & 0 deletions google-beta/services/cloudrun/resource_cloud_run_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,14 @@ might be configured in the container image.`,
requests per container of the Revision. If not specified or 0, defaults to 80 when
requested CPU >= 1 and defaults to 1 when requested CPU < 1.`,
},
"node_selector": {
Type: schema.TypeMap,
Optional: true,
Description: `Node Selector describes the hardware requirements of the resources.
Use the following node selector keys to configure features on a Revision:
- 'run.googleapis.com/accelerator' sets the [type of GPU](https://cloud.google.com/run/docs/configuring/services/gpu) required by the Revision to run.`,
Elem: &schema.Schema{Type: schema.TypeString},
},
"service_account_name": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -1776,6 +1784,8 @@ func flattenCloudRunServiceSpecTemplateSpec(v interface{}, d *schema.ResourceDat
transformed := make(map[string]interface{})
transformed["containers"] =
flattenCloudRunServiceSpecTemplateSpecContainers(original["containers"], d, config)
transformed["node_selector"] =
flattenCloudRunServiceSpecTemplateSpecNodeSelector(original["nodeSelector"], d, config)
transformed["container_concurrency"] =
flattenCloudRunServiceSpecTemplateSpecContainerConcurrency(original["containerConcurrency"], d, config)
transformed["timeout_seconds"] =
Expand Down Expand Up @@ -2493,6 +2503,10 @@ func flattenCloudRunServiceSpecTemplateSpecContainersLivenessProbeGrpcService(v
return v
}

func flattenCloudRunServiceSpecTemplateSpecNodeSelector(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
return v
}

func flattenCloudRunServiceSpecTemplateSpecContainerConcurrency(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
// Handles the string fixed64 format
if strVal, ok := v.(string); ok {
Expand Down Expand Up @@ -3241,6 +3255,13 @@ func expandCloudRunServiceSpecTemplateSpec(v interface{}, d tpgresource.Terrafor
transformed["containers"] = transformedContainers
}

transformedNodeSelector, err := expandCloudRunServiceSpecTemplateSpecNodeSelector(original["node_selector"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedNodeSelector); val.IsValid() && !tpgresource.IsEmptyValue(val) {
transformed["nodeSelector"] = transformedNodeSelector
}

transformedContainerConcurrency, err := expandCloudRunServiceSpecTemplateSpecContainerConcurrency(original["container_concurrency"], d, config)
if err != nil {
return nil, err
Expand Down Expand Up @@ -4194,6 +4215,17 @@ func expandCloudRunServiceSpecTemplateSpecContainersLivenessProbeGrpcService(v i
return v, nil
}

func expandCloudRunServiceSpecTemplateSpecNodeSelector(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]string, error) {
if v == nil {
return map[string]string{}, nil
}
m := make(map[string]string)
for k, val := range v.(map[string]interface{}) {
m[k] = val.(string)
}
return m, nil
}

func expandCloudRunServiceSpecTemplateSpecContainerConcurrency(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,72 @@ resource "google_cloud_run_service" "default" {
`, context)
}

func TestAccCloudRunService_cloudRunServiceGpuExample(t *testing.T) {
t.Parallel()

context := map[string]interface{}{
"project": envvar.GetTestProjectFromEnv(),
"random_suffix": acctest.RandString(t, 10),
}

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderBetaFactories(t),
CheckDestroy: testAccCheckCloudRunServiceDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccCloudRunService_cloudRunServiceGpuExample(context),
},
{
ResourceName: "google_cloud_run_service.default",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"location", "metadata.0.annotations", "metadata.0.labels", "metadata.0.terraform_labels", "name"},
},
},
})
}

func testAccCloudRunService_cloudRunServiceGpuExample(context map[string]interface{}) string {
return acctest.Nprintf(`
resource "google_cloud_run_service" "default" {
provider = google-beta
name = "tf-test-cloudrun-srv%{random_suffix}"
location = "us-central1"

metadata {
annotations = {
"run.googleapis.com/launch-stage" = "BETA"
}
}

template {
metadata {
annotations = {
"autoscaling.knative.dev/maxScale": "1"
"run.googleapis.com/cpu-throttling": "false"
}
}
spec {
containers {
image = "gcr.io/cloudrun/hello"
resources {
limits = {
"cpu" = "4"
"memory" = "16Gi"
"nvidia.com/gpu" = "1"
}
}
}
node_selector = {
"run.googleapis.com/accelerator" = "nvidia-l4"
}
}
}
}
`, context)
}

func TestAccCloudRunService_cloudRunServiceSqlExample(t *testing.T) {
t.Parallel()

Expand Down
116 changes: 116 additions & 0 deletions google-beta/services/cloudrun/resource_cloud_run_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1474,3 +1474,119 @@ resource "google_cloud_run_service" "default" {
}
`, name, project)
}

func TestAccCloudRunService_resourcesRequirements(t *testing.T) {
t.Parallel()

project := envvar.GetTestProjectFromEnv()
name := "tftest-cloudrun-" + acctest.RandString(t, 6)

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderBetaFactories(t),
Steps: []resource.TestStep{
{
Config: testAccCloudRunV2Service_cloudrunServiceWithoutGpu(name, project),
},
{
ResourceName: "google_cloud_run_service.default",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"metadata.0.resource_version", "metadata.0.annotations", "metadata.0.labels", "metadata.0.terraform_labels", "status.0.conditions"},
},
{
Config: testAccCloudRunV2Service_cloudrunServiceWithGpu(name, project),
},
{
ResourceName: "google_cloud_run_service.default",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"metadata.0.resource_version", "metadata.0.annotations", "metadata.0.labels", "metadata.0.terraform_labels", "status.0.conditions"},
},
{
Config: testAccCloudRunV2Service_cloudrunServiceWithoutGpu(name, project),
},
{
ResourceName: "google_cloud_run_service.default",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"metadata.0.resource_version", "metadata.0.annotations", "metadata.0.labels", "metadata.0.terraform_labels", "status.0.conditions"},
},
},
})
}

func testAccCloudRunV2Service_cloudrunServiceWithoutGpu(name, project string) string {
return fmt.Sprintf(`
resource "google_cloud_run_service" "default" {
provider = google-beta
name = "%s"
location = "us-central1"

metadata {
namespace = "%s"
}

template {
metadata {
annotations = {
"autoscaling.knative.dev/maxScale": "1"
"run.googleapis.com/cpu-throttling": "false"
}
}
spec {
containers {
image = "gcr.io/cloudrun/hello"
resources {
limits = {
"cpu" = "4"
"memory" = "16Gi"
}
}
}
}
}
}
`, name, project)
}

func testAccCloudRunV2Service_cloudrunServiceWithGpu(name, project string) string {
return fmt.Sprintf(`
resource "google_cloud_run_service" "default" {
provider = google-beta
name = "%s"
location = "us-central1"

metadata {
namespace = "%s"
annotations = {
"run.googleapis.com/launch-stage" = "BETA"
}
}

template {
metadata {
annotations = {
"autoscaling.knative.dev/maxScale": "1"
"run.googleapis.com/cpu-throttling": "false"
}
}
spec {
containers {
image = "gcr.io/cloudrun/hello"
resources {
limits = {
"cpu" = "4"
"memory" = "16Gi"
"nvidia.com/gpu" = "1"
}
}
}
node_selector = {
"run.googleapis.com/accelerator" = "nvidia-l4"
}
}
}
}
`, name, project)
}
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ If omitted, a port number will be chosen and passed to the container through the
Type: schema.TypeMap,
Computed: true,
Optional: true,
Description: `Only memory and CPU are supported. Use key 'cpu' for CPU limit and 'memory' for memory limit. Note: The only supported values for CPU are '1', '2', '4', and '8'. Setting 4 CPU requires at least 2Gi of memory. The values of the map is string form of the 'quantity' k8s type: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go`,
Description: `Only memory, CPU, and nvidia.com/gpu are supported. Use key 'cpu' for CPU limit, 'memory' for memory limit, 'nvidia.com/gpu' for gpu limit. Note: The only supported values for CPU are '1', '2', '4', and '8'. Setting 4 CPU requires at least 2Gi of memory. The values of the map is string form of the 'quantity' k8s type: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go`,
Elem: &schema.Schema{Type: schema.TypeString},
},
"startup_cpu_boost": {
Expand Down Expand Up @@ -479,6 +479,21 @@ All system labels in v1 now have a corresponding field in v2 RevisionTemplate.`,
Description: `Sets the maximum number of requests that each serving instance can receive.
If not specified or 0, defaults to 80 when requested CPU >= 1 and defaults to 1 when requested CPU < 1.`,
},
"node_selector": {
Type: schema.TypeList,
Optional: true,
Description: `Node Selector describes the hardware requirements of the resources.`,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"accelerator": {
Type: schema.TypeString,
Required: true,
Description: `The GPU to attach to an instance. See https://cloud.google.com/run/docs/configuring/services/gpu for configuring GPU.`,
},
},
},
},
"revision": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -1894,6 +1909,8 @@ func flattenCloudRunV2ServiceTemplate(v interface{}, d *schema.ResourceData, con
flattenCloudRunV2ServiceTemplateSessionAffinity(original["sessionAffinity"], d, config)
transformed["service_mesh"] =
flattenCloudRunV2ServiceTemplateServiceMesh(original["serviceMesh"], d, config)
transformed["node_selector"] =
flattenCloudRunV2ServiceTemplateNodeSelector(original["nodeSelector"], d, config)
return []interface{}{transformed}
}
func flattenCloudRunV2ServiceTemplateRevision(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
Expand Down Expand Up @@ -2919,6 +2936,23 @@ func flattenCloudRunV2ServiceTemplateServiceMeshMesh(v interface{}, d *schema.Re
return v
}

func flattenCloudRunV2ServiceTemplateNodeSelector(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
if v == nil {
return nil
}
original := v.(map[string]interface{})
if len(original) == 0 {
return nil
}
transformed := make(map[string]interface{})
transformed["accelerator"] =
flattenCloudRunV2ServiceTemplateNodeSelectorAccelerator(original["accelerator"], d, config)
return []interface{}{transformed}
}
func flattenCloudRunV2ServiceTemplateNodeSelectorAccelerator(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
return v
}

func flattenCloudRunV2ServiceTraffic(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
if v == nil {
return v
Expand Down Expand Up @@ -3390,6 +3424,13 @@ func expandCloudRunV2ServiceTemplate(v interface{}, d tpgresource.TerraformResou
transformed["serviceMesh"] = transformedServiceMesh
}

transformedNodeSelector, err := expandCloudRunV2ServiceTemplateNodeSelector(original["node_selector"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedNodeSelector); val.IsValid() && !tpgresource.IsEmptyValue(val) {
transformed["nodeSelector"] = transformedNodeSelector
}

return transformed, nil
}

Expand Down Expand Up @@ -4672,6 +4713,29 @@ func expandCloudRunV2ServiceTemplateServiceMeshMesh(v interface{}, d tpgresource
return v, nil
}

func expandCloudRunV2ServiceTemplateNodeSelector(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
l := v.([]interface{})
if len(l) == 0 || l[0] == nil {
return nil, nil
}
raw := l[0]
original := raw.(map[string]interface{})
transformed := make(map[string]interface{})

transformedAccelerator, err := expandCloudRunV2ServiceTemplateNodeSelectorAccelerator(original["accelerator"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedAccelerator); val.IsValid() && !tpgresource.IsEmptyValue(val) {
transformed["accelerator"] = transformedAccelerator
}

return transformed, nil
}

func expandCloudRunV2ServiceTemplateNodeSelectorAccelerator(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}

func expandCloudRunV2ServiceTraffic(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
l := v.([]interface{})
req := make([]interface{}, 0, len(l))
Expand Down
Loading