github.com/michaelhenkel/operator-sdk@v0.8.1/test/test-framework/pkg/controller/memcachedrs/memcachedrs_controller.go (about) 1 // Copyright 2019 The Operator-SDK Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package memcachedrs 16 17 import ( 18 "context" 19 "reflect" 20 21 cachev1alpha1 "github.com/operator-framework/operator-sdk/test/test-framework/pkg/apis/cache/v1alpha1" 22 23 appsv1 "k8s.io/api/apps/v1" 24 corev1 "k8s.io/api/core/v1" 25 "k8s.io/apimachinery/pkg/api/errors" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/labels" 28 "k8s.io/apimachinery/pkg/runtime" 29 "k8s.io/apimachinery/pkg/types" 30 "sigs.k8s.io/controller-runtime/pkg/client" 31 "sigs.k8s.io/controller-runtime/pkg/controller" 32 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 33 "sigs.k8s.io/controller-runtime/pkg/handler" 34 "sigs.k8s.io/controller-runtime/pkg/manager" 35 "sigs.k8s.io/controller-runtime/pkg/reconcile" 36 logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" 37 "sigs.k8s.io/controller-runtime/pkg/source" 38 ) 39 40 var log = logf.Log.WithName("controller_memcachedrs") 41 42 /** 43 * USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller 44 * business logic. Delete these comments after modifying this file.* 45 */ 46 47 // Add creates a new MemcachedRS Controller and adds it to the Manager. The Manager will set fields on the Controller 48 // and Start it when the Manager is Started. 49 func Add(mgr manager.Manager) error { 50 return add(mgr, newReconciler(mgr)) 51 } 52 53 // newReconciler returns a new reconcile.Reconciler 54 func newReconciler(mgr manager.Manager) reconcile.Reconciler { 55 return &ReconcileMemcachedRS{client: mgr.GetClient(), scheme: mgr.GetScheme()} 56 } 57 58 // add adds a new Controller to mgr with r as the reconcile.Reconciler 59 func add(mgr manager.Manager, r reconcile.Reconciler) error { 60 // Create a new controller 61 c, err := controller.New("memcachedrs-controller", mgr, controller.Options{Reconciler: r}) 62 if err != nil { 63 return err 64 } 65 66 // Watch for changes to primary resource MemcachedRS 67 err = c.Watch(&source.Kind{Type: &cachev1alpha1.MemcachedRS{}}, &handler.EnqueueRequestForObject{}) 68 if err != nil { 69 return err 70 } 71 72 // TODO(user): Modify this to be the types you create that are owned by the primary resource 73 // Watch for changes to secondary resource Pods and requeue the owner MemcachedRS 74 err = c.Watch(&source.Kind{Type: &appsv1.ReplicaSet{}}, &handler.EnqueueRequestForOwner{ 75 IsController: true, 76 OwnerType: &cachev1alpha1.MemcachedRS{}, 77 }) 78 if err != nil { 79 return err 80 } 81 82 return nil 83 } 84 85 // blank assignment to verify that ReconcileMemcachedRS implements reconcile.Reconciler 86 var _ reconcile.Reconciler = &ReconcileMemcachedRS{} 87 88 // ReconcileMemcachedRS reconciles a MemcachedRS object 89 type ReconcileMemcachedRS struct { 90 // This client, initialized using mgr.Client() above, is a split client 91 // that reads objects from the cache and writes to the apiserver 92 client client.Client 93 scheme *runtime.Scheme 94 } 95 96 // Reconcile reads that state of the cluster for a MemcachedRS object and makes changes based on the state read 97 // and what is in the MemcachedRS.Spec 98 // TODO(user): Modify this Reconcile function to implement your Controller logic. This example creates 99 // a Pod as an example 100 // Note: 101 // The Controller will requeue the Request to be processed again if the returned error is non-nil or 102 // Result.Requeue is true, otherwise upon completion it will remove the work from the queue. 103 func (r *ReconcileMemcachedRS) Reconcile(request reconcile.Request) (reconcile.Result, error) { 104 reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) 105 reqLogger.Info("Reconciling MemcachedRS") 106 107 // Fetch the MemcachedRS instance 108 memcachedrs := &cachev1alpha1.MemcachedRS{} 109 err := r.client.Get(context.TODO(), request.NamespacedName, memcachedrs) 110 if err != nil { 111 if errors.IsNotFound(err) { 112 // Request object not found, could have been deleted after reconcile request. 113 // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. 114 // Return and don't requeue 115 return reconcile.Result{}, nil 116 } 117 // Error reading the object - requeue the request. 118 return reconcile.Result{}, err 119 } 120 121 // Check if the replicaSet already exists, if not create a new one 122 found := &appsv1.ReplicaSet{} 123 err = r.client.Get(context.TODO(), types.NamespacedName{Name: memcachedrs.Name, Namespace: memcachedrs.Namespace}, found) 124 if err != nil && errors.IsNotFound(err) { 125 // Define a new replicaSet 126 dep := r.replicaSetForMemcached(memcachedrs) 127 reqLogger.Info("Creating a new ReplicaSet", "ReplicaSet.Namespace", dep.Namespace, "ReplicaSet.Name", dep.Name) 128 err = r.client.Create(context.TODO(), dep) 129 if err != nil { 130 reqLogger.Error(err, "Failed to create new ReplicaSet", "ReplicaSet.Namespace", dep.Namespace, "ReplicaSet.Name", dep.Name) 131 return reconcile.Result{}, err 132 } 133 // ReplicaSet created successfully - return and requeue 134 return reconcile.Result{Requeue: true}, nil 135 } else if err != nil { 136 reqLogger.Error(err, "Failed to get ReplicaSet") 137 return reconcile.Result{}, err 138 } 139 140 // Ensure the replicaSet size is the same as the spec 141 size := memcachedrs.Spec.NumNodes 142 if *found.Spec.Replicas != size { 143 found.Spec.Replicas = &size 144 err = r.client.Update(context.TODO(), found) 145 if err != nil { 146 reqLogger.Error(err, "Failed to update ReplicaSet", "ReplicaSet.Namespace", found.Namespace, "ReplicaSet.Name", found.Name) 147 return reconcile.Result{}, err 148 } 149 // Spec updated - return and requeue 150 return reconcile.Result{Requeue: true}, nil 151 } 152 153 // Update the Memcached status with the pod names 154 // List the pods for this memcached's replicaSet 155 podList := &corev1.PodList{} 156 labelSelector := labels.SelectorFromSet(labelsForMemcached(memcachedrs.Name)) 157 listOps := &client.ListOptions{Namespace: memcachedrs.Namespace, LabelSelector: labelSelector} 158 err = r.client.List(context.TODO(), listOps, podList) 159 if err != nil { 160 reqLogger.Error(err, "Failed to list pods", "Memcached.Namespace", memcachedrs.Namespace, "Memcached.Name", memcachedrs.Name) 161 return reconcile.Result{}, err 162 } 163 podNames := getPodNames(podList.Items) 164 165 // Update status.Nodes if needed 166 if !reflect.DeepEqual(podNames, memcachedrs.Status.NodeList) { 167 memcachedrs.Status.NodeList = podNames 168 err := r.client.Status().Update(context.TODO(), memcachedrs) 169 if err != nil { 170 reqLogger.Error(err, "Failed to update Memcached status") 171 return reconcile.Result{}, err 172 } 173 } 174 175 // Switch testing bool 176 if memcachedrs.Status.Test { 177 memcachedrs.Status.Test = false 178 } else { 179 memcachedrs.Status.Test = true 180 } 181 err = r.client.Status().Update(context.TODO(), memcachedrs) 182 if err != nil { 183 reqLogger.Error(err, "Failed to update Memcached status") 184 return reconcile.Result{}, err 185 } 186 187 return reconcile.Result{}, nil 188 } 189 190 // rsForMemcached returns a memcached ReplicaSet object 191 func (r *ReconcileMemcachedRS) replicaSetForMemcached(m *cachev1alpha1.MemcachedRS) *appsv1.ReplicaSet { 192 ls := labelsForMemcached(m.Name) 193 replicas := m.Spec.NumNodes 194 195 replicaSet := &appsv1.ReplicaSet{ 196 TypeMeta: metav1.TypeMeta{ 197 APIVersion: "apps/v1", 198 Kind: "ReplicaSet", 199 }, 200 ObjectMeta: metav1.ObjectMeta{ 201 Name: m.Name, 202 Namespace: m.Namespace, 203 }, 204 Spec: appsv1.ReplicaSetSpec{ 205 Replicas: &replicas, 206 Selector: &metav1.LabelSelector{ 207 MatchLabels: ls, 208 }, 209 Template: corev1.PodTemplateSpec{ 210 ObjectMeta: metav1.ObjectMeta{ 211 Labels: ls, 212 }, 213 Spec: corev1.PodSpec{ 214 Containers: []corev1.Container{{ 215 Image: "memcached:1.4.36-alpine", 216 Name: "memcached", 217 Command: []string{"memcached", "-m=64", "-o", "modern", "-v"}, 218 Ports: []corev1.ContainerPort{{ 219 ContainerPort: 11211, 220 Name: "memcached", 221 }}, 222 }}, 223 }, 224 }, 225 }, 226 } 227 // Set Memcached instance as the owner and controller 228 controllerutil.SetControllerReference(m, replicaSet, r.scheme) 229 return replicaSet 230 } 231 232 // labelsForMemcached returns the labels for selecting the resources 233 // belonging to the given memcached CR name. 234 func labelsForMemcached(name string) map[string]string { 235 return map[string]string{"app": "memcached-rs", "memcached_cr": name} 236 } 237 238 // getPodNames returns the pod names of the array of pods passed in 239 func getPodNames(pods []corev1.Pod) []string { 240 var podNames []string 241 for _, pod := range pods { 242 podNames = append(podNames, pod.Name) 243 } 244 return podNames 245 }