Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: make ingress controller compatible with ingress.extensions/v1beta1 #315

Merged
merged 3 commits into from Mar 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 8 additions & 1 deletion cmd/ingress/ingress.go
Expand Up @@ -77,6 +77,13 @@ For Kubernetes cluster version older than v1.19.0, you should always set the --i
--kubeconfig /path/to/kubeconfig \
--ingress-version networking/v1beta1

If your Kubernetes cluster version is prior to v1.14+, only ingress.extensions/v1beta1 can be used.

apisix-ingress-controller ingress \
--apisix-base-url http://apisix-service:9180/apisix/admin \
--kubeconfig /path/to/kubeconfig \
--ingress-version extensions/v1beta1

If you run apisix-ingress-controller outside the Kubernetes cluster, --kubeconfig option (or kubeconfig item in configuration file) should be specified explicitly,
or if you run it inside cluster, leave it alone and in-cluster configuration will be discovered and used.

Expand Down Expand Up @@ -138,7 +145,7 @@ the apisix cluster and others are created`,
cmd.PersistentFlags().StringSliceVar(&cfg.Kubernetes.AppNamespaces, "app-namespace", []string{config.NamespaceAll}, "namespaces that controller will watch for resources")
cmd.PersistentFlags().StringVar(&cfg.Kubernetes.IngressClass, "ingress-class", config.IngressClass, "the class of an Ingress object is set using the field IngressClassName in Kubernetes clusters version v1.18.0 or higher or the annotation \"kubernetes.io/ingress.class\" (deprecated)")
cmd.PersistentFlags().StringVar(&cfg.Kubernetes.ElectionID, "election-id", config.IngressAPISIXLeader, "election id used for campaign the controller leader")
cmd.PersistentFlags().StringVar(&cfg.Kubernetes.IngressVersion, "ingress-version", config.IngressNetworkingV1, "the supported ingress api group version, can be \"networking/v1beta1\" or \"networking/v1\" (for Kubernetes version v1.19.0 or higher)")
cmd.PersistentFlags().StringVar(&cfg.Kubernetes.IngressVersion, "ingress-version", config.IngressNetworkingV1, "the supported ingress api group version, can be \"networking/v1beta1\", \"networking/v1\" (for Kubernetes version v1.19.0 or higher) and \"extensions/v1beta1\"")
cmd.PersistentFlags().StringVar(&cfg.Kubernetes.ApisixRouteVersion, "apisix-route-version", config.ApisixRouteV2alpha1, "the supported apisixroute api group version, can be \"apisix.apache.org/v1\" or \"apisix.apache.org/v2alpha1\"")
cmd.PersistentFlags().StringVar(&cfg.APISIX.BaseURL, "apisix-base-url", "", "the base URL for APISIX admin api / manager api")
cmd.PersistentFlags().StringVar(&cfg.APISIX.AdminKey, "apisix-admin-key", "", "admin key used for the authorization of APISIX admin api / manager api")
Expand Down
4 changes: 2 additions & 2 deletions conf/config-default.yaml
Expand Up @@ -48,8 +48,8 @@ kubernetes:
# or higher or the annotation "kubernetes.io/ingress.class"
# (deprecated).
ingress_version: "networking/v1" # the supported ingress api group version, can be "networking/v1beta1"
# or "networking/v1" (for Kubernetes version v1.19.0 or higher), default
# is "networking/v1".
# , "networking/v1" (for Kubernetes version v1.19.0 or higher), and
# "extensions/v1beta1", default is "networking/v1".

apisix_route_version: "apisix.apache.org/v2alpha1" # the supported apisixroute api group version, can be
# "apisix.apache.org/v1" or "apisix.apache.org/v2alpha1",
Expand Down
9 changes: 8 additions & 1 deletion pkg/config/config.go
Expand Up @@ -42,6 +42,10 @@ const (
IngressNetworkingV1 = "networking/v1"
// IngressNetworkingV1beta1 represents ingress.networking/v1beta1
IngressNetworkingV1beta1 = "networking/v1beta1"
// IngressExtensionsV1beta1 represents ingress.extensions/v1beta1
// WARNING: ingress.extensions/v1beta1 is deprecated in v1.14+, and will be unavilable
// in v1.22.
IngressExtensionsV1beta1 = "extensions/v1beta1"
// ApisixRouteV1 represents apisixroute.apisix.apache.org/v1
ApisixRouteV1 = "apisix.apache.org/v1"
// ApisixRouteV2alpha1 represents apisixroute.apisix.apache.org/v2alpha1
Expand Down Expand Up @@ -129,7 +133,10 @@ func (cfg *Config) Validate() error {
if cfg.APISIX.BaseURL == "" {
return errors.New("apisix base url is required")
}
if cfg.Kubernetes.IngressVersion != IngressNetworkingV1 && cfg.Kubernetes.IngressVersion != IngressNetworkingV1beta1 {
switch cfg.Kubernetes.IngressVersion {
case IngressNetworkingV1, IngressNetworkingV1beta1, IngressExtensionsV1beta1:
break
default:
return errors.New("unsupported ingress version")
}
cfg.Kubernetes.AppNamespaces = purifyAppNamespaces(cfg.Kubernetes.AppNamespaces)
Expand Down
11 changes: 8 additions & 3 deletions pkg/ingress/controller/controller.go
Expand Up @@ -129,15 +129,20 @@ func NewController(cfg *config.Config) (*Controller, error) {
}
kube.EndpointsInformer = kube.CoreSharedInformerFactory.Core().V1().Endpoints()

ingressLister := kube.NewIngressLister(kube.CoreSharedInformerFactory.Networking().V1().Ingresses().Lister(),
kube.CoreSharedInformerFactory.Networking().V1beta1().Ingresses().Lister())
ingressLister := kube.NewIngressLister(
kube.CoreSharedInformerFactory.Networking().V1().Ingresses().Lister(),
kube.CoreSharedInformerFactory.Networking().V1beta1().Ingresses().Lister(),
kube.CoreSharedInformerFactory.Extensions().V1beta1().Ingresses().Lister(),
)
apisixRouteLister := kube.NewApisixRouteLister(sharedInformerFactory.Apisix().V1().ApisixRoutes().Lister(),
sharedInformerFactory.Apisix().V2alpha1().ApisixRoutes().Lister())

if cfg.Kubernetes.IngressVersion == config.IngressNetworkingV1 {
ingressInformer = kube.CoreSharedInformerFactory.Networking().V1().Ingresses().Informer()
} else {
} else if cfg.Kubernetes.IngressVersion == config.IngressNetworkingV1beta1 {
ingressInformer = kube.CoreSharedInformerFactory.Networking().V1beta1().Ingresses().Informer()
} else {
ingressInformer = kube.CoreSharedInformerFactory.Extensions().V1beta1().Ingresses().Informer()
}
if cfg.Kubernetes.ApisixRouteVersion == config.ApisixRouteV2alpha1 {
apisixRouteInformer = sharedInformerFactory.Apisix().V2alpha1().ApisixRoutes().Informer()
Expand Down
27 changes: 19 additions & 8 deletions pkg/ingress/controller/ingress.go
Expand Up @@ -319,16 +319,27 @@ func (c *ingressController) OnDelete(obj interface{}) {
}

func (c *ingressController) isIngressEffective(ing kube.Ingress) bool {
var ingressClass string
var (
ic *string
ica string
)
if ing.GroupVersion() == kube.IngressV1 {
ic, ok := ing.V1().GetAnnotations()[_ingressKey]
if !ok && ing.V1().Spec.IngressClassName != nil {
ic = *ing.V1().Spec.IngressClassName
}
ingressClass = ic
ic = ing.V1().Spec.IngressClassName
ica = ing.V1().GetAnnotations()[_ingressKey]
} else if ing.GroupVersion() == kube.IngressV1beta1 {
ic = ing.V1beta1().Spec.IngressClassName
ica = ing.V1beta1().GetAnnotations()[_ingressKey]
} else {
ingressClass = ing.V1beta1().GetAnnotations()[_ingressKey]
ic = ing.ExtensionsV1beta1().Spec.IngressClassName
ica = ing.ExtensionsV1beta1().GetAnnotations()[_ingressKey]
}

return ingressClass == config.IngressClass
// kubernetes.io/ingress.class takes the precedence.
if ica != "" {
return ica == config.IngressClass
}
if ic != nil {
return *ic == config.IngressClass
}
return false
}
166 changes: 166 additions & 0 deletions pkg/ingress/controller/ingress_test.go
@@ -0,0 +1,166 @@
// 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 controller

import (
"testing"

"github.com/stretchr/testify/assert"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
networkingv1 "k8s.io/api/networking/v1"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/apache/apisix-ingress-controller/pkg/kube"
)

func TestIsIngressEffective(t *testing.T) {
c := &ingressController{}
cn := "ingress"
ingV1 := &networkingv1.Ingress{
TypeMeta: metav1.TypeMeta{
Kind: "ingress",
APIVersion: "networking/v1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "v1-ing",
Annotations: map[string]string{
_ingressKey: "apisix",
},
},
Spec: networkingv1.IngressSpec{
IngressClassName: &cn,
},
}
ing, err := kube.NewIngress(ingV1)
assert.Nil(t, err)
// Annotations takes precedence.
assert.Equal(t, c.isIngressEffective(ing), true)

ingV1 = &networkingv1.Ingress{
TypeMeta: metav1.TypeMeta{
Kind: "ingress",
APIVersion: "networking/v1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "v1-ing",
},
Spec: networkingv1.IngressSpec{
IngressClassName: &cn,
},
}
ing, err = kube.NewIngress(ingV1)
assert.Nil(t, err)
// Spec.IngressClassName takes the precedence.
assert.Equal(t, c.isIngressEffective(ing), false)

ingV1beta1 := &networkingv1beta1.Ingress{
TypeMeta: metav1.TypeMeta{
Kind: "ingress",
APIVersion: "networking/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "v1beta1-ing",
Annotations: map[string]string{
_ingressKey: "apisix",
},
},
Spec: networkingv1beta1.IngressSpec{
IngressClassName: &cn,
},
}
ing, err = kube.NewIngress(ingV1beta1)
assert.Nil(t, err)
// Annotations takes precedence.
assert.Equal(t, c.isIngressEffective(ing), true)

ingV1beta1 = &networkingv1beta1.Ingress{
TypeMeta: metav1.TypeMeta{
Kind: "ingress",
APIVersion: "networking/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "v1beta1-ing",
},
Spec: networkingv1beta1.IngressSpec{
IngressClassName: &cn,
},
}
ing, err = kube.NewIngress(ingV1beta1)
assert.Nil(t, err)
// Spec.IngressClassName takes the precedence.
assert.Equal(t, c.isIngressEffective(ing), false)

ingV1 = &networkingv1.Ingress{
TypeMeta: metav1.TypeMeta{
Kind: "ingress",
APIVersion: "networking/v1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "v1-ing",
},
Spec: networkingv1.IngressSpec{
IngressClassName: &cn,
},
}
ing, err = kube.NewIngress(ingV1)
assert.Nil(t, err)
// Spec.IngressClassName takes the precedence.
assert.Equal(t, c.isIngressEffective(ing), false)

ingExtV1beta1 := &extensionsv1beta1.Ingress{
TypeMeta: metav1.TypeMeta{
Kind: "ingress",
APIVersion: "extensions/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "v1extbeta1-ing",
Annotations: map[string]string{
_ingressKey: "apisix",
},
},
Spec: extensionsv1beta1.IngressSpec{
IngressClassName: &cn,
},
}
ing, err = kube.NewIngress(ingExtV1beta1)
assert.Nil(t, err)
// Annotations takes precedence.
assert.Equal(t, c.isIngressEffective(ing), true)

ingExtV1beta1 = &extensionsv1beta1.Ingress{
TypeMeta: metav1.TypeMeta{
Kind: "ingress",
APIVersion: "extensions/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "v1extbeta1-ing",
},
Spec: extensionsv1beta1.IngressSpec{
IngressClassName: &cn,
},
}
ing, err = kube.NewIngress(ingExtV1beta1)
assert.Nil(t, err)
// Spec.IngressClassName takes the precedence.
assert.Equal(t, c.isIngressEffective(ing), false)
}