@@ -14,6 +14,7 @@ import (
1414 "github.com/hashicorp/terraform-provider-google/google/tpgresource"
1515 transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport"
1616
17+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
1718 "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1819 "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
1920
@@ -28,11 +29,14 @@ import (
2829
2930func ResourceStorageBucketObject () * schema.Resource {
3031 return & schema.Resource {
31- Create : resourceStorageBucketObjectCreate ,
32- Read : resourceStorageBucketObjectRead ,
33- Update : resourceStorageBucketObjectUpdate ,
34- Delete : resourceStorageBucketObjectDelete ,
35- CustomizeDiff : resourceStorageBucketObjectCustomizeDiff ,
32+ Create : resourceStorageBucketObjectCreate ,
33+ Read : resourceStorageBucketObjectRead ,
34+ Update : resourceStorageBucketObjectUpdate ,
35+ Delete : resourceStorageBucketObjectDelete ,
36+ CustomizeDiff : customdiff .All (
37+ resourceStorageBucketObjectCustomizeDiff ,
38+ validateContexts ,
39+ ),
3640
3741 Timeouts : & schema.ResourceTimeout {
3842 Create : schema .DefaultTimeout (4 * time .Minute ),
@@ -109,6 +113,46 @@ func ResourceStorageBucketObject() *schema.Resource {
109113 Description : `Data as string to be uploaded. Must be defined if source is not. Note: The content field is marked as sensitive. To view the raw contents of the object, please define an output.` ,
110114 },
111115
116+ "contexts" : {
117+ Type : schema .TypeList ,
118+ Optional : true ,
119+ MaxItems : 1 ,
120+ Description : "Contexts attached to an object, in key-value pairs." ,
121+ Elem : & schema.Resource {
122+ Schema : map [string ]* schema.Schema {
123+ "custom" : {
124+ Type : schema .TypeList ,
125+ Required : true ,
126+ Description : "A list of custom context key-value pairs." ,
127+ Elem : & schema.Resource {
128+ Schema : map [string ]* schema.Schema {
129+ "key" : {
130+ Type : schema .TypeString ,
131+ Required : true ,
132+ Description : "An individual object context. Context keys and their corresponding values must start with an alphanumeric character." ,
133+ },
134+ "value" : {
135+ Type : schema .TypeString ,
136+ Required : true ,
137+ Description : "The value associated with this context. This field holds the primary information for the given context key." ,
138+ },
139+ "create_time" : {
140+ Type : schema .TypeString ,
141+ Computed : true ,
142+ Description : "The time when context was first added to the storage#object in RFC 3339 format." ,
143+ },
144+ "update_time" : {
145+ Type : schema .TypeString ,
146+ Computed : true ,
147+ Description : "The time when context was last updated in RFC 3339 format." ,
148+ },
149+ },
150+ },
151+ },
152+ },
153+ },
154+ },
155+
112156 "generation" : {
113157 Type : schema .TypeInt ,
114158 Computed : true ,
@@ -393,6 +437,10 @@ func resourceStorageBucketObjectCreate(d *schema.ResourceData, meta interface{})
393437 object .TemporaryHold = v .(bool )
394438 }
395439
440+ if v , ok := d .GetOk ("contexts" ); ok {
441+ object .Contexts = expandCustomObjectContexts (v .([]interface {}))
442+ }
443+
396444 insertCall := objectsService .Insert (bucket , object )
397445 insertCall .Name (name )
398446 if v , ok := d .GetOk ("force_empty_content_type" ); ok && v .(bool ) {
@@ -461,6 +509,11 @@ func resourceStorageBucketObjectUpdate(d *schema.ResourceData, meta interface{})
461509 res .TemporaryHold = v .(bool )
462510 }
463511
512+ if d .HasChange ("contexts" ) {
513+ v := d .Get ("contexts" )
514+ res .Contexts = expandCustomObjectContexts (v .([]interface {}))
515+ }
516+
464517 updateCall := objectsService .Update (bucket , name , res )
465518 if hasRetentionChanges {
466519 updateCall .OverrideUnlockedRetention (true )
@@ -471,7 +524,7 @@ func resourceStorageBucketObjectUpdate(d *schema.ResourceData, meta interface{})
471524 return fmt .Errorf ("Error updating object %s: %s" , name , err )
472525 }
473526
474- return nil
527+ return resourceStorageBucketObjectRead ( d , meta )
475528 }
476529}
477530
@@ -565,6 +618,9 @@ func resourceStorageBucketObjectRead(d *schema.ResourceData, meta interface{}) e
565618 if err := d .Set ("temporary_hold" , res .TemporaryHold ); err != nil {
566619 return fmt .Errorf ("Error setting temporary_hold: %s" , err )
567620 }
621+ if err := d .Set ("contexts" , flattenContexts (d , res .Contexts )); err != nil {
622+ return fmt .Errorf ("Error reading Contexts: %s" , err )
623+ }
568624
569625 d .SetId (objectGetID (res ))
570626
@@ -651,6 +707,35 @@ func expandObjectRetention(configured interface{}) *storage.ObjectRetention {
651707 return objectRetention
652708}
653709
710+ func expandCustomObjectContexts (objectContexts []interface {}) * storage.ObjectContexts {
711+ if len (objectContexts ) == 0 {
712+ return nil
713+ }
714+
715+ contextsObj := objectContexts [0 ].(map [string ]interface {})
716+
717+ customList := contextsObj ["custom" ]
718+
719+ tfCustomList := customList .([]interface {})
720+
721+ objContextPayload := make (map [string ]storage.ObjectCustomContextPayload )
722+ for _ , item := range tfCustomList {
723+ itemMap := item .(map [string ]interface {})
724+
725+ key := itemMap ["key" ].(string )
726+ value := itemMap ["value" ].(string )
727+
728+ objContextPayload [key ] = storage.ObjectCustomContextPayload {
729+ Value : value ,
730+ }
731+ }
732+
733+ contexts := & storage.ObjectContexts {
734+ Custom : objContextPayload ,
735+ }
736+ return contexts
737+ }
738+
654739func flattenObjectRetention (objectRetention * storage.ObjectRetention ) []map [string ]interface {} {
655740 retentions := make ([]map [string ]interface {}, 0 , 1 )
656741
@@ -667,6 +752,51 @@ func flattenObjectRetention(objectRetention *storage.ObjectRetention) []map[stri
667752 return retentions
668753}
669754
755+ func flattenContexts (d * schema.ResourceData , contexts * storage.ObjectContexts ) interface {} {
756+ if contexts == nil || contexts .Custom == nil || len (contexts .Custom ) == 0 {
757+ return nil
758+ }
759+ c , _ := d .GetOk ("contexts" )
760+ contextsList := c .([]interface {})
761+ if len (contextsList ) == 0 {
762+ return nil
763+ }
764+
765+ contextsObj := contextsList [0 ].(map [string ]interface {})
766+
767+ customObjectList := contextsObj ["custom" ]
768+
769+ customkeyValueList := customObjectList .([]interface {})
770+
771+ flattenKeyValueList := make ([]interface {}, 0 , len (contexts .Custom ))
772+ for _ , customKv := range customkeyValueList {
773+ customKvItem := customKv .(map [string ]interface {})
774+
775+ k := customKvItem ["key" ].(string )
776+ customItem , ok := contexts .Custom [k ]
777+ if ! ok {
778+ continue
779+ } else {
780+ itemMap := make (map [string ]interface {})
781+
782+ itemMap ["key" ] = k
783+ itemMap ["value" ] = customItem .Value
784+ itemMap ["create_time" ] = customItem .CreateTime
785+ itemMap ["update_time" ] = customItem .UpdateTime
786+
787+ flattenKeyValueList = append (flattenKeyValueList , itemMap )
788+ }
789+ }
790+
791+ customList := map [string ]interface {}{
792+ "custom" : flattenKeyValueList ,
793+ }
794+
795+ contextList := []interface {}{customList }
796+
797+ return contextList
798+ }
799+
670800func resourceStorageBucketObjectCustomizeDiff (ctx context.Context , d * schema.ResourceDiff , meta interface {}) error {
671801 localMd5Hash := ""
672802
@@ -707,3 +837,36 @@ func showDiff(d *schema.ResourceDiff) error {
707837
708838 return nil
709839}
840+
841+ // validate keys for duplicates in custom object contexts
842+ func validateContexts (ctx context.Context , d * schema.ResourceDiff , meta interface {}) error {
843+ if ! d .HasChange ("contexts" ) {
844+ return nil
845+ }
846+
847+ _ , new := d .GetChange ("contexts" )
848+
849+ contextsList := new .([]interface {})
850+ if len (contextsList ) == 0 {
851+ return nil
852+ }
853+
854+ contextsObj := contextsList [0 ].(map [string ]interface {})
855+
856+ customList := contextsObj ["custom" ]
857+
858+ keyValueList := customList .([]interface {})
859+
860+ keys := make (map [string ]bool )
861+ for _ , item := range keyValueList {
862+ itemMap := item .(map [string ]interface {})
863+
864+ key := itemMap ["key" ].(string )
865+
866+ if keys [key ] {
867+ return fmt .Errorf ("duplicate key found in 'contexts' block: %s. Each 'key' must be unique" , key )
868+ }
869+ keys [key ] = true
870+ }
871+ return nil
872+ }
0 commit comments