-
Notifications
You must be signed in to change notification settings - Fork 4.8k
CONSOLE-5209: Add e2e tests for IngressComponentRouteLabels feature gate #31385
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,252 @@ | ||
| package console | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "strings" | ||
| "time" | ||
|
|
||
| g "github.com/onsi/ginkgo/v2" | ||
| o "github.com/onsi/gomega" | ||
|
|
||
| configv1 "github.com/openshift/api/config/v1" | ||
| routev1 "github.com/openshift/api/route/v1" | ||
| configclient "github.com/openshift/client-go/config/clientset/versioned" | ||
| routeclient "github.com/openshift/client-go/route/clientset/versioned" | ||
| apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| "k8s.io/apimachinery/pkg/util/wait" | ||
| "k8s.io/client-go/util/retry" | ||
| e2e "k8s.io/kubernetes/test/e2e/framework" | ||
| ) | ||
|
|
||
| const ( | ||
| consoleNamespace = "openshift-console" | ||
| additionalRouteLabel = "console.openshift.io/additional-route" | ||
| pollTimeout = 60 * time.Second | ||
| pollInterval = 2 * time.Second | ||
| ) | ||
|
|
||
| var _ = g.Describe("[sig-console][OCPFeatureGate:IngressComponentRouteLabels] Console operator route label propagation", func() { | ||
| defer g.GinkgoRecover() | ||
|
|
||
| var ( | ||
| configClient configclient.Interface | ||
| routeV1 routeclient.Interface | ||
| domain string | ||
| ) | ||
|
|
||
| g.BeforeEach(func() { | ||
| kubeconfig, err := e2e.LoadConfig() | ||
| o.Expect(err).NotTo(o.HaveOccurred()) | ||
|
|
||
| configClient, err = configclient.NewForConfig(kubeconfig) | ||
| o.Expect(err).NotTo(o.HaveOccurred()) | ||
|
|
||
| routeV1, err = routeclient.NewForConfig(kubeconfig) | ||
| o.Expect(err).NotTo(o.HaveOccurred()) | ||
|
|
||
| ingress, err := configClient.ConfigV1().Ingresses().Get(context.TODO(), "cluster", metav1.GetOptions{}) | ||
| o.Expect(err).NotTo(o.HaveOccurred()) | ||
| domain = ingress.Spec.Domain | ||
| }) | ||
|
|
||
| g.AfterEach(func() { | ||
| removeAllTestComponentRoutes(configClient) | ||
| }) | ||
|
|
||
| g.It("should propagate labels from componentRoute spec to the route object [Serial]", func() { | ||
| name := "console-label-test-1" | ||
| hostname := fmt.Sprintf("%s-%s.%s", name, consoleNamespace, domain) | ||
| labels := map[string]configv1.LabelValue{"ingress": "shard-test", "env": "ci"} | ||
|
|
||
| addComponentRouteWithLabels(configClient, name, hostname, labels) | ||
| route := waitForRouteWithLabels(routeV1, name, hostname, labels) | ||
|
|
||
| o.Expect(route.Labels[additionalRouteLabel]).To(o.Equal("true")) | ||
| o.Expect(route.Labels["app"]).To(o.Equal("console")) | ||
| }) | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| g.It("should update route labels when componentRoute labels change [Serial]", func() { | ||
| name := "console-label-test-2" | ||
| hostname := fmt.Sprintf("%s-%s.%s", name, consoleNamespace, domain) | ||
| labels := map[string]configv1.LabelValue{"ingress": "shard-test", "env": "ci"} | ||
|
|
||
| addComponentRouteWithLabels(configClient, name, hostname, labels) | ||
| waitForRouteWithLabels(routeV1, name, hostname, labels) | ||
|
|
||
| updatedLabels := map[string]configv1.LabelValue{"ingress": "shard-updated", "tier": "frontend"} | ||
| updateComponentRouteLabels(configClient, name, updatedLabels) | ||
|
|
||
| err := wait.Poll(pollInterval, pollTimeout, func() (bool, error) { | ||
| route, err := routeV1.RouteV1().Routes(consoleNamespace).Get(context.TODO(), name, metav1.GetOptions{}) | ||
| if err != nil { | ||
| return false, nil | ||
| } | ||
| return route.Labels["ingress"] == "shard-updated" && route.Labels["tier"] == "frontend", nil | ||
| }) | ||
| o.Expect(err).NotTo(o.HaveOccurred(), "route labels should be updated") | ||
| }) | ||
|
|
||
| g.It("should remove stale labels from route when removed from componentRoute spec [Serial]", func() { | ||
| name := "console-label-test-3" | ||
| hostname := fmt.Sprintf("%s-%s.%s", name, consoleNamespace, domain) | ||
| labels := map[string]configv1.LabelValue{"ingress": "shard-test", "env": "ci"} | ||
|
|
||
| addComponentRouteWithLabels(configClient, name, hostname, labels) | ||
| waitForRouteWithLabels(routeV1, name, hostname, labels) | ||
|
|
||
| updateComponentRouteLabels(configClient, name, map[string]configv1.LabelValue{"ingress": "shard-test"}) | ||
|
|
||
| err := wait.Poll(pollInterval, pollTimeout, func() (bool, error) { | ||
| route, err := routeV1.RouteV1().Routes(consoleNamespace).Get(context.TODO(), name, metav1.GetOptions{}) | ||
| if err != nil { | ||
| return false, nil | ||
| } | ||
| _, hasEnv := route.Labels["env"] | ||
| return !hasEnv && route.Labels["ingress"] == "shard-test", nil | ||
| }) | ||
| o.Expect(err).NotTo(o.HaveOccurred(), "stale label 'env' should be removed") | ||
| }) | ||
|
|
||
| g.It("should preserve operator-managed labels when user labels are applied [Serial]", func() { | ||
| name := "console-label-test-4" | ||
| hostname := fmt.Sprintf("%s-%s.%s", name, consoleNamespace, domain) | ||
| labels := map[string]configv1.LabelValue{"custom-key": "custom-value"} | ||
|
|
||
| addComponentRouteWithLabels(configClient, name, hostname, labels) | ||
| route := waitForRouteWithLabels(routeV1, name, hostname, labels) | ||
|
|
||
| o.Expect(route.Labels[additionalRouteLabel]).To(o.Equal("true")) | ||
| o.Expect(route.Labels["app"]).To(o.Equal("console")) | ||
| }) | ||
|
|
||
| g.It("should clean up labeled route when componentRoute is removed [Serial]", func() { | ||
| name := "console-label-test-5" | ||
| hostname := fmt.Sprintf("%s-%s.%s", name, consoleNamespace, domain) | ||
| labels := map[string]configv1.LabelValue{"ingress": "shard-test"} | ||
|
|
||
| addComponentRouteWithLabels(configClient, name, hostname, labels) | ||
| waitForRouteWithLabels(routeV1, name, hostname, labels) | ||
|
|
||
| removeComponentRoute(configClient, name) | ||
|
|
||
| err := wait.Poll(pollInterval, pollTimeout, func() (bool, error) { | ||
| _, err := routeV1.RouteV1().Routes(consoleNamespace).Get(context.TODO(), name, metav1.GetOptions{}) | ||
| if apierrors.IsNotFound(err) { | ||
| return true, nil | ||
| } | ||
| if err != nil { | ||
| return false, nil | ||
| } | ||
| return false, nil | ||
| }) | ||
| o.Expect(err).NotTo(o.HaveOccurred(), "route should be garbage collected") | ||
| }) | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| }) | ||
|
|
||
| func addComponentRouteWithLabels(client configclient.Interface, name, hostname string, labels map[string]configv1.LabelValue) { | ||
| err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { | ||
| ingress, err := client.ConfigV1().Ingresses().Get(context.TODO(), "cluster", metav1.GetOptions{}) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| ingress.Spec.ComponentRoutes = append(ingress.Spec.ComponentRoutes, configv1.ComponentRouteSpec{ | ||
| Namespace: consoleNamespace, | ||
| Name: name, | ||
| Hostname: configv1.Hostname(hostname), | ||
| Labels: labels, | ||
| }) | ||
| _, err = client.ConfigV1().Ingresses().Update(context.TODO(), ingress, metav1.UpdateOptions{}) | ||
| return err | ||
| }) | ||
| o.Expect(err).NotTo(o.HaveOccurred(), "failed to add componentRoute %s", name) | ||
| } | ||
|
|
||
| func updateComponentRouteLabels(client configclient.Interface, name string, labels map[string]configv1.LabelValue) { | ||
| err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { | ||
| ingress, err := client.ConfigV1().Ingresses().Get(context.TODO(), "cluster", metav1.GetOptions{}) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| for i, cr := range ingress.Spec.ComponentRoutes { | ||
| if string(cr.Name) == name { | ||
| ingress.Spec.ComponentRoutes[i].Labels = labels | ||
| break | ||
| } | ||
| } | ||
| _, err = client.ConfigV1().Ingresses().Update(context.TODO(), ingress, metav1.UpdateOptions{}) | ||
| return err | ||
| }) | ||
| o.Expect(err).NotTo(o.HaveOccurred(), "failed to update labels on componentRoute %s", name) | ||
| } | ||
|
|
||
| func removeComponentRoute(client configclient.Interface, name string) { | ||
| err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { | ||
| ingress, err := client.ConfigV1().Ingresses().Get(context.TODO(), "cluster", metav1.GetOptions{}) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| var filtered []configv1.ComponentRouteSpec | ||
| for _, cr := range ingress.Spec.ComponentRoutes { | ||
| if string(cr.Name) != name { | ||
| filtered = append(filtered, cr) | ||
| } | ||
| } | ||
| ingress.Spec.ComponentRoutes = filtered | ||
| _, err = client.ConfigV1().Ingresses().Update(context.TODO(), ingress, metav1.UpdateOptions{}) | ||
| return err | ||
| }) | ||
| o.Expect(err).NotTo(o.HaveOccurred(), "failed to remove componentRoute %s", name) | ||
| } | ||
|
|
||
| func removeAllTestComponentRoutes(client configclient.Interface) { | ||
| err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { | ||
| ingress, err := client.ConfigV1().Ingresses().Get(context.TODO(), "cluster", metav1.GetOptions{}) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| var filtered []configv1.ComponentRouteSpec | ||
| for _, cr := range ingress.Spec.ComponentRoutes { | ||
| if !strings.HasPrefix(string(cr.Name), "console-label-test-") { | ||
| filtered = append(filtered, cr) | ||
| } | ||
| } | ||
| if len(filtered) == len(ingress.Spec.ComponentRoutes) { | ||
| return nil | ||
| } | ||
| ingress.Spec.ComponentRoutes = filtered | ||
| _, err = client.ConfigV1().Ingresses().Update(context.TODO(), ingress, metav1.UpdateOptions{}) | ||
| return err | ||
| }) | ||
| if err != nil { | ||
| e2e.Logf("warning: failed to clean up test componentRoutes: %v", err) | ||
| } | ||
| } | ||
|
|
||
| // waitForRouteWithLabels polls until a route exists with the expected hostname, | ||
| // the operator-managed additional-route label, and all expected user labels. | ||
| func waitForRouteWithLabels(client routeclient.Interface, name, hostname string, expectedLabels map[string]configv1.LabelValue) *routev1.Route { | ||
| var route *routev1.Route | ||
| err := wait.Poll(pollInterval, pollTimeout, func() (bool, error) { | ||
| var err error | ||
| route, err = client.RouteV1().Routes(consoleNamespace).Get(context.TODO(), name, metav1.GetOptions{}) | ||
| if err != nil { | ||
| return false, nil | ||
| } | ||
| if route.Spec.Host != hostname { | ||
| return false, nil | ||
| } | ||
| if route.Labels[additionalRouteLabel] != "true" { | ||
| return false, nil | ||
| } | ||
| for k, v := range expectedLabels { | ||
| if route.Labels[k] != string(v) { | ||
| return false, nil | ||
| } | ||
| } | ||
| return true, nil | ||
| }) | ||
| o.Expect(err).NotTo(o.HaveOccurred(), "route %s not created with expected labels within %s", name, pollTimeout) | ||
| return route | ||
| } | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.