Add CRD Controller for Network
[ovn4nfv-k8s-plugin.git] / pkg / controller / network / network_controller.go
1 package network
2
3 import (
4         "context"
5         "fmt"
6         k8sv1alpha1 "ovn4nfv-k8s-plugin/pkg/apis/k8s/v1alpha1"
7
8         //      corev1 "k8s.io/api/core/v1"
9         "github.com/go-logr/logr"
10         "k8s.io/apimachinery/pkg/api/errors"
11         "k8s.io/apimachinery/pkg/runtime"
12         "ovn4nfv-k8s-plugin/internal/pkg/ovn"
13         "ovn4nfv-k8s-plugin/pkg/utils"
14         "sigs.k8s.io/controller-runtime/pkg/client"
15         "sigs.k8s.io/controller-runtime/pkg/controller"
16         "sigs.k8s.io/controller-runtime/pkg/handler"
17         "sigs.k8s.io/controller-runtime/pkg/manager"
18         "sigs.k8s.io/controller-runtime/pkg/reconcile"
19         logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
20         "sigs.k8s.io/controller-runtime/pkg/source"
21 )
22
23 var log = logf.Log.WithName("network_controller")
24
25 // Add creates a new Network Controller and adds it to the Manager. The Manager will set fields on the Controller
26 // and Start it when the Manager is Started.
27 func Add(mgr manager.Manager) error {
28         return add(mgr, newReconciler(mgr))
29 }
30
31 // newReconciler returns a new reconcile.Reconciler
32 func newReconciler(mgr manager.Manager) reconcile.Reconciler {
33         return &ReconcileNetwork{client: mgr.GetClient(), scheme: mgr.GetScheme()}
34 }
35
36 // add adds a new Controller to mgr with r as the reconcile.Reconciler
37 func add(mgr manager.Manager, r reconcile.Reconciler) error {
38         // Create a new controller
39         c, err := controller.New("network-controller", mgr, controller.Options{Reconciler: r})
40         if err != nil {
41                 return err
42         }
43         // Watch for changes to primary resource Network
44         err = c.Watch(&source.Kind{Type: &k8sv1alpha1.Network{}}, &handler.EnqueueRequestForObject{})
45         if err != nil {
46                 return err
47         }
48
49         return nil
50 }
51
52 // blank assignment to verify that ReconcileNetwork implements reconcile.Reconciler
53 var _ reconcile.Reconciler = &ReconcileNetwork{}
54
55 // ReconcileNetwork reconciles a Network object
56 type ReconcileNetwork struct {
57         // This client, initialized using mgr.Client() above, is a split client
58         // that reads objects from the cache and writes to the apiserver
59         client client.Client
60         scheme *runtime.Scheme
61 }
62 type reconcileFun func(instance *k8sv1alpha1.Network, reqLogger logr.Logger) error
63
64 // Reconcile reads that state of the cluster for a Network object and makes changes based on the state read
65 // and what is in the Network.Spec
66 // The Controller will requeue the Request to be processed again if the returned error is non-nil or
67 // Result.Requeue is true, otherwise upon completion it will remove the work from the queue.
68 func (r *ReconcileNetwork) Reconcile(request reconcile.Request) (reconcile.Result, error) {
69         reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
70         reqLogger.V(1).Info("Reconciling Network")
71
72         // Fetch the Network instance
73         instance := &k8sv1alpha1.Network{}
74         err := r.client.Get(context.TODO(), request.NamespacedName, instance)
75         if err != nil {
76                 if errors.IsNotFound(err) {
77                         // Request object not found, could have been deleted after reconcile request.
78                         // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
79                         // Return and don't requeue
80                         reqLogger.V(1).Info("Network Object not found")
81                         return reconcile.Result{}, nil
82                 }
83                 // Error reading the object - requeue the request.
84                 return reconcile.Result{}, err
85         }
86         for _, fun := range []reconcileFun{
87                 r.reconcileFinalizers,
88                 r.createNetwork,
89         } {
90                 if err = fun(instance, reqLogger); err != nil {
91                         return reconcile.Result{}, err
92                 }
93         }
94         return reconcile.Result{}, nil
95 }
96
97 const (
98         nfnNetworkFinalizer = "nfnCleanUpNetwork"
99 )
100
101 func (r *ReconcileNetwork) createNetwork(cr *k8sv1alpha1.Network, reqLogger logr.Logger) error {
102
103         if !cr.DeletionTimestamp.IsZero() {
104                 // Marked for deletion
105                 return nil
106         }
107         switch {
108         case cr.Spec.CniType == "ovn4nfv":
109                 ovnCtl, err := ovn.GetOvnController()
110                 if err != nil {
111                         return err
112                 }
113                 err = ovnCtl.CreateNetwork(cr)
114                 if err != nil {
115                         // Log the error
116                         reqLogger.Error(err, "Error Creating Network")
117                         cr.Status.State = k8sv1alpha1.CreateInternalError
118                 } else {
119                         cr.Status.State = k8sv1alpha1.Created
120                 }
121                 err = r.client.Status().Update(context.TODO(), cr)
122                 if err != nil {
123                         return err
124                 }
125                 // If OVN internal error don't requeue
126                 return nil
127                 // Add other CNI types here
128         }
129         reqLogger.Info("CNI type not supported", "name", cr.Spec.CniType)
130         return fmt.Errorf("CNI type not supported")
131
132 }
133
134 func (r *ReconcileNetwork) deleteNetwork(cr *k8sv1alpha1.Network, reqLogger logr.Logger) error {
135
136         switch {
137         case cr.Spec.CniType == "ovn4nfv":
138                 ovnCtl, err := ovn.GetOvnController()
139                 if err != nil {
140                         return err
141                 }
142                 err = ovnCtl.DeleteNetwork(cr)
143                 if err != nil {
144                         // Log the error
145                         reqLogger.Error(err, "Error Delete Network")
146                         cr.Status.State = k8sv1alpha1.DeleteInternalError
147                         err = r.client.Status().Update(context.TODO(), cr)
148                         if err != nil {
149                                 return err
150                         }
151                 }
152                 // If OVN internal error don't requeue
153                 return nil
154                 // Add other CNI types here
155         }
156         reqLogger.Info("CNI type not supported", "name", cr.Spec.CniType)
157         return fmt.Errorf("CNI type not supported")
158 }
159
160 func (r *ReconcileNetwork) reconcileFinalizers(instance *k8sv1alpha1.Network, reqLogger logr.Logger) (err error) {
161
162         if !instance.DeletionTimestamp.IsZero() {
163                 // Instance marked for deletion
164                 if utils.Contains(instance.ObjectMeta.Finalizers, nfnNetworkFinalizer) {
165                         reqLogger.V(1).Info("Finalizer found - delete network")
166                         if err = r.deleteNetwork(instance, reqLogger); err != nil {
167                                 reqLogger.Error(err, "Delete network")
168                         }
169                         // Remove the finalizer even if Delete Network fails. Fatal error retry will not resolve
170                         instance.ObjectMeta.Finalizers = utils.Remove(instance.ObjectMeta.Finalizers, nfnNetworkFinalizer)
171                         if err = r.client.Update(context.TODO(), instance); err != nil {
172                                 reqLogger.Error(err, "Removing Finalize")
173                                 return err
174                         }
175                 }
176
177         } else {
178                 // If finalizer doesn't exist add it
179                 if !utils.Contains(instance.GetFinalizers(), nfnNetworkFinalizer) {
180                         instance.SetFinalizers(append(instance.GetFinalizers(), nfnNetworkFinalizer))
181                         if err = r.client.Update(context.TODO(), instance); err != nil {
182                                 reqLogger.Error(err, "Adding Finalize")
183                                 return err
184                         }
185                         reqLogger.V(1).Info("Finalizer added")
186                 }
187         }
188         return nil
189 }