Skip to content

Commit

Permalink
wasm http impl
Browse files Browse the repository at this point in the history
Signed-off-by: huabing zhao <zhaohuabing@gmail.com>
  • Loading branch information
zhaohuabing committed Apr 11, 2024
1 parent 0cb3299 commit d0cb214
Show file tree
Hide file tree
Showing 19 changed files with 1,195 additions and 63 deletions.
4 changes: 2 additions & 2 deletions api/v1alpha1/envoyextensionypolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ type EnvoyExtensionPolicySpec struct {
// TargetRef
TargetRef gwapiv1a2.PolicyTargetReferenceWithSectionName `json:"targetRef"`

// WASM is a list of Wasm extensions to be loaded by the Gateway.
// Wasm is a list of Wasm extensions to be loaded by the Gateway.
// Order matters, as the extensions will be loaded in the order they are
// defined in this list.
//
// +optional
WASM []Wasm `json:"wasm,omitempty"`
Wasm []Wasm `json:"wasm,omitempty"`

// ExtProc is an ordered list of external processing filters
// that should added to the envoy filter chain
Expand Down
8 changes: 6 additions & 2 deletions api/v1alpha1/wasm_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,9 @@ type WasmCodeSource struct {
Image *ImageWasmCodeSource `json:"image,omitempty"`

// SHA256 checksum that will be used to verify the wasm code.
// +optional
// SHA256 *string `json:"sha256,omitempty"`
//
// kubebuilder:validation:Pattern=`^[a-f0-9]{64}$`
SHA256 string `json:"sha256"`
}

// WasmCodeSourceType specifies the types of sources for the wasm code.
Expand All @@ -92,6 +93,9 @@ const (

// ImageWasmCodeSourceType allows the user to specify the wasm code in an OCI image.
ImageWasmCodeSourceType WasmCodeSourceType = "Image"

// ConfigMapCodeSourceType allows the user to specify the wasm code in a ConfigMap.
ConfigMapWasmCodeSourceType WasmCodeSourceType = "ConfigMap"
)

// HTTPWasmCodeSource defines the HTTP URL containing the wasm code.
Expand Down
4 changes: 2 additions & 2 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ spec:
rule: '!has(self.sectionName)'
wasm:
description: |-
WASM is a list of Wasm extensions to be loaded by the Gateway.
Wasm is a list of Wasm extensions to be loaded by the Gateway.
Order matters, as the extensions will be loaded in the order they are
defined in this list.
items:
Expand Down Expand Up @@ -301,6 +301,13 @@ spec:
- pullSecret
- url
type: object
sha256:
description: |-
SHA256 checksum that will be used to verify the wasm code.
kubebuilder:validation:Pattern=`^[a-f0-9]{64}$`
type: string
type:
allOf:
- enum:
Expand All @@ -314,6 +321,7 @@ spec:
Valid WasmCodeSourceType values are "HTTP" or "Image".
type: string
required:
- sha256
- type
type: object
config:
Expand Down
114 changes: 96 additions & 18 deletions internal/gatewayapi/envoyextensionpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package gatewayapi

import (
"errors"
"fmt"
"sort"
"strings"
Expand Down Expand Up @@ -293,24 +294,34 @@ func resolveEEPolicyRouteTargetRef(policy *egv1a1.EnvoyExtensionPolicy, routes m

func (t *Translator) translateEnvoyExtensionPolicyForRoute(policy *egv1a1.EnvoyExtensionPolicy, route RouteContext,
xdsIR XdsIRMap, resources *Resources) error {
var (
extProcs []ir.ExtProc
wasms []ir.Wasm
err, errs error
)

if extProcs, err = t.buildExtProcs(policy, resources); err != nil {
errs = errors.Join(errs, err)
}
if wasms, err = t.buildWasms(policy); err != nil {
errs = errors.Join(errs, err)
}

// Apply IR to all relevant routes
prefix := irRoutePrefix(route)
for _, ir := range xdsIR {
for _, http := range ir.HTTP {
for _, r := range http.Routes {
// Apply if there is a match
if strings.HasPrefix(r.Name, prefix) {
if extProcs, err := t.buildExtProcs(policy, resources); err == nil {
r.ExtProcs = extProcs
} else {
return err
}
r.ExtProcs = extProcs
r.Wasms = wasms
}
}
}
}

return nil
return errs
}

func (t *Translator) buildExtProcs(policy *egv1a1.EnvoyExtensionPolicy, resources *Resources) ([]ir.ExtProc, error) {
Expand All @@ -320,21 +331,24 @@ func (t *Translator) buildExtProcs(policy *egv1a1.EnvoyExtensionPolicy, resource
return nil, nil
}

if len(policy.Spec.ExtProc) > 0 {
for idx, ep := range policy.Spec.ExtProc {
name := irConfigNameForEEP(policy, idx)
extProcIR, err := t.buildExtProc(name, utils.NamespacedName(policy), ep, idx, resources)
if err != nil {
return nil, err
}
extProcIRList = append(extProcIRList, *extProcIR)
for idx, ep := range policy.Spec.ExtProc {
name := irConfigNameForEEP(policy, idx)
extProcIR, err := t.buildExtProc(name, utils.NamespacedName(policy), ep, idx, resources)
if err != nil {
return nil, err
}
extProcIRList = append(extProcIRList, *extProcIR)
}
return extProcIRList, nil
}

func (t *Translator) translateEnvoyExtensionPolicyForGateway(policy *egv1a1.EnvoyExtensionPolicy,
gateway *GatewayContext, xdsIR XdsIRMap, resources *Resources) error {
var (
extProcs []ir.ExtProc
wasms []ir.Wasm
err, errs error
)

irKey := t.getIRKey(gateway.Gateway)
// Should exist since we've validated this
Expand All @@ -345,9 +359,11 @@ func (t *Translator) translateEnvoyExtensionPolicyForGateway(policy *egv1a1.Envo
string(policy.Spec.TargetRef.Name),
)

extProcs, err := t.buildExtProcs(policy, resources)
if err != nil {
return err
if extProcs, err = t.buildExtProcs(policy, resources); err != nil {
errs = errors.Join(errs, err)
}
if wasms, err = t.buildWasms(policy); err != nil {
errs = errors.Join(errs, err)
}

for _, http := range ir.HTTP {
Expand All @@ -360,13 +376,21 @@ func (t *Translator) translateEnvoyExtensionPolicyForGateway(policy *egv1a1.Envo
// targeting a lesser specific scope(Gateway).
for _, r := range http.Routes {
// if already set - there's a route level policy, so skip
if r.ExtProcs != nil ||
r.Wasms != nil {
continue
}

if r.ExtProcs == nil {
r.ExtProcs = extProcs
}
if r.Wasms == nil {
r.Wasms = wasms
}
}
}

return nil
return errs
}

func (t *Translator) buildExtProc(
Expand Down Expand Up @@ -428,3 +452,57 @@ func irConfigNameForEEP(policy *egv1a1.EnvoyExtensionPolicy, idx int) string {
utils.NamespacedName(policy).String(),
idx)
}

func (t *Translator) buildWasms(policy *egv1a1.EnvoyExtensionPolicy) ([]ir.Wasm, error) {
var wasmIRList []ir.Wasm

if policy == nil {
return nil, nil
}

for idx, wasm := range policy.Spec.Wasm {
name := irConfigNameForEEP(policy, idx)
wasmIR, err := t.buildWasm(name, wasm)
if err != nil {
return nil, err
}
wasmIRList = append(wasmIRList, *wasmIR)
}
return wasmIRList, nil
}

func (t *Translator) buildWasm(name string, wasm egv1a1.Wasm) (*ir.Wasm, error) {
var (
failOpen = false
httpWasmCode *ir.HTTPWasmCode
)

if wasm.FailOpen != nil {
failOpen = *wasm.FailOpen
}

switch wasm.Code.Type {
case egv1a1.HTTPWasmCodeSourceType:
httpWasmCode = &ir.HTTPWasmCode{
URL: wasm.Code.HTTP.URL,
SHA256: wasm.Code.SHA256,
}
case egv1a1.ConfigMapWasmCodeSourceType:
return nil, fmt.Errorf("ConfigMap Wasm code source is not supported yet")
case egv1a1.ImageWasmCodeSourceType:
return nil, fmt.Errorf("OCI image Wasm code source is not supported yet")
default:
// should never happen because of kubebuilder validation, just a sanity check
return nil, fmt.Errorf("unsupported Wasm code source type %q", wasm.Code.Type)
}

wasmIR := &ir.Wasm{
Name: name,
WasmName: wasm.Name,
Config: wasm.Config,
FailOpen: failOpen,
HTTPWasmCode: httpWasmCode,
}

return wasmIR, nil
}
112 changes: 112 additions & 0 deletions internal/gatewayapi/testdata/envoyextensionpolicy-with-wasm.in.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
gateways:
- apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
namespace: envoy-gateway
name: gateway-1
spec:
gatewayClassName: envoy-gateway-class
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All
httpRoutes:
- apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: default
name: httproute-1
spec:
hostnames:
- www.example.com
parentRefs:
- namespace: envoy-gateway
name: gateway-1
sectionName: http
rules:
- matches:
- path:
value: "/foo"
backendRefs:
- name: service-1
port: 8080
- apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: default
name: httproute-2
spec:
hostnames:
- www.example.com
parentRefs:
- namespace: envoy-gateway
name: gateway-1
sectionName: http
rules:
- matches:
- path:
value: "/bar"
backendRefs:
- name: service-1
port: 8080
envoyextensionpolicies:
- apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyExtensionPolicy
metadata:
namespace: envoy-gateway
name: policy-for-gateway # This policy should attach httproute-2
spec:
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
name: gateway-1
namespace: envoy-gateway
wasm:
- name: wasm-filter-1
code:
type: HTTP
http:
url: https://www.example.com/wasm-filter-1.wasm
sha256: 746df05c8f3a0b07a46c0967cfbc5cbe5b9d48d0f79b6177eeedf8be6c8b34b5
config:
parameter1:
key1: value1
key2: value2
parameter2: value3
- name: wasm-filter-2
code:
type: HTTP
http:
url: https://www.example.com/wasm-filter-2.wasm
sha256: a1efca12ea51069abb123bf9c77889fcc2a31cc5483fc14d115e44fdf07c7980
config:
parameter1: value1
parameter2: value2
- apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyExtensionPolicy
metadata:
namespace: default
name: policy-for-http-route # This policy should attach httproute-1
spec:
targetRef:
group: gateway.networking.k8s.io
kind: HTTPRoute
name: httproute-1
namespace: default
wasm:
- name: wasm-filter-3
code:
type: HTTP
http:
url: https://www.test.com/wasm-filter-3.wasm
sha256: a1f0b78b8c1320690327800e3a5de10e7dbba7b6c752e702193a395a52c727b6
config:
parameter1:
key1: value1
parameter2:
key2:
key3: value3
failOpen: true
Loading

0 comments on commit d0cb214

Please sign in to comment.