Skip to content

Commit e66a915

Browse files
Add SecretManager integration support to GCF. (#5663) (#4040)
* Add SecretManager integration support to GCF. * bug fix * Correcting field requirements. * Using zips created as part of the test. * bug fix. * addressing review comments. * project_id is computed, otherwise project number * test computedness of project_id for secret_env_vars * test computedness * removes unused declaration * updating doc for project number. Co-authored-by: Bakh Inamov <[email protected]> Signed-off-by: Modular Magician <[email protected]> Co-authored-by: Bakh Inamov <[email protected]>
1 parent 54ed41f commit e66a915

File tree

6 files changed

+488
-0
lines changed

6 files changed

+488
-0
lines changed

.changelog/5663.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
gcf: Added SecretManager integration support to GCF.
3+
```

google-beta/resource_cloudfunctions_function.go

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,82 @@ func resourceCloudFunctionsFunction() *schema.Resource {
315315
ForceNew: true,
316316
Description: `Region of function. If it is not provided, the provider region is used.`,
317317
},
318+
319+
"secret_environment_variables": {
320+
Type: schema.TypeList,
321+
Optional: true,
322+
Description: `Secret environment variables configuration`,
323+
Elem: &schema.Resource{
324+
Schema: map[string]*schema.Schema{
325+
"key": {
326+
Type: schema.TypeString,
327+
Required: true,
328+
Description: `Name of the environment variable.`,
329+
},
330+
"project_id": {
331+
Type: schema.TypeString,
332+
Optional: true,
333+
Computed: true,
334+
Description: `Project identifier (due to a known limitation, only project number is supported by this field) of the project that contains the secret. If not set, it will be populated with the function's project, assuming that the secret exists in the same project as of the function.`,
335+
},
336+
"secret": {
337+
Type: schema.TypeString,
338+
Required: true,
339+
Description: `ID of the secret in secret manager (not the full resource name).`,
340+
},
341+
"version": {
342+
Type: schema.TypeString,
343+
Required: true,
344+
Description: `Version of the secret (version number or the string "latest"). It is recommended to use a numeric version for secret environment variables as any updates to the secret value is not reflected until new clones start.`,
345+
},
346+
},
347+
},
348+
},
349+
350+
"secret_volumes": {
351+
Type: schema.TypeList,
352+
Optional: true,
353+
Description: `Secret volumes configuration.`,
354+
Elem: &schema.Resource{
355+
Schema: map[string]*schema.Schema{
356+
"mount_path": {
357+
Type: schema.TypeString,
358+
Required: true,
359+
Description: `The path within the container to mount the secret volume. For example, setting the mount_path as "/etc/secrets" would mount the secret value files under the "/etc/secrets" directory. This directory will also be completely shadowed and unavailable to mount any other secrets. Recommended mount paths: "/etc/secrets" Restricted mount paths: "/cloudsql", "/dev/log", "/pod", "/proc", "/var/log".`,
360+
},
361+
"project_id": {
362+
Type: schema.TypeString,
363+
Optional: true,
364+
Computed: true,
365+
Description: `Project identifier (due to a known limitation, only project number is supported by this field) of the project that contains the secret. If not set, it will be populated with the function's project, assuming that the secret exists in the same project as of the function.`,
366+
},
367+
"secret": {
368+
Type: schema.TypeString,
369+
Required: true,
370+
Description: `ID of the secret in secret manager (not the full resource name).`,
371+
},
372+
"versions": {
373+
Type: schema.TypeList,
374+
Optional: true,
375+
Description: `List of secret versions to mount for this secret. If empty, the "latest" version of the secret will be made available in a file named after the secret under the mount point.`,
376+
Elem: &schema.Resource{
377+
Schema: map[string]*schema.Schema{
378+
"path": {
379+
Type: schema.TypeString,
380+
Required: true,
381+
Description: `Relative path of the file under the mount path where the secret value for this version will be fetched and made available. For example, setting the mount_path as "/etc/secrets" and path as "/secret_foo" would mount the secret value file at "/etc/secrets/secret_foo".`,
382+
},
383+
"version": {
384+
Type: schema.TypeString,
385+
Required: true,
386+
Description: `Version of the secret (version number or the string "latest"). It is preferable to use "latest" version with secret volumes as secret value changes are reflected immediately.`,
387+
},
388+
},
389+
},
390+
},
391+
},
392+
},
393+
},
318394
},
319395
UseJSONNumber: true,
320396
}
@@ -362,6 +438,16 @@ func resourceCloudFunctionsCreate(d *schema.ResourceData, meta interface{}) erro
362438
function.SourceArchiveUrl = fmt.Sprintf("gs://%v/%v", sourceArchiveBucket, sourceArchiveObj)
363439
}
364440

441+
secretEnv := d.Get("secret_environment_variables").([]interface{})
442+
if len(secretEnv) > 0 {
443+
function.SecretEnvironmentVariables = expandSecretEnvironmentVariables(secretEnv)
444+
}
445+
446+
secretVolume := d.Get("secret_volumes").([]interface{})
447+
if len(secretVolume) > 0 {
448+
function.SecretVolumes = expandSecretVolumes(secretVolume)
449+
}
450+
365451
if v, ok := d.GetOk("available_memory_mb"); ok {
366452
availableMemoryMb := v.(int)
367453
function.AvailableMemoryMb = int64(availableMemoryMb)
@@ -523,6 +609,14 @@ func resourceCloudFunctionsRead(d *schema.ResourceData, meta interface{}) error
523609
return fmt.Errorf("Error setting source_repository: %s", err)
524610
}
525611

