Skip to content

Commit ea75066

Browse files
committed
Merge branch 'main' of github.com:kgateway-dev/ingress2gateway into fixssl
2 parents f22c823 + ef43e0d commit ea75066

30 files changed

Lines changed: 2272 additions & 63 deletions

Makefile

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,23 +38,32 @@ help:
3838
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-17s\033[0m %s\n", $$1, $$2}'
3939

4040
.PHONY: all
41-
all: vet fmt verify test build;$(info $(M)...Begin to test, verify and build this project.) @ ## Test, verify and build this project.
41+
all: vet fmt verify build test-all;$(info $(M)...Begin to test, verify and build this project.) @ ## Test, verify and build this project.
4242

4343
# Run go fmt against code
4444
.PHONY: fmt
4545
fmt: ;$(info $(M)...Begin to run go fmt against code.) @ ## Run go fmt against code.
46-
gofmt -w ./pkg ./cmd
46+
gofmt -w ./pkg ./cmd ./test
4747

4848
# Run go vet against code
4949
.PHONY: vet
5050
vet: ;$(info $(M)...Begin to run go vet against code.) @ ## Run go vet against code.
51-
go vet ./pkg/... ./cmd/...
51+
go vet ./pkg/... ./cmd/... ./test/...
5252

53-
# Run go test against code
53+
# Run integration tests
5454
.PHONY: test
55-
test: vet;$(info $(M)...Begin to run tests.) @ ## Run tests.
55+
test: vet;$(info $(M)...Begin to run integration tests.) @ ## Run integration tests.
5656
go test -race -cover ./pkg/... ./cmd/...
5757

58+
# Run e2e tests
59+
.PHONY: test-e2e
60+
test-e2e: vet;$(info $(M)...Begin to run e2e tests.) @ ## Run e2e tests.
61+
go test -v ./test/e2e/...
62+
63+
# Run integration and e2e tests
64+
.PHONY: test-all
65+
test-all: test test-e2e;$(info $(M)...Completed test-all.) @ ## Run integration and e2e tests.
66+
5867
# Build the binary
5968
.PHONY: build
6069
build: vet;$(info $(M)...Build the binary.) @ ## Build the binary.

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.25.3
44

55
require (
66
github.com/google/go-cmp v0.7.0
7-
github.com/kgateway-dev/kgateway/v2 v2.2.0-beta.1.0.20251203210329-f0eb663ac5bd
7+
github.com/kgateway-dev/kgateway/v2 v2.2.0-beta.3
88
github.com/olekukonko/tablewriter v0.0.5
99
github.com/samber/lo v1.39.0
1010
github.com/spf13/cobra v1.10.1

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm
5151
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
5252
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
5353
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
54-
github.com/kgateway-dev/kgateway/v2 v2.2.0-beta.1.0.20251203210329-f0eb663ac5bd h1:l6uyDclySJpQmXuT433sX/q1d1NpzYUCl6Nxbys5vGo=
55-
github.com/kgateway-dev/kgateway/v2 v2.2.0-beta.1.0.20251203210329-f0eb663ac5bd/go.mod h1:PTFdy5PWnh0b4FNvc4X93UkinNobzyct5oCtN6RNsV0=
54+
github.com/kgateway-dev/kgateway/v2 v2.2.0-beta.3 h1:+JISpfq0iPP1rWfLGBH+6b0GuWV7E1uJo6AR5uqE90U=
55+
github.com/kgateway-dev/kgateway/v2 v2.2.0-beta.3/go.mod h1:+WYYf8FRhr5YJ67+3DB0c3TzIntaGyLTtHQCVqzBkTk=
5656
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
5757
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
5858
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=

pkg/i2gw/implementations/kgateway/README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,15 @@ The command should generate Gateway API and Kgateway resources.
8585
- `nginx.ingress.kubernetes.io/session-cookie-max-age`: Sets the TTL/expiration time for the cookie (takes precedence over `session-cookie-expires`). Maps to `BackendConfigPolicy.spec.loadBalancer.ringHash.hashPolicies[].cookie.ttl`.
8686
- `nginx.ingress.kubernetes.io/session-cookie-secure`: Sets the Secure flag on the cookie. Maps to `BackendConfigPolicy.spec.loadBalancer.ringHash.hashPolicies[].cookie.secure`.
8787
- `nginx.ingress.kubernetes.io/service-upstream`: When set to `"true"`, configures Kgateway to route to the Service’s cluster IP (or equivalent static host) instead of individual Pod IPs. For each covered Service, the emitter creates a `Backend` resource with `spec.type: Static` and rewrites the corresponding `HTTPRoute.spec.rules[].backendRefs[]` to reference that `Backend` (group `gateway.kgateway.dev`, kind `Backend`).
88+
- `nginx.ingress.kubernetes.io/backend-protocol`: Indicates the L7 protocol that is used to communicate with the proxied backend.
89+
- **Supported values (mapped):** `GRPC`, `GRPCS`
90+
- If `service-upstream: "true"` is also set for the same Service backend, the emitter sets `spec.static.appProtocol: grpc` on the generated `Backend`.
91+
- Otherwise, the emitter does **not** create or modify Kubernetes `Service` resources. Instead, it emits an **INFO** notification with a `kubectl patch`
92+
command to update the existing Service port with `appProtocol: grpc`.
93+
- **Values treated as default HTTP/1.x (no-op):** `HTTP`, `HTTPS`, `AUTO_HTTP`
94+
- **Unsupported values (rejected by provider):** `FCGI` (and others)
95+
- **Safety note:** Because emitting Service manifests could overwrite user-managed Service configuration, ingress2gateway intentionally avoids generating
96+
Service resources for this annotation.
8897

8998
### External Auth
9099

@@ -186,14 +195,19 @@ Currently supported:
186195
- `spec.type: Static`
187196
- `spec.static.hosts` containing a single `{host, port}` entry derived from the Service (e.g. `myservice.default.svc.cluster.local:80`).
188197
- Matching `HTTPRoute.spec.rules[].backendRefs[]` are rewritten to reference this `Backend` instead of the core Service.
198+
- `nginx.ingress.kubernetes.io/backend-protocol`:
199+
- When set to `GRPC` or `GRPCS` **and** `service-upstream: "true"` is set for the same backend, the emitter stamps `spec.static.appProtocol: grpc` on the generated `Backend`.
200+
- When set to `GRPC` or `GRPCS` **without** `service-upstream: "true"`, the emitter emits an **INFO** notification that includes a `kubectl patch service ...`
201+
command to set `spec.ports[].appProtocol` on the existing Service.
202+
- `HTTP`, `HTTPS`, and `AUTO_HTTP` are treated as default HTTP/1.x behavior and do not emit additional config.
189203

190204
### Summary of Policy Types
191205

192206
| Annotation Type | Kgateway Resource |
193207
|------------------------------------|-----------------------|
194208
| Request/response behavior | `TrafficPolicy` |
195209
| Upstream connection behavior | `BackendConfigPolicy` |
196-
| Upstream representation (static IP)| `Backend` |
210+
| Upstream representation. | `Backend` |
197211
| TLS passthrough | `TLSRoute` |
198212

199213
## Limitations
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
Copyright 2023 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package kgateway
18+
19+
import (
20+
"github.com/kgateway-dev/ingress2gateway/pkg/i2gw/intermediate"
21+
kgw "github.com/kgateway-dev/kgateway/v2/api/v1alpha1/kgateway"
22+
23+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24+
"k8s.io/apimachinery/pkg/types"
25+
gwv1 "sigs.k8s.io/gateway-api/apis/v1"
26+
)
27+
28+
// sourceIngressAnnotation is used to label Backend CRs with the source Ingress name.
29+
const sourceIngressAnnotation = "ingress2gateway.kubernetes.io/source-ingress"
30+
31+
func backendNameForService(svcName string) string {
32+
return svcName + "-service-upstream"
33+
}
34+
35+
func backendKeyForService(ns, svcName string) types.NamespacedName {
36+
return types.NamespacedName{
37+
Namespace: ns,
38+
Name: backendNameForService(svcName),
39+
}
40+
}
41+
42+
// ensureStaticBackendForService ensures there is a Static kgateway.Backend for the given
43+
// service in the provided map. If it already exists, it is reused and optionally
44+
// updated with the given protocol. If not, a new Backend is created.
45+
//
46+
// ingressName is only used for the source-ingress label.
47+
func ensureStaticBackendForService(
48+
ingressName string,
49+
httpRouteKey types.NamespacedName,
50+
svcName string,
51+
host string,
52+
port int32,
53+
protocol *intermediate.BackendProtocol,
54+
backends map[types.NamespacedName]*kgw.Backend,
55+
) *kgw.Backend {
56+
backendKey := backendKeyForService(httpRouteKey.Namespace, svcName)
57+
58+
// Reuse existing Backend CR if present.
59+
if kb, ok := backends[backendKey]; ok {
60+
if protocol != nil && kb.Spec.Static != nil && kb.Spec.Static.AppProtocol == nil {
61+
switch *protocol {
62+
case intermediate.BackendProtocolGRPC:
63+
ap := kgw.AppProtocolGrpc
64+
kb.Spec.Static.AppProtocol = &ap
65+
}
66+
}
67+
return kb
68+
}
69+
70+
kb := &kgw.Backend{
71+
TypeMeta: metav1.TypeMeta{
72+
Kind: BackendGVK.Kind,
73+
APIVersion: BackendGVK.GroupVersion().String(),
74+
},
75+
ObjectMeta: metav1.ObjectMeta{
76+
Name: backendKey.Name,
77+
Namespace: backendKey.Namespace,
78+
Labels: map[string]string{
79+
sourceIngressAnnotation: ingressName,
80+
},
81+
},
82+
Spec: kgw.BackendSpec{
83+
Type: kgw.BackendTypeStatic,
84+
Static: &kgw.StaticBackend{
85+
Hosts: []kgw.Host{
86+
{
87+
Host: host,
88+
Port: gwv1.PortNumber(port),
89+
},
90+
},
91+
},
92+
},
93+
}
94+
95+
if protocol != nil {
96+
switch *protocol {
97+
case intermediate.BackendProtocolGRPC:
98+
ap := kgw.AppProtocolGrpc
99+
kb.Spec.Static.AppProtocol = &ap
100+
}
101+
}
102+
103+
backends[backendKey] = kb
104+
return kb
105+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
Copyright 2023 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package kgateway
18+
19+
import (
20+
"github.com/kgateway-dev/ingress2gateway/pkg/i2gw/intermediate"
21+
kgw "github.com/kgateway-dev/kgateway/v2/api/v1alpha1/kgateway"
22+
23+
"k8s.io/apimachinery/pkg/types"
24+
gwv1 "sigs.k8s.io/gateway-api/apis/v1"
25+
)
26+
27+
// applyBackendProtocol projects backend protocol metadata on IR Backends into
28+
// typed Kgateway Backend CRs and rewrites HTTPRoute backendRefs to reference
29+
// those Backends.
30+
//
31+
// Semantics:
32+
// - For each Policy.Backends entry produced by the ingress-nginx provider,
33+
// we ensure there is a Static Backend CR with the correct host / port and
34+
// (if set) appProtocol (currently only gRPC).
35+
// - HTTPRoute backendRefs that were originally core Services and are covered
36+
// by this Policy are rewritten to:
37+
// group: gateway.kgateway.dev
38+
// kind: Backend
39+
// name: <svc>-service-upstream
40+
// - This works regardless of whether service-upstream was also used.
41+
func applyBackendProtocol(
42+
pol intermediate.Policy,
43+
ingressName string,
44+
httpRouteKey types.NamespacedName,
45+
httpRouteCtx *intermediate.HTTPRouteContext,
46+
backends map[types.NamespacedName]*kgw.Backend,
47+
) {
48+
if len(pol.Backends) == 0 || len(pol.RuleBackendSources) == 0 {
49+
return
50+
}
51+
52+
for _, idx := range pol.RuleBackendSources {
53+
if idx.Rule >= len(httpRouteCtx.Spec.Rules) {
54+
continue
55+
}
56+
rule := &httpRouteCtx.Spec.Rules[idx.Rule]
57+
if idx.Backend >= len(rule.BackendRefs) {
58+
continue
59+
}
60+
61+
br := &rule.BackendRefs[idx.Backend]
62+
63+
// Only core Service backends.
64+
if br.BackendRef.Group != nil && *br.BackendRef.Group != "" {
65+
continue
66+
}
67+
if br.BackendRef.Kind != nil && *br.BackendRef.Kind != "Service" {
68+
continue
69+
}
70+
if br.BackendRef.Name == "" {
71+
continue
72+
}
73+
74+
svcName := string(br.BackendRef.Name)
75+
if svcName == "" {
76+
continue
77+
}
78+
79+
backendKey := backendKeyForService(httpRouteKey.Namespace, svcName)
80+
81+
be, ok := pol.Backends[backendKey]
82+
if !ok {
83+
// Provider did not produce metadata for this Service backend.
84+
continue
85+
}
86+
87+
// Ensure / create the typed Backend CR (host, port, protocol).
88+
kb := ensureStaticBackendForService(
89+
ingressName,
90+
httpRouteKey,
91+
svcName,
92+
be.Host,
93+
be.Port,
94+
be.Protocol,
95+
backends,
96+
)
97+
98+
// Rewrite BackendRef to point to this Backend.
99+
group := gwv1.Group(BackendGVK.Group)
100+
kind := gwv1.Kind(BackendGVK.Kind)
101+
102+
br.BackendRef.Group = &group
103+
br.BackendRef.Kind = &kind
104+
br.BackendRef.Name = gwv1.ObjectName(kb.Name)
105+
// Backend controls the port.
106+
br.BackendRef.Port = nil
107+
}
108+
}

0 commit comments

Comments
 (0)