@@ -5,6 +5,7 @@ package fargo
5
5
import (
6
6
"encoding/json"
7
7
"encoding/xml"
8
+ "io"
8
9
"strconv"
9
10
)
10
11
@@ -122,17 +123,22 @@ func (i *InstanceMetadata) MarshalJSON() ([]byte, error) {
122
123
return i .Raw , nil
123
124
}
124
125
126
+ // startLocalName creates a start-tag of an XML element with the given local name and no namespace name.
127
+ func startLocalName (local string ) xml.StartElement {
128
+ return xml.StartElement {Name : xml.Name {Space : "" , Local : local }}
129
+ }
130
+
125
131
// MarshalXML is a custom XML marshaler for InstanceMetadata.
126
132
func (i InstanceMetadata ) MarshalXML (e * xml.Encoder , start xml.StartElement ) error {
127
133
tokens := []xml.Token {start }
128
134
129
135
if i .parsed != nil {
130
136
for key , value := range i .parsed {
131
- t := xml. StartElement { Name : xml. Name { "" , key }}
132
- tokens = append (tokens , t , xml .CharData (value .(string )), xml.EndElement {t .Name })
137
+ t := startLocalName ( key )
138
+ tokens = append (tokens , t , xml .CharData (value .(string )), xml.EndElement {Name : t .Name })
133
139
}
134
140
}
135
- tokens = append (tokens , xml.EndElement {start .Name })
141
+ tokens = append (tokens , xml.EndElement {Name : start .Name })
136
142
137
143
for _ , t := range tokens {
138
144
err := e .EncodeToken (t )
@@ -144,3 +150,152 @@ func (i InstanceMetadata) MarshalXML(e *xml.Encoder, start xml.StartElement) err
144
150
// flush to ensure tokens are written
145
151
return e .Flush ()
146
152
}
153
+
154
+ type metadataMap map [string ]string
155
+
156
+ // MarshalXML is a custom XML marshaler for metadataMap, mapping each metadata name/value pair to a
157
+ // correspondingly named XML element with the pair's value as character data content.
158
+ func (m metadataMap ) MarshalXML (e * xml.Encoder , start xml.StartElement ) error {
159
+ if err := e .EncodeToken (start ); err != nil {
160
+ return err
161
+ }
162
+
163
+ for k , v := range m {
164
+ if err := e .EncodeElement (v , startLocalName (k )); err != nil {
165
+ return err
166
+ }
167
+ }
168
+
169
+ return e .EncodeToken (start .End ())
170
+ }
171
+
172
+ // UnmarshalXML is a custom XML unmarshaler for metadataMap, mapping each XML element's name and
173
+ // character data content to a corresponding metadata name/value pair.
174
+ func (m metadataMap ) UnmarshalXML (d * xml.Decoder , start xml.StartElement ) error {
175
+ var v string
176
+ for {
177
+ t , err := d .Token ()
178
+ if err != nil {
179
+ if err == io .EOF {
180
+ break
181
+ }
182
+ return err
183
+ }
184
+ if k , ok := t .(xml.StartElement ); ok {
185
+ if err := d .DecodeElement (& v , & k ); err != nil {
186
+ return err
187
+ }
188
+ m [k .Name .Local ] = v
189
+ }
190
+ }
191
+ return nil
192
+ }
193
+
194
+ func metadataValue (i DataCenterInfo ) interface {} {
195
+ if i .Name == Amazon {
196
+ return i .Metadata
197
+ }
198
+ return metadataMap (i .AlternateMetadata )
199
+ }
200
+
201
+ var (
202
+ startName = startLocalName ("name" )
203
+ startMetadata = startLocalName ("metadata" )
204
+ )
205
+
206
+ // MarshalXML is a custom XML marshaler for DataCenterInfo, writing either Metadata or AlternateMetadata
207
+ // depending on the type of data center indicated by the Name.
208
+ func (i DataCenterInfo ) MarshalXML (e * xml.Encoder , start xml.StartElement ) error {
209
+ if err := e .EncodeToken (start ); err != nil {
210
+ return err
211
+ }
212
+
213
+ if err := e .EncodeElement (i .Name , startName ); err != nil {
214
+ return err
215
+ }
216
+ if err := e .EncodeElement (metadataValue (i ), startMetadata ); err != nil {
217
+ return err
218
+ }
219
+
220
+ return e .EncodeToken (start .End ())
221
+ }
222
+
223
+ type preliminaryDataCenterInfo struct {
224
+ Name string `xml:"name" json:"name"`
225
+ Metadata metadataMap `xml:"metadata" json:"metadata"`
226
+ }
227
+
228
+ func bindValue (dst * string , src map [string ]string , k string ) bool {
229
+ if v , ok := src [k ]; ok {
230
+ * dst = v
231
+ return true
232
+ }
233
+ return false
234
+ }
235
+
236
+ func populateAmazonMetadata (dst * AmazonMetadataType , src map [string ]string ) {
237
+ bindValue (& dst .AmiLaunchIndex , src , "ami-launch-index" )
238
+ bindValue (& dst .LocalHostname , src , "local-hostname" )
239
+ bindValue (& dst .AvailabilityZone , src , "availability-zone" )
240
+ bindValue (& dst .InstanceID , src , "instance-id" )
241
+ bindValue (& dst .PublicIpv4 , src , "public-ipv4" )
242
+ bindValue (& dst .PublicHostname , src , "public-hostname" )
243
+ bindValue (& dst .AmiManifestPath , src , "ami-manifest-path" )
244
+ bindValue (& dst .LocalIpv4 , src , "local-ipv4" )
245
+ bindValue (& dst .HostName , src , "hostname" )
246
+ bindValue (& dst .AmiID , src , "ami-id" )
247
+ bindValue (& dst .InstanceType , src , "instance-type" )
248
+ }
249
+
250
+ func adaptDataCenterInfo (dst * DataCenterInfo , src preliminaryDataCenterInfo ) {
251
+ dst .Name = src .Name
252
+ if src .Name == Amazon {
253
+ populateAmazonMetadata (& dst .Metadata , src .Metadata )
254
+ } else {
255
+ dst .AlternateMetadata = src .Metadata
256
+ }
257
+ }
258
+
259
+ // UnmarshalXML is a custom XML unmarshaler for DataCenterInfo, populating either Metadata or AlternateMetadata
260
+ // depending on the type of data center indicated by the Name.
261
+ func (i * DataCenterInfo ) UnmarshalXML (d * xml.Decoder , start xml.StartElement ) error {
262
+ p := preliminaryDataCenterInfo {
263
+ Metadata : make (map [string ]string , 11 ),
264
+ }
265
+ if err := d .DecodeElement (& p , & start ); err != nil {
266
+ return err
267
+ }
268
+ adaptDataCenterInfo (i , p )
269
+ return nil
270
+ }
271
+
272
+ // MarshalJSON is a custom JSON marshaler for DataCenterInfo, writing either Metadata or AlternateMetadata
273
+ // depending on the type of data center indicated by the Name.
274
+ func (i DataCenterInfo ) MarshalJSON () ([]byte , error ) {
275
+ type named struct {
276
+ Name string `json:"name"`
277
+ }
278
+ if i .Name == Amazon {
279
+ return json .Marshal (struct {
280
+ named
281
+ Metadata AmazonMetadataType `json:"metadata"`
282
+ }{named {i .Name }, i .Metadata })
283
+ }
284
+ return json .Marshal (struct {
285
+ named
286
+ Metadata map [string ]string `json:"metadata"`
287
+ }{named {i .Name }, i .AlternateMetadata })
288
+ }
289
+
290
+ // UnmarshalJSON is a custom JSON unmarshaler for DataCenterInfo, populating either Metadata or AlternateMetadata
291
+ // depending on the type of data center indicated by the Name.
292
+ func (i * DataCenterInfo ) UnmarshalJSON (b []byte ) error {
293
+ p := preliminaryDataCenterInfo {
294
+ Metadata : make (map [string ]string , 11 ),
295
+ }
296
+ if err := json .Unmarshal (b , & p ); err != nil {
297
+ return err
298
+ }
299
+ adaptDataCenterInfo (i , p )
300
+ return nil
301
+ }
0 commit comments