mirror of
https://github.com/vee1e/runtimeclass-debugger.git
synced 2026-09-01 10:18:42 +00:00
On timeout the manager goroutine still finishes WaitForCacheSync and closes the synced channel, but nobody reads it: on an unbuffered channel that close blocks forever. A buffer of one lets the goroutine finish.
84 lines
2.9 KiB
Go
84 lines
2.9 KiB
Go
// Package bridge mirrors the RuntimeClass resolution path used by the
|
|
// KubeEdge edged kubelet: a kubeclientbridge whose NodeV1 group is not
|
|
// wired to the metaclient, plus the kubelet's runtimeclass manager.
|
|
package bridge
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
|
|
nodev1 "k8s.io/api/node/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/client-go/kubernetes"
|
|
kubefake "k8s.io/client-go/kubernetes/fake"
|
|
"k8s.io/kubernetes/pkg/kubelet/runtimeclass"
|
|
)
|
|
|
|
// bridgeClient mirrors kubeedge/edge/pkg/edged/kubeclientbridge: a fake
|
|
// clientset whose NodeV1 group is not overridden, so node.k8s.io resources
|
|
// are served from the empty in-memory fake store and never reach the
|
|
// metaclient or the local SQLite store.
|
|
type bridgeClient struct {
|
|
kubernetes.Interface
|
|
}
|
|
|
|
// NewNaiveManager builds the resolution manager the same way edged builds
|
|
// it today: through the kubeclientbridge with NodeV1 backed by the fake
|
|
// store. Lookups against it always return NotFound.
|
|
func NewNaiveManager() (*runtimeclass.Manager, error) {
|
|
return NewManagerForClient(&bridgeClient{Interface: kubefake.NewSimpleClientset()})
|
|
}
|
|
|
|
// NewWiredManager builds the resolution manager as it would be built once
|
|
// NodeV1 is wired to the local store: a client seeded with the classes
|
|
// found locally. Lookups against it resolve class names to handlers.
|
|
func NewWiredManager(classes []nodev1.RuntimeClass) (*runtimeclass.Manager, error) {
|
|
objs := make([]runtime.Object, 0, len(classes))
|
|
for i := range classes {
|
|
objs = append(objs, &classes[i])
|
|
}
|
|
return NewManagerForClient(kubefake.NewSimpleClientset(objs...))
|
|
}
|
|
|
|
// Resolver mirrors the kubelet runtimeclass manager: it resolves a class
|
|
// name to the handler string the kubelet would use.
|
|
type Resolver interface {
|
|
LookupRuntimeHandler(*string) (string, error)
|
|
}
|
|
|
|
// NewManagerForClient builds a kubelet runtimeclass manager over the given
|
|
// client, starts its informer and waits for the cache to sync.
|
|
func NewManagerForClient(client kubernetes.Interface) (*runtimeclass.Manager, error) {
|
|
return NewManagerForClientWithTimeout(client, 10*time.Second)
|
|
}
|
|
|
|
// NewManagerForClientWithTimeout is NewManagerForClient with a configurable
|
|
// cache-sync timeout.
|
|
func NewManagerForClientWithTimeout(client kubernetes.Interface, timeout time.Duration) (*runtimeclass.Manager, error) {
|
|
m := runtimeclass.NewManager(client)
|
|
stop := make(chan struct{})
|
|
m.Start(stop)
|
|
synced := make(chan struct{}, 1)
|
|
go func() {
|
|
m.WaitForCacheSync(stop)
|
|
close(synced)
|
|
}()
|
|
select {
|
|
case <-synced:
|
|
close(stop)
|
|
return m, nil
|
|
case <-time.After(timeout):
|
|
close(stop)
|
|
return nil, errors.New("timed out waiting for runtimeclass informer cache sync")
|
|
}
|
|
}
|
|
|
|
// ResolveHandler applies the upstream kubelet resolution rules:
|
|
// no class name -> empty handler (default runtime); known class -> the
|
|
// class handler string; unknown class -> NotFound.
|
|
func ResolveHandler(m Resolver, name string) (string, error) {
|
|
if name == "" {
|
|
return "", nil
|
|
}
|
|
return m.LookupRuntimeHandler(&name)
|
|
}
|