Skip to content

Commit

Permalink
Added checking for MachineDeployments availability to update ManagedC…
Browse files Browse the repository at this point in the history
…luster status
  • Loading branch information
Slava Lysunkin committed Oct 29, 2024
1 parent b6172e6 commit a6c7920
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 37 deletions.
117 changes: 80 additions & 37 deletions internal/controller/managedcluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
fluxmeta "github.com/fluxcd/pkg/apis/meta"
fluxconditions "github.com/fluxcd/pkg/runtime/conditions"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
"github.com/hashicorp/go-multierror"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -73,6 +74,11 @@ type ManagedClusterReconciler struct {
SystemNamespace string
}

type objectsToCheck struct {
gvr schema.GroupVersionResource
requeueIfNoObjects bool
}

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
func (r *ManagedClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
Expand Down Expand Up @@ -110,60 +116,61 @@ func (r *ManagedClusterReconciler) Reconcile(ctx context.Context, req ctrl.Reque
return r.Update(ctx, managedCluster)
}

func (r *ManagedClusterReconciler) setStatusFromClusterStatus(ctx context.Context, managedCluster *hmc.ManagedCluster) (requeue bool, _ error) {
func (r *ManagedClusterReconciler) setStatusFromChildObjects(ctx context.Context, managedCluster *hmc.ManagedCluster, resourceID schema.GroupVersionResource, requeueIfNoObjects bool) (requeue bool, _ error) {
l := ctrl.LoggerFrom(ctx)

resourceID := schema.GroupVersionResource{
Group: "cluster.x-k8s.io",
Version: "v1beta1",
Resource: "clusters",
}

list, err := r.DynamicClient.Resource(resourceID).Namespace(managedCluster.Namespace).List(ctx, metav1.ListOptions{
LabelSelector: labels.SelectorFromSet(map[string]string{hmc.FluxHelmChartNameKey: managedCluster.Name}).String(),
})

if apierrors.IsNotFound(err) || len(list.Items) == 0 {
l.Info("Clusters not found, ignoring since object must be deleted or not yet created")
return true, nil
message := resourceID.Resource + " not found, ignoring since object must be deleted or not yet created"
if requeueIfNoObjects {
l.Info(message)
return true, nil
}
return false, errors.New(message)
}

if err != nil {
return true, fmt.Errorf("failed to get cluster information for managedCluster %s in namespace: %s: %w",
managedCluster.Namespace, managedCluster.Name, err)
}
conditions, found, err := unstructured.NestedSlice(list.Items[0].Object, "status", "conditions")
if err != nil {
return true, fmt.Errorf("failed to get cluster information for managedCluster %s in namespace: %s: %w",
managedCluster.Namespace, managedCluster.Name, err)
}
if !found {
return true, fmt.Errorf("failed to get cluster information for managedCluster %s in namespace: %s: status.conditions not found",
managedCluster.Namespace, managedCluster.Name)
return true, fmt.Errorf("failed to get %s information for managedCluster %s in namespace: %s: %w",
resourceID.Resource, managedCluster.Namespace, managedCluster.Name, err)
}

allConditionsComplete := true
for _, condition := range conditions {
conditionMap, ok := condition.(map[string]any)
if !ok {
return true, fmt.Errorf("failed to cast condition to map[string]any for managedCluster: %s in namespace: %s: %w",
managedCluster.Namespace, managedCluster.Name, err)
for _, item := range list.Items {
conditions, found, err := unstructured.NestedSlice(item.Object, "status", "conditions")
if err != nil {
return true, fmt.Errorf("failed to get %s information for managedCluster %s in namespace: %s: %w",
resourceID.Resource, managedCluster.Namespace, managedCluster.Name, err)
}

var metaCondition metav1.Condition
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(conditionMap, &metaCondition); err != nil {
return true, fmt.Errorf("failed to convert unstructured conditions to metav1.Condition for managedCluster %s in namespace: %s: %w",
managedCluster.Namespace, managedCluster.Name, err)
if !found {
return true, fmt.Errorf("failed to get %s information for managedCluster %s in namespace: %s: status.conditions not found",
resourceID.Resource, managedCluster.Namespace, managedCluster.Name)
}

if metaCondition.Status != "True" {
allConditionsComplete = false
}
for _, condition := range conditions {
conditionMap, ok := condition.(map[string]any)
if !ok {
return true, fmt.Errorf("failed to cast condition to map[string]any for managedCluster: %s in namespace: %s: %w",
managedCluster.Namespace, managedCluster.Name, err)
}

var metaCondition metav1.Condition
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(conditionMap, &metaCondition); err != nil {
return true, fmt.Errorf("failed to convert unstructured conditions to metav1.Condition for managedCluster %s in namespace: %s: %w",
managedCluster.Namespace, managedCluster.Name, err)
}

if metaCondition.Reason == "" && metaCondition.Status == "True" {
metaCondition.Reason = "Succeeded"
if metaCondition.Status != "True" {
allConditionsComplete = false
}

if metaCondition.Reason == "" && metaCondition.Status == "True" {
metaCondition.Reason = "Succeeded"
}
apimeta.SetStatusCondition(managedCluster.GetConditions(), metaCondition)
}
apimeta.SetStatusCondition(managedCluster.GetConditions(), metaCondition)
}

return !allConditionsComplete, nil
Expand Down Expand Up @@ -340,7 +347,7 @@ func (r *ManagedClusterReconciler) Update(ctx context.Context, managedCluster *h
})
}

requeue, err := r.setStatusFromClusterStatus(ctx, managedCluster)
requeue, err := r.needToRequeue(ctx, managedCluster)
if err != nil {
if requeue {
return ctrl.Result{RequeueAfter: DefaultRequeueInterval}, err
Expand Down Expand Up @@ -368,6 +375,42 @@ func (r *ManagedClusterReconciler) Update(ctx context.Context, managedCluster *h
return ctrl.Result{}, nil
}

func (r *ManagedClusterReconciler) needToRequeue(ctx context.Context, managedCluster *hmc.ManagedCluster) (bool, error) {
childObjects := []objectsToCheck{
{
gvr: schema.GroupVersionResource{
Group: "cluster.x-k8s.io",
Version: "v1beta1",
Resource: "clusters",
},
requeueIfNoObjects: true,
},
{
gvr: schema.GroupVersionResource{
Group: "cluster.x-k8s.io",
Version: "v1beta1",
Resource: "machinedeployments",
},
requeueIfNoObjects: false,
},
}

var requeue bool
var errs error
for _, obj := range childObjects {
r, err := r.setStatusFromChildObjects(ctx, managedCluster, obj.gvr, obj.requeueIfNoObjects)
if err != nil {
errs = multierror.Append(errs, err)
}

if r {
requeue = true
}
}

return requeue, errs
}

// updateServices reconciles services provided in ManagedCluster.Spec.Services.
// TODO(https://github.com/Mirantis/hmc/issues/361): Set status to ManagedCluster object at appropriate places.
func (r *ManagedClusterReconciler) updateServices(ctx context.Context, mc *hmc.ManagedCluster) (ctrl.Result, error) {
Expand Down
5 changes: 5 additions & 0 deletions templates/provider/hmc/templates/rbac/controller/roles.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ rules:
resources:
- clusters
verbs: {{ include "rbac.viewerVerbs" . | nindent 4 }}
- apiGroups:
- cluster.x-k8s.io
resources:
- machinedeployments
verbs: {{ include "rbac.viewerVerbs" . | nindent 4 }}
- apiGroups:
- helm.toolkit.fluxcd.io
resources:
Expand Down

0 comments on commit a6c7920

Please sign in to comment.