Skip to content

Commit fccca13

Browse files
google_cloudfunctions_function base image policy fields (#14552) (#10448)
[upstream:f9f4fc5cf971a406015ecf994f8db33c6fa51d04] Signed-off-by: Modular Magician <[email protected]>
1 parent 57f8b2a commit fccca13

File tree

5 files changed

+272
-0
lines changed

5 files changed

+272
-0
lines changed

.changelog/14552.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
unknown: google_cloudfunctions_function base image policy fields

google-beta/services/cloudfunctions/resource_cloudfunctions_function.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,11 +508,42 @@ func ResourceCloudFunctionsFunction() *schema.Resource {
508508
},
509509
},
510510
},
511+
512+
"automatic_update_policy": {
513+
Type: schema.TypeList,
514+
Optional: true,
515+
Computed: true,
516+
ConflictsWith: []string{"on_deploy_update_policy"},
517+
MaxItems: 1,
518+
Description: `Security patches are applied automatically to the runtime without requiring the function to be redeployed.`,
519+
Elem: &schema.Resource{
520+
Schema: map[string]*schema.Schema{},
521+
},
522+
},
523+
524+
"on_deploy_update_policy": {
525+
Type: schema.TypeList,
526+
Optional: true,
527+
ConflictsWith: []string{"automatic_update_policy"},
528+
MaxItems: 1,
529+
Description: `Security patches are only applied when a function is redeployed.`,
530+
Elem: &schema.Resource{
531+
Schema: map[string]*schema.Schema{
532+
"runtime_version": {
533+
Type: schema.TypeString,
534+
Computed: true,
535+
Description: `The runtime version which was used during latest function deployment.`,
536+
},
537+
},
538+
},
539+
},
540+
511541
"status": {
512542
Type: schema.TypeString,
513543
Computed: true,
514544
Description: `Describes the current stage of a deployment.`,
515545
},
546+
516547
"version_id": {
517548
Type: schema.TypeString,
518549
Computed: true,
@@ -606,6 +637,14 @@ func resourceCloudFunctionsCreate(d *schema.ResourceData, meta interface{}) erro
606637
"You must specify a trigger when deploying a new function.")
607638
}
608639

640+
if v, ok := d.GetOk("automatic_update_policy"); ok {
641+
function.AutomaticUpdatePolicy = expandAutomaticUpdatePolicy(v.([]interface{}))
642+
function.OnDeployUpdatePolicy = nil
643+
} else if v, ok := d.GetOk("on_deploy_update_policy"); ok {
644+
function.OnDeployUpdatePolicy = expandOnDeployUpdatePolicy(v.([]interface{}))
645+
function.AutomaticUpdatePolicy = nil
646+
}
647+
609648
if v, ok := d.GetOk("ingress_settings"); ok {
610649
function.IngressSettings = v.(string)
611650
}
@@ -824,6 +863,25 @@ func resourceCloudFunctionsRead(d *schema.ResourceData, meta interface{}) error
824863
if err := d.Set("version_id", strconv.FormatInt(function.VersionId, 10)); err != nil {
825864
return fmt.Errorf("Error setting version_id: %s", err)
826865
}
866+
// check the on_deploy_update_policy first as it's mutually exclusive to automatice_update_policy, and the latter is system default
867+
if function.OnDeployUpdatePolicy != nil {
868+
if err := d.Set("on_deploy_update_policy", flattenOnDeployUpdatePolicy(function.OnDeployUpdatePolicy)); err != nil {
869+
return fmt.Errorf("Error setting on_deploy_update_policy: %s", err)
870+
}
871+
function.AutomaticUpdatePolicy = nil
872+
d.Set("automatic_update_policy", nil)
873+
} else {
874+
d.Set("on_deploy_update_policy", nil)
875+
}
876+
877+
if function.AutomaticUpdatePolicy != nil {
878+
if err := d.Set("automatic_update_policy", flattenAutomaticUpdatePolicy(function.AutomaticUpdatePolicy)); err != nil {
879+
return fmt.Errorf("Error setting automatic_update_policy: %s", err)
880+
}
881+
d.Set("on_deploy_update_policy", nil)
882+
} else {
883+
d.Set("automatic_update_policy", nil)
884+
}
827885

