Repository: devopsspiral/KubeLibrary (language: python)

Issue:
Reach 90% unit test coverage
Reach 90% unit test coverage and set CI threshold so that we don't go below it forcing creation of unit tests for each new keyword.


Discussion / hints:

Fix patch:
diff --git a/.coveragerc b/.coveragerc
index ed3100d5..91d7a892 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -3,4 +3,4 @@ command_line = -m unittest discover
 source =
     src/
 [report]
-fail_under = 76
+fail_under = 81


Test patch:
diff --git a/test/resources/cronjob_details.json b/test/resources/cronjob_details.json
new file mode 100644
index 00000000..bf020dbb
--- /dev/null
+++ b/test/resources/cronjob_details.json
@@ -0,0 +1,115 @@
+{
+    "apiVersion": "batch/v1beta1",
+    "kind": "CronJob",
+    "metadata": {
+        "creationTimestamp": "2021-04-19T10:54:19Z",
+        "labels": {
+            "TestLabel": "mytestlabel"
+        },
+        "managedFields": [
+            {
+                "apiVersion": "batch/v1beta1",
+                "fieldsType": "FieldsV1",
+                "fieldsV1": {
+                    "f:metadata": {
+                        "f:labels": {
+                            ".": {},
+                            "f:TestLabel": {}
+                        }
+                    },
+                    "f:spec": {
+                        "f:concurrencyPolicy": {},
+                        "f:failedJobsHistoryLimit": {},
+                        "f:jobTemplate": {
+                            "f:spec": {
+                                "f:template": {
+                                    "f:metadata": {
+                                        "f:labels": {
+                                            ".": {},
+                                            "f:TestLabel": {}
+                                        }
+                                    },
+                                    "f:spec": {
+                                        "f:containers": {
+                                            "k:{\"name\":\"hello\"}": {
+                                                ".": {},
+                                                "f:args": {},
+                                                "f:image": {},
+                                                "f:imagePullPolicy": {},
+                                                "f:name": {},
+                                                "f:resources": {},
+                                                "f:terminationMessagePath": {},
+                                                "f:terminationMessagePolicy": {}
+                                            }
+                                        },
+                                        "f:dnsPolicy": {},
+                                        "f:restartPolicy": {},
+                                        "f:schedulerName": {},
+                                        "f:securityContext": {},
+                                        "f:terminationGracePeriodSeconds": {}
+                                    }
+                                }
+                            }
+                        },
+                        "f:schedule": {},
+                        "f:successfulJobsHistoryLimit": {},
+                        "f:suspend": {}
+                    }
+                },
+                "manager": "kubectl",
+                "operation": "Update",
+                "time": "2021-04-19T10:54:19Z"
+            }
+        ],
+        "name": "hello",
+        "namespace": "default",
+        "resourceVersion": "744",
+        "selfLink": "/apis/batch/v1beta1/namespaces/default/cronjobs/hello",
+        "uid": "a4030af5-39e0-4b8e-a6a1-65811afbbab7"
+    },
+    "spec": {
+        "concurrencyPolicy": "Allow",
+        "failedJobsHistoryLimit": 1,
+        "jobTemplate": {
+            "metadata": {
+                "creationTimestamp": null
+            },
+            "spec": {
+                "template": {
+                    "metadata": {
+                        "creationTimestamp": null,
+                        "labels": {
+                            "TestLabel": "mytestlabel"
+                        }
+                    },
+                    "spec": {
+                        "containers": [
+                            {
+                                "args": [
+                                    "/bin/sh",
+                                    "-c",
+                                    "date; echo Hello from the Kubernetes cluster"
+                                ],
+                                "image": "busybox",
+                                "imagePullPolicy": "IfNotPresent",
+                                "name": "hello",
+                                "resources": {},
+                                "terminationMessagePath": "/dev/termination-log",
+                                "terminationMessagePolicy": "File"
+                            }
+                        ],
+                        "dnsPolicy": "ClusterFirst",
+                        "restartPolicy": "OnFailure",
+                        "schedulerName": "default-scheduler",
+                        "securityContext": {},
+                        "terminationGracePeriodSeconds": 30
+                    }
+                }
+            }
+        },
+        "schedule": "*/1 * * * *",
+        "successfulJobsHistoryLimit": 3,
+        "suspend": false
+    },
+    "status": {}
+}
diff --git a/test/resources/daemonset_details.json b/test/resources/daemonset_details.json
new file mode 100644
index 00000000..54a94572
--- /dev/null
+++ b/test/resources/daemonset_details.json
@@ -0,0 +1,235 @@
+{
+    "apiVersion": "apps/v1",
+    "kind": "DaemonSet",
+    "metadata": {
+        "annotations": {
+            "deprecated.daemonset.template.generation": "1"
+        },
+        "creationTimestamp": "2021-04-22T14:18:47Z",
+        "generation": 1,
+        "labels": {
+            "TestLabel": "mytestlabel"
+        },
+        "managedFields": [
+            {
+                "apiVersion": "apps/v1",
+                "fieldsType": "FieldsV1",
+                "fieldsV1": {
+                    "f:metadata": {
+                        "f:annotations": {
+                            ".": {},
+                            "f:deprecated.daemonset.template.generation": {}
+                        },
+                        "f:labels": {
+                            ".": {},
+                            "f:TestLabel": {}
+                        }
+                    },
+                    "f:spec": {
+                        "f:revisionHistoryLimit": {},
+                        "f:selector": {
+                            "f:matchLabels": {
+                                ".": {},
+                                "f:name": {}
+                            }
+                        },
+                        "f:template": {
+                            "f:metadata": {
+                                "f:labels": {
+                                    ".": {},
+                                    "f:name": {}
+                                }
+                            },
+                            "f:spec": {
+                                "f:containers": {
+                                    "k:{\"name\":\"fluentd-elasticsearch\"}": {
+                                        ".": {},
+                                        "f:image": {},
+                                        "f:imagePullPolicy": {},
+                                        "f:name": {},
+                                        "f:resources": {
+                                            ".": {},
+                                            "f:limits": {
+                                                ".": {},
+                                                "f:memory": {}
+                                            },
+                                            "f:requests": {
+                                                ".": {},
+                                                "f:cpu": {},
+                                                "f:memory": {}
+                                            }
+                                        },
+                                        "f:terminationMessagePath": {},
+                                        "f:terminationMessagePolicy": {},
+                                        "f:volumeMounts": {
+                                            ".": {},
+                                            "k:{\"mountPath\":\"/var/lib/docker/containers\"}": {
+                                                ".": {},
+                                                "f:mountPath": {},
+                                                "f:name": {},
+                                                "f:readOnly": {}
+                                            },
+                                            "k:{\"mountPath\":\"/var/log\"}": {
+                                                ".": {},
+                                                "f:mountPath": {},
+                                                "f:name": {}
+                                            }
+                                        }
+                                    }
+                                },
+                                "f:dnsPolicy": {},
+                                "f:restartPolicy": {},
+                                "f:schedulerName": {},
+                                "f:securityContext": {},
+                                "f:terminationGracePeriodSeconds": {},
+                                "f:tolerations": {},
+                                "f:volumes": {
+                                    ".": {},
+                                    "k:{\"name\":\"varlibdockercontainers\"}": {
+                                        ".": {},
+                                        "f:hostPath": {
+                                            ".": {},
+                                            "f:path": {},
+                                            "f:type": {}
+                                        },
+                                        "f:name": {}
+                                    },
+                                    "k:{\"name\":\"varlog\"}": {
+                                        ".": {},
+                                        "f:hostPath": {
+                                            ".": {},
+                                            "f:path": {},
+                                            "f:type": {}
+                                        },
+                                        "f:name": {}
+                                    }
+                                }
+                            }
+                        },
+                        "f:updateStrategy": {
+                            "f:rollingUpdate": {
+                                ".": {},
+                                "f:maxUnavailable": {}
+                            },
+                            "f:type": {}
+                        }
+                    }
+                },
+                "manager": "kubectl",
+                "operation": "Update",
+                "time": "2021-04-22T14:18:47Z"
+            },
+            {
+                "apiVersion": "apps/v1",
+                "fieldsType": "FieldsV1",
+                "fieldsV1": {
+                    "f:status": {
+                        "f:currentNumberScheduled": {},
+                        "f:desiredNumberScheduled": {},
+                        "f:numberAvailable": {},
+                        "f:numberReady": {},
+                        "f:observedGeneration": {},
+                        "f:updatedNumberScheduled": {}
+                    }
+                },
+                "manager": "kube-controller-manager",
+                "operation": "Update",
+                "time": "2021-04-22T14:19:00Z"
+            }
+        ],
+        "name": "fluentd-elasticsearch",
+        "namespace": "default",
+        "resourceVersion": "1498",
+        "selfLink": "/apis/apps/v1/namespaces/default/daemonsets/fluentd-elasticsearch",
+        "uid": "684cdcd7-28c3-4d52-ba26-3aea85895961"
+    },
+    "spec": {
+        "revisionHistoryLimit": 10,
+        "selector": {
+            "matchLabels": {
+                "name": "fluentd-elasticsearch"
+            }
+        },
+        "template": {
+            "metadata": {
+                "creationTimestamp": null,
+                "labels": {
+                    "name": "fluentd-elasticsearch"
+                }
+            },
+            "spec": {
+                "containers": [
+                    {
+                        "image": "quay.io/fluentd_elasticsearch/fluentd:v2.5.2",
+                        "imagePullPolicy": "IfNotPresent",
+                        "name": "fluentd-elasticsearch",
+                        "resources": {
+                            "limits": {
+                                "memory": "200Mi"
+                            },
+                            "requests": {
+                                "cpu": "100m",
+                                "memory": "200Mi"
+                            }
+                        },
+                        "terminationMessagePath": "/dev/termination-log",
+                        "terminationMessagePolicy": "File",
+                        "volumeMounts": [
+                            {
+                                "mountPath": "/var/log",
+                                "name": "varlog"
+                            },
+                            {
+                                "mountPath": "/var/lib/docker/containers",
+                                "name": "varlibdockercontainers",
+                                "readOnly": true
+                            }
+                        ]
+                    }
+                ],
+                "dnsPolicy": "ClusterFirst",
+                "restartPolicy": "Always",
+                "schedulerName": "default-scheduler",
+                "securityContext": {},
+                "terminationGracePeriodSeconds": 30,
+                "tolerations": [
+                    {
+                        "effect": "NoSchedule",
+                        "key": "node-role.kubernetes.io/master"
+                    }
+                ],
+                "volumes": [
+                    {
+                        "hostPath": {
+                            "path": "/var/log",
+                            "type": ""
+                        },
+                        "name": "varlog"
+                    },
+                    {
+                        "hostPath": {
+                            "path": "/var/lib/docker/containers",
+                            "type": ""
+                        },
+                        "name": "varlibdockercontainers"
+                    }
+                ]
+            }
+        },
+        "updateStrategy": {
+            "rollingUpdate": {
+                "maxUnavailable": 1
+            },
+            "type": "RollingUpdate"
+        }
+    },
+    "status": {
+        "currentNumberScheduled": 2,
+        "desiredNumberScheduled": 2,
+        "numberAvailable": 2,
+        "numberMisscheduled": 0,
+        "numberReady": 2,
+        "observedGeneration": 1,
+        "updatedNumberScheduled": 2
+    }
+}
diff --git a/test/resources/ingress_details.json b/test/resources/ingress_details.json
new file mode 100644
index 00000000..7df1adb0
--- /dev/null
+++ b/test/resources/ingress_details.json
@@ -0,0 +1,51 @@
+{
+    "apiVersion": "extensions/v1beta1",
+    "kind": "Ingress",
+    "metadata": {
+        "creationTimestamp": "2021-03-25T04:04:21Z",
+        "labels": {
+            "TestLabel": "mytestlabel"
+        },
+        "generation": 1,
+        "managedFields": [
+            {
+                "apiVersion": "networking.k8s.io/v1beta1",
+                "fieldsType": "FieldsV1",
+                "fieldsV1": {
+                    "f:spec": {
+                        "f:rules": {}
+                    }
+                },
+                "manager": "kubectl",
+                "operation": "Update",
+                "time": "2021-03-25T04:04:21Z"
+            }
+        ],
+        "name": "minimal-ingress",
+        "namespace": "default",
+        "resourceVersion": "17529",
+        "selfLink": "/apis/extensions/v1beta1/namespaces/default/ingresses/minimal-ingress",
+        "uid": "e2dc27e7-4a75-4837-bf6a-9a13d7cbaaf3"
+    },
+    "spec": {
+        "rules": [
+            {
+                "http": {
+                    "paths": [
+                        {
+                            "backend": {
+                                "serviceName": "test",
+                                "servicePort": 80
+                            },
+                            "path": "/testpath",
+                            "pathType": "ImplementationSpecific"
+                        }
+                    ]
+                }
+            }
+        ]
+    },
+    "status": {
+        "loadBalancer": {}
+    }
+}
diff --git a/test/resources/service_details.json b/test/resources/service_details.json
new file mode 100644
index 00000000..4ce7c59c
--- /dev/null
+++ b/test/resources/service_details.json
@@ -0,0 +1,67 @@
+{
+    "apiVersion": "v1",
+    "kind": "Service",
+    "metadata": {
+        "creationTimestamp": "2021-04-22T10:24:37Z",
+        "labels": {
+            "Test": "mytest"
+        },
+        "managedFields": [
+            {
+                "apiVersion": "v1",
+                "fieldsType": "FieldsV1",
+                "fieldsV1": {
+                    "f:metadata": {
+                        "f:labels": {
+                            ".": {},
+                            "f:Test": {}
+                        }
+                    },
+                    "f:spec": {
+                        "f:ports": {
+                            ".": {},
+                            "k:{\"port\":80,\"protocol\":\"TCP\"}": {
+                                ".": {},
+                                "f:port": {},
+                                "f:protocol": {},
+                                "f:targetPort": {}
+                            }
+                        },
+                        "f:selector": {
+                            ".": {},
+                            "f:app": {}
+                        },
+                        "f:sessionAffinity": {},
+                        "f:type": {}
+                    }
+                },
+                "manager": "kubectl",
+                "operation": "Update",
+                "time": "2021-04-22T10:24:37Z"
+            }
+        ],
+        "name": "my-service2",
+        "namespace": "default",
+        "resourceVersion": "909",
+        "selfLink": "/api/v1/namespaces/default/services/my-service2",
+        "uid": "6fd4d12c-8b5f-4a0b-b153-96489ab3c0be"
+    },
+    "spec": {
+        "clusterIP": "10.97.158.206",
+        "ports": [
+            {
+                "port": 80,
+                "protocol": "TCP",
+                "targetPort": 9376
+            }
+        ],
+        "selector": {
+            "app": "MyApp"
+        },
+        "sessionAffinity": "None",
+        "type": "ClusterIP"
+    },
+    "status": {
+        "loadBalancer": {}
+    }
+}
diff --git a/test/test_KubeLibrary.py b/test/test_KubeLibrary.py
index 718cc0da..2d993e29 100644
--- a/test/test_KubeLibrary.py
+++ b/test/test_KubeLibrary.py
@@ -39,6 +39,38 @@ def __getitem__(self, key):
         return getattr(self, key)
 
 
