Skip to content

Commit

Permalink
Merge pull request hudl#6 from cquinn/master
Browse files Browse the repository at this point in the history
Add JSON support: (credit: @cquinn)
  • Loading branch information
ryansb committed Jun 18, 2014
2 parents 97aaaf8 + 6ad71c1 commit 0dffa3a
Show file tree
Hide file tree
Showing 12 changed files with 804 additions and 244 deletions.
2 changes: 1 addition & 1 deletion connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
7 changes: 7 additions & 0 deletions log.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
109 changes: 109 additions & 0 deletions marshal.go
Original file line number Diff line number Diff line change
@@ -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
}
31 changes: 22 additions & 9 deletions metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package fargo
// MIT Licensed (see README.md) - Copyright (c) 2013 Hudl <@Hudl>

import (
"encoding/json"
"fmt"
"github.com/clbanning/x2j"
)
Expand All @@ -12,27 +13,39 @@ 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
}
}
return nil
}

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("<d>"), im.Raw...), []byte("</d>")...)
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("<d>"), im.Raw...), []byte("</d>")...)
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
}

Expand All @@ -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
Expand Down
Loading

0 comments on commit 0dffa3a

Please sign in to comment.