828886
return nil
829887
}
@@ -980,6 +1038,22 @@ func resourceCloudFunctionsUpdate(d *schema.ResourceData, meta interface{}) erro
9801038
updateMaskArr = append(updateMaskArr, "buildServiceAccount")
9811039
}
9821040

1041+
if d.HasChange("automatic_update_policy") {
1042+
function.AutomaticUpdatePolicy = expandAutomaticUpdatePolicy(d.Get("automatic_update_policy").([]interface{}))
1043+
if function.AutomaticUpdatePolicy != nil {
1044+
function.OnDeployUpdatePolicy = nil
1045+
}
1046+
updateMaskArr = append(updateMaskArr, "automatic_update_policy")
1047+
}
1048+
1049+
if d.HasChange("on_deploy_update_policy") {
1050+
function.OnDeployUpdatePolicy = expandOnDeployUpdatePolicy(d.Get("on_deploy_update_policy").([]interface{}))
1051+
if function.OnDeployUpdatePolicy != nil {
1052+
function.AutomaticUpdatePolicy = nil
1053+
}
1054+
updateMaskArr = append(updateMaskArr, "on_deploy_update_policy")
1055+
}
1056+
9831057
if len(updateMaskArr) > 0 {
9841058
log.Printf("[DEBUG] Send Patch CloudFunction Configuration request: %#v", function)
9851059
updateMask := strings.Join(updateMaskArr, ",")
@@ -1248,3 +1322,42 @@ func flattenSecretVersion(secretVersions []*cloudfunctions.SecretVersion) []map[
12481322
}
12491323
return result
12501324
}
1325+
1326+
func expandAutomaticUpdatePolicy(configured []interface{}) *cloudfunctions.AutomaticUpdatePolicy {
1327+
if len(configured) == 0 {
1328+
return nil
1329+
}
1330+
return &cloudfunctions.AutomaticUpdatePolicy{}
1331+
}
1332+
1333+
func flattenAutomaticUpdatePolicy(policy *cloudfunctions.AutomaticUpdatePolicy) []map[string]interface{} {
1334+
result := make([]map[string]interface{}, 0, 1)
1335+
if policy == nil {
1336+
return nil
1337+
}
1338+
// Have to append an empty element for empty message type
1339+
result = append(result, map[string]interface{}{})
1340+
return result
1341+
}
1342+
1343+
func expandOnDeployUpdatePolicy(configured []interface{}) *cloudfunctions.OnDeployUpdatePolicy {
1344+
if len(configured) == 0 {
1345+
return nil
1346+
}
1347+
return &cloudfunctions.OnDeployUpdatePolicy{}
1348+
}
1349+
1350+
func flattenOnDeployUpdatePolicy(policy *cloudfunctions.OnDeployUpdatePolicy) []map[string]interface{} {
1351+
result := make([]map[string]interface{}, 0, 1)
1352+
if policy == nil {
1353+
return nil
1354+
}
1355+
1356+
result = append(result, map[string]interface{}{
1357+
"runtime_version": policy.RuntimeVersion,
1358+
})
1359+
1360+
log.Printf("flatten on_deploy_update_policy to: %s", result)
1361+
1362+
return result
1363+
}

google-beta/services/cloudfunctions/resource_cloudfunctions_function_meta.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,5 @@ fields:
5151
- field: 'version_id'
5252
- field: 'vpc_connector'
5353
- field: 'vpc_connector_egress_settings'
54+
- field: 'automatic_update_policy'
55+
- field: 'on_deploy_update_policy.runtime_version'

google-beta/services/cloudfunctions/resource_cloudfunctions_function_test.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,70 @@ func TestAccCloudFunctionsFunction_buildServiceAccount(t *testing.T) {
640640
})
641641
}
642642

