Skip to content

Commit 54b1fe6

Browse files
yuval-knfuden
andauthored
feat: support h2c app protocol (#10737)
Co-authored-by: Nathan Fudenberg <[email protected]>
1 parent 964ebab commit 54b1fe6

File tree

7 files changed

+118
-2
lines changed

7 files changed

+118
-2
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,7 @@ $(TEST_ASSET_DIR)/conformance/conformance_test.go:
691691
cat $(shell go list -json -m sigs.k8s.io/gateway-api | jq -r '.Dir')/conformance/conformance_test.go >> $@
692692
go fmt $@
693693

694-
CONFORMANCE_SUPPORTED_FEATURES ?= -supported-features=Gateway,ReferenceGrant,HTTPRoute,HTTPRouteQueryParamMatching,HTTPRouteMethodMatching,HTTPRouteResponseHeaderModification,HTTPRoutePortRedirect,HTTPRouteHostRewrite,HTTPRouteSchemeRedirect,HTTPRoutePathRedirect,HTTPRouteHostRewrite,HTTPRoutePathRewrite,HTTPRouteRequestMirror,TLSRoute
694+
CONFORMANCE_SUPPORTED_FEATURES ?= -supported-features=Gateway,ReferenceGrant,HTTPRoute,HTTPRouteQueryParamMatching,HTTPRouteMethodMatching,HTTPRouteResponseHeaderModification,HTTPRoutePortRedirect,HTTPRouteHostRewrite,HTTPRouteSchemeRedirect,HTTPRoutePathRedirect,HTTPRouteHostRewrite,HTTPRoutePathRewrite,HTTPRouteRequestMirror,TLSRoute,HTTPRouteBackendProtocolH2C
695695
CONFORMANCE_SUPPORTED_PROFILES ?= -conformance-profiles=GATEWAY-HTTP
696696
CONFORMANCE_GATEWAY_CLASS ?= kgateway
697697
CONFORMANCE_REPORT_ARGS ?= -report-output=$(TEST_ASSET_DIR)/conformance/$(VERSION)-report.yaml -organization=kgateway-dev -project=kgateway -version=$(VERSION) -url=github.com/kgateway-dev/kgateway -contact=github.com/kgateway-dev/kgateway/issues/new/choose

internal/kgateway/extensions2/plugins/kubernetes/k8s.go

+13
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ func NewPluginFromCollections(
5656
},
5757
Obj: svc,
5858
Port: port.Port,
59+
AppProtocol: translateAppProtocol(port.AppProtocol),
5960
GvPrefix: "kube",
6061
CanonicalHostname: fmt.Sprintf("%s.%s.svc.%s", svc.Name, svc.Namespace, clusterDomain),
6162
})
@@ -79,6 +80,18 @@ func NewPluginFromCollections(
7980
}
8081
}
8182

