Skip to content

Commit 047f333

Browse files
authored
Merge pull request #491 from uniemimu/master
operator: GPU-plugin initImage
2 parents 4835bba + c935570 commit 047f333

File tree

6 files changed

+127
-52
lines changed

6 files changed

+127
-52
lines changed

deployments/operator/crd/bases/deviceplugin.intel.com_gpudeviceplugins.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ spec:
5252
image:
5353
description: Image is a container image with GPU device plugin executable.
5454
type: string
55+
initImage:
56+
description: InitImage is a container image with tools (e.g., GPU NFD
57+
source hook) installed on each node.
58+
type: string
5559
logLevel:
5660
description: LogLevel sets the plugin's log level.
5761
minimum: 0

pkg/apis/deviceplugin/v1/fpgadeviceplugin_webhook.go

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
package v1
1616

1717
import (
18-
"strings"
19-
2018
"github.com/pkg/errors"
2119
"k8s.io/apimachinery/pkg/runtime"
2220
"k8s.io/apimachinery/pkg/util/version"
@@ -98,29 +96,3 @@ func (r *FpgaDevicePlugin) validatePlugin() error {
9896

9997
return validatePluginImage(r.Spec.InitImage, "intel-fpga-initcontainer", fpgaMinVersion)
10098
}
101-
102-
func validatePluginImage(image, expectedName string, expectedMinVersion *version.Version) error {
103-
parts := strings.SplitN(image, ":", 2)
104-
if len(parts) != 2 {
105-
return errors.Errorf("incorrect image field %q", image)
106-
}
107-
namespacedName := parts[0]
108-
versionStr := parts[1]
109-
110-
parts = strings.Split(namespacedName, "/")
111-
name := parts[len(parts)-1]
112-
if name != expectedName {
113-
return errors.Errorf("incorrect image name %q. Make sure you use '<vendor>/%s:<version>'", name, expectedName)
114-
}
115-
116-
ver, err := version.ParseSemantic(versionStr)
117-
if err != nil {
118-
return errors.Wrapf(err, "unable to parse version %q", versionStr)
119-
}
120-
121-
if !ver.AtLeast(expectedMinVersion) {
122-
return errors.Errorf("version %q is too low. Should be at least %q", ver, expectedMinVersion)
123-
}
124-
125-
return nil
126-
}

pkg/apis/deviceplugin/v1/gpudeviceplugin_types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ type GpuDevicePluginSpec struct {
2828
// Image is a container image with GPU device plugin executable.
2929
Image string `json:"image,omitempty"`
3030

31+
// InitImage is a container image with tools (e.g., GPU NFD source hook) installed on each node.
32+
InitImage string `json:"initImage,omitempty"`
33+
3134
// SharedDevNum is a number of containers that can share the same GPU device.
3235
// +kubebuilder:validation:Minimum=1
3336
SharedDevNum int `json:"sharedDevNum,omitempty"`

pkg/apis/deviceplugin/v1/gpudeviceplugin_webhook.go

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
package v1
1616

1717
import (
18-
"strings"
19-
2018
"github.com/pkg/errors"
2119
"k8s.io/apimachinery/pkg/runtime"
2220
"k8s.io/apimachinery/pkg/util/version"
@@ -88,27 +86,11 @@ func (r *GpuDevicePlugin) ValidateDelete() error {
8886
}
8987

9088
func (r *GpuDevicePlugin) validatePlugin() error {
91-
parts := strings.SplitN(r.Spec.Image, ":", 2)
92-
if len(parts) != 2 {
93-
return errors.Errorf("incorrect image field %q", r.Spec.Image)
94-
}
95-
namespacedName := parts[0]
96-
versionStr := parts[1]
97-
98-
parts = strings.Split(namespacedName, "/")
99-
name := parts[len(parts)-1]
100-
if name != "intel-gpu-plugin" {
101-
return errors.Errorf("incorrect image name %q. Make sure you use '<vendor>/intel-gpu-plugin:<version>'", name)
102-
}
103-
104-
ver, err := version.ParseSemantic(versionStr)
105-
if err != nil {
106-
return errors.Wrapf(err, "unable to parse version %q", versionStr)
107-
}
108-
109-
if !ver.AtLeast(gpuMinVersion) {
110-
return errors.Errorf("version %q is too low. Should be at least %q", ver, gpuMinVersion)
89+
if r.Spec.InitImage != "" {
90+
if err := validatePluginImage(r.Spec.InitImage, "intel-gpu-initcontainer", gpuMinVersion); err != nil {
91+
return err
92+
}
11193
}
11294

113-
return nil
95+
return validatePluginImage(r.Spec.Image, "intel-gpu-plugin", gpuMinVersion)
11496
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2020 Intel Corporation. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package v1
16+
17+
import (
18+
"strings"
19+
20+
"github.com/pkg/errors"
21+
"k8s.io/apimachinery/pkg/util/version"
22+
)
23+
24+
// common functions for webhooks
25+
26+
func validatePluginImage(image, expectedName string, expectedMinVersion *version.Version) error {
27+
parts := strings.SplitN(image, ":", 2)
28+
if len(parts) != 2 {
29+
return errors.Errorf("incorrect image field %q", image)
30+
}
31+
namespacedName := parts[0]
32+
versionStr := parts[1]
33+
34+
parts = strings.Split(namespacedName, "/")
35+
name := parts[len(parts)-1]
36+
if name != expectedName {
37+
return errors.Errorf("incorrect image name %q. Make sure you use '<vendor>/%s:<version>'", name, expectedName)
38+
}
39+
40+
ver, err := version.ParseSemantic(versionStr)
41+
if err != nil {
42+
return errors.Wrapf(err, "unable to parse version %q", versionStr)
43+
}
44+
45+
if !ver.AtLeast(expectedMinVersion) {
46+
return errors.Errorf("version %q is too low. Should be at least %q", ver, expectedMinVersion)
47+
}
48+
49+
return nil
50+
}

pkg/controllers/gpu/controller.go

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ func (c *controller) NewDaemonSet(rawObj runtime.Object) *apps.DaemonSet {
8181
}
8282

8383
yes := true
84-
return &apps.DaemonSet{
84+
daemonSet := apps.DaemonSet{
8585
ObjectMeta: metav1.ObjectMeta{
8686
Namespace: devicePlugin.Namespace,
8787
GenerateName: devicePlugin.Name + "-",
@@ -170,6 +170,59 @@ func (c *controller) NewDaemonSet(rawObj runtime.Object) *apps.DaemonSet {
170170
},
171171
},
172172
}
173+
// add the optional InitImage
174+
if devicePlugin.Spec.InitImage != "" {
175+
setInitContainer(&daemonSet.Spec.Template.Spec, devicePlugin.Spec.InitImage)
176+
}
177+
return &daemonSet
178+
}
179+
180+
func setInitContainer(spec *v1.PodSpec, imageName string) {
181+
yes := true
182+
spec.InitContainers = []v1.Container{
183+
{
184+
Image: imageName,
185+
ImagePullPolicy: "IfNotPresent",
186+
Name: "intel-gpu-initcontainer",
187+
SecurityContext: &v1.SecurityContext{
188+
ReadOnlyRootFilesystem: &yes,
189+
},
190+
VolumeMounts: []v1.VolumeMount{
191+
{
192+
MountPath: "/etc/kubernetes/node-feature-discovery/source.d/",
193+
Name: "nfd-source-hooks",
194+
},
195+
},
196+
}}
197+
directoryOrCreate := v1.HostPathDirectoryOrCreate
198+
missing := true
199+
for _, vol := range spec.Volumes {
200+
if vol.Name == "nfd-source-hooks" {
201+
missing = false
202+
break
203+
}
204+
}
205+
if missing {
206+
spec.Volumes = append(spec.Volumes, v1.Volume{
207+
Name: "nfd-source-hooks",
208+
VolumeSource: v1.VolumeSource{
209+
HostPath: &v1.HostPathVolumeSource{
210+
Path: "/etc/kubernetes/node-feature-discovery/source.d/",
211+
Type: &directoryOrCreate,
212+
},
213+
},
214+
})
215+
}
216+
}
217+
218+
func removeVolume(volumes []v1.Volume, name string) []v1.Volume {
219+
newVolumes := []v1.Volume{}
220+
for _, volume := range volumes {
221+
if volume.Name != name {
222+
newVolumes = append(newVolumes, volume)
223+
}
224+
}
225+
return newVolumes
173226
}
174227

175228
func (c *controller) UpdateDaemonSet(rawObj runtime.Object, ds *apps.DaemonSet) (updated bool) {
@@ -180,6 +233,17 @@ func (c *controller) UpdateDaemonSet(rawObj runtime.Object, ds *apps.DaemonSet)
180233
updated = true
181234
}
182235

236+
if dp.Spec.InitImage == "" {
237+
if ds.Spec.Template.Spec.InitContainers != nil {
238+
ds.Spec.Template.Spec.InitContainers = nil
239+
ds.Spec.Template.Spec.Volumes = removeVolume(ds.Spec.Template.Spec.Volumes, "nfd-source-hooks")
240+
updated = true
241+
}
242+
} else {
243+
setInitContainer(&ds.Spec.Template.Spec, dp.Spec.InitImage)
244+
updated = true
245+
}
246+
183247
if dp.Spec.NodeSelector == nil {
184248
dp.Spec.NodeSelector = map[string]string{"kubernetes.io/arch": "amd64"}
185249
} else {

0 commit comments

Comments
 (0)