This is an automated email from the ASF dual-hosted git repository.
kvn pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-ingress-controller.git
The following commit(s) were added to refs/heads/master by this push:
new 8824bbd chore: refactor the process of annotations (#443)
8824bbd is described below
commit 8824bbdf113bbf72649ccd5dc43af3a66773bf5b
Author: Alex Zhang <[email protected]>
AuthorDate: Fri May 14 11:15:45 2021 +0800
chore: refactor the process of annotations (#443)
---
docs/en/latest/concepts/annotations.md | 55 ++++
pkg/kube/translation/annotations.go | 29 +-
pkg/kube/translation/annotations/cors.go | 47 ++--
pkg/kube/translation/annotations/cors_test.go | 46 ++++
pkg/kube/translation/annotations/iprestriction.go | 35 ++-
.../translation/annotations/iprestriction_test.go | 43 +++
pkg/kube/translation/annotations/types.go | 72 +++++
pkg/kube/translation/apisix_route.go | 2 +-
pkg/kube/translation/ingress.go | 23 +-
pkg/types/apisix/v1/plugin_types.go | 49 ++++
pkg/types/apisix/v1/types.go | 20 --
pkg/types/apisix/v1/zz_generated.deepcopy.go | 42 +++
test/e2e/annotations/cors.go | 296 +++++++++++++++++++++
test/e2e/annotations/iprestriction.go | 119 +++++++++
test/e2e/e2e.go | 1 +
15 files changed, 808 insertions(+), 71 deletions(-)
diff --git a/docs/en/latest/concepts/annotations.md
b/docs/en/latest/concepts/annotations.md
new file mode 100644
index 0000000..c565c42
--- /dev/null
+++ b/docs/en/latest/concepts/annotations.md
@@ -0,0 +1,55 @@
+---
+title: Annotations
+---
+
+<!--
+#
+# 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.
+#
+-->
+
+This document describes all supported annotations and their functions. You can
add these annotations in the
[Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/)
resources so that advanced features in [Apache
APISIX](https://apisix.apache.org) can be combined into
[Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress)
resources.
+
+> Note all keys and values of annotations are strings, so boolean value like
`true` and `false` should be represented as `"true"` and `"false"`.
+
+CORS Support
+------------
+
+In order to enable
[CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing), the
annotation `k8s.apisix.apache.org/enable-cors` should be set to `"true"`, also,
there are some other annotations to customize the cors behavior.
+
+* `k8s.apisix.apache.org/cors-allow-origin`
+
+This annotation controls which origins will be allowed, multiple origins join
together with `,`, for instance: `https://foo.com,http://bar.com:8080`
+
+Default value is `"*"`, which means all origins are allowed.
+
+* `k8s.apisix.apache.org/cors-allow-headers`
+
+This annotation controls which headers are accepted, multiple headers join
together with `,`.
+
+Default is `"*"`, which means all headers are accepted.
+
+* `k8s.apisix.apache.org/cors-allow-methods`
+
+This annotation controls which methods are accepted, multiple methods join
together with `,`.
+
+Default is `"*"`, which means all HTTP methods are accepted.
+
+Allowlist Source Range
+-----------------------
+
+You can specify the allowed client IP addresses or nets by the annotation
`k8s.apisix.apache.org/allowlist-source-range`, multiple IP adddresses or nets
join together with `,`,
+for instance, `k8s.apisix.apache.org/allowlist-source-range:
10.0.5.0/16,127.0.0.1,192.168.3.98`. Default value is *empty*, which means the
sources are unlimited.
diff --git a/pkg/kube/translation/annotations.go
b/pkg/kube/translation/annotations.go
index 57a4c59..6d515ee 100644
--- a/pkg/kube/translation/annotations.go
+++ b/pkg/kube/translation/annotations.go
@@ -15,17 +15,34 @@
package translation
import (
+ "go.uber.org/zap"
+
"github.com/apache/apisix-ingress-controller/pkg/kube/translation/annotations"
+ "github.com/apache/apisix-ingress-controller/pkg/log"
apisix "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
)
-func (t *translator) TranslateAnnotations(anno map[string]string)
apisix.Plugins {
- plugins := make(apisix.Plugins)
- if cors := annotations.BuildCorsPlugin(anno); cors != nil {
- plugins["cors"] = cors
+var (
+ _handlers = []annotations.Handler{
+ annotations.NewCorsHandler(),
+ annotations.NewIPRestrictionHandler(),
}
- if ipRestriction := annotations.BuildIpRestrictionPlugin(anno);
ipRestriction != nil {
- plugins["ip-restriction"] = ipRestriction
+)
+
+func (t *translator) translateAnnotations(anno map[string]string)
apisix.Plugins {
+ extractor := annotations.NewExtractor(anno)
+ plugins := make(apisix.Plugins)
+ for _, handler := range _handlers {
+ out, err := handler.Handle(extractor)
+ if err != nil {
+ log.Warnw("failed to handle annotations",
+ zap.Error(err),
+ )
+ continue
+ }
+ if out != nil {
+ plugins[handler.PluginName()] = out
+ }
}
return plugins
}
diff --git a/pkg/kube/translation/annotations/cors.go
b/pkg/kube/translation/annotations/cors.go
index 15bb1c6..51f8e87 100644
--- a/pkg/kube/translation/annotations/cors.go
+++ b/pkg/kube/translation/annotations/cors.go
@@ -14,37 +14,36 @@
// limitations under the License.
package annotations
-// CorsPlugin is the cors plugin.
-type CorsPlugin struct {
- Origins string `json:"origins,omitempty"`
- Headers string `json:"headers,omitempty"`
- Methods string `json:"methods,omitempty"`
- MaxAge int64 `json:"max_age,omitempty"`
-}
+import (
+ apisixv1
"github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
+)
-var (
+const (
_enableCors = "k8s.apisix.apache.org/enable-cors"
_corsAllowOrigin = "k8s.apisix.apache.org/cors-allow-origin"
_corsAllowHeaders = "k8s.apisix.apache.org/cors-allow-headers"
_corsAllowMethods = "k8s.apisix.apache.org/cors-allow-methods"
)
-// BuildCorsPlugin build the cors plugin config body.
-func BuildCorsPlugin(annotations map[string]string) *CorsPlugin {
- enable, ok := annotations[_enableCors]
- if !ok || enable == "false" {
- return nil
- }
+type cors struct{}
- var cors CorsPlugin
- if ao, ok := annotations[_corsAllowOrigin]; ok {
- cors.Origins = ao
- }
- if ah, ok := annotations[_corsAllowHeaders]; ok {
- cors.Headers = ah
- }
- if am, ok := annotations[_corsAllowMethods]; ok {
- cors.Methods = am
+// NewCorsHandler creates a handler to convert annotations about
+// cors to APISIX cors plugin.
+func NewCorsHandler() Handler {
+ return &cors{}
+}
+
+func (c *cors) PluginName() string {
+ return "cors"
+}
+
+func (c *cors) Handle(e Extractor) (interface{}, error) {
+ if !e.GetBoolAnnotation(_enableCors) {
+ return nil, nil
}
- return &cors
+ return &apisixv1.CorsConfig{
+ AllowOrigins: e.GetStringAnnotation(_corsAllowOrigin),
+ AllowMethods: e.GetStringAnnotation(_corsAllowMethods),
+ AllowHeaders: e.GetStringAnnotation(_corsAllowHeaders),
+ }, nil
}
diff --git a/pkg/kube/translation/annotations/cors_test.go
b/pkg/kube/translation/annotations/cors_test.go
new file mode 100644
index 0000000..d1b6b22
--- /dev/null
+++ b/pkg/kube/translation/annotations/cors_test.go
@@ -0,0 +1,46 @@
+// 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 (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+
+ apisixv1
"github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
+)
+
+func TestCorsHandler(t *testing.T) {
+ annotations := map[string]string{
+ _enableCors: "true",
+ _corsAllowHeaders: "abc,def",
+ _corsAllowOrigin: "https://a.com",
+ _corsAllowMethods: "GET,HEAD",
+ }
+ p := NewCorsHandler()
+ out, err := p.Handle(NewExtractor(annotations))
+ assert.Nil(t, err, "checking given error")
+ config := out.(*apisixv1.CorsConfig)
+ assert.Equal(t, config.AllowHeaders, "abc,def")
+ assert.Equal(t, config.AllowOrigins, "https://a.com")
+ assert.Equal(t, config.AllowMethods, "GET,HEAD")
+
+ assert.Equal(t, p.PluginName(), "cors")
+
+ annotations[_enableCors] = "false"
+ out, err = p.Handle(NewExtractor(annotations))
+ assert.Nil(t, err, "checking given error")
+ assert.Nil(t, out, "checking given output")
+}
diff --git a/pkg/kube/translation/annotations/iprestriction.go
b/pkg/kube/translation/annotations/iprestriction.go
index ddc7e02..73f98b5 100644
--- a/pkg/kube/translation/annotations/iprestriction.go
+++ b/pkg/kube/translation/annotations/iprestriction.go
@@ -14,23 +14,32 @@
// limitations under the License.
package annotations
-import "strings"
+import (
+ apisixv1
"github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
+)
-var (
- _whitelist = "k8s.apisix.apache.org/whitelist-source-range"
+const (
+ _allowlistSourceRange = "k8s.apisix.apache.org/allowlist-source-range"
)
-// IpRestrictionPlugin is the ip-restriction plugin.
-type IpRestrictionPlugin struct {
- Whitelist []string `json:"whitelist,omitempty"`
+type ipRestriction struct{}
+
+// NewIPRestrictionHandler creates a handler to convert
+// annotations about client ips control to APISIX ip-restrict plugin.
+func NewIPRestrictionHandler() Handler {
+ return &ipRestriction{}
+}
+
+func (i *ipRestriction) PluginName() string {
+ return "ip-restriction"
}
-// BuildIpRestrictionPlugin builds the ip-restriction plugin from annotations.
-func BuildIpRestrictionPlugin(annotations map[string]string)
*IpRestrictionPlugin {
- if whitelist, ok := annotations[_whitelist]; ok {
- return &IpRestrictionPlugin{
- Whitelist: strings.Split(whitelist, ","),
- }
+func (i *ipRestriction) Handle(e Extractor) (interface{}, error) {
+ var plugin apisixv1.IPRestrictConfig
+ if allowlist := e.GetStringsAnnotation(_allowlistSourceRange);
len(allowlist) > 0 {
+ plugin.Whitelist = allowlist
+ } else {
+ return nil, nil
}
- return nil
+ return &plugin, nil
}
diff --git a/pkg/kube/translation/annotations/iprestriction_test.go
b/pkg/kube/translation/annotations/iprestriction_test.go
new file mode 100644
index 0000000..a38f31e
--- /dev/null
+++ b/pkg/kube/translation/annotations/iprestriction_test.go
@@ -0,0 +1,43 @@
+// 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 (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+
+ apisixv1
"github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
+)
+
+func TestIPRestrictionHandler(t *testing.T) {
+ annotations := map[string]string{
+ _allowlistSourceRange: "10.2.2.2,192.168.0.0/16",
+ }
+ p := NewIPRestrictionHandler()
+ out, err := p.Handle(NewExtractor(annotations))
+ assert.Nil(t, err, "checking given error")
+ config := out.(*apisixv1.IPRestrictConfig)
+ assert.Len(t, config.Whitelist, 2, "checking size of white list")
+ assert.Equal(t, config.Whitelist[0], "10.2.2.2")
+ assert.Equal(t, config.Whitelist[1], "192.168.0.0/16")
+
+ assert.Equal(t, p.PluginName(), "ip-restriction")
+
+ delete(annotations, _allowlistSourceRange)
+ out, err = p.Handle(NewExtractor(annotations))
+ assert.Nil(t, err, "checking given error")
+ assert.Nil(t, out, "checking the given ip-restrction plugin config")
+}
diff --git a/pkg/kube/translation/annotations/types.go
b/pkg/kube/translation/annotations/types.go
new file mode 100644
index 0000000..3746759
--- /dev/null
+++ b/pkg/kube/translation/annotations/types.go
@@ -0,0 +1,72 @@
+// 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 (
+ "strings"
+)
+
+// Extractor encapsulates some auxiliary methods to extract annotations.
+type Extractor interface {
+ // GetStringAnnotation returns the string value of the target
annotation.
+ // When the target annoatation is missing, empty string will be given.
+ GetStringAnnotation(string) string
+ // GetStringsAnnotation returns a string slice which splits the value
of target
+ // annotation by the comma symbol. When the target annotation is
missing, a nil
+ // slice will be given.
+ GetStringsAnnotation(string) []string
+ // GetBoolAnnotation returns a boolean value from the given annotation.
+ // When value is "true", true will be given, other values will be
treated as
+ // false.
+ GetBoolAnnotation(string) bool
+}
+
+// Handler abstracts the behavior so that the apisix-ingress-controller knows
+// how to parse some annotations and convert them to APISIX plugins.
+type Handler interface {
+ // Handle parses the target annotation and converts it to the
type-agnostic structure.
+ // The return value might be nil since some features have an explicit
switch, users should
+ // judge whether Handle is failed by the second error value.
+ Handle(Extractor) (interface{}, error)
+ // PluginName returns a string which indicates the target plugin name
in APISIX.
+ PluginName() string
+}
+
+type extractor struct {
+ annotations map[string]string
+}
+
+func (e *extractor) GetStringAnnotation(name string) string {
+ return e.annotations[name]
+}
+
+func (e *extractor) GetStringsAnnotation(name string) []string {
+ value := e.GetStringAnnotation(name)
+ if value == "" {
+ return nil
+ }
+ return strings.Split(e.annotations[name], ",")
+}
+
+func (e *extractor) GetBoolAnnotation(name string) bool {
+ return e.annotations[name] == "true"
+}
+
+// NewExtractor creates an annotations extractor.
+func NewExtractor(annotations map[string]string) Extractor {
+ return &extractor{
+ annotations: annotations,
+ }
+}
diff --git a/pkg/kube/translation/apisix_route.go
b/pkg/kube/translation/apisix_route.go
index df5c9aa..dceda4f 100644
--- a/pkg/kube/translation/apisix_route.go
+++ b/pkg/kube/translation/apisix_route.go
@@ -31,7 +31,7 @@ func (t *translator) TranslateRouteV1(ar
*configv1.ApisixRoute) (*TranslateConte
ctx := &TranslateContext{
upstreamMap: make(map[string]struct{}),
}
- plugins := t.TranslateAnnotations(ar.Annotations)
+ plugins := t.translateAnnotations(ar.Annotations)
for _, r := range ar.Spec.Rules {
for _, p := range r.Http.Paths {
diff --git a/pkg/kube/translation/ingress.go b/pkg/kube/translation/ingress.go
index 0590592..ab0ea6f 100644
--- a/pkg/kube/translation/ingress.go
+++ b/pkg/kube/translation/ingress.go
@@ -33,6 +33,7 @@ func (t *translator) translateIngressV1(ing
*networkingv1.Ingress) (*TranslateCo
ctx := &TranslateContext{
upstreamMap: make(map[string]struct{}),
}
+ plugins := t.translateAnnotations(ing.Annotations)
for _, rule := range ing.Spec.Rules {
for _, pathRule := range rule.HTTP.Paths {
@@ -75,6 +76,9 @@ func (t *translator) translateIngressV1(ing
*networkingv1.Ingress) (*TranslateCo
route.ID = id.GenID(route.Name)
route.Host = rule.Host
route.Uris = uris
+ if len(plugins) > 0 {
+ route.Plugins = *(plugins.DeepCopy())
+ }
if ups != nil {
route.UpstreamId = ups.ID
}
@@ -88,6 +92,7 @@ func (t *translator) translateIngressV1beta1(ing
*networkingv1beta1.Ingress) (*T
ctx := &TranslateContext{
upstreamMap: make(map[string]struct{}),
}
+ plugins := t.translateAnnotations(ing.Annotations)
for _, rule := range ing.Spec.Rules {
for _, pathRule := range rule.HTTP.Paths {
@@ -130,6 +135,9 @@ func (t *translator) translateIngressV1beta1(ing
*networkingv1beta1.Ingress) (*T
route.ID = id.GenID(route.Name)
route.Host = rule.Host
route.Uris = uris
+ if len(plugins) > 0 {
+ route.Plugins = *(plugins.DeepCopy())
+ }
if ups != nil {
route.UpstreamId = ups.ID
}
@@ -174,6 +182,7 @@ func (t *translator) translateIngressExtensionsV1beta1(ing
*extensionsv1beta1.In
ctx := &TranslateContext{
upstreamMap: make(map[string]struct{}),
}
+ plugins := t.translateAnnotations(ing.Annotations)
for _, rule := range ing.Spec.Rules {
for _, pathRule := range rule.HTTP.Paths {
@@ -212,14 +221,14 @@ func (t *translator)
translateIngressExtensionsV1beta1(ing *extensionsv1beta1.In
}
uris = append(uris, prefix)
}
- route := &apisixv1.Route{
- Metadata: apisixv1.Metadata{
- Name:
composeIngressRouteName(rule.Host, pathRule.Path),
- },
- Host: rule.Host,
- Uris: uris,
- }
+ route := apisixv1.NewDefaultRoute()
+ route.Name = composeIngressRouteName(rule.Host,
pathRule.Path)
route.ID = id.GenID(route.Name)
+ route.Host = rule.Host
+ route.Uris = uris
+ if len(plugins) > 0 {
+ route.Plugins = *(plugins.DeepCopy())
+ }
if ups != nil {
route.UpstreamId = ups.ID
}
diff --git a/pkg/types/apisix/v1/plugin_types.go
b/pkg/types/apisix/v1/plugin_types.go
new file mode 100644
index 0000000..b1090db
--- /dev/null
+++ b/pkg/types/apisix/v1/plugin_types.go
@@ -0,0 +1,49 @@
+// 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 v1
+
+// TrafficSplitConfig is the config of traffic-split plugin.
+// +k8s:deepcopy-gen=true
+type TrafficSplitConfig struct {
+ Rules []TrafficSplitConfigRule `json:"rules"`
+}
+
+// TrafficSplitConfigRule is the rule config in traffic-split plugin config.
+// +k8s:deepcopy-gen=true
+type TrafficSplitConfigRule struct {
+ WeightedUpstreams []TrafficSplitConfigRuleWeightedUpstream
`json:"weighted_upstreams"`
+}
+
+// TrafficSplitConfigRuleWeightedUpstream is the weighted upstream config in
+// the traffic split plugin rule.
+// +k8s:deepcopy-gen=true
+type TrafficSplitConfigRuleWeightedUpstream struct {
+ UpstreamID string `json:"upstream_id,omitempty"`
+ Weight int `json:"weight"`
+}
+
+// IPRestrictConfig is the rule config for ip-restriction plugin.
+// +k8s:deepcopy-gen=true
+type IPRestrictConfig struct {
+ Whitelist []string `json:"whitelist,omitempty"`
+}
+
+// CorsConfig is the rule config for cors plugin.
+// +k8s:deepcopy-gen=true
+type CorsConfig struct {
+ AllowOrigins string `json:"allow_origins,omitempty"`
+ AllowMethods string `json:"allow_methods,omitempty"`
+ AllowHeaders string `json:"allow_headers,omitempty"`
+}
diff --git a/pkg/types/apisix/v1/types.go b/pkg/types/apisix/v1/types.go
index 082beec..4dfc71f 100644
--- a/pkg/types/apisix/v1/types.go
+++ b/pkg/types/apisix/v1/types.go
@@ -303,26 +303,6 @@ type Ssl struct {
Labels map[string]string `json:"labels,omitempty"
yaml:"labels,omitempty"`
}
-// TrafficSplitConfig is the config of traffic-split plugin.
-// +k8s:deepcopy-gen=true
-type TrafficSplitConfig struct {
- Rules []TrafficSplitConfigRule `json:"rules"`
-}
-
-// TrafficSplitConfigRule is the rule config in traffic-split plugin config.
-// +k8s:deepcopy-gen=true
-type TrafficSplitConfigRule struct {
- WeightedUpstreams []TrafficSplitConfigRuleWeightedUpstream
`json:"weighted_upstreams"`
-}
-
-// TrafficSplitConfigRuleWeightedUpstream is the weighted upstream config in
-// the traffic split plugin rule.
-// +k8s:deepcopy-gen=true
-type TrafficSplitConfigRuleWeightedUpstream struct {
- UpstreamID string `json:"upstream_id,omitempty"`
- Weight int `json:"weight"`
-}
-
// StreamRoute represents the stream_route object in APISIX.
// +k8s:deepcopy-gen=true
type StreamRoute struct {
diff --git a/pkg/types/apisix/v1/zz_generated.deepcopy.go
b/pkg/types/apisix/v1/zz_generated.deepcopy.go
index a264ac0..b270fdb 100644
--- a/pkg/types/apisix/v1/zz_generated.deepcopy.go
+++ b/pkg/types/apisix/v1/zz_generated.deepcopy.go
@@ -21,6 +21,22 @@ limitations under the License.
package v1
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver,
writing into out. in must be non-nil.
+func (in *CorsConfig) DeepCopyInto(out *CorsConfig) {
+ *out = *in
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver,
creating a new CorsConfig.
+func (in *CorsConfig) DeepCopy() *CorsConfig {
+ if in == nil {
+ return nil
+ }
+ out := new(CorsConfig)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver,
writing into out. in must be non-nil.
func (in *GlobalRule) DeepCopyInto(out *GlobalRule) {
*out = *in
in.Plugins.DeepCopyInto(&out.Plugins)
@@ -38,6 +54,27 @@ func (in *GlobalRule) DeepCopy() *GlobalRule {
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver,
writing into out. in must be non-nil.
+func (in *IPRestrictConfig) DeepCopyInto(out *IPRestrictConfig) {
+ *out = *in
+ if in.Whitelist != nil {
+ in, out := &in.Whitelist, &out.Whitelist
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver,
creating a new IPRestrictConfig.
+func (in *IPRestrictConfig) DeepCopy() *IPRestrictConfig {
+ if in == nil {
+ return nil
+ }
+ out := new(IPRestrictConfig)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver,
writing into out. in must be non-nil.
func (in *Metadata) DeepCopyInto(out *Metadata) {
*out = *in
if in.Labels != nil {
@@ -149,6 +186,11 @@ func (in *StreamRoute) DeepCopyInto(out *StreamRoute) {
(*out)[key] = val
}
}
+ if in.Upstream != nil {
+ in, out := &in.Upstream, &out.Upstream
+ *out = new(Upstream)
+ (*in).DeepCopyInto(*out)
+ }
return
}
diff --git a/test/e2e/annotations/cors.go b/test/e2e/annotations/cors.go
new file mode 100644
index 0000000..3c09eb2
--- /dev/null
+++ b/test/e2e/annotations/cors.go
@@ -0,0 +1,296 @@
+// 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"
+
+ "github.com/onsi/ginkgo"
+ "github.com/stretchr/testify/assert"
+
+ "github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+)
+
+var _ = ginkgo.Describe("cors 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/enable-cors: "true"
+ k8s.apisix.apache.org/cors-allow-origin: https://foo.com,https://bar.com
+ k8s.apisix.apache.org/cors-allow-headers: x-foo-1,x-foo-2
+ k8s.apisix.apache.org/cors-allow-methods: GET,POST,PUT
+ name: ingress-v1
+spec:
+ rules:
+ - host: httpbin.org
+ http:
+ paths:
+ - path: /ip
+ pathType: Exact
+ 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)
+
+ resp := s.NewAPISIXClient().GET("/ip").WithHeader("Host",
"httpbin.org").Expect()
+ resp.Status(http.StatusOK)
+ // As httpbin itself adds this header, we don't check it here.
+ //resp.Header("Access-Control-Allow-Origin").Empty()
+ resp.Header("Access-Control-Allow-Methods").Empty()
+ resp.Header("Access-Control-Allow-Headers").Empty()
+
+ resp = s.NewAPISIXClient().GET("/ip").WithHeader("Host",
"httpbin.org").WithHeader("Origin", "https://baz.com").Expect()
+ resp.Status(http.StatusOK)
+ // As httpbin itself adds this header, we don't check it here.
+ //resp.Header("Access-Control-Allow-Origin").Empty()
+ resp.Header("Access-Control-Allow-Methods").Empty()
+ resp.Header("Access-Control-Allow-Headers").Empty()
+
+ resp = s.NewAPISIXClient().GET("/ip").WithHeader("Host",
"httpbin.org").WithHeader("Origin", "https://foo.com").Expect()
+ resp.Status(http.StatusOK)
+
resp.Header("Access-Control-Allow-Origin").Equal("https://foo.com")
+
resp.Header("Access-Control-Allow-Methods").Equal("GET,POST,PUT")
+
resp.Header("Access-Control-Allow-Headers").Equal("x-foo-1,x-foo-2")
+ })
+
+ ginkgo.It("disable 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/enable-cors: "false"
+ k8s.apisix.apache.org/cors-allow-origin: https://foo.com,https://bar.com
+ k8s.apisix.apache.org/cors-allow-headers: x-foo-1,x-foo-2
+ k8s.apisix.apache.org/cors-allow-methods: GET,POST,PUT
+ name: ingress-v1
+spec:
+ rules:
+ - host: httpbin.org
+ http:
+ paths:
+ - path: /ip
+ pathType: Exact
+ 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)
+
+ resp := s.NewAPISIXClient().GET("/ip").WithHeader("Host",
"httpbin.org").WithHeader("Origin", "https://foo.com").Expect()
+ resp.Status(http.StatusOK)
+ // As httpbin itself adds this header, we don't check it here.
+ //resp.Header("Access-Control-Allow-Origin").Empty()
+ resp.Header("Access-Control-Allow-Methods").Empty()
+ resp.Header("Access-Control-Allow-Headers").Empty()
+ })
+
+ 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/enable-cors: "true"
+ k8s.apisix.apache.org/cors-allow-origin: https://foo.com,https://bar.com
+ k8s.apisix.apache.org/cors-allow-headers: x-foo-1,x-foo-2
+ k8s.apisix.apache.org/cors-allow-methods: GET,POST,PUT
+ name: ingress-v1beta1
+spec:
+ rules:
+ - host: httpbin.org
+ http:
+ paths:
+ - path: /ip
+ pathType: Exact
+ backend:
+ serviceName: %s
+ servicePort: %d
+`, backendSvc, backendPort[0])
+ err := s.CreateResourceFromString(ing)
+ assert.Nil(ginkgo.GinkgoT(), err, "creating ingress")
+ time.Sleep(5 * time.Second)
+
+ resp := s.NewAPISIXClient().GET("/ip").WithHeader("Host",
"httpbin.org").Expect()
+ resp.Status(http.StatusOK)
+ // As httpbin itself adds this header, we don't check it here.
+ //resp.Header("Access-Control-Allow-Origin").Empty()
+ resp.Header("Access-Control-Allow-Methods").Empty()
+ resp.Header("Access-Control-Allow-Headers").Empty()
+
+ resp = s.NewAPISIXClient().GET("/ip").WithHeader("Host",
"httpbin.org").WithHeader("Origin", "https://baz.com").Expect()
+ resp.Status(http.StatusOK)
+ // As httpbin itself adds this header, we don't check it here.
+ //resp.Header("Access-Control-Allow-Origin").Empty()
+ resp.Header("Access-Control-Allow-Methods").Empty()
+ resp.Header("Access-Control-Allow-Headers").Empty()
+
+ resp = s.NewAPISIXClient().GET("/ip").WithHeader("Host",
"httpbin.org").WithHeader("Origin", "https://foo.com").Expect()
+ resp.Status(http.StatusOK)
+
resp.Header("Access-Control-Allow-Origin").Equal("https://foo.com")
+
resp.Header("Access-Control-Allow-Methods").Equal("GET,POST,PUT")
+
resp.Header("Access-Control-Allow-Headers").Equal("x-foo-1,x-foo-2")
+ })
+
+ ginkgo.It("disable 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/enable-cors: "false"
+ k8s.apisix.apache.org/cors-allow-origin: https://foo.com,https://bar.com
+ k8s.apisix.apache.org/cors-allow-headers: x-foo-1,x-foo-2
+ k8s.apisix.apache.org/cors-allow-methods: GET,POST,PUT
+ name: ingress-v1beta1
+spec:
+ rules:
+ - host: httpbin.org
+ http:
+ paths:
+ - path: /ip
+ pathType: Exact
+ backend:
+ serviceName: %s
+ servicePort: %d
+`, backendSvc, backendPort[0])
+ err := s.CreateResourceFromString(ing)
+ assert.Nil(ginkgo.GinkgoT(), err, "creating ingress")
+ time.Sleep(5 * time.Second)
+
+ resp := s.NewAPISIXClient().GET("/ip").WithHeader("Host",
"httpbin.org").Expect()
+ resp.Status(http.StatusOK)
+ // As httpbin itself adds this header, we don't check it here.
+ //resp.Header("Access-Control-Allow-Origin").Empty()
+ resp.Header("Access-Control-Allow-Methods").Empty()
+ resp.Header("Access-Control-Allow-Headers").Empty()
+
+ resp = s.NewAPISIXClient().GET("/ip").WithHeader("Host",
"httpbin.org").WithHeader("Origin", "https://foo.com").Expect()
+ resp.Status(http.StatusOK)
+ // As httpbin itself adds this header, we don't check it here.
+ //resp.Header("Access-Control-Allow-Origin").Empty()
+ resp.Header("Access-Control-Allow-Methods").Empty()
+ resp.Header("Access-Control-Allow-Headers").Empty()
+ })
+
+ 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/enable-cors: "true"
+ k8s.apisix.apache.org/cors-allow-origin: https://foo.com,https://bar.com
+ k8s.apisix.apache.org/cors-allow-headers: x-foo-1,x-foo-2
+ k8s.apisix.apache.org/cors-allow-methods: GET,POST,PUT
+ name: ingress-extensions-v1beta1
+spec:
+ rules:
+ - host: httpbin.org
+ http:
+ paths:
+ - path: /ip
+ pathType: Exact
+ backend:
+ serviceName: %s
+ servicePort: %d
+`, backendSvc, backendPort[0])
+ err := s.CreateResourceFromString(ing)
+ assert.Nil(ginkgo.GinkgoT(), err, "creating ingress")
+ time.Sleep(5 * time.Second)
+
+ resp := s.NewAPISIXClient().GET("/ip").WithHeader("Host",
"httpbin.org").Expect()
+ resp.Status(http.StatusOK)
+ // As httpbin itself adds this header, we don't check it here.
+ //resp.Header("Access-Control-Allow-Origin").Empty()
+ resp.Header("Access-Control-Allow-Methods").Empty()
+ resp.Header("Access-Control-Allow-Headers").Empty()
+
+ resp = s.NewAPISIXClient().GET("/ip").WithHeader("Host",
"httpbin.org").WithHeader("Origin", "https://baz.com").Expect()
+ resp.Status(http.StatusOK)
+ // As httpbin itself adds this header, we don't check it here.
+ //resp.Header("Access-Control-Allow-Origin").Empty()
+ resp.Header("Access-Control-Allow-Methods").Empty()
+ resp.Header("Access-Control-Allow-Headers").Empty()
+
+ resp = s.NewAPISIXClient().GET("/ip").WithHeader("Host",
"httpbin.org").WithHeader("Origin", "https://foo.com").Expect()
+ resp.Status(http.StatusOK)
+
resp.Header("Access-Control-Allow-Origin").Equal("https://foo.com")
+
resp.Header("Access-Control-Allow-Methods").Equal("GET,POST,PUT")
+
resp.Header("Access-Control-Allow-Headers").Equal("x-foo-1,x-foo-2")
+ })
+
+ ginkgo.It("disable 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/enable-cors: "false"
+ k8s.apisix.apache.org/cors-allow-origin: https://foo.com,https://bar.com
+ k8s.apisix.apache.org/cors-allow-headers: x-foo-1,x-foo-2
+ k8s.apisix.apache.org/cors-allow-methods: GET,POST,PUT
+ name: ingress-extensions-v1beta1
+spec:
+ rules:
+ - host: httpbin.org
+ http:
+ paths:
+ - path: /ip
+ pathType: Exact
+ backend:
+ serviceName: %s
+ servicePort: %d
+`, backendSvc, backendPort[0])
+ err := s.CreateResourceFromString(ing)
+ assert.Nil(ginkgo.GinkgoT(), err, "creating ingress")
+ time.Sleep(5 * time.Second)
+
+ resp := s.NewAPISIXClient().GET("/ip").WithHeader("Host",
"httpbin.org").WithHeader("Origin", "https://foo.com").Expect()
+ resp.Status(http.StatusOK)
+ // As httpbin itself adds this header, we don't check it here.
+ //resp.Header("Access-Control-Allow-Origin").Empty()
+ resp.Header("Access-Control-Allow-Methods").Empty()
+ resp.Header("Access-Control-Allow-Headers").Empty()
+ })
+})
diff --git a/test/e2e/annotations/iprestriction.go
b/test/e2e/annotations/iprestriction.go
new file mode 100644
index 0000000..253b2bb
--- /dev/null
+++ b/test/e2e/annotations/iprestriction.go
@@ -0,0 +1,119 @@
+// 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"
+
+ "github.com/onsi/ginkgo"
+ "github.com/stretchr/testify/assert"
+
+ "github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+)
+
+var _ = ginkgo.Describe("allowlist-source-range 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/allowlist-source-range: "10.0.5.0/16"
+ name: ingress-v1
+spec:
+ rules:
+ - host: httpbin.org
+ http:
+ paths:
+ - path: /ip
+ pathType: Exact
+ 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)
+
+ resp := s.NewAPISIXClient().GET("/ip").WithHeader("Host",
"httpbin.org").Expect()
+ resp.Status(http.StatusForbidden)
+ })
+
+ 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/allowlist-source-range: "10.0.5.0/16"
+ name: ingress-v1beta1
+spec:
+ rules:
+ - host: httpbin.org
+ http:
+ paths:
+ - path: /ip
+ pathType: Exact
+ backend:
+ serviceName: %s
+ servicePort: %d
+`, backendSvc, backendPort[0])
+ err := s.CreateResourceFromString(ing)
+ assert.Nil(ginkgo.GinkgoT(), err, "creating ingress")
+ time.Sleep(5 * time.Second)
+
+ resp := s.NewAPISIXClient().GET("/ip").WithHeader("Host",
"httpbin.org").Expect()
+ resp.Status(http.StatusForbidden)
+ })
+
+ 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/allowlist-source-range: "10.0.5.0/16"
+ name: ingress-extensions-v1beta1
+spec:
+ rules:
+ - host: httpbin.org
+ http:
+ paths:
+ - path: /ip
+ pathType: Exact
+ backend:
+ serviceName: %s
+ servicePort: %d
+`, backendSvc, backendPort[0])
+ err := s.CreateResourceFromString(ing)
+ assert.Nil(ginkgo.GinkgoT(), err, "creating ingress")
+ time.Sleep(5 * time.Second)
+
+ resp := s.NewAPISIXClient().GET("/ip").WithHeader("Host",
"httpbin.org").Expect()
+ resp.Status(http.StatusForbidden)
+ })
+})
diff --git a/test/e2e/e2e.go b/test/e2e/e2e.go
index d2ea114..8c5c597 100644
--- a/test/e2e/e2e.go
+++ b/test/e2e/e2e.go
@@ -15,6 +15,7 @@
package e2e
import (
+ _ "github.com/apache/apisix-ingress-controller/test/e2e/annotations"
_ "github.com/apache/apisix-ingress-controller/test/e2e/endpoints"
_ "github.com/apache/apisix-ingress-controller/test/e2e/features"
_ "github.com/apache/apisix-ingress-controller/test/e2e/ingress"