diff --git a/pkg/api/validation/utils.go b/pkg/api/validation/utils.go index 84180be955..5d2145cf9a 100644 --- a/pkg/api/validation/utils.go +++ b/pkg/api/validation/utils.go @@ -99,14 +99,3 @@ func validateSchema(schemaLoader *gojsonschema.JSONLoader, obj interface{}) (boo return false, resultErr } - -func HasValueInSyncMap(m *sync.Map) bool { - hasValue := false - if m != nil { - m.Range(func(k, v interface{}) bool { - hasValue = true - return false - }) - } - return hasValue -} diff --git a/pkg/api/validation/utils_test.go b/pkg/api/validation/utils_test.go index 6e2c2d6716..2faffe574d 100644 --- a/pkg/api/validation/utils_test.go +++ b/pkg/api/validation/utils_test.go @@ -16,10 +16,8 @@ package validation import ( - "sync" "testing" - "github.com/stretchr/testify/assert" "github.com/xeipuuv/gojsonschema" v2beta3 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2beta3" @@ -48,10 +46,3 @@ func Test_validateSchema(t *testing.T) { }) } } - -func TestHasValueInSyncMap(t *testing.T) { - m := new(sync.Map) - assert.False(t, HasValueInSyncMap(m), "sync.Map should be empty") - m.Store("hello", "test") - assert.True(t, HasValueInSyncMap(m), "sync.Map should not be empty") -} diff --git a/pkg/providers/k8s/namespace/namespace_provider.go b/pkg/providers/k8s/namespace/namespace_provider.go index b4a69e9292..b791574384 100644 --- a/pkg/providers/k8s/namespace/namespace_provider.go +++ b/pkg/providers/k8s/namespace/namespace_provider.go @@ -19,17 +19,16 @@ package namespace import ( "context" + "fmt" "strings" "sync" "go.uber.org/zap" - v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" listerscorev1 "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/tools/cache" - "github.com/apache/apisix-ingress-controller/pkg/api/validation" "github.com/apache/apisix-ingress-controller/pkg/config" "github.com/apache/apisix-ingress-controller/pkg/kube" "github.com/apache/apisix-ingress-controller/pkg/log" @@ -48,44 +47,28 @@ type WatchingNamespaceProvider interface { } func NewWatchingNamespaceProvider(ctx context.Context, kube *kube.KubeClient, cfg *config.Config) (WatchingNamespaceProvider, error) { - var ( - watchingNamespaces = new(sync.Map) - watchingLabels = make(map[string]string) - ) - if len(cfg.Kubernetes.AppNamespaces) > 1 || cfg.Kubernetes.AppNamespaces[0] != v1.NamespaceAll { - for _, ns := range cfg.Kubernetes.AppNamespaces { - watchingNamespaces.Store(ns, struct{}{}) - } + c := &watchingProvider{ + kube: kube, + cfg: cfg, + + watchingNamespaces: new(sync.Map), + watchingLabels: make(map[string]string), + + enableLabelsWatching: false, + } + + if len(cfg.Kubernetes.NamespaceSelector) == 0 { + return c, nil } + // support namespace label-selector + c.enableLabelsWatching = true for _, selector := range cfg.Kubernetes.NamespaceSelector { labelSlice := strings.Split(selector, "=") - watchingLabels[labelSlice[0]] = labelSlice[1] - } - - // watchingNamespaces and watchingLabels are empty means to monitor all namespaces. - if !validation.HasValueInSyncMap(watchingNamespaces) && len(watchingLabels) == 0 { - opts := metav1.ListOptions{} - // list all namespaces - nsList, err := kube.Client.CoreV1().Namespaces().List(ctx, opts) - if err != nil { - log.Error(err.Error()) - ctx.Done() - } else { - wns := new(sync.Map) - for _, v := range nsList.Items { - wns.Store(v.Name, struct{}{}) - } - watchingNamespaces = wns + if len(labelSlice) != 2 { + return nil, fmt.Errorf("Bad namespace-selector format: %s, expected namespace-selector format: xxx=xxx", selector) } - } - - c := &watchingProvider{ - kube: kube, - cfg: cfg, - - watchingNamespaces: watchingNamespaces, - watchingLabels: watchingLabels, + c.watchingLabels[labelSlice[0]] = labelSlice[1] } kubeFactory := kube.NewSharedIndexInformerFactory() @@ -108,6 +91,8 @@ type watchingProvider struct { namespaceLister listerscorev1.NamespaceLister controller *namespaceController + + enableLabelsWatching bool } func (c *watchingProvider) Init(ctx context.Context) error { @@ -138,12 +123,14 @@ func (c *watchingProvider) initWatchingNamespacesByLabels(ctx context.Context) e } func (c *watchingProvider) Run(ctx context.Context) { - e := utils.ParallelExecutor{} + if !c.enableLabelsWatching { + return + } + e := utils.ParallelExecutor{} e.Add(func() { c.namespaceInformer.Run(ctx.Done()) }) - e.Add(func() { c.controller.run(ctx) }) @@ -153,17 +140,30 @@ func (c *watchingProvider) Run(ctx context.Context) { func (c *watchingProvider) WatchingNamespaces() []string { var keys []string - c.watchingNamespaces.Range(func(key, _ interface{}) bool { - keys = append(keys, key.(string)) - return true - }) + if c.enableLabelsWatching { + c.watchingNamespaces.Range(func(key, _ interface{}) bool { + keys = append(keys, key.(string)) + return true + }) + } else { + namespaces, err := c.kube.Client.CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{}) + if err != nil { + log.Warnw("Namespace list get failed", + zap.Error(err), + ) + return nil + } + for _, ns := range namespaces.Items { + keys = append(keys, ns.Name) + } + } return keys } // IsWatchingNamespace accepts a resource key, getting the namespace part // and checking whether the namespace is being watched. func (c *watchingProvider) IsWatchingNamespace(key string) (ok bool) { - if !validation.HasValueInSyncMap(c.watchingNamespaces) { + if !c.enableLabelsWatching { ok = true return } diff --git a/test/e2e/scaffold/ingress.go b/test/e2e/scaffold/ingress.go index 5117e33190..42ea50f1b8 100644 --- a/test/e2e/scaffold/ingress.go +++ b/test/e2e/scaffold/ingress.go @@ -428,13 +428,17 @@ func (s *Scaffold) newIngressAPISIXController() error { }) var ingressAPISIXDeployment string - label := fmt.Sprintf("apisix.ingress.watch=%s", s.namespace) + label := `""` + if labels := s.NamespaceSelectorLabelStrings(); labels != nil && !s.opts.DisableNamespaceSelector { + label = labels[0] + } + if s.opts.EnableWebhooks { ingressAPISIXDeployment = fmt.Sprintf(s.FormatRegistry(_ingressAPISIXDeploymentTemplate), s.opts.IngressAPISIXReplicas, s.namespace, s.opts.ApisixResourceSyncInterval, - s.FormatNamespaceLabel(label), s.opts.ApisixResourceVersion, s.opts.APISIXPublishAddress, _volumeMounts, _webhookCertSecret) + label, s.opts.ApisixResourceVersion, s.opts.APISIXPublishAddress, _volumeMounts, _webhookCertSecret) } else { ingressAPISIXDeployment = fmt.Sprintf(s.FormatRegistry(_ingressAPISIXDeploymentTemplate), s.opts.IngressAPISIXReplicas, s.namespace, s.opts.ApisixResourceSyncInterval, - s.FormatNamespaceLabel(label), s.opts.ApisixResourceVersion, s.opts.APISIXPublishAddress, "", _webhookCertSecret) + label, s.opts.ApisixResourceVersion, s.opts.APISIXPublishAddress, "", _webhookCertSecret) } err = k8s.KubectlApplyFromStringE(s.t, s.kubectlOptions, ingressAPISIXDeployment) @@ -538,7 +542,10 @@ func (s *Scaffold) GetIngressPodDetails() ([]corev1.Pod, error) { // ScaleIngressController scales the number of Ingress Controller pods to desired. func (s *Scaffold) ScaleIngressController(desired int) error { var ingressDeployment string - label := fmt.Sprintf("apisix.ingress.watch=%s", s.namespace) + var label string + if labels := s.NamespaceSelectorLabelStrings(); labels != nil { + label = labels[0] + } if s.opts.EnableWebhooks { ingressDeployment = fmt.Sprintf(s.FormatRegistry(_ingressAPISIXDeploymentTemplate), desired, s.namespace, s.opts.ApisixResourceSyncInterval, label, s.opts.ApisixResourceVersion, s.opts.APISIXPublishAddress, _volumeMounts, _webhookCertSecret) } else { diff --git a/test/e2e/scaffold/k8s.go b/test/e2e/scaffold/k8s.go index be3689b86e..23f16b76f2 100644 --- a/test/e2e/scaffold/k8s.go +++ b/test/e2e/scaffold/k8s.go @@ -161,16 +161,20 @@ func (s *Scaffold) CreateResourceFromStringWithNamespace(yaml, namespace string) s.kubectlOptions.Namespace = originalNamespace }() s.addFinalizers(func() { - originalNamespace := s.kubectlOptions.Namespace - s.kubectlOptions.Namespace = namespace - defer func() { - s.kubectlOptions.Namespace = originalNamespace - }() - assert.Nil(s.t, k8s.KubectlDeleteFromStringE(s.t, s.kubectlOptions, yaml)) + _ = s.DeleteResourceFromStringWithNamespace(yaml, namespace) }) return k8s.KubectlApplyFromStringE(s.t, s.kubectlOptions, yaml) } +func (s *Scaffold) DeleteResourceFromStringWithNamespace(yaml, namespace string) error { + originalNamespace := s.kubectlOptions.Namespace + s.kubectlOptions.Namespace = namespace + defer func() { + s.kubectlOptions.Namespace = originalNamespace + }() + return k8s.KubectlDeleteFromStringE(s.t, s.kubectlOptions, yaml) +} + func (s *Scaffold) ensureNumApisixCRDsCreated(url string, desired int) error { condFunc := func() (bool, error) { req, err := http.NewRequest("GET", url, nil) diff --git a/test/e2e/scaffold/scaffold.go b/test/e2e/scaffold/scaffold.go index a8b97be90d..2c2d03ca82 100644 --- a/test/e2e/scaffold/scaffold.go +++ b/test/e2e/scaffold/scaffold.go @@ -53,9 +53,12 @@ type Options struct { APISIXAdminAPIKey string EnableWebhooks bool APISIXPublishAddress string - disableNamespaceSelector bool ApisixResourceSyncInterval string ApisixResourceVersion string + + NamespaceSelectorLabel map[string]string + DisableNamespaceSelector bool + DisableNamespaceLabel bool } type Scaffold struct { @@ -96,10 +99,10 @@ var ( } createVersionedApisixResourceMap = map[string]struct{}{ - "ApisixRoute": struct{}{}, - "ApisixConsumer": struct{}{}, - "ApisixPluginConfig": struct{}{}, - "ApisixUpstream": struct{}{}, + "ApisixRoute": {}, + "ApisixConsumer": {}, + "ApisixPluginConfig": {}, + "ApisixUpstream": {}, } ) @@ -368,6 +371,19 @@ func (s *Scaffold) RestartAPISIXDeploy() { assert.NoError(s.t, err, "renew apisix tunnels") } +func (s *Scaffold) RestartIngressControllerDeploy() { + pods, err := k8s.ListPodsE(s.t, s.kubectlOptions, metav1.ListOptions{ + LabelSelector: "app=ingress-apisix-controller-deployment-e2e-test", + }) + assert.NoError(s.t, err, "list ingress-controller pod") + for _, pod := range pods { + err = s.KillPod(pod.Name) + assert.NoError(s.t, err, "killing ingress-controller pod") + } + err = s.WaitAllIngressControllerPodsAvailable() + assert.NoError(s.t, err, "waiting for new ingress-controller instance ready") +} + func (s *Scaffold) beforeEach() { var err error s.namespace = fmt.Sprintf("ingress-apisix-e2e-tests-%s-%d", s.opts.Name, time.Now().Nanosecond()) @@ -375,11 +391,19 @@ func (s *Scaffold) beforeEach() { ConfigPath: s.opts.Kubeconfig, Namespace: s.namespace, } - s.finializers = nil - labels := make(map[string]string) - labels["apisix.ingress.watch"] = s.namespace - k8s.CreateNamespaceWithMetadata(s.t, s.kubectlOptions, metav1.ObjectMeta{Name: s.namespace, Labels: labels}) + + label := map[string]string{} + if !s.opts.DisableNamespaceLabel { + if s.opts.NamespaceSelectorLabel == nil { + label["apisix.ingress.watch"] = s.namespace + s.opts.NamespaceSelectorLabel = label + } else { + label = s.opts.NamespaceSelectorLabel + } + } + + k8s.CreateNamespaceWithMetadata(s.t, s.kubectlOptions, metav1.ObjectMeta{Name: s.namespace, Labels: label}) s.nodes, err = k8s.GetReadyNodesE(s.t, s.kubectlOptions) assert.Nil(s.t, err, "querying ready nodes") @@ -541,14 +565,6 @@ func (s *Scaffold) FormatRegistry(workloadTemplate string) string { } } -// FormatNamespaceLabel set label to be empty if s.opts.disableNamespaceSelector is true. -func (s *Scaffold) FormatNamespaceLabel(label string) string { - if s.opts.disableNamespaceSelector { - return "\"\"" - } - return label -} - var ( versionRegex = regexp.MustCompile(`apiVersion: apisix.apache.org/v.*?\n`) kindRegex = regexp.MustCompile(`kind: (.*?)\n`) @@ -566,10 +582,6 @@ func (s *Scaffold) getKindValue(yml string) string { return subStr[1] } -func (s *Scaffold) DisableNamespaceSelector() { - s.opts.disableNamespaceSelector = true -} - func waitExponentialBackoff(condFunc func() (bool, error)) error { backoff := wait.Backoff{ Duration: 500 * time.Millisecond, @@ -614,3 +626,15 @@ func (s *Scaffold) CreateVersionedApisixResourceWithNamespace(yml, namespace str func ApisixResourceVersion() *apisixResourceVersionInfo { return apisixResourceVersion } + +func (s *Scaffold) NamespaceSelectorLabelStrings() []string { + var labels []string + for k, v := range s.opts.NamespaceSelectorLabel { + labels = append(labels, fmt.Sprintf("%s=%s", k, v)) + } + return labels +} + +func (s *Scaffold) NamespaceSelectorLabel() map[string]string { + return s.opts.NamespaceSelectorLabel +} diff --git a/test/e2e/suite-ingress/suite-ingress-features/namespace.go b/test/e2e/suite-ingress/suite-ingress-features/namespace.go index d8e5fd74af..a41b6fb4e3 100644 --- a/test/e2e/suite-ingress/suite-ingress-features/namespace.go +++ b/test/e2e/suite-ingress/suite-ingress-features/namespace.go @@ -16,6 +16,7 @@ package ingress import ( + "context" "encoding/json" "fmt" "net/http" @@ -24,6 +25,8 @@ import ( "github.com/gruntwork-io/terratest/modules/k8s" ginkgo "github.com/onsi/ginkgo/v2" "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/apache/apisix-ingress-controller/test/e2e/scaffold" ) @@ -37,12 +40,37 @@ type headers struct { } var _ = ginkgo.Describe("suite-ingress-features: namespacing filtering enable", func() { - s := scaffold.NewDefaultScaffold() + s := scaffold.NewScaffold(&scaffold.Options{ + Name: "enable-namespace-selector", + IngressAPISIXReplicas: 1, + ApisixResourceVersion: scaffold.ApisixResourceVersion().Default, + NamespaceSelectorLabel: map[string]string{ + fmt.Sprintf("namespace-selector-%d", time.Now().Nanosecond()): "watch", + }, + DisableNamespaceLabel: true, + }) ginkgo.Context("with namespace_selector", func() { + namespace1 := fmt.Sprintf("namespace-selector-1-%d", time.Now().Nanosecond()) + namespace2 := fmt.Sprintf("namespace-selector-2-%d", time.Now().Nanosecond()) + + createNamespaceLabel := func(namespace string) { + k8s.CreateNamespaceWithMetadata(ginkgo.GinkgoT(), &k8s.KubectlOptions{ConfigPath: scaffold.GetKubeconfig()}, metav1.ObjectMeta{Name: namespace, Labels: s.NamespaceSelectorLabel()}) + _, err := s.NewHTTPBINWithNamespace(namespace) + time.Sleep(6 * time.Second) + assert.Nil(ginkgo.GinkgoT(), err, "create second httpbin service") + } + + deleteNamespace := func(namespace string) { + _ = k8s.DeleteNamespaceE(ginkgo.GinkgoT(), &k8s.KubectlOptions{ConfigPath: scaffold.GetKubeconfig()}, namespace) + } + ginkgo.It("resources in other namespaces should be ignored", func() { + createNamespaceLabel(namespace1) + defer deleteNamespace(namespace1) + backendSvc, backendSvcPort := s.DefaultHTTPBackend() - route := fmt.Sprintf(` + route1 := fmt.Sprintf(` apiVersion: networking.k8s.io/v1 kind: Ingress metadata: @@ -61,17 +89,16 @@ spec: port: number: %d `, backendSvc, backendSvcPort[0]) - - assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(route), "creating ingress") - time.Sleep(6 * time.Second) - + assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromStringWithNamespace(route1, namespace1), "creating ingress") + assert.Nil(ginkgo.GinkgoT(), s.EnsureNumApisixRoutesCreated(1)) + time.Sleep(time.Second * 6) body := s.NewAPISIXClient().GET("/ip").WithHeader("Host", "httpbin.com").Expect().Status(http.StatusOK).Body().Raw() var placeholder ip err := json.Unmarshal([]byte(body), &placeholder) assert.Nil(ginkgo.GinkgoT(), err, "unmarshalling IP") // Now create another ingress in default namespace. - route = fmt.Sprintf(` + route2 := fmt.Sprintf(` apiVersion: networking.k8s.io/v1 kind: Ingress metadata: @@ -79,7 +106,33 @@ metadata: spec: ingressClassName: apisix rules: - - host: httpbin.com + - host: httpbin.org + http: + paths: + - path: /headers + pathType: Exact + backend: + service: + name: %s + port: + number: %d +`, backendSvc, backendSvcPort[0]) + assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromStringWithNamespace(route2, "default"), "creating ingress") + time.Sleep(6 * time.Second) + routes, err := s.ListApisixRoutes() + assert.Nil(ginkgo.GinkgoT(), err) + assert.Len(ginkgo.GinkgoT(), routes, 1) + _ = s.NewAPISIXClient().GET("/headers").WithHeader("Host", "httpbin.org").Expect().Status(http.StatusNotFound) + + route3 := fmt.Sprintf(` +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: httpbin-route +spec: + ingressClassName: apisix + rules: + - host: local.httpbin.org http: paths: - path: /headers @@ -90,18 +143,48 @@ spec: port: number: %d `, backendSvc, backendSvcPort[0]) + assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(route3), "creating ingress") + time.Sleep(6 * time.Second) + routes, err = s.ListApisixRoutes() + assert.Nil(ginkgo.GinkgoT(), err) + assert.Len(ginkgo.GinkgoT(), routes, 1) + _ = s.NewAPISIXClient().GET("/headers").WithHeader("Host", "local.httpbin.org").Expect().Status(http.StatusNotFound) + + // remove route1 + assert.Nil(ginkgo.GinkgoT(), s.DeleteResourceFromStringWithNamespace(route1, namespace1), "delete ingress") + time.Sleep(6 * time.Second) + + deleteNamespace(namespace1) + time.Sleep(6 * time.Second) + routes, err = s.ListApisixRoutes() + assert.Nil(ginkgo.GinkgoT(), err) + assert.Len(ginkgo.GinkgoT(), routes, 0) + + // restart ingress-controller + s.RestartIngressControllerDeploy() + time.Sleep(6 * time.Second) + assert.Nil(ginkgo.GinkgoT(), err) + assert.Len(ginkgo.GinkgoT(), routes, 0) - assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromStringWithNamespace(route, "default"), "creating ingress") - _ = s.NewAPISIXClient().GET("/headers").WithHeader("Host", "httpbin.com").Expect().Status(http.StatusNotFound) + createNamespaceLabel(namespace2) + defer deleteNamespace(namespace2) + assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromStringWithNamespace(route1, namespace2), "creating ingress") + assert.Nil(ginkgo.GinkgoT(), s.EnsureNumApisixRoutesCreated(1)) + _ = s.NewAPISIXClient().GET("/ip").WithHeader("Host", "httpbin.com").Expect().Status(http.StatusOK) + _ = s.NewAPISIXClient().GET("/headers").WithHeader("Host", "httpbin.org").Expect().Status(http.StatusNotFound) + _ = s.NewAPISIXClient().GET("/headers").WithHeader("Host", "local.httpbin.org").Expect().Status(http.StatusNotFound) }) }) }) var _ = ginkgo.Describe("suite-ingress-features: namespacing filtering disable", func() { - s := scaffold.NewDefaultScaffold() + s := scaffold.NewScaffold(&scaffold.Options{ + Name: "disable-namespace-selector", + IngressAPISIXReplicas: 1, + ApisixResourceVersion: scaffold.ApisixResourceVersion().Default, + DisableNamespaceSelector: true, + }) ginkgo.Context("without namespace_selector", func() { - // make namespace_selector empty - s.DisableNamespaceSelector() namespace := "second-httpbin-service-namespace" // create another http-bin service in a new namespace. @@ -110,13 +193,12 @@ var _ = ginkgo.Describe("suite-ingress-features: namespacing filtering disable", ConfigPath: scaffold.GetKubeconfig(), }, namespace) _, err := s.NewHTTPBINWithNamespace(namespace) - assert.Nil(ginkgo.GinkgoT(), err, "create second httpbin service") + assert.Nil(ginkgo.GinkgoT(), err, "create new httpbin service") }) // clean this tmp namespace when test case is done. ginkgo.AfterEach(func() { - err := k8s.DeleteNamespaceE(ginkgo.GinkgoT(), &k8s.KubectlOptions{ - ConfigPath: scaffold.GetKubeconfig()}, namespace) + err := k8s.DeleteNamespaceE(ginkgo.GinkgoT(), &k8s.KubectlOptions{ConfigPath: scaffold.GetKubeconfig()}, namespace) assert.Nilf(ginkgo.GinkgoT(), err, "deleting namespace %s", namespace) }) @@ -189,3 +271,134 @@ spec: }) }) }) + +var _ = ginkgo.Describe("suite-ingress-features: namespacing un-label", func() { + labelName, labelValue := fmt.Sprintf("namespace-selector-%d", time.Now().Nanosecond()), "watch" + s := scaffold.NewScaffold(&scaffold.Options{ + Name: "un-label", + IngressAPISIXReplicas: 1, + ApisixResourceVersion: scaffold.ApisixResourceVersion().Default, + NamespaceSelectorLabel: map[string]string{ + labelName: labelValue, + }, + DisableNamespaceLabel: true, + }) + namespace1 := fmt.Sprintf("un-label-%d", time.Now().Nanosecond()) + + ginkgo.It("un-label", func() { + client := s.GetKubernetesClient() + + ns := fmt.Sprintf(` +apiVersion: v1 +kind: Namespace +metadata: + name: %s + labels: + %s: %s +`, namespace1, labelName, labelValue) + assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromStringWithNamespace(ns, namespace1), "creating namespace") + //defer s.DeleteResourceFromStringWithNamespace(ns, namespace1) + _, err := s.NewHTTPBINWithNamespace(namespace1) + assert.Nil(ginkgo.GinkgoT(), err, "create httpbin service in", namespace1) + + backendSvc, backendSvcPort := s.DefaultHTTPBackend() + route1 := fmt.Sprintf(` +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: httpbin-route +spec: + ingressClassName: apisix + rules: + - host: httpbin.com + http: + paths: + - path: /ip + pathType: Exact + backend: + service: + name: %s + port: + number: %d +`, backendSvc, backendSvcPort[0]) + assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromStringWithNamespace(route1, namespace1), "creating ingress") + assert.Nil(ginkgo.GinkgoT(), s.EnsureNumApisixRoutesCreated(1)) + time.Sleep(time.Second * 6) + _ = s.NewAPISIXClient().GET("/ip").WithHeader("Host", "httpbin.com").Expect().Status(http.StatusOK).Body().Raw() + + assert.Nil(ginkgo.GinkgoT(), s.DeleteResourceFromStringWithNamespace(route1, namespace1), "deleting ingress") + // un-label + _, err = client.CoreV1().Namespaces().Update( + context.Background(), + &v1.Namespace{ObjectMeta: metav1.ObjectMeta{ + Name: namespace1, + Labels: map[string]string{}, + }}, + metav1.UpdateOptions{}, + ) + assert.Nil(ginkgo.GinkgoT(), err, "unlabel the namespace") + time.Sleep(6 * time.Second) + routes, err := s.ListApisixRoutes() + assert.Nil(ginkgo.GinkgoT(), err) + assert.Len(ginkgo.GinkgoT(), routes, 0) + + route2 := fmt.Sprintf(` +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: httpbin-route +spec: + ingressClassName: apisix + rules: + - host: httpbin.org + http: + paths: + - path: /headers + pathType: Exact + backend: + service: + name: %s + port: + number: %d +`, backendSvc, backendSvcPort[0]) + assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromString(route2), "creating ingress") + time.Sleep(6 * time.Second) + routes, err = s.ListApisixRoutes() + assert.Nil(ginkgo.GinkgoT(), err) + assert.Len(ginkgo.GinkgoT(), routes, 0) + _ = s.NewAPISIXClient().GET("/headers").WithHeader("Host", "httpbin.org").Expect().Status(http.StatusNotFound) + + route3 := fmt.Sprintf(` +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: httpbin-route +spec: + ingressClassName: apisix + rules: + - host: local.httpbin.org + http: + paths: + - path: /headers + pathType: Exact + backend: + service: + name: %s + port: + number: %d +`, backendSvc, backendSvcPort[0]) + assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromStringWithNamespace(route3, "default"), "creating ingress") + time.Sleep(6 * time.Second) + routes, err = s.ListApisixRoutes() + assert.Nil(ginkgo.GinkgoT(), err) + assert.Len(ginkgo.GinkgoT(), routes, 0) + _ = s.NewAPISIXClient().GET("/headers").WithHeader("Host", "local.httpbin.org").Expect().Status(http.StatusNotFound) + + assert.Nil(ginkgo.GinkgoT(), s.CreateResourceFromStringWithNamespace(route1, namespace1), "creating ingress") + time.Sleep(time.Second * 6) + routes, err = s.ListApisixRoutes() + assert.Nil(ginkgo.GinkgoT(), err) + assert.Len(ginkgo.GinkgoT(), routes, 0) + _ = s.NewAPISIXClient().GET("/ip").WithHeader("Host", "httpbin.com").Expect().Status(http.StatusNotFound) + }) +})