mirror of https://github.com/k3s-io/k3s
Merge pull request #73285 from tallclair/typed-runtimeclass
Migrate RuntimeClass support to the generated typed clientpull/564/head
commit
6a149864fb
|
@ -122,7 +122,6 @@ go_library(
|
||||||
"//staging/src/k8s.io/apiserver/pkg/server/healthz:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/server/healthz:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/util/flag:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/util/flag:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/dynamic:go_default_library",
|
|
||||||
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/kubernetes/typed/authentication/v1beta1:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes/typed/authentication/v1beta1:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/kubernetes/typed/authorization/v1beta1:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes/typed/authorization/v1beta1:go_default_library",
|
||||||
|
@ -136,6 +135,7 @@ go_library(
|
||||||
"//staging/src/k8s.io/cloud-provider:go_default_library",
|
"//staging/src/k8s.io/cloud-provider:go_default_library",
|
||||||
"//staging/src/k8s.io/csi-api/pkg/client/clientset/versioned:go_default_library",
|
"//staging/src/k8s.io/csi-api/pkg/client/clientset/versioned:go_default_library",
|
||||||
"//staging/src/k8s.io/kubelet/config/v1beta1:go_default_library",
|
"//staging/src/k8s.io/kubelet/config/v1beta1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/node-api/pkg/client/clientset/versioned:go_default_library",
|
||||||
"//vendor/github.com/coreos/go-systemd/daemon:go_default_library",
|
"//vendor/github.com/coreos/go-systemd/daemon:go_default_library",
|
||||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||||
"//vendor/github.com/spf13/pflag:go_default_library",
|
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||||
|
|
|
@ -48,7 +48,6 @@ import (
|
||||||
"k8s.io/apiserver/pkg/server/healthz"
|
"k8s.io/apiserver/pkg/server/healthz"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/apiserver/pkg/util/flag"
|
"k8s.io/apiserver/pkg/util/flag"
|
||||||
"k8s.io/client-go/dynamic"
|
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
certificatesclient "k8s.io/client-go/kubernetes/typed/certificates/v1beta1"
|
certificatesclient "k8s.io/client-go/kubernetes/typed/certificates/v1beta1"
|
||||||
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
|
@ -95,6 +94,7 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/util/rlimit"
|
"k8s.io/kubernetes/pkg/util/rlimit"
|
||||||
"k8s.io/kubernetes/pkg/version"
|
"k8s.io/kubernetes/pkg/version"
|
||||||
"k8s.io/kubernetes/pkg/version/verflag"
|
"k8s.io/kubernetes/pkg/version/verflag"
|
||||||
|
nodeapiclientset "k8s.io/node-api/pkg/client/clientset/versioned"
|
||||||
"k8s.io/utils/exec"
|
"k8s.io/utils/exec"
|
||||||
"k8s.io/utils/nsenter"
|
"k8s.io/utils/nsenter"
|
||||||
)
|
)
|
||||||
|
@ -545,12 +545,11 @@ func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies, stopCh <-chan
|
||||||
switch {
|
switch {
|
||||||
case standaloneMode:
|
case standaloneMode:
|
||||||
kubeDeps.KubeClient = nil
|
kubeDeps.KubeClient = nil
|
||||||
kubeDeps.DynamicKubeClient = nil
|
|
||||||
kubeDeps.EventClient = nil
|
kubeDeps.EventClient = nil
|
||||||
kubeDeps.HeartbeatClient = nil
|
kubeDeps.HeartbeatClient = nil
|
||||||
klog.Warningf("standalone mode, no API client")
|
klog.Warningf("standalone mode, no API client")
|
||||||
|
|
||||||
case kubeDeps.KubeClient == nil, kubeDeps.EventClient == nil, kubeDeps.HeartbeatClient == nil, kubeDeps.DynamicKubeClient == nil:
|
case kubeDeps.KubeClient == nil, kubeDeps.EventClient == nil, kubeDeps.HeartbeatClient == nil:
|
||||||
clientConfig, closeAllConns, err := buildKubeletClientConfig(s, nodeName)
|
clientConfig, closeAllConns, err := buildKubeletClientConfig(s, nodeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -562,11 +561,6 @@ func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies, stopCh <-chan
|
||||||
return fmt.Errorf("failed to initialize kubelet client: %v", err)
|
return fmt.Errorf("failed to initialize kubelet client: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
kubeDeps.DynamicKubeClient, err = dynamic.NewForConfig(clientConfig)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to initialize kubelet dynamic client: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// make a separate client for events
|
// make a separate client for events
|
||||||
eventClientConfig := *clientConfig
|
eventClientConfig := *clientConfig
|
||||||
eventClientConfig.QPS = float32(s.EventRecordQPS)
|
eventClientConfig.QPS = float32(s.EventRecordQPS)
|
||||||
|
@ -593,12 +587,17 @@ func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies, stopCh <-chan
|
||||||
}
|
}
|
||||||
|
|
||||||
// CRDs are JSON only, and client renegotiation for streaming is not correct as per #67803
|
// CRDs are JSON only, and client renegotiation for streaming is not correct as per #67803
|
||||||
csiClientConfig := restclient.CopyConfig(clientConfig)
|
crdClientConfig := restclient.CopyConfig(clientConfig)
|
||||||
csiClientConfig.ContentType = "application/json"
|
crdClientConfig.ContentType = "application/json"
|
||||||
kubeDeps.CSIClient, err = csiclientset.NewForConfig(csiClientConfig)
|
kubeDeps.CSIClient, err = csiclientset.NewForConfig(crdClientConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to initialize kubelet storage client: %v", err)
|
return fmt.Errorf("failed to initialize kubelet storage client: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kubeDeps.NodeAPIClient, err = nodeapiclientset.NewForConfig(crdClientConfig)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to initialize kubelet node-api client: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the kubelet config controller is available, and dynamic config is enabled, start the config and status sync loops
|
// If the kubelet config controller is available, and dynamic config is enabled, start the config and status sync loops
|
||||||
|
|
|
@ -131,7 +131,6 @@ go_library(
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/dynamic:go_default_library",
|
|
||||||
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
|
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
|
||||||
|
@ -141,6 +140,7 @@ go_library(
|
||||||
"//staging/src/k8s.io/client-go/util/flowcontrol:go_default_library",
|
"//staging/src/k8s.io/client-go/util/flowcontrol:go_default_library",
|
||||||
"//staging/src/k8s.io/cloud-provider:go_default_library",
|
"//staging/src/k8s.io/cloud-provider:go_default_library",
|
||||||
"//staging/src/k8s.io/csi-api/pkg/client/clientset/versioned:go_default_library",
|
"//staging/src/k8s.io/csi-api/pkg/client/clientset/versioned:go_default_library",
|
||||||
|
"//staging/src/k8s.io/node-api/pkg/client/clientset/versioned:go_default_library",
|
||||||
"//third_party/forked/golang/expansion:go_default_library",
|
"//third_party/forked/golang/expansion:go_default_library",
|
||||||
"//vendor/github.com/golang/groupcache/lru:go_default_library",
|
"//vendor/github.com/golang/groupcache/lru:go_default_library",
|
||||||
"//vendor/github.com/google/cadvisor/events:go_default_library",
|
"//vendor/github.com/google/cadvisor/events:go_default_library",
|
||||||
|
|
|
@ -43,7 +43,6 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/client-go/dynamic"
|
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
corelisters "k8s.io/client-go/listers/core/v1"
|
corelisters "k8s.io/client-go/listers/core/v1"
|
||||||
|
@ -114,6 +113,7 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/util/oom"
|
"k8s.io/kubernetes/pkg/util/oom"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
"k8s.io/kubernetes/pkg/volume/csi"
|
"k8s.io/kubernetes/pkg/volume/csi"
|
||||||
|
nodeapiclientset "k8s.io/node-api/pkg/client/clientset/versioned"
|
||||||
utilexec "k8s.io/utils/exec"
|
utilexec "k8s.io/utils/exec"
|
||||||
"k8s.io/utils/integer"
|
"k8s.io/utils/integer"
|
||||||
)
|
)
|
||||||
|
@ -249,7 +249,7 @@ type Dependencies struct {
|
||||||
OnHeartbeatFailure func()
|
OnHeartbeatFailure func()
|
||||||
KubeClient clientset.Interface
|
KubeClient clientset.Interface
|
||||||
CSIClient csiclientset.Interface
|
CSIClient csiclientset.Interface
|
||||||
DynamicKubeClient dynamic.Interface
|
NodeAPIClient nodeapiclientset.Interface
|
||||||
Mounter mount.Interface
|
Mounter mount.Interface
|
||||||
OOMAdjuster *oom.OOMAdjuster
|
OOMAdjuster *oom.OOMAdjuster
|
||||||
OSInterface kubecontainer.OSInterface
|
OSInterface kubecontainer.OSInterface
|
||||||
|
@ -658,8 +658,8 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
|
||||||
}
|
}
|
||||||
klet.runtimeService = runtimeService
|
klet.runtimeService = runtimeService
|
||||||
|
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.RuntimeClass) && kubeDeps.DynamicKubeClient != nil {
|
if utilfeature.DefaultFeatureGate.Enabled(features.RuntimeClass) && kubeDeps.NodeAPIClient != nil {
|
||||||
klet.runtimeClassManager = runtimeclass.NewManager(kubeDeps.DynamicKubeClient)
|
klet.runtimeClassManager = runtimeclass.NewManager(kubeDeps.NodeAPIClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime, err := kuberuntime.NewKubeGenericRuntimeManager(
|
runtime, err := kuberuntime.NewKubeGenericRuntimeManager(
|
||||||
|
@ -1426,7 +1426,7 @@ func (kl *Kubelet) Run(updates <-chan kubetypes.PodUpdate) {
|
||||||
|
|
||||||
// Start syncing RuntimeClasses if enabled.
|
// Start syncing RuntimeClasses if enabled.
|
||||||
if kl.runtimeClassManager != nil {
|
if kl.runtimeClassManager != nil {
|
||||||
go kl.runtimeClassManager.Run(wait.NeverStop)
|
kl.runtimeClassManager.Start(wait.NeverStop)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the pod lifecycle event generator.
|
// Start the pod lifecycle event generator.
|
||||||
|
|
|
@ -61,8 +61,8 @@ func TestCreatePodSandbox(t *testing.T) {
|
||||||
func TestCreatePodSandbox_RuntimeClass(t *testing.T) {
|
func TestCreatePodSandbox_RuntimeClass(t *testing.T) {
|
||||||
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RuntimeClass, true)()
|
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RuntimeClass, true)()
|
||||||
|
|
||||||
rcm := runtimeclass.NewManager(rctest.NewPopulatedDynamicClient())
|
rcm := runtimeclass.NewManager(rctest.NewPopulatedClient())
|
||||||
defer rctest.StartManagerSync(t, rcm)()
|
defer rctest.StartManagerSync(rcm)()
|
||||||
|
|
||||||
fakeRuntime, _, m, err := createTestRuntimeManager()
|
fakeRuntime, _, m, err := createTestRuntimeManager()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -7,12 +7,10 @@ go_library(
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/dynamic:go_default_library",
|
"//staging/src/k8s.io/node-api/pkg/client/clientset/versioned:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
|
"//staging/src/k8s.io/node-api/pkg/client/informers/externalversions:go_default_library",
|
||||||
|
"//staging/src/k8s.io/node-api/pkg/client/listers/node/v1alpha1:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -20,12 +20,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/client-go/dynamic"
|
nodeapiclient "k8s.io/node-api/pkg/client/clientset/versioned"
|
||||||
"k8s.io/client-go/tools/cache"
|
nodeapiinformer "k8s.io/node-api/pkg/client/informers/externalversions"
|
||||||
|
nodev1alpha1 "k8s.io/node-api/pkg/client/listers/node/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -38,33 +36,36 @@ var (
|
||||||
|
|
||||||
// Manager caches RuntimeClass API objects, and provides accessors to the Kubelet.
|
// Manager caches RuntimeClass API objects, and provides accessors to the Kubelet.
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
informer cache.SharedInformer
|
informerFactory nodeapiinformer.SharedInformerFactory
|
||||||
|
lister nodev1alpha1.RuntimeClassLister
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager returns a new RuntimeClass Manager. Run must be called before the manager can be used.
|
// NewManager returns a new RuntimeClass Manager. Run must be called before the manager can be used.
|
||||||
func NewManager(client dynamic.Interface) *Manager {
|
func NewManager(client nodeapiclient.Interface) *Manager {
|
||||||
rc := client.Resource(runtimeClassGVR)
|
const resyncPeriod = 0
|
||||||
lw := &cache.ListWatch{
|
factory := nodeapiinformer.NewSharedInformerFactory(client, resyncPeriod)
|
||||||
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
|
lister := factory.Node().V1alpha1().RuntimeClasses().Lister()
|
||||||
return rc.List(options)
|
|
||||||
},
|
|
||||||
WatchFunc: rc.Watch,
|
|
||||||
}
|
|
||||||
informer := cache.NewSharedInformer(lw, &unstructured.Unstructured{}, 0)
|
|
||||||
|
|
||||||
return &Manager{
|
return &Manager{
|
||||||
informer: informer,
|
informerFactory: factory,
|
||||||
|
lister: lister,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run starts syncing the RuntimeClass cache with the apiserver.
|
// Start starts syncing the RuntimeClass cache with the apiserver.
|
||||||
func (m *Manager) Run(stopCh <-chan struct{}) {
|
func (m *Manager) Start(stopCh <-chan struct{}) {
|
||||||
m.informer.Run(stopCh)
|
m.informerFactory.Start(stopCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitForCacheSync exposes the WaitForCacheSync method on the informer factory for testing
|
||||||
|
// purposes.
|
||||||
|
func (m *Manager) WaitForCacheSync(stopCh <-chan struct{}) {
|
||||||
|
m.informerFactory.WaitForCacheSync(stopCh)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupRuntimeHandler returns the RuntimeHandler string associated with the given RuntimeClass
|
// LookupRuntimeHandler returns the RuntimeHandler string associated with the given RuntimeClass
|
||||||
// name (or the default of "" for nil). If the RuntimeClass is not found, it returns an
|
// name (or the default of "" for nil). If the RuntimeClass is not found, it returns an
|
||||||
// apierrors.NotFound error.
|
// errors.NotFound error.
|
||||||
func (m *Manager) LookupRuntimeHandler(runtimeClassName *string) (string, error) {
|
func (m *Manager) LookupRuntimeHandler(runtimeClassName *string) (string, error) {
|
||||||
if runtimeClassName == nil || *runtimeClassName == "" {
|
if runtimeClassName == nil || *runtimeClassName == "" {
|
||||||
// The default RuntimeClass always resolves to the empty runtime handler.
|
// The default RuntimeClass always resolves to the empty runtime handler.
|
||||||
|
@ -72,26 +73,18 @@ func (m *Manager) LookupRuntimeHandler(runtimeClassName *string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
name := *runtimeClassName
|
name := *runtimeClassName
|
||||||
item, exists, err := m.informer.GetStore().GetByKey(name)
|
|
||||||
|
rc, err := m.lister.Get(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
return "", fmt.Errorf("Failed to lookup RuntimeClass %s: %v", name, err)
|
return "", fmt.Errorf("Failed to lookup RuntimeClass %s: %v", name, err)
|
||||||
}
|
}
|
||||||
if !exists {
|
|
||||||
return "", errors.NewNotFound(schema.GroupResource{
|
|
||||||
Group: runtimeClassGVR.Group,
|
|
||||||
Resource: runtimeClassGVR.Resource,
|
|
||||||
}, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
rc, ok := item.(*unstructured.Unstructured)
|
handler := rc.Spec.RuntimeHandler
|
||||||
if !ok {
|
if handler == nil {
|
||||||
return "", fmt.Errorf("unexpected RuntimeClass type %T", item)
|
return "", nil
|
||||||
}
|
}
|
||||||
|
return *handler, nil
|
||||||
handler, _, err := unstructured.NestedString(rc.Object, "spec", "runtimeHandler")
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("Invalid RuntimeClass object: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return handler, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/kubelet/runtimeclass"
|
"k8s.io/kubernetes/pkg/kubelet/runtimeclass"
|
||||||
rctest "k8s.io/kubernetes/pkg/kubelet/runtimeclass/testing"
|
rctest "k8s.io/kubernetes/pkg/kubelet/runtimeclass/testing"
|
||||||
"k8s.io/utils/pointer"
|
"k8s.io/utils/pointer"
|
||||||
|
@ -36,12 +37,11 @@ func TestLookupRuntimeHandler(t *testing.T) {
|
||||||
{rcn: pointer.StringPtr(""), expected: ""},
|
{rcn: pointer.StringPtr(""), expected: ""},
|
||||||
{rcn: pointer.StringPtr(rctest.EmptyRuntimeClass), expected: ""},
|
{rcn: pointer.StringPtr(rctest.EmptyRuntimeClass), expected: ""},
|
||||||
{rcn: pointer.StringPtr(rctest.SandboxRuntimeClass), expected: "kata-containers"},
|
{rcn: pointer.StringPtr(rctest.SandboxRuntimeClass), expected: "kata-containers"},
|
||||||
{rcn: pointer.StringPtr(rctest.InvalidRuntimeClass), expectError: true},
|
|
||||||
{rcn: pointer.StringPtr("phantom"), expectError: true},
|
{rcn: pointer.StringPtr("phantom"), expectError: true},
|
||||||
}
|
}
|
||||||
|
|
||||||
manager := runtimeclass.NewManager(rctest.NewPopulatedDynamicClient())
|
manager := runtimeclass.NewManager(rctest.NewPopulatedClient())
|
||||||
defer rctest.StartManagerSync(t, manager)()
|
defer rctest.StartManagerSync(manager)()
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
tname := "nil"
|
tname := "nil"
|
||||||
|
|
|
@ -7,14 +7,10 @@ go_library(
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/kubelet/runtimeclass:go_default_library",
|
"//pkg/kubelet/runtimeclass:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
"//staging/src/k8s.io/node-api/pkg/apis/node/v1alpha1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//staging/src/k8s.io/node-api/pkg/client/clientset/versioned:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
"//staging/src/k8s.io/node-api/pkg/client/clientset/versioned/fake:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/dynamic:go_default_library",
|
|
||||||
"//staging/src/k8s.io/client-go/dynamic/fake:go_default_library",
|
|
||||||
"//vendor/github.com/stretchr/testify/require:go_default_library",
|
|
||||||
"//vendor/k8s.io/utils/pointer:go_default_library",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -17,18 +17,11 @@ limitations under the License.
|
||||||
package testing
|
package testing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
"k8s.io/client-go/dynamic"
|
|
||||||
fakedynamic "k8s.io/client-go/dynamic/fake"
|
|
||||||
"k8s.io/kubernetes/pkg/kubelet/runtimeclass"
|
"k8s.io/kubernetes/pkg/kubelet/runtimeclass"
|
||||||
"k8s.io/utils/pointer"
|
nodev1alpha1 "k8s.io/node-api/pkg/apis/node/v1alpha1"
|
||||||
|
nodeapiclient "k8s.io/node-api/pkg/client/clientset/versioned"
|
||||||
|
nodeapifake "k8s.io/node-api/pkg/client/clientset/versioned/fake"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -39,64 +32,38 @@ const (
|
||||||
|
|
||||||
// EmptyRuntimeClass is a valid RuntimeClass without a handler pre-populated in the populated dynamic client.
|
// EmptyRuntimeClass is a valid RuntimeClass without a handler pre-populated in the populated dynamic client.
|
||||||
EmptyRuntimeClass = "native"
|
EmptyRuntimeClass = "native"
|
||||||
// InvalidRuntimeClass is an invalid RuntimeClass pre-populated in the populated dynamic client.
|
|
||||||
InvalidRuntimeClass = "foo"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewPopulatedDynamicClient creates a dynamic client for use with the runtimeclass.Manager,
|
// NewPopulatedClient creates a node-api client for use with the runtimeclass.Manager,
|
||||||
// and populates it with a few test RuntimeClass objects.
|
// and populates it with a few test RuntimeClass objects.
|
||||||
func NewPopulatedDynamicClient() dynamic.Interface {
|
func NewPopulatedClient() nodeapiclient.Interface {
|
||||||
invalidRC := NewUnstructuredRuntimeClass(InvalidRuntimeClass, "")
|
return nodeapifake.NewSimpleClientset(
|
||||||
invalidRC.Object["spec"].(map[string]interface{})["runtimeHandler"] = true
|
NewRuntimeClass(EmptyRuntimeClass, ""),
|
||||||
|
NewRuntimeClass(SandboxRuntimeClass, SandboxRuntimeHandler),
|
||||||
client := fakedynamic.NewSimpleDynamicClient(runtime.NewScheme(),
|
|
||||||
NewUnstructuredRuntimeClass(EmptyRuntimeClass, ""),
|
|
||||||
NewUnstructuredRuntimeClass(SandboxRuntimeClass, SandboxRuntimeHandler),
|
|
||||||
invalidRC,
|
|
||||||
)
|
)
|
||||||
return client
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartManagerSync runs the manager, and waits for startup by polling for the expected "native"
|
// StartManagerSync starts the manager, and waits for the informer cache to sync.
|
||||||
// RuntimeClass to be populated. Returns a function to stop the manager, which should be called with
|
// Returns a function to stop the manager, which should be called with a defer:
|
||||||
// a defer:
|
|
||||||
// defer StartManagerSync(t, m)()
|
// defer StartManagerSync(t, m)()
|
||||||
// Any errors are considered fatal to the test.
|
func StartManagerSync(m *runtimeclass.Manager) func() {
|
||||||
func StartManagerSync(t *testing.T, m *runtimeclass.Manager) func() {
|
|
||||||
stopCh := make(chan struct{})
|
stopCh := make(chan struct{})
|
||||||
go m.Run(stopCh)
|
m.Start(stopCh)
|
||||||
|
m.WaitForCacheSync(stopCh)
|
||||||
// Wait for informer to populate.
|
|
||||||
err := wait.PollImmediate(100*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
|
|
||||||
_, err := m.LookupRuntimeHandler(pointer.StringPtr(EmptyRuntimeClass))
|
|
||||||
if err != nil {
|
|
||||||
if errors.IsNotFound(err) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
})
|
|
||||||
require.NoError(t, err, "Failed to start manager")
|
|
||||||
|
|
||||||
return func() {
|
return func() {
|
||||||
close(stopCh)
|
close(stopCh)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUnstructuredRuntimeClass is a helper to generate an unstructured RuntimeClass resource with
|
// NewRuntimeClass is a helper to generate a RuntimeClass resource with
|
||||||
// the given name & handler.
|
// the given name & handler.
|
||||||
func NewUnstructuredRuntimeClass(name, handler string) *unstructured.Unstructured {
|
func NewRuntimeClass(name, handler string) *nodev1alpha1.RuntimeClass {
|
||||||
return &unstructured.Unstructured{
|
return &nodev1alpha1.RuntimeClass{
|
||||||
Object: map[string]interface{}{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
"apiVersion": "node.k8s.io/v1alpha1",
|
Name: name,
|
||||||
"kind": "RuntimeClass",
|
},
|
||||||
"metadata": map[string]interface{}{
|
Spec: nodev1alpha1.RuntimeClassSpec{
|
||||||
"name": name,
|
RuntimeHandler: &handler,
|
||||||
},
|
|
||||||
"spec": map[string]interface{}{
|
|
||||||
"runtimeHandler": handler,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,6 +129,7 @@ go_library(
|
||||||
"//staging/src/k8s.io/client-go/util/retry:go_default_library",
|
"//staging/src/k8s.io/client-go/util/retry:go_default_library",
|
||||||
"//staging/src/k8s.io/csi-api/pkg/client/clientset/versioned:go_default_library",
|
"//staging/src/k8s.io/csi-api/pkg/client/clientset/versioned:go_default_library",
|
||||||
"//staging/src/k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset:go_default_library",
|
"//staging/src/k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset:go_default_library",
|
||||||
|
"//staging/src/k8s.io/node-api/pkg/client/clientset/versioned:go_default_library",
|
||||||
"//test/e2e/framework/ginkgowrapper:go_default_library",
|
"//test/e2e/framework/ginkgowrapper:go_default_library",
|
||||||
"//test/e2e/framework/metrics:go_default_library",
|
"//test/e2e/framework/metrics:go_default_library",
|
||||||
"//test/e2e/framework/testfiles:go_default_library",
|
"//test/e2e/framework/testfiles:go_default_library",
|
||||||
|
|
|
@ -52,6 +52,7 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||||
"k8s.io/kubernetes/test/e2e/framework/metrics"
|
"k8s.io/kubernetes/test/e2e/framework/metrics"
|
||||||
testutils "k8s.io/kubernetes/test/utils"
|
testutils "k8s.io/kubernetes/test/utils"
|
||||||
|
nodeapiclient "k8s.io/node-api/pkg/client/clientset/versioned"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
@ -78,6 +79,7 @@ type Framework struct {
|
||||||
KubemarkExternalClusterClientSet clientset.Interface
|
KubemarkExternalClusterClientSet clientset.Interface
|
||||||
APIExtensionsClientSet apiextensionsclient.Interface
|
APIExtensionsClientSet apiextensionsclient.Interface
|
||||||
CSIClientSet csi.Interface
|
CSIClientSet csi.Interface
|
||||||
|
NodeAPIClientSet nodeapiclient.Interface
|
||||||
|
|
||||||
InternalClientset *internalclientset.Clientset
|
InternalClientset *internalclientset.Clientset
|
||||||
AggregatorClient *aggregatorclient.Clientset
|
AggregatorClient *aggregatorclient.Clientset
|
||||||
|
@ -197,6 +199,9 @@ func (f *Framework) BeforeEach() {
|
||||||
jsonConfig.ContentType = "application/json"
|
jsonConfig.ContentType = "application/json"
|
||||||
f.CSIClientSet, err = csi.NewForConfig(jsonConfig)
|
f.CSIClientSet, err = csi.NewForConfig(jsonConfig)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
// node.k8s.io is also based on CRD
|
||||||
|
f.NodeAPIClientSet, err = nodeapiclient.NewForConfig(jsonConfig)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
// create scales getter, set GroupVersion and NegotiatedSerializer to default values
|
// create scales getter, set GroupVersion and NegotiatedSerializer to default values
|
||||||
// as they are required when creating a REST client.
|
// as they are required when creating a REST client.
|
||||||
|
|
|
@ -69,14 +69,15 @@ var _ = SIGDescribe("RuntimeClass [Feature:RuntimeClass]", func() {
|
||||||
|
|
||||||
It("should reject a Pod requesting a deleted RuntimeClass", func() {
|
It("should reject a Pod requesting a deleted RuntimeClass", func() {
|
||||||
rcName := createRuntimeClass(f, "delete-me", "")
|
rcName := createRuntimeClass(f, "delete-me", "")
|
||||||
|
rcClient := f.NodeAPIClientSet.NodeV1alpha1().RuntimeClasses()
|
||||||
|
|
||||||
By("Deleting RuntimeClass "+rcName, func() {
|
By("Deleting RuntimeClass "+rcName, func() {
|
||||||
err := f.DynamicClient.Resource(runtimeClassGVR).Delete(rcName, nil)
|
err := rcClient.Delete(rcName, nil)
|
||||||
framework.ExpectNoError(err, "failed to delete RuntimeClass %s", rcName)
|
framework.ExpectNoError(err, "failed to delete RuntimeClass %s", rcName)
|
||||||
|
|
||||||
By("Waiting for the RuntimeClass to disappear")
|
By("Waiting for the RuntimeClass to disappear")
|
||||||
framework.ExpectNoError(wait.PollImmediate(framework.Poll, time.Minute, func() (bool, error) {
|
framework.ExpectNoError(wait.PollImmediate(framework.Poll, time.Minute, func() (bool, error) {
|
||||||
_, err := f.DynamicClient.Resource(runtimeClassGVR).Get(rcName, metav1.GetOptions{})
|
_, err := rcClient.Get(rcName, metav1.GetOptions{})
|
||||||
if errors.IsNotFound(err) {
|
if errors.IsNotFound(err) {
|
||||||
return true, nil // done
|
return true, nil // done
|
||||||
}
|
}
|
||||||
|
@ -132,18 +133,22 @@ var _ = SIGDescribe("RuntimeClass [Feature:RuntimeClass]", func() {
|
||||||
|
|
||||||
rcName := createRuntimeClass(f, "valid", "")
|
rcName := createRuntimeClass(f, "valid", "")
|
||||||
pod := createRuntimeClassPod(f, rcName)
|
pod := createRuntimeClassPod(f, rcName)
|
||||||
expectPodSuccess(f, pod)
|
|
||||||
|
// Before the pod can be run, the RuntimeClass informer must time out, by which time the Kubelet
|
||||||
|
// will probably be in a backoff state, so the pod can take a long time to start.
|
||||||
|
framework.ExpectNoError(framework.WaitForPodSuccessInNamespaceSlow(
|
||||||
|
f.ClientSet, pod.Name, f.Namespace.Name))
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO(tallclair): Test an actual configured non-default runtimeHandler.
|
// TODO(tallclair): Test an actual configured non-default runtimeHandler.
|
||||||
})
|
})
|
||||||
|
|
||||||
// createRuntimeClass generates a RuntimeClass with the desired handler and a "namespaced" name,
|
// createRuntimeClass generates a RuntimeClass with the desired handler and a "namespaced" name,
|
||||||
// synchronously creates it with the dynamic client, and returns the resulting name.
|
// synchronously creates it, and returns the generated name.
|
||||||
func createRuntimeClass(f *framework.Framework, name, handler string) string {
|
func createRuntimeClass(f *framework.Framework, name, handler string) string {
|
||||||
uniqueName := fmt.Sprintf("%s-%s", f.Namespace.Name, name)
|
uniqueName := fmt.Sprintf("%s-%s", f.Namespace.Name, name)
|
||||||
rc := runtimeclasstest.NewUnstructuredRuntimeClass(uniqueName, handler)
|
rc := runtimeclasstest.NewRuntimeClass(uniqueName, handler)
|
||||||
rc, err := f.DynamicClient.Resource(runtimeClassGVR).Create(rc, metav1.CreateOptions{})
|
rc, err := f.NodeAPIClientSet.NodeV1alpha1().RuntimeClasses().Create(rc)
|
||||||
framework.ExpectNoError(err, "failed to create RuntimeClass resource")
|
framework.ExpectNoError(err, "failed to create RuntimeClass resource")
|
||||||
return rc.GetName()
|
return rc.GetName()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue