1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-05 15:37:19 +00:00
kyverno/pkg/engine/context/deferred.go
Vishal Choudhary b2515154f3
fix: return error in LoadMatching (#8234)
Signed-off-by: Vishal Choudhary <sendtovishalchoudhary@gmail.com>
Co-authored-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
2023-09-04 22:42:27 +00:00

185 lines
3.8 KiB
Go

package context
import (
"regexp"
"github.com/go-logr/logr"
)
type deferredLoader struct {
name string
matcher regexp.Regexp
loader Loader
logger logr.Logger
}
func NewDeferredLoader(name string, loader Loader, logger logr.Logger) (DeferredLoader, error) {
// match on ASCII word boundaries except do not allow starting with a `.`
// this allows `x` to match `x.y` but not `y.x` or `y.x.z`
matcher, err := regexp.Compile(`(?:\A|\z|\s|[^.0-9A-Za-z])` + name + `\b`)
if err != nil {
return nil, err
}
return &deferredLoader{
name: name,
matcher: *matcher,
loader: loader,
logger: logger,
}, nil
}
func (dl *deferredLoader) Name() string {
return dl.name
}
func (dl *deferredLoader) HasLoaded() bool {
return dl.loader.HasLoaded()
}
func (dl *deferredLoader) LoadData() error {
if err := dl.loader.LoadData(); err != nil {
dl.logger.Error(err, "failed to load data", "name", dl.name)
return err
}
return nil
}
func (d *deferredLoader) Matches(query string) bool {
return d.matcher.MatchString(query)
}
type leveledLoader struct {
level int
matched bool
loader DeferredLoader
}
func (cl *leveledLoader) Level() int {
return cl.level
}
func (cl *leveledLoader) Name() string {
return cl.loader.Name()
}
func (cl *leveledLoader) Matches(query string) bool {
return cl.loader.Matches(query)
}
func (cl *leveledLoader) HasLoaded() bool {
return cl.loader.HasLoaded()
}
func (cl *leveledLoader) LoadData() error {
return cl.loader.LoadData()
}
type deferredLoaders struct {
level int
index int
loaders []*leveledLoader
}
func NewDeferredLoaders() DeferredLoaders {
return &deferredLoaders{
loaders: make([]*leveledLoader, 0),
level: -1,
index: -1,
}
}
func (d *deferredLoaders) Add(dl DeferredLoader, level int) {
d.loaders = append(d.loaders, &leveledLoader{level, false, dl})
}
func (d *deferredLoaders) Reset(restore bool, level int) {
d.clearMatches()
for i := 0; i < len(d.loaders); i++ {
l := d.loaders[i]
if l.level > level {
i = d.removeLoader(i)
} else {
if l.loader.HasLoaded() {
// reload data into the current context for restore, and
// for a reset but only if loader is at a prior level
if restore || (l.level < level) {
if err := d.loadData(l, i); err != nil {
logger.Error(err, "failed to reload context entry", "name", l.loader.Name())
}
}
if l.level == level {
i = d.removeLoader(i)
}
} else if !restore {
if l.level == level {
i = d.removeLoader(i)
}
}
}
}
}
// removeLoader removes loader at the specified index
// and returns the prior index
func (d *deferredLoaders) removeLoader(i int) int {
d.loaders = append(d.loaders[:i], d.loaders[i+1:]...)
return i - 1
}
func (d *deferredLoaders) clearMatches() {
for _, dl := range d.loaders {
dl.matched = false
}
}
func (d *deferredLoaders) LoadMatching(query string, level int) error {
if d.level >= 0 {
level = d.level
}
index := len(d.loaders)
if d.index >= 0 {
index = d.index
}
for l, idx := d.match(query, level, index); l != nil; l, idx = d.match(query, level, index) {
if err := d.loadData(l, idx); err != nil {
return err
}
}
return nil
}
func (d *deferredLoaders) loadData(l *leveledLoader, index int) error {
d.setLevelAndIndex(l.level, index)
defer d.setLevelAndIndex(-1, -1)
if err := l.LoadData(); err != nil {
return err
}
return nil
}
func (d *deferredLoaders) setLevelAndIndex(level, index int) {
d.level = level
d.index = index
}
func (d *deferredLoaders) match(query string, level, index int) (*leveledLoader, int) {
for i := 0; i < index; i++ {
dl := d.loaders[i]
if dl.matched || dl.loader.HasLoaded() {
continue
}
if dl.Matches(query) && dl.level <= level {
idx := i
d.loaders[i].matched = true
return dl, idx
}
}
return nil, -1
}