Skip to content

Commit

Permalink
Update Reconcile & add better logs
Browse files Browse the repository at this point in the history
Due to CRDs changes the reconcile must be updated
  • Loading branch information
razo7 committed Feb 21, 2023
1 parent 51ee429 commit dabc652
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 68 deletions.
118 changes: 63 additions & 55 deletions controllers/fenceagentsremediation_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,15 @@ limitations under the License.

package controllers

//TODO mshitrit make sure fence agents and other necessary executables are installed in the pod

import (
"context"
"errors"
"fmt"
"net/http"

"github.com/go-logr/logr"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
apiErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -37,11 +35,6 @@ import (
"github.com/medik8s/fence-agents-remediation/pkg/cli"
)

const (
//TODO mshitrit verify that template is created with this name
fenceAgentsTemplateName = "fenceagentsremediationtemplate-default"
)

var (
faPodLabels = map[string]string{"app": "fence-agents-remediation-operator"}
)
Expand All @@ -53,6 +46,13 @@ type FenceAgentsRemediationReconciler struct {
Scheme *runtime.Scheme
}

// SetupWithManager sets up the controller with the Manager.
func (r *FenceAgentsRemediationReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&v1alpha1.FenceAgentsRemediation{}).
Complete(r)
}

//+kubebuilder:rbac:groups=core,resources=pods/exec,verbs=create
//+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;update;delete;deletecollection
//+kubebuilder:rbac:groups=fence-agents-remediation.medik8s.io,resources=fenceagentsremediations,verbs=get;list;watch;create;update;patch;delete
Expand All @@ -69,69 +69,54 @@ type FenceAgentsRemediationReconciler struct {
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile
func (r *FenceAgentsRemediationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
r.Log.Info("started reconcile")
defer r.Log.Info("finished reconcile")
r.Log.Info("Begin FenceAgentsRemediation Reconcile")
defer r.Log.Info("Finish FenceAgentsRemediation Reconcile")
emptyResult := ctrl.Result{}

// Fetch the FenceAgentsRemediation instance
far := &v1alpha1.FenceAgentsRemediation{}
if err := r.Get(ctx, req.NamespacedName, far); err != nil {
if apiErrors.IsNotFound(err) {
// FAR is deleted, stop reconciling
r.Log.Info("Fence Agents Remediation not found, nothing to do")
return ctrl.Result{}, nil
r.Log.Info("FAR CR is deleted - nothing to do", "CR Name", req.Name, "CR Namespace", req.Namespace)
return emptyResult, nil
}
r.Log.Error(err, "failed to get FAR")
return ctrl.Result{}, err
r.Log.Error(err, "failed to get FAR CR")
return emptyResult, err
}
key := client.ObjectKey{Namespace: req.Namespace, Name: fenceAgentsTemplateName}
farTemplate := &v1alpha1.FenceAgentsRemediationTemplate{}
if err := r.Get(ctx, key, farTemplate); err != nil {
r.Log.Error(err, "failed to get FAR template")
return ctrl.Result{}, err
}

pod, err := r.getFAPod(req.NamespacedName.Namespace)
// TODO: Validate FAR CR name to nodeName. Run isNodeNameValid
// Fetch the FAR's pod
r.Log.Info("Fetch FAR's pod")
pod, err := r.getFenceAgentsPod(req.Namespace)
if err != nil {
return ctrl.Result{}, err
return emptyResult, err
}

// Build CLI executer for FAR's pod
r.Log.Info("Build CLI executer for FAR's pod")
ex, err := cli.NewExecuter(pod)
if err != nil {
return ctrl.Result{}, err
}

faParams := buildFenceAgentParams(farTemplate, far)
cmd := append([]string{farTemplate.Spec.Agent}, faParams...)
//fence_ipmilan --ip=192.168.111.1 --ipport=6233 --username=admin --password=password --action=status --lanplus --verbose
if _, _, err := ex.Execute(cmd); err != nil {
return ctrl.Result{}, err
return emptyResult, err
}

return ctrl.Result{}, nil
}

func buildFenceAgentParams(farTemplate *v1alpha1.FenceAgentsRemediationTemplate, far *v1alpha1.FenceAgentsRemediation) []string {
var fenceAgentParams []string
for paramName, paramVal := range farTemplate.Spec.SharedParameters {
fenceAgentParams = appendParamToSlice(fenceAgentParams, string(paramName), paramVal)

//TODO: Check that FA is excutable? run cli.IsExecuteable
r.Log.Info("Create and execute the fence agent", "Fence Agent", far.Spec.Agent)
faParams, err := buildFenceAgentParams(far)
if err != nil {
return emptyResult, err
}

nodeName := v1alpha1.NodeName(far.Name)
for paramName, nodeMap := range farTemplate.Spec.NodeParameters {
fenceAgentParams = appendParamToSlice(fenceAgentParams, string(paramName), nodeMap[nodeName])
cmd := append([]string{far.Spec.Agent}, faParams...)
// The Fence Agent is excutable and the parameters are valid but we don't know about their values
if _, _, err := ex.Execute(cmd); err != nil {
//TODO: better seperation between errors from wrong shared parameters values and wrong node parameters values
return emptyResult, err
}

return fenceAgentParams
}

// SetupWithManager sets up the controller with the Manager.
func (r *FenceAgentsRemediationReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&v1alpha1.FenceAgentsRemediation{}).
Complete(r)
return emptyResult, nil
}

func (r *FenceAgentsRemediationReconciler) getFAPod(namespace string) (*corev1.Pod, error) {
// getFenceAgentsPod fetches the FAR pod based on FAR's label and namespace
func (r *FenceAgentsRemediationReconciler) getFenceAgentsPod(namespace string) (*corev1.Pod, error) {

pods := new(corev1.PodList)

Expand All @@ -143,11 +128,12 @@ func (r *FenceAgentsRemediationReconciler) getFAPod(namespace string) (*corev1.P
}
if err := r.Client.List(context.Background(), pods, &options); err != nil {
r.Log.Error(err, "failed fetching Fence Agent layer pod")
// err := errors.New("failed fetching Fence Agent layer pod")
return nil, err
}
if len(pods.Items) == 0 {
r.Log.Info("No Fence Agent pods were found")
podNotFoundErr := &errors.StatusError{ErrStatus: metav1.Status{
podNotFoundErr := &apiErrors.StatusError{ErrStatus: metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusNotFound,
Reason: metav1.StatusReasonNotFound,
Expand All @@ -158,11 +144,33 @@ func (r *FenceAgentsRemediationReconciler) getFAPod(namespace string) (*corev1.P

}

func appendParamToSlice(fenceAgentParams []string, paramName string, paramVal string) []string {
// buildFenceAgentParams collects the FAR's parameters for the node based on FAR CR
func buildFenceAgentParams(far *v1alpha1.FenceAgentsRemediation) ([]string, error) {
var fenceAgentParams []string
for paramName, paramVal := range far.Spec.SharedParameters {
fenceAgentParams = appendParamToSlice(fenceAgentParams, paramName, paramVal)
}

nodeName := v1alpha1.NodeName(far.Name)
for paramName, nodeMap := range far.Spec.NodeParameters {
if nodeVal, isFound := nodeMap[nodeName]; isFound {
fenceAgentParams = appendParamToSlice(fenceAgentParams, paramName, nodeVal)
} else {
err := errors.New("node parameter is required, and cannot be empty")
return nil, err
}
}
return fenceAgentParams, nil
}

// appendParamToSlice appends parameters in a key-value manner, when value can be empty
func appendParamToSlice(fenceAgentParams []string, paramName v1alpha1.ParameterName, paramVal string) []string {
if paramVal != "" {
fenceAgentParams = append(fenceAgentParams, fmt.Sprintf("%s=%s", paramName, paramVal))
} else {
fenceAgentParams = append(fenceAgentParams, paramName)
fenceAgentParams = append(fenceAgentParams, string(paramName))
}
return fenceAgentParams
}

// TODO: Add isNodeNameValid function which call listNodeNames to validate the FAR's name with the cluster node names
37 changes: 24 additions & 13 deletions pkg/cli/cliexecuter.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,18 @@ import (
)

type Executer interface {
Execute(command []string) (stdout, stderr string, err error)
Execute(command []string) (stdout string, stderr string, err error)
}

type executer struct {
Log logr.Logger
kClient *kubernetes.Clientset
k8sClientConfig *restclient.Config
containerName string
pod *corev1.Pod
}

// NewExecuter verifies that the pod has running containers and it builds the executer struct
func NewExecuter(pod *corev1.Pod) (Executer, error) {
logger := ctrl.Log.WithName("controllers").WithName("Executer")
if len(pod.Spec.Containers) == 0 {
Expand All @@ -37,16 +46,9 @@ func NewExecuter(pod *corev1.Pod) (Executer, error) {
return &ce, nil
}

type executer struct {
Log logr.Logger
kClient *kubernetes.Clientset
k8sClientConfig *restclient.Config
containerName string
pod *corev1.Pod
}

// buildK8sClient reutrn nil when it successfuly built a Kubernetes client for CLI executer, otherwise an error
func (e *executer) buildK8sClient() error {
//client was already built stop here
//client was already built, then stop here
if e.kClient != nil {
return nil
}
Expand All @@ -67,7 +69,13 @@ func (e *executer) buildK8sClient() error {
return nil
}

func (e *executer) Execute(command []string) (stdout, stderr string, err error) {
// // IsExecuteable checks whether the Fence Agent is executeable
// func (e *executer) IsExecuteable(command []string) (stdout, stderr string, err error) {

// }

// Execute builds and runs a Post request on contianer for SPDY (shell) executor
func (e *executer) Execute(command []string) (stdout string, stderr string, err error) {
if err := e.buildK8sClient(); err != nil {
return "", "", err
}
Expand All @@ -84,6 +92,7 @@ func (e *executer) Execute(command []string) (stdout, stderr string, err error)
SubResource("exec").
Param("container", e.containerName)

// Build the Post request for SPDY (shell) executor
req.VersionedParams(&corev1.PodExecOptions{
Container: e.containerName,
Command: command,
Expand All @@ -92,6 +101,8 @@ func (e *executer) Execute(command []string) (stdout, stderr string, err error)
Stderr: true,
TTY: false,
}, scheme.ParameterCodec)

// Execute the Post request for SPDY (shell) executor
execSPDY, err := remotecommand.NewSPDYExecutor(e.k8sClientConfig, "POST", req.URL())
if err != nil {
e.Log.Error(err, "failed building SPDY (shell) executor")
Expand All @@ -104,8 +115,8 @@ func (e *executer) Execute(command []string) (stdout, stderr string, err error)
})
if err != nil {
e.Log.Error(err, "Failed to run exec command", "command", command, "stdout", stdoutBuf.String(), "stderr", stderrBuf.String())
} else {
e.Log.Info("Command has been executed successfully", "command", command, "standard output", stdoutBuf.String())
}

e.Log.Info("finished executing command successfully", "command", command, "standard output", stdoutBuf.String())
return stdoutBuf.String(), stderrBuf.String(), err
}

0 comments on commit dabc652

Please sign in to comment.