kubeform.dev/terraform-backend-sdk@v0.0.0-20220310143633-45f07fe731c5/configs/module_merge_body.go (about) 1 package configs 2 3 import ( 4 "github.com/hashicorp/hcl/v2" 5 ) 6 7 // MergeBodies creates a new HCL body that contains a combination of the 8 // given base and override bodies. Attributes and blocks defined in the 9 // override body take precedence over those of the same name defined in 10 // the base body. 11 // 12 // If any block of a particular type appears in "override" then it will 13 // replace _all_ of the blocks of the same type in "base" in the new 14 // body. 15 func MergeBodies(base, override hcl.Body) hcl.Body { 16 return mergeBody{ 17 Base: base, 18 Override: override, 19 } 20 } 21 22 // mergeBody is a hcl.Body implementation that wraps a pair of other bodies 23 // and allows attributes and blocks within the override to take precedence 24 // over those defined in the base body. 25 // 26 // This is used to deal with dynamically-processed bodies in Module.mergeFile. 27 // It uses a shallow-only merging strategy where direct attributes defined 28 // in Override will override attributes of the same name in Base, while any 29 // blocks defined in Override will hide all blocks of the same type in Base. 30 // 31 // This cannot possibly "do the right thing" in all cases, because we don't 32 // have enough information about user intent. However, this behavior is intended 33 // to be reasonable for simple overriding use-cases. 34 type mergeBody struct { 35 Base hcl.Body 36 Override hcl.Body 37 } 38 39 var _ hcl.Body = mergeBody{} 40 41 func (b mergeBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) { 42 var diags hcl.Diagnostics 43 baseSchema := schemaWithDynamic(schema) 44 overrideSchema := schemaWithDynamic(schemaForOverrides(schema)) 45 46 baseContent, _, cDiags := b.Base.PartialContent(baseSchema) 47 diags = append(diags, cDiags...) 48 overrideContent, _, cDiags := b.Override.PartialContent(overrideSchema) 49 diags = append(diags, cDiags...) 50 51 content := b.prepareContent(baseContent, overrideContent) 52 53 return content, diags 54 } 55 56 func (b mergeBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) { 57 var diags hcl.Diagnostics 58 baseSchema := schemaWithDynamic(schema) 59 overrideSchema := schemaWithDynamic(schemaForOverrides(schema)) 60 61 baseContent, baseRemain, cDiags := b.Base.PartialContent(baseSchema) 62 diags = append(diags, cDiags...) 63 overrideContent, overrideRemain, cDiags := b.Override.PartialContent(overrideSchema) 64 diags = append(diags, cDiags...) 65 66 content := b.prepareContent(baseContent, overrideContent) 67 68 remain := MergeBodies(baseRemain, overrideRemain) 69 70 return content, remain, diags 71 } 72 73 func (b mergeBody) prepareContent(base *hcl.BodyContent, override *hcl.BodyContent) *hcl.BodyContent { 74 content := &hcl.BodyContent{ 75 Attributes: make(hcl.Attributes), 76 } 77 78 // For attributes we just assign from each map in turn and let the override 79 // map clobber any matching entries from base. 80 for k, a := range base.Attributes { 81 content.Attributes[k] = a 82 } 83 for k, a := range override.Attributes { 84 content.Attributes[k] = a 85 } 86 87 // Things are a little more interesting for blocks because they arrive 88 // as a flat list. Our merging semantics call for us to suppress blocks 89 // from base if at least one block of the same type appears in override. 90 // We explicitly do not try to correlate and deeply merge nested blocks, 91 // since we don't have enough context here to infer user intent. 92 93 overriddenBlockTypes := make(map[string]bool) 94 for _, block := range override.Blocks { 95 if block.Type == "dynamic" { 96 overriddenBlockTypes[block.Labels[0]] = true 97 continue 98 } 99 overriddenBlockTypes[block.Type] = true 100 } 101 for _, block := range base.Blocks { 102 // We skip over dynamic blocks whose type label is an overridden type 103 // but note that below we do still leave them as dynamic blocks in 104 // the result because expanding the dynamic blocks that are left is 105 // done much later during the core graph walks, where we can safely 106 // evaluate the expressions. 107 if block.Type == "dynamic" && overriddenBlockTypes[block.Labels[0]] { 108 continue 109 } 110 if overriddenBlockTypes[block.Type] { 111 continue 112 } 113 content.Blocks = append(content.Blocks, block) 114 } 115 content.Blocks = append(content.Blocks, override.Blocks...) 116 117 return content 118 } 119 120 func (b mergeBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) { 121 var diags hcl.Diagnostics 122 ret := make(hcl.Attributes) 123 124 baseAttrs, aDiags := b.Base.JustAttributes() 125 diags = append(diags, aDiags...) 126 overrideAttrs, aDiags := b.Override.JustAttributes() 127 diags = append(diags, aDiags...) 128 129 for k, a := range baseAttrs { 130 ret[k] = a 131 } 132 for k, a := range overrideAttrs { 133 ret[k] = a 134 } 135 136 return ret, diags 137 } 138 139 func (b mergeBody) MissingItemRange() hcl.Range { 140 return b.Base.MissingItemRange() 141 }