643+
func TestAccCloudFunctionsFunction_abiuCRUD(t *testing.T) {
644+
t.Parallel()
645+
646+
var function cloudfunctions.CloudFunction
647+
648+
funcResourceName := "google_cloudfunctions_function.function"
649+
functionName := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10))
650+
bucketName := fmt.Sprintf("tf-test-bucket-%d", acctest.RandInt(t))
651+
zipFilePath := acctest.CreateZIPArchiveForCloudFunctionSource(t, testHTTPTriggerPath)
652+
defer os.Remove(zipFilePath) // clean up
653+
654+
acctest.VcrTest(t, resource.TestCase{
655+
PreCheck: func() { acctest.AccTestPreCheck(t) },
656+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
657+
Steps: []resource.TestStep{
658+
{
659+
Config: testAccCloudFunctionsFunction_abiuAutomatic(functionName, bucketName, zipFilePath),
660+
Check: resource.ComposeTestCheckFunc(
661+
testAccCloudFunctionsFunctionExists(
662+
t, funcResourceName, &function),
663+
resource.TestCheckResourceAttrSet(funcResourceName,
664+
"automatic_update_policy.#"),
665+
),
666+
},
667+
{
668+
ResourceName: funcResourceName,
669+
ImportState: true,
670+
ImportStateVerify: true,
671+
ImportStateVerifyIgnore: []string{"build_environment_variables", "labels", "terraform_labels"},
672+
},
673+
{
674+
Config: testAccCloudFunctionsFunction_abiuOndeploy(functionName, bucketName, zipFilePath),
675+
Check: resource.ComposeTestCheckFunc(
676+
testAccCloudFunctionsFunctionExists(
677+
t, funcResourceName, &function),
678+
resource.TestCheckResourceAttrSet(funcResourceName,
679+
"on_deploy_update_policy.#"),
680+
),
681+
},
682+
{
683+
ResourceName: funcResourceName,
684+
ImportState: true,
685+
ImportStateVerify: true,
686+
ImportStateVerifyIgnore: []string{"build_environment_variables", "labels", "terraform_labels"},
687+
},
688+
{
689+
Config: testAccCloudFunctionsFunction_basic(functionName, bucketName, zipFilePath),
690+
Check: resource.ComposeTestCheckFunc(
691+
testAccCloudFunctionsFunctionExists(
692+
t, funcResourceName, &function),
693+
resource.TestCheckResourceAttrSet(funcResourceName,
694+
"automatic_update_policy.#"),
695+
),
696+
},
697+
{
698+
ResourceName: funcResourceName,
699+
ImportState: true,
700+
ImportStateVerify: true,
701+
ImportStateVerifyIgnore: []string{"build_environment_variables", "labels", "terraform_labels"},
702+
},
703+
},
704+
})
705+
}
706+
643707
func testAccCheckCloudFunctionsFunctionDestroyProducer(t *testing.T) func(s *terraform.State) error {
644708
return func(s *terraform.State) error {
645709
config := acctest.GoogleProviderConfig(t)
@@ -1510,3 +1574,87 @@ resource "google_cloudfunctions_function" "function" {
15101574
}
15111575
`, bucketName, zipFilePath, saName, serviceAccount, functionName)
15121576
}
1577+
1578+
func testAccCloudFunctionsFunction_abiuAutomatic(functionName string, bucketName string, zipFilePath string) string {
1579+
return fmt.Sprintf(`
1580+
resource "google_storage_bucket" "bucket" {
1581+
name = "%s"
1582+
location = "US"
1583+
uniform_bucket_level_access = true
1584+
}
1585+
1586+
resource "google_storage_bucket_object" "archive" {
1587+
name = "index.zip"
1588+
bucket = google_storage_bucket.bucket.name
1589+
source = "%s"
1590+
}
1591+
1592+
resource "google_cloudfunctions_function" "function" {
1593+
name = "%s"
1594+
runtime = "nodejs20"
1595+
description = "test function"
1596+
docker_registry = "ARTIFACT_REGISTRY"
1597+
available_memory_mb = 128
1598+
source_archive_bucket = google_storage_bucket.bucket.name
1599+
source_archive_object = google_storage_bucket_object.archive.name
1600+
trigger_http = true
1601+
timeout = 61
1602+
entry_point = "helloGET"
1603+
ingress_settings = "ALLOW_INTERNAL_ONLY"
1604+
labels = {
1605+
my-label = "my-label-value"
1606+
}
1607+
environment_variables = {
1608+
TEST_ENV_VARIABLE = "test-env-variable-value"
1609+
}
1610+
build_environment_variables = {
1611+
TEST_ENV_VARIABLE = "test-build-env-variable-value"
1612+
}
1613+
automatic_update_policy {}
1614+
max_instances = 10
1615+
min_instances = 3
1616+
}
1617+
`, bucketName, zipFilePath, functionName)
1618+
}
1619+
1620+
func testAccCloudFunctionsFunction_abiuOndeploy(functionName string, bucketName string, zipFilePath string) string {
1621+
return fmt.Sprintf(`
1622+
resource "google_storage_bucket" "bucket" {
1623+
name = "%s"
1624+
location = "US"
1625+
uniform_bucket_level_access = true
1626+
}
1627+
1628+
resource "google_storage_bucket_object" "archive" {
1629+
name = "index.zip"
1630+
bucket = google_storage_bucket.bucket.name
1631+
source = "%s"
1632+
}
1633+
1634+
resource "google_cloudfunctions_function" "function" {
1635+
name = "%s"
1636+
runtime = "nodejs20"
1637+
description = "test function"
1638+
docker_registry = "ARTIFACT_REGISTRY"
1639+
available_memory_mb = 128
1640+
source_archive_bucket = google_storage_bucket.bucket.name
1641+
source_archive_object = google_storage_bucket_object.archive.name
1642+
trigger_http = true
1643+
timeout = 61
1644+
entry_point = "helloGET"
1645+
ingress_settings = "ALLOW_INTERNAL_ONLY"
1646+
labels = {
1647+
my-label = "my-label-value"
1648+
}
1649+
environment_variables = {
1650+
TEST_ENV_VARIABLE = "test-env-variable-value"
1651+
}
1652+
build_environment_variables = {
1653+
TEST_ENV_VARIABLE = "test-build-env-variable-value"
1654+
}
1655+
on_deploy_update_policy {}
1656+
max_instances = 10
1657+
min_instances = 3
1658+
}
1659+
`, bucketName, zipFilePath, functionName)
1660+
}

website/docs/r/cloudfunctions_function.html.markdown

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ Please refer to the field 'effective_labels' for all of the labels present on th
193193

194194
* `secret_volumes` - (Optional) Secret volumes configuration. Structure is [documented below](#nested_secret_volumes).
195195

196+
* `automatic_update_policy` - (Optional) Security patches are applied automatically to the runtime without requiring the function to be redeployed. This should be specified as an empty block and cannot be set alongside `on_deploy_update_policy`.
197+
198+
* `on_deploy_update_policy` - (Optional) Security patches are only applied when a function is redeployed. This should be specified as an empty block and cannot be set alongside `automatic_update_policy`. Structure is [documented below](#nested_on_deploy_update_policy).
199+
196200
<a name="nested_event_trigger"></a>The `event_trigger` block supports:
197201

198202
* `event_type` - (Required) The type of event to observe. For example: `"google.storage.object.finalize"`.
@@ -226,6 +230,10 @@ which to observe events. For example, `"myBucket"` or `"projects/my-project/topi
226230

227231
* `version` - (Required) 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.
228232

233+
<a name="nested_on_deploy_update_policy"></a>The `on_deploy_update_policy` block supports:
234+
235+
* `runtime_version` - (Output) The runtime version which was used during latest function deployment.
236+
229237
<a name="nested_secret_volumes"></a>The `secret_volumes` block supports:
230238

231239
* `mount_path` - (Required) 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".

0 commit comments

Comments
 (0)