From f8ab2ef50a50cc37153d4b2e2fc755d2c60d4959 Mon Sep 17 00:00:00 2001 From: mprahl Date: Tue, 17 Oct 2023 15:29:58 -0400 Subject: [PATCH] Update to go-template-utils v4.0.0 This adds additional Sprig functions as well as a temporary cache to prevent duplicate API queries in a single ResolveTemplate call. Relates: https://issues.redhat.com/browse/ACM-7652 https://issues.redhat.com/browse/ACM-7398 Signed-off-by: mprahl --- controllers/configurationpolicy_controller.go | 94 ++++++------------- controllers/encryption.go | 2 +- go.mod | 6 +- go.sum | 12 +-- 4 files changed, 34 insertions(+), 80 deletions(-) diff --git a/controllers/configurationpolicy_controller.go b/controllers/configurationpolicy_controller.go index 6095f880..8ffb7901 100644 --- a/controllers/configurationpolicy_controller.go +++ b/controllers/configurationpolicy_controller.go @@ -17,7 +17,7 @@ import ( gocmp "github.com/google/go-cmp/cmp" "github.com/prometheus/client_golang/prometheus" - templates "github.com/stolostron/go-template-utils/v3/pkg/templates" + templates "github.com/stolostron/go-template-utils/v4/pkg/templates" "golang.org/x/mod/semver" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -432,20 +432,6 @@ func (r *ConfigurationPolicyReconciler) shouldEvaluatePolicy( return true } -// getTemplateConfigErrorMsg converts a configuration error from `NewResolver` or `SetEncryptionConfig` to a message -// to be used as a policy noncompliant message. -func getTemplateConfigErrorMsg(err error) string { - if errors.Is(err, templates.ErrInvalidAESKey) || errors.Is(err, templates.ErrAESKeyNotSet) { - return `The "policy-encryption-key" Secret contains an invalid AES key` - } else if errors.Is(err, templates.ErrInvalidIV) { - return fmt.Sprintf(`The "%s" annotation value is not a valid initialization vector`, IVAnnotation) - } - - // This should never happen unless go-template-utils is updated and this doesn't account for a new error - // type that can happen with the input configuration. - return fmt.Sprintf("An unexpected error occurred when configuring the template resolver: %v", err) -} - type objectTemplateDetails struct { kind string name string @@ -885,12 +871,10 @@ func (r *ConfigurationPolicyReconciler) handleObjectTemplates(plc policyv1.Confi r.checkRelatedAndUpdate(plc, relatedObjects, oldRelated, parentStatusUpdateNeeded, false) } - // initialize apiresources for template processing before starting objectTemplate processing - // this is optional but since apiresourcelist is already available, - // use this rather than re-discovering the list for generic-lookup - r.lock.RLock() - tmplResolverCfg := templates.Config{KubeAPIResourceList: r.apiResourceList} - r.lock.RUnlock() + // Cache the result of a missing API resource. Note that it's not actually 10 seconds since the cache is + // cleared automatically after every ResolveTemplate call. + tmplResolverCfg := templates.Config{MissingAPIResourceCacheTTL: 10 * time.Second} + resolveOptions := templates.ResolveOptions{} usedKeyCache := false @@ -906,7 +890,7 @@ func (r *ConfigurationPolicyReconciler) handleObjectTemplates(plc policyv1.Confi return } - tmplResolverCfg.EncryptionConfig = encryptionConfig + resolveOptions.EncryptionConfig = encryptionConfig } annotations := plc.GetAnnotations() @@ -939,15 +923,10 @@ func (r *ConfigurationPolicyReconciler) handleObjectTemplates(plc policyv1.Confi tmplResolverCfg.InputIsYAML = isRawObjTemplate - tmplResolver, err := templates.NewResolver(&r.TargetK8sClient, r.TargetK8sConfig, tmplResolverCfg) + tmplResolver, err := templates.NewResolver(r.TargetK8sConfig, tmplResolverCfg) if err != nil { - // If the encryption key is invalid, clear the cache. - if errors.Is(err, templates.ErrInvalidAESKey) || errors.Is(err, templates.ErrAESKeyNotSet) { - r.cachedEncryptionKey = &cachedEncryptionKey{} - } - - msg := getTemplateConfigErrorMsg(err) - addTemplateErrorViolation("", msg) + log.Error(err, "Failed to instantiate a template resolver") + addTemplateErrorViolation("", err.Error()) return } @@ -986,32 +965,14 @@ func (r *ConfigurationPolicyReconciler) handleObjectTemplates(plc policyv1.Confi if templates.HasTemplate(rawData, "", true) { log.V(1).Info("Processing policy templates") - resolvedTemplate, tplErr := tmplResolver.ResolveTemplate(rawData, nil) - - if errors.Is(tplErr, templates.ErrMissingAPIResource) || - errors.Is(tplErr, templates.ErrMissingAPIResourceInvalidTemplate) { - log.V(2).Info( - "A template encountered an API resource which was not in the API resource list. Refreshing " + - "it and trying again.", - ) - - discoveryErr := r.refreshDiscoveryInfo() - if discoveryErr == nil { - tmplResolver.SetKubeAPIResourceList(r.apiResourceList) - resolvedTemplate, tplErr = tmplResolver.ResolveTemplate(rawData, nil) - } else { - log.V(2).Info( - "Failed to refresh the API discovery information after a template encountered an unknown " + - "API resource type. Continuing with the assumption that the discovery information " + - "was correct.", - ) - } - } + resolvedTemplate, tplErr := tmplResolver.ResolveTemplate(rawData, nil, &resolveOptions) // If the error is because the padding is invalid, this either means the encrypted value was not // generated by the "protect" template function or the AES key is incorrect. Control for a stale // cached key. - if usedKeyCache && errors.Is(tplErr, templates.ErrInvalidPKCS7Padding) { + if usedKeyCache && (errors.Is(tplErr, templates.ErrInvalidPKCS7Padding) || + errors.Is(tplErr, templates.ErrInvalidAESKey) || + errors.Is(tplErr, templates.ErrAESKeyNotSet)) { log.V(2).Info( "The template decryption failed likely due to an invalid encryption key, will refresh " + "the encryption key cache and try the decryption again", @@ -1025,26 +986,25 @@ func (r *ConfigurationPolicyReconciler) handleObjectTemplates(plc policyv1.Confi return } - tmplResolverCfg.EncryptionConfig = encryptionConfig + resolveOptions.EncryptionConfig = encryptionConfig - err := tmplResolver.SetEncryptionConfig(encryptionConfig) - if err != nil { - // If the encryption key is invalid, clear the cache. - if errors.Is(err, templates.ErrInvalidAESKey) || errors.Is(err, templates.ErrAESKeyNotSet) { - r.cachedEncryptionKey = &cachedEncryptionKey{} - } + resolvedTemplate, tplErr = tmplResolver.ResolveTemplate(rawData, nil, &resolveOptions) + } - msg := getTemplateConfigErrorMsg(err) - addTemplateErrorViolation("", msg) + if tplErr != nil { + var msg string - return + if errors.Is(tplErr, templates.ErrInvalidAESKey) || errors.Is(tplErr, templates.ErrAESKeyNotSet) { + msg = `The "policy-encryption-key" Secret contains an invalid AES key` + } else if errors.Is(tplErr, templates.ErrInvalidIV) { + msg = fmt.Sprintf( + `The "%s" annotation value is not a valid initialization vector`, IVAnnotation, + ) + } else { + msg = tplErr.Error() } - resolvedTemplate, tplErr = tmplResolver.ResolveTemplate(rawData, nil) - } - - if tplErr != nil { - addTemplateErrorViolation("", tplErr.Error()) + addTemplateErrorViolation("", msg) return } diff --git a/controllers/encryption.go b/controllers/encryption.go index b6e4ef81..54e1f03c 100644 --- a/controllers/encryption.go +++ b/controllers/encryption.go @@ -8,7 +8,7 @@ import ( "encoding/base64" "fmt" - "github.com/stolostron/go-template-utils/v3/pkg/templates" + "github.com/stolostron/go-template-utils/v4/pkg/templates" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" diff --git a/go.mod b/go.mod index e5656633..4f1e3be0 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/prometheus/client_golang v1.15.1 github.com/spf13/pflag v1.0.5 github.com/stolostron/go-log-utils v0.1.2 - github.com/stolostron/go-template-utils/v3 v3.2.1 + github.com/stolostron/go-template-utils/v4 v4.0.0 github.com/stretchr/testify v1.8.1 golang.org/x/mod v0.10.0 k8s.io/api v0.27.1 @@ -57,7 +57,6 @@ require ( github.com/google/uuid v1.3.0 // indirect github.com/huandu/xstrings v1.4.0 // indirect github.com/imdario/mergo v0.3.15 // indirect - github.com/jellydator/ttlcache/v3 v3.0.1 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.7.7 // indirect @@ -76,7 +75,7 @@ require ( github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.0 // indirect github.com/spf13/cast v1.5.0 // indirect - github.com/stolostron/kubernetes-dependency-watches v0.2.1 // indirect + github.com/stolostron/kubernetes-dependency-watches v0.5.1 // indirect github.com/xlab/treeprint v1.1.0 // indirect go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect go.uber.org/atomic v1.11.0 // indirect @@ -85,7 +84,6 @@ require ( golang.org/x/crypto v0.8.0 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect - golang.org/x/sync v0.2.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/term v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect diff --git a/go.sum b/go.sum index 30e986be..db5ec314 100644 --- a/go.sum +++ b/go.sum @@ -122,8 +122,6 @@ github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= -github.com/jellydator/ttlcache/v3 v3.0.1 h1:cHgCSMS7TdQcoprXnWUptJZzyFsqs18Lt8VVhRuZYVU= -github.com/jellydator/ttlcache/v3 v3.0.1/go.mod h1:WwTaEmcXQ3MTjOm4bsZoDFiCu/hMvNWLO1w67RXz6h4= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= @@ -195,10 +193,10 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stolostron/go-log-utils v0.1.2 h1:7l1aJWvBqU2+DUyimcslT5SJpdygVY/clRDmX5sO29c= github.com/stolostron/go-log-utils v0.1.2/go.mod h1:8zrB8UJmp1rXhv3Ck9bBl5SpNfKk3SApeElbo96YRtQ= -github.com/stolostron/go-template-utils/v3 v3.2.1 h1:wRiCgHG1pcRYa4ZOb/rbzP45Quh8ChNrqXjHDsSaTQg= -github.com/stolostron/go-template-utils/v3 v3.2.1/go.mod h1:D9uOV787ztGsIqh4FxZzkPfBQgy2zz9vK0cv8uDO5vM= -github.com/stolostron/kubernetes-dependency-watches v0.2.1 h1:42oLxw+wm/LxHj/3WkJOpyry6dy9Svwgm6jHCttQ6qs= -github.com/stolostron/kubernetes-dependency-watches v0.2.1/go.mod h1:u2NLFgX12/XwNJvxBpqEhfwGwx8dHWGPxzpDy5L3DIk= +github.com/stolostron/go-template-utils/v4 v4.0.0 h1:gvSfhXIoymo5Ql9MH/ofTTOtBVkaUBq8HokCGR4xkkc= +github.com/stolostron/go-template-utils/v4 v4.0.0/go.mod h1:svIOPUJpG/ObRn3WwZMBGMEMsBgKH8LVfhsaIArgAAQ= +github.com/stolostron/kubernetes-dependency-watches v0.5.1 h1:NZ9N5/VWtwKcawgg4TGI4A5+weSkLrXZMjU7w91xfvU= +github.com/stolostron/kubernetes-dependency-watches v0.5.1/go.mod h1:8vRsL1GGBw0jjCwP8CH8d30NVNYKhUy0rdBSQZ2znx8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= @@ -275,8 +273,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=