Browse Source

Initial implementation of UnmarshalJSON for Node type

Kaelan Patel 4 years ago
parent
commit
34392acc9c
3 changed files with 178 additions and 0 deletions
  1. 145 0
      pkg/kubecost/asset.go
  2. 30 0
      pkg/kubecost/asset_unmarshal_test.go
  3. 3 0
      pkg/util/json/json.go

+ 145 - 0
pkg/kubecost/asset.go

@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"encoding"
 	"fmt"
+	"reflect"
 	"strings"
 	"sync"
 	"time"
@@ -2019,6 +2020,105 @@ func (n *Node) MarshalJSON() ([]byte, error) {
 	return buffer.Bytes(), nil
 }
 
+func (n *Node) UnmarshalJSON(b []byte) error {
+	//f := reflect.New(reflect.TypeOf((*Node)(nil)).Elem()).Interface().(interface{ UnmarshalBinary([]byte) error })
+	//var f map[string]*json.RawMessage
+	var f interface{}
+	//fmt.Println(fmt.Sprintf("%T", f))
+	err := json.Unmarshal(b, &f)
+	if err != nil {
+		return err
+	}
+	//fmt.Println(f)
+
+	fmap := f.(map[string]interface{})
+
+	// parse properties map to AssetProperties
+	fproperties := fmap["properties"].(map[string]interface{})
+	properties := toAssetProp(fproperties)
+
+	// parse labels map to AssetLabels
+	labels := make(map[string]string)
+	for k, v := range fmap["labels"].(map[string]interface{}) {
+		labels[k] = v.(string)
+	}
+
+	// parse start and end strings to time.Time
+	start, err := time.Parse(time.RFC3339, fmap["start"].(string))
+	if err != nil {
+		return err
+	}
+	end, err := time.Parse(time.RFC3339, fmap["start"].(string))
+	if err != nil {
+		return err
+	}
+
+	n.properties = &properties
+	n.labels = labels
+	n.start = start
+	n.end = end
+	n.window = Window{
+		start: &start,
+		end:   &end,
+	}
+
+	if adjustment, err := getTypedVal(fmap["adjustment"]); err == nil {
+		n.adjustment = adjustment.(float64)
+	}
+	if NodeType, err := getTypedVal(fmap["nodeType"]); err == nil {
+		n.NodeType = NodeType.(string)
+	}
+	if CPUCoreHours, err := getTypedVal(fmap["cpuCoreHours"]); err == nil {
+		n.CPUCoreHours = CPUCoreHours.(float64)
+	}
+	if RAMByteHours, err := getTypedVal(fmap["ramByteHours"]); err == nil {
+		n.RAMByteHours = RAMByteHours.(float64)
+	}
+	if GPUHours, err := getTypedVal(fmap["GPUHours"]); err == nil {
+		n.GPUHours = GPUHours.(float64)
+	}
+	if CPUCost, err := getTypedVal(fmap["cpuCost"]); err == nil {
+		n.CPUCost = CPUCost.(float64)
+	}
+	if GPUCost, err := getTypedVal(fmap["gpuCost"]); err == nil {
+		n.GPUCost = GPUCost.(float64)
+	}
+	if GPUCount, err := getTypedVal(fmap["gpuCount"]); err == nil {
+		n.GPUCount = GPUCount.(float64)
+	}
+	if RAMCost, err := getTypedVal(fmap["ramCost"]); err == nil {
+		n.RAMCost = RAMCost.(float64)
+	}
+	if Discount, err := getTypedVal(fmap["discount"]); err == nil {
+		n.Discount = Discount.(float64)
+	}
+	if Preemptible, err := getTypedVal(fmap["preemptible"]); err == nil {
+		n.Preemptible = Preemptible.(float64)
+	}
+
+	fmt.Println(n)
+
+	return nil
+}
+
+// labels       AssetLabels
+// 	start        time.Time
+// 	end          time.Time
+// 	window       Window
+// 	adjustment   float64
+// 	NodeType     string
+// 	CPUCoreHours float64
+// 	RAMByteHours float64
+// 	GPUHours     float64
+// 	CPUBreakdown *Breakdown
+// 	RAMBreakdown *Breakdown
+// 	CPUCost      float64
+// 	GPUCost      float64
+// 	GPUCount     float64
+// 	RAMCost      float64
+// 	Discount     float64
+// 	Preemptible  float64
+
 // String implements fmt.Stringer
 func (n *Node) String() string {
 	return toString(n)
@@ -3076,3 +3176,48 @@ func contains(slice []string, item string) bool {
 	}
 	return false
 }
+
+// Creates an AssetProperties directly from map[string]interface{}
+func toAssetProp(fproperties map[string]interface{}) AssetProperties {
+	var properties AssetProperties
+
+	if category, v := fproperties["category"].(string); v {
+		properties.Category = category
+	}
+	if provider, v := fproperties["provider"].(string); v {
+		properties.Provider = provider
+	}
+	if account, v := fproperties["account"].(string); v {
+		properties.Account = account
+	}
+	if project, v := fproperties["project"].(string); v {
+		properties.Project = project
+	}
+	if service, v := fproperties["service"].(string); v {
+		properties.Service = service
+	}
+	if cluster, v := fproperties["cluster"].(string); v {
+		properties.Cluster = cluster
+	}
+	if name, v := fproperties["name"].(string); v {
+		properties.Name = name
+	}
+	if providerID, v := fproperties["providerID"].(string); v {
+		properties.ProviderID = providerID
+	}
+
+	return properties
+
+}
+
+func getTypedVal(itf interface{}) (interface{}, error) {
+	switch itf := itf.(type) {
+	case float64:
+		return float64(itf), nil
+	case string:
+		return string(itf), nil
+	default:
+		unktype := reflect.ValueOf(itf)
+		return nil, fmt.Errorf("Type %v is an invalid type", unktype)
+	}
+}

+ 30 - 0
pkg/kubecost/asset_unmarshal_test.go

@@ -0,0 +1,30 @@
+package kubecost
+
+import (
+	"encoding/json"
+	"fmt"
+	"testing"
+	"time"
+)
+
+func TestNode_Unmarshal(t *testing.T) {
+
+	var s time.Time
+	var e time.Time
+	unmarshalWindow := NewWindow(&s, &e)
+
+	node1 := NewNode("node1", "cluster1", "provider1", *unmarshalWindow.start, *unmarshalWindow.end, unmarshalWindow)
+
+	node1.CPUCost = 1000
+
+	fmt.Println(node1)
+	bytes, _ := json.Marshal(node1)
+	//fmt.Println(string(bytes))
+	var node2 Node
+	//fmt.Println(node2)
+	err := json.Unmarshal(bytes, &node2)
+	fmt.Println("error:", err)
+	fmt.Println(node2.CPUCost)
+	//bytes2, _ := json.Marshal(&node2)
+	//fmt.Println(string(bytes2))
+}

+ 3 - 0
pkg/util/json/json.go

@@ -10,5 +10,8 @@ var Marshal = jsoniter.ConfigCompatibleWithStandardLibrary.Marshal
 var Unmarshal = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal
 
 type Marshaler json.Marshaler
+type Unmarshaler json.Unmarshaler
+
+type RawMessage json.RawMessage
 
 var NewDecoder = json.NewDecoder