Skip to content

Commit

Permalink
chore: Add unit test for nv-ipam-cni objects rendering and sync
Browse files Browse the repository at this point in the history
Signed-off-by: Soule BA <[email protected]>
  • Loading branch information
souleb committed Jul 23, 2024
1 parent fdcb8a3 commit 08d5f65
Show file tree
Hide file tree
Showing 3 changed files with 345 additions and 35 deletions.
113 changes: 95 additions & 18 deletions pkg/state/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,28 @@ import (

mellanoxv1alpha1 "github.com/Mellanox/network-operator/api/v1alpha1"
clustertype_mocks "github.com/Mellanox/network-operator/pkg/clustertype/mocks"
"github.com/Mellanox/network-operator/pkg/consts"
"github.com/Mellanox/network-operator/pkg/state"
"github.com/Mellanox/network-operator/pkg/staticconfig"
staticconfig_mocks "github.com/Mellanox/network-operator/pkg/staticconfig/mocks"

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
)

const (
defaultTestRepository = "myRepo"
defaultTestImage = "myImage"
defaultTestVersion = "myVersion"
)

func getTestCatalog() state.InfoCatalog {
catalog := state.NewInfoCatalog()
clusterTypeProvider := clustertype_mocks.Provider{}
Expand Down Expand Up @@ -88,42 +98,53 @@ func assertCommonPodTemplateFields(template *corev1.PodTemplateSpec, image *mell
// Container Resources
Expect(template.Spec.Containers[0].Resources.Limits).To(Equal(image.ContainerResources[0].Limits))
Expect(template.Spec.Containers[0].Resources.Requests).To(Equal(image.ContainerResources[0].Requests))

Expect(template.Spec.Tolerations).To(ContainElements(
corev1.Toleration{
Key: "nvidia.com/gpu",
Operator: "Exists",
Value: "",
Effect: "NoSchedule",
TolerationSeconds: nil,
},
))
}

func assertCommonDeploymentFields(u *unstructured.Unstructured, image *mellanoxv1alpha1.ImageSpec) {
func assertCommonDeploymentFieldsFromUnstructured(u *unstructured.Unstructured, image *mellanoxv1alpha1.ImageSpec) {
d := &appsv1.Deployment{}
err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), d)
Expect(err).ToNot(HaveOccurred())
assertCommonDeploymentFields(d, image)
}

func assertCommonDeploymentFields(d *appsv1.Deployment, image *mellanoxv1alpha1.ImageSpec) {
assertCommonPodTemplateFields(&d.Spec.Template, image)
}

func assertCommonDaemonSetFields(u *unstructured.Unstructured,
func assertCommonDaemonSetFieldsFromUnstructured(u *unstructured.Unstructured,
image *mellanoxv1alpha1.ImageSpec, policy *mellanoxv1alpha1.NicClusterPolicy) {
ds := &appsv1.DaemonSet{}
err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), ds)
Expect(err).ToNot(HaveOccurred())
assertCommonDaemonSetFields(ds, image, policy)
}

func assertCommonDaemonSetFields(ds *appsv1.DaemonSet, image *mellanoxv1alpha1.ImageSpec,
policy *mellanoxv1alpha1.NicClusterPolicy) {
assertCommonPodTemplateFields(&ds.Spec.Template, image)
// Tolerations

Expect(ds.Spec.Template.Spec.Tolerations).To(ContainElements(
corev1.Toleration{Key: "first-taint"},
corev1.Toleration{
Key: "nvidia.com/gpu",
Operator: "Exists",
Value: "",
Effect: "NoSchedule",
TolerationSeconds: nil,
},
))

// NodeAffinity
Expect(ds.Spec.Template.Spec.Affinity.NodeAffinity).To(Equal(policy.Spec.NodeAffinity))
}

func getTestImageSpec() *mellanoxv1alpha1.ImageSpec {
return &mellanoxv1alpha1.ImageSpec{
Image: "image-one",
Repository: "repository",
Version: "five",
Image: defaultTestImage,
Repository: defaultTestRepository,
Version: defaultTestVersion,
ImagePullSecrets: []string{"secret-one", "secret-two"},
}
}
Expand All @@ -148,10 +169,14 @@ func isNamespaced(obj *unstructured.Unstructured) bool {
obj.GetKind() != "ValidatingWebhookConfiguration"
}

