From 60f23e78301dd98e9678ec84c8423f99c30161de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E6=9D=B0=E9=B2=81?= Date: Tue, 31 Aug 2021 03:11:47 +0800 Subject: [PATCH 1/3] feat: add retry_timeout for upstream --- api/conf/schema.json | 4 ++ api/internal/core/entity/entity.go | 1 + web/src/components/Upstream/UpstreamForm.tsx | 2 + .../Upstream/components/RetryTimeout.tsx | 38 +++++++++++++++++++ web/src/components/Upstream/locales/en-US.ts | 4 ++ web/src/components/Upstream/locales/zh-CN.ts | 4 ++ 6 files changed, 53 insertions(+) create mode 100644 web/src/components/Upstream/components/RetryTimeout.tsx diff --git a/api/conf/schema.json b/api/conf/schema.json index 9b454b5bf7..59dedcfed6 100644 --- a/api/conf/schema.json +++ b/api/conf/schema.json @@ -1182,6 +1182,10 @@ "minimum": 0, "type": "integer" }, + "retry_timeout": { + "minimum": 0, + "type": "integer" + }, "scheme": { "default": "http", "enum": ["grpc", "grpcs", "http", "https"] diff --git a/api/internal/core/entity/entity.go b/api/internal/core/entity/entity.go index 272020b7f0..ce03f35db1 100644 --- a/api/internal/core/entity/entity.go +++ b/api/internal/core/entity/entity.go @@ -164,6 +164,7 @@ type UpstreamTLS struct { type UpstreamDef struct { Nodes interface{} `json:"nodes,omitempty"` Retries int `json:"retries,omitempty"` + RetryTimeout int `json:"retry_timeout,omitempty"` Timeout interface{} `json:"timeout,omitempty"` Type string `json:"type,omitempty"` Checks interface{} `json:"checks,omitempty"` diff --git a/web/src/components/Upstream/UpstreamForm.tsx b/web/src/components/Upstream/UpstreamForm.tsx index 2a7cb84f2e..fd9640b25d 100644 --- a/web/src/components/Upstream/UpstreamForm.tsx +++ b/web/src/components/Upstream/UpstreamForm.tsx @@ -28,6 +28,7 @@ import Timeout from './components/Timeout'; import Type from './components/Type'; import UpstreamSelector from './components/UpstreamSelector'; import Retries from './components/Retries'; +import RetryTimeout from './components/RetryTimeout' import PassHost from './components/PassHost'; import TLSComponent from './components/TLS'; import { convertToRequestData } from './service'; @@ -290,6 +291,7 @@ const UpstreamForm: React.FC = forwardRef( + prev.scheme !== next.scheme}> diff --git a/web/src/components/Upstream/components/RetryTimeout.tsx b/web/src/components/Upstream/components/RetryTimeout.tsx new file mode 100644 index 0000000000..d3338d9b7f --- /dev/null +++ b/web/src/components/Upstream/components/RetryTimeout.tsx @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { Form, InputNumber } from 'antd'; +import { useIntl } from 'umi'; + +type Props = { + readonly?: boolean; +}; + +const Component: React.FC = ({ readonly }) => { + const { formatMessage } = useIntl(); + return ( + + + + ); +}; + +export default Component; diff --git a/web/src/components/Upstream/locales/en-US.ts b/web/src/components/Upstream/locales/en-US.ts index 34cae8c68a..22837f7d29 100644 --- a/web/src/components/Upstream/locales/en-US.ts +++ b/web/src/components/Upstream/locales/en-US.ts @@ -41,6 +41,10 @@ export default { 'component.upstream.fields.retries.tooltip': 'The retry mechanism sends the request to the next upstream node. A value of 0 disables the retry mechanism and leaves the table empty to use the number of available backend nodes.', + 'component.upstream.fields.retry_timeout': 'Retry Timeout', + 'component.upstream.fields.retry_timeout.tooltip': + 'Limit the retry time to avoid spending too much time retrying requests. A value of 0 disables the retry timeout mechanism', + 'component.upstream.fields.checks.active.type': 'Type', 'component.upstream.fields.checks.active.type.tooltip': 'Whether to perform active health checks using HTTP or HTTPS, or just attempt a TCP connection.', diff --git a/web/src/components/Upstream/locales/zh-CN.ts b/web/src/components/Upstream/locales/zh-CN.ts index 3a803cad86..1270716f1c 100644 --- a/web/src/components/Upstream/locales/zh-CN.ts +++ b/web/src/components/Upstream/locales/zh-CN.ts @@ -41,6 +41,10 @@ export default { 'component.upstream.fields.retries.tooltip': '重试机制将请求发到下一个上游节点。值为 0 表示禁用重试机制,留空表示使用可用后端节点的数量。', + 'component.upstream.fields.retry_timeout': '重试超时时间', + 'component.upstream.fields.retry_timeout.tooltip': + '限制是否继续重试的时间,若之前的请求和重试请求花费太多时间就不再继续重试。`0` 代表不启用重试超时机制。。', + 'component.upstream.fields.checks.active.type': '类型', 'component.upstream.fields.checks.active.type.tooltip': '是使用 HTTP 或 HTTPS 进行主动健康检查,还是只尝试 TCP 连接。', From 5a324fcec75803c1a87e5d7c181fbf4638310b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E6=9D=B0=E9=B2=81?= Date: Wed, 1 Sep 2021 23:09:14 +0800 Subject: [PATCH 2/3] feat:add test case for upstream with retry_timeout --- api/conf/schema.json | 16 ++++ api/test/e2enew/upstream/upstream_test.go | 1 + docs/en/latest/api/api.md | 2 + ...create_upstream_with_retry_timeout.spec.js | 92 +++++++++++++++++++ web/src/components/Upstream/locales/zh-CN.ts | 2 +- web/src/components/Upstream/typings.d.ts | 1 + web/src/pages/Upstream/typing.d.ts | 1 + 7 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 web/cypress/integration/upstream/create_upstream_with_retry_timeout.spec.js diff --git a/api/conf/schema.json b/api/conf/schema.json index 59dedcfed6..37c67820b6 100644 --- a/api/conf/schema.json +++ b/api/conf/schema.json @@ -728,6 +728,10 @@ "minimum": 0, "type": "integer" }, + "retry_timeout": { + "minimum": 0, + "type": "integer" + }, "scheme": { "default": "http", "enum": ["grpc", "grpcs", "http", "https"] @@ -1757,6 +1761,10 @@ "minimum": 0, "type": "integer" }, + "retry_timeout": { + "minimum": 0, + "type": "integer" + }, "scheme": { "default": "http", "enum": ["grpc", "grpcs", "http", "https"] @@ -2139,6 +2147,10 @@ "minimum": 0, "type": "integer" }, + "retry_timeout": { + "minimum": 0, + "type": "integer" + }, "scheme": { "default": "http", "enum": ["grpc", "grpcs", "http", "https"] @@ -4675,6 +4687,10 @@ "minimum": 0, "type": "integer" }, + "retry_timeout": { + "minimum": 0, + "type": "integer" + }, "scheme": { "default": "http", "enum": ["grpc", "grpcs", "http", "https"] diff --git a/api/test/e2enew/upstream/upstream_test.go b/api/test/e2enew/upstream/upstream_test.go index f0ff9383a4..d1a931bbee 100644 --- a/api/test/e2enew/upstream/upstream_test.go +++ b/api/test/e2enew/upstream/upstream_test.go @@ -571,6 +571,7 @@ var _ = ginkgo.Describe("Upstream chash remote addr", func() { Path: "/apisix/admin/upstreams/u2", Body: `{ "retries": 1, + "retry_timeout":3, "timeout": { "connect":15, "send":15, diff --git a/docs/en/latest/api/api.md b/docs/en/latest/api/api.md index b270c4e8fd..992f1c0548 100644 --- a/docs/en/latest/api/api.md +++ b/docs/en/latest/api/api.md @@ -383,6 +383,7 @@ user login. | nodes | object | | No | | pass_host | string | | No | | retries | long | | No | +| retry_timeout | long | | No | | service_name | string | | No | | timeout | object | | No | | type | string | | No | @@ -403,6 +404,7 @@ user login. | nodes | object | | No | | pass_host | string | | No | | retries | long | | No | +| retry_timeout | long | | No | | service_name | string | | No | | timeout | object | | No | | type | string | | No | diff --git a/web/cypress/integration/upstream/create_upstream_with_retry_timeout.spec.js b/web/cypress/integration/upstream/create_upstream_with_retry_timeout.spec.js new file mode 100644 index 0000000000..88b67b16ec --- /dev/null +++ b/web/cypress/integration/upstream/create_upstream_with_retry_timeout.spec.js @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* eslint-disable no-undef */ + +context('Create Upstream With retry_timeout', () => { + const selector = { + name: '#name', + nodes_0_host: '#nodes_0_host', + nodes_0_port: '#nodes_0_port', + nodes_0_weight: '#nodes_0_weight', + retry_timeout: '#retry_timeout', + input: ':input', + notification: '.ant-notification-notice-message', + nameSelector: '[title=Name]', + upstreamType: '.ant-select-item-option-content', + drawer: '.ant-drawer-content', + monacoScroll: '.monaco-scrollable-element', + description: '#desc', + }; + const data = { + upstreamName: 'test_upstream', + description: 'desc_by_autotest', + ip1: '127.0.0.1', + createUpstreamSuccess: 'Create Upstream Successfully', + deleteUpstreamSuccess: 'Delete Upstream Successfully', + port0: '7000', + weight0: '2', + port1: '7001', + weight1: '2', + retry_timeout: '3', + }; + beforeEach(() => { + cy.login(); + }); + it('should create upstream with retry_timeout (roundrobin)', function () { + cy.visit('/'); + cy.contains('Upstream').click(); + cy.contains('Create').click(); + + cy.get(selector.name).type(data.upstreamName); + cy.get(selector.description).type(data.description); + + cy.get(selector.nodes_0_host).type(data.ip1); + cy.get(selector.nodes_0_port).clear().type('8000'); + cy.get(selector.nodes_0_weight).clear().type(1); + cy.get(selector.retry_timeout).clear().type('6'); + cy.get('#custom_checks_active').click(); + cy.get('#checks_active_port').clear(); + cy.contains('Next').click(); + cy.get(selector.input).should('be.disabled'); + cy.contains('Submit').click(); + cy.get(selector.notification).should('contain', data.createUpstreamSuccess); + cy.contains(data.createUpstreamSuccess); + cy.url().should('contains', 'upstream/list'); + }); + it('should view the (roundrobin) upstream with retry_timeout', function () { + cy.visit('/'); + cy.contains('Upstream').click(); + + cy.get(selector.nameSelector).type(data.upstreamName); + cy.contains('Search').click(); + cy.contains(data.upstreamName).siblings().contains('View').click(); + cy.get('.ant-drawer-content').should('be.visible'); + + cy.get(selector.monacoScroll).within(() => { + cy.contains('nodes').should('exist'); + cy.contains('roundrobin').should('exist'); + cy.contains(data.upstreamName).should('exist'); + }); + }); + it('should delete the upstream', function () { + cy.visit('/'); + cy.contains('Upstream').click(); + cy.contains(data.upstreamName).siblings().contains('Delete').click(); + cy.contains('button', 'Confirm').click(); + cy.get(selector.notification).should('contain', data.deleteUpstreamSuccess); + }); +}); diff --git a/web/src/components/Upstream/locales/zh-CN.ts b/web/src/components/Upstream/locales/zh-CN.ts index 1270716f1c..49b1ff172f 100644 --- a/web/src/components/Upstream/locales/zh-CN.ts +++ b/web/src/components/Upstream/locales/zh-CN.ts @@ -43,7 +43,7 @@ export default { 'component.upstream.fields.retry_timeout': '重试超时时间', 'component.upstream.fields.retry_timeout.tooltip': - '限制是否继续重试的时间,若之前的请求和重试请求花费太多时间就不再继续重试。`0` 代表不启用重试超时机制。。', + '限制是否继续重试的时间,若之前的请求和重试请求花费太多时间就不再继续重试。`0` 代表不启用重试超时机制。', 'component.upstream.fields.checks.active.type': '类型', 'component.upstream.fields.checks.active.type.tooltip': diff --git a/web/src/components/Upstream/typings.d.ts b/web/src/components/Upstream/typings.d.ts index dc2cee1ec0..d66a91e048 100644 --- a/web/src/components/Upstream/typings.d.ts +++ b/web/src/components/Upstream/typings.d.ts @@ -44,6 +44,7 @@ declare namespace UpstreamComponent { type ResponseData = { nodes?: Node[]; retries?: number; + retry_timeout?: number; timeout?: Timeout; tls?: TLS; type?: string; diff --git a/web/src/pages/Upstream/typing.d.ts b/web/src/pages/Upstream/typing.d.ts index 7adf15045b..910aa4bc04 100644 --- a/web/src/pages/Upstream/typing.d.ts +++ b/web/src/pages/Upstream/typing.d.ts @@ -72,6 +72,7 @@ declare namespace UpstreamModule { key?: string; checks?: HealthCheck; retries?: number; + retry_timeout?: number; enable_websocket?: boolean; timeout?: Timeout; name?: string; From 0e9dfa7dbff0362ccb28d38b3906ab67fd577457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E6=9D=B0=E9=B2=81?= Date: Thu, 2 Sep 2021 00:52:20 +0800 Subject: [PATCH 3/3] feat:add an assertion to make sure retry_timeout exists --- .../upstream/create_upstream_with_retry_timeout.spec.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/cypress/integration/upstream/create_upstream_with_retry_timeout.spec.js b/web/cypress/integration/upstream/create_upstream_with_retry_timeout.spec.js index 88b67b16ec..ceaede0725 100644 --- a/web/cypress/integration/upstream/create_upstream_with_retry_timeout.spec.js +++ b/web/cypress/integration/upstream/create_upstream_with_retry_timeout.spec.js @@ -55,9 +55,9 @@ context('Create Upstream With retry_timeout', () => { cy.get(selector.description).type(data.description); cy.get(selector.nodes_0_host).type(data.ip1); - cy.get(selector.nodes_0_port).clear().type('8000'); + cy.get(selector.nodes_0_port).clear().type('7000'); cy.get(selector.nodes_0_weight).clear().type(1); - cy.get(selector.retry_timeout).clear().type('6'); + cy.get(selector.retry_timeout).clear().type(data.retry_timeout); cy.get('#custom_checks_active').click(); cy.get('#checks_active_port').clear(); cy.contains('Next').click(); @@ -77,6 +77,7 @@ context('Create Upstream With retry_timeout', () => { cy.get('.ant-drawer-content').should('be.visible'); cy.get(selector.monacoScroll).within(() => { + cy.contains(data.retry_timeout).should('exist'); cy.contains('nodes').should('exist'); cy.contains('roundrobin').should('exist'); cy.contains(data.upstreamName).should('exist');