+def mock_read_daemonset_details_in_namespace(name, namespace):
+    if namespace == 'default':
+        with open('test/resources/daemonset_details.json') as json_file:
+            daemonset_details_content = json.load(json_file)
+            read_daemonset_details = AttributeDict({'items': daemonset_details_content})
+            return read_daemonset_details
+
+
+def mock_read_service_details_in_namespace(name, namespace):
+    if namespace == 'default':
+        with open('test/resources/service_details.json') as json_file:
+            service_details_content = json.load(json_file)
+            read_service_details = AttributeDict({'items': service_details_content})
+            return read_service_details
+
+
+def mock_read_ingress_details_in_namespace(name, namespace):
+    if namespace == 'default':
+        with open('test/resources/ingress_details.json') as json_file:
+            ingress_details_content = json.load(json_file)
+            read_ingress_details = AttributeDict({'items': ingress_details_content})
+            return read_ingress_details
+
+
+def mock_read_cron_job_details_in_namespace(name, namespace):
+    if namespace == 'default':
+        with open('test/resources/cronjob_details.json') as json_file:
+            cron_job_details_content = json.load(json_file)
+            read_cron_job_details = AttributeDict({'items': cron_job_details_content})
+            return read_cron_job_details
+
+
 def mock_list_namespaced_daemonsets(namespace, watch=False, label_selector=""):
     if namespace == 'default':
         with open('test/resources/daemonset.json') as json_file:
