1
1
package envoy
2
2
3
3
import (
4
+ "encoding/json"
4
5
"fmt"
5
6
"log"
7
+ "os"
6
8
"path/filepath"
7
9
"strings"
8
10
@@ -15,13 +17,16 @@ import (
15
17
tracing "github.com/envoyproxy/go-control-plane/envoy/config/trace/v3"
16
18
eal "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v3"
17
19
gal "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/grpc/v3"
20
+ buffer "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/buffer/v3"
18
21
eauthz "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3"
19
22
hcfg "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/health_check/v3"
23
+ wasm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/wasm/v3"
20
24
tls_inspector "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/tls_inspector/v3"
21
25
hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
22
26
previousHosts "github.com/envoyproxy/go-control-plane/envoy/extensions/retry/host/previous_hosts/v3"
23
27
auth "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
24
28
envoy_extension_http "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3"
29
+ wasmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/wasm/v3"
25
30
matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
26
31
any "github.com/golang/protobuf/ptypes/any"
27
32
"github.com/golang/protobuf/ptypes/duration"
@@ -125,6 +130,124 @@ func makeVirtualHost(vhost *virtualHost, reselectionAttempts int64, defaultRetry
125
130
return & virtualHost , nil
126
131
}
127
132
133
+ func loadCustomHttpFilters (filePath string ) ([]* hcm.HttpFilter , error ) {
134
+ data , err := os .ReadFile (filePath )
135
+ if err != nil {
136
+ return nil , fmt .Errorf ("failed to read custom HTTP filter file `%s`: %w" , filePath , err )
137
+ }
138
+ var config CustomHttpFiltersConfig
139
+ if err := json .Unmarshal (data , & config ); err != nil {
140
+ return nil , fmt .Errorf ("failed to unmarshal custom HTTP filter file `%s`: %w" , filePath , err )
141
+ }
142
+ var filters []* hcm.HttpFilter
143
+ for _ , filter := range config {
144
+ var anyConfig * anypb.Any
145
+ switch filter .Name {
146
+ case "envoy.filters.http.wasm" :
147
+ wasmConfig , err := makeWasmConfig (filter .TypedConfig )
148
+ if err != nil {
149
+ return nil , fmt .Errorf ("failed to create wasm config: %w" , err )
150
+ }
151
+ anyConfig , err = anypb .New (wasmConfig )
152
+ if err != nil {
153
+ return nil , fmt .Errorf ("failed to convert wasm config to Any: %w" , err )
154
+ }
155
+ case "envoy.filters.http.buffer" :
156
+ bufferConfig , err := makeBufferConfig (filter .TypedConfig )
157
+ if err != nil {
158
+ return nil , fmt .Errorf ("failed to create buffer config: %w" , err )
159
+ }
160
+ anyConfig , err = anypb .New (bufferConfig )
161
+ if err != nil {
162
+ return nil , fmt .Errorf ("failed to convert buffer config to Any: %w" , err )
163
+ }
164
+ default :
165
+ return nil , fmt .Errorf ("unsupported filter type: %s" , filter .Name )
166
+ }
167
+ filters = append (filters , & hcm.HttpFilter {
168
+ Name : filter .Name ,
169
+ ConfigType : & hcm.HttpFilter_TypedConfig {
170
+ TypedConfig : anyConfig ,
171
+ },
172
+ })
173
+ }
174
+ return filters , nil
175
+ }
176
+
177
+ func makeBufferConfig (typedConfig map [string ]interface {}) (* buffer.Buffer , error ) {
178
+ maxRequestBytes , ok := typedConfig ["maxRequestBytes" ].(float64 )
179
+ if ! ok {
180
+ return nil , fmt .Errorf ("missing or invalid `maxRequestBytes` field in buffer typedConfig" )
181
+ }
182
+
183
+ return & buffer.Buffer {
184
+ MaxRequestBytes : wrapperspb .UInt32 (uint32 (maxRequestBytes )),
185
+ }, nil
186
+ }
187
+
188
+ func makeWasmConfig (typedConfig map [string ]interface {}) (* wasm.Wasm , error ) {
189
+ value , ok := typedConfig ["value" ].(map [string ]interface {})
190
+ if ! ok {
191
+ return nil , fmt .Errorf ("missing or invalid `value` field in wasm typedConfig" )
192
+ }
193
+ config , ok := value ["config" ].(map [string ]interface {})
194
+ if ! ok {
195
+ return nil , fmt .Errorf ("missing or invalid `config` field in wasm typedConfig" )
196
+ }
197
+ vmConfig , ok := config ["vm_config" ].(map [string ]interface {})
198
+ if ! ok {
199
+ return nil , fmt .Errorf ("missing or invalid `vm_config` field in wasm config" )
200
+ }
201
+ runtime , ok := vmConfig ["runtime" ].(string )
202
+ if ! ok {
203
+ return nil , fmt .Errorf ("missing or invalid `runtime` field in wasm vm_config" )
204
+ }
205
+ vmID , ok := vmConfig ["vm_id" ].(string )
206
+ if ! ok {
207
+ return nil , fmt .Errorf ("missing or invalid `vm_id` field in wasm vm_config" )
208
+ }
209
+ code , ok := vmConfig ["code" ].(map [string ]interface {})
210
+ if ! ok {
211
+ return nil , fmt .Errorf ("missing or invalid `code` field in wasm vm_config" )
212
+ }
213
+ local , ok := code ["local" ].(map [string ]interface {})
214
+ if ! ok {
215
+ return nil , fmt .Errorf ("missing or invalid `local` field in wasm code" )
216
+ }
217
+ filename , ok := local ["filename" ].(string )
218
+ if ! ok {
219
+ return nil , fmt .Errorf ("missing or invalid `filename` field in wasm local code" )
220
+ }
221
+ configuration , ok := config ["configuration" ].(map [string ]interface {})
222
+ if ! ok {
223
+ return nil , fmt .Errorf ("missing or invalid `configuration` field in wasm config" )
224
+ }
225
+ return & wasm.Wasm {
226
+ Config : & wasmv3.PluginConfig {
227
+ Name : config ["name" ].(string ),
228
+ RootId : config ["root_id" ].(string ),
229
+ Configuration : & anypb.Any {
230
+ Value : []byte (configuration ["value" ].(string )),
231
+ },
232
+ Vm : & wasmv3.PluginConfig_VmConfig {
233
+ VmConfig : & wasmv3.VmConfig {
234
+ Runtime : runtime ,
235
+ VmId : vmID ,
236
+ Code : & core.AsyncDataSource {
237
+ Specifier : & core.AsyncDataSource_Local {
238
+ Local : & core.DataSource {
239
+ Specifier : & core.DataSource_Filename {
240
+ Filename : filename ,
241
+ },
242
+ },
243
+ },
244
+ },
245
+ },
246
+ },
247
+ },
248
+ }, nil
249
+ }
250
+
128
251
func makeHealthConfig () * hcfg.HealthCheck {
129
252
return & hcfg.HealthCheck {
130
253
PassThroughMode : & wrappers.BoolValue {Value : false },
@@ -247,11 +370,21 @@ func (c *KubernetesConfigurator) makeConnectionManager(virtualHosts []*route.Vir
247
370
// HTTP Filters
248
371
filterBuilder := & httpFilterBuilder {}
249
372
373
+ // custom HTTP filters
374
+ if c .customHttpFilterFile != "" {
375
+ customFilters , err := loadCustomHttpFilters (c .customHttpFilterFile )
376
+ if err != nil {
377
+ log .Fatalf ("failed to load custom HTTP filters: %s" , err )
378
+ }
379
+ for _ , filter := range customFilters {
380
+ filterBuilder .Add (filter )
381
+ }
382
+ }
383
+
250
384
anyHealthConfig , err := anypb .New (makeHealthConfig ())
251
385
if err != nil {
252
386
log .Fatalf ("failed to marshal healthcheck config struct to typed struct: %s" , err )
253
387
}
254
-
255
388
filterBuilder .Add (& hcm.HttpFilter {
256
389
Name : "envoy.filters.http.health_check" ,
257
390
ConfigType : & hcm.HttpFilter_TypedConfig {TypedConfig : anyHealthConfig },
@@ -268,7 +401,6 @@ func (c *KubernetesConfigurator) makeConnectionManager(virtualHosts []*route.Vir
268
401
ConfigType : & hcm.HttpFilter_TypedConfig {TypedConfig : anyExtAuthzConfig },
269
402
})
270
403
}
271
-
272
404
filter , err := filterBuilder .Filters ()
273
405
if err != nil {
274
406
return & hcm.HttpConnectionManager {}, err
0 commit comments