Skip to content

Commit

Permalink
Validate CR/node name from the node names in the cluster
Browse files Browse the repository at this point in the history
We want to validate that CR has been created correctly. Also UT functionality
  • Loading branch information
razo7 committed Jun 1, 2023
1 parent 8130cf7 commit 91969cf
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 27 deletions.
9 changes: 5 additions & 4 deletions controllers/fenceagentsremediation_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,11 @@ func (r *FenceAgentsRemediationReconciler) Reconcile(ctx context.Context, req ct
r.Log.Error(err, "failed to get FAR CR")
return emptyResult, err
}
// TODO: Validate FAR CR name to nodeName. Run isNodeNameValid
// Validate FAR CR name to match a nodeName from the cluster
if err := farUtils.CheckNodeName(r.Client, req.Name); err != nil {
return emptyResult, err
}

// Fetch the FAR's pod
r.Log.Info("Fetch FAR's pod")
pod, err := farUtils.GetFenceAgentsRemediationPod(r.Client)
Expand All @@ -99,7 +103,6 @@ func (r *FenceAgentsRemediationReconciler) Reconcile(ctx context.Context, req ct
//TODO: better seperation between errors from wrong shared parameters values and wrong node parameters values
return emptyResult, err
}

return emptyResult, nil
}

Expand Down Expand Up @@ -131,5 +134,3 @@ func appendParamToSlice(fenceAgentParams []string, paramName v1alpha1.ParameterN
}
return fenceAgentParams
}

// TODO: Add isNodeNameValid function which call listNodeNames to validate the FAR's name with the cluster node names
55 changes: 41 additions & 14 deletions controllers/fenceagentsremediation_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ import (
. "github.com/onsi/gomega"

corev1 "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"

"github.com/medik8s/fence-agents-remediation/api/v1alpha1"
farUtils "github.com/medik8s/fence-agents-remediation/pkg/utils"
)

