diff --git a/docs/en/latest/concepts/annotations.md b/docs/en/latest/concepts/annotations.md index 5b6d3ede7f..d7327907aa 100644 --- a/docs/en/latest/concepts/annotations.md +++ b/docs/en/latest/concepts/annotations.md @@ -99,6 +99,31 @@ k8s.apisix.apache.org/blocklist-source-range: "127.0.0.1,172.17.0.0/16" The default value is empty which means no IP addresses are blocked. +## Customized http methods + +> `http-allow-methods` and `http-block-methods` are mutually exclusive. +> If they're both set, only `http-allow-methods` works + +### Allow http methods + +This annotation can be used to specify which http method are allowed. Multiple methods can also be specified by separating them with commas. + +```yaml +k8s.apisix.apache.org/http-allow-methods: "GET,POST" +``` + +The default value is empty which means all methods are allowed. + +### Block http methods + +This annotation can be used to specify which http method are blocked. Multiple methods can also be specified by separating them with commas. + +```yaml +k8s.apisix.apache.org/http-block-method: "PUT,DELETE" +``` + +The default value is empty which means no methods are blocked. + ## Rewrite target These annotations are used to rewrite requests. diff --git a/go.mod b/go.mod index e3fa8cfd96..ef0fd595ce 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/hashicorp/go-memdb v1.3.4 github.com/hashicorp/go-multierror v1.1.1 github.com/imdario/mergo v0.3.13 + github.com/incubator4/go-resty-expr v0.1.1 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.14.0 github.com/prometheus/client_model v0.3.0 diff --git a/go.sum b/go.sum index f80d9196d7..a951e2c88a 100644 --- a/go.sum +++ b/go.sum @@ -205,6 +205,8 @@ github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/incubator4/go-resty-expr v0.1.1 h1:9ur1M+p0wDzL1bprdGzHugGkfK0Yd3Ba/ijcgvL+a1k= +github.com/incubator4/go-resty-expr v0.1.1/go.mod h1:w9YQkQLUs1cArOb4O7SGJwJL/L8kuAo6y5CVS2o9eag= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= diff --git a/pkg/providers/ingress/translation/annotations/plugins/http_method.go b/pkg/providers/ingress/translation/annotations/plugins/http_method.go new file mode 100644 index 0000000000..1b4c96c580 --- /dev/null +++ b/pkg/providers/ingress/translation/annotations/plugins/http_method.go @@ -0,0 +1,64 @@ +// 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. +package plugins + +import ( + "net/http" + + "github.com/apache/apisix-ingress-controller/pkg/providers/ingress/translation/annotations" + apisixv1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1" + + "github.com/incubator4/go-resty-expr/expr" +) + +type HttpMethod struct{} + +// NewHttpHandler creates a handler to convert annotations about +// HttpMethod to APISIX cors plugin. +func NewHttpMethodHandler() PluginAnnotationsHandler { + return &HttpMethod{} +} + +func (h HttpMethod) PluginName() string { + return "response-rewrite" +} + +func (h HttpMethod) Handle(e annotations.Extractor) (interface{}, error) { + var plugin apisixv1.ResponseRewriteConfig + + allowMethods := e.GetStringsAnnotation(annotations.AnnotationsHttpAllowMethods) + blockMethods := e.GetStringsAnnotation(annotations.AnnotationsHttpBlockMethods) + + plugin.StatusCode = http.StatusMethodNotAllowed + + if len(allowMethods) > 0 { + plugin.LuaRestyExpr = []expr.Expr{ + expr.StringExpr("request_method").Not().In( + expr.ArrayExpr(expr.ExprArrayFromStrings(allowMethods)...), + ), + } + + } else if len(blockMethods) > 0 { + plugin.LuaRestyExpr = []expr.Expr{ + expr.StringExpr("request_method").In( + expr.ArrayExpr(expr.ExprArrayFromStrings(blockMethods)...), + ), + } + + } else { + return nil, nil + } + return &plugin, nil +} diff --git a/pkg/providers/ingress/translation/annotations/plugins/http_method_test.go b/pkg/providers/ingress/translation/annotations/plugins/http_method_test.go new file mode 100644 index 0000000000..7591ac19cb --- /dev/null +++ b/pkg/providers/ingress/translation/annotations/plugins/http_method_test.go @@ -0,0 +1,93 @@ +// 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. +package plugins + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/apache/apisix-ingress-controller/pkg/providers/ingress/translation/annotations" + apisixv1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1" + + "github.com/incubator4/go-resty-expr/expr" +) + +// annotations: +// +// k8s.apisix.apache.org/allow-http-methods: GET,POST +func TestAnnotationsHttpAllowMethod(t *testing.T) { + anno := map[string]string{ + annotations.AnnotationsHttpAllowMethods: "GET,POST", + } + p := NewHttpMethodHandler() + out, err := p.Handle(annotations.NewExtractor(anno)) + assert.Nil(t, err, "checking given error") + config := out.(*apisixv1.ResponseRewriteConfig) + + assert.Equal(t, http.StatusMethodNotAllowed, config.StatusCode) + assert.Equal(t, []expr.Expr{ + expr.StringExpr("request_method").Not().In(expr.ArrayExpr( + expr.StringExpr("GET"), + expr.StringExpr("POST"), + )), + }, config.LuaRestyExpr) +} + +// annotations: +// +// k8s.apisix.apache.org/block-http-methods: GET,PUT +func TestAnnotationsHttpBlockMethod(t *testing.T) { + anno := map[string]string{ + annotations.AnnotationsHttpBlockMethods: "GET,PUT", + } + p := NewHttpMethodHandler() + out, err := p.Handle(annotations.NewExtractor(anno)) + assert.Nil(t, err, "checking given error") + config := out.(*apisixv1.ResponseRewriteConfig) + + assert.Equal(t, 405, config.StatusCode) + assert.Equal(t, []expr.Expr{ + expr.StringExpr("request_method").In(expr.ArrayExpr( + expr.StringExpr("GET"), + expr.StringExpr("PUT"), + )), + }, config.LuaRestyExpr) +} + +// annotations: +// +// k8s.apisix.apache.org/allow-http-methods: GET +// k8s.apisix.apache.org/block-http-methods: POST,PUT +// +// Only allow methods would be accepted, block methods would be ignored. +func TestAnnotationsHttpBothMethod(t *testing.T) { + anno := map[string]string{ + annotations.AnnotationsHttpAllowMethods: "GET", + annotations.AnnotationsHttpBlockMethods: "POST,PUT", + } + p := NewHttpMethodHandler() + out, err := p.Handle(annotations.NewExtractor(anno)) + assert.Nil(t, err, "checking given error") + config := out.(*apisixv1.ResponseRewriteConfig) + + assert.Equal(t, http.StatusMethodNotAllowed, config.StatusCode) + assert.Equal(t, []expr.Expr{ + expr.StringExpr("request_method").Not().In(expr.ArrayExpr( + expr.StringExpr("GET"), + )), + }, config.LuaRestyExpr) +} diff --git a/pkg/providers/ingress/translation/annotations/plugins/plugins.go b/pkg/providers/ingress/translation/annotations/plugins/plugins.go index a0ae79bdaf..c228159f1e 100644 --- a/pkg/providers/ingress/translation/annotations/plugins/plugins.go +++ b/pkg/providers/ingress/translation/annotations/plugins/plugins.go @@ -43,6 +43,7 @@ var ( NewBasicAuthHandler(), NewKeyAuthHandler(), NewCSRFHandler(), + NewHttpMethodHandler(), } ) diff --git a/pkg/providers/ingress/translation/annotations/types.go b/pkg/providers/ingress/translation/annotations/types.go index f23cc6e7ae..89168f5586 100644 --- a/pkg/providers/ingress/translation/annotations/types.go +++ b/pkg/providers/ingress/translation/annotations/types.go @@ -62,6 +62,10 @@ const ( AnnotationsAllowlistSourceRange = AnnotationsPrefix + "allowlist-source-range" AnnotationsBlocklistSourceRange = AnnotationsPrefix + "blocklist-source-range" + // http-method plugin + AnnotationsHttpAllowMethods = AnnotationsPrefix + "http-allow-methods" + AnnotationsHttpBlockMethods = AnnotationsPrefix + "http-block-methods" + // key-auth plugin and basic-auth plugin // auth-type: keyAuth | basicAuth AnnotationsAuthType = AnnotationsPrefix + "auth-type" diff --git a/pkg/types/apisix/v1/plugin_types.go b/pkg/types/apisix/v1/plugin_types.go index 37e74b0bc8..73eda73e8b 100644 --- a/pkg/types/apisix/v1/plugin_types.go +++ b/pkg/types/apisix/v1/plugin_types.go @@ -14,7 +14,11 @@ // limitations under the License. package v1 -import "encoding/json" +import ( + "encoding/json" + + "github.com/incubator4/go-resty-expr/expr" +) // TrafficSplitConfig is the config of traffic-split plugin. // +k8s:deepcopy-gen=true @@ -128,6 +132,17 @@ type RewriteConfig struct { Headers Headers `json:"headers,omitempty"` } +// ResponseRewriteConfig is the rule config for response-rewrite plugin. +// +k8s:deepcopy-gen=true +type ResponseRewriteConfig struct { + StatusCode int `json:"status_code,omitempty"` + Body string `json:"body,omitempty"` + BodyBase64 bool `json:"body_base64,omitempty"` + Headers Headers `json:"headers,omitempty"` + LuaRestyExpr []expr.Expr `json:"vars,omitempty"` + Filters []map[string]string `json:"filters,omitempty"` +} + // RedirectConfig is the rule config for redirect plugin. // +k8s:deepcopy-gen=true type RedirectConfig struct { diff --git a/pkg/types/apisix/v1/zz_generated.deepcopy.go b/pkg/types/apisix/v1/zz_generated.deepcopy.go index f098172207..ea41a3ca7d 100644 --- a/pkg/types/apisix/v1/zz_generated.deepcopy.go +++ b/pkg/types/apisix/v1/zz_generated.deepcopy.go @@ -20,6 +20,10 @@ package v1 +import ( + expr "github.com/incubator4/go-resty-expr/expr" +) + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BasicAuthConfig) DeepCopyInto(out *BasicAuthConfig) { *out = *in @@ -356,6 +360,43 @@ func (in *RequestMirror) DeepCopy() *RequestMirror { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResponseRewriteConfig) DeepCopyInto(out *ResponseRewriteConfig) { + *out = *in + in.Headers.DeepCopyInto(&out.Headers) + if in.LuaRestyExpr != nil { + in, out := &in.LuaRestyExpr, &out.LuaRestyExpr + *out = make([]expr.Expr, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Filters != nil { + in, out := &in.Filters, &out.Filters + *out = make([]map[string]string, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResponseRewriteConfig. +func (in *ResponseRewriteConfig) DeepCopy() *ResponseRewriteConfig { + if in == nil { + return nil + } + out := new(ResponseRewriteConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RewriteConfig) DeepCopyInto(out *RewriteConfig) { *out = *in diff --git a/test/e2e/go.mod b/test/e2e/go.mod index 5250a5eca1..f43c66f53b 100644 --- a/test/e2e/go.mod +++ b/test/e2e/go.mod @@ -58,6 +58,7 @@ require ( github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/imkira/go-interpol v1.1.0 // indirect + github.com/incubator4/go-resty-expr v0.1.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect diff --git a/test/e2e/go.sum b/test/e2e/go.sum index 08b21bc9db..8e5a0c4d27 100644 --- a/test/e2e/go.sum +++ b/test/e2e/go.sum @@ -262,12 +262,9 @@ github.com/gruntwork-io/go-commons v0.8.0 h1:k/yypwrPqSeYHevLlEDmvmgQzcyTwrlZGRa github.com/gruntwork-io/go-commons v0.8.0/go.mod h1:gtp0yTtIBExIZp7vyIV9I0XQkVwiQZze678hvDXof78= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-immutable-radix v1.3.0 h1:8exGP7ego3OmkfksihtSouGMZ+hQrhxx+FVELeXpVPE= github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-memdb v1.3.3 h1:oGfEWrFuxtIUF3W2q/Jzt6G85TrMk9ey6XfYLvVe1Wo= -github.com/hashicorp/go-memdb v1.3.3/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg= github.com/hashicorp/go-memdb v1.3.4 h1:XSL3NR682X/cVk2IeV0d70N4DZ9ljI885xAEU8IoK3c= github.com/hashicorp/go-memdb v1.3.4/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -286,6 +283,8 @@ github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= +github.com/incubator4/go-resty-expr v0.1.1 h1:9ur1M+p0wDzL1bprdGzHugGkfK0Yd3Ba/ijcgvL+a1k= +github.com/incubator4/go-resty-expr v0.1.1/go.mod h1:w9YQkQLUs1cArOb4O7SGJwJL/L8kuAo6y5CVS2o9eag= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -388,14 +387,11 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= diff --git a/test/e2e/suite-annotations/http_method.go b/test/e2e/suite-annotations/http_method.go new file mode 100644 index 0000000000..60304535b7 --- /dev/null +++ b/test/e2e/suite-annotations/http_method.go @@ -0,0 +1,248 @@ +// 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. +package annotations + +import ( + "fmt" + "net/http" + "time" + + ginkgo "github.com/onsi/ginkgo/v2" + "github.com/stretchr/testify/assert" + + "github.com/apache/apisix-ingress-controller/test/e2e/scaffold" +) + +var _ = ginkgo.Describe("suite-annotations: allow-http-methods annotations", func() { + s := scaffold.NewDefaultScaffold() + + ginkgo.It("enable in ingress networking/v1", func() { + backendSvc, backendPort := s.DefaultHTTPBackend() + ing := fmt.Sprintf(` +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + kubernetes.io/ingress.class: apisix + k8s.apisix.apache.org/http-allow-methods: POST,PUT + name: ingress-v1 +spec: + rules: + - host: httpbin.org + http: + paths: + - path: /* + pathType: Prefix + backend: + service: + name: %s + port: + number: %d +`, backendSvc, backendPort[0]) + err := s.CreateResourceFromString(ing) + assert.Nil(ginkgo.GinkgoT(), err, "creating ingress") + time.Sleep(5 * time.Second) + + respGet := s.NewAPISIXClient().GET("/get").WithHeader("Host", "httpbin.org").Expect() + respGet.Status(http.StatusMethodNotAllowed) + + respPost := s.NewAPISIXClient().POST("/post").WithHeader("Host", "httpbin.org").Expect() + respPost.Status(http.StatusOK) + + respPut := s.NewAPISIXClient().PUT("/put").WithHeader("Host", "httpbin.org").Expect() + respPut.Status(http.StatusOK) + }) + + ginkgo.It("enable in ingress networking/v1beta1", func() { + backendSvc, backendPort := s.DefaultHTTPBackend() + ing := fmt.Sprintf(` +apiVersion: networking.k8s.io/v1beta1 +kind: Ingress +metadata: + annotations: + kubernetes.io/ingress.class: apisix + k8s.apisix.apache.org/http-allow-methods: POST,PUT + name: ingress-v1beta1 +spec: + rules: + - host: httpbin.org + http: + paths: + - path: /* + pathType: Prefix + backend: + serviceName: %s + servicePort: %d +`, backendSvc, backendPort[0]) + err := s.CreateResourceFromString(ing) + assert.Nil(ginkgo.GinkgoT(), err, "creating ingress") + time.Sleep(5 * time.Second) + + respGet := s.NewAPISIXClient().GET("/get").WithHeader("Host", "httpbin.org").Expect() + respGet.Status(http.StatusMethodNotAllowed) + + respPost := s.NewAPISIXClient().POST("/post").WithHeader("Host", "httpbin.org").Expect() + respPost.Status(http.StatusOK) + + respPut := s.NewAPISIXClient().PUT("/put").WithHeader("Host", "httpbin.org").Expect() + respPut.Status(http.StatusOK) + }) + + ginkgo.It("enable in ingress extensions/v1beta1", func() { + backendSvc, backendPort := s.DefaultHTTPBackend() + ing := fmt.Sprintf(` +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + kubernetes.io/ingress.class: apisix + k8s.apisix.apache.org/http-allow-methods: POST,PUT + name: ingress-extensions-v1beta1 +spec: + rules: + - host: httpbin.org + http: + paths: + - path: /* + pathType: Prefix + backend: + serviceName: %s + servicePort: %d +`, backendSvc, backendPort[0]) + err := s.CreateResourceFromString(ing) + assert.Nil(ginkgo.GinkgoT(), err, "creating ingress") + time.Sleep(5 * time.Second) + + respGet := s.NewAPISIXClient().GET("/get").WithHeader("Host", "httpbin.org").Expect() + respGet.Status(http.StatusMethodNotAllowed) + + respPost := s.NewAPISIXClient().POST("/post").WithHeader("Host", "httpbin.org").Expect() + respPost.Status(http.StatusOK) + + respPut := s.NewAPISIXClient().PUT("/put").WithHeader("Host", "httpbin.org").Expect() + respPut.Status(http.StatusOK) + }) +}) + +var _ = ginkgo.Describe("suite-annotations: blocklist-http-methods annotations", func() { + s := scaffold.NewDefaultScaffold() + + ginkgo.It("enable in ingress networking/v1", func() { + backendSvc, backendPort := s.DefaultHTTPBackend() + ing := fmt.Sprintf(` +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + kubernetes.io/ingress.class: apisix + k8s.apisix.apache.org/http-block-methods: GET + name: ingress-v1 +spec: + rules: + - host: httpbin.org + http: + paths: + - path: /* + pathType: Prefix + backend: + service: + name: %s + port: + number: %d +`, backendSvc, backendPort[0]) + err := s.CreateResourceFromString(ing) + assert.Nil(ginkgo.GinkgoT(), err, "creating ingress") + time.Sleep(5 * time.Second) + + respGet := s.NewAPISIXClient().GET("/get").WithHeader("Host", "httpbin.org").Expect() + respGet.Status(http.StatusMethodNotAllowed) + + respPost := s.NewAPISIXClient().POST("/post").WithHeader("Host", "httpbin.org").Expect() + respPost.Status(http.StatusOK) + + respPut := s.NewAPISIXClient().PUT("/put").WithHeader("Host", "httpbin.org").Expect() + respPut.Status(http.StatusOK) + }) + + ginkgo.It("enable in ingress networking/v1beta1", func() { + backendSvc, backendPort := s.DefaultHTTPBackend() + ing := fmt.Sprintf(` +apiVersion: networking.k8s.io/v1beta1 +kind: Ingress +metadata: + annotations: + kubernetes.io/ingress.class: apisix + k8s.apisix.apache.org/http-block-methods: GET + name: ingress-v1beta1 +spec: + rules: + - host: httpbin.org + http: + paths: + - path: /* + pathType: Prefix + backend: + serviceName: %s + servicePort: %d +`, backendSvc, backendPort[0]) + err := s.CreateResourceFromString(ing) + assert.Nil(ginkgo.GinkgoT(), err, "creating ingress") + time.Sleep(5 * time.Second) + + respGet := s.NewAPISIXClient().GET("/get").WithHeader("Host", "httpbin.org").Expect() + respGet.Status(http.StatusMethodNotAllowed) + + respPost := s.NewAPISIXClient().POST("/post").WithHeader("Host", "httpbin.org").Expect() + respPost.Status(http.StatusOK) + + respPut := s.NewAPISIXClient().PUT("/put").WithHeader("Host", "httpbin.org").Expect() + respPut.Status(http.StatusOK) + }) + + ginkgo.It("enable in ingress extensions/v1beta1", func() { + backendSvc, backendPort := s.DefaultHTTPBackend() + ing := fmt.Sprintf(` +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + kubernetes.io/ingress.class: apisix + k8s.apisix.apache.org/http-block-methods: GET + name: ingress-extensions-v1beta1 +spec: + rules: + - host: httpbin.org + http: + paths: + - path: /* + pathType: Prefix + backend: + serviceName: %s + servicePort: %d +`, backendSvc, backendPort[0]) + err := s.CreateResourceFromString(ing) + assert.Nil(ginkgo.GinkgoT(), err, "creating ingress") + time.Sleep(5 * time.Second) + + respGet := s.NewAPISIXClient().GET("/get").WithHeader("Host", "httpbin.org").Expect() + respGet.Status(http.StatusMethodNotAllowed) + + respPost := s.NewAPISIXClient().POST("/post").WithHeader("Host", "httpbin.org").Expect() + respPost.Status(http.StatusOK) + + respPut := s.NewAPISIXClient().PUT("/put").WithHeader("Host", "httpbin.org").Expect() + respPut.Status(http.StatusOK) + }) +})