Skip to content

Commit

Permalink
Merge pull request hudl#4 from cquinn/master
Browse files Browse the repository at this point in the history
Added ReregisterInstance courtesy of @cquinn
  • Loading branch information
ryansb committed Jun 9, 2014
2 parents 79ea519 + c4574c4 commit 81866b7
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 19 deletions.
57 changes: 53 additions & 4 deletions net.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ func (e EurekaConnection) AddMetadataString(ins *Instance, key, value string) er
return nil
}

// RegisterInstance will register the relevant Instance with eureka but DOES
// NOT automatically send heartbeats. See HeartBeatInstance for that
// RegisterInstance will register the given Instance with eureka if it is not already registered,
// but DOES NOT automatically send heartbeats. See HeartBeatInstance for that
// functionality
func (e *EurekaConnection) RegisterInstance(ins *Instance) error {
slug := fmt.Sprintf("%s/%s", EurekaURLSlugs["Apps"], ins.App)
Expand All @@ -112,10 +112,18 @@ func (e *EurekaConnection) RegisterInstance(ins *Instance) error {
return nil
}
log.Notice("Instance=%s not yet registered with App=%s. Registering.", ins.HostName, ins.App)
return e.ReregisterInstance(ins)
}

// ReregisterInstance will register the given Instance with eureka but DOES
// NOT automatically send heartbeats. See HeartBeatInstance for that
// functionality
func (e *EurekaConnection) ReregisterInstance(ins *Instance) error {
slug := fmt.Sprintf("%s/%s", EurekaURLSlugs["Apps"], ins.App)
reqURL := e.generateURL(slug)
out, err := xml.Marshal(ins)
if err != nil {
// marshal the xml *with* indents so it's readable if there's an error
// marshal the xml *with* indents so it's readable in the error message
out, _ := xml.MarshalIndent(ins, "", " ")
log.Error("Error marshalling XML Instance=%s App=%s. Error:\"%s\" XML body=\"%s\"", err.Error(), ins.HostName, ins.App, string(out))
return err
Expand All @@ -127,14 +135,55 @@ func (e *EurekaConnection) RegisterInstance(ins *Instance) error {
}
if rcode != 204 {
log.Warning("HTTP returned %d registering Instance=%s App=%s Body=\"%s\"", rcode, ins.HostName, ins.App, string(body))
return fmt.Errorf("http returned %d possible failure creating instance\n", rcode)
return fmt.Errorf("http returned %d possible failure registering instance\n", rcode)
}

// read back our registration to ensure that it stuck
body, rcode, err = getXML(reqURL + "/" + ins.HostName)
xml.Unmarshal(body, ins)
return nil
}

// DeregisterInstance will deregister the given Instance from eureka. This is good practice
// to do before exiting or otherwise going off line.
func (e *EurekaConnection) DeregisterInstance(ins *Instance) error {
slug := fmt.Sprintf("%s/%s/%s", EurekaURLSlugs["Apps"], ins.App, ins.HostName)
reqURL := e.generateURL(slug)
log.Debug("Deregistering instance with url %s", reqURL)

rcode, err := deleteReq(reqURL)
if err != nil {
log.Error("Could not complete deregistration Error: ", err.Error())
return err
}
if rcode != 204 {
log.Warning("HTTP returned %d deregistering Instance=%s App=%s", rcode, ins.HostName, ins.App)
return fmt.Errorf("http returned %d possible failure deregistering instance\n", rcode)
}

return nil
}

// UpdateInstanceStatus updates the status of a given instance with eureka.
func (e EurekaConnection) UpdateInstanceStatus(ins *Instance, status StatusType) error {
slug := fmt.Sprintf("%s/%s/%s/status", EurekaURLSlugs["Apps"], ins.App, ins.HostName)
reqURL := e.generateURL(slug)

params := map[string]string{"value": string(status)}

log.Debug("Updating instance status url=%s value=%s", reqURL, status)
body, rcode, err := putKV(reqURL, params)
if err != nil {
log.Error("Could not complete update with Error: ", err.Error())
return err
}
if rcode < 200 || rcode >= 300 {
log.Warning("HTTP returned %d updating status Instance=%s App=%s Body=\"%s\"", rcode, ins.HostName, ins.App, string(body))
return fmt.Errorf("http returned %d possible failure updating instance status ", rcode)
}
return nil
}

// HeartBeatInstance sends a single eureka heartbeat. Does not continue sending
// heartbeats. Errors if the response is not 200.
func (e *EurekaConnection) HeartBeatInstance(ins *Instance) error {
Expand Down
16 changes: 15 additions & 1 deletion rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func putKV(reqURL string, pairs map[string]string) ([]byte, int, error) {
params.Add(k, v)
}
parameterizedURL := reqURL + "?" + params.Encode()
log.Notice("Sending metadata request with URL %s", parameterizedURL)
log.Notice("Sending KV request with URL %s", parameterizedURL)
req, err := http.NewRequest("PUT", parameterizedURL, nil)
if err != nil {
log.Error("Could not create PUT %s with Error: %s", reqURL, err.Error())
Expand Down Expand Up @@ -61,6 +61,20 @@ func getXML(reqURL string) ([]byte, int, error) {
return body, rcode, nil
}

func deleteReq(reqURL string) (int, error) {
req, err := http.NewRequest("DELETE", reqURL, nil)
if err != nil {
log.Error("Could not create DELETE %s with Error: %s", reqURL, err.Error())
return -1, err
}
_, rcode, err := netReq(req)
if err != nil {
log.Error("Could not complete DELETE %s with Error: %s", reqURL, err.Error())
return rcode, err
}
return rcode, nil
}

func reqXML(req *http.Request) ([]byte, int, error) {
req.Header.Set("Content-Type", "application/xml")
req.Header.Set("Accept", "application/xml")
Expand Down
8 changes: 5 additions & 3 deletions struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ type StatusType string

// Supported statuses
const (
UP StatusType = "UP"
DOWN StatusType = "DOWN"
STARTING StatusType = "STARTING"
UP StatusType = "UP"
DOWN StatusType = "DOWN"
STARTING StatusType = "STARTING"
OUTOFSERVICE StatusType = "OUT_OF_SERVICE"
UNKNOWN StatusType = "UNKNOWN"
)

// Datacenter names
Expand Down
110 changes: 99 additions & 11 deletions tests/net_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ import (
"testing"
)

func TestConnectionCreation(t *testing.T) {
Convey("Pull applications", t, func() {
cfg, err := fargo.ReadConfig("./config_sample/local.gcfg")
So(err, ShouldBeNil)
e := fargo.NewConnFromConfig(cfg)
apps, err := e.GetApps()
So(err, ShouldBeNil)
So(len(apps["EUREKA"].Instances), ShouldEqual, 2)
})
}

func TestGetApps(t *testing.T) {
e, _ := fargo.NewConnFromConfigFile("./config_sample/local.gcfg")
Convey("Pull applications", t, func() {
Expand Down Expand Up @@ -67,21 +78,98 @@ func TestRegistration(t *testing.T) {
})
}

func TestConnectionCreation(t *testing.T) {
Convey("Pull applications", t, func() {
cfg, err := fargo.ReadConfig("./config_sample/local.gcfg")
So(err, ShouldBeNil)
e := fargo.NewConnFromConfig(cfg)
apps, err := e.GetApps()
So(err, ShouldBeNil)
So(len(apps["EUREKA"].Instances), ShouldEqual, 2)
func TestReregistration(t *testing.T) {
e, _ := fargo.NewConnFromConfigFile("./config_sample/local.gcfg")
i := fargo.Instance{
HostName: "i-123456",
Port: 9090,
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,
}
Convey("Register a TESTAPP instance", t, func() {
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("Instance can check in", func() {
err := e.HeartBeatInstance(&i)
So(err, ShouldBeNil)
})
})
}

func DontTestDeregistration(t *testing.T) {
e, _ := fargo.NewConnFromConfigFile("./config_sample/local.gcfg")
i := fargo.Instance{
HostName: "i-123456",
Port: 9090,
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,
}
Convey("Register a TESTAPP instance", t, func() {
Convey("Instance registers correctly", func() {
err := e.RegisterInstance(&i)
So(err, ShouldBeNil)
})
})
Convey("Deregister the TESTAPP instance", t, func() {
Convey("Instance deregisters correctly", func() {
err := e.DeregisterInstance(&i)
So(err, ShouldBeNil)
})
Convey("Instance cannot check in", func() {
err := e.HeartBeatInstance(&i)
So(err, ShouldNotBeNil)
})
})
}

func TestUpdateStatus(t *testing.T) {
e, _ := fargo.NewConnFromConfigFile("./config_sample/local.gcfg")
i := fargo.Instance{
HostName: "i-123456",
Port: 9090,
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,
}
Convey("Register an instance to TESTAPP", t, func() {
Convey("Instance registers correctly", func() {
err := e.RegisterInstance(&i)
So(err, ShouldBeNil)
})
})
Convey("Update an instance status", t, func() {
Convey("Instance updates to OUT_OF_SERVICE correctly", func() {
err := e.UpdateInstanceStatus(&i, fargo.OUTOFSERVICE)
So(err, ShouldBeNil)
})
Convey("Instance updates to UP corectly", func() {
err := e.UpdateInstanceStatus(&i, fargo.UP)
So(err, ShouldBeNil)
})
})
}

func TestMetadataReading(t *testing.T) {
cfg, err := fargo.ReadConfig("./config_sample/local.gcfg")
So(err, ShouldBeNil)
e := fargo.NewConnFromConfig(cfg)
e, _ := fargo.NewConnFromConfigFile("./config_sample/local.gcfg")
Convey("Read empty instance metadata", t, func() {
a, err := e.GetApp("EUREKA")
So(err, ShouldBeNil)
Expand Down

0 comments on commit 81866b7

Please sign in to comment.