diff --git a/api/internal/core/entity/entity.go b/api/internal/core/entity/entity.go index 4f0677034e..f7d4857057 100644 --- a/api/internal/core/entity/entity.go +++ b/api/internal/core/entity/entity.go @@ -156,9 +156,9 @@ type UpstreamTLS struct { } type UpstreamKeepalivePool struct { - IdleTimeout TimeoutValue `json:"idle_timeout,omitempty"` - Requests int `json:"requests,omitempty"` - Size int `json:"size"` + IdleTimeout *TimeoutValue `json:"idle_timeout,omitempty"` + Requests int `json:"requests,omitempty"` + Size int `json:"size"` } type UpstreamDef struct { diff --git a/api/internal/core/entity/format_test.go b/api/internal/core/entity/format_test.go index 56e2458bef..810c43dfac 100644 --- a/api/internal/core/entity/format_test.go +++ b/api/internal/core/entity/format_test.go @@ -180,6 +180,31 @@ func TestNodesFormat_no_nodes(t *testing.T) { assert.Contains(t, jsonStr, `null`) } +func Test_Idle_Timeout_nil_and_zero(t *testing.T) { + ukp0 := UpstreamKeepalivePool{} + // Unmarshal from zero value + err := json.Unmarshal([]byte(`{"idle_timeout":0}`), &ukp0) + assert.Nil(t, err) + assert.Equal(t, *ukp0.IdleTimeout, TimeoutValue(0)) + + // Marshal with zero value + marshaled, err := json.Marshal(ukp0) + assert.Nil(t, err) + assert.Contains(t, string(marshaled), `"idle_timeout":0`) + + ukpNil := UpstreamKeepalivePool{} + + // Unmarshal from nil value + err = json.Unmarshal([]byte(`{}`), &ukpNil) + assert.Nil(t, err) + assert.Nil(t, ukpNil.IdleTimeout) + + // Marshal with nil value + marshaledNil, err := json.Marshal(ukpNil) + assert.Nil(t, err) + assert.Equal(t, string(marshaledNil), `{"size":0}`) +} + func TestUpstream_nil_and_zero_retries(t *testing.T) { ud0 := UpstreamDef{} // Unmarshal from zero value diff --git a/api/test/e2enew/upstream/upstream_keepalive_pool.go b/api/test/e2enew/upstream/upstream_keepalive_pool.go index a16ebe0ae8..21e8c50346 100644 --- a/api/test/e2enew/upstream/upstream_keepalive_pool.go +++ b/api/test/e2enew/upstream/upstream_keepalive_pool.go @@ -64,3 +64,86 @@ var _ = ginkgo.Describe("Upstream keepalive pool", func() { }) }) }) + +// Test idle timeout zero and nil +var _ = ginkgo.Describe("Test Upstream keepalive pool", func() { + ginkgo.It("create upstream with idle_timeout zero", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPut, + Path: "/apisix/admin/upstreams/zero_idle_timeout", + Body: `{ + "name":"upstream1", + "nodes":[{ + "host": "` + base.UpstreamIp + `", + "port": 1980, + "weight": 1 + }], + "keepalive_pool":{ + "size": 320, + "requests": 1000, + "idle_timeout": 0 + }, + "type":"roundrobin" + }`, + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + }) + }) + + ginkgo.It("get upstream with idle_timeout zero", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodGet, + Path: "/apisix/admin/upstreams/zero_idle_timeout", + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + ExpectBody: []string{`"id":"zero_idle_timeout"`, `"idle_timeout":0`, `"name":"upstream1"`}, + }) + }) + + ginkgo.It("create upstream with idle_timeout nil", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPut, + Path: "/apisix/admin/upstreams/nil_idle_timeout", + Body: `{ + "name":"upstream2", + "nodes":[{ + "host":"` + base.UpstreamIp + `", + "port":1980, + "weight":1 + }], + "keepalive_pool":{ + "size": 320, + "requests": 1000 + }, + "type":"roundrobin" + }`, + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + }) + }) + + ginkgo.It("get upstream with idle_timeout nil", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodGet, + Path: "/apisix/admin/upstreams/nil_idle_timeout", + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + ExpectBody: []string{`"id":"nil_idle_timeout"`, `"name":"upstream2"`}, + UnexpectBody: []string{`"idle_timeout":0`}, + }) + }) + + ginkgo.It("delete upstream", func() { + base.RunTestCase(base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodDelete, + Path: "/apisix/admin/upstreams/zero_idle_timeout,nil_idle_timeout", + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + }) + }) +})