Skip to content
instance.go 6.13 KiB
Newer Older
package reconciler

import (
	"context"
	"sort"

	"github.com/hashicorp/go-version"
	"k8s.libre.sh/application"
	interfaces "k8s.libre.sh/interfaces"
	"k8s.libre.sh/status"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

type InstanceReconciler struct {
	*ReconcilerBase
	Instance  application.Instance
	SyncOrder map[int]string
}

func NewInstanceReconciler(r *ReconcilerBase, i application.Instance, order map[int]string) *InstanceReconciler {
	return &InstanceReconciler{
		ReconcilerBase: r,
		Instance:       i,
		SyncOrder:      order,
	}
}

/* func (er *InstanceReconciler) ManageSuccess(instance apis.Resource) (reconcile.Result, error) {
	if enforcingReconcileStatusAware, updateStatus := (instance).(apis.EnforcingReconcileStatusAware); updateStatus {
		condition := status.Condition{
			Type:               "ReconcileSuccess",
			LastTransitionTime: metav1.Now(),
			Message:            astatus.SuccessfulMessage,
			Reason:             astatus.SuccessfulReason,
			Status:             corev1.ConditionTrue,
		}
		status := apis.EnforcingReconcileStatus{
			Conditions:             status.NewConditions(condition),
			LockedResourceStatuses: er.GetLockedResourceStatuses(instance),
			LockedPatchStatuses:    er.GetLockedPatchStatuses(instance),
		}
		enforcingReconcileStatusAware.SetEnforcingReconcileStatus(status)
		log.V(1).Info("about to modify state for", "instance version", instance.GetResourceVersion())
		err := er.GetClient().Status().Update(context.Background(), instance)
		if err != nil {
			if errors.IsResourceExpired(err) {
				log.Info("unable to update status for", "object version", instance.GetResourceVersion(), "resource version expired, will trigger another reconcile cycle", "")
			} else {
				log.Error(err, "unable to update status for", "object", instance)
			}
			return reconcile.Result{}, err
		}
	} else {
		log.V(1).Info("object is not RecocileStatusAware, not setting status")
	}
	return reconcile.Result{}, nil
}
*/
func (r *InstanceReconciler) ManageError(owner status.ObjectWithStatus) (reconcile.Result, error) {

	return reconcile.Result{}, nil
}

func (r *InstanceReconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) {

Timothee Gosselin's avatar
Timothee Gosselin committed
	appStatus := r.Instance.GetApplicationStatus()
	oldStatus := appStatus.DeepCopy()

	err := application.Init(r.Instance, r.ReconcilerBase)
	if err != nil {
		return reconcile.Result{}, err
	}

Timothee Gosselin's avatar
Timothee Gosselin committed
	// Sync Settings
Timothee Gosselin's avatar
Timothee Gosselin committed
	for name, s := range r.Instance.GetSettings() {

		res, err := SyncComponent(context.TODO(), r.ReconcilerBase, s, r.Instance.GetOwner())
		cptStatus := status.ComponentStatus{
			Resources: res,
		}
		if len(appStatus.Settings) == 0 {
			appStatus.Settings = make(map[string]status.ComponentStatus)
		}
Timothee Gosselin's avatar
Timothee Gosselin committed
		if err != nil {
Timothee Gosselin's avatar
Timothee Gosselin committed
			cptStatus.ComponentsReady = "NotReady"
			appStatus.Settings[name] = cptStatus

Timothee Gosselin's avatar
Timothee Gosselin committed
			return ctrl.Result{}, err
		}
Timothee Gosselin's avatar
Timothee Gosselin committed
		cptStatus.ComponentsReady = "Ready"

		appStatus.Settings[name] = cptStatus
	}

	jobs := r.Instance.GetJobs()

	current, err := version.NewVersion(r.Instance.GetApplicationStatus().Version)
	// get nextcloud desired version in spec
	desired, err := version.NewVersion(r.Instance.GetVersion())

	if len(r.Instance.GetApplicationStatus().Version) == 0 {
		// Sync INSTALL
		res, err := SyncComponent(context.TODO(), r.ReconcilerBase, jobs["install"], r.Instance.GetOwner())
		cptStatus := status.ComponentStatus{
			Resources: res,
		}
		if len(appStatus.Components) == 0 {
			appStatus.Components = make(map[string]status.ComponentStatus)
		}
		if err != nil {
			cptStatus.ComponentsReady = "NotReady"
			appStatus.Components["jobs"] = cptStatus

			return ctrl.Result{}, err
		}
		cptStatus.ComponentsReady = "Ready"

		appStatus.Components["jobs"] = cptStatus

		// ADD manage success

		//		return ctrl.Result{}, nil

		delete(jobs, "upgrade")
	} else {
		switch current.Compare(desired) {
		case 1:
			// current version is greater than desired, no downgrade
			// TODO Format error
			return ctrl.Result{}, err
		case -1:
			// current version lower than desired, upgrade job

			// SYNC UPGRADE
			delete(jobs, "install")
		case 0:
			// current version and desired are the same, normal start
			delete(jobs, "install")
			delete(jobs, "upgrade")
		}
	}

	cpts := make(map[string]application.ComponentMutate)

	for name, j := range jobs {
		cpts[name] = j
	}

Timothee Gosselin's avatar
Timothee Gosselin committed
	// Sync Components
	for name, c := range r.Instance.GetComponents() {
		cpts[name] = c
	}

	sts, err := SyncComponents(context.TODO(), r.ReconcilerBase, cpts, r.Instance.GetOwner(), r.SyncOrder)

	appStatus.Components = sts.Components
	appStatus.SetConditions(sts.Conditions...)
Timothee Gosselin's avatar
Timothee Gosselin committed

	appStatus.Version = desired.String()
	owner := r.Instance.GetOwner()
	owner.SetApplicationStatus(*appStatus)
	// TODO Improve
	if err != nil {
		if err := r.ReconcilerBase.GetClient().Status().Update(context.TODO(), owner); err != nil {
			return ctrl.Result{}, err
		}
		return ctrl.Result{}, err
	}

	if err := r.ReconcilerBase.GetClient().Status().Update(context.TODO(), owner); err != nil {
Timothee Gosselin's avatar
Timothee Gosselin committed
		return ctrl.Result{}, err
	}
	// Clean up resources and update status
	err = CleanUpResources(oldStatus.GetResources(), appStatus.GetResources(), r.ReconcilerBase.GetClient(), r.Instance.GetNamespace())
	if err != nil {
Timothee Gosselin's avatar
Timothee Gosselin committed
		return ctrl.Result{}, err
	}

	return ctrl.Result{}, nil
}

func SyncComponents(ctx context.Context, r interfaces.Reconcile, cpts map[string]application.ComponentMutate, owner interfaces.Object, order map[int]string) (appStatus status.ApplicationStatus, err error) {

	keys := []int{}
	appStatus.Components = make(map[string]status.ComponentStatus)

	for k := range order {
		keys = append(keys, k)
	}

	sort.Ints(keys)

	for _, k := range keys {
		if cpts[order[k]] != nil {
			resources, err := SyncComponent(ctx, r, cpts[order[k]], owner)
			cptStatus := status.ComponentStatus{
				Resources: resources,
			}
			if err != nil {
				// manage error
				cptStatus.ComponentsReady = "NotReady"
				appStatus.Components[order[k]] = cptStatus
				appStatus.SetConditions(status.ReconcileError(err))
				return appStatus, err
			}
			cptStatus.ComponentsReady = "Ready"
			appStatus.Components[order[k]] = cptStatus
	appStatus.SetConditions(status.ReconcileSuccess())
	return appStatus, nil
}