@@ -452,3 +484,31 @@ def test_get_configmaps_in_namespace(self, mock_lnp):
         kl = KubeLibrary(kube_config='test/resources/k3d')
         configmaps = kl.get_configmaps_in_namespace('.*', 'default')
         self.assertEqual(['game-demo'], kl.filter_configmap_names(configmaps))
+
+    @mock.patch('kubernetes.client.AppsV1Api.read_namespaced_daemon_set')
+    def test_get_daemonset_details_in_namespace(self, mock_lnp):
+        mock_lnp.side_effect = mock_read_daemonset_details_in_namespace
+        kl = KubeLibrary(kube_config='test/resources/k3d')
+        daemonset_details = kl.get_daemonset_details_in_namespace('fluentd-elasticsearch', 'default')
+        self.assertEqual('mytestlabel', daemonset_details.items.metadata.labels.TestLabel)
+
+    @mock.patch('kubernetes.client.CoreV1Api.read_namespaced_service')
+    def test_get_service_details_in_namespace(self, mock_lnp):
+        mock_lnp.side_effect = mock_read_service_details_in_namespace
+        kl = KubeLibrary(kube_config='test/resources/k3d')
+        service_details = kl.get_service_details_in_namespace('minimal-ingress', 'default')
+        self.assertEqual('mytest', service_details.items.metadata.labels.Test)
+
+    @mock.patch('kubernetes.client.ExtensionsV1beta1Api.read_namespaced_ingress')
+    def test_get_ingress_details_in_namespace(self, mock_lnp):
+        mock_lnp.side_effect = mock_read_ingress_details_in_namespace
+        kl = KubeLibrary(kube_config='test/resources/k3d')
+        ingress_details = kl.get_ingress_details_in_namespace('max-ingress', 'default')
+        self.assertEqual('mytestlabel', ingress_details.items.metadata.labels.TestLabel)
+
+    @mock.patch('kubernetes.client.BatchV1beta1Api.read_namespaced_cron_job')
+    def test_get_cron_job_details_in_namespace(self, mock_lnp):
+        mock_lnp.side_effect = mock_read_cron_job_details_in_namespace
+        kl = KubeLibrary(kube_config='test/resources/k3d')
+        cron_job_details = kl.get_cron_job_details_in_namespace('hello', 'default')
+        self.assertEqual('mytestlabel', cron_job_details.items.metadata.labels.TestLabel)