83+
func translateAppProtocol(appProtocol *string) ir.AppProtocol {
84+
if appProtocol == nil {
85+
return ir.DefaultAppProtocol
86+
}
87+
switch *appProtocol {
88+
case "kubernetes.io/h2c":
89+
return ir.HTTP2AppProtocol
90+
default:
91+
return ir.DefaultAppProtocol
92+
}
93+
}
94+
8295
func processUpstream(ctx context.Context, in ir.BackendObjectIR, out *envoy_config_cluster_v3.Cluster) {
8396
out.ClusterDiscoveryType = &envoy_config_cluster_v3.Cluster_Type{
8497
Type: envoy_config_cluster_v3.Cluster_EDS,

internal/kgateway/ir/backend.go

+9
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,21 @@ func (c ObjectSource) Equals(in ObjectSource) bool {
4747
return c.Namespace == in.Namespace && c.Name == in.Name && c.Group == in.Group && c.Kind == in.Kind
4848
}
4949

50+
type AppProtocol string
51+
52+
const (
53+
DefaultAppProtocol AppProtocol = ""
54+
HTTP2AppProtocol AppProtocol = "http2"
55+
)
56+
5057
type BackendObjectIR struct {
5158
// Ref to source object. sometimes the group and kind are not populated from api-server, so
5259
// set them explicitly here, and pass this around as the reference.
5360
ObjectSource `json:",inline"`
5461
// optional port for if ObjectSource is a service that can have multiple ports.
5562
Port int32
63+
// optional application protocol for the backend. Can be used to enable http2.
64+
AppProtocol AppProtocol
5665

5766
// prefix the cluster name with this string to distinguish it from other GVKs.
5867
// here explicitly as it shows up in stats. each (group, kind) pair should have a unique prefix.

internal/kgateway/setup/testdata/accesslog-filterhttp-httplisteneropt-out.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ clusters:
9797
typedConfig:
9898
'@type': type.googleapis.com/envoy.extensions.transport_sockets.raw_buffer.v3.RawBuffer
9999
type: EDS
100+
typedExtensionProtocolOptions:
101+
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
102+
'@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
103+
explicitHttpConfig:
104+
http2ProtocolOptions: {}
100105
- connectTimeout: 5s
101106
edsClusterConfig:
102107
edsConfig:

internal/kgateway/setup/testdata/accesslog-filterhttp-httplisteneropt.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ spec:
8787
ports:
8888
- name: grpc
8989
port: 50051
90+
appProtocol: kubernetes.io/h2c
9091
targetPort: 50051
9192
selector:
9293
app: log-test

internal/kgateway/translator/irtranslator/backend.go

+37-1
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@ import (
77

88
envoy_config_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
99
envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
10+
envoy_upstreams_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3"
11+
"google.golang.org/protobuf/proto"
12+
"google.golang.org/protobuf/types/known/anypb"
1013
"google.golang.org/protobuf/types/known/durationpb"
1114
"istio.io/istio/pkg/kube/krt"
1215

1316
"k8s.io/apimachinery/pkg/runtime/schema"
1417

1518
extensionsplug "github.com/kgateway-dev/kgateway/v2/internal/kgateway/extensions2/plugin"
1619
"github.com/kgateway-dev/kgateway/v2/internal/kgateway/ir"
20+
"github.com/kgateway-dev/kgateway/v2/internal/kgateway/utils"
1721
)
1822

1923
var (
@@ -78,6 +82,36 @@ func (t *BackendTranslator) runPlugins(
7882
}
7983
}
8084

85+
var (
86+
h2Options = func() *anypb.Any {
87+
http2ProtocolOptions := &envoy_upstreams_v3.HttpProtocolOptions{
88+
UpstreamProtocolOptions: &envoy_upstreams_v3.HttpProtocolOptions_ExplicitHttpConfig_{
89+
ExplicitHttpConfig: &envoy_upstreams_v3.HttpProtocolOptions_ExplicitHttpConfig{
90+
ProtocolConfig: &envoy_upstreams_v3.HttpProtocolOptions_ExplicitHttpConfig_Http2ProtocolOptions{
91+
Http2ProtocolOptions: &envoy_config_core_v3.Http2ProtocolOptions{},
92+
},
93+
},
94+
},
95+
}
96+
97+
a, err := utils.MessageToAny(http2ProtocolOptions)
98+
if err != nil {
99+
// should never happen - all values are known ahead of time.
100+
panic(err)
101+
}
102+
return a
103+
}()
104+
)
105+
106+
func translateAppProtocol(appProtocol ir.AppProtocol) map[string]*anypb.Any {
107+
typedExtensionProtocolOptions := map[string]*anypb.Any{}
108+
switch appProtocol {
109+
case ir.HTTP2AppProtocol:
110+
typedExtensionProtocolOptions["envoy.extensions.upstreams.http.v3.HttpProtocolOptions"] = proto.Clone(h2Options).(*anypb.Any)
111+
}
112+
return typedExtensionProtocolOptions
113+
}
114+
81115
// initializeCluster creates a default envoy cluster with minimal configuration,
82116
// that will then be augmented by various backend plugins
83117
func initializeCluster(u ir.BackendObjectIR) *envoy_config_cluster_v3.Cluster {
@@ -92,7 +126,9 @@ func initializeCluster(u ir.BackendObjectIR) *envoy_config_cluster_v3.Cluster {
92126
// defaults to Cluster_USE_CONFIGURED_PROTOCOL
93127
// ProtocolSelection: envoy_config_cluster_v3.Cluster_ClusterProtocolSelection(upstream.GetProtocolSelection()),
94128
// this field can be overridden by plugins
95-
ConnectTimeout: durationpb.New(ClusterConnectionTimeout),
129+
ConnectTimeout: durationpb.New(ClusterConnectionTimeout),
130+
TypedExtensionProtocolOptions: translateAppProtocol(u.AppProtocol),
131+
96132
// Http2ProtocolOptions: getHttp2options(upstream),
97133
// IgnoreHealthOnHostRemoval: upstream.GetIgnoreHealthOnHostRemoval().GetValue(),
98134
// RespectDnsTtl: upstream.GetRespectDnsTtl().GetValue(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package irtranslator_test
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
envoy_config_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
8+
envoy_upstreams_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3"
9+
"istio.io/istio/pkg/kube/krt"
10+
"k8s.io/apimachinery/pkg/runtime/schema"
11+
12+
. "github.com/onsi/gomega"
13+
14+
"github.com/kgateway-dev/kgateway/v2/internal/kgateway/ir"
15+
"github.com/kgateway-dev/kgateway/v2/internal/kgateway/translator/irtranslator"
16+
)
17+
18+
func testBInitBackend(ctx context.Context, in ir.BackendObjectIR, out *envoy_config_cluster_v3.Cluster) {
19+
}
20+
21+
func TestBackendTranslatorTranslatesAppProtocol(t *testing.T) {
22+
g := NewWithT(t)
23+
var bt irtranslator.BackendTranslator
24+
var ucc ir.UniqlyConnectedClient
25+
var kctx krt.TestingDummyContext
26+
backend := ir.BackendObjectIR{
27+
ObjectSource: ir.ObjectSource{
28+
Group: "group",
29+
Kind: "kind",
30+
Name: "name",
31+
Namespace: "namespace",
32+
},
33+
AppProtocol: ir.HTTP2AppProtocol,
34+
}
35+
bt.ContributedBackends = map[schema.GroupKind]ir.BackendInit{
36+
{Group: "group", Kind: "kind"}: {
37+
InitBackend: testBInitBackend,
38+
},
39+
}
40+
41+
c, err := bt.TranslateBackend(kctx, ucc, backend)
42+
g.Expect(err).NotTo(HaveOccurred())
43+
opts := c.GetTypedExtensionProtocolOptions()["envoy.extensions.upstreams.http.v3.HttpProtocolOptions"]
44+
g.Expect(opts).NotTo(BeNil())
45+
46+
p, err := opts.UnmarshalNew()
47+
g.Expect(err).NotTo(HaveOccurred())
48+
49+
httpOpts, ok := p.(*envoy_upstreams_v3.HttpProtocolOptions)
50+
g.Expect(ok).To(BeTrue())
51+
g.Expect(httpOpts.GetExplicitHttpConfig().GetHttp2ProtocolOptions()).NotTo(BeNil())
52+
}

0 commit comments

Comments
 (0)