From e9d729e664671373aaf91b60e82c361ed22e0bd0 Mon Sep 17 00:00:00 2001 From: Sergiy Kulanov Date: Tue, 7 Jul 2026 14:08:11 +0300 Subject: [PATCH] EPMDEDP-17139: feat: add Envoy Gateway HTTPRoute support for GitServer webhook Add Gateway API (Envoy Gateway) as an alternative to nginx Ingress for exposing the Tekton EventListener webhook receiver, selectable per install so a cluster runs either nginx or Envoy. - Introduce INGRESS_CONTROLLER_TYPE (nginx|envoy) and GATEWAY_NAME/GATEWAY_NAMESPACE env vars, wired from the shared global.gatewayApi Helm values to stay consistent with the Gateway referenced by other components - Create an HTTPRoute attached to the shared Gateway when envoy is selected, mirroring the existing Ingress/Route create-if-absent logic; read it back to build the webhook URL - Grant httproutes RBAC and register the Gateway API scheme - Default platform type to kubernetes so the common case works without extra configuration Signed-off-by: Sergiy Kulanov --- cmd/main.go | 2 + config/rbac/role.yaml | 9 + .../codebase/service/chain/put_webhook.go | 31 ++- .../service/chain/put_webhook_test.go | 194 ++++++++++++++++++ .../gitserver/create_event_listener.go | 105 ++++++++++ .../gitserver/create_event_listener_test.go | 140 +++++++++++++ controllers/gitserver/gitserver_controller.go | 1 + .../gitserver/gitserver_controller_test.go | 47 ++++- .../integrationsecret_controller.go | 1 - deploy-templates/README.md | 4 + deploy-templates/templates/deployment.yaml | 6 + .../templates/role_kubernetes.yaml | 9 + deploy-templates/values.yaml | 9 + go.mod | 3 +- go.sum | 6 +- pkg/autodeploy/autodeploy_test.go | 112 +++------- pkg/platform/helper.go | 24 ++- pkg/platform/helper_test.go | 68 +++++- 18 files changed, 673 insertions(+), 98 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 375c3127..55c287e8 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -27,6 +27,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/metrics/filters" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" ctrlwebhook "sigs.k8s.io/controller-runtime/pkg/webhook" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" cdPipeApi "github.com/epam/edp-cd-pipeline-operator/v2/api/v1" buildInfo "github.com/epam/edp-common/pkg/config" @@ -207,6 +208,7 @@ func main() { utilruntime.Must(cdPipeApi.AddToScheme(scheme)) utilruntime.Must(networkingV1.AddToScheme(scheme)) utilruntime.Must(routeApi.AddToScheme(scheme)) + utilruntime.Must(gatewayv1.Install(scheme)) utilruntime.Must(tektonTriggersApi.AddToScheme(scheme)) utilruntime.Must(tektonpipelineApi.AddToScheme(scheme)) diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 3007b578..5bdf4355 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -50,6 +50,15 @@ rules: - patch - update - watch +- apiGroups: + - gateway.networking.k8s.io + resources: + - httproutes + verbs: + - create + - get + - list + - watch - apiGroups: - networking.k8s.io resources: diff --git a/controllers/codebase/service/chain/put_webhook.go b/controllers/codebase/service/chain/put_webhook.go index 9c8f3443..66f72295 100644 --- a/controllers/codebase/service/chain/put_webhook.go +++ b/controllers/codebase/service/chain/put_webhook.go @@ -11,6 +11,7 @@ import ( networkingV1 "k8s.io/api/networking/v1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" codebaseApi "github.com/epam/edp-codebase-operator/v2/api/v1" "github.com/epam/edp-codebase-operator/v2/controllers/gitserver" @@ -160,11 +161,15 @@ func (s *PutWebHook) getWebHookUrl(ctx context.Context, gitServer *codebaseApi.G return gitServer.Spec.WebhookUrl, nil } - if platform.IsK8S() { - return s.getWebhookIngressUrl(ctx, gitServer.Name, gitServer.Namespace) + if !platform.IsK8S() { + return s.getWebhookRouteUrl(ctx, gitServer.Name, gitServer.Namespace) } - return s.getWebhookRouteUrl(ctx, gitServer.Name, gitServer.Namespace) + if platform.IsEnvoy() { + return s.getWebhookHTTPRouteUrl(ctx, gitServer.Name, gitServer.Namespace) + } + + return s.getWebhookIngressUrl(ctx, gitServer.Name, gitServer.Namespace) } func (*PutWebHook) processCodebaseError(codebase *codebaseApi.Codebase, err error) error { @@ -193,6 +198,26 @@ func (s *PutWebHook) getWebhookIngressUrl(ctx context.Context, gitServerName, na return util.GetHostWithProtocol(ingress.Spec.Rules[0].Host), nil } +func (s *PutWebHook) getWebhookHTTPRouteUrl(ctx context.Context, gitServerName, namespace string) (string, error) { + route := &gatewayv1.HTTPRoute{} + if err := s.client.Get( + ctx, + client.ObjectKey{ + Name: gitserver.GenerateIngressName(gitServerName), + Namespace: namespace, + }, + route, + ); err != nil { + return "", fmt.Errorf("failed to get webhook HTTPRoute: %w", err) + } + + if len(route.Spec.Hostnames) == 0 { + return "", fmt.Errorf("HTTPRoute %s doesn't have hostnames", route.Name) + } + + return util.GetHostWithProtocol(string(route.Spec.Hostnames[0])), nil +} + func (s *PutWebHook) getWebhookRouteUrl(ctx context.Context, gitServerName, namespace string) (string, error) { route := &routeApi.Route{} if err := s.client.Get( diff --git a/controllers/codebase/service/chain/put_webhook_test.go b/controllers/codebase/service/chain/put_webhook_test.go index 5067452e..c01d8592 100644 --- a/controllers/codebase/service/chain/put_webhook_test.go +++ b/controllers/codebase/service/chain/put_webhook_test.go @@ -19,6 +19,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" codebaseApi "github.com/epam/edp-codebase-operator/v2/api/v1" "github.com/epam/edp-codebase-operator/v2/controllers/gitserver" @@ -42,6 +43,7 @@ func TestPutWebHook_ServeRequest(t *testing.T) { require.NoError(t, coreV1.AddToScheme(schema)) require.NoError(t, networkingV1.AddToScheme(schema)) require.NoError(t, routeApi.AddToScheme(schema)) + require.NoError(t, gatewayv1.Install(schema)) gitURL := "test-owner/test-repo" fakeUrlRegexp := regexp.MustCompile(`.*`) @@ -247,6 +249,66 @@ func TestPutWebHook_ServeRequest(t *testing.T) { }, wantErr: require.NoError, }, + { + name: "success gitlab with envoy httproute", + codebase: &codebaseApi.Codebase{ + ObjectMeta: metaV1.ObjectMeta{ + Namespace: namespace, + Name: "test-codebase", + ResourceVersion: "1", + }, + Spec: codebaseApi.CodebaseSpec{ + GitServer: "test-git-server", + GitUrlPath: gitURL, + CiTool: util.CITekton, + }, + }, + prepare: func(t *testing.T) { + t.Setenv(platform.TypeEnv, platform.K8S) + t.Setenv(platform.IngressControllerEnv, platform.IngressControllerEnvoy) + }, + k8sObjects: []client.Object{ + &codebaseApi.Codebase{ + ObjectMeta: metaV1.ObjectMeta{ + Namespace: namespace, + Name: "test-codebase", + ResourceVersion: "1", + }, + }, + &codebaseApi.GitServer{ + ObjectMeta: metaV1.ObjectMeta{ + Namespace: namespace, + Name: "test-git-server", + }, + Spec: codebaseApi.GitServerSpec{ + GitHost: "fake.gitlab.com", + GitUser: "git", + HttpsPort: 443, + NameSshKeySecret: "test-secret", + GitProvider: codebaseApi.GitProviderGitlab, + }, + }, + &coreV1.Secret{ + ObjectMeta: metaV1.ObjectMeta{ + Namespace: namespace, + Name: "test-secret", + }, + Data: map[string][]byte{ + util.GitServerSecretTokenField: []byte("test-token"), + util.GitServerSecretWebhookSecretField: []byte("test-webhook-secret"), + }, + }, + fakeHTTPRoute(gitserver.GenerateIngressName("test-git-server"), "fake.gitlab.com"), + }, + responder: func(t *testing.T) { + POSTResponder := httpmock.NewStringResponder(http.StatusOK, "") + httpmock.RegisterRegexpResponder(http.MethodPost, fakeUrlRegexp, POSTResponder) + + GETResponder := httpmock.NewStringResponder(http.StatusOK, "") + httpmock.RegisterRegexpResponder(http.MethodGet, fakeUrlRegexp, GETResponder) + }, + wantErr: require.NoError, + }, { name: "success github", codebase: &codebaseApi.Codebase{ @@ -787,6 +849,105 @@ func TestPutWebHook_ServeRequest(t *testing.T) { wantErr: require.Error, errContains: "failed to get webhook ingress", }, + { + name: "failed to get getWebHookUrl - no hostnames on httproute", + codebase: &codebaseApi.Codebase{ + ObjectMeta: metaV1.ObjectMeta{ + Namespace: namespace, + Name: "test-codebase", + }, + Spec: codebaseApi.CodebaseSpec{ + GitServer: "test-git-server", + GitUrlPath: gitURL, + CiTool: util.CITekton, + }, + }, + prepare: func(t *testing.T) { + t.Setenv(platform.TypeEnv, platform.K8S) + t.Setenv(platform.IngressControllerEnv, platform.IngressControllerEnvoy) + }, + k8sObjects: []client.Object{ + &codebaseApi.GitServer{ + ObjectMeta: metaV1.ObjectMeta{ + Namespace: namespace, + Name: "test-git-server", + }, + Spec: codebaseApi.GitServerSpec{ + GitHost: "fake.gitlab.com", + GitUser: "git", + HttpsPort: 443, + NameSshKeySecret: "test-secret", + GitProvider: codebaseApi.GitProviderGitlab, + }, + }, + &coreV1.Secret{ + ObjectMeta: metaV1.ObjectMeta{ + Namespace: namespace, + Name: "test-secret", + }, + Data: map[string][]byte{ + util.GitServerSecretTokenField: []byte("test-token"), + }, + }, + &gatewayv1.HTTPRoute{ + ObjectMeta: metaV1.ObjectMeta{ + Namespace: namespace, + Name: gitserver.GenerateIngressName("test-git-server"), + Labels: map[string]string{ + "app.edp.epam.com/gitServer": "test-git-server", + }, + }, + }, + }, + responder: func(t *testing.T) {}, + wantErr: require.Error, + errContains: "doesn't have hostnames", + }, + { + name: "failed to get getWebHookUrl - no httproute", + codebase: &codebaseApi.Codebase{ + ObjectMeta: metaV1.ObjectMeta{ + Namespace: namespace, + Name: "test-codebase", + }, + Spec: codebaseApi.CodebaseSpec{ + GitServer: "test-git-server", + GitUrlPath: gitURL, + CiTool: util.CITekton, + }, + }, + prepare: func(t *testing.T) { + t.Setenv(platform.TypeEnv, platform.K8S) + t.Setenv(platform.IngressControllerEnv, platform.IngressControllerEnvoy) + }, + k8sObjects: []client.Object{ + &codebaseApi.GitServer{ + ObjectMeta: metaV1.ObjectMeta{ + Namespace: namespace, + Name: "test-git-server", + }, + Spec: codebaseApi.GitServerSpec{ + GitHost: "fake.gitlab.com", + GitUser: "git", + HttpsPort: 443, + NameSshKeySecret: "test-secret", + GitProvider: codebaseApi.GitProviderGitlab, + }, + }, + &coreV1.Secret{ + ObjectMeta: metaV1.ObjectMeta{ + Namespace: namespace, + Name: "test-secret", + }, + Data: map[string][]byte{ + util.GitServerSecretTokenField: []byte("test-token"), + }, + }, + }, + responder: func(t *testing.T) {}, + wantErr: require.Error, + errContains: "failed to get webhook HTTPRoute", + }, { name: "failed to get secret - no required field", codebase: &codebaseApi.Codebase{ @@ -908,6 +1069,39 @@ func TestPutWebHook_ServeRequest(t *testing.T) { } } +func TestPutWebHook_getWebhookHTTPRouteUrl(t *testing.T) { + schema := runtime.NewScheme() + require.NoError(t, gatewayv1.Install(schema)) + + host := "el-test-git-server-test-ns.example.com" + + k8sClient := fake.NewClientBuilder(). + WithScheme(schema). + WithObjects(fakeHTTPRoute(gitserver.GenerateIngressName("test-git-server"), host)). + Build() + + s := NewPutWebHook(k8sClient, resty.New()) + + got, err := s.getWebhookHTTPRouteUrl(context.Background(), "test-git-server", namespace) + require.NoError(t, err) + assert.Equal(t, util.GetHostWithProtocol(host), got) +} + +func fakeHTTPRoute(name, host string) *gatewayv1.HTTPRoute { + return &gatewayv1.HTTPRoute{ + ObjectMeta: metaV1.ObjectMeta{ + Namespace: namespace, + Name: name, + Labels: map[string]string{ + "app.edp.epam.com/gitServer": "test-git-server", + }, + }, + Spec: gatewayv1.HTTPRouteSpec{ + Hostnames: []gatewayv1.Hostname{gatewayv1.Hostname(host)}, + }, + } +} + func fakeIngress(name, host string) *networkingV1.Ingress { return &networkingV1.Ingress{ ObjectMeta: metaV1.ObjectMeta{ diff --git a/controllers/gitserver/create_event_listener.go b/controllers/gitserver/create_event_listener.go index 1029bfbc..fd797383 100644 --- a/controllers/gitserver/create_event_listener.go +++ b/controllers/gitserver/create_event_listener.go @@ -13,6 +13,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" codebaseApi "github.com/epam/edp-codebase-operator/v2/api/v1" "github.com/epam/edp-codebase-operator/v2/pkg/platform" @@ -48,6 +49,10 @@ func (h *CreateEventListener) ServeRequest(ctx context.Context, gitServer *codeb return h.createRoute(ctx, gitServer) } + if platform.IsEnvoy() { + return h.createHTTPRoute(ctx, gitServer) + } + return h.createIngress(ctx, gitServer) } @@ -218,6 +223,106 @@ func (h *CreateEventListener) createIngress(ctx context.Context, gitServer *code return nil } +func (h *CreateEventListener) createHTTPRoute(ctx context.Context, gitServer *codebaseApi.GitServer) error { + log := ctrl.LoggerFrom(ctx) + + log.Info("Creating HTTPRoute for EventListener") + + gatewayName := platform.GatewayName() + gatewayNamespace := platform.GatewayNamespace() + + if gatewayName == "" || gatewayNamespace == "" { + return fmt.Errorf("GATEWAY_NAME/GATEWAY_NAMESPACE must be set when INGRESS_CONTROLLER_TYPE=envoy") + } + + name := GenerateIngressName(gitServer.Name) + + err := h.k8sClient.Get(ctx, client.ObjectKey{ + Namespace: gitServer.Namespace, + Name: name, + }, &gatewayv1.HTTPRoute{}) + if err == nil { + log.Info("HTTPRoute already exists", "HTTPRoute", name) + + return nil + } + + if client.IgnoreNotFound(err) != nil { + return fmt.Errorf("failed to get HTTPRoute: %w", err) + } + + config, err := platform.GetKrciConfig(ctx, h.k8sClient, gitServer.Namespace) + if err != nil { + return fmt.Errorf("failed to get dnsWildcard: %w", err) + } + + // This port is hardcoded in Tekton Triggers. + // https://github.com/tektoncd/triggers/blob/v0.31.0/pkg/reconciler/eventlistener/resources/service.go#L37 + const elServicePort = 8080 + + httpRoute := &gatewayv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: gitServer.Namespace, + Labels: map[string]string{ + "app.edp.epam.com/gitServer": gitServer.Name, + }, + }, + Spec: gatewayv1.HTTPRouteSpec{ + CommonRouteSpec: gatewayv1.CommonRouteSpec{ + ParentRefs: []gatewayv1.ParentReference{ + { + Name: gatewayv1.ObjectName(gatewayName), + Namespace: ptr.To(gatewayv1.Namespace(gatewayNamespace)), + }, + }, + }, + Hostnames: []gatewayv1.Hostname{ + gatewayv1.Hostname(fmt.Sprintf( + "el-%s-%s.%s", + gitServer.Name, + gitServer.Namespace, + config.DnsWildcard, + )), + }, + Rules: []gatewayv1.HTTPRouteRule{ + { + Matches: []gatewayv1.HTTPRouteMatch{ + { + Path: &gatewayv1.HTTPPathMatch{ + Type: ptr.To(gatewayv1.PathMatchPathPrefix), + Value: ptr.To("/"), + }, + }, + }, + BackendRefs: []gatewayv1.HTTPBackendRef{ + { + BackendRef: gatewayv1.BackendRef{ + BackendObjectReference: gatewayv1.BackendObjectReference{ + Name: gatewayv1.ObjectName(generateServiceName(gitServer.Name)), + Port: ptr.To(gatewayv1.PortNumber(elServicePort)), + }, + }, + }, + }, + }, + }, + }, + } + + if err = controllerutil.SetControllerReference(gitServer, httpRoute, h.k8sClient.Scheme()); err != nil { + return fmt.Errorf("failed to set controller reference for HTTPRoute: %w", err) + } + + if err = h.k8sClient.Create(ctx, httpRoute); err != nil { + return fmt.Errorf("failed to create HTTPRoute: %w", err) + } + + log.Info("HTTPRoute has been created", "HTTPRoute", httpRoute.Name) + + return nil +} + func (h *CreateEventListener) createRoute(ctx context.Context, gitServer *codebaseApi.GitServer) error { log := ctrl.LoggerFrom(ctx) diff --git a/controllers/gitserver/create_event_listener_test.go b/controllers/gitserver/create_event_listener_test.go index 231b780b..c702b089 100644 --- a/controllers/gitserver/create_event_listener_test.go +++ b/controllers/gitserver/create_event_listener_test.go @@ -15,6 +15,7 @@ import ( controllerruntime "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" codebaseApi "github.com/epam/edp-codebase-operator/v2/api/v1" "github.com/epam/edp-codebase-operator/v2/pkg/platform" @@ -28,6 +29,7 @@ func TestCreateEventListener_ServeRequest(t *testing.T) { require.NoError(t, networkingv1.AddToScheme(scheme)) require.NoError(t, routeApi.AddToScheme(scheme)) require.NoError(t, corev1.AddToScheme(scheme)) + require.NoError(t, gatewayv1.Install(scheme)) tests := []struct { name string @@ -333,6 +335,144 @@ func TestCreateEventListener_ServeRequest(t *testing.T) { }, i)) }, }, + { + name: "create event listener success envoy", + gitServer: &codebaseApi.GitServer{ + ObjectMeta: controllerruntime.ObjectMeta{ + Name: "test-git-server", + Namespace: "default", + }, + }, + k8sClient: func(t *testing.T) client.Client { + return fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects(&corev1.ConfigMap{ + ObjectMeta: controllerruntime.ObjectMeta{ + Namespace: "default", + Name: platform.KrciConfigMap, + }, + }). + Build() + }, + prepare: func(t *testing.T) { + t.Setenv(platform.TypeEnv, platform.K8S) + t.Setenv(platform.IngressControllerEnv, platform.IngressControllerEnvoy) + t.Setenv(platform.GatewayNameEnv, "main-gateway") + t.Setenv(platform.GatewayNamespaceEnv, "envoy-gateway-system") + }, + wantErr: require.NoError, + want: func(t *testing.T, k8sClient client.Client) { + el := tektoncd.NewEventListenerUnstructured() + require.NoError(t, k8sClient.Get(context.Background(), client.ObjectKey{ + Namespace: "default", + Name: generateEventListenerName("test-git-server"), + }, el)) + + hr := &gatewayv1.HTTPRoute{} + require.NoError(t, k8sClient.Get(context.Background(), client.ObjectKey{ + Namespace: "default", + Name: GenerateIngressName("test-git-server"), + }, hr)) + + require.Len(t, hr.Spec.ParentRefs, 1) + require.Equal(t, gatewayv1.ObjectName("main-gateway"), hr.Spec.ParentRefs[0].Name) + require.NotNil(t, hr.Spec.ParentRefs[0].Namespace) + require.Equal(t, gatewayv1.Namespace("envoy-gateway-system"), *hr.Spec.ParentRefs[0].Namespace) + + require.Len(t, hr.Spec.Hostnames, 1) + require.Equal(t, gatewayv1.Hostname("el-test-git-server-default."), hr.Spec.Hostnames[0]) + + require.Len(t, hr.Spec.Rules, 1) + require.Len(t, hr.Spec.Rules[0].BackendRefs, 1) + require.Equal(t, gatewayv1.ObjectName(generateServiceName("test-git-server")), hr.Spec.Rules[0].BackendRefs[0].Name) + }, + }, + { + name: "http route already exists", + gitServer: &codebaseApi.GitServer{ + ObjectMeta: controllerruntime.ObjectMeta{ + Name: "test-git-server", + Namespace: "default", + }, + }, + k8sClient: func(t *testing.T) client.Client { + return fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects( + &corev1.ConfigMap{ + ObjectMeta: controllerruntime.ObjectMeta{ + Namespace: "default", + Name: platform.KrciConfigMap, + }, + }, + &gatewayv1.HTTPRoute{ + ObjectMeta: controllerruntime.ObjectMeta{ + Namespace: "default", + Name: GenerateIngressName("test-git-server"), + }, + }, + ). + Build() + }, + prepare: func(t *testing.T) { + t.Setenv(platform.TypeEnv, platform.K8S) + t.Setenv(platform.IngressControllerEnv, platform.IngressControllerEnvoy) + t.Setenv(platform.GatewayNameEnv, "main-gateway") + t.Setenv(platform.GatewayNamespaceEnv, "envoy-gateway-system") + }, + wantErr: require.NoError, + want: func(t *testing.T, k8sClient client.Client) { + el := tektoncd.NewEventListenerUnstructured() + require.NoError(t, k8sClient.Get(context.Background(), client.ObjectKey{ + Namespace: "default", + Name: generateEventListenerName("test-git-server"), + }, el)) + + hr := &gatewayv1.HTTPRoute{} + require.NoError(t, k8sClient.Get(context.Background(), client.ObjectKey{ + Namespace: "default", + Name: GenerateIngressName("test-git-server"), + }, hr)) + }, + }, + { + name: "envoy selected but gateway name is empty", + gitServer: &codebaseApi.GitServer{ + ObjectMeta: controllerruntime.ObjectMeta{ + Name: "test-git-server", + Namespace: "default", + }, + }, + k8sClient: func(t *testing.T) client.Client { + return fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects(&corev1.ConfigMap{ + ObjectMeta: controllerruntime.ObjectMeta{ + Namespace: "default", + Name: platform.KrciConfigMap, + }, + }). + Build() + }, + prepare: func(t *testing.T) { + t.Setenv(platform.TypeEnv, platform.K8S) + t.Setenv(platform.IngressControllerEnv, platform.IngressControllerEnvoy) + t.Setenv(platform.GatewayNamespaceEnv, "envoy-gateway-system") + }, + wantErr: func(t require.TestingT, err error, i ...interface{}) { + require.Error(t, err) + require.Contains(t, err.Error(), "GATEWAY_NAME/GATEWAY_NAMESPACE must be set") + }, + want: func(t *testing.T, k8sClient client.Client) { + hr := &gatewayv1.HTTPRoute{} + err := k8sClient.Get(context.Background(), client.ObjectKey{ + Namespace: "default", + Name: GenerateIngressName("test-git-server"), + }, hr) + require.Error(t, err) + require.True(t, k8sErrors.IsNotFound(err)) + }, + }, { name: "config not found", gitServer: &codebaseApi.GitServer{ diff --git a/controllers/gitserver/gitserver_controller.go b/controllers/gitserver/gitserver_controller.go index 2722d463..13247979 100644 --- a/controllers/gitserver/gitserver_controller.go +++ b/controllers/gitserver/gitserver_controller.go @@ -53,6 +53,7 @@ func (r *ReconcileGitServer) SetupWithManager(mgr ctrl.Manager) error { // +kubebuilder:rbac:groups=v2.edp.epam.com,namespace=placeholder,resources=gitservers/finalizers,verbs=update // +kubebuilder:rbac:groups="",namespace=placeholder,resources=secrets,verbs=get;list;watch;update;patch // +kubebuilder:rbac:groups="networking.k8s.io",namespace=placeholder,resources=ingresses,verbs=get;list;watch;create +// +kubebuilder:rbac:groups="gateway.networking.k8s.io",namespace=placeholder,resources=httproutes,verbs=get;list;watch;create // +kubebuilder:rbac:groups="route.openshift.io",namespace=placeholder,resources=routes,verbs=get;list;watch;create // Reconcile reads that state of the cluster for a GitServer object and makes changes based on the state. diff --git a/controllers/gitserver/gitserver_controller_test.go b/controllers/gitserver/gitserver_controller_test.go index 86a29968..89a7b12f 100644 --- a/controllers/gitserver/gitserver_controller_test.go +++ b/controllers/gitserver/gitserver_controller_test.go @@ -413,6 +413,7 @@ func TestReconcileGitServer_EnsureSecretOwnership(t *testing.T) { } gsOwner := func(name string) metaV1.OwnerReference { controller := true + return metaV1.OwnerReference{ APIVersion: codebaseApi.GroupVersion.String(), Kind: "GitServer", @@ -423,6 +424,7 @@ func TestReconcileGitServer_EnsureSecretOwnership(t *testing.T) { } extOwner := func() metaV1.OwnerReference { controller := true + return metaV1.OwnerReference{ APIVersion: "external-secrets.io/v1beta1", Kind: "ExternalSecret", @@ -444,7 +446,9 @@ func TestReconcileGitServer_EnsureSecretOwnership(t *testing.T) { { name: "adopts an unowned secret", secretRef: "ssh-secret", - objects: func() []client.Object { return []client.Object{gitServer(targetName, "ssh-secret"), secret("ssh-secret")} }, + objects: func() []client.Object { + return []client.Object{gitServer(targetName, "ssh-secret"), secret("ssh-secret")} + }, verify: func(t *testing.T, s *corev1.Secret, getErr error) { require.NoError(t, getErr) require.Len(t, s.OwnerReferences, 1) @@ -457,7 +461,9 @@ func TestReconcileGitServer_EnsureSecretOwnership(t *testing.T) { { name: "leaves a secret controlled by another resource untouched", secretRef: "ssh-secret", - objects: func() []client.Object { return []client.Object{gitServer(targetName, "ssh-secret"), secret("ssh-secret", extOwner())} }, + objects: func() []client.Object { + return []client.Object{gitServer(targetName, "ssh-secret"), secret("ssh-secret", extOwner())} + }, verify: func(t *testing.T, s *corev1.Secret, getErr error) { require.NoError(t, getErr) require.Len(t, s.OwnerReferences, 1) @@ -467,7 +473,9 @@ func TestReconcileGitServer_EnsureSecretOwnership(t *testing.T) { { name: "is idempotent when already owned by this GitServer", secretRef: "ssh-secret", - objects: func() []client.Object { return []client.Object{gitServer(targetName, "ssh-secret"), secret("ssh-secret", gsOwner(targetName))} }, + objects: func() []client.Object { + return []client.Object{gitServer(targetName, "ssh-secret"), secret("ssh-secret", gsOwner(targetName))} + }, verify: func(t *testing.T, s *corev1.Secret, getErr error) { require.NoError(t, getErr) require.Len(t, s.OwnerReferences, 1) @@ -486,7 +494,11 @@ func TestReconcileGitServer_EnsureSecretOwnership(t *testing.T) { name: "does not exclusively own a secret shared by multiple GitServers", secretRef: "ssh-secret", objects: func() []client.Object { - return []client.Object{gitServer(targetName, "ssh-secret"), gitServer("other-git-server", "ssh-secret"), secret("ssh-secret")} + return []client.Object{ + gitServer(targetName, "ssh-secret"), + gitServer("other-git-server", "ssh-secret"), + secret("ssh-secret"), + } }, verify: func(t *testing.T, s *corev1.Secret, getErr error) { require.NoError(t, getErr) @@ -496,12 +508,21 @@ func TestReconcileGitServer_EnsureSecretOwnership(t *testing.T) { { name: "returns an error when reading the secret fails", secretRef: "ssh-secret", - objects: func() []client.Object { return []client.Object{gitServer(targetName, "ssh-secret"), secret("ssh-secret")} }, + objects: func() []client.Object { + return []client.Object{gitServer(targetName, "ssh-secret"), secret("ssh-secret")} + }, interceptor: interceptor.Funcs{ - Get: func(_ context.Context, _ client.WithWatch, _ client.ObjectKey, obj client.Object, _ ...client.GetOption) error { + Get: func( + _ context.Context, + _ client.WithWatch, + _ client.ObjectKey, + obj client.Object, + _ ...client.GetOption, + ) error { if _, ok := obj.(*corev1.Secret); ok { return errors.New("get failed") } + return nil }, }, @@ -510,7 +531,9 @@ func TestReconcileGitServer_EnsureSecretOwnership(t *testing.T) { { name: "returns an error when listing GitServers fails", secretRef: "ssh-secret", - objects: func() []client.Object { return []client.Object{gitServer(targetName, "ssh-secret"), secret("ssh-secret")} }, + objects: func() []client.Object { + return []client.Object{gitServer(targetName, "ssh-secret"), secret("ssh-secret")} + }, interceptor: interceptor.Funcs{ List: func(_ context.Context, _ client.WithWatch, _ client.ObjectList, _ ...client.ListOption) error { return errors.New("list failed") @@ -521,7 +544,9 @@ func TestReconcileGitServer_EnsureSecretOwnership(t *testing.T) { { name: "returns an error when updating the secret fails", secretRef: "ssh-secret", - objects: func() []client.Object { return []client.Object{gitServer(targetName, "ssh-secret"), secret("ssh-secret")} }, + objects: func() []client.Object { + return []client.Object{gitServer(targetName, "ssh-secret"), secret("ssh-secret")} + }, interceptor: interceptor.Funcs{ Update: func(_ context.Context, _ client.WithWatch, _ client.Object, _ ...client.UpdateOption) error { return errors.New("update failed") @@ -600,7 +625,11 @@ func TestReconcileGitServer_Reconcile_SecretOwnershipFailureSetsFailedStatus(t * assert.Equal(t, defaultRequeueTime, res.RequeueAfter) gotGitServer := &codebaseApi.GitServer{} - require.NoError(t, fakeCl.Get(context.Background(), types.NamespacedName{Name: gs.Name, Namespace: gs.Namespace}, gotGitServer)) + require.NoError(t, fakeCl.Get( + context.Background(), + types.NamespacedName{Name: gs.Name, Namespace: gs.Namespace}, + gotGitServer, + )) assert.Equal(t, "failed", gotGitServer.Status.Status) assert.False(t, gotGitServer.Status.Connected) } diff --git a/controllers/integrationsecret/integrationsecret_controller.go b/controllers/integrationsecret/integrationsecret_controller.go index 6fe9ab11..ec129e11 100644 --- a/controllers/integrationsecret/integrationsecret_controller.go +++ b/controllers/integrationsecret/integrationsecret_controller.go @@ -308,4 +308,3 @@ func hasIntegrationSecretLabelLabel(object client.Object) bool { return label == "true" } - diff --git a/deploy-templates/README.md b/deploy-templates/README.md index 4accbb7a..a76e4336 100644 --- a/deploy-templates/README.md +++ b/deploy-templates/README.md @@ -27,12 +27,16 @@ A Helm chart for KubeRocketCI Codebase Operator | envs[0].name | string | `"RECONCILATION_PERIOD"` | | | envs[0].value | string | `"360"` | | | envs[1] | object | `{"name":"CODEBASE_BRANCH_MAX_CONCURRENT_RECONCILES","value":"3"}` | Maximum number of parallel reconciliation codebasebranches | +| global.gatewayApi | object | `{"gatewayName":"main-gateway","gatewayNamespace":"envoy-gateway-system"}` | Gateway API parent Gateway that EventListener HTTPRoutes attach to (used when ingressController=envoy) | +| global.gatewayApi.gatewayName | string | `"main-gateway"` | Name of the parent Gateway resource | +| global.gatewayApi.gatewayNamespace | string | `"envoy-gateway-system"` | Namespace of the parent Gateway resource | | global.platform | string | `"kubernetes"` | platform type that can be "kubernetes" or "openshift" | | image.registry | string | `""` | KubeRocketCI codebase-operator Docker image registry. | | image.repository | string | `"epamedp/codebase-operator"` | KubeRocketCI codebase-operator Docker image name. The released image can be found on [Dockerhub](https://hub.docker.com/r/epamedp/codebase-operator) | | image.tag | string | `nil` | KubeRocketCI codebase-operator Docker image tag. The released image can be found on [Dockerhub](https://hub.docker.com/r/epamedp/codebase-operator/tags) | | imagePullPolicy | string | `"IfNotPresent"` | | | imagePullSecrets | list | `[]` | Optional array of imagePullSecrets containing private registry credentials # Ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry | +| ingressController | string | `"nginx"` | Ingress controller for the GitServer EventListener webhook: "nginx" (Ingress) or "envoy" (Gateway API HTTPRoute) | | jira.apiUrl | string | `"https://jiraeu-api.example.com"` | API URL for development | | jira.credentialName | string | `"ci-jira"` | Name of secret with credentials to Jira server | | jira.integration | bool | `false` | Flag to enable/disable Jira integration | diff --git a/deploy-templates/templates/deployment.yaml b/deploy-templates/templates/deployment.yaml index f5d3d834..bca1448e 100644 --- a/deploy-templates/templates/deployment.yaml +++ b/deploy-templates/templates/deployment.yaml @@ -63,6 +63,12 @@ spec: value: {{ .Values.name }} - name: PLATFORM_TYPE value: "{{ .Values.global.platform }}" + - name: INGRESS_CONTROLLER_TYPE + value: "{{ .Values.ingressController }}" + - name: GATEWAY_NAME + value: "{{ .Values.global.gatewayApi.gatewayName }}" + - name: GATEWAY_NAMESPACE + value: "{{ .Values.global.gatewayApi.gatewayNamespace }}" - name: TELEMETRY_ENABLED value: "{{ .Values.telemetryEnabled }}" - name: ENABLE_WEBHOOKS diff --git a/deploy-templates/templates/role_kubernetes.yaml b/deploy-templates/templates/role_kubernetes.yaml index d8072860..9c371bde 100644 --- a/deploy-templates/templates/role_kubernetes.yaml +++ b/deploy-templates/templates/role_kubernetes.yaml @@ -72,6 +72,15 @@ rules: - list - watch - create +- apiGroups: + - gateway.networking.k8s.io + resources: + - httproutes + verbs: + - get + - list + - watch + - create - apiGroups: - "" resources: diff --git a/deploy-templates/values.yaml b/deploy-templates/values.yaml index 1d8372e7..f2092f6a 100644 --- a/deploy-templates/values.yaml +++ b/deploy-templates/values.yaml @@ -1,6 +1,15 @@ global: # -- platform type that can be "kubernetes" or "openshift" platform: "kubernetes" + # -- Gateway API parent Gateway that EventListener HTTPRoutes attach to (used when ingressController=envoy) + gatewayApi: + # -- Name of the parent Gateway resource + gatewayName: "main-gateway" + # -- Namespace of the parent Gateway resource + gatewayNamespace: "envoy-gateway-system" + +# -- Ingress controller for the GitServer EventListener webhook: "nginx" (Ingress) or "envoy" (Gateway API HTTPRoute) +ingressController: "nginx" # -- component name name: codebase-operator diff --git a/go.mod b/go.mod index dff77d13..d64be6c0 100644 --- a/go.mod +++ b/go.mod @@ -26,6 +26,7 @@ require ( k8s.io/client-go v0.33.11 k8s.io/utils v0.0.0-20241210054802-24370beab758 sigs.k8s.io/controller-runtime v0.21.0 + sigs.k8s.io/gateway-api v1.3.0 ) require ( @@ -150,6 +151,6 @@ require ( sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/go.sum b/go.sum index 17de0391..42d6442d 100644 --- a/go.sum +++ b/go.sum @@ -1457,6 +1457,8 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUo sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= +sigs.k8s.io/gateway-api v1.3.0 h1:q6okN+/UKDATola4JY7zXzx40WO4VISk7i9DIfOvr9M= +sigs.k8s.io/gateway-api v1.3.0/go.mod h1:d8NV8nJbaRbEKem+5IuxkL8gJGOZ+FJ+NvOIltV8gDk= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= @@ -1465,8 +1467,8 @@ sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxO sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= -sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/pkg/autodeploy/autodeploy_test.go b/pkg/autodeploy/autodeploy_test.go index bfbc22d9..3c28df2f 100644 --- a/pkg/autodeploy/autodeploy_test.go +++ b/pkg/autodeploy/autodeploy_test.go @@ -19,6 +19,28 @@ import ( codebaseApi "github.com/epam/edp-codebase-operator/v2/api/v1" ) +func newCBIS(name, codebase, tag, digest string) *codebaseApi.CodebaseImageStream { + return &codebaseApi.CodebaseImageStream{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: "default", + Labels: map[string]string{ + codebaseApi.CodebaseBranchLabel: name, + }, + }, + Spec: codebaseApi.CodebaseImageStreamSpec{ + Codebase: codebase, + Tags: []codebaseApi.Tag{ + { + Name: tag, + Created: time.Now().Format(time.RFC3339), + Digest: digest, + }, + }, + }, + } +} + func TestStrategyManager_GetAppPayloadForAllLatestStrategy(t *testing.T) { t.Parallel() @@ -620,7 +642,8 @@ func TestStrategyManager_GetAppPayloadForCurrentWithStableStrategy(t *testing.T) }, ).Build() }, - want: `{"app1":{"imageTag":"1.0","imageDigest":"sha256:app1digest"},"app2":{"imageTag":"3.0","imageDigest":"sha256:app2digest"}}`, + want: `{"app1":{"imageTag":"1.0","imageDigest":"sha256:app1digest"},` + + `"app2":{"imageTag":"3.0","imageDigest":"sha256:app2digest"}}`, wantErr: require.NoError, }, { @@ -648,44 +671,8 @@ func TestStrategyManager_GetAppPayloadForCurrentWithStableStrategy(t *testing.T) }, k8sClient: func(t *testing.T) client.Client { return fake.NewClientBuilder().WithScheme(scheme).WithObjects( - &codebaseApi.CodebaseImageStream{ - ObjectMeta: metav1.ObjectMeta{ - Name: "app1-main", - Namespace: "default", - Labels: map[string]string{ - codebaseApi.CodebaseBranchLabel: "app1-main", - }, - }, - Spec: codebaseApi.CodebaseImageStreamSpec{ - Codebase: "app1", - Tags: []codebaseApi.Tag{ - { - Name: "1.0", - Created: time.Now().Format(time.RFC3339), - Digest: "sha256:app1digest", - }, - }, - }, - }, - &codebaseApi.CodebaseImageStream{ - ObjectMeta: metav1.ObjectMeta{ - Name: "app2-main", - Namespace: "default", - Labels: map[string]string{ - codebaseApi.CodebaseBranchLabel: "app2-main", - }, - }, - Spec: codebaseApi.CodebaseImageStreamSpec{ - Codebase: "app2", - Tags: []codebaseApi.Tag{ - { - Name: "3.0", - Created: time.Now().Format(time.RFC3339), - Digest: "sha256:app2digest", - }, - }, - }, - }, + newCBIS("app1-main", "app1", "1.0", "sha256:app1digest"), + newCBIS("app2-main", "app2", "3.0", "sha256:app2digest"), ).Build() }, want: `{"app1":{"imageTag":"1.0","imageDigest":"sha256:app1digest"},"app2":{"imageTag":"old-tag"}}`, @@ -710,47 +697,12 @@ func TestStrategyManager_GetAppPayloadForCurrentWithStableStrategy(t *testing.T) stage: &pipelineAPi.Stage{}, k8sClient: func(t *testing.T) client.Client { return fake.NewClientBuilder().WithScheme(scheme).WithObjects( - &codebaseApi.CodebaseImageStream{ - ObjectMeta: metav1.ObjectMeta{ - Name: "app1-main", - Namespace: "default", - Labels: map[string]string{ - codebaseApi.CodebaseBranchLabel: "app1-main", - }, - }, - Spec: codebaseApi.CodebaseImageStreamSpec{ - Codebase: "app1", - Tags: []codebaseApi.Tag{ - { - Name: "1.0", - Created: time.Now().Format(time.RFC3339), - Digest: "sha256:app1digest", - }, - }, - }, - }, - &codebaseApi.CodebaseImageStream{ - ObjectMeta: metav1.ObjectMeta{ - Name: "app2-main", - Namespace: "default", - Labels: map[string]string{ - codebaseApi.CodebaseBranchLabel: "app2-main", - }, - }, - Spec: codebaseApi.CodebaseImageStreamSpec{ - Codebase: "app2", - Tags: []codebaseApi.Tag{ - { - Name: "5.0", - Created: time.Now().Format(time.RFC3339), - Digest: "sha256:app2latest", - }, - }, - }, - }, + newCBIS("app1-main", "app1", "1.0", "sha256:app1digest"), + newCBIS("app2-main", "app2", "5.0", "sha256:app2latest"), ).Build() }, - want: `{"app1":{"imageTag":"1.0","imageDigest":"sha256:app1digest"},"app2":{"imageTag":"5.0","imageDigest":"sha256:app2latest"}}`, + want: `{"app1":{"imageTag":"1.0","imageDigest":"sha256:app1digest"},` + + `"app2":{"imageTag":"5.0","imageDigest":"sha256:app2latest"}}`, wantErr: require.NoError, }, { @@ -837,7 +789,9 @@ func TestStrategyManager_GetAppPayloadForCurrentWithStableStrategy(t *testing.T) }, ).Build() }, - want: `{"app1":{"imageTag":"1.0","imageDigest":"sha256:currentdigest"},"app2":{"imageTag":"2.0"},"app3":{"imageTag":"7.0","imageDigest":"sha256:latestdigest"}}`, + want: `{"app1":{"imageTag":"1.0","imageDigest":"sha256:currentdigest"},` + + `"app2":{"imageTag":"2.0"},` + + `"app3":{"imageTag":"7.0","imageDigest":"sha256:latestdigest"}}`, wantErr: require.NoError, }, } diff --git a/pkg/platform/helper.go b/pkg/platform/helper.go index 641fd42c..86a7dfbc 100644 --- a/pkg/platform/helper.go +++ b/pkg/platform/helper.go @@ -7,7 +7,14 @@ const ( K8S = "kubernetes" TypeEnv = "PLATFORM_TYPE" - defaultPt = Openshift + defaultPt = K8S + + IngressControllerEnv = "INGRESS_CONTROLLER_TYPE" + IngressControllerNginx = "nginx" + IngressControllerEnvoy = "envoy" + + GatewayNameEnv = "GATEWAY_NAME" + GatewayNamespaceEnv = "GATEWAY_NAMESPACE" ) func lookup() string { @@ -29,3 +36,18 @@ func IsOpenshift() bool { func GetPlatformType() string { return lookup() } + +// IsEnvoy returns true when the Envoy Gateway (Gateway API) ingress controller is selected. +func IsEnvoy() bool { + return os.Getenv(IngressControllerEnv) == IngressControllerEnvoy +} + +// GatewayName returns the name of the parent Gateway API Gateway resource. +func GatewayName() string { + return os.Getenv(GatewayNameEnv) +} + +// GatewayNamespace returns the namespace of the parent Gateway API Gateway resource. +func GatewayNamespace() string { + return os.Getenv(GatewayNamespaceEnv) +} diff --git a/pkg/platform/helper_test.go b/pkg/platform/helper_test.go index 8a148750..65275393 100644 --- a/pkg/platform/helper_test.go +++ b/pkg/platform/helper_test.go @@ -12,12 +12,26 @@ func TestIsK8S_True(t *testing.T) { } } -func TestIsK8S_False(t *testing.T) { - if IsK8S() { +func TestIsK8S_DefaultTrue(t *testing.T) { + if !IsK8S() { + t.Error("kubernetes should be the default platform when PLATFORM_TYPE is unset") + } +} + +func TestIsOpenshift_True(t *testing.T) { + t.Setenv("PLATFORM_TYPE", "openshift") + + if !IsOpenshift() { t.Error("Should fail") } } +func TestIsOpenshift_DefaultFalse(t *testing.T) { + if IsOpenshift() { + t.Error("openshift should not be the default platform when PLATFORM_TYPE is unset") + } +} + func TestGetPlatformType_K8s(t *testing.T) { t.Setenv("PLATFORM_TYPE", "kubernetes") @@ -27,7 +41,57 @@ func TestGetPlatformType_K8s(t *testing.T) { } func TestGetPlatformType_Openshift(t *testing.T) { + t.Setenv("PLATFORM_TYPE", "openshift") + if GetPlatformType() == K8S { t.Error("Should fail") } } + +func TestGetPlatformType_DefaultKubernetes(t *testing.T) { + if GetPlatformType() != K8S { + t.Error("kubernetes should be the default platform when PLATFORM_TYPE is unset") + } +} + +func TestIsEnvoy_True(t *testing.T) { + t.Setenv(IngressControllerEnv, IngressControllerEnvoy) + + if !IsEnvoy() { + t.Error("Should fail") + } +} + +func TestIsEnvoy_DefaultFalse(t *testing.T) { + if IsEnvoy() { + t.Error("envoy should not be the default ingress controller when INGRESS_CONTROLLER_TYPE is unset") + } +} + +func TestGatewayName(t *testing.T) { + t.Setenv(GatewayNameEnv, "main-gateway") + + if GatewayName() != "main-gateway" { + t.Error("Should fail") + } +} + +func TestGatewayName_DefaultEmpty(t *testing.T) { + if GatewayName() != "" { + t.Error("Should fail") + } +} + +func TestGatewayNamespace(t *testing.T) { + t.Setenv(GatewayNamespaceEnv, "envoy-gateway-system") + + if GatewayNamespace() != "envoy-gateway-system" { + t.Error("Should fail") + } +} + +func TestGatewayNamespace_DefaultEmpty(t *testing.T) { + if GatewayNamespace() != "" { + t.Error("Should fail") + } +}