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'])