From e28877b7a22b959e8d72df16e5d4fdd49e1fb9e8 Mon Sep 17 00:00:00 2001 From: Daniil Sharko Date: Wed, 29 Nov 2023 21:03:52 +0300 Subject: [PATCH 1/2] Support storage mounts for serverless containers --- ...data_source_yandex_serverless_container.go | 26 +++++++ yandex/resource_yandex_function_test.go | 7 ++ .../resource_yandex_serverless_container.go | 67 ++++++++++++++++++- ...source_yandex_serverless_container_test.go | 50 ++++++++++++++ 4 files changed, 149 insertions(+), 1 deletion(-) diff --git a/yandex/data_source_yandex_serverless_container.go b/yandex/data_source_yandex_serverless_container.go index 6ee114dc8..2e465ac9d 100644 --- a/yandex/data_source_yandex_serverless_container.go +++ b/yandex/data_source_yandex_serverless_container.go @@ -98,6 +98,32 @@ func dataSourceYandexServerlessContainer() *schema.Resource { }, }, + "storage_mounts": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "mount_point_path": { + Type: schema.TypeString, + Required: true, + }, + "bucket": { + Type: schema.TypeString, + Required: true, + }, + "prefix": { + Type: schema.TypeString, + Optional: true, + }, + "read_only": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, + "image": { Type: schema.TypeList, Computed: true, diff --git a/yandex/resource_yandex_function_test.go b/yandex/resource_yandex_function_test.go index 7c672b887..d13a29682 100644 --- a/yandex/resource_yandex_function_test.go +++ b/yandex/resource_yandex_function_test.go @@ -368,6 +368,13 @@ type testSecretParameters struct { secretValue string } +type testStorageMountParameters struct { + storageMountPointPath string + storageMountBucket string + storageMountPrefix string + storageMountReadOnly bool +} + func testYandexFunctionFull(params testYandexFunctionParameters) string { return fmt.Sprintf(` resource "yandex_function" "test-function" { diff --git a/yandex/resource_yandex_serverless_container.go b/yandex/resource_yandex_serverless_container.go index c9847a1b0..15cf51537 100644 --- a/yandex/resource_yandex_serverless_container.go +++ b/yandex/resource_yandex_serverless_container.go @@ -117,6 +117,31 @@ func resourceYandexServerlessContainer() *schema.Resource { }, }, }, + "storage_mounts": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "mount_point_path": { + Type: schema.TypeString, + Required: true, + }, + "bucket": { + Type: schema.TypeString, + Required: true, + }, + "prefix": { + Type: schema.TypeString, + Optional: true, + }, + "read_only": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, "image": { Type: schema.TypeList, @@ -274,7 +299,7 @@ func resourceYandexServerlessContainerUpdate(d *schema.ResourceData, meta interf lastRevisionPaths := []string{ "memory", "cores", "core_fraction", "execution_timeout", "service_account_id", - "secrets", "image", "concurrency", "connectivity", + "secrets", "image", "concurrency", "connectivity", "storage_mounts", } var revisionUpdatePaths []string for _, p := range lastRevisionPaths { @@ -427,6 +452,31 @@ func expandLastRevision(d *schema.ResourceData) (*containers.DeployContainerRevi } } + if v, ok := d.GetOk("storage_mounts"); ok { + storageMountsList := v.([]interface{}) + + revisionReq.StorageMounts = make([]*containers.StorageMount, len(storageMountsList)) + for i, sm := range storageMountsList { + storageMount := sm.(map[string]interface{}) + + fsm := &containers.StorageMount{} + if mountPointPath, ok := storageMount["mount_point_path"]; ok { + fsm.MountPointPath = mountPointPath.(string) + } + if bucket, ok := storageMount["bucket"]; ok { + fsm.BucketId = bucket.(string) + } + if prefix, ok := storageMount["prefix"]; ok { + fsm.Prefix = prefix.(string) + } + if readOnly, ok := storageMount["read_only"]; ok { + fsm.ReadOnly = readOnly.(bool) + } + + revisionReq.StorageMounts[i] = fsm + } + } + revisionReq.ImageSpec = &containers.ImageSpec{ ImageUrl: d.Get("image.0.url").(string), WorkingDir: d.Get("image.0.work_dir").(string), @@ -484,6 +534,7 @@ func flattenYandexServerlessContainer(d *schema.ResourceData, container *contain d.Set("concurrency", int(revision.Concurrency)) d.Set("service_account_id", revision.ServiceAccountId) d.Set("secrets", flattenRevisionSecrets(revision.Secrets)) + d.Set("storage_mounts", flattenRevisionStorageMounts(revision.StorageMounts)) if revision.Image != nil { m := make(map[string]interface{}) @@ -521,6 +572,20 @@ func flattenRevisionSecrets(secrets []*containers.Secret) []map[string]interface return s } +func flattenRevisionStorageMounts(storageMounts []*containers.StorageMount) []map[string]interface{} { + s := make([]map[string]interface{}, len(storageMounts)) + + for i, storageMount := range storageMounts { + s[i] = map[string]interface{}{ + "mount_point_path": storageMount.MountPointPath, + "bucket": storageMount.BucketId, + "prefix": storageMount.Prefix, + "read_only": storageMount.ReadOnly, + } + } + return s +} + func expandServerlessContainerConnectivity(d *schema.ResourceData) *containers.Connectivity { if id, ok := d.GetOk("connectivity.0.network_id"); ok { return &containers.Connectivity{NetworkId: id.(string)} diff --git a/yandex/resource_yandex_serverless_container_test.go b/yandex/resource_yandex_serverless_container_test.go index 545aec682..600dc57e4 100644 --- a/yandex/resource_yandex_serverless_container_test.go +++ b/yandex/resource_yandex_serverless_container_test.go @@ -177,6 +177,13 @@ func TestAccYandexServerlessContainer_full(t *testing.T) { secretValue: "tf-container-secret-value", } + params.storageMount = testStorageMountParameters{ + storageMountPointPath: "/mount/point/path", + storageMountBucket: "tf-container-bucket", + storageMountPrefix: "tf-container-path", + storageMountReadOnly: false, + } + paramsUpdated := testYandexServerlessContainerParameters{} paramsUpdated.name = acctest.RandomWithPrefix("tf-container-updated") paramsUpdated.desc = acctest.RandomWithPrefix("tf-container-desc-updated") @@ -201,6 +208,13 @@ func TestAccYandexServerlessContainer_full(t *testing.T) { secretValue: "tf-container-secret-value-updated", } + paramsUpdated.storageMount = testStorageMountParameters{ + storageMountPointPath: "/mount/point/path/updated", + storageMountBucket: "tf-container-bucket-updated", + storageMountPrefix: "tf-container-path-updated", + storageMountReadOnly: true, + } + testConfigFunc := func(params testYandexServerlessContainerParameters) resource.TestStep { return resource.TestStep{ Config: testYandexServerlessContainerFull(params), @@ -223,6 +237,10 @@ func TestAccYandexServerlessContainer_full(t *testing.T) { resource.TestCheckResourceAttrSet(serverlessContainerResource, "secrets.0.version_id"), resource.TestCheckResourceAttr(serverlessContainerResource, "secrets.0.key", params.secret.secretKey), resource.TestCheckResourceAttr(serverlessContainerResource, "secrets.0.environment_variable", params.secret.secretEnvVar), + resource.TestCheckResourceAttr(serverlessContainerResource, "storage_mounts.0.mount_point_path", params.storageMount.storageMountPointPath), + resource.TestCheckResourceAttr(serverlessContainerResource, "storage_mounts.0.bucket", params.storageMount.storageMountBucket), + resource.TestCheckResourceAttr(serverlessContainerResource, "storage_mounts.0.prefix", params.storageMount.storageMountPrefix), + resource.TestCheckResourceAttr(serverlessContainerResource, "storage_mounts.0.read_only", params.storageMount.storageMountPrefix), // metadata resource.TestCheckResourceAttrSet(serverlessContainerResource, "folder_id"), resource.TestCheckResourceAttrSet(serverlessContainerResource, "url"), @@ -494,6 +512,7 @@ type testYandexServerlessContainerParameters struct { envVarValue string serviceAccount string secret testSecretParameters + storageMount testStorageMountParameters } func testYandexServerlessContainerFull(params testYandexServerlessContainerParameters) string { @@ -520,6 +539,13 @@ resource "yandex_serverless_container" "test-container" { key = "%s" environment_variable = "%s" } + + storage_mounts { + mount_point_path = "%s" + bucket = yandex_storage_bucket.another-bucket.bucket + prefix = "%s" + read_only = %v + } image { url = "%s" work_dir = "%s" @@ -531,6 +557,26 @@ resource "yandex_serverless_container" "test-container" { } } +resource "yandex_iam_service_account" "s3-test-sa" { + name = "tf-test-sa" +} + +resource "yandex_resourcemanager_folder_iam_member" "sa-editor" { + folder_id = yandex_iam_service_account.s3-test-sa.folder_id + role = "storage.editor" + member = "serviceAccount:${yandex_iam_service_account.s3-test-sa.id}" +} + +resource "yandex_iam_service_account_static_access_key" "sa-static-key" { + service_account_id = yandex_iam_service_account.s3-test-sa.id +} + +resource "yandex_storage_bucket" "another-bucket" { + access_key = yandex_iam_service_account_static_access_key.sa-static-key.access_key + secret_key = yandex_iam_service_account_static_access_key.sa-static-key.secret_key + bucket = "%s" +} + resource "yandex_iam_service_account" "test-account" { name = "%s" } @@ -566,12 +612,16 @@ resource "yandex_lockbox_secret_version" "secret_version" { params.concurrency, params.secret.secretKey, params.secret.secretEnvVar, + params.storageMount.storageMountPointPath, + params.storageMount.storageMountPrefix, + params.storageMount.storageMountReadOnly, params.imageURL, params.workDir, params.command, params.argument, params.envVarKey, params.envVarValue, + params.storageMount.storageMountBucket, params.serviceAccount, params.secret.secretName, params.secret.secretKey, From 6e8fe23c570f9d2810944792a42e98191475008b Mon Sep 17 00:00:00 2001 From: Daniil Sharko Date: Thu, 30 Nov 2023 13:25:50 +0300 Subject: [PATCH 2/2] Support storage mounts for functions --- yandex/resource_yandex_function.go | 70 +++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/yandex/resource_yandex_function.go b/yandex/resource_yandex_function.go index a3af907f7..cfe89b305 100644 --- a/yandex/resource_yandex_function.go +++ b/yandex/resource_yandex_function.go @@ -195,6 +195,32 @@ func resourceYandexFunction() *schema.Resource { }, }, + "storage_mounts": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "mount_point_name": { + Type: schema.TypeString, + Required: true, + }, + "bucket": { + Type: schema.TypeString, + Required: true, + }, + "prefix": { + Type: schema.TypeString, + Optional: true, + }, + "read_only": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, + "connectivity": { Type: schema.TypeList, MaxItems: 1, @@ -358,7 +384,7 @@ func resourceYandexFunctionUpdate(d *schema.ResourceData, meta interface{}) erro lastVersionPaths := []string{ "user_hash", "runtime", "entrypoint", "memory", "execution_timeout", "service_account_id", - "environment", "tags", "package", "content", "secrets", "connectivity", + "environment", "tags", "package", "content", "secrets", "connectivity", "storage_mounts", } var versionPartialPaths []string for _, p := range lastVersionPaths { @@ -534,6 +560,32 @@ func expandLastVersion(d *schema.ResourceData) (*functions.CreateFunctionVersion versionReq.Secrets[i] = fs } } + + if v, ok := d.GetOk("storage_mounts"); ok { + storageMountsList := v.([]interface{}) + + versionReq.StorageMounts = make([]*functions.StorageMount, len(storageMountsList)) + for i, sm := range storageMountsList { + storageMount := sm.(map[string]interface{}) + + fsm := &functions.StorageMount{} + if mountPointName, ok := storageMount["mount_point_name"]; ok { + fsm.MountPointName = mountPointName.(string) + } + if bucket, ok := storageMount["bucket"]; ok { + fsm.BucketId = bucket.(string) + } + if prefix, ok := storageMount["prefix"]; ok { + fsm.Prefix = prefix.(string) + } + if readOnly, ok := storageMount["read_only"]; ok { + fsm.ReadOnly = readOnly.(bool) + } + + versionReq.StorageMounts[i] = fsm + } + } + if connectivity := expandFunctionConnectivity(d); connectivity != nil { versionReq.Connectivity = connectivity } @@ -597,6 +649,8 @@ func flattenYandexFunction(d *schema.ResourceData, function *functions.Function, } d.Set("secrets", flattenFunctionSecrets(version.Secrets)) + d.Set("storage_mounts", flattenVersionStorageMounts(version.StorageMounts)) + return d.Set("tags", tags) } @@ -693,6 +747,20 @@ func flattenFunctionSecrets(secrets []*functions.Secret) []map[string]interface{ return s } +func flattenVersionStorageMounts(storageMounts []*functions.StorageMount) []map[string]interface{} { + s := make([]map[string]interface{}, len(storageMounts)) + + for i, storageMount := range storageMounts { + s[i] = map[string]interface{}{ + "mount_point_name": storageMount.MountPointName, + "bucket": storageMount.BucketId, + "prefix": storageMount.Prefix, + "read_only": storageMount.ReadOnly, + } + } + return s +} + func expandFunctionConnectivity(d *schema.ResourceData) *functions.Connectivity { if id, ok := d.GetOk("connectivity.0.network_id"); ok { return &functions.Connectivity{NetworkId: id.(string)}