Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(translator): implement backend API #3495

Merged
merged 13 commits into from
Jun 4, 2024
13 changes: 0 additions & 13 deletions api/v1alpha1/backend_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ const (
// AppProtocolType defines various backend applications protocols supported by Envoy Gateway
//
// +kubebuilder:validation:Enum=gateway.envoyproxy.io/h2c;gateway.envoyproxy.io/ws;gateway.envoyproxy.io/wss
// +notImplementedHide
type AppProtocolType string

const (
Expand All @@ -37,7 +36,6 @@ const (
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Accepted")].reason`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// +notImplementedHide
type Backend struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Expand All @@ -54,7 +52,6 @@ type Backend struct {
//
// +kubebuilder:validation:XValidation:rule="(has(self.fqdn) || has(self.ipv4) || has(self.unix))",message="one of fqdn, ipv4 or unix must be specified"
// +kubebuilder:validation:XValidation:rule="((has(self.fqdn) && !(has(self.ipv4) || has(self.unix))) || (has(self.ipv4) && !(has(self.fqdn) || has(self.unix))) || (has(self.unix) && !(has(self.ipv4) || has(self.fqdn))))",message="only one of fqdn, ipv4 or unix can be specified"
// +notImplementedHide
type BackendEndpoint struct {
// FQDN defines a FQDN endpoint
//
Expand All @@ -74,8 +71,6 @@ type BackendEndpoint struct {

// IPv4Endpoint describes TCP/UDP socket address, corresponding to Envoy's Socket Address
// https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/address.proto#config-core-v3-socketaddress
//
// +notImplementedHide
type IPv4Endpoint struct {
// Address defines the IPv4 address of the backend endpoint.
//
Expand All @@ -93,8 +88,6 @@ type IPv4Endpoint struct {

// FQDNEndpoint describes TCP/UDP socket address, corresponding to Envoy's Socket Address
// https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/address.proto#config-core-v3-socketaddress
//
// +notImplementedHide
type FQDNEndpoint struct {
// Hostname defines the FQDN hostname of the backend endpoint.
//
Expand All @@ -112,16 +105,12 @@ type FQDNEndpoint struct {

// UnixSocket describes TCP/UDP unix domain socket address, corresponding to Envoy's Pipe
// https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/address.proto#config-core-v3-pipe
//
// +notImplementedHide
type UnixSocket struct {
// Path defines the unix domain socket path of the backend endpoint.
Path string `json:"path"`
}

// BackendSpec describes the desired state of BackendSpec.
//
// +notImplementedHide
type BackendSpec struct {
// Endpoints defines the endpoints to be used when connecting to the backend.
//
Expand Down Expand Up @@ -167,7 +156,6 @@ const (
)

// BackendStatus defines the state of Backend
// +notImplementedHide
type BackendStatus struct {
// Conditions describe the current conditions of the Backend.
//
Expand All @@ -181,7 +169,6 @@ type BackendStatus struct {
// BackendList contains a list of Backend resources.
//
// +kubebuilder:object:root=true
// +notImplementedHide
type BackendList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Expand Down
4 changes: 2 additions & 2 deletions api/v1alpha1/ext_proc_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ type ExtProc struct {
//
// +kubebuilder:validation:MinItems=1
// +kubebuilder:validation:MaxItems=1
// +kubebuilder:validation:XValidation:message="BackendRefs only supports Service kind.",rule="self.all(f, f.kind == 'Service')"
// +kubebuilder:validation:XValidation:message="BackendRefs only supports Core group.",rule="self.all(f, f.group == '')"
// +kubebuilder:validation:XValidation:message="BackendRefs only supports Service and Backend kind.",rule="self.all(f, f.kind == 'Service' || f.kind == 'Backend')"
// +kubebuilder:validation:XValidation:message="BackendRefs only supports Core and gateway.envoyproxy.io group.",rule="self.all(f, f.group == '' || f.group == 'gateway.envoyproxy.io')"
BackendRefs []BackendRef `json:"backendRefs"`

// MessageTimeout is the timeout for a response to be returned from the external processor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,11 @@ spec:
minItems: 1
type: array
x-kubernetes-validations:
- message: BackendRefs only supports Service kind.
rule: self.all(f, f.kind == 'Service')
- message: BackendRefs only supports Core group.
rule: self.all(f, f.group == '')
- message: BackendRefs only supports Service and Backend kind.
rule: self.all(f, f.kind == 'Service' || f.kind == 'Backend')
- message: BackendRefs only supports Core and gateway.envoyproxy.io
group.
rule: self.all(f, f.group == '' || f.group == 'gateway.envoyproxy.io')
failOpen:
description: |-
FailOpen defines if requests or responses that cannot be processed due to connectivity to the
Expand Down
2 changes: 2 additions & 0 deletions charts/gateway-helm/templates/_rbac.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ resources:
- backendtrafficpolicies
- securitypolicies
- envoyextensionpolicies
- backends
verbs:
- get
- list
Expand All @@ -85,6 +86,7 @@ resources:
- backendtrafficpolicies/status
- securitypolicies/status
- envoyextensionpolicies/status
- backends/status
verbs:
- update
{{- end }}
Expand Down
1 change: 1 addition & 0 deletions examples/redis/redis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ data:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
extensionApis:
enableEnvoyPatchPolicy: true
enableBackend: true
rateLimit:
backend:
type: Redis
Expand Down
3 changes: 3 additions & 0 deletions internal/cmd/egctl/translate.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ func translateGatewayAPIToIR(resources *gatewayapi.Resources) (*gatewayapi.Trans
GlobalRateLimitEnabled: true,
EndpointRoutingDisabled: true,
EnvoyPatchPolicyEnabled: true,
BackendEnabled: true,
}

// Fix the services in the resources section so that they have an IP address - this prevents nasty
Expand Down Expand Up @@ -309,6 +310,7 @@ func translateGatewayAPIToGatewayAPI(resources *gatewayapi.Resources) (gatewayap
GlobalRateLimitEnabled: true,
EndpointRoutingDisabled: true,
EnvoyPatchPolicyEnabled: true,
BackendEnabled: true,
}
gRes := gTranslator.Translate(resources)
// Update the status of the GatewayClass based on EnvoyProxy validation
Expand Down Expand Up @@ -341,6 +343,7 @@ func translateGatewayAPIToXds(dnsDomain string, resourceType string, resources *
GlobalRateLimitEnabled: true,
EndpointRoutingDisabled: true,
EnvoyPatchPolicyEnabled: true,
BackendEnabled: true,
}
gRes := gTranslator.Translate(resources)

Expand Down
29 changes: 29 additions & 0 deletions internal/gatewayapi/backend.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright Envoy Gateway Authors
// SPDX-License-Identifier: Apache-2.0
// The full text of the Apache license is available in the LICENSE file at
// the root of the repo.

package gatewayapi

import (
egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
"github.com/envoyproxy/gateway/internal/gatewayapi/status"
)

func (t *Translator) ProcessBackends(backends []*egv1a1.Backend) []*egv1a1.Backend {
var res []*egv1a1.Backend
for _, backend := range backends {
backend := backend.DeepCopy()

// Ensure Backends are enabled
if !t.BackendEnabled {
status.UpdateBackendStatusAcceptedCondition(backend, false)
} else {
status.UpdateBackendStatusAcceptedCondition(backend, true)
}

res = append(res, backend)
}

return res
}
17 changes: 12 additions & 5 deletions internal/gatewayapi/envoyextensionpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,11 +439,18 @@
Settings: dsl,
}

authority = fmt.Sprintf(
"%s.%s:%d",
extProc.BackendRefs[0].Name,
NamespaceDerefOr(extProc.BackendRefs[0].Namespace, policyNamespacedName.Namespace),
*extProc.BackendRefs[0].Port)
if extProc.BackendRefs[0].Port != nil {
authority = fmt.Sprintf(
"%s.%s:%d",
extProc.BackendRefs[0].Name,
NamespaceDerefOr(extProc.BackendRefs[0].Namespace, policyNamespacedName.Namespace),
*extProc.BackendRefs[0].Port)
} else {
authority = fmt.Sprintf(
"%s.%s",
extProc.BackendRefs[0].Name,
NamespaceDerefOr(extProc.BackendRefs[0].Namespace, policyNamespacedName.Namespace))

Check warning on line 452 in internal/gatewayapi/envoyextensionpolicy.go

View check run for this annotation

Codecov / codecov/patch

internal/gatewayapi/envoyextensionpolicy.go#L449-L452

Added lines #L449 - L452 were not covered by tests
}

extProcIR := &ir.ExtProc{
Name: name,
Expand Down
64 changes: 25 additions & 39 deletions internal/gatewayapi/ext_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@
"fmt"
"strings"

v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/ptr"
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
egv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"

egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
"github.com/envoyproxy/gateway/internal/ir"
Expand All @@ -29,68 +28,55 @@
resources *Resources,
) (*ir.DestinationSetting, error) {
var (
endpoints []*ir.DestinationEndpoint
addrType *ir.DestinationAddressType
servicePort v1.ServicePort
backendTLS *ir.TLSUpstreamConfig
backendTLS *ir.TLSUpstreamConfig
ds *ir.DestinationSetting
)

serviceNamespace := NamespaceDerefOr(backendRef.Namespace, policyNamespacedName.Namespace)
service := resources.GetService(serviceNamespace, string(backendRef.Name))
for _, port := range service.Spec.Ports {
if port.Port == int32(*backendRef.Port) {
servicePort = port
break
}
}
backendNamespace := NamespaceDerefOr(backendRef.Namespace, policyNamespacedName.Namespace)

if servicePort.AppProtocol != nil &&
*servicePort.AppProtocol == "kubernetes.io/h2c" {
protocol = ir.HTTP2
switch KindDerefOr(backendRef.Kind, KindService) {
case KindService:
ds = t.processServiceDestinationSetting(*backendRef, backendNamespace, protocol, resources)
case egv1a1.KindBackend:
if !t.BackendEnabled {
return nil, fmt.Errorf("resource %s of type Backend cannot be used since Backend is disabled in Envoy Gateway configuration", string(backendRef.Name))
}
ds = t.processBackendDestinationSetting(*backendRef, backendNamespace, resources)
ds.Protocol = protocol
}

// Route to endpoints by default
if !t.EndpointRoutingDisabled {
endpointSlices := resources.GetEndpointSlicesForBackend(
serviceNamespace, string(backendRef.Name), KindService)
endpoints, addrType = getIREndpointsFromEndpointSlices(
endpointSlices, servicePort.Name, servicePort.Protocol)
} else {
// Fall back to Service ClusterIP routing
ep := ir.NewDestEndpoint(
service.Spec.ClusterIP,
uint32(*backendRef.Port))
endpoints = append(endpoints, ep)
if ds == nil {
return nil, errors.New(
"failed to translate external service backendRef")

Check warning on line 50 in internal/gatewayapi/ext_service.go

View check run for this annotation

Codecov / codecov/patch

internal/gatewayapi/ext_service.go#L49-L50

Added lines #L49 - L50 were not covered by tests
}

// TODO: support mixed endpointslice address type for the same backendRef
if !t.EndpointRoutingDisabled && addrType != nil && *addrType == ir.MIXED {
if !t.EndpointRoutingDisabled && ds.AddressType != nil && *ds.AddressType == ir.MIXED {
return nil, errors.New(
"mixed endpointslice address type for the same backendRef is not supported")
}

backendTLS = t.applyBackendTLSSetting(
*backendRef,
serviceNamespace,
backendNamespace,
// Gateway is not the appropriate parent reference here because the owner
// of the BackendRef is the policy, and there is no hierarchy
// relationship between the policy and a gateway.
// The owner policy of the BackendRef is used as the parent reference here.
egv1a2.ParentReference{
gwapiv1a2.ParentReference{
Group: ptr.To(gwapiv1.Group(egv1a1.GroupName)),
Kind: ptr.To(gwapiv1.Kind(policyKind)),
Namespace: ptr.To(gwapiv1.Namespace(policyNamespacedName.Namespace)),
Name: gwapiv1.ObjectName(policyNamespacedName.Name),
},
resources)

return &ir.DestinationSetting{
Weight: ptr.To(uint32(1)),
Protocol: protocol,
Endpoints: endpoints,
AddressType: addrType,
TLS: backendTLS,
}, nil
ds.TLS = backendTLS

// TODO: support weighted non-xRoute backends
ds.Weight = ptr.To(uint32(1))

return ds, nil
}

// TODO: also refer to extension type, as WASM may also introduce destinations
Expand Down
12 changes: 12 additions & 0 deletions internal/gatewayapi/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
SecurityPolicies []*egv1a1.SecurityPolicy `json:"securityPolicies,omitempty" yaml:"securityPolicies,omitempty"`
BackendTLSPolicies []*gwapiv1a3.BackendTLSPolicy `json:"backendTLSPolicies,omitempty" yaml:"backendTLSPolicies,omitempty"`
EnvoyExtensionPolicies []*egv1a1.EnvoyExtensionPolicy `json:"envoyExtensionPolicies,omitempty" yaml:"envoyExtensionPolicies,omitempty"`
Backends []*egv1a1.Backend `json:"backends,omitempty" yaml:"backends,omitempty"`
}

func NewResources() *Resources {
Expand All @@ -77,6 +78,7 @@
SecurityPolicies: []*egv1a1.SecurityPolicy{},
BackendTLSPolicies: []*gwapiv1a3.BackendTLSPolicy{},
EnvoyExtensionPolicies: []*egv1a1.EnvoyExtensionPolicy{},
Backends: []*egv1a1.Backend{},

Check warning on line 81 in internal/gatewayapi/resource.go

View check run for this annotation

Codecov / codecov/patch

internal/gatewayapi/resource.go#L81

Added line #L81 was not covered by tests
}
}

Expand Down Expand Up @@ -110,6 +112,16 @@
return nil
}

func (r *Resources) GetBackend(namespace, name string) *egv1a1.Backend {
for _, be := range r.Backends {
if be.Namespace == namespace && be.Name == name {
return be
}
}

return nil

Check warning on line 122 in internal/gatewayapi/resource.go

View check run for this annotation

Codecov / codecov/patch

internal/gatewayapi/resource.go#L122

Added line #L122 was not covered by tests
}

func (r *Resources) GetSecret(namespace, name string) *v1.Secret {
for _, secret := range r.Secrets {
if secret.Namespace == namespace && secret.Name == name {
Expand Down
Loading