@@ -2,12 +2,18 @@ package rego
22
33import (
44 "net/http"
5+ "time"
56
67 "github.com/open-policy-agent/frameworks/constraint/pkg/externaldata"
78 "github.com/open-policy-agent/opa/ast"
89 "github.com/open-policy-agent/opa/rego"
910)
1011
12+ const (
13+ providerResponseAPIVersion = "externaldata.gatekeeper.sh/v1beta1"
14+ providerResponseKind = "ProviderResponse"
15+ )
16+
1117func externalDataBuiltin (d * Driver ) func (bctx rego.BuiltinContext , regorequest * ast.Term ) (* ast.Term , error ) {
1218 return func (bctx rego.BuiltinContext , regorequest * ast.Term ) (* ast.Term , error ) {
1319 var regoReq externaldata.RegoRequest
@@ -25,12 +31,78 @@ func externalDataBuiltin(d *Driver) func(bctx rego.BuiltinContext, regorequest *
2531 return externaldata .HandleError (http .StatusBadRequest , err )
2632 }
2733
28- externaldataResponse , statusCode , err := d .sendRequestToProvider (bctx .Context , & provider , regoReq .Keys , clientCert )
29- if err != nil {
30- return externaldata .HandleError (statusCode , err )
34+ // check provider response cache
35+ var providerRequestKeys []string
36+ var providerResponseStatusCode int
37+ var prepareResponse externaldata.Response
38+
39+ prepareResponse .Idempotent = true
40+ for _ , k := range regoReq .Keys {
41+ cachedResponse , err := d .providerResponseCache .Get (
42+ externaldata.CacheKey {
43+ ProviderName : regoReq .ProviderName ,
44+ Key : k ,
45+ },
46+ )
47+ if err != nil || time .Since (time .Unix (cachedResponse .Received , 0 )) > d .providerResponseCache .TTL {
48+ // key is not found or cache entry is stale, add key to the provider request keys
49+ providerRequestKeys = append (providerRequestKeys , k )
50+ } else {
51+ prepareResponse .Items = append (
52+ prepareResponse .Items , externaldata.Item {
53+ Key : k ,
54+ Value : cachedResponse .Value ,
55+ Error : cachedResponse .Error ,
56+ },
57+ )
58+
59+ // we are taking conservative approach here, if any of the cached response is not idempotent
60+ // we will mark the whole response as not idempotent
61+ if ! cachedResponse .Idempotent {
62+ prepareResponse .Idempotent = false
63+ }
64+ }
65+ }
66+
67+ if len (providerRequestKeys ) > 0 {
68+ externaldataResponse , statusCode , err := d .sendRequestToProvider (bctx .Context , & provider , providerRequestKeys , clientCert )
69+ if err != nil {
70+ return externaldata .HandleError (statusCode , err )
71+ }
72+
73+ for _ , item := range externaldataResponse .Response .Items {
74+ d .providerResponseCache .Upsert (
75+ externaldata.CacheKey {
76+ ProviderName : regoReq .ProviderName ,
77+ Key : item .Key ,
78+ },
79+ externaldata.CacheValue {
80+ Received : time .Now ().Unix (),
81+ Value : item .Value ,
82+ Error : item .Error ,
83+ Idempotent : externaldataResponse .Response .Idempotent ,
84+ },
85+ )
86+ }
87+
88+ // we are taking conservative approach here, if any of the response is not idempotent
89+ // we will mark the whole response as not idempotent
90+ if ! externaldataResponse .Response .Idempotent {
91+ prepareResponse .Idempotent = false
92+ }
93+
94+ prepareResponse .Items = append (prepareResponse .Items , externaldataResponse .Response .Items ... )
95+ prepareResponse .SystemError = externaldataResponse .Response .SystemError
96+ providerResponseStatusCode = statusCode
97+ }
98+
99+ providerResponse := & externaldata.ProviderResponse {
100+ APIVersion : providerResponseAPIVersion ,
101+ Kind : providerResponseKind ,
102+ Response : prepareResponse ,
31103 }
32104
33- regoResponse := externaldata .NewRegoResponse (statusCode , externaldataResponse )
105+ regoResponse := externaldata .NewRegoResponse (providerResponseStatusCode , providerResponse )
34106 return externaldata .PrepareRegoResponse (regoResponse )
35107 }
36108}
0 commit comments