From ca063cdfd400f6bd54421dd3168ebb4d42266fd8 Mon Sep 17 00:00:00 2001 From: Jintao Zhang Date: Wed, 17 Aug 2022 17:55:27 +0800 Subject: [PATCH] fix: nodes convert failed (#1222) (#1250) Co-authored-by: Xin Rong --- pkg/apisix/resource_test.go | 45 ++++++++++++++++++++++++++++++++++++ pkg/types/apisix/v1/types.go | 43 ++++++++++++++++++++++++++++++---- 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/pkg/apisix/resource_test.go b/pkg/apisix/resource_test.go index 2c9699605c..528f801dae 100644 --- a/pkg/apisix/resource_test.go +++ b/pkg/apisix/resource_test.go @@ -78,6 +78,51 @@ func TestItemConvertRoute(t *testing.T) { assert.Equal(t, r.Name, "unknown") } +func TestItemConvertUpstream(t *testing.T) { + ite := &item{ + Key: "/apisix/upstreams/419655639963271872", + Value: json.RawMessage(`{ "nodes":{"httpbin.org:80":1, "foo.com:8080": 2}}`), + } + ups, err := ite.upstream() + assert.Nil(t, err) + assert.Len(t, ups.Nodes, 2) + assert.Equal(t, ups.Nodes[0], v1.UpstreamNode{Host: "httpbin.org", Port: 80, Weight: 1}) + assert.Equal(t, ups.Nodes[1], v1.UpstreamNode{Host: "foo.com", Port: 8080, Weight: 2}) + + ite = &item{ + Key: "/apisix/upstreams/419655639963271872", + Value: json.RawMessage(` +{ + "id": "419655639963271872", + "nodes": [ + { + "host": "httpbin.org", + "port": 80, + "weight": 1 + }, + { + "host": "httpbin.com", + "port": 8080, + "weight": 1 + } + ] +}`), + } + ups, err = ite.upstream() + assert.Nil(t, err) + assert.Len(t, ups.Nodes, 2) + assert.Equal(t, ups.Nodes[0], v1.UpstreamNode{Host: "httpbin.org", Port: 80, Weight: 1}) + assert.Equal(t, ups.Nodes[1], v1.UpstreamNode{Host: "httpbin.com", Port: 8080, Weight: 1}) + + ite = &item{ + Key: "/apisix/upstreams/419655639963271872", + Value: json.RawMessage(`{ "id":"419655639963271872" }`), + } + ups, err = ite.upstream() + assert.Nil(t, err) + assert.Len(t, ups.Nodes, 0) +} + func TestRouteVarsUnmarshalJSONCompatibility(t *testing.T) { var route v1.Route data := `{"vars":{}}` diff --git a/pkg/types/apisix/v1/types.go b/pkg/types/apisix/v1/types.go index 3da9d4621c..8ffcd0d373 100644 --- a/pkg/types/apisix/v1/types.go +++ b/pkg/types/apisix/v1/types.go @@ -18,6 +18,7 @@ import ( "bytes" "encoding/json" "errors" + "fmt" "strconv" "strings" "time" @@ -216,14 +217,22 @@ type UpstreamNodes []UpstreamNode // and by default empty array will be encoded as '{}'. // We have to maintain the compatibility. func (n *UpstreamNodes) UnmarshalJSON(p []byte) error { + var data []UpstreamNode if p[0] == '{' { - if len(p) != 2 { - return errors.New("unexpected non-empty object") + value := map[string]float64{} + if err := json.Unmarshal(p, &value); err != nil { + return err + } + for k, v := range value { + node, err := mapKV2Node(k, v) + if err != nil { + return err + } + data = append(data, *node) } - *n = UpstreamNodes{} + *n = data return nil } - var data []UpstreamNode if err := json.Unmarshal(p, &data); err != nil { return err } @@ -231,6 +240,32 @@ func (n *UpstreamNodes) UnmarshalJSON(p []byte) error { return nil } +func mapKV2Node(key string, val float64) (*UpstreamNode, error) { + hp := strings.Split(key, ":") + host := hp[0] + // according to APISIX upstream nodes policy, port is required + port := "80" + + if len(hp) > 2 { + return nil, errors.New("invalid upstream node") + } else if len(hp) == 2 { + port = hp[1] + } + + portInt, err := strconv.Atoi(port) + if err != nil { + return nil, fmt.Errorf("parse port to int fail: %s", err.Error()) + } + + node := &UpstreamNode{ + Host: host, + Port: portInt, + Weight: int(val), + } + + return node, nil +} + // UpstreamNode is the node in upstream // +k8s:deepcopy-gen=true type UpstreamNode struct {