Refactored BPA controller code for better testing
[icn.git] / cmd / bpa-operator / pkg / controller / provisioning / provisioning_controller_test.go
1 package provisioning
2
3 import (
4
5        "testing"
6        "io/ioutil"
7        "os"
8
9        bpav1alpha1 "github.com/bpa-operator/pkg/apis/bpa/v1alpha1"
10        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11        logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
12        "k8s.io/apimachinery/pkg/runtime"
13        "k8s.io/apimachinery/pkg/types"
14        "k8s.io/client-go/kubernetes/scheme"
15        "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
16        "sigs.k8s.io/controller-runtime/pkg/client/fake"
17        "sigs.k8s.io/controller-runtime/pkg/reconcile"
18        fakedynamic "k8s.io/client-go/dynamic/fake"
19        fakeclientset "k8s.io/client-go/kubernetes/fake"
20 )
21
22 func TestProvisioningController(t *testing.T) {
23
24      logf.SetLogger(logf.ZapLogger(true))
25      bpaName1 := "bpa-test-cr"
26      bpaName2 := "bpa-test-2"
27      bpaName3 := "bpa-test-3"
28      namespace := "default"
29      clusterName := "test-cluster"
30      clusterName2 :=  "test-cluster-2"
31      clusterName3 := "test-cluster-3"
32      macAddress1 := "08:00:27:00:ab:2c"
33      macAddress2 := "08:00:27:00:ab:3d"
34      macAddress3 := "08:00:27:00:ab:1c"
35
36      // Create Fake DHCP file
37      err := createFakeDHCP()
38      if err != nil {
39         t.Fatalf("Cannot create Fake DHCP file for testing\n")
40      }
41
42      // Create Fake baremetalhost
43      bmhList := newBMList()
44
45     // Create Fake Provisioning CR
46     provisioning := newBPA(bpaName1, namespace, clusterName, macAddress1)
47     provisioning2 := newBPA(bpaName2, namespace, clusterName2, macAddress2)
48     provisioning3 := newBPA(bpaName3, namespace, clusterName3, macAddress3)
49
50     // Objects to track in the fake Client
51     objs := []runtime.Object{provisioning, provisioning2, provisioning3}
52
53     // Register operator types with the runtime scheme
54     sc := scheme.Scheme
55
56     sc.AddKnownTypes(bpav1alpha1.SchemeGroupVersion, provisioning, provisioning2, provisioning3)
57
58     // Create Fake Clients and Clientset
59     fakeClient := fake.NewFakeClient(objs...)
60     fakeDyn := fakedynamic.NewSimpleDynamicClient(sc, bmhList,)
61     fakeClientSet := fakeclientset.NewSimpleClientset()
62
63     r := &ReconcileProvisioning{client: fakeClient, scheme: sc, clientset: fakeClientSet, bmhClient: fakeDyn}
64
65     // Mock request to simulate Reconcile() being called on an event for a watched resource 
66     req := simulateRequest(provisioning)
67     _, err = r.Reconcile(req)
68     if err != nil {
69        t.Fatalf("reconcile: (%v)", err)
70     }
71
72    // Test 1: Check the job was created with the expected name
73    jobClient := r.clientset.BatchV1().Jobs(namespace)
74    job, err := jobClient.Get("kud-test-cluster", metav1.GetOptions{})
75
76     if err != nil {
77         t.Fatalf("Error occured while getting job: (%v)", err)
78     }
79
80    // Test 2: Check that cluster name metadata in job is the expected cluster name
81    jobClusterName := job.Labels["cluster"]
82    if jobClusterName != clusterName {
83       t.Fatalf("Job cluster Name is wrong")
84    }
85
86
87    // Test 3: Check that the right error is produced when host with MAC address does not exist
88    req = simulateRequest(provisioning2)
89     _, err = r.Reconcile(req)
90     expectedErr := "Host with MAC Address " + macAddress2 + " not found\n"
91     if err.Error() != expectedErr {
92        t.Fatalf("Failed, Unexpected error occured %v\n", err)
93     }
94
95    // Test 4: Check that the right error is produced when MAC address is not found in the DHCP lease file
96    req = simulateRequest(provisioning3)
97     _, err = r.Reconcile(req)
98     expectedErr = "IP address not found for host with MAC address " + macAddress3 + " \n"
99     if err.Error() != expectedErr {
100        t.Fatalf("Failed, Unexpected error occured %v\n", err)
101     }
102
103    // Delete Fake DHCP file and cluster directories
104    err = os.Remove("/var/lib/dhcp/dhcpd.leases")
105    if err != nil {
106       t.Logf("\nUnable to delete fake DHCP file\n")
107    }
108    err = os.RemoveAll("/multi-cluster/" + clusterName)
109    if err != nil {
110       t.Logf("\nUnable to delete cluster directory %s\n", clusterName)
111    }
112    err = os.RemoveAll("/multi-cluster/" + clusterName2)
113    if err != nil {
114       t.Logf("\nUnable to delete cluster directory %s\n", clusterName2)
115    }
116    err = os.RemoveAll("/multi-cluster/" + clusterName3)
117    if err != nil {
118       t.Logf("\nUnable to delete cluster directory %s\n", clusterName3)
119    }
120
121
122 }
123
124 func simulateRequest(bpaCR *bpav1alpha1.Provisioning) reconcile.Request {
125         namespacedName := types.NamespacedName{
126                 Name:      bpaCR.ObjectMeta.Name,
127                 Namespace: bpaCR.ObjectMeta.Namespace,
128         }
129         return reconcile.Request{NamespacedName: namespacedName}
130 }
131
132
133
134 func newBPA(name, namespace, clusterName, macAddress string) *bpav1alpha1.Provisioning {
135
136      provisioningCR := &bpav1alpha1.Provisioning{
137         ObjectMeta: metav1.ObjectMeta{
138             Name:      name,
139             Namespace: namespace,
140             Labels: map[string]string{
141                 "cluster": clusterName,
142             },
143         },
144         Spec: bpav1alpha1.ProvisioningSpec{
145                Masters: []map[string]bpav1alpha1.Master{
146                          map[string]bpav1alpha1.Master{
147                            "test-master" : bpav1alpha1.Master{
148                                  MACaddress: macAddress,
149                             },
150
151                },
152               },
153        },
154
155     }
156     return provisioningCR
157 }
158
159
160 func newBMList() *unstructured.UnstructuredList{
161
162         bmMap := map[string]interface{}{
163                            "apiVersion": "metal3.io/v1alpha1",
164                            "kind": "BareMetalHostList",
165                            "metaData": map[string]interface{}{
166                                "continue": "",
167                                    "resourceVersion": "11830058",
168                                    "selfLink": "/apis/metal3.io/v1alpha1/baremetalhosts",
169
170                  },
171                  }
172
173
174
175
176         metaData := map[string]interface{}{
177                          "creationTimestamp": "2019-10-24T04:51:15Z",
178                          "generation":"1",
179                          "name": "fake-test-bmh",
180                          "namespace": "default",
181                          "resourceVersion": "11829263",
182                          "selfLink": "/apis/metal3.io/v1alpha1/namespaces/default/baremetalhosts/bpa-test-bmh",
183                          "uid": "e92cb312-f619-11e9-90bc-00219ba0c77a",
184         }
185
186
187
188         nicMap1 := map[string]interface{}{
189                         "ip": "",
190                          "mac": "08:00:27:00:ab:2c",
191                          "model": "0x8086 0x1572",
192                          "name": "eth3",
193                          "pxe": "false",
194                          "speedGbps": "0",
195                          "vlanId": "0",
196         }
197
198        nicMap2 := map[string]interface{}{
199                         "ip": "",
200                          "mac": "08:00:27:00:ab:1c",
201                          "model": "0x8086 0x1572",
202                          "name": "eth4",
203                          "pxe": "false",
204                          "speedGbps": "0",
205                          "vlanId": "0",
206         }
207
208         specMap  := map[string]interface{}{
209                           "status" : map[string]interface{}{
210                                    "errorMessage": "",
211                                         "hardware": map[string]interface{}{
212                                            "nics": map[string]interface{}{
213                                                 "nic1" : nicMap1,
214                                                 "nic2" : nicMap2,
215                                             },
216                           },
217                           },
218
219
220         }
221
222         itemMap := map[string]interface{}{
223                            "apiVersion": "metal3.io/v1alpha1",
224                            "kind": "BareMetalHost",
225                            "metadata": metaData,
226                            "spec": specMap,
227                  }
228         itemU := unstructured.Unstructured{
229                          Object: itemMap,
230                    }
231
232         itemsList := []unstructured.Unstructured{itemU,}
233
234         bmhList := &unstructured.UnstructuredList{
235                                         Object: bmMap,
236                                         Items: itemsList,
237          }
238
239
240       return bmhList
241 }
242
243
244 // Create DHCP file for testing
245 func createFakeDHCP() error{
246
247
248      dhcpData := []byte(`lease 192.168.50.63 {
249   starts 4 2019/08/08 22:32:49;
250   ends 4 2019/08/08 23:52:49;
251   cltt 4 2019/08/08 22:32:49;
252   binding state active;
253   next binding state free;
254   rewind binding state free;
255   hardware ethernet 08:00:27:00:ab:2c;
256   client-hostname "fake-test-bmh"";
257 }`)
258      err := ioutil.WriteFile("/var/lib/dhcp/dhcpd.leases", dhcpData, 0777)
259
260      if (err != nil) {
261         return err
262      }
263
264     return nil
265 }