@@ -121,7 +121,7 @@ func BenchmarkMinimalPolicyWithDecisionLogs(b *testing.B) {
121121 OpaControlPlaneUrl : opaControlPlane .URL (),
122122 DecisionConsumerUrl : decisionLogsConsumer .URL ,
123123 DecisionPath : testDecisionPath ,
124- BundleName : testBundleName ,
124+ BundleNames : [] string { testBundleName } ,
125125 DecisionLogging : true ,
126126 ContextExtensions : "" ,
127127 }
@@ -297,16 +297,19 @@ func BenchmarkJwtValidation(b *testing.B) {
297297// bundlePath, and filterOpts variables are correctly configured to match your bundle.
298298func BenchmarkMinimalPolicyBundle (b * testing.B ) {
299299 bundleName := "simple-opa-bundle.tar.gz"
300- bundlePath := fmt .Sprintf ("testResources/%s" , bundleName )
301300
302- opaControlPlane := newOpaControlPlaneServingBundle (bundlePath , bundleName , b )
301+ bundleFiles := map [string ]string {
302+ bundleName : fmt .Sprintf ("testResources/%s" , bundleName ),
303+ }
304+
305+ opaControlPlane := newOpaControlPlaneServingDataAndPolicyBundles (b , bundleFiles )
303306 defer opaControlPlane .Close ()
304307
305308 filterOpts := FilterOptions {
306309 OpaControlPlaneUrl : opaControlPlane .URL ,
307310 DecisionConsumerUrl : opaControlPlane .URL ,
308311 DecisionPath : "envoy/authz/allow" ,
309- BundleName : bundleName ,
312+ BundleNames : [] string { bundleName } ,
310313 DecisionLogging : false ,
311314 }
312315 f , err := createOpaFilter (filterOpts )
@@ -333,56 +336,120 @@ func BenchmarkMinimalPolicyBundle(b *testing.B) {
333336 })
334337}
335338
336- func newDecisionConsumer () * httptest.Server {
337- return httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
338- w .WriteHeader (http .StatusOK )
339- }))
340- }
339+ // BenchmarkSplitPolicyAndDataBundles measures the performance of the OPA authorization filter
340+ // when using a pre-compiled split policy and data bundles loaded from a .tar.gz file.
341341
342- func newOpaControlPlaneServingBundle (bundlePath , bundleName string , b * testing.B ) * httptest.Server {
343- if ! (strings .HasSuffix (bundlePath , ".tar.gz" ) || strings .HasSuffix (bundlePath , ".tgz" )) {
344- b .Fatalf ("bundle file %q does not have the expected .tar.gz or .tgz extension" , bundlePath )
342+ // This benchmark evaluates the filter's authorization decision overhead with a
343+ // pre-compiled bundles, serving as a representative use case.
344+
345+ // A sample bundles for this benchmark are located at testResources/split-bundles.
346+ // To generate a .tgz bundle, use the following command:
347+ //
348+ // opa build -b <bundle_directory> -o <output_file.tgz>
349+ //
350+ // For example:
351+ //
352+ // cd testResources/split-bundles
353+ // opa build -b data -o context-data.tgz
354+ // opa build -b policy -o policy.tgz
355+
356+ // You can also use your own bundles. If you do so, ensure that the bundlePaths,
357+ // and filterOpts variables are correctly configured to match your bundle and
358+ // the roots in .manifest files in your bundles do not overlap.
359+ func BenchmarkSplitPolicyAndDataBundles (b * testing.B ) {
360+ policyBundle := "policy"
361+ dataBundle := "context-data"
362+
363+ bundleFiles := map [string ]string {
364+ policyBundle : "testResources/split-bundles/policy.tgz" ,
365+ dataBundle : "testResources/split-bundles/context-data.tgz" ,
345366 }
346367
347- fileData , err := os .ReadFile (bundlePath )
348- if err != nil {
349- b .Fatalf ("failed to read bundle file from path %q: %v" , bundlePath , err )
368+ opaControlPlane := newOpaControlPlaneServingDataAndPolicyBundles (b , bundleFiles )
369+ defer opaControlPlane .Close ()
370+
371+ filterOpts := FilterOptions {
372+ OpaControlPlaneUrl : opaControlPlane .URL ,
373+ DecisionConsumerUrl : opaControlPlane .URL ,
374+ DecisionPath : "policy/allow" ,
375+ BundleNames : []string {policyBundle , dataBundle },
376+ DecisionLogging : false ,
350377 }
351378
352- return httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
353- if r .URL .Path == "/bundles/" + bundleName {
354- w .Header ().Set ("Content-Type" , "application/gzip" )
355- w .Header ().Set ("Content-Disposition" , "attachment; filename=" + bundleName )
356- _ , err := w .Write (fileData )
357- if err != nil {
358- fmt .Printf ("failed to write bundle file: %v" , err )
359- w .WriteHeader (http .StatusInternalServerError )
379+ f , err := createOpaFilterForMultipleBundles (filterOpts )
380+ require .NoError (b , err )
381+
382+ requestUrl , err := url .Parse ("http://opa-authorized.test/allow/alice" )
383+ require .NoError (b , err )
384+
385+ b .ResetTimer ()
386+ b .RunParallel (func (pb * testing.PB ) {
387+ for pb .Next () {
388+ ctx := & filtertest.Context {
389+ FStateBag : map [string ]interface {}{},
390+ FResponse : & http.Response {},
391+ FRequest : & http.Request {
392+ URL : requestUrl ,
393+ Method : "GET" ,
394+ },
395+ FMetrics : & metricstest.MockMetrics {},
396+ }
397+ f .Request (ctx )
398+ assert .False (b , ctx .FServed )
399+ assert .NotEqual (b , 403 , ctx .FResponse .StatusCode , "Expected 403 Forbidden response" )
400+ }
401+ })
402+
403+ }
404+
405+ func newOpaControlPlaneServingDataAndPolicyBundles (b * testing.B , bundleFiles map [string ]string ) * httptest.Server {
406+
407+ server := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
408+ for bundleName , bundlePath := range bundleFiles {
409+ if r .URL .Path == "/bundles/" + bundleName {
410+ fileData , err := os .ReadFile (bundlePath )
411+ if err != nil {
412+ b .Fatalf ("failed to read bundle file from path %q: %v" , bundlePath , err )
413+ }
414+ w .Header ().Set ("Content-Type" , "application/gzip" )
415+ w .Header ().Set ("Content-Disposition" , "attachment; filename=" + bundleName )
416+ _ , err = w .Write (fileData )
417+ if err != nil {
418+ fmt .Printf ("failed to write bundle file: %v" , err )
419+ w .WriteHeader (http .StatusInternalServerError )
420+ }
421+ return
360422 }
361- return
362423 }
363424
364- // Decision logs consumer endpoint
365425 if r .URL .Path == "/logs" {
366426 w .WriteHeader (http .StatusOK )
367427 return
368428 }
369-
370429 w .WriteHeader (http .StatusNotFound )
371430 }))
431+
432+ return server
433+ }
434+
435+ func newDecisionConsumer () * httptest.Server {
436+ return httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
437+ w .WriteHeader (http .StatusOK )
438+ }))
372439}
373440
374441func createOpaFilter (opts FilterOptions ) (filters.Filter , error ) {
375442 config := generateConfig (opts .OpaControlPlaneUrl , opts .DecisionConsumerUrl , opts .DecisionPath , opts .DecisionLogging )
376443 opaFactory := openpolicyagent .NewOpenPolicyAgentRegistry ()
377444 spec := NewOpaAuthorizeRequestSpec (opaFactory , openpolicyagent .WithConfigTemplate (config ))
378- return spec .CreateFilter ([]interface {}{opts .BundleName , opts .ContextExtensions })
445+ return spec .CreateFilter ([]interface {}{opts .BundleNames [ 0 ] , opts .ContextExtensions })
379446}
380447
381448func createBodyBasedOpaFilter (opts FilterOptions ) (filters.Filter , error ) {
382449 config := generateConfig (opts .OpaControlPlaneUrl , opts .DecisionConsumerUrl , opts .DecisionPath , opts .DecisionLogging )
383450 opaFactory := openpolicyagent .NewOpenPolicyAgentRegistry ()
384451 spec := NewOpaAuthorizeRequestWithBodySpec (opaFactory , openpolicyagent .WithConfigTemplate (config ))
385- return spec .CreateFilter ([]interface {}{opts .BundleName , opts .ContextExtensions })
452+ return spec .CreateFilter ([]interface {}{opts .BundleNames [ 0 ] , opts .ContextExtensions })
386453}
387454
388455func generateConfig (opaControlPlane string , decisionLogConsumer string , decisionPath string , decisionLogging bool ) []byte {
@@ -432,11 +499,73 @@ func generateConfig(opaControlPlane string, decisionLogConsumer string, decision
432499 }` , opaControlPlane , decisionLogConsumer , decisionPlugin , decisionPath ))
433500}
434501
502+ func createOpaFilterForMultipleBundles (opts FilterOptions ) (filters.Filter , error ) {
503+ config := generateConfigForMultipleBundles (opts .BundleNames , opts .OpaControlPlaneUrl , opts .DecisionConsumerUrl , opts .DecisionPath , opts .DecisionLogging )
504+ opaFactory := openpolicyagent .NewOpenPolicyAgentRegistry ()
505+ spec := NewOpaAuthorizeRequestSpec (opaFactory , openpolicyagent .WithConfigTemplate (config ))
506+ return spec .CreateFilter ([]interface {}{opts .BundleNames [0 ], opts .ContextExtensions })
507+ }
508+
509+ func generateConfigForMultipleBundles (bundlesNames []string , opaControlPlane string , decisionLogConsumer string , decisionPath string , decisionLogging bool ) []byte {
510+ var decisionPlugin string
511+ if decisionLogging {
512+ decisionPlugin = `
513+ "decision_logs": {
514+ "console": false,
515+ "service": "decision_svc",
516+ "reporting": {
517+ "min_delay_seconds": 300,
518+ "max_delay_seconds": 600
519+ }
520+ },
521+ `
522+ }
523+
524+ return []byte (fmt .Sprintf (`{
525+ "services": {
526+ "bundle_svc": {
527+ "url": %q
528+ },
529+ "decision_svc": {
530+ "url": %q
531+ }
532+ },
533+ "bundles": {
534+ "policy": {
535+ "service": "bundle_svc",
536+ "resource": "/bundles/%s",
537+ "polling": {
538+ "min_delay_seconds": 600,
539+ "max_delay_seconds": 1200
540+ }
541+ },
542+ "context-data": {
543+ "service": "bundle_svc",
544+ "resource": "/bundles/%s",
545+ "polling": {
546+ "min_delay_seconds": 600,
547+ "max_delay_seconds": 1200
548+ }
549+ }
550+ },
551+ "labels": {
552+ "environment": "test"
553+ },
554+ %s
555+ "plugins": {
556+ "envoy_ext_authz_grpc": {
557+ "path": %q,
558+ "dry-run": false
559+ }
560+ }
561+ }` , opaControlPlane , decisionLogConsumer , bundlesNames [0 ], bundlesNames [1 ], decisionPlugin , decisionPath ))
562+ }
563+
435564type FilterOptions struct {
436565 OpaControlPlaneUrl string
437566 DecisionConsumerUrl string
438567 DecisionPath string
439- BundleName string
568+ BundleNames [] string
440569 DecisionLogging bool
441570 ContextExtensions string
442571}
@@ -446,7 +575,7 @@ func NewFilterOptionsWithDefaults(opaControlPlaneURL string) FilterOptions {
446575 OpaControlPlaneUrl : opaControlPlaneURL ,
447576 DecisionConsumerUrl : "" ,
448577 DecisionPath : testDecisionPath ,
449- BundleName : testBundleName ,
578+ BundleNames : [] string { testBundleName } ,
450579 DecisionLogging : false ,
451580 ContextExtensions : "" ,
452581 }
0 commit comments