Skip to content

Commit

Permalink
Accommodate JSON data center metadata translation
Browse files Browse the repository at this point in the history
When the Eureka server encodes an instance as JSON, it will sometimes
write the data center metadata values that look like numbers as JSON
numbers, rather than writing all metadata values as strings. Demanding
that all metadata values arrive as JSON strings precludes proper
decoding of instances, so be more flexible by first decoding the
metadata values as any JSON type, and then projecting the values that
are not strings back to strings.

Note that we only project decoded Go types bool, float64, and nil;
while it's possible that Go can decode a value of type []interface{}
or map[string]interface{}, we make no effort to project those values.
  • Loading branch information
seh committed Jan 26, 2017
1 parent 2b3ac93 commit 6e72b26
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 7 deletions.
40 changes: 34 additions & 6 deletions marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ func populateAmazonMetadata(dst *AmazonMetadataType, src map[string]string) {
bindValue(&dst.InstanceType, src, "instance-type")
}

func adaptDataCenterInfo(dst *DataCenterInfo, src preliminaryDataCenterInfo) {
func adaptDataCenterInfo(dst *DataCenterInfo, src *preliminaryDataCenterInfo) {
dst.Name = src.Name
dst.Class = src.Class
if src.Name == Amazon {
Expand All @@ -396,7 +396,7 @@ func (i *DataCenterInfo) UnmarshalXML(d *xml.Decoder, start xml.StartElement) er
if err := d.DecodeElement(&p, &start); err != nil {
return err
}
adaptDataCenterInfo(i, p)
adaptDataCenterInfo(i, &p)
return nil
}

Expand Down Expand Up @@ -429,15 +429,43 @@ func (i *DataCenterInfo) MarshalJSON() ([]byte, error) {
})
}

func jsonValueAsString(i interface{}) string {
switch v := i.(type) {
case string:
return v
case float64:
return fmt.Sprintf("%.f", v)
case bool:
return strconv.FormatBool(v)
case []interface{}, map[string]interface{}:
// Don't bother trying to decode these.
return ""
case nil:
return ""
default:
panic("type of unexpected value")
}
}

// UnmarshalJSON is a custom JSON unmarshaler for DataCenterInfo, populating either Metadata or AlternateMetadata
// depending on the type of data center indicated by the Name.
func (i *DataCenterInfo) UnmarshalJSON(b []byte) error {
p := preliminaryDataCenterInfo{
Metadata: make(map[string]string, 11),
// The Eureka server will mistakenly convert metadata values that look like numbers to JSON numbers.
// Convert them back to strings.
aux := struct {
*preliminaryDataCenterInfo
PreliminaryMetadata map[string]interface{} `json:"metadata"`
}{
PreliminaryMetadata: make(map[string]interface{}, 11),
}
if err := json.Unmarshal(b, &p); err != nil {
if err := json.Unmarshal(b, &aux); err != nil {
return err
}
adaptDataCenterInfo(i, p)
metadata := make(map[string]string, len(aux.PreliminaryMetadata))
for k, v := range aux.PreliminaryMetadata {
metadata[k] = jsonValueAsString(v)
}
aux.Metadata = metadata
adaptDataCenterInfo(i, aux.preliminaryDataCenterInfo)
return nil
}
2 changes: 1 addition & 1 deletion metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package fargo
import (
"encoding/json"
"fmt"

"github.com/clbanning/x2j"
)

Expand Down Expand Up @@ -32,7 +33,6 @@ func (ins *Instance) SetMetadataString(key, value string) {
func (im *InstanceMetadata) parse() error {
if len(im.Raw) == 0 {
im.parsed = make(map[string]interface{})
log.Debug("len(Metadata)==0. Quitting parsing.")
return nil
}
metadataLog.Debugf("InstanceMetadata.parse: %s", im.Raw)
Expand Down
10 changes: 10 additions & 0 deletions tests/marshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,16 @@ func TestDataCenterInfoMarshal(t *testing.T) {

So(err, ShouldBeNil)
So(d, ShouldResemble, ins.DataCenterInfo)

Convey("Even if the server translates strings to other types", func() {
translated := bytes.Replace(b, []byte(`"123"`), []byte("123"), 1)

d := fargo.DataCenterInfo{}
err := json.Unmarshal(translated, &d)

So(err, ShouldBeNil)
So(d, ShouldResemble, ins.DataCenterInfo)
})
})
})
})
Expand Down

0 comments on commit 6e72b26

Please sign in to comment.