mirror of
https://github.com/k3s-io/k3s.git
synced 2024-06-07 19:41:36 +00:00
54a7c860c7
Signed-off-by: Brad Davidson <brad.davidson@rancher.com>
134 lines
2.3 KiB
Go
134 lines
2.3 KiB
Go
package controller
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
|
|
"k8s.io/apimachinery/pkg/api/meta"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
)
|
|
|
|
var (
|
|
ErrIgnore = errors.New("ignore handler error")
|
|
)
|
|
|
|
type handlerEntry struct {
|
|
id int64
|
|
name string
|
|
handler SharedControllerHandler
|
|
}
|
|
|
|
type SharedHandler struct {
|
|
// keep first because arm32 needs atomic.AddInt64 target to be mem aligned
|
|
idCounter int64
|
|
|
|
lock sync.RWMutex
|
|
handlers []handlerEntry
|
|
}
|
|
|
|
func (h *SharedHandler) Register(ctx context.Context, name string, handler SharedControllerHandler) {
|
|
h.lock.Lock()
|
|
defer h.lock.Unlock()
|
|
|
|
id := atomic.AddInt64(&h.idCounter, 1)
|
|
h.handlers = append(h.handlers, handlerEntry{
|
|
id: id,
|
|
name: name,
|
|
handler: handler,
|
|
})
|
|
|
|
go func() {
|
|
<-ctx.Done()
|
|
|
|
h.lock.Lock()
|
|
defer h.lock.Unlock()
|
|
|
|
for i := range h.handlers {
|
|
if h.handlers[i].id == id {
|
|
h.handlers = append(h.handlers[:i], h.handlers[i+1:]...)
|
|
break
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (h *SharedHandler) OnChange(key string, obj runtime.Object) error {
|
|
var (
|
|
errs errorList
|
|
)
|
|
h.lock.RLock()
|
|
handlers := h.handlers
|
|
h.lock.RUnlock()
|
|
|
|
for _, handler := range handlers {
|
|
newObj, err := handler.handler.OnChange(key, obj)
|
|
if err != nil && !errors.Is(err, ErrIgnore) {
|
|
errs = append(errs, &handlerError{
|
|
HandlerName: handler.name,
|
|
Err: err,
|
|
})
|
|
}
|
|
if newObj != nil && !reflect.ValueOf(newObj).IsNil() {
|
|
meta, err := meta.Accessor(newObj)
|
|
if err == nil && meta.GetUID() != "" {
|
|
// avoid using an empty object
|
|
obj = newObj
|
|
} else if err != nil {
|
|
// assign if we can't determine metadata
|
|
obj = newObj
|
|
}
|
|
}
|
|
}
|
|
|
|
return errs.ToErr()
|
|
}
|
|
|
|
type errorList []error
|
|
|
|
func (e errorList) Error() string {
|
|
buf := strings.Builder{}
|
|
for _, err := range e {
|
|
if buf.Len() > 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(err.Error())
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
func (e errorList) ToErr() error {
|
|
switch len(e) {
|
|
case 0:
|
|
return nil
|
|
case 1:
|
|
return e[0]
|
|
default:
|
|
return e
|
|
}
|
|
}
|
|
|
|
func (e errorList) Cause() error {
|
|
if len(e) > 0 {
|
|
return e[0]
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type handlerError struct {
|
|
HandlerName string
|
|
Err error
|
|
}
|
|
|
|
func (h handlerError) Error() string {
|
|
return fmt.Sprintf("handler %s: %v", h.HandlerName, h.Err)
|
|
}
|
|
|
|
func (h handlerError) Cause() error {
|
|
return h.Err
|
|
}
|