github.com/ndarilek/terraform@v0.3.8-0.20150320140257-d3135c1b2bac/helper/schema/field_reader_diff.go (about) 1 package schema 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/hashicorp/terraform/terraform" 8 "github.com/mitchellh/mapstructure" 9 ) 10 11 // DiffFieldReader reads fields out of a diff structures. 12 // 13 // It also requires access to a Reader that reads fields from the structure 14 // that the diff was derived from. This is usually the state. This is required 15 // because a diff on its own doesn't have complete data about full objects 16 // such as maps. 17 // 18 // The Source MUST be the data that the diff was derived from. If it isn't, 19 // the behavior of this struct is undefined. 20 // 21 // Reading fields from a DiffFieldReader is identical to reading from 22 // Source except the diff will be applied to the end result. 23 // 24 // The "Exists" field on the result will be set to true if the complete 25 // field exists whether its from the source, diff, or a combination of both. 26 // It cannot be determined whether a retrieved value is composed of 27 // diff elements. 28 type DiffFieldReader struct { 29 Diff *terraform.InstanceDiff 30 Source FieldReader 31 Schema map[string]*Schema 32 } 33 34 func (r *DiffFieldReader) ReadField(address []string) (FieldReadResult, error) { 35 schemaList := addrToSchema(address, r.Schema) 36 if len(schemaList) == 0 { 37 return FieldReadResult{}, nil 38 } 39 40 schema := schemaList[len(schemaList)-1] 41 switch schema.Type { 42 case TypeBool: 43 fallthrough 44 case TypeInt: 45 fallthrough 46 case TypeFloat: 47 fallthrough 48 case TypeString: 49 return r.readPrimitive(address, schema) 50 case TypeList: 51 return readListField(r, address, schema) 52 case TypeMap: 53 return r.readMap(address, schema) 54 case TypeSet: 55 return r.readSet(address, schema) 56 case typeObject: 57 return readObjectField(r, address, schema.Elem.(map[string]*Schema)) 58 default: 59 panic(fmt.Sprintf("Unknown type: %#v", schema.Type)) 60 } 61 } 62 63 func (r *DiffFieldReader) readMap( 64 address []string, schema *Schema) (FieldReadResult, error) { 65 result := make(map[string]interface{}) 66 resultSet := false 67 68 // First read the map from the underlying source 69 source, err := r.Source.ReadField(address) 70 if err != nil { 71 return FieldReadResult{}, err 72 } 73 if source.Exists { 74 result = source.Value.(map[string]interface{}) 75 resultSet = true 76 } 77 78 // Next, read all the elements we have in our diff, and apply 79 // the diff to our result. 80 prefix := strings.Join(address, ".") + "." 81 for k, v := range r.Diff.Attributes { 82 if !strings.HasPrefix(k, prefix) { 83 continue 84 } 85 if strings.HasPrefix(k, prefix+"#") { 86 // Ignore the count field 87 continue 88 } 89 90 resultSet = true 91 92 k = k[len(prefix):] 93 if v.NewRemoved { 94 delete(result, k) 95 continue 96 } 97 98 result[k] = v.New 99 } 100 101 var resultVal interface{} 102 if resultSet { 103 resultVal = result 104 } 105 106 return FieldReadResult{ 107 Value: resultVal, 108 Exists: resultSet, 109 }, nil 110 } 111 112 func (r *DiffFieldReader) readPrimitive( 113 address []string, schema *Schema) (FieldReadResult, error) { 114 result, err := r.Source.ReadField(address) 115 if err != nil { 116 return FieldReadResult{}, err 117 } 118 119 attrD, ok := r.Diff.Attributes[strings.Join(address, ".")] 120 if !ok { 121 return result, nil 122 } 123 124 var resultVal string 125 if !attrD.NewComputed { 126 resultVal = attrD.New 127 if attrD.NewExtra != nil { 128 result.ValueProcessed = resultVal 129 if err := mapstructure.WeakDecode(attrD.NewExtra, &resultVal); err != nil { 130 return FieldReadResult{}, err 131 } 132 } 133 } 134 135 result.Computed = attrD.NewComputed 136 result.Exists = true 137 result.Value, err = stringToPrimitive(resultVal, false, schema) 138 if err != nil { 139 return FieldReadResult{}, err 140 } 141 142 return result, nil 143 } 144 145 func (r *DiffFieldReader) readSet( 146 address []string, schema *Schema) (FieldReadResult, error) { 147 prefix := strings.Join(address, ".") + "." 148 149 // Create the set that will be our result 150 set := &Set{F: schema.Set} 151 152 // Go through the map and find all the set items 153 for k, _ := range r.Diff.Attributes { 154 if !strings.HasPrefix(k, prefix) { 155 continue 156 } 157 if strings.HasSuffix(k, "#") { 158 // Ignore any count field 159 continue 160 } 161 162 // Split the key, since it might be a sub-object like "idx.field" 163 parts := strings.Split(k[len(prefix):], ".") 164 idx := parts[0] 165 166 raw, err := r.ReadField(append(address, idx)) 167 if err != nil { 168 return FieldReadResult{}, err 169 } 170 if !raw.Exists { 171 // This shouldn't happen because we just verified it does exist 172 panic("missing field in set: " + k + "." + idx) 173 } 174 175 set.Add(raw.Value) 176 } 177 178 // Determine if the set "exists". It exists if there are items or if 179 // the diff explicitly wanted it empty. 180 exists := set.Len() > 0 181 if !exists { 182 // We could check if the diff value is "0" here but I think the 183 // existence of "#" on its own is enough to show it existed. This 184 // protects us in the future from the zero value changing from 185 // "0" to "" breaking us (if that were to happen). 186 if _, ok := r.Diff.Attributes[prefix+"#"]; ok { 187 exists = true 188 } 189 } 190 191 return FieldReadResult{ 192 Value: set, 193 Exists: exists, 194 }, nil 195 }