sunhao-java opened a new issue #5026: URL: https://github.com/apache/apisix/issues/5026
### Issue description 1. Update upstream info via Apisix Admin api. ``` curl --location --request PATCH 'http://localhost:9080/apisix/admin/upstreams/804436770317012992' \ --header 'x-api-key: 0a86d8c17dcb4608b20d9b6d5473dd01' \ --header 'Content-Type: application/json' \ --data '{ "nodes": { "ip:port": 100 }, "timeout": { "connect": 60, "send": 60, "read": 60 }, "type": "roundrobin", "pass_host": "pass", "scheme": "http", "id": "804436770317012992" }' ``` 2. This http request's method is `PATCH` 3. Docs about upstream api: [Upstream API](https://apisix.apache.org/docs/apisix/admin-api#upstream).  4. But, when I request the request (In step 1) for more times, the `nodes` field in this upstream will be more and more. 5. The description of patch in docs says, when the value of the attribute is an array, the attribute will be updated in full. ### Environment - apisix version (cmd: `apisix version`): the latest. - OS (cmd: `uname -a`): docker on windows - OpenResty / Nginx version (cmd: `nginx -V` or `openresty -V`): openresty/1.19.3.1 - etcd version, if have (cmd: run `curl http://127.0.0.1:9090/v1/server_info` to get the info from server-info API): 3.4.0 - apisix-dashboard version, if have: no - luarocks version, if the issue is about installation (cmd: `luarocks --version`): no ### Steps to reproduce 1. run this command ``` curl --location --request PUT 'http://localhost:9080/apisix/admin/upstreams/804436770317012992' \ --header 'x-api-key: 0a86d8c17dcb4608b20d9b6d5473dd01' \ --header 'Content-Type: application/json' \ --data '{ "nodes": { "ip:port": 100 }, "timeout": { "connect": 60, "send": 60, "read": 60 }, "type": "roundrobin", "pass_host": "pass", "scheme": "http", "id": "804436770317012992" }' ``` 2. run this command ``` curl --location --request PATCH 'http://localhost:9080/apisix/admin/upstreams/804436770317012992' \ --header 'x-api-key: 0a86d8c17dcb4608b20d9b6d5473dd01' \ --header 'Content-Type: application/json' \ --data '{ "nodes": { "ip1:port1": 100 }, "timeout": { "connect": 60, "send": 60, "read": 60 }, "type": "roundrobin", "pass_host": "pass", "scheme": "http", "id": "804436770317012992" }' ``` 3. First request use mthod `PUT`, Second request use method `PATCH`. ### Actual result ``` { "update_time": 1629795074, "create_time": 1629795074, "type": "roundrobin", "scheme": "http", "hash_on": "vars", "id": "804436770317012992", "pass_host": "node", "nodes": { "ip:port": 100, "ip1:port1": 100 } } ``` ### Error log ``` 2021/09/09 09:07:59 [info] 49#49: *111589 [lua] upstreams.lua:264: key: /upstreams/804436770317012992 old value: {"body_reader":"function: 0x7f8b2d7e8798","reason":"OK","has_body":true,"body":{"action":"get","count":"1","node":{"key":"\/apisix\/upstreams\/804436770317012992","value":{"id":"804436770317012992","hash_on":"vars","timeout":{"send":60,"read":60,"connect":60},"nodes":{"ip:port":100},"type":"roundrobin","create_time":1629795074,"update_time":1631178467,"scheme":"http","pass_host":"pass"},"createdIndex":15403,"modifiedIndex":16741},"header":{"cluster_id":"14841639068965178418","member_id":"10276657743932975437","revision":"16741","raft_term":"22"}},"status":200,"read_body":"function: 0x7f8b3048cb48","headers":{"Access-Control-Allow-Origin":"*","X-Etcd-Index":"16741","Access-Control-Allow-Headers":"accept, content-type, authorization","Grpc-Metadata-Content-Type":"application\/grpc","Content-Length":"565","Content-Type":"application\/json","Access-Control-Allow-Methods":"P OST, GET, OPTIONS, PUT, DELETE","Date":"Thu, 09 Sep 2021 09:07:59 GMT"},"read_trailers":"function: 0x7f8b3048cc00"}, client: 172.21.0.1, server: _, request: "PATCH /apisix/admin/upstreams/804436770317012992 HTTP/1.1", host: "localhost:9080" 2021/09/09 09:07:59 [info] 49#49: *111589 [lua] upstreams.lua:293: new value {"id":"804436770317012992","hash_on":"vars","timeout":{"send":60,"read":60,"connect":60},"nodes":{"ip1:port1":100,"ip:port":100},"type":"roundrobin","create_time":1629795074,"update_time":1631178479,"scheme":"http","pass_host":"pass"}, client: 172.21.0.1, server: _, request: "PATCH /apisix/admin/upstreams/804436770317012992 HTTP/1.1", host: "localhost:9080" 2021/09/09 09:07:59 [info] 49#49: *111589 [lua] upstreams.lua:123: check_conf(): schema: {"oneOf":[{"required":["type","nodes"]},{"required":["type","service_name","discovery_type"]}],"properties":{"id":{"anyOf":[{"maxLength":64,"pattern":"^[a-zA-Z0-9-_.]+$","type":"string","minLength":1},{"minimum":1,"type":"integer"}]},"hash_on":{"enum":["vars","header","cookie","consumer","vars_combinations"],"default":"vars","type":"string"},"timeout":{"properties":{"send":{"exclusiveMinimum":0,"type":"number"},"read":{"exclusiveMinimum":0,"type":"number"},"connect":{"exclusiveMinimum":0,"type":"number"}},"required":["connect","send","read"],"type":"object"},"labels":{"maxProperties":16,"description":"key\/value pairs to specify attributes","patternProperties":{".*":{"maxLength":64,"description":"value of label","pattern":"^\\S+$","minLength":1,"type":"string"}},"type":"object"},"discovery_type":{"type":"string","description":"discovery type"},"desc":{"maxLength":256,"type":"string"},"pass_hos t":{"enum":["pass","node","rewrite"],"description":"mod of host passing","default":"pass","type":"string"},"upstream_host":{"pattern":"^\\*?[0-9a-zA-Z-._]+$","type":"string"},"nodes":{"anyOf":[{"patternProperties":{".*":{"description":"weight of node","minimum":0,"type":"integer"}},"type":"object"},{"items":{"properties":{"host":{"pattern":"^\\*?[0-9a-zA-Z-._]+$","type":"string"},"weight":{"description":"weight of node","minimum":0,"type":"integer"},"priority":{"description":"priority of node","default":0,"type":"integer"},"port":{"description":"port of node","minimum":1,"type":"integer"},"metadata":{"type":"object","description":"metadata of node"}},"required":["host","port","weight"],"type":"object"},"type":"array"}]},"type":{"enum":["chash","roundrobin","ewma","least_conn"],"description":"algorithms of load balancing","type":"string"},"key":{"type":"string","description":"the key of chash for dynamic load balancing"},"service_name":{"maxLength":256,"minLength":1,"type":"string"}, "checks":{"anyOf":[{"required":["active"]},{"required":["active","passive"]}],"properties":{"active":{"properties":{"healthy":{"properties":{"interval":{"default":1,"minimum":1,"type":"integer"},"successes":{"maximum":254,"default":2,"minimum":1,"type":"integer"},"http_statuses":{"default":[200,302],"type":"array","minItems":1,"items":{"maximum":599,"minimum":200,"type":"integer"},"uniqueItems":true}},"type":"object"},"req_headers":{"minItems":1,"items":{"uniqueItems":true,"type":"string"},"type":"array"},"timeout":{"default":1,"type":"number"},"unhealthy":{"properties":{"http_failures":{"maximum":254,"default":5,"minimum":1,"type":"integer"},"tcp_failures":{"maximum":254,"default":2,"minimum":1,"type":"integer"},"timeouts":{"maximum":254,"default":3,"minimum":1,"type":"integer"},"interval":{"default":1,"minimum":1,"type":"integer"},"http_statuses":{"default":[429,404,500,501,502,503,504,505],"type":"array","minItems":1,"items":{"maximum":599,"minimum":200,"type":"integer"},"uniqueI tems":true}},"type":"object"},"type":{"enum":["http","https","tcp"],"default":"http","type":"string"},"host":{"pattern":"^\\*?[0-9a-zA-Z-._]+$","type":"string"},"port":{"maximum":65535,"minimum":1,"type":"integer"},"concurrency":{"default":10,"type":"integer"},"http_path":{"default":"\/","type":"string"},"https_verify_certificate":{"default":true,"type":"boolean"}},"type":"object"},"passive":{"properties":{"healthy":{"properties":{"successes":{"maximum":254,"default":5,"minimum":1,"type":"integer"},"http_statuses":{"default":[200,201,202,203,204,205,206,207,208,226,300,301,302,303,304,305,306,307,308],"type":"array","minItems":1,"items":{"maximum":599,"minimum":200,"type":"integer"},"uniqueItems":true}},"type":"object"},"unhealthy":{"properties":{"http_failures":{"maximum":254,"default":5,"minimum":1,"type":"integer"},"tcp_failures":{"maximum":254,"default":2,"minimum":1,"type":"integer"},"timeouts":{"maximum":254,"default":7,"minimum":1,"type":"integer"},"http_statuses":{"default": [429,500,503],"type":"array","minItems":1,"items":{"maximum":599,"minimum":200,"type":"integer"},"uniqueItems 2021/09/09 09:07:59 [info] 49#49: *111589 [lua] upstreams.lua:124: check_conf(): conf : {"id":"804436770317012992","hash_on":"vars","timeout":{"send":60,"connect":60,"read":60},"nodes":{"ip1:port1":100,"ip:port":100},"type":"roundrobin","create_time":1629795074,"pass_host":"pass","scheme":"http","update_time":1631178479}, client: 172.21.0.1, server: _, request: "PATCH /apisix/admin/upstreams/804436770317012992 HTTP/1.1", host: "localhost:9080" ``` ### Expected result ``` { "id": "804436770317012992", "hash_on": "vars", "timeout": { "send": 60, "connect": 60, "read": 60 }, "nodes": { "ip1:port1": 100 }, "type": "roundrobin", "create_time": 1629795074, "pass_host": "pass", "scheme": "http", "update_time": 1631178479 } ``` I read the code of apisix, `/apisix/admin/upstream.lua`. The function _M.patch: ``` -- I think this new_value named wrong. It maybe named old_value?? local new_value = res_old.body.node.value local modified_index = res_old.body.node.modifiedIndex if sub_path and sub_path ~= "" then local code, err, node_val = core.table.patch(new_value, sub_path, conf) new_value = node_val if code then return code, err end utils.inject_timestamp(new_value, nil, true) else -- method `table.merge` in lua maybe merge array too? new_value = core.table.merge(new_value, conf); utils.inject_timestamp(new_value, nil, conf) end ``` -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
