7 "github.com/go-logr/logr"
9 onapv1alpha1 "demo/vnfs/DAaaS/microservices/collectd-operator/pkg/apis/onap/v1alpha1"
11 corev1 "k8s.io/api/core/v1"
12 extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
13 "k8s.io/apimachinery/pkg/api/errors"
14 "k8s.io/apimachinery/pkg/runtime"
15 "sigs.k8s.io/controller-runtime/pkg/client"
16 "sigs.k8s.io/controller-runtime/pkg/controller"
17 "sigs.k8s.io/controller-runtime/pkg/handler"
18 "sigs.k8s.io/controller-runtime/pkg/manager"
19 "sigs.k8s.io/controller-runtime/pkg/reconcile"
20 logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
21 "sigs.k8s.io/controller-runtime/pkg/source"
24 var log = logf.Log.WithName("controller_collectdplugin")
26 // ResourceMap to hold objects to update/reload
27 type ResourceMap struct {
28 configMap *corev1.ConfigMap
29 daemonSet *extensionsv1beta1.DaemonSet
30 collectdPlugins *[]onapv1alpha1.CollectdPlugin
34 * USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller
35 * business logic. Delete these comments after modifying this file.*
38 // Add creates a new CollectdPlugin Controller and adds it to the Manager. The Manager will set fields on the Controller
39 // and Start it when the Manager is Started.
40 func Add(mgr manager.Manager) error {
41 return add(mgr, newReconciler(mgr))
44 // newReconciler returns a new reconcile.Reconciler
45 func newReconciler(mgr manager.Manager) reconcile.Reconciler {
46 return &ReconcileCollectdPlugin{client: mgr.GetClient(), scheme: mgr.GetScheme()}
49 // add adds a new Controller to mgr with r as the reconcile.Reconciler
50 func add(mgr manager.Manager, r reconcile.Reconciler) error {
51 // Create a new controller
52 log.V(1).Info("Creating a new controller for CollectdPlugin")
53 c, err := controller.New("collectdplugin-controller", mgr, controller.Options{Reconciler: r})
58 // Watch for changes to primary resource CollectdPlugin
59 log.V(1).Info("Add watcher for primary resource CollectdPlugin")
60 err = c.Watch(&source.Kind{Type: &onapv1alpha1.CollectdPlugin{}}, &handler.EnqueueRequestForObject{})
68 // blank assignment to verify that ReconcileCollectdPlugin implements reconcile.Reconciler
69 var _ reconcile.Reconciler = &ReconcileCollectdPlugin{}
71 // ReconcileCollectdPlugin reconciles a CollectdPlugin object
72 type ReconcileCollectdPlugin struct {
73 // This client, initialized using mgr.Client() above, is a split client
74 // that reads objects from the cache and writes to the apiserver
76 scheme *runtime.Scheme
79 // Define the collectdPlugin finalizer for handling deletion
80 const collectdPluginFinalizer = "finalizer.collectdplugin.onap.org"
82 // Reconcile reads that state of the cluster for a CollectdPlugin object and makes changes based on the state read
83 // and what is in the CollectdPlugin.Spec
84 // TODO(user): Modify this Reconcile function to implement your Controller logic. This example creates
85 // a Pod as an example
87 // The Controller will requeue the Request to be processed again if the returned error is non-nil or
88 // Result.Requeue is true, otherwise upon completion it will remove the work from the queue.
89 func (r *ReconcileCollectdPlugin) Reconcile(request reconcile.Request) (reconcile.Result, error) {
90 reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
91 reqLogger.Info("Reconciling CollectdPlugin")
93 // Fetch the CollectdPlugin instance
94 instance := &onapv1alpha1.CollectdPlugin{}
95 err := r.client.Get(context.TODO(), request.NamespacedName, instance)
97 if errors.IsNotFound(err) {
98 // Request object not found, could have been deleted after reconcile request.
99 // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
100 // Return and don't requeue
101 reqLogger.V(1).Info("CollectdPlugin object Not found")
102 return reconcile.Result{}, nil
104 // Error reading the object - requeue the request.
105 reqLogger.V(1).Info("Error reading the CollectdPlugin object, Requeuing")
106 return reconcile.Result{}, err
109 // Handle Delete CR for additional cleanup
110 isDelete, err := r.handleDelete(reqLogger, instance)
112 return reconcile.Result{}, err
115 // Add finalizer for this CR
116 if !contains(instance.GetFinalizers(), collectdPluginFinalizer) {
117 if err := r.addFinalizer(reqLogger, instance); err != nil {
118 return reconcile.Result{}, err
121 err = r.handleCollectdPlugin(reqLogger, instance)
122 return reconcile.Result{}, err
125 // handleCollectdPlugin regenerates the collectd conf on CR Create, Update, Delete events
126 func (r *ReconcileCollectdPlugin) handleCollectdPlugin(reqLogger logr.Logger, cr *onapv1alpha1.CollectdPlugin) error {
128 rmap, err := r.findResourceMapForCR(cr)
130 reqLogger.Error(err, "Skip reconcile: Resources not found")
136 collectPlugins := rmap.collectdPlugins
137 reqLogger.V(1).Info("Found ResourceMap")
138 reqLogger.V(1).Info(":::: ConfigMap Info ::::", "ConfigMap.Namespace", cm.Namespace, "ConfigMap.Name", cm.Name)
139 reqLogger.V(1).Info(":::: DaemonSet Info ::::", "DaemonSet.Namespace", ds.Namespace, "DaemonSet.Name", ds.Name)
141 collectdConf, err := rebuildCollectdConf(collectPlugins)
143 //Restart Collectd Pods
144 //Restart only if hash of configmap has changed.
145 ds.Spec.Template.SetAnnotations(map[string]string{
146 "daaas-random": ComputeSHA256([]byte(collectdConf)),
148 cm.SetAnnotations(map[string]string{
149 "daaas-random": ComputeSHA256([]byte(collectdConf)),
152 cm.Data["node-collectd.conf"] = collectdConf
154 // Update the ConfigMap with new Spec and reload DaemonSets
155 reqLogger.Info("Updating the ConfigMap", "ConfigMap.Namespace", cm.Namespace, "ConfigMap.Name", cm.Name)
156 log.V(1).Info("ConfigMap Data", "Map: ", cm.Data)
157 err = r.client.Update(context.TODO(), cm)
159 reqLogger.Error(err, "Update the ConfigMap failed", "ConfigMap.Namespace", cm.Namespace, "ConfigMap.Name", cm.Name)
163 err = r.client.Update(context.TODO(), ds)
165 reqLogger.Error(err, "Update the DaemonSet failed", "DaemonSet.Namespace", ds.Namespace, "DaemonSet.Name", ds.Name)
170 reqLogger.Info("Reconcile success!!")
174 // ComputeSHA256 returns hash of data as string
175 func ComputeSHA256(data []byte) string {
176 hash := sha256.Sum256(data)
177 return fmt.Sprintf("%x", hash)
180 // findResourceMapForCR returns the configMap, collectd Daemonset and list of Collectd Plugins
181 func (r *ReconcileCollectdPlugin) findResourceMapForCR(cr *onapv1alpha1.CollectdPlugin) (ResourceMap, error) {
182 cmList := &corev1.ConfigMapList{}
183 opts := &client.ListOptions{}
184 rmap := ResourceMap{}
186 // Select ConfigMaps with label app=collectd
187 opts.SetLabelSelector("app=collectd")
188 opts.InNamespace(cr.Namespace)
189 err := r.client.List(context.TODO(), opts, cmList)
194 if cmList.Items == nil || len(cmList.Items) == 0 {
198 // Select DaemonSets with label app=collectd
199 dsList := &extensionsv1beta1.DaemonSetList{}
200 err = r.client.List(context.TODO(), opts, dsList)
205 if dsList.Items == nil || len(dsList.Items) == 0 {
209 // Get all collectd plugins in the current namespace to rebuild conf.
210 collectdPlugins := &onapv1alpha1.CollectdPluginList{}
211 cpOpts := &client.ListOptions{}
212 cpOpts.InNamespace(cr.Namespace)
213 err = r.client.List(context.TODO(), cpOpts, collectdPlugins)
218 rmap.configMap = &cmList.Items[0]
219 rmap.daemonSet = &dsList.Items[0]
220 rmap.collectdPlugins = &collectdPlugins.Items //will be nil if no plugins exist
224 // Get all collectd plugins and reconstruct, compute Hash and check for changes
225 func rebuildCollectdConf(cpList *[]onapv1alpha1.CollectdPlugin) (string, error) {
226 var collectdConf string
227 if *cpList == nil || len(*cpList) == 0 {
228 return "", errors.NewNotFound(corev1.Resource("collectdplugin"), "CollectdPlugin")
230 loadPlugin := make(map[string]string)
231 for _, cp := range *cpList {
232 if cp.Spec.PluginName == "global" {
233 collectdConf += cp.Spec.PluginConf + "\n"
235 loadPlugin[cp.Spec.PluginName] = cp.Spec.PluginConf
239 log.V(1).Info("::::::: Plugins Map ::::::: ", "PluginMap ", loadPlugin)
241 for cpName, cpConf := range loadPlugin {
242 collectdConf += "LoadPlugin" + " " + cpName + "\n"
243 collectdConf += cpConf + "\n"
246 collectdConf += "\n#Last line (collectd requires ā\\nā at the last line)"
248 return collectdConf, nil
251 // Handle Delete CR event for additional cleanup
252 func (r *ReconcileCollectdPlugin) handleDelete(reqLogger logr.Logger, cr *onapv1alpha1.CollectdPlugin) (bool, error) {
253 // Check if the CollectdPlugin instance is marked to be deleted, which is
254 // indicated by the deletion timestamp being set.
255 isMarkedToBeDeleted := cr.GetDeletionTimestamp() != nil
256 if isMarkedToBeDeleted {
257 if contains(cr.GetFinalizers(), collectdPluginFinalizer) {
258 // Run finalization logic for collectdPluginFinalizer. If the
259 // finalization logic fails, don't remove the finalizer so
260 // that we can retry during the next reconciliation.
261 if err := r.finalizeCollectdPlugin(reqLogger, cr); err != nil {
262 return isMarkedToBeDeleted, err
265 // Remove collectdPluginFinalizer. Once all finalizers have been
266 // removed, the object will be deleted.
267 cr.SetFinalizers(remove(cr.GetFinalizers(), collectdPluginFinalizer))
268 err := r.client.Update(context.TODO(), cr)
270 return isMarkedToBeDeleted, err
274 return isMarkedToBeDeleted, nil
277 func (r *ReconcileCollectdPlugin) updateStatus(cr *onapv1alpha1.CollectdPlugin) error {
278 podList := &corev1.PodList{}
279 opts := &client.ListOptions{}
280 opts.SetLabelSelector("app=collectd")
282 opts.InNamespace(cr.Namespace)
283 err := r.client.List(context.TODO(), opts, podList)
288 if podList.Items == nil || len(podList.Items) == 0 {
292 for _, pod := range podList.Items {
293 pods = append(pods, pod.Name)
295 cr.Status.CollectdAgents = pods
296 err = r.client.Status().Update(context.TODO(), cr)
300 func (r *ReconcileCollectdPlugin) finalizeCollectdPlugin(reqLogger logr.Logger, cr *onapv1alpha1.CollectdPlugin) error {
301 // Cleanup by regenerating new collectd conf and rolling update of DaemonSet
302 if err := r.handleCollectdPlugin(reqLogger, cr); err != nil {
303 reqLogger.Error(err, "Finalize CollectdPlugin failed!!")
306 reqLogger.Info("Successfully finalized CollectdPlugin!!")
310 func (r *ReconcileCollectdPlugin) addFinalizer(reqLogger logr.Logger, cr *onapv1alpha1.CollectdPlugin) error {
311 reqLogger.Info("Adding Finalizer for the CollectdPlugin")
312 cr.SetFinalizers(append(cr.GetFinalizers(), collectdPluginFinalizer))
315 err := r.client.Update(context.TODO(), cr)
317 reqLogger.Error(err, "Failed to update CollectdPlugin with finalizer")
323 func contains(list []string, s string) bool {
324 for _, v := range list {
332 func remove(list []string, s string) []string {
333 for i, v := range list {
335 list = append(list[:i], list[i+1:]...)