612+
if err := d.Set("secret_environment_variables", flattenSecretEnvironmentVariables(function.SecretEnvironmentVariables)); err != nil {
613+
return fmt.Errorf("Error setting secret_environment_variables: %s", err)
614+
}
615+
616+
if err := d.Set("secret_volumes", flattenSecretVolumes(function.SecretVolumes)); err != nil {
617+
return fmt.Errorf("Error setting secret_volumes: %s", err)
618+
}
619+
526620
if function.HttpsTrigger != nil {
527621
if err := d.Set("trigger_http", true); err != nil {
528622
return fmt.Errorf("Error setting trigger_http: %s", err)
@@ -600,6 +694,16 @@ func resourceCloudFunctionsUpdate(d *schema.ResourceData, meta interface{}) erro
600694
updateMaskArr = append(updateMaskArr, "sourceRepository")
601695
}
602696

697+
if d.HasChange("secret_environment_variables") {
698+
function.SecretEnvironmentVariables = expandSecretEnvironmentVariables(d.Get("secret_environment_variables").([]interface{}))
699+
updateMaskArr = append(updateMaskArr, "secretEnvironmentVariables")
700+
}
701+
702+
if d.HasChange("secret_volumes") {
703+
function.SecretVolumes = expandSecretVolumes(d.Get("secret_volumes").([]interface{}))
704+
updateMaskArr = append(updateMaskArr, "secretVolumes")
705+
}
706+
603707
if d.HasChange("description") {
604708
function.Description = d.Get("description").(string)
605709
updateMaskArr = append(updateMaskArr, "description")
@@ -818,3 +922,110 @@ func flattenSourceRepository(sourceRepo *cloudfunctions.SourceRepository) []map[
818922

819923
return result
820924
}
925+
926+
func expandSecretEnvironmentVariables(configured []interface{}) []*cloudfunctions.SecretEnvVar {
927+
if len(configured) == 0 {
928+
return nil
929+
}
930+
result := make([]*cloudfunctions.SecretEnvVar, 0, len(configured))
931+
for _, e := range configured {
932+
data := e.(map[string]interface{})
933+
result = append(result, &cloudfunctions.SecretEnvVar{
934+
Key: data["key"].(string),
935+
ProjectId: data["project_id"].(string),
936+
Secret: data["secret"].(string),
937+
Version: data["version"].(string),
938+
})
939+
}
940+
return result
941+
}
942+
943+
func flattenSecretEnvironmentVariables(envVars []*cloudfunctions.SecretEnvVar) []map[string]interface{} {
944+
if envVars == nil {
945+
return nil
946+
}
947+
var result []map[string]interface{}
948+
949+
for _, envVar := range envVars {
950+
if envVar != nil {
951+
data := map[string]interface{}{
952+
"key": envVar.Key,
953+
"project_id": envVar.ProjectId,
954+
"secret": envVar.Secret,
955+
"version": envVar.Version,
956+
}
957+
result = append(result, data)
958+
}
959+
}
960+
return result
961+
}
962+
963+
func expandSecretVolumes(configured []interface{}) []*cloudfunctions.SecretVolume {
964+
if len(configured) == 0 {
965+
return nil
966+
}
967+
result := make([]*cloudfunctions.SecretVolume, 0, len(configured))
968+
for _, e := range configured {
969+
data := e.(map[string]interface{})
970+
result = append(result, &cloudfunctions.SecretVolume{
971+
MountPath: data["mount_path"].(string),
972+
ProjectId: data["project_id"].(string),
973+
Secret: data["secret"].(string),
974+
Versions: expandSecretVersion(data["versions"].([]interface{})), //TODO
975+
})
976+
}
977+
return result
978+
}
979+
980+
func flattenSecretVolumes(secretVolumes []*cloudfunctions.SecretVolume) []map[string]interface{} {
981+
if secretVolumes == nil {
982+
return nil
983+
}
984+
var result []map[string]interface{}
985+
986+
for _, secretVolume := range secretVolumes {
987+
if secretVolume != nil {
988+
data := map[string]interface{}{
989+
"mount_path": secretVolume.MountPath,
990+
"project_id": secretVolume.ProjectId,
991+
"secret": secretVolume.Secret,
992+
"versions": flattenSecretVersion(secretVolume.Versions),
993+
}
994+
result = append(result, data)
995+
}
996+
}
997+
return result
998+
}
999+
1000+
func expandSecretVersion(configured []interface{}) []*cloudfunctions.SecretVersion {
1001+
if len(configured) == 0 {
1002+
return nil
1003+
}
1004+
result := make([]*cloudfunctions.SecretVersion, 0, len(configured))
1005+
for _, e := range configured {
1006+
data := e.(map[string]interface{})
1007+
result = append(result, &cloudfunctions.SecretVersion{
1008+
Path: data["path"].(string),
1009+
Version: data["version"].(string),
1010+
})
1011+
}
1012+
return result
1013+
}
1014+
1015+
func flattenSecretVersion(secretVersions []*cloudfunctions.SecretVersion) []map[string]interface{} {
1016+
if secretVersions == nil {
1017+
return nil
1018+
}
1019+
var result []map[string]interface{}
1020+
1021+
for _, secretVersion := range secretVersions {
1022+
if secretVersion != nil {
1023+
data := map[string]interface{}{
1024+
"path": secretVersion.Path,
1025+
"version": secretVersion.Version,
1026+
}
1027+
result = append(result, data)
1028+
}
1029+
}
1030+
return result
1031+
}

0 commit comments

Comments
 (0)