1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 16:06:56 +00:00
kyverno/pkg/globalcontext/externalapi/entry.go
Khaled Emara 511df7a466
fix(globalcontext): old WaitGroup not stopping (#9813)
* fix(globalcontext): old waitgroup not stopping

Signed-off-by: Khaled Emara <khaled.emara@nirmata.com>

* chore(globalcontext): add AGE

Signed-off-by: Khaled Emara <khaled.emara@nirmata.com>

* feat(globalcontext): add lastRefreshTime

Signed-off-by: Khaled Emara <khaled.emara@nirmata.com>

* fix(globalcontext): unhandled intormer run exception

Signed-off-by: Khaled Emara <khaled.emara@nirmata.com>

* chore(globalcontext): comment wording

Signed-off-by: Khaled Emara <khaled.emara@nirmata.com>

* chore(globalcontext): codegen

Signed-off-by: Khaled Emara <khaled.emara@nirmata.com>

* fix(globalcontext): linter

Signed-off-by: Khaled Emara <khaled.emara@nirmata.com>

---------

Signed-off-by: Khaled Emara <khaled.emara@nirmata.com>
2024-02-27 18:24:39 +00:00

153 lines
3.9 KiB
Go

package externalapi
import (
"context"
"fmt"
"sync"
"time"
"github.com/go-logr/logr"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
kyvernov2alpha1 "github.com/kyverno/kyverno/api/kyverno/v2alpha1"
"github.com/kyverno/kyverno/pkg/client/clientset/versioned"
kyvernov2alpha1listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v2alpha1"
"github.com/kyverno/kyverno/pkg/engine/apicall"
"github.com/kyverno/kyverno/pkg/event"
entryevent "github.com/kyverno/kyverno/pkg/globalcontext/event"
"github.com/kyverno/kyverno/pkg/globalcontext/store"
controllerutils "github.com/kyverno/kyverno/pkg/utils/controller"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/util/retry"
)
type entry struct {
sync.Mutex
data any
err error
stop func()
}
func New(
ctx context.Context,
gce *kyvernov2alpha1.GlobalContextEntry,
eventGen event.Interface,
kyvernoClient versioned.Interface,
gceLister kyvernov2alpha1listers.GlobalContextEntryLister,
logger logr.Logger,
client apicall.ClientInterface,
call kyvernov1.APICall,
period time.Duration,
maxResponseLength int64,
shouldUpdateStatus bool,
) (store.Entry, error) {
var group wait.Group
ctx, cancel := context.WithCancel(ctx)
stop := func() {
// Send stop signal to informer's goroutine
cancel()
// Wait for the group to terminate
group.Wait()
}
e := &entry{
stop: stop,
}
group.StartWithContext(ctx, func(ctx context.Context) {
config := apicall.NewAPICallConfiguration(maxResponseLength)
caller := apicall.NewCaller(logger, "globalcontext", client, config)
wait.UntilWithContext(ctx, func(ctx context.Context) {
if data, err := doCall(ctx, caller, call); err != nil {
e.setData(nil, err)
logger.Error(err, "failed to get data from api caller")
eventGen.Add(entryevent.NewErrorEvent(corev1.ObjectReference{
APIVersion: gce.APIVersion,
Kind: gce.Kind,
Name: gce.Name,
Namespace: gce.Namespace,
UID: gce.UID,
}, entryevent.ReasonAPICallFailure, err))
if shouldUpdateStatus {
if updateErr := updateStatus(ctx, gce.Name, kyvernoClient, false, entryevent.ReasonAPICallFailure); updateErr != nil {
logger.Error(updateErr, "failed to update status")
}
}
} else {
e.setData(data, nil)
if shouldUpdateStatus {
if updateErr := updateStatus(ctx, gce.Name, kyvernoClient, true, "APICallSuccess"); updateErr != nil {
logger.Error(updateErr, "failed to update status")
}
}
}
}, period)
})
return e, nil
}
func (e *entry) Get() (any, error) {
e.Lock()
defer e.Unlock()
if e.err != nil {
return nil, e.err
}
if e.data == nil {
return nil, fmt.Errorf("no data available")
}
return e.data, nil
}
func (e *entry) Stop() {
e.Lock()
defer e.Unlock()
e.stop()
}
func (e *entry) setData(data any, err error) {
e.Lock()
defer e.Unlock()
if err != nil {
e.err = err
} else {
e.data = data
}
}
func doCall(ctx context.Context, caller apicall.Caller, call kyvernov1.APICall) (any, error) {
return caller.Execute(ctx, &call)
}
func updateStatus(ctx context.Context, gceName string, kyvernoClient versioned.Interface, ready bool, reason string) error {
retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
latestGCE, getErr := kyvernoClient.KyvernoV2alpha1().GlobalContextEntries().Get(ctx, gceName, metav1.GetOptions{})
if getErr != nil {
return getErr
}
_, updateErr := controllerutils.UpdateStatus(ctx, latestGCE, kyvernoClient.KyvernoV2alpha1().GlobalContextEntries(), func(latest *kyvernov2alpha1.GlobalContextEntry) error {
if latest == nil {
return fmt.Errorf("failed to update status: %s", latestGCE.Name)
}
latest.Status.SetReady(ready, reason)
if ready {
latest.Status.UpdateRefreshTime()
}
return nil
})
return updateErr
})
return retryErr
}