diff --git a/connection.go b/connection.go
index 65487a0..c8900b1 100644
--- a/connection.go
+++ b/connection.go
@@ -60,7 +60,7 @@ func (e *EurekaConnection) UpdateApp(app *Application) {
go func() {
for {
log.Notice("Updating app %s", app.Name)
- err := e.readAppInto(app.Name, app)
+ err := e.readAppInto(app)
if err != nil {
log.Error("Failure updating %s in goroutine", app.Name)
}
diff --git a/log.go b/log.go
index 50a9378..910f3c3 100644
--- a/log.go
+++ b/log.go
@@ -7,3 +7,10 @@ import (
)
var log = logging.MustGetLogger("fargo")
+var metadataLog = logging.MustGetLogger("fargo.metadata")
+var marshalLog = logging.MustGetLogger("fargo.marshal")
+
+func init() {
+ logging.SetLevel(logging.WARNING, "fargo.metadata")
+ logging.SetLevel(logging.WARNING, "fargo.marshal")
+}
diff --git a/marshal.go b/marshal.go
new file mode 100644
index 0000000..a500661
--- /dev/null
+++ b/marshal.go
@@ -0,0 +1,109 @@
+package fargo
+
+// MIT Licensed (see README.md) - Copyright (c) 2013 Hudl <@Hudl>
+
+import (
+ "encoding/json"
+ "strconv"
+)
+
+// Temporary structs used for GetAppsResponse unmarshalling
+type getAppsResponseArray GetAppsResponse
+type getAppsResponseSingle struct {
+ Application *Application `json:"application"`
+ AppsHashcode string `json:"apps__hashcode"`
+ VersionsDelta int `json:"versions__delta"`
+}
+
+// UnmarshalJSON is a custom JSON unmarshaler for GetAppsResponse to deal with
+// sometimes non-wrapped Application arrays when there is only a single Application item.
+func (r *GetAppsResponse) UnmarshalJSON(b []byte) error {
+ marshalLog.Debug("GetAppsResponse.UnmarshalJSON b:%s\n", string(b))
+ var err error
+
+ // Normal array case
+ var ra getAppsResponseArray
+ if err = json.Unmarshal(b, &ra); err == nil {
+ marshalLog.Debug("GetAppsResponse.UnmarshalJSON ra:%+v\n", ra)
+ *r = GetAppsResponse(ra)
+ return nil
+ }
+ // Bogus non-wrapped case
+ var rs getAppsResponseSingle
+ if err = json.Unmarshal(b, &rs); err == nil {
+ marshalLog.Debug("GetAppsResponse.UnmarshalJSON rs:%+v\n", rs)
+ r.Applications = make([]*Application, 1, 1)
+ r.Applications[0] = rs.Application
+ r.AppsHashcode = rs.AppsHashcode
+ r.VersionsDelta = rs.VersionsDelta
+ return nil
+ }
+ return err
+}
+
+// Temporary structs used for Application unmarshalling
+type applicationArray Application
+type applicationSingle struct {
+ Name string `json:"name"`
+ Instance *Instance `json:"instance"`
+}
+
+// UnmarshalJSON is a custom JSON unmarshaler for Application to deal with
+// sometimes non-wrapped Instance array when there is only a single Instance item.
+func (a *Application) UnmarshalJSON(b []byte) error {
+ marshalLog.Debug("Application.UnmarshalJSON b:%s\n", string(b))
+ var err error
+
+ // Normal array case
+ var aa applicationArray
+ if err = json.Unmarshal(b, &aa); err == nil {
+ marshalLog.Debug("Application.UnmarshalJSON aa:%+v\n", aa)
+ *a = Application(aa)
+ return nil
+ }
+
+ // Bogus non-wrapped case
+ var as applicationSingle
+ if err = json.Unmarshal(b, &as); err == nil {
+ marshalLog.Debug("Application.UnmarshalJSON as:%+v\n", as)
+ a.Name = as.Name
+ a.Instances = make([]*Instance, 1, 1)
+ a.Instances[0] = as.Instance
+ return nil
+ }
+ return err
+}
+
+type instance Instance
+
+// UnmarshalJSON is a custom JSON unmarshaler for Instance to deal with the
+// different Port encodings between XML and JSON. Here we copy the values from the JSON
+// Port struct into the simple XML int field.
+func (i *Instance) UnmarshalJSON(b []byte) error {
+ var err error
+ var ii instance
+ if err = json.Unmarshal(b, &ii); err == nil {
+ marshalLog.Debug("Instance.UnmarshalJSON ii:%+v\n", ii)
+ *i = Instance(ii)
+ i.Port = parsePort(ii.PortJ.Number)
+ i.SecurePort = parsePort(ii.SecurePortJ.Number)
+ return nil
+ }
+ return err
+}
+
+func parsePort(s string) int {
+ n, err := strconv.Atoi(s)
+ if err != nil {
+ log.Warning("Invalid port error: %s", err.Error())
+ }
+ return n
+}
+
+// UnmarshalJSON is a custom JSON unmarshaler for InstanceMetadata to handle squirreling away
+// the raw JSON for later parsing.
+func (i *InstanceMetadata) UnmarshalJSON(b []byte) error {
+ i.Raw = b
+ // TODO(cq) could actually parse Raw here, and in a parallel UnmarshalXML as well.
+ return nil
+}
diff --git a/metadata.go b/metadata.go
index e59dde4..4721e8f 100644
--- a/metadata.go
+++ b/metadata.go
@@ -3,6 +3,7 @@ package fargo
// MIT Licensed (see README.md) - Copyright (c) 2013 Hudl <@Hudl>
import (
+ "encoding/json"
"fmt"
"github.com/clbanning/x2j"
)
@@ -12,7 +13,8 @@ func (a *Application) ParseAllMetadata() error {
for _, instance := range a.Instances {
err := instance.Metadata.parse()
if err != nil {
- log.Error("Failed parsing metadata for Instance=%s of Application=%s: ", instance.HostName, a.Name, err.Error())
+ log.Error("Failed parsing metadata for Instance=%s of Application=%s: %s",
+ instance.HostName, a.Name, err.Error())
return err
}
}
@@ -20,19 +22,30 @@ func (a *Application) ParseAllMetadata() error {
}
func (im *InstanceMetadata) parse() error {
- // wrap in a BS xml tag so all metadata tags are pulled
if len(im.Raw) == 0 {
im.parsed = make(map[string]interface{})
log.Debug("len(Metadata)==0. Quitting parsing.")
return nil
}
- fullDoc := append(append([]byte(""), im.Raw...), []byte("")...)
- parsedDoc, err := x2j.ByteDocToMap(fullDoc, true)
- if err != nil {
- log.Error("Error unmarshalling: ", err.Error())
- return fmt.Errorf("error unmarshalling: ", err.Error())
+ metadataLog.Debug("InstanceMetadata.parse: %s", im.Raw)
+
+ if len(im.Raw) > 0 && im.Raw[0] == '{' {
+ // JSON
+ err := json.Unmarshal(im.Raw, &im.parsed)
+ if err != nil {
+ log.Error("Error unmarshalling: %s", err.Error())
+ return fmt.Errorf("error unmarshalling: %s", err.Error())
+ }
+ } else {
+ // XML: wrap in a BS xml tag so all metadata tags are pulled
+ fullDoc := append(append([]byte(""), im.Raw...), []byte("")...)
+ parsedDoc, err := x2j.ByteDocToMap(fullDoc, true)
+ if err != nil {
+ log.Error("Error unmarshalling: %s", err.Error())
+ return fmt.Errorf("error unmarshalling: %s", err.Error())
+ }
+ im.parsed = parsedDoc["d"].(map[string]interface{})
}
- im.parsed = parsedDoc["d"].(map[string]interface{})
return nil
}
@@ -44,7 +57,7 @@ func (im *InstanceMetadata) GetMap() map[string]interface{} {
func (im *InstanceMetadata) getItem(key string) (interface{}, bool, error) {
err := im.parse()
if err != nil {
- return "", false, fmt.Errorf("parsing error: ", err.Error())
+ return "", false, fmt.Errorf("parsing error: %s", err.Error())
}
val, present := im.parsed[key]
return val, present, nil
diff --git a/net.go b/net.go
index 1e53848..4d67edd 100644
--- a/net.go
+++ b/net.go
@@ -3,9 +3,11 @@ package fargo
// MIT Licensed (see README.md) - Copyright (c) 2013 Hudl <@Hudl>
import (
+ "encoding/json"
"encoding/xml"
"fmt"
"net/http"
+ "strconv"
"strings"
)
@@ -13,87 +15,109 @@ func (e *EurekaConnection) generateURL(slugs ...string) string {
return strings.Join(append([]string{e.SelectServiceURL()}, slugs...), "/")
}
+func (e *EurekaConnection) marshal(v interface{}) ([]byte, error) {
+ if e.UseJson {
+ out, err := json.Marshal(v)
+ if err != nil {
+ // marshal the JSON *with* indents so it's readable in the error message
+ out, _ := json.MarshalIndent(v, "", " ")
+ log.Error("Error marshalling JSON value=%v. Error:\"%s\" JSON body=\"%s\"", v, err.Error(), string(out))
+ return nil, err
+ }
+ return out, nil
+ } else {
+ out, err := xml.Marshal(v)
+ if err != nil {
+ // marshal the XML *with* indents so it's readable in the error message
+ out, _ := xml.MarshalIndent(v, "", " ")
+ log.Error("Error marshalling XML value=%v. Error:\"%s\" JSON body=\"%s\"", v, err.Error(), string(out))
+ return nil, err
+ }
+ return out, nil
+ }
+}
+
// GetApp returns a single eureka application by name
-func (e *EurekaConnection) GetApp(name string) (Application, error) {
+func (e *EurekaConnection) GetApp(name string) (*Application, error) {
slug := fmt.Sprintf("%s/%s", EurekaURLSlugs["Apps"], name)
reqURL := e.generateURL(slug)
log.Debug("Getting app %s from url %s", name, reqURL)
- out, rcode, err := getXML(reqURL)
+ out, rcode, err := getBody(reqURL, e.UseJson)
if err != nil {
- log.Error("Couldn't get XML. Error: %s", err.Error())
- return Application{}, err
+ log.Error("Couldn't get app %s, error: %s", name, err.Error())
+ return nil, err
}
if rcode == 404 {
- log.Error("application %s not found (received 404)", name)
- return Application{}, AppNotFoundError{specific: name}
- }
- var v Application
- err = xml.Unmarshal(out, &v)
- if err != nil {
- log.Error("Unmarshalling error. Error: %s", err.Error())
- return Application{}, err
+ log.Error("App %s not found (received 404)", name)
+ return nil, AppNotFoundError{specific: name}
}
if rcode > 299 || rcode < 200 {
log.Warning("Non-200 rcode of %d", rcode)
}
+
+ var v *Application
+ if e.UseJson {
+ var r GetAppResponseJson
+ err = json.Unmarshal(out, &r)
+ v = &r.Application
+ } else {
+ err = xml.Unmarshal(out, &v)
+ }
+ if err != nil {
+ log.Error("Unmarshalling error: %s", err.Error())
+ return nil, err
+ }
+
v.ParseAllMetadata()
return v, nil
}
+func (e *EurekaConnection) readAppInto(app *Application) error {
+ tapp, err := e.GetApp(app.Name)
+ if err == nil {
+ *app = *tapp
+ }
+ return err
+}
+
// GetApps returns a map of all Applications
func (e *EurekaConnection) GetApps() (map[string]*Application, error) {
slug := EurekaURLSlugs["Apps"]
reqURL := e.generateURL(slug)
log.Debug("Getting all apps from url %s", reqURL)
- out, rcode, err := getXML(reqURL)
+ body, rcode, err := getBody(reqURL, e.UseJson)
if err != nil {
- log.Error("Couldn't get XML: " + err.Error())
+ log.Error("Couldn't get apps, error: %s", err.Error())
return nil, err
}
- var v GetAppsResponse
- err = xml.Unmarshal(out, &v)
+ if rcode > 299 || rcode < 200 {
+ log.Warning("Non-200 rcode of %d", rcode)
+ }
+
+ var r *GetAppsResponse
+ if e.UseJson {
+ var rj GetAppsResponseJson
+ err = json.Unmarshal(body, &rj)
+ r = rj.Response
+ } else {
+ err = xml.Unmarshal(body, &r)
+ }
if err != nil {
- log.Error("Unmarshalling error: " + err.Error())
+ log.Error("Unmarshalling error: %s", err.Error())
return nil, err
}
+
apps := map[string]*Application{}
- for i, a := range v.Applications {
- apps[a.Name] = &v.Applications[i]
- }
- if rcode > 299 || rcode < 200 {
- log.Warning("Non-200 rcode of %d", rcode)
+ for i, a := range r.Applications {
+ apps[a.Name] = r.Applications[i]
}
for name, app := range apps {
- log.Debug("Parsing metadata for Application=%s", name)
+ log.Debug("Parsing metadata for app %s", name)
app.ParseAllMetadata()
}
return apps, nil
}
-// AddMetadataString to a given instance. Is immediately sent to Eureka server.
-func (e EurekaConnection) AddMetadataString(ins *Instance, key, value string) error {
- slug := fmt.Sprintf("%s/%s/%s/metadata", EurekaURLSlugs["Apps"], ins.App, ins.HostName)
- reqURL := e.generateURL(slug)
-
- params := map[string]string{key: value}
- if ins.Metadata.parsed == nil {
- ins.Metadata.parsed = map[string]interface{}{}
- }
- ins.Metadata.parsed[key] = value
-
- log.Debug("Updating instance metadata url=%s metadata=%s", reqURL, params)
- 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 metadata Instance=%s App=%s Body=\"%s\"", rcode, ins.HostName, ins.App, string(body))
- return fmt.Errorf("http returned %d possible failure updating instance metadata ", rcode)
- }
- return nil
-}
-
// 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
@@ -101,17 +125,17 @@ func (e *EurekaConnection) RegisterInstance(ins *Instance) error {
slug := fmt.Sprintf("%s/%s", EurekaURLSlugs["Apps"], ins.App)
reqURL := e.generateURL(slug)
log.Debug("Registering instance with url %s", reqURL)
- _, rcode, err := getXML(reqURL + "/" + ins.HostName)
+ _, rcode, err := getBody(reqURL+"/"+ins.HostName, e.UseJson)
if err != nil {
- log.Error("Failed check if Instance=%s exists in App=%s. Error=\"%s\"",
+ log.Error("Failed check if Instance=%s exists in app=%s, error: %s",
ins.HostName, ins.App, err.Error())
return err
}
if rcode == 200 {
- log.Notice("Instance=%s already exists in App=%s aborting registration", ins.HostName, ins.App)
+ log.Notice("Instance=%s already exists in App=%s, aborting registration", ins.HostName, ins.App)
return nil
}
- log.Notice("Instance=%s not yet registered with App=%s. Registering.", ins.HostName, ins.App)
+ log.Notice("Instance=%s not yet registered with App=%s, registering.", ins.HostName, ins.App)
return e.ReregisterInstance(ins)
}
@@ -121,29 +145,62 @@ func (e *EurekaConnection) RegisterInstance(ins *Instance) error {
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 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
+
+ var out []byte
+ var err error
+ if e.UseJson {
+ ins.PortJ.Number = strconv.Itoa(ins.Port)
+ ins.SecurePortJ.Number = strconv.Itoa(ins.SecurePort)
+ out, err = e.marshal(&RegisterInstanceJson{ins})
+ } else {
+ out, err = e.marshal(ins)
}
- body, rcode, err := postXML(reqURL, out)
+
+ body, rcode, err := postBody(reqURL, out, e.UseJson)
if err != nil {
- log.Error("Could not complete registration Error: ", err.Error())
+ log.Error("Could not complete registration, error: %s", err.Error())
return err
}
if rcode != 204 {
- log.Warning("HTTP returned %d registering Instance=%s App=%s Body=\"%s\"", rcode, ins.HostName, ins.App, string(body))
+ 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 registering instance\n", rcode)
}
- // read back our registration to ensure that it stuck
- body, rcode, err = getXML(reqURL + "/" + ins.HostName)
- xml.Unmarshal(body, ins)
+ // read back our registration to pick up eureka-supplied values
+ e.readInstanceInto(ins)
+
return nil
}
+// GetInstance gets an Instance from eureka given its app and hostname.
+func (e *EurekaConnection) GetInstance(app, hostname string) (*Instance, error) {
+ slug := fmt.Sprintf("%s/%s/%s", EurekaURLSlugs["Apps"], app, hostname)
+ reqURL := e.generateURL(slug)
+ log.Debug("Getting instance with url %s", reqURL)
+ body, _, err := getBody(reqURL, e.UseJson)
+ if err != nil {
+ return nil, err
+ }
+ var ins *Instance
+ if e.UseJson {
+ var ij RegisterInstanceJson
+ err = json.Unmarshal(body, &ij)
+ ins = ij.Instance
+ } else {
+ err = xml.Unmarshal(body, &ins)
+ }
+ return ins, err
+}
+
+func (e *EurekaConnection) readInstanceInto(ins *Instance) error {
+ tins, err := e.GetInstance(ins.App, ins.HostName)
+ if err == nil {
+ *ins = *tins
+ }
+ return err
+}
+
// 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 {
@@ -153,7 +210,7 @@ func (e *EurekaConnection) DeregisterInstance(ins *Instance) error {
rcode, err := deleteReq(reqURL)
if err != nil {
- log.Error("Could not complete deregistration Error: ", err.Error())
+ log.Error("Could not complete deregistration, error: %s", err.Error())
return err
}
if rcode != 204 {
@@ -164,6 +221,31 @@ func (e *EurekaConnection) DeregisterInstance(ins *Instance) error {
return nil
}
+// AddMetadataString to a given instance. Is immediately sent to Eureka server.
+func (e EurekaConnection) AddMetadataString(ins *Instance, key, value string) error {
+ slug := fmt.Sprintf("%s/%s/%s/metadata", EurekaURLSlugs["Apps"], ins.App, ins.HostName)
+ reqURL := e.generateURL(slug)
+
+ params := map[string]string{key: value}
+ if ins.Metadata.parsed == nil {
+ ins.Metadata.parsed = map[string]interface{}{}
+ }
+ ins.Metadata.parsed[key] = value
+
+ log.Debug("Updating instance metadata url=%s metadata=%s", reqURL, params)
+ body, rcode, err := putKV(reqURL, params)
+ if err != nil {
+ log.Error("Could not complete update, error: %s", err.Error())
+ return err
+ }
+ if rcode < 200 || rcode >= 300 {
+ log.Warning("HTTP returned %d updating metadata Instance=%s App=%s Body=\"%s\"", rcode,
+ ins.HostName, ins.App, string(body))
+ return fmt.Errorf("http returned %d possible failure updating instance metadata ", 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)
@@ -174,11 +256,12 @@ func (e EurekaConnection) UpdateInstanceStatus(ins *Instance, status StatusType)
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())
+ log.Error("Could not complete update, 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))
+ 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
@@ -192,40 +275,17 @@ func (e *EurekaConnection) HeartBeatInstance(ins *Instance) error {
log.Debug("Sending heartbeat with url %s", reqURL)
req, err := http.NewRequest("PUT", reqURL, nil)
if err != nil {
- log.Error("Could not create request for heartbeat. Error: %s", err.Error())
+ log.Error("Could not create request for heartbeat, error: %s", err.Error())
return err
}
- _, rcode, err := reqXML(req)
+ _, rcode, err := netReq(req)
if err != nil {
- log.Error("Error sending heartbeat for Instance=%s App=%s error: %s", ins.HostName, ins.App, err.Error())
+ log.Error("Error sending heartbeat for Instance=%s App=%s, error: %s", ins.HostName, ins.App, err.Error())
return err
}
if rcode != 200 {
- log.Error("Sending heartbeat for Instance=%s App=%s returned code %d\n", ins.HostName, ins.App, rcode)
+ log.Error("Sending heartbeat for Instance=%s App=%s returned code %d", ins.HostName, ins.App, rcode)
return fmt.Errorf("heartbeat returned code %d\n", rcode)
}
return nil
}
-
-func (e *EurekaConnection) readAppInto(name string, app *Application) error {
- slug := fmt.Sprintf("%s/%s", EurekaURLSlugs["Apps"], name)
- reqURL := e.generateURL(slug)
- log.Debug("Getting app %s from url %s", name, reqURL)
- out, rcode, err := getXML(reqURL)
- if err != nil {
- log.Error("Couldn't get XML. Error: %s", err.Error())
- return err
- }
- oldInstances := app.Instances
- app.Instances = []*Instance{}
- err = xml.Unmarshal(out, app)
- if err != nil {
- app.Instances = oldInstances
- log.Error("Unmarshalling error. Error: %s", err.Error())
- return err
- }
- if rcode > 299 || rcode < 200 {
- log.Warning("Non-200 rcode of %d", rcode)
- }
- return nil
-}
diff --git a/rpc.go b/rpc.go
index 3e2c2d1..8b374dd 100644
--- a/rpc.go
+++ b/rpc.go
@@ -12,15 +12,16 @@ import (
"time"
)
-func postXML(reqURL string, reqBody []byte) ([]byte, int, error) {
+func postBody(reqURL string, reqBody []byte, isJson bool) ([]byte, int, error) {
req, err := http.NewRequest("POST", reqURL, bytes.NewReader(reqBody))
if err != nil {
- log.Error("Could not create POST %s with body %s Error: %s", reqURL, string(reqBody), err.Error())
+ log.Error("Could not create POST %s with body %s, error: %s", reqURL, string(reqBody), err.Error())
return nil, -1, err
}
- body, rcode, err := reqXML(req)
+ log.Debug("postBody: %s %s : %s\n", req.Method, req.URL, string(reqBody))
+ body, rcode, err := netReqTyped(req, isJson)
if err != nil {
- log.Error("Could not complete POST %s with body %s Error: %s", reqURL, string(reqBody), err.Error())
+ log.Error("Could not complete POST %s with body %s, error: %s", reqURL, string(reqBody), err.Error())
return nil, rcode, err
}
//eurekaCache.Flush()
@@ -36,26 +37,26 @@ func putKV(reqURL string, pairs map[string]string) ([]byte, int, error) {
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())
+ log.Error("Could not create PUT %s, error: %s", reqURL, err.Error())
return nil, -1, err
}
- body, rcode, err := reqXML(req)
+ body, rcode, err := netReq(req) // TODO(cq) I think this can just be netReq() since there is no body
if err != nil {
- log.Error("Could not complete PUT %s with Error: %s", reqURL, err.Error())
+ log.Error("Could not complete PUT %s, error: %s", reqURL, err.Error())
return nil, rcode, err
}
return body, rcode, nil
}
-func getXML(reqURL string) ([]byte, int, error) {
+func getBody(reqURL string, isJson bool) ([]byte, int, error) {
req, err := http.NewRequest("GET", reqURL, nil)
if err != nil {
- log.Error("Could not create GET %s with Error: %s", reqURL, err.Error())
+ log.Error("Could not create GET %s, error: %s", reqURL, err.Error())
return nil, -1, err
}
- body, rcode, err := reqXML(req)
+ body, rcode, err := netReqTyped(req, isJson)
if err != nil {
- log.Error("Could not complete GET %s with Error: %s", reqURL, err.Error())
+ log.Error("Could not complete GET %s, error: %s", reqURL, err.Error())
return nil, rcode, err
}
return body, rcode, nil
@@ -64,20 +65,25 @@ func getXML(reqURL string) ([]byte, int, error) {
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())
+ log.Error("Could not create DELETE %s, 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())
+ log.Error("Could not complete DELETE %s, 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")
+func netReqTyped(req *http.Request, isJson bool) ([]byte, int, error) {
+ if isJson {
+ req.Header.Set("Content-Type", "application/json")
+ req.Header.Set("Accept", "application/json")
+ } else {
+ req.Header.Set("Content-Type", "application/xml")
+ req.Header.Set("Accept", "application/xml")
+ }
return netReq(req)
}
@@ -111,10 +117,10 @@ func netReq(req *http.Request) ([]byte, int, error) {
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
- log.Error("Failure reading request body Error: %s", err.Error())
+ log.Error("Failure reading request body, error: %s", err.Error())
return nil, -1, err
}
// At this point we're done and shit worked, simply return the bytes
- log.Info("Got eureka info from url=%v", req.URL)
+ log.Info("Got eureka response from url=%v", req.URL)
return body, resp.StatusCode, nil
}
diff --git a/struct.go b/struct.go
index 6cd4747..5067c06 100644
--- a/struct.go
+++ b/struct.go
@@ -19,19 +19,30 @@ type EurekaConnection struct {
PollInterval time.Duration
PreferSameZone bool
Retries int
+ UseJson bool
+}
+
+// GetAppsResponseJson lets us deserialize the eureka/v2/apps response JSON--a wrapped GetAppsResponse
+type GetAppsResponseJson struct {
+ Response *GetAppsResponse `json:"applications"`
}
// GetAppsResponse lets us deserialize the eureka/v2/apps response XML
type GetAppsResponse struct {
- VersionDelta int `xml:"versions__delta"`
- AppsHashCode string `xml:"apps__hashcode"`
- Applications []Application `xml:"application"`
+ Applications []*Application `xml:"application" json:"application"`
+ AppsHashcode string `xml:"apps__hashcode" json:"apps__hashcode"`
+ VersionsDelta int `xml:"versions__delta" json:"versions__delta"`
+}
+
+// Application deserializeable from Eureka JSON
+type GetAppResponseJson struct {
+ Application Application `json:"application"`
}
// Application deserializeable from Eureka XML
type Application struct {
- Name string `xml:"name"`
- Instances []*Instance `xml:"instance"`
+ Name string `xml:"name" json:"name"`
+ Instances []*Instance `xml:"instance" json:"instance"`
}
// StatusType is an enum of the different statuses allowed by Eureka
@@ -52,20 +63,40 @@ const (
MyOwn = "MyOwn"
)
+// RegisterInstanceJson lets us serialize the eureka/v2/apps/ request JSON--a wrapped Instance
+type RegisterInstanceJson struct {
+ Instance *Instance `json:"instance"`
+}
+
// Instance [de]serializeable [to|from] Eureka XML
type Instance struct {
- XMLName struct{} `xml:"instance"`
- HostName string `xml:"hostName"`
- App string `xml:"app"`
- IPAddr string `xml:"ipAddr"`
- VipAddress string `xml:"vipAddress"`
- SecureVipAddress string `xml:"secureVipAddress"`
- Status StatusType `xml:"status"`
- Port int `xml:"port"`
- SecurePort int `xml:"securePort"`
- DataCenterInfo DataCenterInfo `xml:"dataCenterInfo"`
- LeaseInfo LeaseInfo `xml:"leaseInfo"`
- Metadata InstanceMetadata `xml:"metadata"`
+ XMLName struct{} `xml:"instance" json:"-"`
+ HostName string `xml:"hostName" json:"hostName"`
+ App string `xml:"app" json:"app"`
+ IPAddr string `xml:"ipAddr" json:"ipAddr"`
+ VipAddress string `xml:"vipAddress" json:"vipAddress"`
+ SecureVipAddress string `xml:"secureVipAddress" json:"secureVipAddress"`
+
+ Status StatusType `xml:"status" json:"status"`
+ Overriddenstatus StatusType `xml:"overriddenstatus" json:"overriddenstatus"`
+
+ Port int `xml:"port" json:"-"`
+ PortJ Port `json:"port" xml:"-"`
+ SecurePort int `xml:"securePort" json:"-"`
+ SecurePortJ Port `json:"securePort" xml:"-"`
+
+ CountryId int64 `xml:"countryId" json:"countryId"`
+ DataCenterInfo DataCenterInfo `xml:"dataCenterInfo" json:"dataCenterInfo"`
+
+ LeaseInfo LeaseInfo `xml:"leaseInfo" json:"leaseInfo"`
+ Metadata InstanceMetadata `xml:"metadata" json:"metadata"`
+}
+
+// Port struct used for JSON [un]marshaling only
+// looks like: "port":{"@enabled":"true", "$":"7101"},
+type Port struct {
+ Number string `json:"$"`
+ Enabled string `json:"@enabled"`
}
// InstanceMetadata represents the eureka metadata, which is arbitrary XML. See
@@ -79,31 +110,31 @@ type InstanceMetadata struct {
//
// from http://docs.amazonwebservices.com/AWSEC2/latest/DeveloperGuide/index.html?AESDG-chapter-instancedata.html
type AmazonMetadataType struct {
- AmiLaunchIndex string `xml:"ami-launch-index"`
- LocalHostname string `xml:"local-hostname"`
- AvailabilityZone string `xml:"availability-zone"`
- InstanceID string `xml:"instance-id"`
- PublicIpv4 string `xml:"public-ipv4"`
- PublicHostname string `xml:"public-hostname"`
- AmiManifestPath string `xml:"ami-manifest-path"`
- LocalIpv4 string `xml:"local-ipv4"`
- HostName string `xml:"hostname"`
- AmiID string `xml:"ami-id"`
- InstanceType string `xml:"instance-type"`
+ AmiLaunchIndex string `xml:"ami-launch-index" json:"ami-launch-index"`
+ LocalHostname string `xml:"local-hostname" json:"local-hostname"`
+ AvailabilityZone string `xml:"availability-zone" json:"availability-zone"`
+ InstanceID string `xml:"instance-id" json:"instance-id"`
+ PublicIpv4 string `xml:"public-ipv4" json:"public-ipv4"`
+ PublicHostname string `xml:"public-hostname" json:"public-hostname"`
+ AmiManifestPath string `xml:"ami-manifest-path" json:"ami-manifest-path"`
+ LocalIpv4 string `xml:"local-ipv4" json:"local-ipv4"`
+ HostName string `xml:"hostname" json:"hostname"`
+ AmiID string `xml:"ami-id" json:"ami-id"`
+ InstanceType string `xml:"instance-type" json:"instance-type"`
}
// DataCenterInfo is only really useful when running in AWS.
type DataCenterInfo struct {
- Name string `xml:"name"`
- Metadata AmazonMetadataType `xml:"metadata"`
+ Name string `xml:"name" json:"name"`
+ Metadata AmazonMetadataType `xml:"metadata" json:"metadata"`
}
// LeaseInfo tells us about the renewal from Eureka, including how old it is
type LeaseInfo struct {
- RenewalIntervalInSecs int32 `xml:"renewalIntervalInSecs"`
- DurationInSecs int32 `xml:"durationInSecs"`
- RegistrationTimestamp int64 `xml:"registrationTimestamp"`
- LastRenewalTimestamp int64 `xml:"lastRenewalTimestamp"`
- EvictionTimestamp int32 `xml:"evictionTimestamp"`
- ServiceUpTimestamp int64 `xml:"serviceUpTimestamp"`
+ RenewalIntervalInSecs int32 `xml:"renewalIntervalInSecs" json:"renewalIntervalInSecs"`
+ DurationInSecs int32 `xml:"durationInSecs" json:"durationInSecs"`
+ RegistrationTimestamp int64 `xml:"registrationTimestamp" json:"registrationTimestamp"`
+ LastRenewalTimestamp int64 `xml:"lastRenewalTimestamp" json:"lastRenewalTimestamp"`
+ EvictionTimestamp int64 `xml:"evictionTimestamp" json:"evictionTimestamp"`
+ ServiceUpTimestamp int64 `xml:"serviceUpTimestamp" json:"serviceUpTimestamp"`
}
diff --git a/tests/marshal_sample/apps-sample-1-1.json b/tests/marshal_sample/apps-sample-1-1.json
new file mode 100644
index 0000000..2aaeaba
--- /dev/null
+++ b/tests/marshal_sample/apps-sample-1-1.json
@@ -0,0 +1,49 @@
+{
+ "applications":{
+ "versions__delta":1,
+ "apps__hashcode":"UP_24_",
+ "application":[
+ {
+ "name":"TESTENG.METRICSD",
+ "instance":{
+ "hostName":"192.168.40.94:7101",
+ "app":"TESTENG.METRICSD",
+ "ipAddr":"192.168.40.94",
+ "status":"UP",
+ "overriddenstatus":"UP",
+ "port":{
+ "@enabled":"true",
+ "$":"7101"
+ },
+ "securePort":{
+ "@enabled":"true",
+ "$":"7101"
+ },
+ "countryId":1,
+ "dataCenterInfo":{
+ "@class":"com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
+ "name":"MyOwn"
+ },
+ "leaseInfo":{
+ "renewalIntervalInSecs":30,
+ "durationInSecs":90,
+ "registrationTimestamp":1402450388650,
+ "lastRenewalTimestamp":1402552694257,
+ "evictionTimestamp":0,
+ "serviceUpTimestamp":1402450388774
+ },
+ "metadata":{
+ "service-query-uri":"https://192.168.40.94:7101",
+ "base-uri":"https://192.168.40.94:7101",
+ "swagger-api-docs":"https://192.168.40.94:7101/api-docs"
+ },
+ "vipAddress":"[]",
+ "isCoordinatingDiscoveryServer":false,
+ "lastUpdatedTimestamp":1402450388774,
+ "lastDirtyTimestamp":1402450388774,
+ "actionType":"MODIFIED"
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/marshal_sample/apps-sample-1-2.json b/tests/marshal_sample/apps-sample-1-2.json
new file mode 100644
index 0000000..c253d8f
--- /dev/null
+++ b/tests/marshal_sample/apps-sample-1-2.json
@@ -0,0 +1,89 @@
+{
+ "applications":{
+ "versions__delta":1,
+ "apps__hashcode":"UP_24_",
+ "application":[
+ {
+ "name":"TESTENG.LSM",
+ "instance":[
+ {
+ "hostName":"qa16gs.qa.somecompany.com:7100",
+ "app":"TESTENG.LSM",
+ "ipAddr":"192.168.191.150",
+ "status":"UP",
+ "overriddenstatus":"UNKNOWN",
+ "port":{
+ "@enabled":"true",
+ "$":"7100"
+ },
+ "securePort":{
+ "@enabled":"true",
+ "$":"7100"
+ },
+ "countryId":1,
+ "dataCenterInfo":{
+ "@class":"com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
+ "name":"MyOwn"
+ },
+ "leaseInfo":{
+ "renewalIntervalInSecs":30,
+ "durationInSecs":90,
+ "registrationTimestamp":1402357291394,
+ "lastRenewalTimestamp":1402552702008,
+ "evictionTimestamp":0,
+ "serviceUpTimestamp":1402357291394
+ },
+ "metadata":{
+ "service-query-uri":"https://qa16gs.qa.somecompany.com:7100",
+ "base-uri":"https://qa16gs.qa.somecompany.com:7100",
+ "swagger-api-docs":"https://qa16gs.qa.somecompany.com:7100/api-docs"
+ },
+ "vipAddress":"[test.lax1.qa16]",
+ "isCoordinatingDiscoveryServer":false,
+ "lastUpdatedTimestamp":1402357291394,
+ "lastDirtyTimestamp":1402357289731,
+ "actionType":"ADDED"
+ },
+ {
+ "hostName":"qa22gs:7100",
+ "app":"TESTENG.LSM",
+ "ipAddr":"192.168.191.235",
+ "status":"UP",
+ "overriddenstatus":"UNKNOWN",
+ "port":{
+ "@enabled":"true",
+ "$":"7100"
+ },
+ "securePort":{
+ "@enabled":"true",
+ "$":"7100"
+ },
+ "countryId":1,
+ "dataCenterInfo":{
+ "@class":"com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
+ "name":"MyOwn"
+ },
+ "leaseInfo":{
+ "renewalIntervalInSecs":30,
+ "durationInSecs":90,
+ "registrationTimestamp":1402096233497,
+ "lastRenewalTimestamp":1402552691629,
+ "evictionTimestamp":0,
+ "serviceUpTimestamp":1402096233497
+ },
+ "metadata":{
+ "service-query-uri":"https://qa22gs:7100",
+ "base-uri":"https://qa22gs:7100",
+ "swagger-api-docs":"https://qa22gs:7100/api-docs"
+ },
+ "vipAddress":"[test.lax1.qa22]",
+ "isCoordinatingDiscoveryServer":false,
+ "lastUpdatedTimestamp":1402096233497,
+ "lastDirtyTimestamp":1401527685391,
+ "actionType":"ADDED"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/marshal_sample/apps-sample-2-2.json b/tests/marshal_sample/apps-sample-2-2.json
new file mode 100644
index 0000000..54ffd5b
--- /dev/null
+++ b/tests/marshal_sample/apps-sample-2-2.json
@@ -0,0 +1,131 @@
+{
+ "applications":{
+ "versions__delta":1,
+ "apps__hashcode":"UP_24_",
+ "application":[
+ {
+ "name":"TESTENG.METRICSD",
+ "instance":{
+ "hostName":"192.168.40.94:7101",
+ "app":"TESTENG.METRICSD",
+ "ipAddr":"192.168.40.94",
+ "status":"UP",
+ "overriddenstatus":"UP",
+ "port":{
+ "@enabled":"true",
+ "$":"7101"
+ },
+ "securePort":{
+ "@enabled":"true",
+ "$":"7101"
+ },
+ "countryId":1,
+ "dataCenterInfo":{
+ "@class":"com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
+ "name":"MyOwn"
+ },
+ "leaseInfo":{
+ "renewalIntervalInSecs":30,
+ "durationInSecs":90,
+ "registrationTimestamp":1402450388650,
+ "lastRenewalTimestamp":1402552694257,
+ "evictionTimestamp":0,
+ "serviceUpTimestamp":1402450388774
+ },
+ "metadata":{
+ "service-query-uri":"https://192.168.40.94:7101",
+ "base-uri":"https://192.168.40.94:7101",
+ "swagger-api-docs":"https://192.168.40.94:7101/api-docs"
+ },
+ "vipAddress":"[]",
+ "isCoordinatingDiscoveryServer":false,
+ "lastUpdatedTimestamp":1402450388774,
+ "lastDirtyTimestamp":1402450388774,
+ "actionType":"MODIFIED"
+ }
+ },
+ {
+ "name":"TESTENG.LSM",
+ "instance":[
+ {
+ "hostName":"qa16gs.qa.somecompany.com:7100",
+ "app":"TESTENG.LSM",
+ "ipAddr":"192.168.191.150",
+ "status":"UP",
+ "overriddenstatus":"UNKNOWN",
+ "port":{
+ "@enabled":"true",
+ "$":"7100"
+ },
+ "securePort":{
+ "@enabled":"true",
+ "$":"7100"
+ },
+ "countryId":1,
+ "dataCenterInfo":{
+ "@class":"com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
+ "name":"MyOwn"
+ },
+ "leaseInfo":{
+ "renewalIntervalInSecs":30,
+ "durationInSecs":90,
+ "registrationTimestamp":1402357291394,
+ "lastRenewalTimestamp":1402552702008,
+ "evictionTimestamp":0,
+ "serviceUpTimestamp":1402357291394
+ },
+ "metadata":{
+ "service-query-uri":"https://qa16gs.qa.somecompany.com:7100",
+ "base-uri":"https://qa16gs.qa.somecompany.com:7100",
+ "swagger-api-docs":"https://qa16gs.qa.somecompany.com:7100/api-docs"
+ },
+ "vipAddress":"[test.lax1.qa16]",
+ "isCoordinatingDiscoveryServer":false,
+ "lastUpdatedTimestamp":1402357291394,
+ "lastDirtyTimestamp":1402357289731,
+ "actionType":"ADDED"
+ },
+ {
+ "hostName":"qa22gs:7100",
+ "app":"TESTENG.LSM",
+ "ipAddr":"192.168.191.235",
+ "status":"UP",
+ "overriddenstatus":"UNKNOWN",
+ "port":{
+ "@enabled":"true",
+ "$":"7100"
+ },
+ "securePort":{
+ "@enabled":"true",
+ "$":"7100"
+ },
+ "countryId":1,
+ "dataCenterInfo":{
+ "@class":"com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
+ "name":"MyOwn"
+ },
+ "leaseInfo":{
+ "renewalIntervalInSecs":30,
+ "durationInSecs":90,
+ "registrationTimestamp":1402096233497,
+ "lastRenewalTimestamp":1402552691629,
+ "evictionTimestamp":0,
+ "serviceUpTimestamp":1402096233497
+ },
+ "metadata":{
+ "service-query-uri":"https://qa22gs:7100",
+ "base-uri":"https://qa22gs:7100",
+ "swagger-api-docs":"https://qa22gs:7100/api-docs"
+ },
+ "vipAddress":"[test.lax1.qa22]",
+ "isCoordinatingDiscoveryServer":false,
+ "lastUpdatedTimestamp":1402096233497,
+ "lastDirtyTimestamp":1401527685391,
+ "actionType":"ADDED"
+ }
+ ]
+ }
+ ]
+ }
+}
+
diff --git a/tests/marshal_test.go b/tests/marshal_test.go
new file mode 100644
index 0000000..695b189
--- /dev/null
+++ b/tests/marshal_test.go
@@ -0,0 +1,44 @@
+package fargo_test
+
+// MIT Licensed (see README.md) - Copyright (c) 2013 Hudl <@Hudl>
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/hudl/fargo"
+ . "github.com/smartystreets/goconvey/convey"
+ "io/ioutil"
+ "testing"
+)
+
+func TestJsonMarshal(t *testing.T) {
+ for _, f := range []string{"apps-sample-1-1.json", "apps-sample-1-2.json", "apps-sample-2-2.json"} {
+ Convey("Reading .", t, func() {
+ blob, err := ioutil.ReadFile("marshal_sample/" + f)
+
+ var v fargo.GetAppsResponseJson
+ err = json.Unmarshal(blob, &v)
+
+ if err != nil {
+ // Handy dump for debugging funky JSON
+ fmt.Printf("v:\n%+v\n", v.Response.Applications)
+ for _, app := range v.Response.Applications {
+ fmt.Printf(" %+v\n", *app)
+ for _, ins := range app.Instances {
+ fmt.Printf(" %+v\n", *ins)
+ }
+ }
+
+ // Print a little more details when there are unmarshalling problems
+ switch ute := err.(type) {
+ case *json.UnmarshalTypeError:
+ fmt.Printf("\nUnmarshalling type error val:%s type:%s: %s\n", ute.Value, ute.Type, err.Error())
+ fmt.Printf("UTE:\n%+v\n", ute)
+ default:
+ fmt.Printf("\nUnmarshalling error: %s\n", err.Error())
+ }
+ }
+ So(err, ShouldBeNil)
+ })
+ }
+}
diff --git a/tests/net_test.go b/tests/net_test.go
index e6f74c2..8e227b9 100644
--- a/tests/net_test.go
+++ b/tests/net_test.go
@@ -21,23 +21,26 @@ func TestConnectionCreation(t *testing.T) {
func TestGetApps(t *testing.T) {
e, _ := fargo.NewConnFromConfigFile("./config_sample/local.gcfg")
- Convey("Pull applications", t, func() {
- a, _ := e.GetApps()
- So(len(a["EUREKA"].Instances), ShouldEqual, 2)
- })
- Convey("Pull single application", t, func() {
- a, _ := e.GetApp("EUREKA")
- So(len(a.Instances), ShouldEqual, 2)
- for idx, ins := range a.Instances {
- if ins.HostName == "node1.localdomain" {
- So(a.Instances[idx].IPAddr, ShouldEqual, "172.16.0.11")
- So(a.Instances[idx].HostName, ShouldEqual, "node1.localdomain")
- } else {
- So(a.Instances[idx].IPAddr, ShouldEqual, "172.16.0.22")
- So(a.Instances[idx].HostName, ShouldEqual, "node2.localdomain")
+ for _, j := range []bool{false, true} {
+ e.UseJson = j
+ Convey("Pull applications", t, func() {
+ a, _ := e.GetApps()
+ So(len(a["EUREKA"].Instances), ShouldEqual, 2)
+ })
+ Convey("Pull single application", t, func() {
+ a, _ := e.GetApp("EUREKA")
+ So(len(a.Instances), ShouldEqual, 2)
+ for idx, ins := range a.Instances {
+ if ins.HostName == "node1.localdomain" {
+ So(a.Instances[idx].IPAddr, ShouldEqual, "172.16.0.11")
+ So(a.Instances[idx].HostName, ShouldEqual, "node1.localdomain")
+ } else {
+ So(a.Instances[idx].IPAddr, ShouldEqual, "172.16.0.22")
+ So(a.Instances[idx].HostName, ShouldEqual, "node2.localdomain")
+ }
}
- }
- })
+ })
+ }
}
func TestRegistration(t *testing.T) {
@@ -52,30 +55,33 @@ func TestRegistration(t *testing.T) {
SecureVipAddress: "127.0.0.10",
Status: fargo.UP,
}
- Convey("Fail to heartbeat a non-existent instance", t, func() {
- j := fargo.Instance{
- HostName: "i-6543",
- 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,
- }
- err := e.HeartBeatInstance(&j)
- So(err, ShouldNotBeNil)
- })
- Convey("Register an instance to TESTAPP", t, func() {
- Convey("Instance registers correctly", func() {
- err := e.RegisterInstance(&i)
- So(err, ShouldBeNil)
+ for _, j := range []bool{false, true} {
+ e.UseJson = j
+ Convey("Fail to heartbeat a non-existent instance", t, func() {
+ j := fargo.Instance{
+ HostName: "i-6543",
+ 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,
+ }
+ err := e.HeartBeatInstance(&j)
+ So(err, ShouldNotBeNil)
})
- Convey("Instance can check in", func() {
- err := e.HeartBeatInstance(&i)
- So(err, ShouldBeNil)
+ Convey("Register an instance to TESTAPP", t, func() {
+ Convey("Instance registers correctly", func() {
+ err := e.RegisterInstance(&i)
+ So(err, ShouldBeNil)
+ })
+ Convey("Instance can check in", func() {
+ err := e.HeartBeatInstance(&i)
+ So(err, ShouldBeNil)
+ })
})
- })
+ }
}
func TestReregistration(t *testing.T) {
@@ -90,22 +96,31 @@ func TestReregistration(t *testing.T) {
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)
+ for _, j := range []bool{false, true} {
+ e.UseJson = j
+ 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)
+ 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)
+ })
+ 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)
+ })
})
- })
+ }
}
func DontTestDeregistration(t *testing.T) {
@@ -150,45 +165,51 @@ func TestUpdateStatus(t *testing.T) {
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)
+ for _, j := range []bool{false, true} {
+ e.UseJson = j
+ 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("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)
+ })
})
- Convey("Instance updates to UP corectly", func() {
- err := e.UpdateInstanceStatus(&i, fargo.UP)
- So(err, ShouldBeNil)
- })
- })
+ }
}
func TestMetadataReading(t *testing.T) {
e, _ := fargo.NewConnFromConfigFile("./config_sample/local.gcfg")
- Convey("Read empty instance metadata", t, func() {
- a, err := e.GetApp("EUREKA")
- So(err, ShouldBeNil)
- i := a.Instances[0]
- _, 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")
- })
+ for _, j := range []bool{false, true} {
+ e.UseJson = j
+ Convey("Read empty instance metadata", t, func() {
+ a, err := e.GetApp("EUREKA")
+ So(err, ShouldBeNil)
+ i := a.Instances[0]
+ _, 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")
+ })
+ }
}