From dcd6f8e056a0431db7fd0a40c7a3d4a4549788e8 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 14 Oct 2020 21:29:18 -0700 Subject: [PATCH] master controller --- controllers/master_controller.go | 134 ++++++++++++++++++++++++++++++- 1 file changed, 131 insertions(+), 3 deletions(-) diff --git a/controllers/master_controller.go b/controllers/master_controller.go index 6f9cd52..2492de4 100644 --- a/controllers/master_controller.go +++ b/controllers/master_controller.go @@ -17,7 +17,13 @@ limitations under the License. package controllers import ( + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" "context" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "reflect" "github.com/go-logr/logr" "k8s.io/apimachinery/pkg/runtime" @@ -38,14 +44,136 @@ type MasterReconciler struct { // +kubebuilder:rbac:groups=seaweed.seaweedfs.com,resources=masters/status,verbs=get;update;patch func (r *MasterReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { - _ = context.Background() - _ = r.Log.WithValues("master", req.NamespacedName) + ctx := context.Background() + log := r.Log.WithValues("master", req.NamespacedName) - // your logic here + // Fetch the Master instance + master := &seaweedv1.Master{} + err := r.Get(ctx, req.NamespacedName, master) + if err != nil { + if errors.IsNotFound(err) { + // Request object not found, could have been deleted after reconcile request. + // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. + // Return and don't requeue + log.Info("Master resource not found. Ignoring since object must be deleted") + return ctrl.Result{}, nil + } + // Error reading the object - requeue the request. + log.Error(err, "Failed to get Master") + return ctrl.Result{}, err + } + + // Check if the deployment already exists, if not create a new one + found := &appsv1.Deployment{} + err = r.Get(ctx, types.NamespacedName{Name: master.Name, Namespace: master.Namespace}, found) + if err != nil && errors.IsNotFound(err) { + // Define a new deployment + dep := r.deploymentForMaster(master) + log.Info("Creating a new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name) + err = r.Create(ctx, dep) + if err != nil { + log.Error(err, "Failed to create new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name) + return ctrl.Result{}, err + } + // Deployment created successfully - return and requeue + return ctrl.Result{Requeue: true}, nil + } else if err != nil { + log.Error(err, "Failed to get Deployment") + return ctrl.Result{}, err + } + + // Ensure the deployment size is the same as the spec + size := master.Spec.Size + if *found.Spec.Replicas != size { + found.Spec.Replicas = &size + err = r.Update(ctx, found) + if err != nil { + log.Error(err, "Failed to update Deployment", "Deployment.Namespace", found.Namespace, "Deployment.Name", found.Name) + return ctrl.Result{}, err + } + // Spec updated - return and requeue + return ctrl.Result{Requeue: true}, nil + } + + // Update the Master status with the pod names + // List the pods for this master's deployment + podList := &corev1.PodList{} + listOpts := []client.ListOption{ + client.InNamespace(master.Namespace), + client.MatchingLabels(labelsForMaster(master.Name)), + } + if err = r.List(ctx, podList, listOpts...); err != nil { + log.Error(err, "Failed to list pods", "Master.Namespace", master.Namespace, "Master.Name", master.Name) + return ctrl.Result{}, err + } + podNames := getPodNames(podList.Items) + + // Update status.Nodes if needed + if !reflect.DeepEqual(podNames, master.Status.Nodes) { + master.Status.Nodes = podNames + err := r.Status().Update(ctx, master) + if err != nil { + log.Error(err, "Failed to update Master status") + return ctrl.Result{}, err + } + } return ctrl.Result{}, nil } +// deploymentForMaster returns a master Deployment object +func (r *MasterReconciler) deploymentForMaster(m *seaweedv1.Master) *appsv1.Deployment { + ls := labelsForMaster(m.Name) + replicas := m.Spec.Size + + dep := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: m.Name, + Namespace: m.Namespace, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: ls, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: ls, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Image: "Master:1.4.36-alpine", + Name: "Master", + Command: []string{"Master", "-m=64", "-o", "modern", "-v"}, + Ports: []corev1.ContainerPort{{ + ContainerPort: 11211, + Name: "Master", + }}, + }}, + }, + }, + }, + } + // Set Master instance as the owner and controller + ctrl.SetControllerReference(m, dep, r.Scheme) + return dep +} + +// labelsForMaster returns the labels for selecting the resources +// belonging to the given Master CR name. +func labelsForMaster(name string) map[string]string { + return map[string]string{"app": "Master", "Master_cr": name} +} + +// getPodNames returns the pod names of the array of pods passed in +func getPodNames(pods []corev1.Pod) []string { + var podNames []string + for _, pod := range pods { + podNames = append(podNames, pod.Name) + } + return podNames +} + func (r *MasterReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&seaweedv1.Master{}).