feat: kubesphere 4.0 (#6115)
* feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> * feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> --------- Signed-off-by: ci-bot <ci-bot@kubesphere.io> Co-authored-by: ks-ci-bot <ks-ci-bot@example.com> Co-authored-by: joyceliu <joyceliu@yunify.com>
This commit is contained in:
committed by
GitHub
parent
b5015ec7b9
commit
447a51f08b
290
vendor/github.com/open-policy-agent/opa/topdown/http.go
generated
vendored
290
vendor/github.com/open-policy-agent/opa/topdown/http.go
generated
vendored
@@ -67,6 +67,7 @@ var allowedKeyNames = [...]string{
|
||||
"force_cache_duration_seconds",
|
||||
"raise_error",
|
||||
"caching_mode",
|
||||
"max_retry_attempts",
|
||||
}
|
||||
|
||||
// ref: https://www.rfc-editor.org/rfc/rfc7231#section-6.1
|
||||
@@ -104,6 +105,12 @@ const (
|
||||
|
||||
// HTTPSendNetworkErr represents a network error.
|
||||
HTTPSendNetworkErr string = "eval_http_send_network_error"
|
||||
|
||||
// minRetryDelay is amount of time to backoff after the first failure.
|
||||
minRetryDelay = time.Millisecond * 100
|
||||
|
||||
// maxRetryDelay is the upper bound of backoff delay.
|
||||
maxRetryDelay = time.Second * 60
|
||||
)
|
||||
|
||||
func builtinHTTPSend(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
|
||||
@@ -161,6 +168,7 @@ func getHTTPResponse(bctx BuiltinContext, req ast.Object) (*ast.Term, error) {
|
||||
if resp == nil {
|
||||
httpResp, err := reqExecutor.ExecuteHTTPRequest()
|
||||
if err != nil {
|
||||
reqExecutor.InsertErrorIntoCache(err)
|
||||
return nil, err
|
||||
}
|
||||
defer util.Close(httpResp)
|
||||
@@ -460,7 +468,7 @@ func createHTTPRequest(bctx BuiltinContext, obj ast.Object) (*http.Request, *htt
|
||||
case "cache", "caching_mode",
|
||||
"force_cache", "force_cache_duration_seconds",
|
||||
"force_json_decode", "force_yaml_decode",
|
||||
"raise_error": // no-op
|
||||
"raise_error", "max_retry_attempts": // no-op
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("invalid parameter %q", key)
|
||||
}
|
||||
@@ -646,8 +654,39 @@ func createHTTPRequest(bctx BuiltinContext, obj ast.Object) (*http.Request, *htt
|
||||
return req, client, nil
|
||||
}
|
||||
|
||||
func executeHTTPRequest(req *http.Request, client *http.Client) (*http.Response, error) {
|
||||
return client.Do(req)
|
||||
func executeHTTPRequest(req *http.Request, client *http.Client, inputReqObj ast.Object) (*http.Response, error) {
|
||||
var err error
|
||||
var retry int
|
||||
|
||||
retry, err = getNumberValFromReqObj(inputReqObj, ast.StringTerm("max_retry_attempts"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := 0; true; i++ {
|
||||
|
||||
var resp *http.Response
|
||||
resp, err = client.Do(req)
|
||||
if err == nil {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// final attempt
|
||||
if i == retry {
|
||||
break
|
||||
}
|
||||
|
||||
if err == context.Canceled {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
select {
|
||||
case <-time.After(util.DefaultBackoff(float64(minRetryDelay), float64(maxRetryDelay), i)):
|
||||
case <-req.Context().Done():
|
||||
return nil, context.Canceled
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func isContentType(header http.Header, typ ...string) bool {
|
||||
@@ -659,51 +698,121 @@ func isContentType(header http.Header, typ ...string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type httpSendCacheEntry struct {
|
||||
response *ast.Value
|
||||
error error
|
||||
}
|
||||
|
||||
// The httpSendCache is used for intra-query caching of http.send results.
|
||||
type httpSendCache struct {
|
||||
entries *util.HashMap
|
||||
}
|
||||
|
||||
func newHTTPSendCache() *httpSendCache {
|
||||
return &httpSendCache{
|
||||
entries: util.NewHashMap(valueEq, valueHash),
|
||||
}
|
||||
}
|
||||
|
||||
func valueHash(v util.T) int {
|
||||
return v.(ast.Value).Hash()
|
||||
}
|
||||
|
||||
func valueEq(a, b util.T) bool {
|
||||
av := a.(ast.Value)
|
||||
bv := b.(ast.Value)
|
||||
return av.Compare(bv) == 0
|
||||
}
|
||||
|
||||
func (cache *httpSendCache) get(k ast.Value) *httpSendCacheEntry {
|
||||
if v, ok := cache.entries.Get(k); ok {
|
||||
v := v.(httpSendCacheEntry)
|
||||
return &v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cache *httpSendCache) putResponse(k ast.Value, v *ast.Value) {
|
||||
cache.entries.Put(k, httpSendCacheEntry{response: v})
|
||||
}
|
||||
|
||||
func (cache *httpSendCache) putError(k ast.Value, v error) {
|
||||
cache.entries.Put(k, httpSendCacheEntry{error: v})
|
||||
}
|
||||
|
||||
// In the BuiltinContext cache we only store a single entry that points to
|
||||
// our ValueMap which is the "real" http.send() cache.
|
||||
func getHTTPSendCache(bctx BuiltinContext) *ast.ValueMap {
|
||||
func getHTTPSendCache(bctx BuiltinContext) *httpSendCache {
|
||||
raw, ok := bctx.Cache.Get(httpSendBuiltinCacheKey)
|
||||
if !ok {
|
||||
// Initialize if it isn't there
|
||||
cache := ast.NewValueMap()
|
||||
bctx.Cache.Put(httpSendBuiltinCacheKey, cache)
|
||||
return cache
|
||||
c := newHTTPSendCache()
|
||||
bctx.Cache.Put(httpSendBuiltinCacheKey, c)
|
||||
return c
|
||||
}
|
||||
|
||||
cache, ok := raw.(*ast.ValueMap)
|
||||
c, ok := raw.(*httpSendCache)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return cache
|
||||
return c
|
||||
}
|
||||
|
||||
// checkHTTPSendCache checks for the given key's value in the cache
|
||||
func checkHTTPSendCache(bctx BuiltinContext, key ast.Object) ast.Value {
|
||||
func checkHTTPSendCache(bctx BuiltinContext, key ast.Object) (ast.Value, error) {
|
||||
requestCache := getHTTPSendCache(bctx)
|
||||
if requestCache == nil {
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return requestCache.Get(key)
|
||||
v := requestCache.get(key)
|
||||
if v != nil {
|
||||
if v.error != nil {
|
||||
return nil, v.error
|
||||
}
|
||||
if v.response != nil {
|
||||
return *v.response, nil
|
||||
}
|
||||
// This should never happen
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func insertIntoHTTPSendCache(bctx BuiltinContext, key ast.Object, value ast.Value) {
|
||||
requestCache := getHTTPSendCache(bctx)
|
||||
if requestCache == nil {
|
||||
// Should never happen.. if it does just skip caching the value
|
||||
// FIXME: return error instead, to prevent inconsistencies?
|
||||
return
|
||||
}
|
||||
requestCache.Put(key, value)
|
||||
requestCache.putResponse(key, &value)
|
||||
}
|
||||
|
||||
func insertErrorIntoHTTPSendCache(bctx BuiltinContext, key ast.Object, err error) {
|
||||
requestCache := getHTTPSendCache(bctx)
|
||||
if requestCache == nil {
|
||||
// Should never happen.. if it does just skip caching the value
|
||||
// FIXME: return error instead, to prevent inconsistencies?
|
||||
return
|
||||
}
|
||||
requestCache.putError(key, err)
|
||||
}
|
||||
|
||||
// checkHTTPSendInterQueryCache checks for the given key's value in the inter-query cache
|
||||
func (c *interQueryCache) checkHTTPSendInterQueryCache() (ast.Value, error) {
|
||||
requestCache := c.bctx.InterQueryBuiltinCache
|
||||
|
||||
value, found := requestCache.Get(c.key)
|
||||
cachedValue, found := requestCache.Get(c.key)
|
||||
if !found {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
value, cerr := requestCache.Clone(cachedValue)
|
||||
if cerr != nil {
|
||||
return nil, handleHTTPSendErr(c.bctx, cerr)
|
||||
}
|
||||
|
||||
c.bctx.Metrics.Counter(httpSendInterQueryCacheHits).Incr()
|
||||
var cachedRespData *interQueryCacheData
|
||||
|
||||
@@ -730,15 +839,12 @@ func (c *interQueryCache) checkHTTPSendInterQueryCache() (ast.Value, error) {
|
||||
return nil, handleHTTPSendErr(c.bctx, err)
|
||||
}
|
||||
|
||||
headers, err := parseResponseHeaders(cachedRespData.Headers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
headers := parseResponseHeaders(cachedRespData.Headers)
|
||||
|
||||
// check with the server if the stale response is still up-to-date.
|
||||
// If server returns a new response (ie. status_code=200), update the cache with the new response
|
||||
// If server returns an unmodified response (ie. status_code=304), update the headers for the existing response
|
||||
result, modified, err := revalidateCachedResponse(c.httpReq, c.httpClient, headers)
|
||||
result, modified, err := revalidateCachedResponse(c.httpReq, c.httpClient, c.key, headers)
|
||||
requestCache.Delete(c.key)
|
||||
if err != nil || result == nil {
|
||||
return nil, err
|
||||
@@ -755,11 +861,16 @@ func (c *interQueryCache) checkHTTPSendInterQueryCache() (ast.Value, error) {
|
||||
}
|
||||
}
|
||||
|
||||
expiresAt, err := expiryFromHeaders(result.Header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if forceCaching(c.forceCacheParams) {
|
||||
createdAt := getCurrentTime(c.bctx)
|
||||
cachedRespData.ExpiresAt = createdAt.Add(time.Second * time.Duration(c.forceCacheParams.forceCacheDurationSeconds))
|
||||
} else {
|
||||
expiresAt, err := expiryFromHeaders(result.Header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cachedRespData.ExpiresAt = expiresAt
|
||||
}
|
||||
cachedRespData.ExpiresAt = expiresAt
|
||||
|
||||
cachingMode, err := getCachingMode(c.key)
|
||||
if err != nil {
|
||||
@@ -777,7 +888,7 @@ func (c *interQueryCache) checkHTTPSendInterQueryCache() (ast.Value, error) {
|
||||
pcv = cachedRespData
|
||||
}
|
||||
|
||||
c.bctx.InterQueryBuiltinCache.Insert(c.key, pcv)
|
||||
c.bctx.InterQueryBuiltinCache.InsertWithExpiry(c.key, pcv, cachedRespData.ExpiresAt)
|
||||
|
||||
return cachedRespData.formatToAST(c.forceJSONDecode, c.forceYAMLDecode)
|
||||
}
|
||||
@@ -813,18 +924,19 @@ func insertIntoHTTPSendInterQueryCache(bctx BuiltinContext, key ast.Value, resp
|
||||
}
|
||||
|
||||
var pcv cache.InterQueryCacheValue
|
||||
|
||||
var pcvData *interQueryCacheData
|
||||
if cachingMode == defaultCachingMode {
|
||||
pcv, err = newInterQueryCacheValue(bctx, resp, respBody, cacheParams)
|
||||
pcv, pcvData, err = newInterQueryCacheValue(bctx, resp, respBody, cacheParams)
|
||||
} else {
|
||||
pcv, err = newInterQueryCacheData(bctx, resp, respBody, cacheParams)
|
||||
pcvData, err = newInterQueryCacheData(bctx, resp, respBody, cacheParams)
|
||||
pcv = pcvData
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
requestCache.Insert(key, pcv)
|
||||
requestCache.InsertWithExpiry(key, pcv, pcvData.ExpiresAt)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -879,6 +991,23 @@ func getBoolValFromReqObj(req ast.Object, key *ast.Term) (bool, error) {
|
||||
return bool(b), nil
|
||||
}
|
||||
|
||||
func getNumberValFromReqObj(req ast.Object, key *ast.Term) (int, error) {
|
||||
term := req.Get(key)
|
||||
if term == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
if t, ok := term.Value.(ast.Number); ok {
|
||||
num, ok := t.Int()
|
||||
if !ok || num < 0 {
|
||||
return 0, fmt.Errorf("invalid value %v for field %v", t.String(), key.String())
|
||||
}
|
||||
return num, nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("invalid value %v for field %v", term.String(), key.String())
|
||||
}
|
||||
|
||||
func getCachingMode(req ast.Object) (cachingMode, error) {
|
||||
key := ast.StringTerm("caching_mode")
|
||||
var s ast.String
|
||||
@@ -902,17 +1031,23 @@ type interQueryCacheValue struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func newInterQueryCacheValue(bctx BuiltinContext, resp *http.Response, respBody []byte, cacheParams *forceCacheParams) (*interQueryCacheValue, error) {
|
||||
func newInterQueryCacheValue(bctx BuiltinContext, resp *http.Response, respBody []byte, cacheParams *forceCacheParams) (*interQueryCacheValue, *interQueryCacheData, error) {
|
||||
data, err := newInterQueryCacheData(bctx, resp, respBody, cacheParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
b, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
return &interQueryCacheValue{Data: b}, nil
|
||||
return &interQueryCacheValue{Data: b}, data, nil
|
||||
}
|
||||
|
||||
func (cb interQueryCacheValue) Clone() (cache.InterQueryCacheValue, error) {
|
||||
dup := make([]byte, len(cb.Data))
|
||||
copy(dup, cb.Data)
|
||||
return &interQueryCacheValue{Data: dup}, nil
|
||||
}
|
||||
|
||||
func (cb interQueryCacheValue) SizeInBytes() int64 {
|
||||
@@ -998,44 +1133,38 @@ func (c *interQueryCacheData) SizeInBytes() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *interQueryCacheData) Clone() (cache.InterQueryCacheValue, error) {
|
||||
dup := make([]byte, len(c.RespBody))
|
||||
copy(dup, c.RespBody)
|
||||
|
||||
return &interQueryCacheData{
|
||||
ExpiresAt: c.ExpiresAt,
|
||||
RespBody: dup,
|
||||
Status: c.Status,
|
||||
StatusCode: c.StatusCode,
|
||||
Headers: c.Headers.Clone()}, nil
|
||||
}
|
||||
|
||||
type responseHeaders struct {
|
||||
date time.Time // origination date and time of response
|
||||
cacheControl map[string]string // response cache-control header
|
||||
maxAge deltaSeconds // max-age cache control directive
|
||||
expires time.Time // date/time after which the response is considered stale
|
||||
etag string // identifier for a specific version of the response
|
||||
lastModified string // date and time response was last modified as per origin server
|
||||
etag string // identifier for a specific version of the response
|
||||
lastModified string // date and time response was last modified as per origin server
|
||||
}
|
||||
|
||||
// deltaSeconds specifies a non-negative integer, representing
|
||||
// time in seconds: http://tools.ietf.org/html/rfc7234#section-1.2.1
|
||||
type deltaSeconds int32
|
||||
|
||||
func parseResponseHeaders(headers http.Header) (*responseHeaders, error) {
|
||||
var err error
|
||||
func parseResponseHeaders(headers http.Header) *responseHeaders {
|
||||
result := responseHeaders{}
|
||||
|
||||
result.date, err = getResponseHeaderDate(headers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result.cacheControl = parseCacheControlHeader(headers)
|
||||
result.maxAge, err = parseMaxAgeCacheDirective(result.cacheControl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result.expires = getResponseHeaderExpires(headers)
|
||||
|
||||
result.etag = headers.Get("etag")
|
||||
|
||||
result.lastModified = headers.Get("last-modified")
|
||||
|
||||
return &result, nil
|
||||
return &result
|
||||
}
|
||||
|
||||
func revalidateCachedResponse(req *http.Request, client *http.Client, headers *responseHeaders) (*http.Response, bool, error) {
|
||||
func revalidateCachedResponse(req *http.Request, client *http.Client, inputReqObj ast.Object, headers *responseHeaders) (*http.Response, bool, error) {
|
||||
etag := headers.etag
|
||||
lastModified := headers.lastModified
|
||||
|
||||
@@ -1053,7 +1182,7 @@ func revalidateCachedResponse(req *http.Request, client *http.Client, headers *r
|
||||
cloneReq.Header.Set("if-modified-since", lastModified)
|
||||
}
|
||||
|
||||
response, err := client.Do(cloneReq)
|
||||
response, err := executeHTTPRequest(cloneReq, client, inputReqObj)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@@ -1233,6 +1362,7 @@ func getResponseHeaders(headers http.Header) map[string]interface{} {
|
||||
type httpRequestExecutor interface {
|
||||
CheckCache() (ast.Value, error)
|
||||
InsertIntoCache(value *http.Response) (ast.Value, error)
|
||||
InsertErrorIntoCache(err error)
|
||||
ExecuteHTTPRequest() (*http.Response, error)
|
||||
}
|
||||
|
||||
@@ -1268,6 +1398,15 @@ func newInterQueryCache(bctx BuiltinContext, key ast.Object, forceCacheParams *f
|
||||
func (c *interQueryCache) CheckCache() (ast.Value, error) {
|
||||
var err error
|
||||
|
||||
// Checking the intra-query cache first ensures consistency of errors and HTTP responses within a query.
|
||||
resp, err := checkHTTPSendCache(c.bctx, c.key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp != nil {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
c.forceJSONDecode, err = getBoolValFromReqObj(c.key, ast.StringTerm("force_json_decode"))
|
||||
if err != nil {
|
||||
return nil, handleHTTPSendErr(c.bctx, err)
|
||||
@@ -1277,14 +1416,14 @@ func (c *interQueryCache) CheckCache() (ast.Value, error) {
|
||||
return nil, handleHTTPSendErr(c.bctx, err)
|
||||
}
|
||||
|
||||
resp, err := c.checkHTTPSendInterQueryCache()
|
||||
|
||||
// fallback to the http send cache if response not found in the inter-query cache or inter-query cache look-up results
|
||||
// in an error
|
||||
if resp == nil || err != nil {
|
||||
return checkHTTPSendCache(c.bctx, c.key), nil
|
||||
resp, err = c.checkHTTPSendInterQueryCache()
|
||||
// Always insert the result of the inter-query cache into the intra-query cache, to maintain consistency within the same query.
|
||||
if err != nil {
|
||||
insertErrorIntoHTTPSendCache(c.bctx, c.key, err)
|
||||
}
|
||||
if resp != nil {
|
||||
insertIntoHTTPSendCache(c.bctx, c.key, resp)
|
||||
}
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
@@ -1295,14 +1434,19 @@ func (c *interQueryCache) InsertIntoCache(value *http.Response) (ast.Value, erro
|
||||
return nil, handleHTTPSendErr(c.bctx, err)
|
||||
}
|
||||
|
||||
// fallback to the http send cache if error encountered while inserting response in inter-query cache
|
||||
err = insertIntoHTTPSendInterQueryCache(c.bctx, c.key, value, respBody, c.forceCacheParams)
|
||||
if err != nil {
|
||||
insertIntoHTTPSendCache(c.bctx, c.key, result)
|
||||
}
|
||||
// Always insert into the intra-query cache, to maintain consistency within the same query.
|
||||
insertIntoHTTPSendCache(c.bctx, c.key, result)
|
||||
|
||||
// We ignore errors when populating the inter-query cache, because we've already populated the intra-cache,
|
||||
// and query consistency is our primary concern.
|
||||
_ = insertIntoHTTPSendInterQueryCache(c.bctx, c.key, value, respBody, c.forceCacheParams)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *interQueryCache) InsertErrorIntoCache(err error) {
|
||||
insertErrorIntoHTTPSendCache(c.bctx, c.key, err)
|
||||
}
|
||||
|
||||
// ExecuteHTTPRequest executes a HTTP request
|
||||
func (c *interQueryCache) ExecuteHTTPRequest() (*http.Response, error) {
|
||||
var err error
|
||||
@@ -1311,7 +1455,7 @@ func (c *interQueryCache) ExecuteHTTPRequest() (*http.Response, error) {
|
||||
return nil, handleHTTPSendErr(c.bctx, err)
|
||||
}
|
||||
|
||||
return executeHTTPRequest(c.httpReq, c.httpClient)
|
||||
return executeHTTPRequest(c.httpReq, c.httpClient, c.key)
|
||||
}
|
||||
|
||||
type intraQueryCache struct {
|
||||
@@ -1325,7 +1469,7 @@ func newIntraQueryCache(bctx BuiltinContext, key ast.Object) (*intraQueryCache,
|
||||
|
||||
// CheckCache checks the cache for the value of the key set on this object
|
||||
func (c *intraQueryCache) CheckCache() (ast.Value, error) {
|
||||
return checkHTTPSendCache(c.bctx, c.key), nil
|
||||
return checkHTTPSendCache(c.bctx, c.key)
|
||||
}
|
||||
|
||||
// InsertIntoCache inserts the key set on this object into the cache with the given value
|
||||
@@ -1351,13 +1495,17 @@ func (c *intraQueryCache) InsertIntoCache(value *http.Response) (ast.Value, erro
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *intraQueryCache) InsertErrorIntoCache(err error) {
|
||||
insertErrorIntoHTTPSendCache(c.bctx, c.key, err)
|
||||
}
|
||||
|
||||
// ExecuteHTTPRequest executes a HTTP request
|
||||
func (c *intraQueryCache) ExecuteHTTPRequest() (*http.Response, error) {
|
||||
httpReq, httpClient, err := createHTTPRequest(c.bctx, c.key)
|
||||
if err != nil {
|
||||
return nil, handleHTTPSendErr(c.bctx, err)
|
||||
}
|
||||
return executeHTTPRequest(httpReq, httpClient)
|
||||
return executeHTTPRequest(httpReq, httpClient, c.key)
|
||||
}
|
||||
|
||||
func useInterQueryCache(req ast.Object) (bool, *forceCacheParams, error) {
|
||||
|
||||
Reference in New Issue
Block a user