Skip to content

Commit e928ad1

Browse files
committed
AMD GPU Operator; Adds DeviceConfig PKG & Tests
Added package & tests for the AMD GPU Operator DeviceConfig
1 parent ce9fc42 commit e928ad1

2 files changed

Lines changed: 609 additions & 0 deletions

File tree

pkg/amdgpu/deviceconfig.go

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
package amdgpu
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/golang/glog"
8+
"github.com/openshift-kni/eco-goinfra/pkg/clients"
9+
"github.com/openshift-kni/eco-goinfra/pkg/msg"
10+
amdgpuv1 "github.com/openshift-kni/eco-goinfra/pkg/schemes/amd/gpu-operator/api/v1alpha1"
11+
12+
k8serrors "k8s.io/apimachinery/pkg/api/errors"
13+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14+
"k8s.io/apimachinery/pkg/util/json"
15+
goclient "sigs.k8s.io/controller-runtime/pkg/client"
16+
)
17+
18+
// Builder provides a struct for DeviceConfig object
19+
// from the cluster and a DeviceConfig definition.
20+
type Builder struct {
21+
// Builder definition. Used to create
22+
// Builder object with minimum set of required elements.
23+
Definition *amdgpuv1.DeviceConfig
24+
// Created Builder object on the cluster.
25+
Object *amdgpuv1.DeviceConfig
26+
// api client to interact with the cluster.
27+
apiClient *clients.Settings
28+
// errorMsg is processed before Builder object is created.
29+
errorMsg string
30+
}
31+
32+
// NewBuilderFromObjectString creates a Builder object from CSV alm-examples.
33+
func NewBuilderFromObjectString(apiClient *clients.Settings, almExample string) *Builder {
34+
glog.V(100).Infof(
35+
"Initializing new Builder structure from almExample string")
36+
37+
if apiClient == nil {
38+
glog.V(100).Info("The apiClient of the DeviceConfig is nil")
39+
40+
return nil
41+
}
42+
43+
err := apiClient.AttachScheme(amdgpuv1.AddToScheme)
44+
if err != nil {
45+
glog.V(100).Info("Failed to add amdgpu v1 scheme to client schemes")
46+
47+
return nil
48+
}
49+
50+
builder := &Builder{
51+
apiClient: apiClient,
52+
}
53+
54+
deviceConfig, err := getDeviceConfigFromAlmExample(almExample)
55+
if err != nil {
56+
glog.V(100).Infof(
57+
"Error initializing DeviceConfig from alm-examples: %s", err.Error())
58+
59+
builder.errorMsg = fmt.Sprintf("error initializing DeviceConfig from alm-examples: %s",
60+
err.Error())
61+
62+
return builder
63+
}
64+
65+
builder.Definition = deviceConfig
66+
67+
glog.V(100).Infof(
68+
"Initializing Builder definition to DeviceConfig object")
69+
70+
if builder.Definition == nil {
71+
glog.V(100).Infof("The DeviceConfig object definition is nil")
72+
73+
builder.errorMsg = "deviceConfig definition is nil"
74+
75+
return builder
76+
}
77+
78+
return builder
79+
}
80+
81+
// Pull loads an existing DeviceConfig into Builder struct.
82+
func Pull(apiClient *clients.Settings, name, namespace string) (*Builder, error) {
83+
glog.V(100).Infof("Pulling existing deviceConfig name: %s in namespace: %s", name, namespace)
84+
85+
if apiClient == nil {
86+
glog.V(100).Info("The apiClient of the Policy is nil")
87+
88+
return nil, fmt.Errorf("the apiClient of the Policy is nil")
89+
}
90+
91+
err := apiClient.AttachScheme(amdgpuv1.AddToScheme)
92+
if err != nil {
93+
glog.V(100).Info("Failed to add amdgpu v1 scheme to client schemes")
94+
95+
return nil, err
96+
}
97+
98+
builder := &Builder{
99+
apiClient: apiClient,
100+
Definition: &amdgpuv1.DeviceConfig{
101+
ObjectMeta: metav1.ObjectMeta{
102+
Name: name,
103+
Namespace: namespace,
104+
},
105+
},
106+
}
107+
108+
if name == "" {
109+
glog.V(100).Infof("DeviceConfig name is empty")
110+
111+
return nil, fmt.Errorf("DeviceConfig 'name' cannot be empty")
112+
}
113+
114+
if namespace == "" {
115+
glog.V(100).Infof("DeviceConfig namespace is empty")
116+
117+
return nil, fmt.Errorf("DeviceConfig 'namespace' cannot be empty")
118+
}
119+
120+
if !builder.Exists() {
121+
return nil, fmt.Errorf("deviceConfig object %s does not exist in namespace %s", name, namespace)
122+
}
123+
124+
builder.Definition = builder.Object
125+
126+
return builder, nil
127+
}
128+
129+
// Get returns DeviceConfig object if found.
130+
func (builder *Builder) Get() (*amdgpuv1.DeviceConfig, error) {
131+
if valid, err := builder.validate(); !valid {
132+
return nil, err
133+
}
134+
135+
glog.V(100).Infof("Collecting DeviceConfig object %s in namespace %s",
136+
builder.Definition.Name, builder.Definition.Namespace)
137+
138+
deviceConfig := &amdgpuv1.DeviceConfig{}
139+
err := builder.apiClient.Get(context.TODO(), goclient.ObjectKey{
140+
Name: builder.Definition.Name,
141+
Namespace: builder.Definition.Namespace,
142+
}, deviceConfig)
143+
144+
if err != nil {
145+
glog.V(100).Infof("DeviceConfig object %s does not exist in namespace %s",
146+
builder.Definition.Name, builder.Definition.Namespace)
147+
148+
return nil, err
149+
}
150+
151+
return deviceConfig, nil
152+
}
153+
154+
// Exists checks whether the given DeviceConfig exists.
155+
func (builder *Builder) Exists() bool {
156+
if valid, _ := builder.validate(); !valid {
157+
return false
158+
}
159+
160+
glog.V(100).Infof(
161+
"Checking if DeviceConfig %s exists in namespace %s", builder.Definition.Name,
162+
builder.Definition.Namespace)
163+
164+
var err error
165+
builder.Object, err = builder.Get()
166+
167+
if err != nil {
168+
glog.V(100).Infof("Failed to collect DeviceConfig object due to %s", err.Error())
169+
}
170+
171+
return err == nil || !k8serrors.IsNotFound(err)
172+
}
173+
174+
// Delete removes a DeviceConfig.
175+
func (builder *Builder) Delete() (*Builder, error) {
176+
if valid, err := builder.validate(); !valid {
177+
return builder, err
178+
}
179+
180+
glog.V(100).Infof("Deleting DeviceConfig %s in namespace %s", builder.Definition.Name,
181+
builder.Definition.Namespace)
182+
183+
if !builder.Exists() {
184+
glog.V(100).Infof("DeviceConfig '%s' in namespace '%s' cannot be deleted because it does not exist",
185+
builder.Definition.Name, builder.Definition.Namespace)
186+
187+
builder.Object = nil
188+
189+
return builder, nil
190+
}
191+
192+
err := builder.apiClient.Delete(context.TODO(), builder.Definition)
193+
194+
if err != nil {
195+
return builder, fmt.Errorf("cannot delete DeviceConfig: %w", err)
196+
}
197+
198+
builder.Object = nil
199+
200+
return builder, nil
201+
}
202+
203+
// Create makes a DeviceConfig in the cluster and stores the created object in struct.
204+
func (builder *Builder) Create() (*Builder, error) {
205+
if valid, err := builder.validate(); !valid {
206+
return builder, err
207+
}
208+
209+
glog.V(100).Infof("Creating the DeviceConfig %s in namespace %s", builder.Definition.Name,
210+
builder.Definition.Namespace)
211+
212+
var err error
213+
if !builder.Exists() {
214+
err = builder.apiClient.Create(context.TODO(), builder.Definition)
215+
216+
if err == nil {
217+
builder.Object = builder.Definition
218+
}
219+
}
220+
221+
return builder, err
222+
}
223+
224+
// Update renovates the existing DeviceConfig object with the definition in builder.
225+
func (builder *Builder) Update(force bool) (*Builder, error) {
226+
if valid, err := builder.validate(); !valid {
227+
return builder, err
228+
}
229+
230+
glog.V(100).Infof("Updating the DeviceConfig object named: %s in namespace: %s",
231+
builder.Definition.Name, builder.Definition.Namespace)
232+
233+
err := builder.apiClient.Update(context.TODO(), builder.Definition)
234+
235+
if err != nil {
236+
if force {
237+
glog.V(100).Infof(
238+
msg.FailToUpdateNotification("DeviceConfig", builder.Definition.Name, builder.Definition.Namespace))
239+
240+
builder, err := builder.Delete()
241+
242+
if err != nil {
243+
glog.V(100).Infof(
244+
msg.FailToUpdateError("DeviceConfig", builder.Definition.Name, builder.Definition.Namespace))
245+
246+
return nil, err
247+
}
248+
249+
return builder.Create()
250+
}
251+
}
252+
253+
return builder, err
254+
}
255+
256+
// getDeviceConfigFromAlmExample extracts the DeviceConfig from the alm-examples block.
257+
func getDeviceConfigFromAlmExample(almExample string) (*amdgpuv1.DeviceConfig, error) {
258+
deviceConfigList := &amdgpuv1.DeviceConfigList{}
259+
260+
if almExample == "" {
261+
return nil, fmt.Errorf("almExample is an empty string")
262+
}
263+
264+
err := json.Unmarshal([]byte(almExample), &deviceConfigList.Items)
265+
266+
if err != nil {
267+
return nil, err
268+
}
269+
270+
if len(deviceConfigList.Items) == 0 {
271+
return nil, fmt.Errorf("failed to get alm examples")
272+
}
273+
274+
return &deviceConfigList.Items[0], nil
275+
}
276+
277+
// validate will check that the builder and builder definition are properly initialized before
278+
// accessing any member fields.
279+
func (builder *Builder) validate() (bool, error) {
280+
resourceCRD := "DeviceConfig"
281+
282+
if builder == nil {
283+
glog.V(100).Infof("The %s builder is uninitialized", resourceCRD)
284+
285+
return false, fmt.Errorf("error: received nil %s builder", resourceCRD)
286+
}
287+
288+
if builder.Definition == nil {
289+
glog.V(100).Infof("The %s is undefined", resourceCRD)
290+
291+
return false, fmt.Errorf("%s", msg.UndefinedCrdObjectErrString(resourceCRD))
292+
}
293+
294+
if builder.apiClient == nil {
295+
glog.V(100).Infof("The %s builder apiclient is nil", resourceCRD)
296+
297+
return false, fmt.Errorf("%s builder cannot have nil apiClient", resourceCRD)
298+
}
299+
300+
return true, nil
301+
}

0 commit comments

Comments
 (0)