const (
Expand All @@ -47,6 +49,7 @@ var (
var _ = Describe("FAR Controller", func() {
var (
underTestFAR *v1alpha1.FenceAgentsRemediation
node *v1.Node
)

testShareParam := map[v1alpha1.ParameterName]string{
Expand All @@ -69,39 +72,53 @@ var _ = Describe("FAR Controller", func() {
underTestFAR = newFenceAgentsRemediation(validNodeName, fenceAgentIPMI, testShareParam, testNodeParam)

Context("Functionality", func() {
Context("buildFenceAgentParams", func() {
When("FAR's name isn't a node name", func() {
Context("buildFenceAgentParams - check CR name", func() {
When("FAR's name doesn't match a node name", func() {
It("should fail", func() {
underTestFAR.ObjectMeta.Name = dummyNodeName
_, err := buildFenceAgentParams(underTestFAR)
Expect(err).To(HaveOccurred())
})
})
When("FAR's name does match a node name", func() {
It("should succeed", func() {
underTestFAR.ObjectMeta.Name = validNodeName
_, err := buildFenceAgentParams(underTestFAR)
Expect(err).NotTo(HaveOccurred())
})
})
})
When("creating a resource", func() {
It("should fail when FAR pod is missing", func() {
//Test getFenceAgentsPod func

Context("CheckNodeName - check node object", func() {
BeforeEach(func() {
node = getNode(validNodeName)
DeferCleanup(k8sClient.Delete, context.Background(), node)
Expect(k8sClient.Create(context.Background(), node)).To(Succeed())
})
When("FAR's name doesn't match to an existing node name", func() {
It("should fail", func() {
Expect(farUtils.CheckNodeName(k8sClient, dummyNodeName)).To(HaveOccurred())
})
})
When("FAR's name does match to an existing node name", func() {
It("should succeed", func() {
Expect(farUtils.CheckNodeName(k8sClient, validNodeName)).To(Succeed())
})
})
})
})
Context("Reconcile", func() {
//Scenarios

BeforeEach(func() {
fenceAgentsPod = buildFarPod()
// Create fenceAgentsPod and FAR
Expect(k8sClient.Create(context.Background(), fenceAgentsPod)).NotTo(HaveOccurred())
Expect(k8sClient.Create(context.Background(), underTestFAR)).NotTo(HaveOccurred())
})

AfterEach(func() {
Expect(k8sClient.Delete(context.Background(), fenceAgentsPod)).NotTo(HaveOccurred())
Expect(k8sClient.Delete(context.Background(), underTestFAR)).NotTo(HaveOccurred())
node = getNode(validNodeName)
// DeferCleanUp and Create node, fenceAgentsPod and FAR CR
DeferCleanup(k8sClient.Delete, context.Background(), node)
DeferCleanup(k8sClient.Delete, context.Background(), fenceAgentsPod)
DeferCleanup(k8sClient.Delete, context.Background(), underTestFAR)
Expect(k8sClient.Create(context.Background(), node)).To(Succeed())
Expect(k8sClient.Create(context.Background(), fenceAgentsPod)).To(Succeed())
Expect(k8sClient.Create(context.Background(), underTestFAR)).To(Succeed())
})

When("creating FAR CR", func() {
Expand All @@ -127,6 +144,16 @@ func newFenceAgentsRemediation(nodeName string, agent string, sharedparameters m
}
}

// used for making new node object for test and have a unique resourceVersion
// getNode returns a node object with the name nodeName
func getNode(nodeName string) *corev1.Node {
return &corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: nodeName,
},
}
}

// buildFarPod builds a dummy pod with FAR label and namespace
func buildFarPod() *corev1.Pod {
fenceAgentsPod := &corev1.Pod{}
Expand Down
36 changes: 36 additions & 0 deletions pkg/utils/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package utils

import (
"fmt"
"net/http"

apiErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// buildApiError returns new error with updated status
func buildApiError(err error, msg string) error {
var buildError error
switch {
case apiErrors.IsNotFound(err):
// Handle "Not Found" error
buildError = &apiErrors.StatusError{ErrStatus: metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusNotFound,
Message: msg,
Reason: metav1.StatusReasonNotFound,
}}
case apiErrors.IsForbidden(err):
// Handle "Forbidden" error
buildError = &apiErrors.StatusError{ErrStatus: metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusForbidden,
Message: msg,
Reason: metav1.StatusReasonForbidden,
}}
default:
// Handle other error types
buildError = fmt.Errorf("error %w does not match the following http error codes: %d, %d", err, http.StatusNotFound, http.StatusForbidden)
}
return buildError
}
24 changes: 24 additions & 0 deletions pkg/utils/nodes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package utils

import (
"context"
"fmt"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)

// CheckNodeName returns an error if nodeName doesn't match any node name int the cluster, otherwise a nil
func CheckNodeName(r client.Reader, nodeName string) error {
node := &corev1.Node{}
objNodeName := types.NamespacedName{Name: nodeName}
err := r.Get(context.Background(), objNodeName, node)
if err != nil {
// no match between CR name and the cluster nodes
msg := fmt.Sprintf("node %s is invalid", nodeName)
err = buildApiError(err, msg)
return fmt.Errorf("CR's name doesn't match any node name in the cluster - %w", err)
}
return nil
}
12 changes: 3 additions & 9 deletions pkg/utils/pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@ package utils
import (
"context"
"fmt"
"net/http"

corev1 "k8s.io/api/core/v1"
apiErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand All @@ -26,15 +23,12 @@ func GetFenceAgentsRemediationPod(r client.Reader) (*corev1.Pod, error) {
}
err = r.List(context.Background(), pods, &client.ListOptions{LabelSelector: selector, Namespace: podNamespace})
if err != nil {
// Build pod not found error
err = buildApiError(err, "not found a pod that match the label")
return nil, fmt.Errorf("failed fetching FAR pod - %w", err)
}
if len(pods.Items) == 0 {
podNotFoundErr := &apiErrors.StatusError{ErrStatus: metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusNotFound,
Reason: metav1.StatusReasonNotFound,
}}
return nil, fmt.Errorf("no Fence Agent pods were found - %w", podNotFoundErr)
return nil, fmt.Errorf("no Fence Agent pods were found")
}
return &pods.Items[0], nil
}

0 comments on commit 91969cf

Please sign in to comment.