Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
02001af7
Commit
02001af7
authored
Aug 22, 2017
by
Jiaying Zhang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Kubelet side extension to support device allocation
parent
7a8ad491
Hide whitespace changes
Inline
Side-by-side
Showing
25 changed files
with
838 additions
and
257 deletions
+838
-257
server.go
cmd/kubelet/app/server.go
+4
-0
kube_features.go
pkg/features/kube_features.go
+2
-2
BUILD
pkg/kubelet/BUILD
+0
-1
constants.go
pkg/kubelet/apis/deviceplugin/v1alpha1/constants.go
+0
-18
BUILD
pkg/kubelet/cm/BUILD
+11
-4
container_manager.go
pkg/kubelet/cm/container_manager.go
+4
-2
container_manager_linux.go
pkg/kubelet/cm/container_manager_linux.go
+105
-15
container_manager_stub.go
pkg/kubelet/cm/container_manager_stub.go
+6
-0
container_manager_unsupported.go
pkg/kubelet/cm/container_manager_unsupported.go
+5
-1
container_manager_windows.go
pkg/kubelet/cm/container_manager_windows.go
+1
-1
device_plugin_handler.go
pkg/kubelet/cm/device_plugin_handler.go
+188
-12
device_plugin_handler_stub.go
pkg/kubelet/cm/device_plugin_handler_stub.go
+42
-0
device_plugin_handler_test.go
pkg/kubelet/cm/device_plugin_handler_test.go
+250
-0
BUILD
pkg/kubelet/deviceplugin/BUILD
+3
-1
device_plugin_stub.go
pkg/kubelet/deviceplugin/device_plugin_stub.go
+20
-11
endpoint.go
pkg/kubelet/deviceplugin/endpoint.go
+36
-18
endpoint_test.go
pkg/kubelet/deviceplugin/endpoint_test.go
+6
-10
manager.go
pkg/kubelet/deviceplugin/manager.go
+72
-45
manager_test.go
pkg/kubelet/deviceplugin/manager_test.go
+3
-3
types.go
pkg/kubelet/deviceplugin/types.go
+34
-38
utils.go
pkg/kubelet/deviceplugin/utils.go
+13
-28
utils_test.go
pkg/kubelet/deviceplugin/utils_test.go
+11
-12
kubelet.go
pkg/kubelet/kubelet.go
+0
-9
kubelet_node_status.go
pkg/kubelet/kubelet_node_status.go
+9
-22
kubelet_pods.go
pkg/kubelet/kubelet_pods.go
+13
-4
No files found.
cmd/kubelet/app/server.go
View file @
02001af7
...
@@ -427,6 +427,9 @@ func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies) (err error) {
...
@@ -427,6 +427,9 @@ func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies) (err error) {
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
devicePluginEnabled
:=
utilfeature
.
DefaultFeatureGate
.
Enabled
(
features
.
DevicePlugins
)
kubeDeps
.
ContainerManager
,
err
=
cm
.
NewContainerManager
(
kubeDeps
.
ContainerManager
,
err
=
cm
.
NewContainerManager
(
kubeDeps
.
Mounter
,
kubeDeps
.
Mounter
,
kubeDeps
.
CAdvisorInterface
,
kubeDeps
.
CAdvisorInterface
,
...
@@ -450,6 +453,7 @@ func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies) (err error) {
...
@@ -450,6 +453,7 @@ func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies) (err error) {
ExperimentalQOSReserved
:
*
experimentalQOSReserved
,
ExperimentalQOSReserved
:
*
experimentalQOSReserved
,
},
},
s
.
FailSwapOn
,
s
.
FailSwapOn
,
devicePluginEnabled
,
kubeDeps
.
Recorder
)
kubeDeps
.
Recorder
)
if
err
!=
nil
{
if
err
!=
nil
{
...
...
pkg/features/kube_features.go
View file @
02001af7
...
@@ -73,11 +73,11 @@ const (
...
@@ -73,11 +73,11 @@ const (
// Works only with Docker Container Runtime.
// Works only with Docker Container Runtime.
Accelerators
utilfeature
.
Feature
=
"Accelerators"
Accelerators
utilfeature
.
Feature
=
"Accelerators"
// owner: @
vishh
// owner: @
jiayingz
// alpha: v1.8
// alpha: v1.8
//
//
// Enables support for Device Plugins
// Enables support for Device Plugins
// Only Nvidia GPUs are
suppor
ted as of v1.8.
// Only Nvidia GPUs are
tes
ted as of v1.8.
DevicePlugins
utilfeature
.
Feature
=
"DevicePlugins"
DevicePlugins
utilfeature
.
Feature
=
"DevicePlugins"
// owner: @gmarek
// owner: @gmarek
...
...
pkg/kubelet/BUILD
View file @
02001af7
...
@@ -43,7 +43,6 @@ go_library(
...
@@ -43,7 +43,6 @@ go_library(
"//pkg/fieldpath:go_default_library",
"//pkg/fieldpath:go_default_library",
"//pkg/kubelet/apis:go_default_library",
"//pkg/kubelet/apis:go_default_library",
"//pkg/kubelet/apis/cri:go_default_library",
"//pkg/kubelet/apis/cri:go_default_library",
"//pkg/kubelet/apis/deviceplugin/v1alpha1:go_default_library",
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
"//pkg/kubelet/apis/kubeletconfig/v1alpha1:go_default_library",
"//pkg/kubelet/apis/kubeletconfig/v1alpha1:go_default_library",
"//pkg/kubelet/cadvisor:go_default_library",
"//pkg/kubelet/cadvisor:go_default_library",
...
...
pkg/kubelet/apis/deviceplugin/v1alpha1/constants.go
View file @
02001af7
...
@@ -30,22 +30,4 @@ const (
...
@@ -30,22 +30,4 @@ const (
DevicePluginPath
=
"/var/lib/kubelet/device-plugins/"
DevicePluginPath
=
"/var/lib/kubelet/device-plugins/"
// KubeletSocket is the path of the Kubelet registry socket
// KubeletSocket is the path of the Kubelet registry socket
KubeletSocket
=
DevicePluginPath
+
"kubelet.sock"
KubeletSocket
=
DevicePluginPath
+
"kubelet.sock"
// InvalidChars are the characters that may not appear in a Vendor or Kind field
InvalidChars
=
"/ "
// ErrFailedToDialDevicePlugin is the error raised when the device plugin could not be
// reached on the registered socket
ErrFailedToDialDevicePlugin
=
"Failed to dial device plugin:"
// ErrUnsuportedVersion is the error raised when the device plugin uses an API version not
// supported by the Kubelet registry
ErrUnsuportedVersion
=
"Unsupported version"
// ErrDevicePluginAlreadyExists is the error raised when a device plugin with the
// same Resource Name tries to register itself
ErrDevicePluginAlreadyExists
=
"Another device plugin already registered this Resource Name"
// ErrInvalidResourceName is the error raised when a device plugin is registering
// itself with an invalid ResourceName
ErrInvalidResourceName
=
"The Resource Name is invalid"
// ErrEmptyResourceName is the error raised when the resource name field is empty
ErrEmptyResourceName
=
"Invalid Empty ResourceName"
)
)
pkg/kubelet/cm/BUILD
View file @
02001af7
...
@@ -8,6 +8,7 @@ go_library(
...
@@ -8,6 +8,7 @@ go_library(
"container_manager_stub.go",
"container_manager_stub.go",
"container_manager_unsupported.go",
"container_manager_unsupported.go",
"device_plugin_handler.go",
"device_plugin_handler.go",
"device_plugin_handler_stub.go",
"helpers_unsupported.go",
"helpers_unsupported.go",
"pod_container_manager_stub.go",
"pod_container_manager_stub.go",
"pod_container_manager_unsupported.go",
"pod_container_manager_unsupported.go",
...
@@ -31,11 +32,13 @@ go_library(
...
@@ -31,11 +32,13 @@ go_library(
"//pkg/kubelet/apis/deviceplugin/v1alpha1:go_default_library",
"//pkg/kubelet/apis/deviceplugin/v1alpha1:go_default_library",
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
"//pkg/kubelet/cadvisor:go_default_library",
"//pkg/kubelet/cadvisor:go_default_library",
"//pkg/kubelet/container:go_default_library",
"//pkg/kubelet/deviceplugin:go_default_library",
"//pkg/kubelet/deviceplugin:go_default_library",
"//pkg/kubelet/eviction/api:go_default_library",
"//pkg/kubelet/eviction/api:go_default_library",
"//pkg/util/mount:go_default_library",
"//pkg/util/mount:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/client-go/tools/record:go_default_library",
"//vendor/k8s.io/client-go/tools/record:go_default_library",
...
@@ -57,7 +60,6 @@ go_library(
...
@@ -57,7 +60,6 @@ go_library(
"//vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs:go_default_library",
"//vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs:go_default_library",
"//vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd:go_default_library",
"//vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd:go_default_library",
"//vendor/github.com/opencontainers/runc/libcontainer/configs:go_default_library",
"//vendor/github.com/opencontainers/runc/libcontainer/configs:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
],
],
...
@@ -69,6 +71,7 @@ go_test(
...
@@ -69,6 +71,7 @@ go_test(
name = "go_default_test",
name = "go_default_test",
srcs = [
srcs = [
"container_manager_unsupported_test.go",
"container_manager_unsupported_test.go",
"device_plugin_handler_test.go",
] + select({
] + select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"@io_bazel_rules_go//go/platform:linux_amd64": [
"cgroup_manager_linux_test.go",
"cgroup_manager_linux_test.go",
...
@@ -81,15 +84,19 @@ go_test(
...
@@ -81,15 +84,19 @@ go_test(
}),
}),
library = ":go_default_library",
library = ":go_default_library",
deps = [
deps = [
"//pkg/kubelet/apis/deviceplugin/v1alpha1:go_default_library",
"//pkg/util/mount:go_default_library",
"//pkg/util/mount:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
] + select({
] + select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"@io_bazel_rules_go//go/platform:linux_amd64": [
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
"//pkg/kubelet/eviction/api:go_default_library",
"//pkg/kubelet/eviction/api:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/github.com/stretchr/testify/require:go_default_library",
"//vendor/github.com/stretchr/testify/require:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
],
],
"//conditions:default": [],
"//conditions:default": [],
}),
}),
...
...
pkg/kubelet/cm/container_manager.go
View file @
02001af7
...
@@ -20,8 +20,8 @@ import (
...
@@ -20,8 +20,8 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/sets"
// TODO: Migrate kubelet to either use its own internal objects or client library.
// TODO: Migrate kubelet to either use its own internal objects or client library.
"k8s.io/api/core/v1"
"k8s.io/api/core/v1"
pluginapi
"k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
kubecontainer
"k8s.io/kubernetes/pkg/kubelet/container"
evictionapi
"k8s.io/kubernetes/pkg/kubelet/eviction/api"
evictionapi
"k8s.io/kubernetes/pkg/kubelet/eviction/api"
"fmt"
"fmt"
...
@@ -68,7 +68,9 @@ type ContainerManager interface {
...
@@ -68,7 +68,9 @@ type ContainerManager interface {
// level QoS containers have their desired state in a thread-safe way
// level QoS containers have their desired state in a thread-safe way
UpdateQOSCgroups
()
error
UpdateQOSCgroups
()
error
InternalContainerLifecycle
()
InternalContainerLifecycle
// Returns RunContainerOptions with devices, mounts, and env fields populated for
// extended resources required by container.
GetResources
(
pod
*
v1
.
Pod
,
container
*
v1
.
Container
,
activePods
[]
*
v1
.
Pod
)
(
*
kubecontainer
.
RunContainerOptions
,
error
)
}
}
type
NodeConfig
struct
{
type
NodeConfig
struct
{
...
...
pkg/kubelet/cm/container_manager_linux.go
View file @
02001af7
...
@@ -41,6 +41,7 @@ import (
...
@@ -41,6 +41,7 @@ import (
"k8s.io/client-go/tools/record"
"k8s.io/client-go/tools/record"
"k8s.io/kubernetes/pkg/kubelet/cadvisor"
"k8s.io/kubernetes/pkg/kubelet/cadvisor"
cmutil
"k8s.io/kubernetes/pkg/kubelet/cm/util"
cmutil
"k8s.io/kubernetes/pkg/kubelet/cm/util"
kubecontainer
"k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/kubelet/qos"
"k8s.io/kubernetes/pkg/kubelet/qos"
utilfile
"k8s.io/kubernetes/pkg/util/file"
utilfile
"k8s.io/kubernetes/pkg/util/file"
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/util/mount"
...
@@ -117,8 +118,8 @@ type containerManagerImpl struct {
...
@@ -117,8 +118,8 @@ type containerManagerImpl struct {
recorder
record
.
EventRecorder
recorder
record
.
EventRecorder
// Interface for QoS cgroup management
// Interface for QoS cgroup management
qosContainerManager
QOSContainerManager
qosContainerManager
QOSContainerManager
// Interface for
device plugin management
.
// Interface for
exporting and allocating devices reported by device plugins
.
devicePluginHdler
DevicePluginHandler
devicePluginH
an
dler
DevicePluginHandler
}
}
type
features
struct
{
type
features
struct
{
...
@@ -181,7 +182,7 @@ func validateSystemRequirements(mountUtil mount.Interface) (features, error) {
...
@@ -181,7 +182,7 @@ func validateSystemRequirements(mountUtil mount.Interface) (features, error) {
// TODO(vmarmol): Add limits to the system containers.
// TODO(vmarmol): Add limits to the system containers.
// Takes the absolute name of the specified containers.
// Takes the absolute name of the specified containers.
// Empty container name disables use of the specified container.
// Empty container name disables use of the specified container.
func
NewContainerManager
(
mountUtil
mount
.
Interface
,
cadvisorInterface
cadvisor
.
Interface
,
nodeConfig
NodeConfig
,
failSwapOn
bool
,
recorder
record
.
EventRecorder
)
(
ContainerManager
,
error
)
{
func
NewContainerManager
(
mountUtil
mount
.
Interface
,
cadvisorInterface
cadvisor
.
Interface
,
nodeConfig
NodeConfig
,
failSwapOn
bool
,
devicePluginEnabled
bool
,
recorder
record
.
EventRecorder
)
(
ContainerManager
,
error
)
{
subsystems
,
err
:=
GetCgroupSubsystems
()
subsystems
,
err
:=
GetCgroupSubsystems
()
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to get mounted cgroup subsystems: %v"
,
err
)
return
nil
,
fmt
.
Errorf
(
"failed to get mounted cgroup subsystems: %v"
,
err
)
...
@@ -252,7 +253,7 @@ func NewContainerManager(mountUtil mount.Interface, cadvisorInterface cadvisor.I
...
@@ -252,7 +253,7 @@ func NewContainerManager(mountUtil mount.Interface, cadvisorInterface cadvisor.I
return
nil
,
err
return
nil
,
err
}
}
return
&
containerManagerImpl
{
cm
:=
&
containerManagerImpl
{
cadvisorInterface
:
cadvisorInterface
,
cadvisorInterface
:
cadvisorInterface
,
mountUtil
:
mountUtil
,
mountUtil
:
mountUtil
,
NodeConfig
:
nodeConfig
,
NodeConfig
:
nodeConfig
,
...
@@ -262,7 +263,31 @@ func NewContainerManager(mountUtil mount.Interface, cadvisorInterface cadvisor.I
...
@@ -262,7 +263,31 @@ func NewContainerManager(mountUtil mount.Interface, cadvisorInterface cadvisor.I
cgroupRoot
:
cgroupRoot
,
cgroupRoot
:
cgroupRoot
,
recorder
:
recorder
,
recorder
:
recorder
,
qosContainerManager
:
qosContainerManager
,
qosContainerManager
:
qosContainerManager
,
},
nil
}
updateDeviceCapacityFunc
:=
func
(
updates
v1
.
ResourceList
)
{
cm
.
Lock
()
defer
cm
.
Unlock
()
for
k
,
v
:=
range
updates
{
if
v
.
Value
()
<=
0
{
delete
(
cm
.
capacity
,
k
)
}
else
{
cm
.
capacity
[
k
]
=
v
}
}
}
glog
.
Infof
(
"Creating device plugin handler: %t"
,
devicePluginEnabled
)
if
devicePluginEnabled
{
cm
.
devicePluginHandler
,
err
=
NewDevicePluginHandlerImpl
(
updateDeviceCapacityFunc
)
}
else
{
cm
.
devicePluginHandler
,
err
=
NewDevicePluginHandlerStub
()
}
if
err
!=
nil
{
return
nil
,
err
}
return
cm
,
nil
}
}
// NewPodContainerManager is a factory method returns a PodContainerManager object
// NewPodContainerManager is a factory method returns a PodContainerManager object
...
@@ -545,6 +570,11 @@ func (cm *containerManagerImpl) Start(node *v1.Node, activePods ActivePodsFunc)
...
@@ -545,6 +570,11 @@ func (cm *containerManagerImpl) Start(node *v1.Node, activePods ActivePodsFunc)
}
}
close
(
stopChan
)
close
(
stopChan
)
},
time
.
Second
,
stopChan
)
},
time
.
Second
,
stopChan
)
// Starts device plugin manager.
if
err
:=
cm
.
devicePluginHandler
.
Start
();
err
!=
nil
{
return
err
}
return
nil
return
nil
}
}
...
@@ -562,6 +592,76 @@ func (cm *containerManagerImpl) setFsCapacity() error {
...
@@ -562,6 +592,76 @@ func (cm *containerManagerImpl) setFsCapacity() error {
return
nil
return
nil
}
}
// TODO: move the GetResources logic to PodContainerManager.
func
(
cm
*
containerManagerImpl
)
GetResources
(
pod
*
v1
.
Pod
,
container
*
v1
.
Container
,
activePods
[]
*
v1
.
Pod
)
(
*
kubecontainer
.
RunContainerOptions
,
error
)
{
opts
:=
&
kubecontainer
.
RunContainerOptions
{}
// Gets devices, mounts, and envs from device plugin handler.
glog
.
V
(
3
)
.
Infof
(
"Calling devicePluginHandler AllocateDevices"
)
// Maps to detect duplicate settings.
devsMap
:=
make
(
map
[
string
]
string
)
mountsMap
:=
make
(
map
[
string
]
string
)
envsMap
:=
make
(
map
[
string
]
string
)
allocResps
,
err
:=
cm
.
devicePluginHandler
.
Allocate
(
pod
,
container
,
activePods
)
if
err
!=
nil
{
return
opts
,
err
}
// Loops through AllocationResponses of all required extended resources.
for
_
,
resp
:=
range
allocResps
{
// Loops through runtime spec of all devices of the given resource.
for
_
,
devRuntime
:=
range
resp
.
Spec
{
// Updates RunContainerOptions.Devices.
for
_
,
dev
:=
range
devRuntime
.
Devices
{
if
d
,
ok
:=
devsMap
[
dev
.
ContainerPath
];
ok
{
glog
.
V
(
3
)
.
Infof
(
"skip existing device %s %s"
,
dev
.
ContainerPath
,
dev
.
HostPath
)
if
d
!=
dev
.
HostPath
{
glog
.
Errorf
(
"Container device %s has conflicting mapping host devices: %s and %s"
,
dev
.
ContainerPath
,
d
,
dev
.
HostPath
)
}
continue
}
devsMap
[
dev
.
ContainerPath
]
=
dev
.
HostPath
opts
.
Devices
=
append
(
opts
.
Devices
,
kubecontainer
.
DeviceInfo
{
PathOnHost
:
dev
.
HostPath
,
PathInContainer
:
dev
.
ContainerPath
,
Permissions
:
dev
.
Permissions
,
})
}
// Updates RunContainerOptions.Mounts.
for
_
,
mount
:=
range
devRuntime
.
Mounts
{
if
m
,
ok
:=
mountsMap
[
mount
.
ContainerPath
];
ok
{
glog
.
V
(
3
)
.
Infof
(
"skip existing mount %s %s"
,
mount
.
ContainerPath
,
mount
.
HostPath
)
if
m
!=
mount
.
HostPath
{
glog
.
Errorf
(
"Container mount %s has conflicting mapping host mounts: %s and %s"
,
mount
.
ContainerPath
,
m
,
mount
.
HostPath
)
}
continue
}
mountsMap
[
mount
.
ContainerPath
]
=
mount
.
HostPath
opts
.
Mounts
=
append
(
opts
.
Mounts
,
kubecontainer
.
Mount
{
Name
:
mount
.
ContainerPath
,
ContainerPath
:
mount
.
ContainerPath
,
HostPath
:
mount
.
HostPath
,
ReadOnly
:
mount
.
ReadOnly
,
SELinuxRelabel
:
false
,
})
}
// Updates RunContainerOptions.Envs.
for
k
,
v
:=
range
devRuntime
.
Envs
{
if
e
,
ok
:=
envsMap
[
k
];
ok
{
glog
.
V
(
3
)
.
Infof
(
"skip existing envs %s %s"
,
k
,
v
)
if
e
!=
v
{
glog
.
Errorf
(
"Environment variable %s has conflicting setting: %s and %s"
,
k
,
e
,
v
)
}
continue
}
envsMap
[
k
]
=
v
opts
.
Envs
=
append
(
opts
.
Envs
,
kubecontainer
.
EnvVar
{
Name
:
k
,
Value
:
v
})
}
}
}
return
opts
,
nil
}
func
(
cm
*
containerManagerImpl
)
SystemCgroupsLimit
()
v1
.
ResourceList
{
func
(
cm
*
containerManagerImpl
)
SystemCgroupsLimit
()
v1
.
ResourceList
{
cpuLimit
:=
int64
(
0
)
cpuLimit
:=
int64
(
0
)
...
@@ -822,13 +922,3 @@ func (cm *containerManagerImpl) GetCapacity() v1.ResourceList {
...
@@ -822,13 +922,3 @@ func (cm *containerManagerImpl) GetCapacity() v1.ResourceList {
defer
cm
.
RUnlock
()
defer
cm
.
RUnlock
()
return
cm
.
capacity
return
cm
.
capacity
}
}
// GetDevicePluginHandler returns the DevicePluginHandler
func
(
m
*
containerManagerImpl
)
GetDevicePluginHandler
()
DevicePluginHandler
{
return
m
.
devicePluginHdler
}
// SetDevicePluginHandler sets the DevicePluginHandler
func
(
m
*
containerManagerImpl
)
SetDevicePluginHandler
(
d
DevicePluginHandler
)
{
m
.
devicePluginHdler
=
d
}
pkg/kubelet/cm/container_manager_stub.go
View file @
02001af7
...
@@ -19,6 +19,8 @@ package cm
...
@@ -19,6 +19,8 @@ package cm
import
(
import
(
"github.com/golang/glog"
"github.com/golang/glog"
"k8s.io/api/core/v1"
"k8s.io/api/core/v1"
kubecontainer
"k8s.io/kubernetes/pkg/kubelet/container"
)
)
type
containerManagerStub
struct
{}
type
containerManagerStub
struct
{}
...
@@ -66,6 +68,10 @@ func (cm *containerManagerStub) NewPodContainerManager() PodContainerManager {
...
@@ -66,6 +68,10 @@ func (cm *containerManagerStub) NewPodContainerManager() PodContainerManager {
return
&
podContainerManagerStub
{}
return
&
podContainerManagerStub
{}
}
}
func
(
cm
*
containerManagerStub
)
GetResources
(
pod
*
v1
.
Pod
,
container
*
v1
.
Container
,
activePods
[]
*
v1
.
Pod
)
(
*
kubecontainer
.
RunContainerOptions
,
error
)
{
return
&
kubecontainer
.
RunContainerOptions
{},
nil
}
func
NewStubContainerManager
()
ContainerManager
{
func
NewStubContainerManager
()
ContainerManager
{
return
&
containerManagerStub
{}
return
&
containerManagerStub
{}
}
}
pkg/kubelet/cm/container_manager_unsupported.go
View file @
02001af7
...
@@ -72,6 +72,10 @@ func (cm *unsupportedContainerManager) NewPodContainerManager() PodContainerMana
...
@@ -72,6 +72,10 @@ func (cm *unsupportedContainerManager) NewPodContainerManager() PodContainerMana
return
&
unsupportedPodContainerManager
{}
return
&
unsupportedPodContainerManager
{}
}
}
func
NewContainerManager
(
_
mount
.
Interface
,
_
cadvisor
.
Interface
,
_
NodeConfig
,
failSwapOn
bool
,
recorder
record
.
EventRecorder
)
(
ContainerManager
,
error
)
{
func
(
cm
*
unsupportedContainerManager
)
GetResources
(
pod
*
v1
.
Pod
,
container
*
v1
.
Container
,
activePods
[]
*
v1
.
Pod
)
(
*
kubecontainer
.
RunContainerOptions
,
error
)
{
return
&
kubecontainer
.
RunContainerOptions
{},
nil
}
func
NewContainerManager
(
_
mount
.
Interface
,
_
cadvisor
.
Interface
,
_
NodeConfig
,
failSwapOn
bool
,
devicePluginEnabled
bool
,
recorder
record
.
EventRecorder
)
(
ContainerManager
,
error
)
{
return
&
unsupportedContainerManager
{},
nil
return
&
unsupportedContainerManager
{},
nil
}
}
pkg/kubelet/cm/container_manager_windows.go
View file @
02001af7
...
@@ -38,6 +38,6 @@ func (cm *containerManagerImpl) Start(_ *v1.Node, _ ActivePodsFunc) error {
...
@@ -38,6 +38,6 @@ func (cm *containerManagerImpl) Start(_ *v1.Node, _ ActivePodsFunc) error {
return
nil
return
nil
}
}
func
NewContainerManager
(
mountUtil
mount
.
Interface
,
cadvisorInterface
cadvisor
.
Interface
,
nodeConfig
NodeConfig
,
failSwapOn
bool
,
recorder
record
.
EventRecorder
)
(
ContainerManager
,
error
)
{
func
NewContainerManager
(
mountUtil
mount
.
Interface
,
cadvisorInterface
cadvisor
.
Interface
,
nodeConfig
NodeConfig
,
failSwapOn
bool
,
devicePluginEnabled
bool
,
recorder
record
.
EventRecorder
)
(
ContainerManager
,
error
)
{
return
&
containerManagerImpl
{},
nil
return
&
containerManagerImpl
{},
nil
}
}
pkg/kubelet/cm/device_plugin_handler.go
View file @
02001af7
...
@@ -18,38 +18,214 @@ package cm
...
@@ -18,38 +18,214 @@ package cm
import
(
import
(
"fmt"
"fmt"
"sync"
"github.com/golang/glog"
"github.com/golang/glog"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/util/sets"
pluginapi
"k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
pluginapi
"k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
"k8s.io/kubernetes/pkg/kubelet/deviceplugin"
"k8s.io/kubernetes/pkg/kubelet/deviceplugin"
)
)
// podDevices represents a list of pod to device Id mappings.
type
containerDevices
map
[
string
]
sets
.
String
type
podDevices
map
[
string
]
containerDevices
func
(
pdev
podDevices
)
pods
()
sets
.
String
{
ret
:=
sets
.
NewString
()
for
k
:=
range
pdev
{
ret
.
Insert
(
k
)
}
return
ret
}
func
(
pdev
podDevices
)
insert
(
podUID
,
contName
string
,
device
string
)
{
if
_
,
exists
:=
pdev
[
podUID
];
!
exists
{
pdev
[
podUID
]
=
make
(
containerDevices
)
}
if
_
,
exists
:=
pdev
[
podUID
][
contName
];
!
exists
{
pdev
[
podUID
][
contName
]
=
sets
.
NewString
()
}
pdev
[
podUID
][
contName
]
.
Insert
(
device
)
}
func
(
pdev
podDevices
)
getDevices
(
podUID
,
contName
string
)
sets
.
String
{
containers
,
exists
:=
pdev
[
podUID
]
if
!
exists
{
return
nil
}
devices
,
exists
:=
containers
[
contName
]
if
!
exists
{
return
nil
}
return
devices
}
func
(
pdev
podDevices
)
delete
(
pods
[]
string
)
{
for
_
,
uid
:=
range
pods
{
delete
(
pdev
,
uid
)
}
}
func
(
pdev
podDevices
)
devices
()
sets
.
String
{
ret
:=
sets
.
NewString
()
for
_
,
containerDevices
:=
range
pdev
{
for
_
,
deviceSet
:=
range
containerDevices
{
ret
=
ret
.
Union
(
deviceSet
)
}
}
return
ret
}
type
DevicePluginHandler
interface
{
// Start starts device plugin registration service.
Start
()
error
// Devices returns all of registered devices keyed by resourceName.
Devices
()
map
[
string
][]
*
pluginapi
.
Device
// Allocate attempts to allocate all of required extended resources for
// the input container, issues an Allocate rpc request for each of such
// resources, and returns their AllocateResponses on success.
Allocate
(
pod
*
v1
.
Pod
,
container
*
v1
.
Container
,
activePods
[]
*
v1
.
Pod
)
([]
*
pluginapi
.
AllocateResponse
,
error
)
}
type
DevicePluginHandlerImpl
struct
{
type
DevicePluginHandlerImpl
struct
{
sync
.
Mutex
devicePluginManager
deviceplugin
.
Manager
devicePluginManager
deviceplugin
.
Manager
// devicePluginManagerMonitorCallback is used for testing only.
devicePluginManagerMonitorCallback
deviceplugin
.
MonitorCallback
// allDevices contains all of registered resourceNames and their exported device IDs.
allDevices
map
[
string
]
sets
.
String
// allocatedDevices contains pod to allocated device mapping, keyed by resourceName.
allocatedDevices
map
[
string
]
podDevices
}
}
// NewDevicePluginHandler create a DevicePluginHandler
// NewDevicePluginHandler create a DevicePluginHandler
func
NewDevicePluginHandler
()
(
*
DevicePluginHandlerImpl
,
error
)
{
// updateCapacityFunc is called to update ContainerManager capacity when
glog
.
V
(
2
)
.
Infof
(
"Starting Device Plugin Handler"
)
// device capacity changes.
func
NewDevicePluginHandlerImpl
(
updateCapacityFunc
func
(
v1
.
ResourceList
))
(
*
DevicePluginHandlerImpl
,
error
)
{
glog
.
V
(
2
)
.
Infof
(
"Creating Device Plugin Handler"
)
handler
:=
&
DevicePluginHandlerImpl
{
allDevices
:
make
(
map
[
string
]
sets
.
String
),
allocatedDevices
:
devicesInUse
(),
}
mgr
,
err
:=
deviceplugin
.
NewManagerImpl
(
pluginapi
.
DevicePluginPath
,
deviceManagerMonitorCallback
:=
func
(
resourceName
string
,
added
,
updated
,
deleted
[]
*
pluginapi
.
Device
)
{
func
(
r
string
,
a
,
u
,
d
[]
*
pluginapi
.
Device
)
{})
var
capacity
=
v1
.
ResourceList
{}
kept
:=
append
(
updated
,
added
...
)
if
_
,
ok
:=
handler
.
allDevices
[
resourceName
];
!
ok
{
handler
.
allDevices
[
resourceName
]
=
sets
.
NewString
()
}
// For now, DevicePluginHandler only keeps track of healthy devices.
// We can revisit this later when the need comes to track unhealthy devices here.
for
_
,
dev
:=
range
kept
{
if
dev
.
Health
==
pluginapi
.
Healthy
{
handler
.
allDevices
[
resourceName
]
.
Insert
(
dev
.
ID
)
}
else
{
handler
.
allDevices
[
resourceName
]
.
Delete
(
dev
.
ID
)
}
}
for
_
,
dev
:=
range
deleted
{
handler
.
allDevices
[
resourceName
]
.
Delete
(
dev
.
ID
)
}
capacity
[
v1
.
ResourceName
(
resourceName
)]
=
*
resource
.
NewQuantity
(
int64
(
handler
.
allDevices
[
resourceName
]
.
Len
()),
resource
.
DecimalSI
)
updateCapacityFunc
(
capacity
)
}
mgr
,
err
:=
deviceplugin
.
NewManagerImpl
(
pluginapi
.
KubeletSocket
,
deviceManagerMonitorCallback
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"Failed to initialize device plugin: %+v"
,
err
)
return
nil
,
fmt
.
Errorf
(
"Failed to initialize device plugin
manager
: %+v"
,
err
)
}
}
if
err
:=
mgr
.
Start
();
err
!=
nil
{
handler
.
devicePluginManager
=
mgr
return
nil
,
err
handler
.
devicePluginManagerMonitorCallback
=
deviceManagerMonitorCallback
}
return
handler
,
nil
}
return
&
DevicePluginHandlerImpl
{
func
(
h
*
DevicePluginHandlerImpl
)
Start
()
error
{
devicePluginManager
:
mgr
,
return
h
.
devicePluginManager
.
Start
()
},
nil
}
}
// TODO cache this
func
(
h
*
DevicePluginHandlerImpl
)
Devices
()
map
[
string
][]
*
pluginapi
.
Device
{
func
(
h
*
DevicePluginHandlerImpl
)
Devices
()
map
[
string
][]
*
pluginapi
.
Device
{
return
h
.
devicePluginManager
.
Devices
()
return
h
.
devicePluginManager
.
Devices
()
}
}
func
(
h
*
DevicePluginHandlerImpl
)
Allocate
(
pod
*
v1
.
Pod
,
container
*
v1
.
Container
,
activePods
[]
*
v1
.
Pod
)
([]
*
pluginapi
.
AllocateResponse
,
error
)
{
var
ret
[]
*
pluginapi
.
AllocateResponse
h
.
updateAllocatedDevices
(
activePods
)
for
k
,
v
:=
range
container
.
Resources
.
Limits
{
resource
:=
string
(
k
)
needed
:=
int
(
v
.
Value
())
glog
.
V
(
3
)
.
Infof
(
"needs %d %s"
,
needed
,
resource
)
if
!
deviceplugin
.
IsDeviceName
(
k
)
||
needed
==
0
{
continue
}
h
.
Lock
()
// Gets list of devices that have already been allocated.
// This can happen if a container restarts for example.
if
h
.
allocatedDevices
[
resource
]
==
nil
{
h
.
allocatedDevices
[
resource
]
=
make
(
podDevices
)
}
devices
:=
h
.
allocatedDevices
[
resource
]
.
getDevices
(
string
(
pod
.
UID
),
container
.
Name
)
if
devices
!=
nil
{
glog
.
V
(
3
)
.
Infof
(
"Found pre-allocated devices for resource %s container %q in Pod %q: %v"
,
resource
,
container
.
Name
,
pod
.
UID
,
devices
.
List
())
needed
=
needed
-
devices
.
Len
()
}
// Get Devices in use.
devicesInUse
:=
h
.
allocatedDevices
[
resource
]
.
devices
()
// Get a list of available devices.
available
:=
h
.
allDevices
[
resource
]
.
Difference
(
devicesInUse
)
if
int
(
available
.
Len
())
<
needed
{
h
.
Unlock
()
return
nil
,
fmt
.
Errorf
(
"requested number of devices unavailable for %s. Requested: %d, Available: %d"
,
resource
,
needed
,
available
.
Len
())
}
allocated
:=
available
.
UnsortedList
()[
:
needed
]
for
_
,
device
:=
range
allocated
{
// Update internal allocated device cache.
h
.
allocatedDevices
[
resource
]
.
insert
(
string
(
pod
.
UID
),
container
.
Name
,
device
)
}
h
.
Unlock
()
// devicePluginManager.Allocate involves RPC calls to device plugin, which
// could be heavy-weight. Therefore we want to perform this operation outside
// mutex lock. Note if Allcate call fails, we may leave container resources
// partially allocated for the failed container. We rely on updateAllocatedDevices()
// to garbage collect these resources later. Another side effect is that if
// we have X resource A and Y resource B in total, and two containers, container1
// and container2 both require X resource A and Y resource B. Both allocation
// requests may fail if we serve them in mixed order.
// TODO: may revisit this part later if we see inefficient resource allocation
// in real use as the result of this.
resp
,
err
:=
h
.
devicePluginManager
.
Allocate
(
resource
,
append
(
devices
.
UnsortedList
(),
allocated
...
))
if
err
!=
nil
{
return
nil
,
err
}
ret
=
append
(
ret
,
resp
)
}
return
ret
,
nil
}
// devicesInUse returns a list of custom devices in use along with the
// respective pods that are using them.
func
devicesInUse
()
map
[
string
]
podDevices
{
// TODO: gets the initial state from checkpointing.
return
make
(
map
[
string
]
podDevices
)
}
// updateAllocatedDevices updates the list of GPUs in use.
// It gets a list of active pods and then frees any GPUs that are bound to
// terminated pods. Returns error on failure.
func
(
h
*
DevicePluginHandlerImpl
)
updateAllocatedDevices
(
activePods
[]
*
v1
.
Pod
)
{
h
.
Lock
()
defer
h
.
Unlock
()
activePodUids
:=
sets
.
NewString
()
for
_
,
pod
:=
range
activePods
{
activePodUids
.
Insert
(
string
(
pod
.
UID
))
}
for
_
,
podDevs
:=
range
h
.
allocatedDevices
{
allocatedPodUids
:=
podDevs
.
pods
()
podsToBeRemoved
:=
allocatedPodUids
.
Difference
(
activePodUids
)
glog
.
V
(
5
)
.
Infof
(
"pods to be removed: %v"
,
podsToBeRemoved
.
List
())
podDevs
.
delete
(
podsToBeRemoved
.
List
())
}
}
pkg/kubelet/cm/device_plugin_handler_stub.go
0 → 100644
View file @
02001af7
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package
cm
import
(
"k8s.io/api/core/v1"
pluginapi
"k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
)
// A simple stub implementation for DevicePluginHandler.
type
DevicePluginHandlerStub
struct
{}
func
NewDevicePluginHandlerStub
()
(
*
DevicePluginHandlerStub
,
error
)
{
return
&
DevicePluginHandlerStub
{},
nil
}
func
(
h
*
DevicePluginHandlerStub
)
Start
()
error
{
return
nil
}
func
(
h
*
DevicePluginHandlerStub
)
Devices
()
map
[
string
][]
*
pluginapi
.
Device
{
return
make
(
map
[
string
][]
*
pluginapi
.
Device
)
}
func
(
h
*
DevicePluginHandlerStub
)
Allocate
(
pod
*
v1
.
Pod
,
container
*
v1
.
Container
,
activePods
[]
*
v1
.
Pod
)
([]
*
pluginapi
.
AllocateResponse
,
error
)
{
var
ret
[]
*
pluginapi
.
AllocateResponse
return
ret
,
nil
}
pkg/kubelet/cm/device_plugin_handler_test.go
0 → 100644
View file @
02001af7
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package
cm
import
(
"flag"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/uuid"
pluginapi
"k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
)
func
TestUpdateCapacity
(
t
*
testing
.
T
)
{
var
expected
=
v1
.
ResourceList
{}
as
:=
assert
.
New
(
t
)
verifyCapacityFunc
:=
func
(
updates
v1
.
ResourceList
)
{
as
.
Equal
(
expected
,
updates
)
}
testDevicePluginHandler
,
err
:=
NewDevicePluginHandlerImpl
(
verifyCapacityFunc
)
as
.
NotNil
(
testDevicePluginHandler
)
as
.
Nil
(
err
)
devs
:=
[]
*
pluginapi
.
Device
{
{
ID
:
"Device1"
,
Health
:
pluginapi
.
Healthy
},
{
ID
:
"Device2"
,
Health
:
pluginapi
.
Healthy
},
{
ID
:
"Device3"
,
Health
:
pluginapi
.
Unhealthy
},
}
resourceName
:=
"resource1"
// Adds three devices for resource1, two healthy and one unhealthy.
// Expects capacity for resource1 to be 2.
expected
[
v1
.
ResourceName
(
resourceName
)]
=
*
resource
.
NewQuantity
(
int64
(
2
),
resource
.
DecimalSI
)
testDevicePluginHandler
.
devicePluginManagerMonitorCallback
(
resourceName
,
devs
,
[]
*
pluginapi
.
Device
{},
[]
*
pluginapi
.
Device
{})
// Deletes an unhealthy device should NOT change capacity.
testDevicePluginHandler
.
devicePluginManagerMonitorCallback
(
resourceName
,
[]
*
pluginapi
.
Device
{},
[]
*
pluginapi
.
Device
{},
[]
*
pluginapi
.
Device
{
devs
[
2
]})
// Updates a healthy device to unhealthy should reduce capacity by 1.
expected
[
v1
.
ResourceName
(
resourceName
)]
=
*
resource
.
NewQuantity
(
int64
(
1
),
resource
.
DecimalSI
)
// Deletes a healthy device should reduce capacity by 1.
expected
[
v1
.
ResourceName
(
resourceName
)]
=
*
resource
.
NewQuantity
(
int64
(
0
),
resource
.
DecimalSI
)
// Tests adding another resource.
delete
(
expected
,
v1
.
ResourceName
(
resourceName
))
resourceName2
:=
"resource2"
expected
[
v1
.
ResourceName
(
resourceName2
)]
=
*
resource
.
NewQuantity
(
int64
(
2
),
resource
.
DecimalSI
)
testDevicePluginHandler
.
devicePluginManagerMonitorCallback
(
resourceName2
,
devs
,
[]
*
pluginapi
.
Device
{},
[]
*
pluginapi
.
Device
{})
}
type
stringPairType
struct
{
value1
string
value2
string
}
// DevicePluginManager stub to test device Allocation behavior.
type
DevicePluginManagerTestStub
struct
{
// All data structs are keyed by resourceName+DevId
devRuntimeDevices
map
[
string
][]
stringPairType
devRuntimeMounts
map
[
string
][]
stringPairType
devRuntimeEnvs
map
[
string
][]
stringPairType
}
func
NewDevicePluginManagerTestStub
()
(
*
DevicePluginManagerTestStub
,
error
)
{
return
&
DevicePluginManagerTestStub
{
devRuntimeDevices
:
make
(
map
[
string
][]
stringPairType
),
devRuntimeMounts
:
make
(
map
[
string
][]
stringPairType
),
devRuntimeEnvs
:
make
(
map
[
string
][]
stringPairType
),
},
nil
}
func
(
m
*
DevicePluginManagerTestStub
)
Start
()
error
{
return
nil
}
func
(
m
*
DevicePluginManagerTestStub
)
Devices
()
map
[
string
][]
*
pluginapi
.
Device
{
return
make
(
map
[
string
][]
*
pluginapi
.
Device
)
}
func
(
m
*
DevicePluginManagerTestStub
)
Allocate
(
resourceName
string
,
devIds
[]
string
)
(
*
pluginapi
.
AllocateResponse
,
error
)
{
resp
:=
new
(
pluginapi
.
AllocateResponse
)
for
_
,
id
:=
range
devIds
{
key
:=
resourceName
+
id
fmt
.
Printf
(
"Alloc device %q for resource %q
\n
"
,
id
,
resourceName
)
devRuntime
:=
new
(
pluginapi
.
DeviceRuntimeSpec
)
for
_
,
dev
:=
range
m
.
devRuntimeDevices
[
key
]
{
devRuntime
.
Devices
=
append
(
devRuntime
.
Devices
,
&
pluginapi
.
DeviceSpec
{
ContainerPath
:
dev
.
value1
,
HostPath
:
dev
.
value2
,
Permissions
:
"mrw"
,
})
}
for
_
,
mount
:=
range
m
.
devRuntimeMounts
[
key
]
{
fmt
.
Printf
(
"Add mount %q %q
\n
"
,
mount
.
value1
,
mount
.
value2
)
devRuntime
.
Mounts
=
append
(
devRuntime
.
Mounts
,
&
pluginapi
.
Mount
{
ContainerPath
:
mount
.
value1
,
HostPath
:
mount
.
value2
,
ReadOnly
:
true
,
})
}
devRuntime
.
Envs
=
make
(
map
[
string
]
string
)
for
_
,
env
:=
range
m
.
devRuntimeEnvs
[
key
]
{
devRuntime
.
Envs
[
env
.
value1
]
=
env
.
value2
}
resp
.
Spec
=
append
(
resp
.
Spec
,
devRuntime
)
}
return
resp
,
nil
}
func
(
m
*
DevicePluginManagerTestStub
)
Stop
()
error
{
return
nil
}
func
TestPodContainerDeviceAllocation
(
t
*
testing
.
T
)
{
flag
.
Set
(
"alsologtostderr"
,
fmt
.
Sprintf
(
"%t"
,
true
))
var
logLevel
string
flag
.
StringVar
(
&
logLevel
,
"logLevel"
,
"4"
,
"test"
)
flag
.
Lookup
(
"v"
)
.
Value
.
Set
(
logLevel
)
var
activePods
[]
*
v1
.
Pod
resourceName1
:=
"domain1.com/resource1"
resourceQuantity1
:=
*
resource
.
NewQuantity
(
int64
(
2
),
resource
.
DecimalSI
)
devId1
:=
"dev1"
devId2
:=
"dev2"
resourceName2
:=
"domain2.com/resource2"
resourceQuantity2
:=
*
resource
.
NewQuantity
(
int64
(
1
),
resource
.
DecimalSI
)
devId3
:=
"dev3"
devId4
:=
"dev4"
m
,
err
:=
NewDevicePluginManagerTestStub
()
as
:=
assert
.
New
(
t
)
as
.
Nil
(
err
)
monitorCallback
:=
func
(
resourceName
string
,
added
,
updated
,
deleted
[]
*
pluginapi
.
Device
)
{}
testDevicePluginHandler
:=
&
DevicePluginHandlerImpl
{
devicePluginManager
:
m
,
devicePluginManagerMonitorCallback
:
monitorCallback
,
allDevices
:
make
(
map
[
string
]
sets
.
String
),
allocatedDevices
:
make
(
map
[
string
]
podDevices
),
}
testDevicePluginHandler
.
allDevices
[
resourceName1
]
=
sets
.
NewString
()
testDevicePluginHandler
.
allDevices
[
resourceName1
]
.
Insert
(
devId1
)
testDevicePluginHandler
.
allDevices
[
resourceName1
]
.
Insert
(
devId2
)
testDevicePluginHandler
.
allDevices
[
resourceName2
]
=
sets
.
NewString
()
testDevicePluginHandler
.
allDevices
[
resourceName2
]
.
Insert
(
devId3
)
testDevicePluginHandler
.
allDevices
[
resourceName2
]
.
Insert
(
devId4
)
m
.
devRuntimeDevices
[
resourceName1
+
devId1
]
=
append
(
m
.
devRuntimeDevices
[
resourceName1
+
devId1
],
stringPairType
{
"/dev/aaa"
,
"/dev/aaa"
})
m
.
devRuntimeDevices
[
resourceName1
+
devId1
]
=
append
(
m
.
devRuntimeDevices
[
resourceName1
+
devId1
],
stringPairType
{
"/dev/bbb"
,
"/dev/bbb"
})
m
.
devRuntimeDevices
[
resourceName1
+
devId2
]
=
append
(
m
.
devRuntimeDevices
[
resourceName1
+
devId2
],
stringPairType
{
"/dev/ccc"
,
"/dev/ccc"
})
m
.
devRuntimeMounts
[
resourceName1
+
devId1
]
=
append
(
m
.
devRuntimeMounts
[
resourceName1
+
devId1
],
stringPairType
{
"/container_dir1/file1"
,
"host_dir1/file1"
})
m
.
devRuntimeMounts
[
resourceName1
+
devId2
]
=
append
(
m
.
devRuntimeMounts
[
resourceName1
+
devId2
],
stringPairType
{
"/container_dir1/file1"
,
"host_dir1/file1"
})
m
.
devRuntimeEnvs
[
resourceName1
+
devId2
]
=
append
(
m
.
devRuntimeEnvs
[
resourceName1
+
devId2
],
stringPairType
{
"key1"
,
"val1"
})
m
.
devRuntimeEnvs
[
resourceName2
+
devId3
]
=
append
(
m
.
devRuntimeEnvs
[
resourceName2
+
devId3
],
stringPairType
{
"key2"
,
"val2"
})
m
.
devRuntimeEnvs
[
resourceName2
+
devId4
]
=
append
(
m
.
devRuntimeEnvs
[
resourceName2
+
devId4
],
stringPairType
{
"key2"
,
"val2"
})
pod
:=
&
v1
.
Pod
{
ObjectMeta
:
metav1
.
ObjectMeta
{
UID
:
uuid
.
NewUUID
(),
},
Spec
:
v1
.
PodSpec
{
Containers
:
[]
v1
.
Container
{
{
Name
:
string
(
uuid
.
NewUUID
()),
Resources
:
v1
.
ResourceRequirements
{
Limits
:
v1
.
ResourceList
{
v1
.
ResourceName
(
resourceName1
)
:
resourceQuantity1
,
v1
.
ResourceName
(
"cpu"
)
:
resourceQuantity1
,
v1
.
ResourceName
(
resourceName2
)
:
resourceQuantity2
,
},
},
},
},
},
}
cm
:=
&
containerManagerImpl
{
devicePluginHandler
:
testDevicePluginHandler
,
}
activePods
=
append
(
activePods
,
pod
)
runContainerOpts
,
err
:=
cm
.
GetResources
(
pod
,
&
pod
.
Spec
.
Containers
[
0
],
activePods
)
as
.
Equal
(
len
(
runContainerOpts
.
Devices
),
3
)
// Two devices require to mount the same path. Expects a single mount entry to be created.
as
.
Equal
(
len
(
runContainerOpts
.
Mounts
),
1
)
as
.
Equal
(
runContainerOpts
.
Mounts
[
0
]
.
ContainerPath
,
"/container_dir1/file1"
)
as
.
Equal
(
len
(
runContainerOpts
.
Envs
),
2
)
// Requesting to create a pod without enough resources should fail.
failPod
:=
&
v1
.
Pod
{
ObjectMeta
:
metav1
.
ObjectMeta
{
UID
:
uuid
.
NewUUID
(),
},
Spec
:
v1
.
PodSpec
{
Containers
:
[]
v1
.
Container
{
{
Name
:
string
(
uuid
.
NewUUID
()),
Resources
:
v1
.
ResourceRequirements
{
Limits
:
v1
.
ResourceList
{
v1
.
ResourceName
(
resourceName1
)
:
resourceQuantity1
,
},
},
},
},
},
}
runContainerOpts2
,
err
:=
cm
.
GetResources
(
failPod
,
&
failPod
.
Spec
.
Containers
[
0
],
activePods
)
as
.
NotNil
(
err
)
as
.
Equal
(
len
(
runContainerOpts2
.
Devices
),
0
)
as
.
Equal
(
len
(
runContainerOpts2
.
Mounts
),
0
)
as
.
Equal
(
len
(
runContainerOpts2
.
Envs
),
0
)
// Requesting to create a new pod with a single resourceName2 should succeed.
newPod
:=
&
v1
.
Pod
{
ObjectMeta
:
metav1
.
ObjectMeta
{
UID
:
uuid
.
NewUUID
(),
},
Spec
:
v1
.
PodSpec
{
Containers
:
[]
v1
.
Container
{
{
Name
:
string
(
uuid
.
NewUUID
()),
Resources
:
v1
.
ResourceRequirements
{
Limits
:
v1
.
ResourceList
{
v1
.
ResourceName
(
resourceName2
)
:
resourceQuantity2
,
},
},
},
},
},
}
runContainerOpts3
,
err
:=
cm
.
GetResources
(
newPod
,
&
newPod
.
Spec
.
Containers
[
0
],
activePods
)
as
.
Nil
(
err
)
as
.
Equal
(
len
(
runContainerOpts3
.
Envs
),
1
)
}
pkg/kubelet/deviceplugin/BUILD
View file @
02001af7
...
@@ -11,18 +11,20 @@ load(
...
@@ -11,18 +11,20 @@ load(
go_library(
go_library(
name = "go_default_library",
name = "go_default_library",
srcs = [
srcs = [
"device_plugin_stub.go",
"endpoint.go",
"endpoint.go",
"manager.go",
"manager.go",
"mock_device_plugin.go",
"types.go",
"types.go",
"utils.go",
"utils.go",
],
],
tags = ["automanaged"],
tags = ["automanaged"],
deps = [
deps = [
"//pkg/api/v1/helper:go_default_library",
"//pkg/kubelet/apis/deviceplugin/v1alpha1:go_default_library",
"//pkg/kubelet/apis/deviceplugin/v1alpha1:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/golang.org/x/net/context:go_default_library",
"//vendor/golang.org/x/net/context:go_default_library",
"//vendor/google.golang.org/grpc:go_default_library",
"//vendor/google.golang.org/grpc:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
],
],
)
)
...
...
pkg/kubelet/deviceplugin/
mock_device_plugin
.go
→
pkg/kubelet/deviceplugin/
device_plugin_stub
.go
View file @
02001af7
...
@@ -20,6 +20,7 @@ import (
...
@@ -20,6 +20,7 @@ import (
"log"
"log"
"net"
"net"
"os"
"os"
"time"
"golang.org/x/net/context"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc"
...
@@ -27,8 +28,8 @@ import (
...
@@ -27,8 +28,8 @@ import (
pluginapi
"k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
pluginapi
"k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
)
)
//
MockDevicePlugin is a mock device plugin
//
Stub implementation for DevicePlugin.
type
MockDevicePlugin
struct
{
type
Stub
struct
{
devs
[]
*
pluginapi
.
Device
devs
[]
*
pluginapi
.
Device
socket
string
socket
string
...
@@ -38,9 +39,9 @@ type MockDevicePlugin struct {
...
@@ -38,9 +39,9 @@ type MockDevicePlugin struct {
server
*
grpc
.
Server
server
*
grpc
.
Server
}
}
// New
MockDevicePlugin returns an initialized MockDevicePlugin
// New
DevicePluginStub returns an initialized DevicePlugin Stub.
func
New
MockDevicePlugin
(
devs
[]
*
pluginapi
.
Device
,
socket
string
)
*
MockDevicePlugin
{
func
New
DevicePluginStub
(
devs
[]
*
pluginapi
.
Device
,
socket
string
)
*
Stub
{
return
&
MockDevicePlugin
{
return
&
Stub
{
devs
:
devs
,
devs
:
devs
,
socket
:
socket
,
socket
:
socket
,
...
@@ -50,7 +51,7 @@ func NewMockDevicePlugin(devs []*pluginapi.Device, socket string) *MockDevicePlu
...
@@ -50,7 +51,7 @@ func NewMockDevicePlugin(devs []*pluginapi.Device, socket string) *MockDevicePlu
}
}
// Start starts the gRPC server of the device plugin
// Start starts the gRPC server of the device plugin
func
(
m
*
MockDevicePlugin
)
Start
()
error
{
func
(
m
*
Stub
)
Start
()
error
{
err
:=
m
.
cleanup
()
err
:=
m
.
cleanup
()
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
...
@@ -65,20 +66,28 @@ func (m *MockDevicePlugin) Start() error {
...
@@ -65,20 +66,28 @@ func (m *MockDevicePlugin) Start() error {
pluginapi
.
RegisterDevicePluginServer
(
m
.
server
,
m
)
pluginapi
.
RegisterDevicePluginServer
(
m
.
server
,
m
)
go
m
.
server
.
Serve
(
sock
)
go
m
.
server
.
Serve
(
sock
)
// Wait till grpc server is ready.
for
i
:=
0
;
i
<
10
;
i
++
{
services
:=
m
.
server
.
GetServiceInfo
()
if
len
(
services
)
>
0
{
break
}
time
.
Sleep
(
1
*
time
.
Second
)
}
log
.
Println
(
"Starting to serve on"
,
m
.
socket
)
log
.
Println
(
"Starting to serve on"
,
m
.
socket
)
return
nil
return
nil
}
}
// Stop stops the gRPC server
// Stop stops the gRPC server
func
(
m
*
MockDevicePlugin
)
Stop
()
error
{
func
(
m
*
Stub
)
Stop
()
error
{
m
.
server
.
Stop
()
m
.
server
.
Stop
()
return
m
.
cleanup
()
return
m
.
cleanup
()
}
}
// ListAndWatch lists devices and update that list according to the Update call
// ListAndWatch lists devices and update that list according to the Update call
func
(
m
*
MockDevicePlugin
)
ListAndWatch
(
e
*
pluginapi
.
Empty
,
s
pluginapi
.
DevicePlugin_ListAndWatchServer
)
error
{
func
(
m
*
Stub
)
ListAndWatch
(
e
*
pluginapi
.
Empty
,
s
pluginapi
.
DevicePlugin_ListAndWatchServer
)
error
{
log
.
Println
(
"ListAndWatch"
)
log
.
Println
(
"ListAndWatch"
)
var
devs
[]
*
pluginapi
.
Device
var
devs
[]
*
pluginapi
.
Device
...
@@ -102,19 +111,19 @@ func (m *MockDevicePlugin) ListAndWatch(e *pluginapi.Empty, s pluginapi.DevicePl
...
@@ -102,19 +111,19 @@ func (m *MockDevicePlugin) ListAndWatch(e *pluginapi.Empty, s pluginapi.DevicePl
}
}
// Update allows the device plugin to send new devices through ListAndWatch
// Update allows the device plugin to send new devices through ListAndWatch
func
(
m
*
MockDevicePlugin
)
Update
(
devs
[]
*
pluginapi
.
Device
)
{
func
(
m
*
Stub
)
Update
(
devs
[]
*
pluginapi
.
Device
)
{
m
.
update
<-
devs
m
.
update
<-
devs
}
}
// Allocate does a mock allocation
// Allocate does a mock allocation
func
(
m
*
MockDevicePlugin
)
Allocate
(
ctx
context
.
Context
,
r
*
pluginapi
.
AllocateRequest
)
(
*
pluginapi
.
AllocateResponse
,
error
)
{
func
(
m
*
Stub
)
Allocate
(
ctx
context
.
Context
,
r
*
pluginapi
.
AllocateRequest
)
(
*
pluginapi
.
AllocateResponse
,
error
)
{
log
.
Printf
(
"Allocate, %+v"
,
r
)
log
.
Printf
(
"Allocate, %+v"
,
r
)
var
response
pluginapi
.
AllocateResponse
var
response
pluginapi
.
AllocateResponse
return
&
response
,
nil
return
&
response
,
nil
}
}
func
(
m
*
MockDevicePlugin
)
cleanup
()
error
{
func
(
m
*
Stub
)
cleanup
()
error
{
if
err
:=
os
.
Remove
(
m
.
socket
);
err
!=
nil
&&
!
os
.
IsNotExist
(
err
)
{
if
err
:=
os
.
Remove
(
m
.
socket
);
err
!=
nil
&&
!
os
.
IsNotExist
(
err
)
{
return
err
return
err
}
}
...
...
pkg/kubelet/deviceplugin/endpoint.go
View file @
02001af7
...
@@ -29,6 +29,9 @@ import (
...
@@ -29,6 +29,9 @@ import (
pluginapi
"k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
pluginapi
"k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
)
)
// endpoint maps to a single registered device plugin. It is responsible
// for managing gRPC communications with the device plugin and caching
// device states reported by the device plugin.
type
endpoint
struct
{
type
endpoint
struct
{
client
pluginapi
.
DevicePluginClient
client
pluginapi
.
DevicePluginClient
...
@@ -44,9 +47,11 @@ type endpoint struct {
...
@@ -44,9 +47,11 @@ type endpoint struct {
ctx
context
.
Context
ctx
context
.
Context
}
}
// newEndpoint creates a new endpoint for the given resourceName.
func
newEndpoint
(
socketPath
,
resourceName
string
,
callback
MonitorCallback
)
(
*
endpoint
,
error
)
{
func
newEndpoint
(
socketPath
,
resourceName
string
,
callback
MonitorCallback
)
(
*
endpoint
,
error
)
{
client
,
err
:=
dial
(
socketPath
)
client
,
err
:=
dial
(
socketPath
)
if
err
!=
nil
{
if
err
!=
nil
{
glog
.
Errorf
(
"Can't create new endpoint with path %s err %v"
,
socketPath
,
err
)
return
nil
,
err
return
nil
,
err
}
}
...
@@ -66,49 +71,65 @@ func newEndpoint(socketPath, resourceName string, callback MonitorCallback) (*en
...
@@ -66,49 +71,65 @@ func newEndpoint(socketPath, resourceName string, callback MonitorCallback) (*en
},
nil
},
nil
}
}
func
(
e
*
endpoint
)
list
()
(
pluginapi
.
DevicePlugin_ListAndWatchClient
,
error
)
{
func
(
e
*
endpoint
)
getDevices
()
[]
*
pluginapi
.
Device
{
glog
.
V
(
2
)
.
Infof
(
"Starting ListAndWatch"
)
e
.
mutex
.
Lock
()
defer
e
.
mutex
.
Unlock
()
return
copyDevices
(
e
.
devices
)
}
// list initializes ListAndWatch gRPC call for the device plugin and gets the
// initial list of the devices. Returns ListAndWatch gRPC stream on success.
func
(
e
*
endpoint
)
list
()
(
pluginapi
.
DevicePlugin_ListAndWatchClient
,
error
)
{
glog
.
V
(
3
)
.
Infof
(
"Starting List"
)
stream
,
err
:=
e
.
client
.
ListAndWatch
(
e
.
ctx
,
&
pluginapi
.
Empty
{})
stream
,
err
:=
e
.
client
.
ListAndWatch
(
e
.
ctx
,
&
pluginapi
.
Empty
{})
if
err
!=
nil
{
if
err
!=
nil
{
glog
.
Errorf
(
E
rrListAndWatch
,
e
.
resourceName
,
err
)
glog
.
Errorf
(
e
rrListAndWatch
,
e
.
resourceName
,
err
)
return
nil
,
err
return
nil
,
err
}
}
devs
,
err
:=
stream
.
Recv
()
devs
,
err
:=
stream
.
Recv
()
if
err
!=
nil
{
if
err
!=
nil
{
glog
.
Errorf
(
E
rrListAndWatch
,
e
.
resourceName
,
err
)
glog
.
Errorf
(
e
rrListAndWatch
,
e
.
resourceName
,
err
)
return
nil
,
err
return
nil
,
err
}
}
devices
:=
make
(
map
[
string
]
*
pluginapi
.
Device
)
devices
:=
make
(
map
[
string
]
*
pluginapi
.
Device
)
var
added
,
updated
,
deleted
[]
*
pluginapi
.
Device
for
_
,
d
:=
range
devs
.
Devices
{
for
_
,
d
:=
range
devs
.
Devices
{
devices
[
d
.
ID
]
=
d
devices
[
d
.
ID
]
=
d
added
=
append
(
added
,
cloneDevice
(
d
))
}
}
e
.
mutex
.
Lock
()
e
.
mutex
.
Lock
()
e
.
devices
=
devices
e
.
devices
=
devices
e
.
mutex
.
Unlock
()
e
.
mutex
.
Unlock
()
e
.
callback
(
e
.
resourceName
,
added
,
updated
,
deleted
)
return
stream
,
nil
return
stream
,
nil
}
}
// listAndWatch blocks on receiving ListAndWatch gRPC stream updates. Each ListAndWatch
// stream update contains a new list of device states. listAndWatch compares the new
// device states with its cached states to get list of new, updated, and deleted devices.
// It then issues a callback to pass this information to the device_plugin_handler which
// will adjust the resource available information accordingly.
func
(
e
*
endpoint
)
listAndWatch
(
stream
pluginapi
.
DevicePlugin_ListAndWatchClient
)
{
func
(
e
*
endpoint
)
listAndWatch
(
stream
pluginapi
.
DevicePlugin_ListAndWatchClient
)
{
glog
.
V
(
2
)
.
Infof
(
"Starting ListAndWatch"
)
glog
.
V
(
3
)
.
Infof
(
"Starting ListAndWatch"
)
devices
:=
make
(
map
[
string
]
*
pluginapi
.
Device
)
devices
:=
make
(
map
[
string
]
*
pluginapi
.
Device
)
e
.
mutex
.
Lock
()
e
.
mutex
.
Lock
()
for
_
,
d
:=
range
e
.
devices
{
for
_
,
d
:=
range
e
.
devices
{
devices
[
d
.
ID
]
=
C
loneDevice
(
d
)
devices
[
d
.
ID
]
=
c
loneDevice
(
d
)
}
}
e
.
mutex
.
Unlock
()
e
.
mutex
.
Unlock
()
for
{
for
{
response
,
err
:=
stream
.
Recv
()
response
,
err
:=
stream
.
Recv
()
if
err
!=
nil
{
if
err
!=
nil
{
glog
.
Errorf
(
E
rrListAndWatch
,
e
.
resourceName
,
err
)
glog
.
Errorf
(
e
rrListAndWatch
,
e
.
resourceName
,
err
)
return
return
}
}
...
@@ -126,7 +147,7 @@ func (e *endpoint) listAndWatch(stream pluginapi.DevicePlugin_ListAndWatchClient
...
@@ -126,7 +147,7 @@ func (e *endpoint) listAndWatch(stream pluginapi.DevicePlugin_ListAndWatchClient
glog
.
V
(
2
)
.
Infof
(
"New device for Endpoint %s: %v"
,
e
.
resourceName
,
d
)
glog
.
V
(
2
)
.
Infof
(
"New device for Endpoint %s: %v"
,
e
.
resourceName
,
d
)
devices
[
d
.
ID
]
=
d
devices
[
d
.
ID
]
=
d
added
=
append
(
added
,
C
loneDevice
(
d
))
added
=
append
(
added
,
c
loneDevice
(
d
))
continue
continue
}
}
...
@@ -142,7 +163,7 @@ func (e *endpoint) listAndWatch(stream pluginapi.DevicePlugin_ListAndWatchClient
...
@@ -142,7 +163,7 @@ func (e *endpoint) listAndWatch(stream pluginapi.DevicePlugin_ListAndWatchClient
}
}
devices
[
d
.
ID
]
=
d
devices
[
d
.
ID
]
=
d
updated
=
append
(
updated
,
C
loneDevice
(
d
))
updated
=
append
(
updated
,
c
loneDevice
(
d
))
}
}
var
deleted
[]
*
pluginapi
.
Device
var
deleted
[]
*
pluginapi
.
Device
...
@@ -153,7 +174,7 @@ func (e *endpoint) listAndWatch(stream pluginapi.DevicePlugin_ListAndWatchClient
...
@@ -153,7 +174,7 @@ func (e *endpoint) listAndWatch(stream pluginapi.DevicePlugin_ListAndWatchClient
glog
.
Errorf
(
"Device %s was deleted"
,
d
.
ID
)
glog
.
Errorf
(
"Device %s was deleted"
,
d
.
ID
)
deleted
=
append
(
deleted
,
C
loneDevice
(
d
))
deleted
=
append
(
deleted
,
c
loneDevice
(
d
))
delete
(
devices
,
id
)
delete
(
devices
,
id
)
}
}
...
@@ -166,14 +187,10 @@ func (e *endpoint) listAndWatch(stream pluginapi.DevicePlugin_ListAndWatchClient
...
@@ -166,14 +187,10 @@ func (e *endpoint) listAndWatch(stream pluginapi.DevicePlugin_ListAndWatchClient
}
}
func
(
e
*
endpoint
)
allocate
(
devs
[]
*
pluginapi
.
Device
)
(
*
pluginapi
.
AllocateResponse
,
error
)
{
// allocate issues Allocate gRPC call to the device plugin.
var
ids
[]
string
func
(
e
*
endpoint
)
allocate
(
devs
[]
string
)
(
*
pluginapi
.
AllocateResponse
,
error
)
{
for
_
,
d
:=
range
devs
{
ids
=
append
(
ids
,
d
.
ID
)
}
return
e
.
client
.
Allocate
(
context
.
Background
(),
&
pluginapi
.
AllocateRequest
{
return
e
.
client
.
Allocate
(
context
.
Background
(),
&
pluginapi
.
AllocateRequest
{
DevicesIDs
:
id
s
,
DevicesIDs
:
dev
s
,
})
})
}
}
...
@@ -181,6 +198,7 @@ func (e *endpoint) stop() {
...
@@ -181,6 +198,7 @@ func (e *endpoint) stop() {
e
.
cancel
()
e
.
cancel
()
}
}
// dial establishes the gRPC communication with the registered device plugin.
func
dial
(
unixSocketPath
string
)
(
pluginapi
.
DevicePluginClient
,
error
)
{
func
dial
(
unixSocketPath
string
)
(
pluginapi
.
DevicePluginClient
,
error
)
{
c
,
err
:=
grpc
.
Dial
(
unixSocketPath
,
grpc
.
WithInsecure
(),
c
,
err
:=
grpc
.
Dial
(
unixSocketPath
,
grpc
.
WithInsecure
(),
grpc
.
WithDialer
(
func
(
addr
string
,
timeout
time
.
Duration
)
(
net
.
Conn
,
error
)
{
grpc
.
WithDialer
(
func
(
addr
string
,
timeout
time
.
Duration
)
(
net
.
Conn
,
error
)
{
...
@@ -189,7 +207,7 @@ func dial(unixSocketPath string) (pluginapi.DevicePluginClient, error) {
...
@@ -189,7 +207,7 @@ func dial(unixSocketPath string) (pluginapi.DevicePluginClient, error) {
)
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
pluginapi
.
E
rrFailedToDialDevicePlugin
+
" %v"
,
err
)
return
nil
,
fmt
.
Errorf
(
e
rrFailedToDialDevicePlugin
+
" %v"
,
err
)
}
}
return
pluginapi
.
NewDevicePluginClient
(
c
),
nil
return
pluginapi
.
NewDevicePluginClient
(
c
),
nil
...
...
pkg/kubelet/deviceplugin/endpoint_test.go
View file @
02001af7
...
@@ -17,7 +17,6 @@ limitations under the License.
...
@@ -17,7 +17,6 @@ limitations under the License.
package
deviceplugin
package
deviceplugin
import
(
import
(
"os"
"path"
"path"
"testing"
"testing"
"time"
"time"
...
@@ -32,8 +31,7 @@ var (
...
@@ -32,8 +31,7 @@ var (
)
)
func
TestNewEndpoint
(
t
*
testing
.
T
)
{
func
TestNewEndpoint
(
t
*
testing
.
T
)
{
wd
,
_
:=
os
.
Getwd
()
socket
:=
path
.
Join
(
"/tmp"
,
esocketName
)
socket
:=
path
.
Join
(
wd
,
esocketName
)
devs
:=
[]
*
pluginapi
.
Device
{
devs
:=
[]
*
pluginapi
.
Device
{
{
ID
:
"ADeviceId"
,
Health
:
pluginapi
.
Healthy
},
{
ID
:
"ADeviceId"
,
Health
:
pluginapi
.
Healthy
},
...
@@ -44,8 +42,7 @@ func TestNewEndpoint(t *testing.T) {
...
@@ -44,8 +42,7 @@ func TestNewEndpoint(t *testing.T) {
}
}
func
TestList
(
t
*
testing
.
T
)
{
func
TestList
(
t
*
testing
.
T
)
{
wd
,
_
:=
os
.
Getwd
()
socket
:=
path
.
Join
(
"/tmp"
,
esocketName
)
socket
:=
path
.
Join
(
wd
,
esocketName
)
devs
:=
[]
*
pluginapi
.
Device
{
devs
:=
[]
*
pluginapi
.
Device
{
{
ID
:
"ADeviceId"
,
Health
:
pluginapi
.
Healthy
},
{
ID
:
"ADeviceId"
,
Health
:
pluginapi
.
Healthy
},
...
@@ -70,8 +67,7 @@ func TestList(t *testing.T) {
...
@@ -70,8 +67,7 @@ func TestList(t *testing.T) {
}
}
func
TestListAndWatch
(
t
*
testing
.
T
)
{
func
TestListAndWatch
(
t
*
testing
.
T
)
{
wd
,
_
:=
os
.
Getwd
()
socket
:=
path
.
Join
(
"/tmp"
,
esocketName
)
socket
:=
path
.
Join
(
wd
,
esocketName
)
devs
:=
[]
*
pluginapi
.
Device
{
devs
:=
[]
*
pluginapi
.
Device
{
{
ID
:
"ADeviceId"
,
Health
:
pluginapi
.
Healthy
},
{
ID
:
"ADeviceId"
,
Health
:
pluginapi
.
Healthy
},
...
@@ -118,8 +114,8 @@ func TestListAndWatch(t *testing.T) {
...
@@ -118,8 +114,8 @@ func TestListAndWatch(t *testing.T) {
}
}
func
esetup
(
t
*
testing
.
T
,
devs
[]
*
pluginapi
.
Device
,
socket
,
resourceName
string
,
callback
MonitorCallback
)
(
*
MockDevicePlugin
,
*
endpoint
)
{
func
esetup
(
t
*
testing
.
T
,
devs
[]
*
pluginapi
.
Device
,
socket
,
resourceName
string
,
callback
MonitorCallback
)
(
*
Stub
,
*
endpoint
)
{
p
:=
New
MockDevicePlugin
(
devs
,
socket
)
p
:=
New
DevicePluginStub
(
devs
,
socket
)
err
:=
p
.
Start
()
err
:=
p
.
Start
()
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
...
@@ -130,7 +126,7 @@ func esetup(t *testing.T, devs []*pluginapi.Device, socket, resourceName string,
...
@@ -130,7 +126,7 @@ func esetup(t *testing.T, devs []*pluginapi.Device, socket, resourceName string,
return
p
,
e
return
p
,
e
}
}
func
ecleanup
(
t
*
testing
.
T
,
p
*
MockDevicePlugin
,
e
*
endpoint
)
{
func
ecleanup
(
t
*
testing
.
T
,
p
*
Stub
,
e
*
endpoint
)
{
p
.
Stop
()
p
.
Stop
()
e
.
stop
()
e
.
stop
()
}
}
pkg/kubelet/deviceplugin/manager.go
View file @
02001af7
...
@@ -21,6 +21,7 @@ import (
...
@@ -21,6 +21,7 @@ import (
"net"
"net"
"os"
"os"
"path/filepath"
"path/filepath"
"sync"
"github.com/golang/glog"
"github.com/golang/glog"
"golang.org/x/net/context"
"golang.org/x/net/context"
...
@@ -29,15 +30,27 @@ import (
...
@@ -29,15 +30,27 @@ import (
pluginapi
"k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
pluginapi
"k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
)
)
// NewManagerImpl creates a new manager on the socket `socketPath` and can
// ManagerImpl is the structure in charge of managing Device Plugins.
// rebuild state from devices and available []Device.
type
ManagerImpl
struct
{
// f is the callback that is called when a device becomes unhealthy
socketname
string
socketdir
string
Endpoints
map
[
string
]
*
endpoint
// Key is ResourceName
mutex
sync
.
Mutex
callback
MonitorCallback
server
*
grpc
.
Server
}
// NewManagerImpl creates a new manager on the socket `socketPath`.
// f is the callback that is called when a device becomes unhealthy.
// socketPath is present for testing purposes in production this is pluginapi.KubeletSocket
// socketPath is present for testing purposes in production this is pluginapi.KubeletSocket
func
NewManagerImpl
(
socketPath
string
,
f
MonitorCallback
)
(
*
ManagerImpl
,
error
)
{
func
NewManagerImpl
(
socketPath
string
,
f
MonitorCallback
)
(
*
ManagerImpl
,
error
)
{
glog
.
V
(
2
)
.
Infof
(
"Creating Device Plugin manager at %s"
,
socketPath
)
glog
.
V
(
2
)
.
Infof
(
"Creating Device Plugin manager at %s"
,
socketPath
)
if
socketPath
==
""
||
!
filepath
.
IsAbs
(
socketPath
)
{
if
socketPath
==
""
||
!
filepath
.
IsAbs
(
socketPath
)
{
return
nil
,
fmt
.
Errorf
(
E
rrBadSocket
+
" %v"
,
socketPath
)
return
nil
,
fmt
.
Errorf
(
e
rrBadSocket
+
" %v"
,
socketPath
)
}
}
dir
,
file
:=
filepath
.
Split
(
socketPath
)
dir
,
file
:=
filepath
.
Split
(
socketPath
)
...
@@ -50,6 +63,26 @@ func NewManagerImpl(socketPath string, f MonitorCallback) (*ManagerImpl, error)
...
@@ -50,6 +63,26 @@ func NewManagerImpl(socketPath string, f MonitorCallback) (*ManagerImpl, error)
},
nil
},
nil
}
}
func
removeContents
(
dir
string
)
error
{
d
,
err
:=
os
.
Open
(
dir
)
if
err
!=
nil
{
return
err
}
defer
d
.
Close
()
names
,
err
:=
d
.
Readdirnames
(
-
1
)
if
err
!=
nil
{
return
err
}
for
_
,
name
:=
range
names
{
// TODO: skip checkpoint file and check for file type.
err
=
os
.
RemoveAll
(
filepath
.
Join
(
dir
,
name
))
if
err
!=
nil
{
return
err
}
}
return
nil
}
// Start starts the Device Plugin Manager
// Start starts the Device Plugin Manager
func
(
m
*
ManagerImpl
)
Start
()
error
{
func
(
m
*
ManagerImpl
)
Start
()
error
{
glog
.
V
(
2
)
.
Infof
(
"Starting Device Plugin manager"
)
glog
.
V
(
2
)
.
Infof
(
"Starting Device Plugin manager"
)
...
@@ -57,14 +90,20 @@ func (m *ManagerImpl) Start() error {
...
@@ -57,14 +90,20 @@ func (m *ManagerImpl) Start() error {
socketPath
:=
filepath
.
Join
(
m
.
socketdir
,
m
.
socketname
)
socketPath
:=
filepath
.
Join
(
m
.
socketdir
,
m
.
socketname
)
os
.
MkdirAll
(
m
.
socketdir
,
0755
)
os
.
MkdirAll
(
m
.
socketdir
,
0755
)
// Removes all stale sockets in m.socketdir. Device plugins can monitor
// this and use it as a signal to re-register with the new Kubelet.
if
err
:=
removeContents
(
m
.
socketdir
);
err
!=
nil
{
glog
.
Errorf
(
"Fail to clean up stale contents under %s: %+v"
,
m
.
socketdir
,
err
)
}
if
err
:=
os
.
Remove
(
socketPath
);
err
!=
nil
&&
!
os
.
IsNotExist
(
err
)
{
if
err
:=
os
.
Remove
(
socketPath
);
err
!=
nil
&&
!
os
.
IsNotExist
(
err
)
{
glog
.
Errorf
(
E
rrRemoveSocket
+
" %+v"
,
err
)
glog
.
Errorf
(
e
rrRemoveSocket
+
" %+v"
,
err
)
return
err
return
err
}
}
s
,
err
:=
net
.
Listen
(
"unix"
,
socketPath
)
s
,
err
:=
net
.
Listen
(
"unix"
,
socketPath
)
if
err
!=
nil
{
if
err
!=
nil
{
glog
.
Errorf
(
E
rrListenSocket
+
" %+v"
,
err
)
glog
.
Errorf
(
e
rrListenSocket
+
" %+v"
,
err
)
return
err
return
err
}
}
...
@@ -77,39 +116,33 @@ func (m *ManagerImpl) Start() error {
...
@@ -77,39 +116,33 @@ func (m *ManagerImpl) Start() error {
}
}
// Devices is the map of devices that are known by the Device
// Devices is the map of devices that are known by the Device
// Plugin manager with the
K
ind of the devices as key
// Plugin manager with the
k
ind of the devices as key
func
(
m
*
ManagerImpl
)
Devices
()
map
[
string
][]
*
pluginapi
.
Device
{
func
(
m
*
ManagerImpl
)
Devices
()
map
[
string
][]
*
pluginapi
.
Device
{
glog
.
V
(
2
)
.
Infof
(
"Devices called"
)
m
.
mutex
.
Lock
()
m
.
mutex
.
Lock
()
defer
m
.
mutex
.
Unlock
()
defer
m
.
mutex
.
Unlock
()
devs
:=
make
(
map
[
string
][]
*
pluginapi
.
Device
)
devs
:=
make
(
map
[
string
][]
*
pluginapi
.
Device
)
for
k
,
e
:=
range
m
.
Endpoints
{
for
k
,
e
:=
range
m
.
Endpoints
{
glog
.
V
(
2
)
.
Infof
(
"Endpoint: %+v: %+v"
,
k
,
e
)
glog
.
V
(
3
)
.
Infof
(
"Endpoint: %+v: %+v"
,
k
,
e
)
e
.
mutex
.
Lock
()
devs
[
k
]
=
e
.
getDevices
()
devs
[
k
]
=
copyDevices
(
e
.
devices
)
e
.
mutex
.
Unlock
()
}
}
return
devs
return
devs
}
}
// Allocate is the call that you can use to allocate a set of Devices
// Allocate is the call that you can use to allocate a set of devices
func
(
m
*
ManagerImpl
)
Allocate
(
resourceName
string
,
// from the registered device plugins.
devs
[]
*
pluginapi
.
Device
)
(
*
pluginapi
.
AllocateResponse
,
error
)
{
func
(
m
*
ManagerImpl
)
Allocate
(
resourceName
string
,
devs
[]
string
)
(
*
pluginapi
.
AllocateResponse
,
error
)
{
m
.
mutex
.
Lock
()
defer
m
.
mutex
.
Unlock
()
if
len
(
devs
)
==
0
{
if
len
(
devs
)
==
0
{
return
nil
,
nil
return
nil
,
nil
}
}
glog
.
Infof
(
"Recieved
request for devices %v for device plugin %s"
,
glog
.
V
(
3
)
.
Infof
(
"Recieved allocation
request for devices %v for device plugin %s"
,
devs
,
resourceName
)
devs
,
resourceName
)
m
.
mutex
.
Lock
()
e
,
ok
:=
m
.
Endpoints
[
resourceName
]
e
,
ok
:=
m
.
Endpoints
[
resourceName
]
m
.
mutex
.
Unlock
()
if
!
ok
{
if
!
ok
{
return
nil
,
fmt
.
Errorf
(
"Unknown Device Plugin %s"
,
resourceName
)
return
nil
,
fmt
.
Errorf
(
"Unknown Device Plugin %s"
,
resourceName
)
}
}
...
@@ -117,45 +150,46 @@ func (m *ManagerImpl) Allocate(resourceName string,
...
@@ -117,45 +150,46 @@ func (m *ManagerImpl) Allocate(resourceName string,
return
e
.
allocate
(
devs
)
return
e
.
allocate
(
devs
)
}
}
// Register registers a device plugin
// Register registers a device plugin
.
func
(
m
*
ManagerImpl
)
Register
(
ctx
context
.
Context
,
func
(
m
*
ManagerImpl
)
Register
(
ctx
context
.
Context
,
r
*
pluginapi
.
RegisterRequest
)
(
*
pluginapi
.
Empty
,
error
)
{
r
*
pluginapi
.
RegisterRequest
)
(
*
pluginapi
.
Empty
,
error
)
{
glog
.
V
(
2
)
.
Infof
(
"Got request for Device Plugin %s"
,
r
.
ResourceName
)
glog
.
V
(
2
)
.
Infof
(
"Got request for Device Plugin %s"
,
r
.
ResourceName
)
if
r
.
Version
!=
pluginapi
.
Version
{
if
r
.
Version
!=
pluginapi
.
Version
{
return
&
pluginapi
.
Empty
{},
return
&
pluginapi
.
Empty
{},
fmt
.
Errorf
(
errUnsuportedVersion
)
fmt
.
Errorf
(
pluginapi
.
ErrUnsuportedVersion
)
}
}
if
err
:=
IsResourceNameValid
(
r
.
ResourceName
);
err
!=
nil
{
if
err
:=
IsResourceNameValid
(
r
.
ResourceName
);
err
!=
nil
{
return
&
pluginapi
.
Empty
{},
err
return
&
pluginapi
.
Empty
{},
err
}
}
if
_
,
ok
:=
m
.
Endpoints
[
r
.
ResourceName
];
ok
{
// TODO: for now, always accepts newest device plugin. Later may consider to
return
&
pluginapi
.
Empty
{},
// add some policies here, e.g., verify whether an old device plugin with the
fmt
.
Errorf
(
pluginapi
.
ErrDevicePluginAlreadyExists
)
// same resource name is still alive to determine whether we want to accept
}
// the new registration.
go
m
.
addEndpoint
(
r
)
go
m
.
addEndpoint
(
r
)
return
&
pluginapi
.
Empty
{},
nil
return
&
pluginapi
.
Empty
{},
nil
}
}
// Stop is the function that can stop the gRPC server
// Stop is the function that can stop the gRPC server
.
func
(
m
*
ManagerImpl
)
Stop
()
error
{
func
(
m
*
ManagerImpl
)
Stop
()
error
{
for
_
,
e
:=
range
m
.
Endpoints
{
for
_
,
e
:=
range
m
.
Endpoints
{
e
.
stop
()
e
.
stop
()
}
}
m
.
server
.
Stop
()
m
.
server
.
Stop
()
return
nil
return
nil
}
}
func
(
m
*
ManagerImpl
)
addEndpoint
(
r
*
pluginapi
.
RegisterRequest
)
{
func
(
m
*
ManagerImpl
)
addEndpoint
(
r
*
pluginapi
.
RegisterRequest
)
{
socketPath
:=
filepath
.
Join
(
m
.
socketdir
,
r
.
Endpoint
)
// Stops existing endpoint if there is any.
m
.
mutex
.
Lock
()
old
,
ok
:=
m
.
Endpoints
[
r
.
ResourceName
]
m
.
mutex
.
Unlock
()
if
ok
&&
old
!=
nil
{
old
.
stop
()
}
socketPath
:=
filepath
.
Join
(
m
.
socketdir
,
r
.
Endpoint
)
e
,
err
:=
newEndpoint
(
socketPath
,
r
.
ResourceName
,
m
.
callback
)
e
,
err
:=
newEndpoint
(
socketPath
,
r
.
ResourceName
,
m
.
callback
)
if
err
!=
nil
{
if
err
!=
nil
{
glog
.
Errorf
(
"Failed to dial device plugin with request %v: %v"
,
r
,
err
)
glog
.
Errorf
(
"Failed to dial device plugin with request %v: %v"
,
r
,
err
)
...
@@ -172,22 +206,15 @@ func (m *ManagerImpl) addEndpoint(r *pluginapi.RegisterRequest) {
...
@@ -172,22 +206,15 @@ func (m *ManagerImpl) addEndpoint(r *pluginapi.RegisterRequest) {
e
.
listAndWatch
(
stream
)
e
.
listAndWatch
(
stream
)
m
.
mutex
.
Lock
()
m
.
mutex
.
Lock
()
e
.
mutex
.
Lock
()
if
old
,
ok
:=
m
.
Endpoints
[
r
.
ResourceName
];
ok
&&
old
==
e
{
delete
(
m
.
Endpoints
,
r
.
ResourceName
)
delete
(
m
.
Endpoints
,
r
.
ResourceName
)
}
glog
.
V
(
2
)
.
Infof
(
"Unregistered endpoint %v"
,
e
)
glog
.
V
(
2
)
.
Infof
(
"Unregistered endpoint %v"
,
e
)
e
.
mutex
.
Unlock
()
m
.
mutex
.
Unlock
()
m
.
mutex
.
Unlock
()
}()
}()
m
.
mutex
.
Lock
()
m
.
mutex
.
Lock
()
e
.
mutex
.
Lock
()
m
.
Endpoints
[
r
.
ResourceName
]
=
e
m
.
Endpoints
[
r
.
ResourceName
]
=
e
glog
.
V
(
2
)
.
Infof
(
"Registered endpoint %v"
,
e
)
glog
.
V
(
2
)
.
Infof
(
"Registered endpoint %v"
,
e
)
e
.
mutex
.
Unlock
()
m
.
mutex
.
Unlock
()
m
.
mutex
.
Unlock
()
}
}
pkg/kubelet/deviceplugin/manager_test.go
View file @
02001af7
...
@@ -49,18 +49,18 @@ func TestNewManagerImplStart(t *testing.T) {
...
@@ -49,18 +49,18 @@ func TestNewManagerImplStart(t *testing.T) {
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
}
}
func
setup
(
t
*
testing
.
T
,
devs
[]
*
pluginapi
.
Device
,
pluginSocket
,
serverSocket
string
,
callback
MonitorCallback
)
(
Manager
,
*
MockDevicePlugin
)
{
func
setup
(
t
*
testing
.
T
,
devs
[]
*
pluginapi
.
Device
,
pluginSocket
,
serverSocket
string
,
callback
MonitorCallback
)
(
Manager
,
*
Stub
)
{
m
,
err
:=
NewManagerImpl
(
serverSocket
,
callback
)
m
,
err
:=
NewManagerImpl
(
serverSocket
,
callback
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
p
:=
New
MockDevicePlugin
(
devs
,
pluginSocket
)
p
:=
New
DevicePluginStub
(
devs
,
pluginSocket
)
err
=
p
.
Start
()
err
=
p
.
Start
()
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
return
m
,
p
return
m
,
p
}
}
func
cleanup
(
t
*
testing
.
T
,
m
Manager
,
p
*
MockDevicePlugin
)
{
func
cleanup
(
t
*
testing
.
T
,
m
Manager
,
p
*
Stub
)
{
p
.
Stop
()
p
.
Stop
()
m
.
Stop
()
m
.
Stop
()
}
}
pkg/kubelet/deviceplugin/types.go
View file @
02001af7
...
@@ -17,58 +17,54 @@ limitations under the License.
...
@@ -17,58 +17,54 @@ limitations under the License.
package
deviceplugin
package
deviceplugin
import
(
import
(
"sync"
"google.golang.org/grpc"
pluginapi
"k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
pluginapi
"k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
)
)
// MonitorCallback is the function called when a device
becomes
// MonitorCallback is the function called when a device
's health state changes,
//
unhealthy (or healthy again)
//
or new devices are reported, or old devices are deleted.
// Updated contains the most recent state of the Device
// Updated contains the most recent state of the Device
.
type
MonitorCallback
func
(
resourceName
string
,
added
,
updated
,
deleted
[]
*
pluginapi
.
Device
)
type
MonitorCallback
func
(
resourceName
string
,
added
,
updated
,
deleted
[]
*
pluginapi
.
Device
)
// Manager manages
the Device Plugins running on a machine
// Manager manages
all the Device Plugins running on a node.
type
Manager
interface
{
type
Manager
interface
{
// Start starts the gRPC
service
// Start starts the gRPC
Registration service.
Start
()
error
Start
()
error
// Devices is the map of devices that have registered themselves
// Devices is the map of devices that have registered themselves
// against the manager.
// against the manager.
// The map key is the ResourceName of the device plugins
// The map key is the ResourceName of the device plugins
.
Devices
()
map
[
string
][]
*
pluginapi
.
Device
Devices
()
map
[
string
][]
*
pluginapi
.
Device
// Allocate is calls the gRPC Allocate on the device plugin
// Allocate takes resourceName and list of device Ids, and calls the
Allocate
(
string
,
[]
*
pluginapi
.
Device
)
(
*
pluginapi
.
AllocateResponse
,
error
)
// gRPC Allocate on the device plugin matching the resourceName.
Allocate
(
string
,
[]
string
)
(
*
pluginapi
.
AllocateResponse
,
error
)
// Stop stops the manager
// Stop stops the manager
.
Stop
()
error
Stop
()
error
}
}
// ManagerImpl is the structure in charge of managing Device Plugins
// TODO: evaluate whether we need these error definitions.
type
ManagerImpl
struct
{
socketname
string
socketdir
string
Endpoints
map
[
string
]
*
endpoint
// Key is ResourceName
mutex
sync
.
Mutex
callback
MonitorCallback
server
*
grpc
.
Server
}
const
(
const
(
// ErrDevicePluginUnknown is the error raised when the device Plugin returned by Monitor is not know by the Device Plugin manager
// errFailedToDialDevicePlugin is the error raised when the device plugin could not be
ErrDevicePluginUnknown
=
"Manager does not have device plugin for device:"
// reached on the registered socket
// ErrDeviceUnknown is the error raised when the device returned by Monitor is not know by the Device Plugin manager
errFailedToDialDevicePlugin
=
"failed to dial device plugin:"
ErrDeviceUnknown
=
"Could not find device in it's Device Plugin's Device List:"
// errUnsuportedVersion is the error raised when the device plugin uses an API version not
// ErrBadSocket is the error raised when the registry socket path is not absolute
// supported by the Kubelet registry
ErrBadSocket
=
"Bad socketPath, must be an absolute path:"
errUnsuportedVersion
=
"unsupported API version by the Kubelet registry"
// ErrRemoveSocket is the error raised when the registry could not remove the existing socket
// errDevicePluginAlreadyExists is the error raised when a device plugin with the
ErrRemoveSocket
=
"Failed to remove socket while starting device plugin registry, with error"
// same Resource Name tries to register itself
// ErrListenSocket is the error raised when the registry could not listen on the socket
errDevicePluginAlreadyExists
=
"another device plugin already registered this Resource Name"
ErrListenSocket
=
"Failed to listen to socket while starting device plugin registry, with error"
// errInvalidResourceName is the error raised when a device plugin is registering
// ErrListAndWatch is the error raised when ListAndWatch ended unsuccessfully
// itself with an invalid ResourceName
ErrListAndWatch
=
"ListAndWatch ended unexpectedly for device plugin %s with error %v"
errInvalidResourceName
=
"the ResourceName is invalid"
// errEmptyResourceName is the error raised when the resource name field is empty
errEmptyResourceName
=
"invalid Empty ResourceName"
// errBadSocket is the error raised when the registry socket path is not absolute
errBadSocket
=
"bad socketPath, must be an absolute path:"
// errRemoveSocket is the error raised when the registry could not remove the existing socket
errRemoveSocket
=
"failed to remove socket while starting device plugin registry, with error"
// errListenSocket is the error raised when the registry could not listen on the socket
errListenSocket
=
"failed to listen to socket while starting device plugin registry, with error"
// errListAndWatch is the error raised when ListAndWatch ended unsuccessfully
errListAndWatch
=
"listAndWatch ended unexpectedly for device plugin %s with error %v"
)
)
pkg/kubelet/deviceplugin/utils.go
View file @
02001af7
...
@@ -18,13 +18,13 @@ package deviceplugin
...
@@ -18,13 +18,13 @@ package deviceplugin
import
(
import
(
"fmt"
"fmt"
"strings"
"k8s.io/api/core/v1"
v1helper
"k8s.io/kubernetes/pkg/api/v1/helper"
pluginapi
"k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
pluginapi
"k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
)
)
// CloneDevice clones a pluginapi.Device
func
cloneDevice
(
d
*
pluginapi
.
Device
)
*
pluginapi
.
Device
{
func
CloneDevice
(
d
*
pluginapi
.
Device
)
*
pluginapi
.
Device
{
return
&
pluginapi
.
Device
{
return
&
pluginapi
.
Device
{
ID
:
d
.
ID
,
ID
:
d
.
ID
,
Health
:
d
.
Health
,
Health
:
d
.
Health
,
...
@@ -36,41 +36,26 @@ func copyDevices(devs map[string]*pluginapi.Device) []*pluginapi.Device {
...
@@ -36,41 +36,26 @@ func copyDevices(devs map[string]*pluginapi.Device) []*pluginapi.Device {
var
clones
[]
*
pluginapi
.
Device
var
clones
[]
*
pluginapi
.
Device
for
_
,
d
:=
range
devs
{
for
_
,
d
:=
range
devs
{
clones
=
append
(
clones
,
C
loneDevice
(
d
))
clones
=
append
(
clones
,
c
loneDevice
(
d
))
}
}
return
clones
return
clones
}
}
// GetDevice returns the Device if a boolean signaling if the device was found or not
// IsResourceNameValid returns an error if the resource is invalid or is not an
func
GetDevice
(
d
*
pluginapi
.
Device
,
devs
[]
*
pluginapi
.
Device
)
(
*
pluginapi
.
Device
,
bool
)
{
// extended resource name.
name
:=
DeviceKey
(
d
)
for
_
,
d
:=
range
devs
{
if
DeviceKey
(
d
)
!=
name
{
continue
}
return
d
,
true
}
return
nil
,
false
}
// IsResourceNameValid returns an error if the resource is invalid,
func
IsResourceNameValid
(
resourceName
string
)
error
{
func
IsResourceNameValid
(
resourceName
string
)
error
{
if
resourceName
==
""
{
if
resourceName
==
""
{
return
fmt
.
Errorf
(
pluginapi
.
E
rrEmptyResourceName
)
return
fmt
.
Errorf
(
e
rrEmptyResourceName
)
}
}
if
!
IsDeviceName
(
v1
.
ResourceName
(
resourceName
))
{
if
strings
.
ContainsAny
(
resourceName
,
pluginapi
.
InvalidChars
)
{
return
fmt
.
Errorf
(
errInvalidResourceName
)
return
fmt
.
Errorf
(
pluginapi
.
ErrInvalidResourceName
)
}
}
return
nil
return
nil
}
}
// DeviceKey returns the Key of a device
// IsDeviceName returns whether the ResourceName points to an extended resource
func
DeviceKey
(
d
*
pluginapi
.
Device
)
string
{
// name exported by a device plugin.
return
d
.
ID
func
IsDeviceName
(
k
v1
.
ResourceName
)
bool
{
return
v1helper
.
IsExtendedResourceName
(
k
)
}
}
pkg/kubelet/deviceplugin/utils_test.go
View file @
02001af7
...
@@ -25,7 +25,7 @@ import (
...
@@ -25,7 +25,7 @@ import (
)
)
func
TestCloneDevice
(
t
*
testing
.
T
)
{
func
TestCloneDevice
(
t
*
testing
.
T
)
{
d
:=
C
loneDevice
(
&
pluginapi
.
Device
{
ID
:
"ADeviceId"
,
Health
:
pluginapi
.
Healthy
})
d
:=
c
loneDevice
(
&
pluginapi
.
Device
{
ID
:
"ADeviceId"
,
Health
:
pluginapi
.
Healthy
})
require
.
Equal
(
t
,
d
.
ID
,
"ADeviceId"
)
require
.
Equal
(
t
,
d
.
ID
,
"ADeviceId"
)
require
.
Equal
(
t
,
d
.
Health
,
pluginapi
.
Healthy
)
require
.
Equal
(
t
,
d
.
Health
,
pluginapi
.
Healthy
)
...
@@ -40,15 +40,14 @@ func TestCopyDevices(t *testing.T) {
...
@@ -40,15 +40,14 @@ func TestCopyDevices(t *testing.T) {
require
.
Len
(
t
,
devs
,
1
)
require
.
Len
(
t
,
devs
,
1
)
}
}
func
TestGetDevice
(
t
*
testing
.
T
)
{
func
TestIsResourceName
(
t
*
testing
.
T
)
{
devs
:=
[]
*
pluginapi
.
Device
{
require
.
NotNil
(
t
,
IsResourceNameValid
(
""
))
{
ID
:
"ADeviceId"
,
Health
:
pluginapi
.
Healthy
},
require
.
NotNil
(
t
,
IsResourceNameValid
(
"cpu"
))
}
require
.
NotNil
(
t
,
IsResourceNameValid
(
"name1"
))
require
.
NotNil
(
t
,
IsResourceNameValid
(
"alpha.kubernetes.io/name1"
))
_
,
ok
:=
GetDevice
(
&
pluginapi
.
Device
{
ID
:
"AnotherDeviceId"
},
devs
)
require
.
NotNil
(
t
,
IsResourceNameValid
(
"beta.kubernetes.io/name1"
))
require
.
False
(
t
,
ok
)
require
.
NotNil
(
t
,
IsResourceNameValid
(
"kubernetes.io/name1"
))
require
.
Nil
(
t
,
IsResourceNameValid
(
"domain1.io/name1"
))
d
,
ok
:=
GetDevice
(
&
pluginapi
.
Device
{
ID
:
"ADeviceId"
},
devs
)
require
.
Nil
(
t
,
IsResourceNameValid
(
"alpha.domain1.io/name1"
))
require
.
True
(
t
,
ok
)
require
.
Nil
(
t
,
IsResourceNameValid
(
"beta.domain1.io/name1"
))
require
.
Equal
(
t
,
d
,
devs
[
0
])
}
}
pkg/kubelet/kubelet.go
View file @
02001af7
...
@@ -754,15 +754,6 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
...
@@ -754,15 +754,6 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
return
nil
,
err
return
nil
,
err
}
}
if
utilfeature
.
DefaultFeatureGate
.
Enabled
(
features
.
DevicePlugins
)
{
devicePluginHdlr
,
err
:=
cm
.
NewDevicePluginHandler
()
if
err
!=
nil
{
return
nil
,
err
}
klet
.
containerManager
.
SetDevicePluginHandler
(
devicePluginHdlr
)
}
// If the experimentalMounterPathFlag is set, we do not want to
// If the experimentalMounterPathFlag is set, we do not want to
// check node capabilities since the mount path is not the default
// check node capabilities since the mount path is not the default
if
len
(
kubeCfg
.
ExperimentalMounterPath
)
!=
0
{
if
len
(
kubeCfg
.
ExperimentalMounterPath
)
!=
0
{
...
...
pkg/kubelet/kubelet_node_status.go
View file @
02001af7
...
@@ -39,7 +39,6 @@ import (
...
@@ -39,7 +39,6 @@ import (
"k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/features"
kubeletapis
"k8s.io/kubernetes/pkg/kubelet/apis"
kubeletapis
"k8s.io/kubernetes/pkg/kubelet/apis"
pluginapi
"k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1alpha1"
"k8s.io/kubernetes/pkg/kubelet/cadvisor"
"k8s.io/kubernetes/pkg/kubelet/cadvisor"
"k8s.io/kubernetes/pkg/kubelet/events"
"k8s.io/kubernetes/pkg/kubelet/events"
"k8s.io/kubernetes/pkg/kubelet/util"
"k8s.io/kubernetes/pkg/kubelet/util"
...
@@ -596,6 +595,15 @@ func (kl *Kubelet) setNodeStatusMachineInfo(node *v1.Node) {
...
@@ -596,6 +595,15 @@ func (kl *Kubelet) setNodeStatusMachineInfo(node *v1.Node) {
node
.
Status
.
Capacity
[
v1
.
ResourceEphemeralStorage
]
=
initialCapacity
[
v1
.
ResourceEphemeralStorage
]
node
.
Status
.
Capacity
[
v1
.
ResourceEphemeralStorage
]
=
initialCapacity
[
v1
.
ResourceEphemeralStorage
]
}
}
}
}
initialCapacity
:=
kl
.
containerManager
.
GetCapacity
()
if
initialCapacity
!=
nil
{
for
k
,
v
:=
range
initialCapacity
{
if
v1helper
.
IsExtendedResourceName
(
k
)
{
node
.
Status
.
Capacity
[
k
]
=
v
}
}
}
}
}
// Set Allocatable.
// Set Allocatable.
...
@@ -622,27 +630,6 @@ func (kl *Kubelet) setNodeStatusMachineInfo(node *v1.Node) {
...
@@ -622,27 +630,6 @@ func (kl *Kubelet) setNodeStatusMachineInfo(node *v1.Node) {
}
}
node
.
Status
.
Allocatable
[
k
]
=
value
node
.
Status
.
Allocatable
[
k
]
=
value
}
}
hdlr
:=
kl
.
containerManager
.
GetDevicePluginHandler
()
if
hdlr
==
nil
{
return
}
for
k
,
v
:=
range
hdlr
.
Devices
()
{
key
:=
v1
.
ResourceName
(
v1
.
ResourceOpaqueIntPrefix
+
k
)
var
n
int64
n
=
0
for
_
,
d
:=
range
v
{
if
d
.
Health
==
pluginapi
.
Unhealthy
{
continue
}
n
++
}
node
.
Status
.
Capacity
[
key
]
=
*
resource
.
NewQuantity
(
n
,
resource
.
DecimalSI
)
}
}
}
// Set versioninfo for the node.
// Set versioninfo for the node.
...
...
pkg/kubelet/kubelet_pods.go
View file @
02001af7
...
@@ -352,8 +352,13 @@ func (kl *Kubelet) GetPodCgroupParent(pod *v1.Pod) string {
...
@@ -352,8 +352,13 @@ func (kl *Kubelet) GetPodCgroupParent(pod *v1.Pod) string {
// the container runtime to set parameters for launching a container.
// the container runtime to set parameters for launching a container.
func
(
kl
*
Kubelet
)
GenerateRunContainerOptions
(
pod
*
v1
.
Pod
,
container
*
v1
.
Container
,
podIP
string
)
(
*
kubecontainer
.
RunContainerOptions
,
bool
,
error
)
{
func
(
kl
*
Kubelet
)
GenerateRunContainerOptions
(
pod
*
v1
.
Pod
,
container
*
v1
.
Container
,
podIP
string
)
(
*
kubecontainer
.
RunContainerOptions
,
bool
,
error
)
{
useClusterFirstPolicy
:=
false
useClusterFirstPolicy
:=
false
opts
,
err
:=
kl
.
containerManager
.
GetResources
(
pod
,
container
,
kl
.
GetActivePods
())
if
err
!=
nil
{
return
nil
,
false
,
err
}
cgroupParent
:=
kl
.
GetPodCgroupParent
(
pod
)
cgroupParent
:=
kl
.
GetPodCgroupParent
(
pod
)
opts
:=
&
kubecontainer
.
RunContainerOptions
{
CgroupParent
:
cgroupParent
}
opts
.
CgroupParent
=
cgroupParent
hostname
,
hostDomainName
,
err
:=
kl
.
GeneratePodHostNameAndDomain
(
pod
)
hostname
,
hostDomainName
,
err
:=
kl
.
GeneratePodHostNameAndDomain
(
pod
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
false
,
err
return
nil
,
false
,
err
...
@@ -364,19 +369,23 @@ func (kl *Kubelet) GenerateRunContainerOptions(pod *v1.Pod, container *v1.Contai
...
@@ -364,19 +369,23 @@ func (kl *Kubelet) GenerateRunContainerOptions(pod *v1.Pod, container *v1.Contai
opts
.
PortMappings
=
kubecontainer
.
MakePortMappings
(
container
)
opts
.
PortMappings
=
kubecontainer
.
MakePortMappings
(
container
)
// TODO(random-liu): Move following convert functions into pkg/kubelet/container
// TODO(random-liu): Move following convert functions into pkg/kubelet/container
opts
.
Devices
,
err
=
kl
.
makeDevices
(
pod
,
container
)
devices
,
err
:
=
kl
.
makeDevices
(
pod
,
container
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
false
,
err
return
nil
,
false
,
err
}
}
opts
.
Devices
=
append
(
opts
.
Devices
,
devices
...
)
opts
.
Mounts
,
err
=
makeMounts
(
pod
,
kl
.
getPodDir
(
pod
.
UID
),
container
,
hostname
,
hostDomainName
,
podIP
,
volumes
)
mounts
,
err
:
=
makeMounts
(
pod
,
kl
.
getPodDir
(
pod
.
UID
),
container
,
hostname
,
hostDomainName
,
podIP
,
volumes
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
false
,
err
return
nil
,
false
,
err
}
}
opts
.
Envs
,
err
=
kl
.
makeEnvironmentVariables
(
pod
,
container
,
podIP
)
opts
.
Mounts
=
append
(
opts
.
Mounts
,
mounts
...
)
envs
,
err
:=
kl
.
makeEnvironmentVariables
(
pod
,
container
,
podIP
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
false
,
err
return
nil
,
false
,
err
}
}
opts
.
Envs
=
append
(
opts
.
Envs
,
envs
...
)
// Disabling adding TerminationMessagePath on Windows as these files would be mounted as docker volume and
// Disabling adding TerminationMessagePath on Windows as these files would be mounted as docker volume and
// Docker for Windows has a bug where only directories can be mounted
// Docker for Windows has a bug where only directories can be mounted
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment