github.com/pmcatominey/terraform@v0.7.0-rc2.0.20160708105029-1401a52a5cc5/helper/diff/resource_builder.go (about) 1 package diff 2 3 import ( 4 "strings" 5 6 "github.com/hashicorp/terraform/flatmap" 7 "github.com/hashicorp/terraform/terraform" 8 ) 9 10 // AttrType is an enum that tells the ResourceBuilder what type of attribute 11 // an attribute is, affecting the overall diff output. 12 // 13 // The valid values are: 14 // 15 // * AttrTypeCreate - This attribute can only be set or updated on create. 16 // This means that if this attribute is changed, it will require a new 17 // resource to be created if it is already created. 18 // 19 // * AttrTypeUpdate - This attribute can be set at create time or updated 20 // in-place. Changing this attribute does not require a new resource. 21 // 22 type AttrType byte 23 24 const ( 25 AttrTypeUnknown AttrType = iota 26 AttrTypeCreate 27 AttrTypeUpdate 28 ) 29 30 // ResourceBuilder is a helper that knows about how a single resource 31 // changes and how those changes affect the diff. 32 type ResourceBuilder struct { 33 // Attrs are the mapping of attributes that can be set from the 34 // configuration, and the affect they have. See the documentation for 35 // AttrType for more info. 36 // 37 // Sometimes attributes in here are also computed. For example, an 38 // "availability_zone" might be optional, but will be chosen for you 39 // by AWS. In that case, specify it both here and in ComputedAttrs. 40 // This will make sure that the absence of the configuration won't 41 // cause a diff by setting it to the empty string. 42 Attrs map[string]AttrType 43 44 // ComputedAttrs are the attributes that are computed at 45 // resource creation time. 46 ComputedAttrs []string 47 48 // ComputedAttrsUpdate are the attributes that are computed 49 // at resource update time (this includes creation). 50 ComputedAttrsUpdate []string 51 52 // PreProcess is a mapping of exact keys that are sent through 53 // a pre-processor before comparing values. The original value will 54 // be put in the "NewExtra" field of the diff. 55 PreProcess map[string]PreProcessFunc 56 } 57 58 // PreProcessFunc is used with the PreProcess field in a ResourceBuilder 59 type PreProcessFunc func(string) string 60 61 // Diff returns the ResourceDiff for a resource given its state and 62 // configuration. 63 func (b *ResourceBuilder) Diff( 64 s *terraform.InstanceState, 65 c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { 66 attrs := make(map[string]*terraform.ResourceAttrDiff) 67 68 // We require a new resource if the ID is empty. Or, later, we set 69 // this to true if any configuration changed that triggers a new resource. 70 requiresNew := s.ID == "" 71 72 // Flatten the raw and processed configuration 73 flatRaw := flatmap.Flatten(c.Raw) 74 flatConfig := flatmap.Flatten(c.Config) 75 76 for ak, at := range b.Attrs { 77 // Keep track of all the keys we saw in the raw structure 78 // so that we can prune our attributes later. 79 seenKeys := make([]string, 0) 80 81 // Go through and find the added/changed keys in flatRaw 82 for k, v := range flatRaw { 83 // Find only the attributes that match our prefix 84 if !strings.HasPrefix(k, ak) { 85 continue 86 } 87 88 // Track that we saw this key 89 seenKeys = append(seenKeys, k) 90 91 // We keep track of this in case we have a pre-processor 92 // so that we can store the original value still. 93 originalV := v 94 95 // If this key is in the cleaned config, then use that value 96 // because it'll have its variables properly interpolated 97 if cleanV, ok := flatConfig[k]; ok { 98 v = cleanV 99 originalV = v 100 101 // If we have a pre-processor for this, run it. 102 if pp, ok := b.PreProcess[k]; ok { 103 v = pp(v) 104 } 105 } 106 107 oldV, ok := s.Attributes[k] 108 109 // If there is an old value and they're the same, no change 110 if ok && oldV == v { 111 continue 112 } 113 114 // Record the change 115 attrs[k] = &terraform.ResourceAttrDiff{ 116 Old: oldV, 117 New: v, 118 NewExtra: originalV, 119 Type: terraform.DiffAttrInput, 120 } 121 122 // If this requires a new resource, record that and flag our 123 // boolean. 124 if at == AttrTypeCreate { 125 attrs[k].RequiresNew = true 126 requiresNew = true 127 } 128 } 129 130 // Find all the keys that are in our attributes right now that 131 // we also care about. 132 matchingKeys := make(map[string]struct{}) 133 for k, _ := range s.Attributes { 134 // Find only the attributes that match our prefix 135 if !strings.HasPrefix(k, ak) { 136 continue 137 } 138 139 // If this key is computed, then we don't ever delete it 140 comp := false 141 for _, ck := range b.ComputedAttrs { 142 if ck == k { 143 comp = true 144 break 145 } 146 147 // If the key is prefixed with the computed key, don't 148 // mark it for delete, ever. 149 if strings.HasPrefix(k, ck+".") { 150 comp = true 151 break 152 } 153 } 154 if comp { 155 continue 156 } 157 158 matchingKeys[k] = struct{}{} 159 } 160 161 // Delete the keys we saw in the configuration from the keys 162 // that are currently set. 163 for _, k := range seenKeys { 164 delete(matchingKeys, k) 165 } 166 for k, _ := range matchingKeys { 167 attrs[k] = &terraform.ResourceAttrDiff{ 168 Old: s.Attributes[k], 169 NewRemoved: true, 170 Type: terraform.DiffAttrInput, 171 } 172 } 173 } 174 175 // If we require a new resource, then process all the attributes 176 // that will be changing due to the creation of the resource. 177 if requiresNew { 178 for _, k := range b.ComputedAttrs { 179 if _, ok := attrs[k]; ok { 180 continue 181 } 182 183 old := s.Attributes[k] 184 attrs[k] = &terraform.ResourceAttrDiff{ 185 Old: old, 186 NewComputed: true, 187 Type: terraform.DiffAttrOutput, 188 } 189 } 190 } 191 192 // If we're changing anything, then mark the updated 193 // attributes. 194 if len(attrs) > 0 { 195 for _, k := range b.ComputedAttrsUpdate { 196 if _, ok := attrs[k]; ok { 197 continue 198 } 199 200 old := s.Attributes[k] 201 attrs[k] = &terraform.ResourceAttrDiff{ 202 Old: old, 203 NewComputed: true, 204 Type: terraform.DiffAttrOutput, 205 } 206 } 207 } 208 209 // Build our resulting diff if we had attributes change 210 var result *terraform.InstanceDiff 211 if len(attrs) > 0 { 212 result = &terraform.InstanceDiff{ 213 Attributes: attrs, 214 } 215 } 216 217 return result, nil 218 }