diff --git a/files/aautoscaler.py b/files/aautoscaler.py index 379a4ba..ec85315 100644 --- a/files/aautoscaler.py +++ b/files/aautoscaler.py @@ -1,10 +1,11 @@ import os from croniter import croniter -from datetime import date, datetime, timezone, timedelta +from datetime import datetime, timezone, timedelta from dateutil import parser from logs import Logs from k8s import K8s + class AAutoscaler: def __init__(self): @@ -19,13 +20,15 @@ def __init__(self): self.k8s = K8s(k8sAPI, k8sToken) # Auth via in-cluster configuration, running inside a pod elif 'KUBERNETES_SERVICE_HOST' in os.environ: - self.logs.info({'message': 'Kubernetes object via in-cluster configuration.'}) + self.logs.info( + {'message': 'Kubernetes object via in-cluster configuration.'}) self.k8s = K8s() else: - self.logs.error({'message': 'Error trying to create the Kubernetes object.'}) + self.logs.error( + {'message': 'Error trying to create the Kubernetes object.'}) raise Exception('Error trying to create the Kubernetes object.') - def __start__(self, namespace:str, deploy:dict, currentTime:datetime): + def __start__(self, namespace: str, deploy: dict, currentTime: datetime): ''' Logic for start the pods ''' @@ -34,25 +37,48 @@ def __start__(self, namespace:str, deploy:dict, currentTime:datetime): deployReplicas = deploy.spec.replicas startAnnotation = 'another-autoscaler/start-time' - if startAnnotation in deployAnnotations: - self.logs.debug({'message': 'Start time detected.', 'namespace': namespace, 'deployment': deployName}) - startTime = deployAnnotations[startAnnotation] - - if croniter.match(startTime, currentTime): - self.logs.debug({'message': 'Start time Cron expression matched.', 'namespace': namespace, 'deployment': deployName, 'startTime': str(startTime), 'currentTime': str(currentTime)}) - - # start-replicas - startReplicas = 1 - startReplicasAnnotation = 'another-autoscaler/start-replicas' - if startReplicasAnnotation in deployAnnotations: - self.logs.debug({'message': 'Number of replicas.', 'namespace': namespace, 'deployment': deployName, 'startReplicas': deployAnnotations[startReplicasAnnotation]}) - startReplicas = int(deployAnnotations[startReplicasAnnotation]) - - if deployReplicas != startReplicas: - self.logs.info({'message': 'Deployment set to start.', 'namespace': namespace, 'deployment': deployName, 'startTime': str(startTime), 'availableReplicas': deploy.status.available_replicas, 'startReplicas': str(startReplicas)}) - self.k8s.setReplicas(namespace, deployName, startReplicas) - - def __stop__(self, namespace:str, deploy:dict, currentTime:datetime): + if startAnnotation not in deployAnnotations: + return + + self.logs.debug({'message': 'Start time detected.', + 'namespace': namespace, 'deployment': deployName}) + startTime = deployAnnotations[startAnnotation] + + try: + if not croniter.match(startTime, currentTime): + return + except: + self.logs.error( + {'message': 'Error in start-time annotation', 'namespace': namespace, 'deployment': deployName}) + return + + self.logs.debug({'message': 'Start time Cron expression matched.', 'namespace': namespace, + 'deployment': deployName, 'startTime': str(startTime), 'currentTime': str(currentTime)}) + + # start-replicas + startReplicas = 1 + startReplicasAnnotation = 'another-autoscaler/start-replicas' + if startReplicasAnnotation not in deployAnnotations: + return + + self.logs.debug({'message': 'Number of replicas.', 'namespace': namespace, + 'deployment': deployName, 'startReplicas': deployAnnotations[startReplicasAnnotation]}) + try: + startReplicas = int( + deployAnnotations[startReplicasAnnotation]) + except: + startReplicas = 1 + + if deployReplicas == startReplicas: + return + + self.logs.info({'message': 'Deployment set to start.', 'namespace': namespace, 'deployment': deployName, + 'startTime': str(startTime), + 'availableReplicas': deploy.status.available_replicas, + 'startReplicas': str(startReplicas)}) + self.k8s.setReplicas(namespace, deployName, startReplicas) + + def __stop__(self, namespace: str, deploy: dict, currentTime: datetime): ''' Logic for stop the pods ''' @@ -61,25 +87,49 @@ def __stop__(self, namespace:str, deploy:dict, currentTime:datetime): deployReplicas = deploy.spec.replicas stopAnnotation = 'another-autoscaler/stop-time' - if stopAnnotation in deployAnnotations: - self.logs.debug({'message': 'Stop time detected.', 'namespace': namespace, 'deployment': deployName}) - stopTime = deployAnnotations[stopAnnotation] - - if croniter.match(stopTime, currentTime): - self.logs.debug({'message': 'Stop time Cron expression matched.', 'namespace': namespace, 'deployment': deployName, 'stopTime': str(stopTime), 'currentTime': str(currentTime)}) - - # stop-replicas - stopReplicas = 0 - stopReplicasAnnotation = 'another-autoscaler/stop-replicas' - if stopReplicasAnnotation in deployAnnotations: - self.logs.debug({'message': 'Number of replicas.', 'namespace': namespace, 'deployment': deployName, 'stopReplicas': deployAnnotations[stopReplicasAnnotation]}) - stopReplicas = int(deployAnnotations[stopReplicasAnnotation]) - - if deployReplicas != stopReplicas: - self.logs.info({'message': 'Deployment set to stop.', 'namespace': namespace, 'deployment': deployName, 'stopTime': str(stopTime), 'availableReplicas': deploy.status.available_replicas, 'stopReplicas': str(stopReplicas)}) - self.k8s.setReplicas(namespace, deployName, stopReplicas) - - def __restart__(self, namespace:str, deploy:dict, currentTime:datetime): + if stopAnnotation not in deployAnnotations: + return + + self.logs.debug({'message': 'Stop time detected.', + 'namespace': namespace, 'deployment': deployName}) + stopTime = deployAnnotations[stopAnnotation] + + try: + if not croniter.match(stopTime, currentTime): + return + except: + self.logs.error( + {'message': 'Error in stop-time annotation', 'namespace': namespace, 'deployment': deployName}) + return + + self.logs.debug({'message': 'Stop time Cron expression matched.', 'namespace': namespace, + 'deployment': deployName, 'stopTime': str(stopTime), 'currentTime': str(currentTime)}) + + # stop-replicas + stopReplicas = 0 + stopReplicasAnnotation = 'another-autoscaler/stop-replicas' + if stopReplicasAnnotation not in deployAnnotations: + return + + self.logs.debug({'message': 'Number of replicas.', 'namespace': namespace, + 'deployment': deployName, 'stopReplicas': deployAnnotations[stopReplicasAnnotation]}) + + try: + stopReplicas = int( + deployAnnotations[stopReplicasAnnotation]) + except: + stopReplicas = 0 + + if deployReplicas == stopReplicas: + return + + self.logs.info({'message': 'Deployment set to stop.', 'namespace': namespace, 'deployment': deployName, + 'stopTime': str(stopTime), + 'availableReplicas': deploy.status.available_replicas, + 'stopReplicas': str(stopReplicas)}) + self.k8s.setReplicas(namespace, deployName, stopReplicas) + + def __restart__(self, namespace: str, deploy: dict, currentTime: datetime): ''' Logic for restart the pods ''' @@ -87,22 +137,38 @@ def __restart__(self, namespace:str, deploy:dict, currentTime:datetime): deployAnnotations = deploy.metadata.annotations restartAnnotation = 'another-autoscaler/restart-time' - if restartAnnotation in deployAnnotations: - self.logs.debug({'message': 'Restart time detected.', 'namespace': namespace, 'deployment': deployName}) - restartTime = deployAnnotations[restartAnnotation] - - if croniter.match(restartTime, currentTime): - self.logs.debug({'message': 'Restart time Cron expression matched.', 'namespace': namespace, 'deployment': deployName, 'restartTime': str(restartTime), 'currentTime': str(currentTime)}) - - # Check if was already restarted - try: - restartedAt = parser.parse(deploy.spec.template.metadata.annotations['kubectl.kubernetes.io/restartedAt']) - except: - restartedAt = currentTime - timedelta(days=1) - - if ((currentTime - restartedAt).total_seconds() / 60.0) > 1: - self.logs.info({'message': 'Deployment set to restart.', 'namespace': namespace, 'deployment': deployName, 'restartTime': str(restartTime), 'currentTime': str(currentTime)}) - self.k8s.rolloutDeployment(namespace, deployName) + if restartAnnotation not in deployAnnotations: + return + + self.logs.debug({'message': 'Restart time detected.', + 'namespace': namespace, 'deployment': deployName}) + restartTime = deployAnnotations[restartAnnotation] + + try: + + if not croniter.match(restartTime, currentTime): + return + except: + self.logs.error( + {'message': 'Error in restart-time annotation', 'namespace': namespace, 'deployment': deployName}) + return + + self.logs.debug({'message': 'Restart time Cron expression matched.', 'namespace': namespace, + 'deployment': deployName, 'restartTime': str(restartTime), 'currentTime': str(currentTime)}) + + # Check if was already restarted + try: + restartedAt = parser.parse( + deploy.spec.template.metadata.annotations['kubectl.kubernetes.io/restartedAt']) + except: + restartedAt = currentTime - timedelta(days=1) + + if ((currentTime - restartedAt).total_seconds() / 60.0) <= 1: + return + + self.logs.info({'message': 'Deployment set to restart.', 'namespace': namespace, + 'deployment': deployName, 'restartTime': str(restartTime), 'currentTime': str(currentTime)}) + self.k8s.rolloutDeployment(namespace, deployName) def execute(self): # Current time in UTC format diff --git a/files/k8s.py b/files/k8s.py index 4d2d3d4..e749297 100644 --- a/files/k8s.py +++ b/files/k8s.py @@ -5,6 +5,7 @@ from kubernetes.client.rest import ApiException from logs import Logs + class K8s: def __init__(self, apiEndpoint:str='', token:str=''): @@ -20,13 +21,14 @@ def __init__(self, apiEndpoint:str='', token:str=''): configuration.host = apiEndpoint self.CoreV1Api = client.CoreV1Api(client.ApiClient(configuration)) self.AppsV1Api = client.AppsV1Api(client.ApiClient(configuration)) - self.ExtensionsV1beta1Api = client.ExtensionsV1beta1Api(client.ApiClient(configuration)) - # Client via in-cluster configuration, running inside a pod with proper service account + self.NetworkingV1Api = client.NetworkingV1Api(client.ApiClient(configuration)) + # Client via in-cluster configuration, + # running inside a pod with proper service account else: config.load_incluster_config() self.CoreV1Api = client.CoreV1Api() self.AppsV1Api = client.AppsV1Api() - self.ExtensionsV1beta1Api = client.ExtensionsV1beta1Api() + self.NetworkingV1Api = client.NetworkingV1Api() def getNamespaces(self) -> list: ''' @@ -153,7 +155,7 @@ def rolloutDeployment(self, namespace:str, deploymentName:str) -> bool: return False def getIngress(self, namespace, ingressName): - response = self.ExtensionsV1beta1Api.read_namespaced_ingress(namespace=namespace, name=ingressName) + response = self.NetworkingV1Api.read_namespaced_ingress(namespace=namespace, name=ingressName) return response def getLogs(self, namespace, podName, containerName, tailLines): diff --git a/files/requirements.txt b/files/requirements.txt index 0401eff..92eb853 100644 --- a/files/requirements.txt +++ b/files/requirements.txt @@ -1,4 +1,4 @@ -kubernetes -schedule -croniter -pytz \ No newline at end of file +kubernetes==23.3.0 +schedule==1.1.0 +croniter==1.3.4 +pytz==2022.1