From faf43e7a335e624ffd702d1415386cef98ad14ef Mon Sep 17 00:00:00 2001 From: Adam Janikowski <12255597+ajanikow@users.noreply.github.com> Date: Fri, 29 Nov 2024 10:27:30 +0100 Subject: [PATCH] [Feature] [Networking] ArangoRoute Protocol (#1773) --- CHANGELOG.md | 1 + docs/api/ArangoRoute.V1Alpha1.md | 30 +++- .../v1alpha1/route_spec_destination.go | 16 ++ .../v1alpha1/route_spec_protocol.go | 55 +++++++ .../v1alpha1/route_status_target.go | 3 + .../v1alpha1/zz_generated.deepcopy.go | 5 + .../networking-route.schema.generated.yaml | 9 ++ .../gateway/gateway_config_destination.go | 21 +-- .../gateway_config_destination_protocol.go | 101 +++++++++++++ .../route/handler_destination_endpoints.go | 1 + .../handler_destination_endpoints_test.go | 143 ++++++++++++++++++ .../route/handler_destination_service.go | 1 + .../route/handler_destination_service_test.go | 109 +++++++++++++ 13 files changed, 475 insertions(+), 20 deletions(-) create mode 100644 pkg/apis/networking/v1alpha1/route_spec_protocol.go create mode 100644 pkg/deployment/resources/gateway/gateway_config_destination_protocol.go diff --git a/CHANGELOG.md b/CHANGELOG.md index ee893c79c..26cd02704 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ - (Feature) (Platform) Chart Integration - (Maintenance) Switch to google.golang.org/protobuf - (Feature) Add DebugPackage to the OPS Binary +- (Feature) (Networking) ArangoRoute Protocol ## [1.2.43](https://github.com/arangodb/kube-arangodb/tree/1.2.43) (2024-10-14) - (Feature) ArangoRoute CRD diff --git a/docs/api/ArangoRoute.V1Alpha1.md b/docs/api/ArangoRoute.V1Alpha1.md index 2c358b396..3a6c98721 100644 --- a/docs/api/ArangoRoute.V1Alpha1.md +++ b/docs/api/ArangoRoute.V1Alpha1.md @@ -83,18 +83,34 @@ UID keeps the information about object UID ### .spec.destination.path -Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.43/pkg/apis/networking/v1alpha1/route_spec_destination.go#L39) +Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.43/pkg/apis/networking/v1alpha1/route_spec_destination.go#L46) Path defines service path used for overrides *** +### .spec.destination.protocol + +Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.43/pkg/apis/networking/v1alpha1/route_spec_destination.go#L40) + +Protocol defines http protocol used for the route + +Possible Values: +* `"http1"` (default) - HTTP 1.1 Protocol +* `"http2"` - HTTP 2 Protocol + +*** + ### .spec.destination.schema -Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.43/pkg/apis/networking/v1alpha1/route_spec_destination.go#L33) +Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.43/pkg/apis/networking/v1alpha1/route_spec_destination.go#L35) Schema defines HTTP/S schema used for connection +Possible Values: +* `"http"` (default) - HTTP Connection +* `"https"` - HTTPS Connection (HTTP with TLS) + *** ### .spec.destination.service.checksum @@ -238,12 +254,20 @@ Type: `integer` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1. ### .status.target.path -Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.43/pkg/apis/networking/v1alpha1/route_status_target.go#L43) +Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.43/pkg/apis/networking/v1alpha1/route_status_target.go#L46) Path specifies request path override *** +### .status.target.protocol + +Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.43/pkg/apis/networking/v1alpha1/route_status_target.go#L40) + +Protocol defines http protocol used for the route + +*** + ### .status.target.TLS.insecure Type: `boolean` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.43/pkg/apis/networking/v1alpha1/route_status_target_tls.go#L27) diff --git a/pkg/apis/networking/v1alpha1/route_spec_destination.go b/pkg/apis/networking/v1alpha1/route_spec_destination.go index 1dfa0ab75..c4d32f683 100644 --- a/pkg/apis/networking/v1alpha1/route_spec_destination.go +++ b/pkg/apis/networking/v1alpha1/route_spec_destination.go @@ -30,8 +30,15 @@ type ArangoRouteSpecDestination struct { Endpoints *ArangoRouteSpecDestinationEndpoints `json:"endpoints,omitempty"` // Schema defines HTTP/S schema used for connection + // +doc/enum: http|HTTP Connection + // +doc/enum: https|HTTPS Connection (HTTP with TLS) Schema *ArangoRouteSpecDestinationSchema `json:"schema,omitempty"` + // Protocol defines http protocol used for the route + // +doc/enum: http1|HTTP 1.1 Protocol + // +doc/enum: http2|HTTP 2 Protocol + Protocol *ArangoRouteDestinationProtocol `json:"protocol,omitempty"` + // TLS defines TLS Configuration TLS *ArangoRouteSpecDestinationTLS `json:"tls,omitempty"` @@ -58,6 +65,14 @@ func (a *ArangoRouteSpecDestination) GetEndpoints() *ArangoRouteSpecDestinationE return a.Endpoints } +func (a *ArangoRouteSpecDestination) GetProtocol() *ArangoRouteDestinationProtocol { + if a == nil || a.Protocol == nil { + return nil + } + + return a.Protocol +} + func (a *ArangoRouteSpecDestination) GetSchema() *ArangoRouteSpecDestinationSchema { if a == nil || a.Schema == nil { return nil @@ -100,6 +115,7 @@ func (a *ArangoRouteSpecDestination) Validate() error { shared.ValidateOptionalInterfacePath("service", a.Service), shared.ValidateOptionalInterfacePath("endpoints", a.Endpoints), shared.ValidateOptionalInterfacePath("schema", a.Schema), + shared.ValidateOptionalInterfacePath("protocol", a.Protocol), shared.ValidateOptionalInterfacePath("tls", a.TLS), shared.ValidateOptionalInterfacePath("authentication", a.Authentication), shared.PrefixResourceError("path", shared.ValidateAPIPath(a.GetPath())), diff --git a/pkg/apis/networking/v1alpha1/route_spec_protocol.go b/pkg/apis/networking/v1alpha1/route_spec_protocol.go new file mode 100644 index 000000000..d83fceac6 --- /dev/null +++ b/pkg/apis/networking/v1alpha1/route_spec_protocol.go @@ -0,0 +1,55 @@ +// +// DISCLAIMER +// +// Copyright 2024 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// + +package v1alpha1 + +import ( + "github.com/arangodb/kube-arangodb/pkg/util/errors" + "github.com/arangodb/kube-arangodb/pkg/util/strings" +) + +type ArangoRouteDestinationProtocol string + +const ( + ArangoRouteDestinationProtocolHTTP1 ArangoRouteDestinationProtocol = "http1" + ArangoRouteDestinationProtocolHTTP2 ArangoRouteDestinationProtocol = "http2" + ArangoRouteDestinationProtocolDefault = ArangoRouteDestinationProtocolHTTP1 +) + +func (a *ArangoRouteDestinationProtocol) Get() ArangoRouteDestinationProtocol { + if a == nil { + return ArangoRouteDestinationProtocolDefault + } + + return ArangoRouteDestinationProtocol(strings.ToLower(string(*a))) +} + +func (a *ArangoRouteDestinationProtocol) String() string { + return string(a.Get()) +} + +func (a *ArangoRouteDestinationProtocol) Validate() error { + switch x := a.Get(); x { + case ArangoRouteDestinationProtocolHTTP1, ArangoRouteDestinationProtocolHTTP2: + return nil + default: + return errors.Errorf("Invalid schema: %s", x.String()) + } +} diff --git a/pkg/apis/networking/v1alpha1/route_status_target.go b/pkg/apis/networking/v1alpha1/route_status_target.go index 8ec626b07..a90021de4 100644 --- a/pkg/apis/networking/v1alpha1/route_status_target.go +++ b/pkg/apis/networking/v1alpha1/route_status_target.go @@ -36,6 +36,9 @@ type ArangoRouteStatusTarget struct { // TLS Keeps target TLS Settings (if not nil, TLS is enabled) TLS *ArangoRouteStatusTargetTLS `json:"TLS,omitempty"` + // Protocol defines http protocol used for the route + Protocol ArangoRouteDestinationProtocol `json:"protocol,omitempty"` + // Authentication specifies the authentication details Authentication ArangoRouteStatusTargetAuthentication `json:"authentication,omitempty"` diff --git a/pkg/apis/networking/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/networking/v1alpha1/zz_generated.deepcopy.go index d69bebac6..2aef9bdb2 100644 --- a/pkg/apis/networking/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/networking/v1alpha1/zz_generated.deepcopy.go @@ -147,6 +147,11 @@ func (in *ArangoRouteSpecDestination) DeepCopyInto(out *ArangoRouteSpecDestinati *out = new(ArangoRouteSpecDestinationSchema) **out = **in } + if in.Protocol != nil { + in, out := &in.Protocol, &out.Protocol + *out = new(ArangoRouteDestinationProtocol) + **out = **in + } if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(ArangoRouteSpecDestinationTLS) diff --git a/pkg/crd/crds/networking-route.schema.generated.yaml b/pkg/crd/crds/networking-route.schema.generated.yaml index 2544f6324..d8ba4e7d7 100644 --- a/pkg/crd/crds/networking-route.schema.generated.yaml +++ b/pkg/crd/crds/networking-route.schema.generated.yaml @@ -49,8 +49,17 @@ v1alpha1: path: description: Path defines service path used for overrides type: string + protocol: + description: Protocol defines http protocol used for the route + enum: + - http1 + - http2 + type: string schema: description: Schema defines HTTP/S schema used for connection + enum: + - http + - https type: string service: description: Service defines service upstream reference diff --git a/pkg/deployment/resources/gateway/gateway_config_destination.go b/pkg/deployment/resources/gateway/gateway_config_destination.go index 602ede7ac..5963c0569 100644 --- a/pkg/deployment/resources/gateway/gateway_config_destination.go +++ b/pkg/deployment/resources/gateway/gateway_config_destination.go @@ -24,10 +24,8 @@ import ( "time" clusterAPI "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" - coreAPI "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" endpointAPI "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" routeAPI "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - upstreamHttpApi "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/durationpb" @@ -63,6 +61,8 @@ type ConfigDestination struct { Type *ConfigDestinationType `json:"type,omitempty"` + Protocol *ConfigDestinationProtocol `json:"protocol,omitempty"` + Path *string `json:"path,omitempty"` AuthExtension *ConfigAuthZExtension `json:"authExtension,omitempty"` @@ -77,6 +77,7 @@ func (c *ConfigDestination) Validate() error { return shared.WithErrors( shared.PrefixResourceError("targets", c.Targets.Validate()), shared.PrefixResourceError("type", c.Type.Validate()), + shared.PrefixResourceError("protocol", c.Protocol.Validate()), shared.PrefixResourceError("path", shared.ValidateAPIPath(c.GetPath())), shared.PrefixResourceError("authExtension", c.AuthExtension.Validate()), shared.PrefixResourceError("upgradeConfigs", c.UpgradeConfigs.Validate()), @@ -130,21 +131,7 @@ func (c *ConfigDestination) getUpgradeConfigs() ConfigDestinationsUpgrade { } func (c *ConfigDestination) RenderCluster(name string) (*clusterAPI.Cluster, error) { - hpo, err := anypb.New(&upstreamHttpApi.HttpProtocolOptions{ - UpstreamProtocolOptions: &upstreamHttpApi.HttpProtocolOptions_ExplicitHttpConfig_{ - ExplicitHttpConfig: &upstreamHttpApi.HttpProtocolOptions_ExplicitHttpConfig{ - ProtocolConfig: &upstreamHttpApi.HttpProtocolOptions_ExplicitHttpConfig_Http2ProtocolOptions{ - Http2ProtocolOptions: &coreAPI.Http2ProtocolOptions{ - ConnectionKeepalive: &coreAPI.KeepaliveSettings{ - Interval: durationpb.New(15 * time.Second), - Timeout: durationpb.New(30 * time.Second), - ConnectionIdleInterval: durationpb.New(60 * time.Second), - }, - }, - }, - }, - }, - }) + hpo, err := anypb.New(c.Protocol.Options()) if err != nil { return nil, err } diff --git a/pkg/deployment/resources/gateway/gateway_config_destination_protocol.go b/pkg/deployment/resources/gateway/gateway_config_destination_protocol.go new file mode 100644 index 000000000..e3c7ab06a --- /dev/null +++ b/pkg/deployment/resources/gateway/gateway_config_destination_protocol.go @@ -0,0 +1,101 @@ +// +// DISCLAIMER +// +// Copyright 2024 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// + +package gateway + +import ( + "time" + + coreAPI "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + upstreamHttpApi "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3" + "google.golang.org/protobuf/types/known/durationpb" + + "github.com/arangodb/kube-arangodb/pkg/util/errors" +) + +type ConfigDestinationProtocol int + +const ( + ConfigDestinationProtocolHTTP1 ConfigDestinationProtocol = iota + ConfigDestinationProtocolHTTP2 +) + +func (c *ConfigDestinationProtocol) Get() ConfigDestinationProtocol { + if c == nil { + return ConfigDestinationProtocolHTTP1 + } + + switch v := *c; v { + case ConfigDestinationProtocolHTTP1, ConfigDestinationProtocolHTTP2: + return v + default: + return ConfigDestinationProtocolHTTP1 + } +} + +func (c *ConfigDestinationProtocol) Options() *upstreamHttpApi.HttpProtocolOptions { + switch c.Get() { + case ConfigDestinationProtocolHTTP1: + return &upstreamHttpApi.HttpProtocolOptions{ + UpstreamProtocolOptions: &upstreamHttpApi.HttpProtocolOptions_ExplicitHttpConfig_{ + ExplicitHttpConfig: &upstreamHttpApi.HttpProtocolOptions_ExplicitHttpConfig{ + ProtocolConfig: &upstreamHttpApi.HttpProtocolOptions_ExplicitHttpConfig_HttpProtocolOptions{ + HttpProtocolOptions: &coreAPI.Http1ProtocolOptions{}, + }, + }, + }, + } + case ConfigDestinationProtocolHTTP2: + return &upstreamHttpApi.HttpProtocolOptions{ + UpstreamProtocolOptions: &upstreamHttpApi.HttpProtocolOptions_ExplicitHttpConfig_{ + ExplicitHttpConfig: &upstreamHttpApi.HttpProtocolOptions_ExplicitHttpConfig{ + ProtocolConfig: &upstreamHttpApi.HttpProtocolOptions_ExplicitHttpConfig_Http2ProtocolOptions{ + Http2ProtocolOptions: &coreAPI.Http2ProtocolOptions{ + ConnectionKeepalive: &coreAPI.KeepaliveSettings{ + Interval: durationpb.New(15 * time.Second), + Timeout: durationpb.New(30 * time.Second), + ConnectionIdleInterval: durationpb.New(60 * time.Second), + }, + }, + }, + }, + }, + } + default: + return &upstreamHttpApi.HttpProtocolOptions{ + UpstreamProtocolOptions: &upstreamHttpApi.HttpProtocolOptions_ExplicitHttpConfig_{ + ExplicitHttpConfig: &upstreamHttpApi.HttpProtocolOptions_ExplicitHttpConfig{ + ProtocolConfig: &upstreamHttpApi.HttpProtocolOptions_ExplicitHttpConfig_HttpProtocolOptions{ + HttpProtocolOptions: &coreAPI.Http1ProtocolOptions{}, + }, + }, + }, + } + } +} + +func (c *ConfigDestinationProtocol) Validate() error { + switch c.Get() { + case ConfigDestinationProtocolHTTP1, ConfigDestinationProtocolHTTP2: + return nil + default: + return errors.Errorf("Invalid destination protocol") + } +} diff --git a/pkg/handlers/networking/route/handler_destination_endpoints.go b/pkg/handlers/networking/route/handler_destination_endpoints.go index c67a9a8a1..b6eedf8d7 100644 --- a/pkg/handlers/networking/route/handler_destination_endpoints.go +++ b/pkg/handlers/networking/route/handler_destination_endpoints.go @@ -120,6 +120,7 @@ func (h *handler) HandleArangoDestinationEndpoints(ctx context.Context, item ope target.Path = dest.GetPath() target.Type = networkingApi.ArangoRouteStatusTargetEndpointsType + target.Protocol = dest.GetProtocol().Get() // Render Auth Settings diff --git a/pkg/handlers/networking/route/handler_destination_endpoints_test.go b/pkg/handlers/networking/route/handler_destination_endpoints_test.go index 469ce724d..3732b5dbb 100644 --- a/pkg/handlers/networking/route/handler_destination_endpoints_test.go +++ b/pkg/handlers/networking/route/handler_destination_endpoints_test.go @@ -96,6 +96,149 @@ func Test_Handler_Destination_Endpoints_Valid(t *testing.T) { require.Len(t, extension.Status.Target.RenderURLs(), 1) require.EqualValues(t, "http://127.0.0.1:10244/", extension.Status.Target.RenderURLs()[0]) + require.EqualValues(t, "http1", extension.Status.Target.Protocol) + + c, ok := extension.Status.Conditions.Get(networkingApi.DestinationValidCondition) + require.True(t, ok) + require.EqualValues(t, c.Reason, "Destination Found") + require.EqualValues(t, c.Reason, "Destination Found") + require.EqualValues(t, c.Hash, extension.Status.Target.Hash()) +} + +func Test_Handler_Destination_Endpoints_Valid_HTTP1(t *testing.T) { + // Setup + handler := newFakeHandler() + + // Arrange + extension := tests.NewMetaObject[*networkingApi.ArangoRoute](t, tests.FakeNamespace, "test", + func(t *testing.T, obj *networkingApi.ArangoRoute) { + obj.Spec.Deployment = util.NewType("deployment") + }, + func(t *testing.T, obj *networkingApi.ArangoRoute) { + obj.Spec.Destination = &networkingApi.ArangoRouteSpecDestination{ + Protocol: util.NewType(networkingApi.ArangoRouteDestinationProtocolHTTP1), + Endpoints: &networkingApi.ArangoRouteSpecDestinationEndpoints{ + Object: &sharedApi.Object{ + Name: "deployment", + }, + Port: util.NewType(intstr.FromInt32(10244)), + }, + } + }) + deployment := tests.NewMetaObject[*api.ArangoDeployment](t, tests.FakeNamespace, "deployment") + svc := tests.NewMetaObject[*core.Service](t, tests.FakeNamespace, "deployment", func(t *testing.T, obj *core.Service) { + obj.Spec.Ports = []core.ServicePort{ + { + Port: 10244, + }, + } + }) + endpoints := tests.NewMetaObject[*core.Endpoints](t, tests.FakeNamespace, "deployment", func(t *testing.T, obj *core.Endpoints) { + obj.Subsets = []core.EndpointSubset{ + { + Addresses: []core.EndpointAddress{ + { + IP: "127.0.0.1", + }, + }, + Ports: []core.EndpointPort{ + { + Name: "", + Port: 10244, + }, + }, + }, + } + }) + + refresh := tests.CreateObjects(t, handler.kubeClient, handler.client, &deployment, &extension, &svc, &endpoints) + + // Test + require.NoError(t, tests.Handle(handler, tests.NewItem(t, operation.Update, extension))) + + // Refresh + refresh(t) + + // Assert + require.True(t, extension.Status.Conditions.IsTrue(networkingApi.SpecValidCondition)) + require.True(t, extension.Status.Conditions.IsTrue(networkingApi.DestinationValidCondition)) + require.True(t, extension.Status.Conditions.IsTrue(networkingApi.ReadyCondition)) + require.Equal(t, networkingApi.ArangoRouteStatusTargetEndpointsType, extension.Status.Target.Type) + + require.Len(t, extension.Status.Target.RenderURLs(), 1) + require.EqualValues(t, "http://127.0.0.1:10244/", extension.Status.Target.RenderURLs()[0]) + require.EqualValues(t, "http1", extension.Status.Target.Protocol) + + c, ok := extension.Status.Conditions.Get(networkingApi.DestinationValidCondition) + require.True(t, ok) + require.EqualValues(t, c.Reason, "Destination Found") + require.EqualValues(t, c.Reason, "Destination Found") + require.EqualValues(t, c.Hash, extension.Status.Target.Hash()) +} + +func Test_Handler_Destination_Endpoints_Valid_HTTP2(t *testing.T) { + // Setup + handler := newFakeHandler() + + // Arrange + extension := tests.NewMetaObject[*networkingApi.ArangoRoute](t, tests.FakeNamespace, "test", + func(t *testing.T, obj *networkingApi.ArangoRoute) { + obj.Spec.Deployment = util.NewType("deployment") + }, + func(t *testing.T, obj *networkingApi.ArangoRoute) { + obj.Spec.Destination = &networkingApi.ArangoRouteSpecDestination{ + Protocol: util.NewType(networkingApi.ArangoRouteDestinationProtocolHTTP2), + Endpoints: &networkingApi.ArangoRouteSpecDestinationEndpoints{ + Object: &sharedApi.Object{ + Name: "deployment", + }, + Port: util.NewType(intstr.FromInt32(10244)), + }, + } + }) + deployment := tests.NewMetaObject[*api.ArangoDeployment](t, tests.FakeNamespace, "deployment") + svc := tests.NewMetaObject[*core.Service](t, tests.FakeNamespace, "deployment", func(t *testing.T, obj *core.Service) { + obj.Spec.Ports = []core.ServicePort{ + { + Port: 10244, + }, + } + }) + endpoints := tests.NewMetaObject[*core.Endpoints](t, tests.FakeNamespace, "deployment", func(t *testing.T, obj *core.Endpoints) { + obj.Subsets = []core.EndpointSubset{ + { + Addresses: []core.EndpointAddress{ + { + IP: "127.0.0.1", + }, + }, + Ports: []core.EndpointPort{ + { + Name: "", + Port: 10244, + }, + }, + }, + } + }) + + refresh := tests.CreateObjects(t, handler.kubeClient, handler.client, &deployment, &extension, &svc, &endpoints) + + // Test + require.NoError(t, tests.Handle(handler, tests.NewItem(t, operation.Update, extension))) + + // Refresh + refresh(t) + + // Assert + require.True(t, extension.Status.Conditions.IsTrue(networkingApi.SpecValidCondition)) + require.True(t, extension.Status.Conditions.IsTrue(networkingApi.DestinationValidCondition)) + require.True(t, extension.Status.Conditions.IsTrue(networkingApi.ReadyCondition)) + require.Equal(t, networkingApi.ArangoRouteStatusTargetEndpointsType, extension.Status.Target.Type) + + require.Len(t, extension.Status.Target.RenderURLs(), 1) + require.EqualValues(t, "http://127.0.0.1:10244/", extension.Status.Target.RenderURLs()[0]) + require.EqualValues(t, "http2", extension.Status.Target.Protocol) c, ok := extension.Status.Conditions.Get(networkingApi.DestinationValidCondition) require.True(t, ok) diff --git a/pkg/handlers/networking/route/handler_destination_service.go b/pkg/handlers/networking/route/handler_destination_service.go index de56f4efb..cc3867c9f 100644 --- a/pkg/handlers/networking/route/handler_destination_service.go +++ b/pkg/handlers/networking/route/handler_destination_service.go @@ -119,6 +119,7 @@ func (h *handler) HandleArangoDestinationService(ctx context.Context, item opera target.Path = dest.GetPath() target.Type = networkingApi.ArangoRouteStatusTargetServiceType + target.Protocol = dest.GetProtocol().Get() // Render Auth Settings diff --git a/pkg/handlers/networking/route/handler_destination_service_test.go b/pkg/handlers/networking/route/handler_destination_service_test.go index c62b354f2..0b960be69 100644 --- a/pkg/handlers/networking/route/handler_destination_service_test.go +++ b/pkg/handlers/networking/route/handler_destination_service_test.go @@ -119,6 +119,115 @@ func Test_Handler_Destination_Service_Valid(t *testing.T) { require.Len(t, extension.Status.Target.RenderURLs(), 1) require.EqualValues(t, "http://deployment.fake.svc:10244/", extension.Status.Target.RenderURLs()[0]) + require.EqualValues(t, "http1", extension.Status.Target.Protocol) + + c, ok := extension.Status.Conditions.Get(networkingApi.DestinationValidCondition) + require.True(t, ok) + require.EqualValues(t, c.Reason, "Destination Found") + require.EqualValues(t, c.Reason, "Destination Found") + require.EqualValues(t, c.Hash, extension.Status.Target.Hash()) +} + +func Test_Handler_Destination_Service_Valid_HTTP1(t *testing.T) { + // Setup + handler := newFakeHandler() + + // Arrange + extension := tests.NewMetaObject[*networkingApi.ArangoRoute](t, tests.FakeNamespace, "test", + func(t *testing.T, obj *networkingApi.ArangoRoute) { + obj.Spec.Deployment = util.NewType("deployment") + }, + func(t *testing.T, obj *networkingApi.ArangoRoute) { + obj.Spec.Destination = &networkingApi.ArangoRouteSpecDestination{ + Protocol: util.NewType(networkingApi.ArangoRouteDestinationProtocolHTTP1), + Service: &networkingApi.ArangoRouteSpecDestinationService{ + Object: &sharedApi.Object{ + Name: "deployment", + }, + Port: util.NewType(intstr.FromInt32(10244)), + }, + } + }) + deployment := tests.NewMetaObject[*api.ArangoDeployment](t, tests.FakeNamespace, "deployment") + svc := tests.NewMetaObject[*core.Service](t, tests.FakeNamespace, "deployment", func(t *testing.T, obj *core.Service) { + obj.Spec.Ports = []core.ServicePort{ + { + Port: 10244, + }, + } + }) + + refresh := tests.CreateObjects(t, handler.kubeClient, handler.client, &deployment, &extension, &svc) + + // Test + require.NoError(t, tests.Handle(handler, tests.NewItem(t, operation.Update, extension))) + + // Refresh + refresh(t) + + // Assert + require.True(t, extension.Status.Conditions.IsTrue(networkingApi.SpecValidCondition)) + require.True(t, extension.Status.Conditions.IsTrue(networkingApi.DestinationValidCondition)) + require.True(t, extension.Status.Conditions.IsTrue(networkingApi.ReadyCondition)) + require.Equal(t, networkingApi.ArangoRouteStatusTargetServiceType, extension.Status.Target.Type) + + require.Len(t, extension.Status.Target.RenderURLs(), 1) + require.EqualValues(t, "http://deployment.fake.svc:10244/", extension.Status.Target.RenderURLs()[0]) + require.EqualValues(t, "http1", extension.Status.Target.Protocol) + + c, ok := extension.Status.Conditions.Get(networkingApi.DestinationValidCondition) + require.True(t, ok) + require.EqualValues(t, c.Reason, "Destination Found") + require.EqualValues(t, c.Reason, "Destination Found") + require.EqualValues(t, c.Hash, extension.Status.Target.Hash()) +} + +func Test_Handler_Destination_Service_Valid_HTTP2(t *testing.T) { + // Setup + handler := newFakeHandler() + + // Arrange + extension := tests.NewMetaObject[*networkingApi.ArangoRoute](t, tests.FakeNamespace, "test", + func(t *testing.T, obj *networkingApi.ArangoRoute) { + obj.Spec.Deployment = util.NewType("deployment") + }, + func(t *testing.T, obj *networkingApi.ArangoRoute) { + obj.Spec.Destination = &networkingApi.ArangoRouteSpecDestination{ + Protocol: util.NewType(networkingApi.ArangoRouteDestinationProtocolHTTP2), + Service: &networkingApi.ArangoRouteSpecDestinationService{ + Object: &sharedApi.Object{ + Name: "deployment", + }, + Port: util.NewType(intstr.FromInt32(10244)), + }, + } + }) + deployment := tests.NewMetaObject[*api.ArangoDeployment](t, tests.FakeNamespace, "deployment") + svc := tests.NewMetaObject[*core.Service](t, tests.FakeNamespace, "deployment", func(t *testing.T, obj *core.Service) { + obj.Spec.Ports = []core.ServicePort{ + { + Port: 10244, + }, + } + }) + + refresh := tests.CreateObjects(t, handler.kubeClient, handler.client, &deployment, &extension, &svc) + + // Test + require.NoError(t, tests.Handle(handler, tests.NewItem(t, operation.Update, extension))) + + // Refresh + refresh(t) + + // Assert + require.True(t, extension.Status.Conditions.IsTrue(networkingApi.SpecValidCondition)) + require.True(t, extension.Status.Conditions.IsTrue(networkingApi.DestinationValidCondition)) + require.True(t, extension.Status.Conditions.IsTrue(networkingApi.ReadyCondition)) + require.Equal(t, networkingApi.ArangoRouteStatusTargetServiceType, extension.Status.Target.Type) + + require.Len(t, extension.Status.Target.RenderURLs(), 1) + require.EqualValues(t, "http://deployment.fake.svc:10244/", extension.Status.Target.RenderURLs()[0]) + require.EqualValues(t, "http2", extension.Status.Target.Protocol) c, ok := extension.Status.Conditions.Get(networkingApi.DestinationValidCondition) require.True(t, ok)