func assertCNIBinDirForDS(u *unstructured.Unstructured) {
func assertCNIBinDirForDSFromUnstructured(u *unstructured.Unstructured) {
ds := &appsv1.DaemonSet{}
err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), ds)
Expect(err).ToNot(HaveOccurred())
assertCNIBinDirForDS(ds)
}

func assertCNIBinDirForDS(ds *appsv1.DaemonSet) {
for i := range ds.Spec.Template.Spec.Volumes {
vol := ds.Spec.Template.Spec.Volumes[i]
if vol.Name == "cnibin" {
Expand All @@ -171,10 +196,10 @@ func GetManifestObjectsTest(ctx context.Context, cr *mellanoxv1alpha1.NicCluster
}
switch got[i].GetKind() {
case "DaemonSet":
assertCommonDaemonSetFields(got[i], imageSpec, cr)
assertCNIBinDirForDS(got[i])
assertCommonDaemonSetFieldsFromUnstructured(got[i], imageSpec, cr)
assertCNIBinDirForDSFromUnstructured(got[i])
case "Deployment":
assertCommonDeploymentFields(got[i], imageSpec)
assertCommonDeploymentFieldsFromUnstructured(got[i], imageSpec)
}
}
}
Expand All @@ -201,3 +226,55 @@ func getTestClusterPolicyWithBaseFields() *mellanoxv1alpha1.NicClusterPolicy {
},
}
}

func getKindState(ctx context.Context, c client.Client, objs []*unstructured.Unstructured,
targetKind string) (state.SyncState, error) {
reqLogger := log.FromContext(ctx)
reqLogger.V(consts.LogLevelInfo).Info("Checking related object states")
for _, obj := range objs {
if obj.GetKind() != targetKind {
continue
}
found := obj.DeepCopy()
err := c.Get(
ctx, types.NamespacedName{Name: found.GetName(), Namespace: found.GetNamespace()}, found)
if err != nil {
if k8serrors.IsNotFound(err) {
return state.SyncStateNotReady, nil
}
return state.SyncStateNotReady, fmt.Errorf("failed to get object: %w", err)
}

buf, err := found.MarshalJSON()
if err != nil {
return state.SyncStateNotReady, fmt.Errorf("failed to marshall unstructured daemonset object: %w", err)
}

switch obj.GetKind() {
case "DaemonSet":
ds := &appsv1.DaemonSet{}
if err = json.Unmarshal(buf, ds); err != nil {
return state.SyncStateNotReady, fmt.Errorf("failed to unmarshall to daemonset object: %w", err)
}
if ds.Status.DesiredNumberScheduled != 0 && ds.Status.DesiredNumberScheduled == ds.Status.NumberAvailable &&
ds.Status.UpdatedNumberScheduled == ds.Status.NumberAvailable {
return state.SyncStateReady, nil
}
return state.SyncStateNotReady, nil
case "Deployment":
d := &appsv1.Deployment{}
if err = json.Unmarshal(buf, d); err != nil {
return state.SyncStateNotReady, fmt.Errorf("failed to unmarshall to deployment object: %w", err)
}

if d.Status.ObservedGeneration > 0 && d.Status.Replicas == d.Status.ReadyReplicas &&
d.Status.UpdatedReplicas == d.Status.AvailableReplicas {
return state.SyncStateReady, nil
}
return state.SyncStateNotReady, nil
default:
return state.SyncStateNotReady, fmt.Errorf("unsupported target kind")
}
}
return state.SyncStateNotReady, fmt.Errorf("objects list does not contain the specified target kind")
}
2 changes: 1 addition & 1 deletion pkg/state/state_nv_ipam_cni.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ type NVIPAMManifestRenderData struct {
//
//nolint:dupl
func (s *stateNVIPAMCNI) Sync(
ctx context.Context, customResource interface{}, infoCatalog InfoCatalog) (SyncState, error) {
ctx context.Context, customResource any, infoCatalog InfoCatalog) (SyncState, error) {
reqLogger := log.FromContext(ctx)
cr := customResource.(*mellanoxv1alpha1.NicClusterPolicy)
reqLogger.V(consts.LogLevelInfo).Info(
Expand Down
Loading

0 comments on commit 08d5f65

Please sign in to comment.