package reconciler import ( "context" "github.com/go-logr/logr" "github.com/pkg/errors" "sigs.k8s.io/controller-runtime/pkg/client" anywherev1 "github.com/aws/eks-anywhere/pkg/api/v1alpha1" "github.com/aws/eks-anywhere/pkg/cluster" "github.com/aws/eks-anywhere/pkg/controller" "github.com/aws/eks-anywhere/pkg/controller/clientutil" "github.com/aws/eks-anywhere/pkg/controller/clusters" "github.com/aws/eks-anywhere/pkg/controller/serverside" "github.com/aws/eks-anywhere/pkg/providers/docker" ) // Reconciler contains dependencies for a docker reconciler. type Reconciler struct { client client.Client cniReconciler CNIReconciler remoteClientRegistry RemoteClientRegistry *serverside.ObjectApplier } // CNIReconciler is an interface for reconciling CNI in the Docker cluster reconciler. type CNIReconciler interface { Reconcile(ctx context.Context, logger logr.Logger, client client.Client, spec *cluster.Spec) (controller.Result, error) } // RemoteClientRegistry is an interface that defines methods for remote clients. type RemoteClientRegistry interface { GetClient(ctx context.Context, cluster client.ObjectKey) (client.Client, error) } // New creates a new Docker provider reconciler. func New(client client.Client, cniReconciler CNIReconciler, remoteClientRegistry RemoteClientRegistry) *Reconciler { return &Reconciler{ client: client, cniReconciler: cniReconciler, remoteClientRegistry: remoteClientRegistry, ObjectApplier: serverside.NewObjectApplier(client), } } // Reconcile brings the cluster to the desired state for the docker provider. func (r *Reconciler) Reconcile(ctx context.Context, log logr.Logger, c *anywherev1.Cluster) (controller.Result, error) { log = log.WithValues("provider", "docker") clusterSpec, err := cluster.BuildSpec(ctx, clientutil.NewKubeClient(r.client), c) if err != nil { return controller.Result{}, err } return controller.NewPhaseRunner[*cluster.Spec]().Register( r.ReconcileControlPlane, r.CheckControlPlaneReady, r.ReconcileCNI, r.ReconcileWorkers, ).Run(ctx, log, clusterSpec) } // CheckControlPlaneReady checks whether the control plane for an eks-a cluster is ready or not. // Requeues with the appropriate wait times whenever the cluster is not ready yet. func (r *Reconciler) CheckControlPlaneReady(ctx context.Context, log logr.Logger, spec *cluster.Spec) (controller.Result, error) { log = log.WithValues("phase", "checkControlPlaneReady") return clusters.CheckControlPlaneReady(ctx, r.client, log, spec.Cluster) } // ReconcileCNI takes the Cilium CNI in a cluster to the desired state defined in a cluster spec. func (r *Reconciler) ReconcileCNI(ctx context.Context, log logr.Logger, clusterSpec *cluster.Spec) (controller.Result, error) { log = log.WithValues("phase", "reconcileCNI") client, err := r.remoteClientRegistry.GetClient(ctx, controller.CapiClusterObjectKey(clusterSpec.Cluster)) if err != nil { return controller.Result{}, err } return r.cniReconciler.Reconcile(ctx, log, client, clusterSpec) } // ReconcileWorkerNodes validates the cluster definition and reconciles the worker nodes // to the desired state. func (r *Reconciler) ReconcileWorkerNodes(ctx context.Context, log logr.Logger, c *anywherev1.Cluster) (controller.Result, error) { log = log.WithValues("provider", "docker", "reconcile type", "workers") clusterSpec, err := cluster.BuildSpec(ctx, clientutil.NewKubeClient(r.client), c) if err != nil { return controller.Result{}, errors.Wrap(err, "building cluster Spec for worker node reconcile") } return controller.NewPhaseRunner[*cluster.Spec]().Register( r.ReconcileWorkers, ).Run(ctx, log, clusterSpec) } // ReconcileWorkers applies the worker CAPI objects to the cluster. func (r *Reconciler) ReconcileWorkers(ctx context.Context, log logr.Logger, spec *cluster.Spec) (controller.Result, error) { log = log.WithValues("phase", "reconcileWorkers") log.Info("Applying worker CAPI objects") w, err := docker.WorkersSpec(ctx, log, clientutil.NewKubeClient(r.client), spec) if err != nil { return controller.Result{}, errors.Wrap(err, "generating workers spec") } return clusters.ReconcileWorkersForEKSA(ctx, log, r.client, spec.Cluster, clusters.ToWorkers(w)) } // ReconcileControlPlane applies the control plane CAPI objects to the cluster. func (r *Reconciler) ReconcileControlPlane(ctx context.Context, log logr.Logger, spec *cluster.Spec) (controller.Result, error) { log = log.WithValues("phase", "reconcileControlPlane") log.Info("Applying control plane CAPI objects") cp, err := docker.ControlPlaneSpec(ctx, log, clientutil.NewKubeClient(r.client), spec) if err != nil { return controller.Result{}, err } return clusters.ReconcileControlPlane(ctx, r.client, &clusters.ControlPlane{ Cluster: cp.Cluster, ProviderCluster: cp.ProviderCluster, KubeadmControlPlane: cp.KubeadmControlPlane, ControlPlaneMachineTemplate: cp.ControlPlaneMachineTemplate, EtcdCluster: cp.EtcdCluster, EtcdMachineTemplate: cp.EtcdMachineTemplate, }) }