Skip to content

Commit

Permalink
Satisfy Eureka 1.2.2 and later when registering
Browse files Browse the repository at this point in the history
As of Eureka version 1.2.2 and later, when registering an instance
encoded as JSON, one must supply a value for the

  instance.dataCenterInfo.@Class

field, which must be set appropriately for the sibling "name" field's
value. Take care of setting this value for the "Amazon" and "MyOwn"
data center types, but allow callers to specify the value for their
own custom data center types for which we cannot anticipate a suitable
value.
  • Loading branch information
seh committed Jan 26, 2017
1 parent df904f6 commit 2b3ac93
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 42 deletions.
27 changes: 20 additions & 7 deletions marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ func (m metadataMap) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error
return nil
}

func metadataValue(i DataCenterInfo) interface{} {
func metadataValue(i *DataCenterInfo) interface{} {
if i.Name == Amazon {
return i.Metadata
}
Expand All @@ -334,7 +334,7 @@ var (

// MarshalXML is a custom XML marshaler for DataCenterInfo, writing either Metadata or AlternateMetadata
// depending on the type of data center indicated by the Name.
func (i DataCenterInfo) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
func (i *DataCenterInfo) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
if err := e.EncodeToken(start); err != nil {
return err
}
Expand All @@ -351,6 +351,7 @@ func (i DataCenterInfo) MarshalXML(e *xml.Encoder, start xml.StartElement) error

type preliminaryDataCenterInfo struct {
Name string `xml:"name" json:"name"`
Class string `xml:"-" json:"@class"`
Metadata metadataMap `xml:"metadata" json:"metadata"`
}

Expand Down Expand Up @@ -378,6 +379,7 @@ func populateAmazonMetadata(dst *AmazonMetadataType, src map[string]string) {

func adaptDataCenterInfo(dst *DataCenterInfo, src preliminaryDataCenterInfo) {
dst.Name = src.Name
dst.Class = src.Class
if src.Name == Amazon {
populateAmazonMetadata(&dst.Metadata, src.Metadata)
} else {
Expand All @@ -400,20 +402,31 @@ func (i *DataCenterInfo) UnmarshalXML(d *xml.Decoder, start xml.StartElement) er

// MarshalJSON is a custom JSON marshaler for DataCenterInfo, writing either Metadata or AlternateMetadata
// depending on the type of data center indicated by the Name.
func (i DataCenterInfo) MarshalJSON() ([]byte, error) {
func (i *DataCenterInfo) MarshalJSON() ([]byte, error) {
type named struct {
Name string `json:"name"`
Name string `json:"name"`
Class string `json:"@class"`
}
if i.Name == Amazon {
return json.Marshal(struct {
named
Metadata AmazonMetadataType `json:"metadata"`
}{named{i.Name}, i.Metadata})
}{
named{i.Name, "com.netflix.appinfo.AmazonInfo"},
i.Metadata,
})
}
class := "com.netflix.appinfo.MyDataCenterInfo"
if i.Name != MyOwn {
class = i.Class
}
return json.Marshal(struct {
named
Metadata map[string]string `json:"metadata"`
}{named{i.Name}, i.AlternateMetadata})
Metadata map[string]string `json:"metadata,omitempty"`
}{
named{i.Name, class},
i.AlternateMetadata,
})
}

// UnmarshalJSON is a custom JSON unmarshaler for DataCenterInfo, populating either Metadata or AlternateMetadata
Expand Down
3 changes: 3 additions & 0 deletions net.go
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,9 @@ func (e *EurekaConnection) ReregisterInstance(ins *Instance) error {
} else {
out, err = e.marshal(ins)
}
if err != nil {
return err
}

body, rcode, err := postBody(reqURL, out, e.UseJson)
if err != nil {
Expand Down
8 changes: 8 additions & 0 deletions struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,14 @@ type AmazonMetadataType struct {
type DataCenterInfo struct {
// Name indicates which type of data center hosts this instance.
Name string
// Class indicates the Java class name representing this structure in the Eureka server,
// noted only when encoding communication with JSON.
//
// When registering an instance, if the name is neither "Amazon" nor "MyOwn", this field's
// value is used. Otherwise, a suitable default value will be supplied to the server. This field
// is available for specifying custom data center types other than the two built-in ones, for
// which no suitable default value could be known.
Class string
// Metadata provides details specific to an Amazon data center,
// populated and honored when the Name field's value is "Amazon".
Metadata AmazonMetadataType
Expand Down
41 changes: 35 additions & 6 deletions tests/marshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ func TestDataCenterInfoMarshal(t *testing.T) {

Convey("When the data center name is \"Amazon\"", func() {
ins.DataCenterInfo.Name = fargo.Amazon
ins.DataCenterInfo.Class = "ignored"
ins.DataCenterInfo.Metadata.InstanceID = "123"
ins.DataCenterInfo.Metadata.HostName = "expected.local"

Expand All @@ -233,14 +234,16 @@ func TestDataCenterInfoMarshal(t *testing.T) {

Convey("The marshalled JSON should have these values", func() {
So(err, ShouldBeNil)
So(string(b), ShouldEqual, `{"name":"Amazon","metadata":{"ami-launch-index":"","local-hostname":"","availability-zone":"","instance-id":"123","public-ipv4":"","public-hostname":"","ami-manifest-path":"","local-ipv4":"","hostname":"expected.local","ami-id":"","instance-type":""}}`)
So(string(b), ShouldEqual, `{"name":"Amazon","@class":"com.netflix.appinfo.AmazonInfo","metadata":{"ami-launch-index":"","local-hostname":"","availability-zone":"","instance-id":"123","public-ipv4":"","public-hostname":"","ami-manifest-path":"","local-ipv4":"","hostname":"expected.local","ami-id":"","instance-type":""}}`)

Convey("The value unmarshalled from JSON should have the same values as the original", func() {
d := fargo.DataCenterInfo{}
err := json.Unmarshal(b, &d)

So(err, ShouldBeNil)
So(d, ShouldResemble, ins.DataCenterInfo)
expected := ins.DataCenterInfo
expected.Class = "com.netflix.appinfo.AmazonInfo"
So(d, ShouldResemble, expected)
})
})
})
Expand All @@ -257,25 +260,49 @@ func TestDataCenterInfoMarshal(t *testing.T) {
err := xml.Unmarshal(b, &d)

So(err, ShouldBeNil)
So(d, ShouldResemble, ins.DataCenterInfo)
expected := ins.DataCenterInfo
expected.Class = ""
So(d, ShouldResemble, expected)
})
})
})
})

Convey("When the data center name is not \"Amazon\"", func() {
ins.DataCenterInfo.Name = fargo.MyOwn
ins.DataCenterInfo.Class = "ignored"
ins.DataCenterInfo.AlternateMetadata = map[string]string{
"instanceId": "123",
"hostName": "expected.local",
}

Convey("When the data center info is marshalled as JSON", func() {
Convey("When the data center info has no class specified and is marshalled as JSON", func() {
b, err := json.Marshal(&ins.DataCenterInfo)

Convey("The marshalled JSON should have these values", func() {
So(err, ShouldBeNil)
So(string(b), ShouldEqual, `{"name":"MyOwn","@class":"com.netflix.appinfo.MyDataCenterInfo","metadata":{"hostName":"expected.local","instanceId":"123"}}`)

Convey("The value unmarshalled from JSON should have the same values as the original", func() {
d := fargo.DataCenterInfo{}
err := json.Unmarshal(b, &d)

So(err, ShouldBeNil)
expected := ins.DataCenterInfo
expected.Class = "com.netflix.appinfo.MyDataCenterInfo"
So(d, ShouldResemble, expected)
})
})
})

Convey("When the data center info has both a custom name and class specified and is marshalled as JSON", func() {
ins.DataCenterInfo.Name = "Custom"
ins.DataCenterInfo.Class = "custom"
b, err := json.Marshal(&ins.DataCenterInfo)

Convey("The marshalled JSON should have these values", func() {
So(err, ShouldBeNil)
So(string(b), ShouldEqual, `{"name":"MyOwn","metadata":{"hostName":"expected.local","instanceId":"123"}}`)
So(string(b), ShouldEqual, `{"name":"Custom","@class":"custom","metadata":{"hostName":"expected.local","instanceId":"123"}}`)

Convey("The value unmarshalled from JSON should have the same values as the original", func() {
d := fargo.DataCenterInfo{}
Expand All @@ -301,7 +328,9 @@ func TestDataCenterInfoMarshal(t *testing.T) {
err := xml.Unmarshal(b, &d)

So(err, ShouldBeNil)
So(d, ShouldResemble, ins.DataCenterInfo)
expected := ins.DataCenterInfo
expected.Class = ""
So(d, ShouldResemble, expected)
})
})
})
Expand Down
73 changes: 44 additions & 29 deletions tests/net_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,25 +279,25 @@ func TestReregistration(t *testing.T) {
Convey("Instance registers correctly", func() {
err := e.RegisterInstance(&i)
So(err, ShouldBeNil)
})
})

Convey("Reregister the TESTAPP instance", t, func() {
Convey("Instance reregisters correctly", func() {
err := e.ReregisterInstance(&i)
So(err, ShouldBeNil)
})
Convey("Reregister the TESTAPP instance", func() {
Convey("Instance reregisters correctly", func() {
err := e.ReregisterInstance(&i)
So(err, ShouldBeNil)

Convey("Instance can check in", func() {
err := e.HeartBeatInstance(&i)
So(err, ShouldBeNil)
})
Convey("Instance can check in", func() {
err := e.HeartBeatInstance(&i)
So(err, ShouldBeNil)
})

Convey("Instance can be gotten correctly", func() {
ii, err := e.GetInstance(i.App, i.HostName)
So(err, ShouldBeNil)
So(ii.App, ShouldEqual, i.App)
So(ii.HostName, ShouldEqual, i.HostName)
Convey("Instance can be gotten correctly", func() {
ii, err := e.GetInstance(i.App, i.HostName)
So(err, ShouldBeNil)
So(ii.App, ShouldEqual, i.App)
So(ii.HostName, ShouldEqual, i.HostName)
})
})
})
})
})
}
Expand Down Expand Up @@ -370,6 +370,17 @@ func TestUpdateStatus(t *testing.T) {

func TestMetadataReading(t *testing.T) {
e, _ := fargo.NewConnFromConfigFile("./config_sample/local.gcfg")
i := fargo.Instance{
HostName: "i-123456",
Port: 9090,
PortEnabled: true,
App: "TESTAPP",
IPAddr: "127.0.0.10",
VipAddress: "127.0.0.10",
DataCenterInfo: fargo.DataCenterInfo{Name: fargo.MyOwn},
SecureVipAddress: "127.0.0.10",
Status: fargo.UP,
}
for _, j := range []bool{false, true} {
e.UseJson = j
Convey("Read empty instance metadata", t, func() {
Expand All @@ -379,19 +390,23 @@ func TestMetadataReading(t *testing.T) {
_, err = i.Metadata.GetString("SomeProp")
So(err, ShouldBeNil)
})
Convey("Read valid instance metadata", t, func() {
a, err := e.GetApp("TESTAPP")
So(err, ShouldBeNil)
So(len(a.Instances), ShouldBeGreaterThan, 0)
if len(a.Instances) == 0 {
return
}
i := a.Instances[0]
err = e.AddMetadataString(i, "SomeProp", "AValue")
So(err, ShouldBeNil)
v, err := i.Metadata.GetString("SomeProp")
So(err, ShouldBeNil)
So(v, ShouldEqual, "AValue")
Convey("Register an instance to TESTAPP", t, func() {
Convey("Instance registers correctly", func() {
err := e.RegisterInstance(&i)
So(err, ShouldBeNil)

Convey("Read valid instance metadata", func() {
a, err := e.GetApp("TESTAPP")
So(err, ShouldBeNil)
So(len(a.Instances), ShouldBeGreaterThan, 0)
i := a.Instances[0]
err = e.AddMetadataString(i, "SomeProp", "AValue")
So(err, ShouldBeNil)
v, err := i.Metadata.GetString("SomeProp")
So(err, ShouldBeNil)
So(v, ShouldEqual, "AValue")
})
})
})
}
}

0 comments on commit 2b3ac93

Please sign in to comment.