k3s/vendor/github.com/rancher/lasso/pkg/controller/sharedhandler.go
Brad Davidson 54a7c860c7 Update kine for etcd v3.5 compat
Signed-off-by: Brad Davidson <brad.davidson@rancher.com>
2021-08-20 18:47:16 -07:00

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
}