Operator Explained¶
In this tutorial, we will explain the code in the previous tutorila Operator
1. nginx.py¶
Initialize
Upon initialization, save the name, namespace and passed kwargs to instance attribute and set the deploy_name of the Nginx instance
from kubernetes import client
class Nginx:
def __init__(self, name, namespace, **kwargs):
self.name = name
self.deploy_name = f'{name}-deploy'
self.namespace = namespace
self.kwargs = kwargs
Property
Return the AppsV1Api client instance. Here because the function may be called more than once, after the first call, the client instance is saved in the attribute _api_client.
@property
def api_client(self):
if not getattr(self, '_api_client', None):
self._api_client = client.AppsV1Api()
return self._api_client
Here returns the label selector we will use to filter the deployments
@property
def label_selector(self):
return f'underneathall-app = nginx-{self.name}'
Here returns the labels we will use to create deployments.
@property
def labels(self):
return {
'underneathall-app': f'nginx-{self.name}',
'app': 'underneathall'
}
Here returns the body we will send to api server to create or update deployments.
@property
def body(self):
return {
'kind': 'Deployment',
'apiVersion': 'apps/v1',
'metadata': {
'labels': self.labels,
'namespace': self.namespace,
'name': self.deploy_name
},
'spec': {
'selector': {
'matchLabels': self.labels
},
'replicas': self.kwargs.get('replicas', 1),
'template': {
'metadata': {
'labels': self.labels
},
'spec': {
'containers': [
{
'image': 'nginx:1.7.9',
'name': 'nginx',
'ports': [
{'containerPort': 80}
]
}
]
}
}
}
}
Check if the deployment already exists in the cluster.
@property
def exists(self):
if not getattr(self, '_deploy', None):
deploys = self.api_client.list_namespaced_deployment(
namespace=self.namespace,
label_selector=self.label_selector)
if len(deploys.items) == 0:
return False
self._deploy = deploys.items[0]
return True
Create, Update and Delete
Depends on the event_type, the operator will perform different actions.
Here, because our codes are pretty simple, we implement the logics in just on function sync.
def sync(self, event_type="ADDED"):
event_type = event_type.upper()
if event_type not in ["ADDED", "MODIFIED", "DELETED"]:
print(f'Invalid event type received: {event_type}')
return False
if event_type in ["ADDED", "MODIFIED"] and not self.exists:
ret = self.api_client.create_namespaced_deployment(
namespace=self.namespace,
body=self.body
)
elif event_type == "MODIFIED":
ret = self.api_client.replace_namespaced_deployment(
name=self.deploy_name,
namespace=self.namespace,
body=self.body
)
elif event_type == "DELETED" and self.exists:
ret = self.api_client.delete_namespaced_deployment(
name=self.deploy_name,
namespace=self.namespace
)
return True
2. operator.py¶
Handle Event
For each event,
def handle_event(event):
nginx = Nginx(
name=event['object']['metadata']['name'],
namespace=event['object']['metadata']['namespace'],
replicas=event['object']['spec']['replicas']
)
nginx.sync(event_type=event['type'])