@@ -56,13 +56,25 @@ var ReservedResourceFields = []string{
5656// being implemented.
5757type Resource struct {
5858 // Schema is the structure and type information for this component. This
59- // field is required for all Resource concepts.
59+ // field, or SchemaFunc, is required for all Resource concepts. To prevent
60+ // storing all schema information in memory for the lifecycle of a provider,
61+ // use SchemaFunc instead.
6062 //
6163 // The keys of this map are the names used in a practitioner configuration,
6264 // such as the attribute or block name. The values describe the structure
6365 // and type information of that attribute or block.
6466 Schema map [string ]* Schema
6567
68+ // SchemaFunc is the structure and type information for this component. This
69+ // field, or Schema, is required for all Resource concepts. Use this field
70+ // instead of Schema on top level Resource declarations to prevent storing
71+ // all schema information in memory for the lifecycle of a provider.
72+ //
73+ // The keys of this map are the names used in a practitioner configuration,
74+ // such as the attribute or block name. The values describe the structure
75+ // and type information of that attribute or block.
76+ SchemaFunc func () map [string ]* Schema
77+
6678 // SchemaVersion is the version number for this resource's Schema
6779 // definition. This field is only valid when the Resource is a managed
6880 // resource.
@@ -585,6 +597,17 @@ type Resource struct {
585597 UseJSONNumber bool
586598}
587599
600+ // SchemaMap returns the schema information for this Resource whether it is
601+ // defined via the SchemaFunc field or Schema field. The SchemaFunc field, if
602+ // defined, takes precedence over the Schema field.
603+ func (r * Resource ) SchemaMap () map [string ]* Schema {
604+ if r .SchemaFunc != nil {
605+ return r .SchemaFunc ()
606+ }
607+
608+ return r .Schema
609+ }
610+
588611// ShimInstanceStateFromValue converts a cty.Value to a
589612// terraform.InstanceState.
590613func (r * Resource ) ShimInstanceStateFromValue (state cty.Value ) (* terraform.InstanceState , error ) {
@@ -594,7 +617,7 @@ func (r *Resource) ShimInstanceStateFromValue(state cty.Value) (*terraform.Insta
594617
595618 // We now rebuild the state through the ResourceData, so that the set indexes
596619 // match what helper/schema expects.
597- data , err := schemaMap (r .Schema ).Data (s , nil )
620+ data , err := schemaMap (r .SchemaMap () ).Data (s , nil )
598621 if err != nil {
599622 return nil , err
600623 }
@@ -767,7 +790,8 @@ func (r *Resource) Apply(
767790 s * terraform.InstanceState ,
768791 d * terraform.InstanceDiff ,
769792 meta interface {}) (* terraform.InstanceState , diag.Diagnostics ) {
770- data , err := schemaMap (r .Schema ).Data (s , d )
793+ schema := schemaMap (r .SchemaMap ())
794+ data , err := schema .Data (s , d )
771795 if err != nil {
772796 return s , diag .FromErr (err )
773797 }
@@ -824,7 +848,7 @@ func (r *Resource) Apply(
824848 }
825849
826850 // Reset the data to be stateless since we just destroyed
827- data , err = schemaMap ( r . Schema ) .Data (nil , d )
851+ data , err = schema .Data (nil , d )
828852 if err != nil {
829853 return nil , append (diags , diag .FromErr (err )... )
830854 }
@@ -868,7 +892,7 @@ func (r *Resource) Diff(
868892 return nil , fmt .Errorf ("[ERR] Error decoding timeout: %s" , err )
869893 }
870894
871- instanceDiff , err := schemaMap (r .Schema ).Diff (ctx , s , c , r .CustomizeDiff , meta , true )
895+ instanceDiff , err := schemaMap (r .SchemaMap () ).Diff (ctx , s , c , r .CustomizeDiff , meta , true )
872896 if err != nil {
873897 return instanceDiff , err
874898 }
@@ -890,7 +914,7 @@ func (r *Resource) SimpleDiff(
890914 c * terraform.ResourceConfig ,
891915 meta interface {}) (* terraform.InstanceDiff , error ) {
892916
893- instanceDiff , err := schemaMap (r .Schema ).Diff (ctx , s , c , r .CustomizeDiff , meta , false )
917+ instanceDiff , err := schemaMap (r .SchemaMap () ).Diff (ctx , s , c , r .CustomizeDiff , meta , false )
894918 if err != nil {
895919 return instanceDiff , err
896920 }
@@ -915,7 +939,7 @@ func (r *Resource) SimpleDiff(
915939
916940// Validate validates the resource configuration against the schema.
917941func (r * Resource ) Validate (c * terraform.ResourceConfig ) diag.Diagnostics {
918- diags := schemaMap (r .Schema ).Validate (c )
942+ diags := schemaMap (r .SchemaMap () ).Validate (c )
919943
920944 if r .DeprecationMessage != "" {
921945 diags = append (diags , diag.Diagnostic {
@@ -937,7 +961,7 @@ func (r *Resource) ReadDataApply(
937961) (* terraform.InstanceState , diag.Diagnostics ) {
938962 // Data sources are always built completely from scratch
939963 // on each read, so the source state is always nil.
940- data , err := schemaMap (r .Schema ).Data (nil , d )
964+ data , err := schemaMap (r .SchemaMap () ).Data (nil , d )
941965 if err != nil {
942966 return nil , diag .FromErr (err )
943967 }
@@ -978,10 +1002,12 @@ func (r *Resource) RefreshWithoutUpgrade(
9781002 }
9791003 }
9801004
1005+ schema := schemaMap (r .SchemaMap ())
1006+
9811007 if r .Exists != nil {
9821008 // Make a copy of data so that if it is modified it doesn't
9831009 // affect our Read later.
984- data , err := schemaMap ( r . Schema ) .Data (s , nil )
1010+ data , err := schema .Data (s , nil )
9851011 if err != nil {
9861012 return s , diag .FromErr (err )
9871013 }
@@ -1004,7 +1030,7 @@ func (r *Resource) RefreshWithoutUpgrade(
10041030 }
10051031 }
10061032
1007- data , err := schemaMap ( r . Schema ) .Data (s , nil )
1033+ data , err := schema .Data (s , nil )
10081034 if err != nil {
10091035 return s , diag .FromErr (err )
10101036 }
@@ -1023,7 +1049,7 @@ func (r *Resource) RefreshWithoutUpgrade(
10231049 state = nil
10241050 }
10251051
1026- schemaMap ( r . Schema ) .handleDiffSuppressOnRefresh (ctx , s , state )
1052+ schema .handleDiffSuppressOnRefresh (ctx , s , state )
10271053 return r .recordCurrentSchemaVersion (state ), diags
10281054}
10291055
@@ -1069,13 +1095,14 @@ func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error
10691095 }
10701096 }
10711097
1098+ schema := schemaMap (r .SchemaMap ())
10721099 tsm := topSchemaMap
10731100
10741101 if r .isTopLevel () && writable {
10751102 // All non-Computed attributes must be ForceNew if Update is not defined
10761103 if ! r .updateFuncSet () {
10771104 nonForceNewAttrs := make ([]string , 0 )
1078- for k , v := range r . Schema {
1105+ for k , v := range schema {
10791106 if ! v .ForceNew && ! v .Computed {
10801107 nonForceNewAttrs = append (nonForceNewAttrs , k )
10811108 }
@@ -1086,19 +1113,19 @@ func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error
10861113 }
10871114 } else {
10881115 nonUpdateableAttrs := make ([]string , 0 )
1089- for k , v := range r . Schema {
1116+ for k , v := range schema {
10901117 if v .ForceNew || v .Computed && ! v .Optional {
10911118 nonUpdateableAttrs = append (nonUpdateableAttrs , k )
10921119 }
10931120 }
1094- updateableAttrs := len (r . Schema ) - len (nonUpdateableAttrs )
1121+ updateableAttrs := len (schema ) - len (nonUpdateableAttrs )
10951122 if updateableAttrs == 0 {
10961123 return fmt .Errorf (
10971124 "All fields are ForceNew or Computed w/out Optional, Update is superfluous" )
10981125 }
10991126 }
11001127
1101- tsm = schemaMap ( r . Schema )
1128+ tsm = schema
11021129
11031130 // Destroy, and Read are required
11041131 if ! r .readFuncSet () {
@@ -1157,14 +1184,18 @@ func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error
11571184
11581185 // Data source
11591186 if r .isTopLevel () && ! writable {
1160- tsm = schemaMap ( r . Schema )
1187+ tsm = schema
11611188 for k := range tsm {
11621189 if isReservedDataSourceFieldName (k ) {
11631190 return fmt .Errorf ("%s is a reserved field name" , k )
11641191 }
11651192 }
11661193 }
11671194
1195+ if r .SchemaFunc != nil && r .Schema != nil {
1196+ return fmt .Errorf ("SchemaFunc and Schema should not both be set" )
1197+ }
1198+
11681199 // check context funcs are not set alongside their nonctx counterparts
11691200 if r .CreateContext != nil && r .Create != nil {
11701201 return fmt .Errorf ("CreateContext and Create should not both be set" )
@@ -1207,7 +1238,7 @@ func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error
12071238 return fmt .Errorf ("Delete and DeleteWithoutTimeout should not both be set" )
12081239 }
12091240
1210- return schemaMap ( r . Schema ) .InternalValidate (tsm )
1241+ return schema .InternalValidate (tsm )
12111242}
12121243
12131244func isReservedDataSourceFieldName (name string ) bool {
@@ -1254,7 +1285,7 @@ func isReservedResourceFieldName(name string) bool {
12541285//
12551286// This function is useful for unit tests and ResourceImporter functions.
12561287func (r * Resource ) Data (s * terraform.InstanceState ) * ResourceData {
1257- result , err := schemaMap (r .Schema ).Data (s , nil )
1288+ result , err := schemaMap (r .SchemaMap () ).Data (s , nil )
12581289 if err != nil {
12591290 // At the time of writing, this isn't possible (Data never returns
12601291 // non-nil errors). We panic to find this in the future if we have to.
@@ -1281,7 +1312,7 @@ func (r *Resource) Data(s *terraform.InstanceState) *ResourceData {
12811312// TODO: May be able to be removed with the above ResourceData function.
12821313func (r * Resource ) TestResourceData () * ResourceData {
12831314 return & ResourceData {
1284- schema : r .Schema ,
1315+ schema : r .SchemaMap () ,
12851316 }
12861317}
12871318
0 commit comments