2024-02-02 12:41:35 +02:00
|
|
|
package externalapi
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2024-02-08 09:46:58 +02:00
|
|
|
"fmt"
|
2024-02-02 12:41:35 +02:00
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/go-logr/logr"
|
|
|
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
2024-02-23 12:34:04 +02:00
|
|
|
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"
|
2024-02-02 12:41:35 +02:00
|
|
|
"github.com/kyverno/kyverno/pkg/engine/apicall"
|
2024-02-23 12:34:04 +02:00
|
|
|
"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"
|
2024-02-02 12:41:35 +02:00
|
|
|
"k8s.io/apimachinery/pkg/util/wait"
|
2024-02-23 12:34:04 +02:00
|
|
|
"k8s.io/client-go/util/retry"
|
2024-02-02 12:41:35 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
type entry struct {
|
|
|
|
sync.Mutex
|
|
|
|
data any
|
2024-02-08 09:46:58 +02:00
|
|
|
err error
|
2024-02-02 12:41:35 +02:00
|
|
|
stop func()
|
|
|
|
}
|
|
|
|
|
2024-02-02 16:35:57 +01:00
|
|
|
func New(
|
|
|
|
ctx context.Context,
|
2024-02-23 12:34:04 +02:00
|
|
|
gce *kyvernov2alpha1.GlobalContextEntry,
|
|
|
|
eventGen event.Interface,
|
|
|
|
kyvernoClient versioned.Interface,
|
|
|
|
gceLister kyvernov2alpha1listers.GlobalContextEntryLister,
|
2024-02-02 16:35:57 +01:00
|
|
|
logger logr.Logger,
|
|
|
|
client apicall.ClientInterface,
|
|
|
|
call kyvernov1.APICall,
|
|
|
|
period time.Duration,
|
|
|
|
maxResponseLength int64,
|
2024-02-23 12:34:04 +02:00
|
|
|
shouldUpdateStatus bool,
|
|
|
|
) (store.Entry, error) {
|
2024-02-02 12:41:35 +02:00
|
|
|
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,
|
|
|
|
}
|
2024-02-23 12:34:04 +02:00
|
|
|
|
2024-02-02 12:41:35 +02:00
|
|
|
group.StartWithContext(ctx, func(ctx context.Context) {
|
2024-02-02 16:35:57 +01:00
|
|
|
config := apicall.NewAPICallConfiguration(maxResponseLength)
|
2024-02-08 09:46:58 +02:00
|
|
|
caller := apicall.NewCaller(logger, "globalcontext", client, config)
|
2024-02-23 12:34:04 +02:00
|
|
|
|
2024-02-02 12:41:35 +02:00
|
|
|
wait.UntilWithContext(ctx, func(ctx context.Context) {
|
|
|
|
if data, err := doCall(ctx, caller, call); err != nil {
|
2024-02-08 09:46:58 +02:00
|
|
|
e.setData(nil, err)
|
2024-02-23 12:34:04 +02:00
|
|
|
|
|
|
|
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")
|
|
|
|
}
|
|
|
|
}
|
2024-02-02 12:41:35 +02:00
|
|
|
} else {
|
2024-02-08 09:46:58 +02:00
|
|
|
e.setData(data, nil)
|
2024-02-23 12:34:04 +02:00
|
|
|
|
|
|
|
if shouldUpdateStatus {
|
|
|
|
if updateErr := updateStatus(ctx, gce.Name, kyvernoClient, true, "APICallSuccess"); updateErr != nil {
|
|
|
|
logger.Error(updateErr, "failed to update status")
|
|
|
|
}
|
|
|
|
}
|
2024-02-02 12:41:35 +02:00
|
|
|
}
|
|
|
|
}, period)
|
|
|
|
})
|
2024-02-23 12:34:04 +02:00
|
|
|
|
2024-02-02 12:41:35 +02:00
|
|
|
return e, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *entry) Get() (any, error) {
|
|
|
|
e.Lock()
|
|
|
|
defer e.Unlock()
|
2024-02-08 09:46:58 +02:00
|
|
|
|
|
|
|
if e.err != nil {
|
|
|
|
return nil, e.err
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.data == nil {
|
|
|
|
return nil, fmt.Errorf("no data available")
|
|
|
|
}
|
|
|
|
|
2024-02-02 12:41:35 +02:00
|
|
|
return e.data, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *entry) Stop() {
|
|
|
|
e.Lock()
|
|
|
|
defer e.Unlock()
|
|
|
|
e.stop()
|
|
|
|
}
|
|
|
|
|
2024-02-08 09:46:58 +02:00
|
|
|
func (e *entry) setData(data any, err error) {
|
2024-02-02 12:41:35 +02:00
|
|
|
e.Lock()
|
|
|
|
defer e.Unlock()
|
2024-02-08 09:46:58 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
e.err = err
|
|
|
|
} else {
|
|
|
|
e.data = data
|
|
|
|
}
|
2024-02-02 12:41:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func doCall(ctx context.Context, caller apicall.Caller, call kyvernov1.APICall) (any, error) {
|
|
|
|
return caller.Execute(ctx, &call)
|
|
|
|
}
|
2024-02-23 12:34:04 +02:00
|
|
|
|
|
|
|
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)
|
2024-02-27 20:24:39 +02:00
|
|
|
if ready {
|
|
|
|
latest.Status.UpdateRefreshTime()
|
|
|
|
}
|
2024-02-23 12:34:04 +02:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
return updateErr
|
|
|
|
})
|
|
|
|
|
|
|
|
return retryErr
|
|
|
|
}
|