OpenShift/k8s Provider: Basic Support for k8s nodeSelectors

This adds support to specify node selectors on Pod node labels.
They are used by the k8s scheduler to place a Pod on specific nodes with
corresponding labels.
This allows to place a build node/Pod on k8s nodes with certain
capabilities (e.g. storage types, number of CPU cores, etc.)

Change-Id: Ic00a84181c8ef66189e4259ef6434dc62b81c3c6
This commit is contained in:
Benjamin Schanzel 2020-08-14 15:22:09 +02:00
parent 6ebac5fd17
commit 19be1a2e26
8 changed files with 65 additions and 14 deletions

View File

@ -1423,6 +1423,14 @@ Selecting the kubernetes driver adds the following options to the
The value of the environment variable passed to the Pod. The value of the environment variable passed to the Pod.
.. attr:: node-selector
:type: dict
Only used by the
:value:`providers.[kubernetes].pools.labels.type.pod` label type;
A map of key-value pairs to ensure the Kubernetes scheduler
places the Pod on a node with specific node labels.
Openshift Driver Openshift Driver
---------------- ----------------
@ -1594,6 +1602,14 @@ Selecting the openshift driver adds the following options to the
The value of the environment variable passed to the Pod. The value of the environment variable passed to the Pod.
.. attr:: node-selector
:type: dict
Only used by the
:value:`providers.[openshift].labels.type.pod` label type;
A map of key-value pairs to ensure the OpenShift scheduler
places the Pod on a node with specific node labels.
Openshift Pods Driver Openshift Pods Driver
--------------------- ---------------------
@ -1724,6 +1740,12 @@ Selecting the openshift pods driver adds the following options to the
The value of the environment variable passed to the Pod. The value of the environment variable passed to the Pod.
.. attr:: node-selector
:type: dict
A map of key-value pairs to ensure the OpenShift scheduler
places the Pod on a node with specific node labels.
AWS EC2 Driver AWS EC2 Driver
-------------- --------------

View File

@ -31,7 +31,8 @@ class KubernetesLabel(ConfigValue):
other.image == self.image and other.image == self.image and
other.cpu == self.cpu and other.cpu == self.cpu and
other.memory == self.memory and other.memory == self.memory and
other.env == self.env) other.env == self.env and
other.node_selector == self.node_selector)
return False return False
def __repr__(self): def __repr__(self):
@ -63,6 +64,7 @@ class KubernetesPool(ConfigPool):
pl.cpu = label.get('cpu') pl.cpu = label.get('cpu')
pl.memory = label.get('memory') pl.memory = label.get('memory')
pl.env = label.get('env', []) pl.env = label.get('env', [])
pl.node_selector = label.get('node-selector')
pl.pool = self pl.pool = self
self.labels[pl.name] = pl self.labels[pl.name] = pl
full_config.labels[label['name']].pools.append(self) full_config.labels[label['name']].pools.append(self)
@ -113,6 +115,7 @@ class KubernetesProviderConfig(ProviderConfig):
'cpu': int, 'cpu': int,
'memory': int, 'memory': int,
'env': [env_var], 'env': [env_var],
'node-selector': dict,
} }
pool = ConfigPool.getCommonSchemaDict() pool = ConfigPool.getCommonSchemaDict()

View File

@ -274,7 +274,7 @@ class KubernetesProvider(Provider, QuotaSupport):
return resource return resource
def createPod(self, node, pool, label): def createPod(self, node, pool, label):
spec_body = { container_body = {
'name': label.name, 'name': label.name,
'image': label.image, 'image': label.image,
'imagePullPolicy': label.image_pull, 'imagePullPolicy': label.image_pull,
@ -285,22 +285,27 @@ class KubernetesProvider(Provider, QuotaSupport):
} }
if label.cpu or label.memory: if label.cpu or label.memory:
spec_body['resources'] = {} container_body['resources'] = {}
for rtype in ('requests', 'limits'): for rtype in ('requests', 'limits'):
rbody = {} rbody = {}
if label.cpu: if label.cpu:
rbody['cpu'] = int(label.cpu) rbody['cpu'] = int(label.cpu)
if label.memory: if label.memory:
rbody['memory'] = '%dMi' % int(label.memory) rbody['memory'] = '%dMi' % int(label.memory)
spec_body['resources'][rtype] = rbody container_body['resources'][rtype] = rbody
spec_body = {
'containers': [container_body]
}
if label.node_selector:
spec_body['nodeSelector'] = label.node_selector
pod_body = { pod_body = {
'apiVersion': 'v1', 'apiVersion': 'v1',
'kind': 'Pod', 'kind': 'Pod',
'metadata': {'name': label.name}, 'metadata': {'name': label.name},
'spec': { 'spec': spec_body,
'containers': [spec_body],
},
'restartPolicy': 'Never', 'restartPolicy': 'Never',
} }

View File

@ -32,7 +32,8 @@ class OpenshiftLabel(ConfigValue):
other.cpu == self.cpu and other.cpu == self.cpu and
other.memory == self.memory and other.memory == self.memory and
other.python_path == self.python_path and other.python_path == self.python_path and
other.env == self.env) other.env == self.env and
other.node_selector == self.node_selector)
return False return False
def __repr__(self): def __repr__(self):
@ -64,6 +65,7 @@ class OpenshiftPool(ConfigPool):
pl.memory = label.get('memory') pl.memory = label.get('memory')
pl.python_path = label.get('python-path', 'auto') pl.python_path = label.get('python-path', 'auto')
pl.env = label.get('env', []) pl.env = label.get('env', [])
pl.node_selector = label.get('node-selector')
pl.pool = self pl.pool = self
self.labels[pl.name] = pl self.labels[pl.name] = pl
full_config.labels[label['name']].pools.append(self) full_config.labels[label['name']].pools.append(self)
@ -115,6 +117,7 @@ class OpenshiftProviderConfig(ProviderConfig):
'memory': int, 'memory': int,
'python-path': str, 'python-path': str,
'env': [env_var], 'env': [env_var],
'node-selector': dict,
} }
pool = ConfigPool.getCommonSchemaDict() pool = ConfigPool.getCommonSchemaDict()

View File

@ -204,7 +204,7 @@ class OpenshiftProvider(Provider):
def createPod(self, project, pod_name, label): def createPod(self, project, pod_name, label):
self.log.debug("%s: creating pod in project %s" % (pod_name, project)) self.log.debug("%s: creating pod in project %s" % (pod_name, project))
spec_body = { container_body = {
'name': label.name, 'name': label.name,
'image': label.image, 'image': label.image,
'imagePullPolicy': label.image_pull, 'imagePullPolicy': label.image_pull,
@ -214,23 +214,30 @@ class OpenshiftProvider(Provider):
'env': label.env, 'env': label.env,
} }
if label.cpu or label.memory: if label.cpu or label.memory:
spec_body['resources'] = {} container_body['resources'] = {}
for rtype in ('requests', 'limits'): for rtype in ('requests', 'limits'):
rbody = {} rbody = {}
if label.cpu: if label.cpu:
rbody['cpu'] = int(label.cpu) rbody['cpu'] = int(label.cpu)
if label.memory: if label.memory:
rbody['memory'] = '%dMi' % int(label.memory) rbody['memory'] = '%dMi' % int(label.memory)
spec_body['resources'][rtype] = rbody container_body['resources'][rtype] = rbody
spec_body = {
'containers': [container_body]
}
if label.node_selector:
spec_body['nodeSelector'] = label.node_selector
pod_body = { pod_body = {
'apiVersion': 'v1', 'apiVersion': 'v1',
'kind': 'Pod', 'kind': 'Pod',
'metadata': {'name': pod_name}, 'metadata': {'name': pod_name},
'spec': { 'spec': spec_body,
'containers': [spec_body],
},
'restartPolicy': 'Never', 'restartPolicy': 'Never',
} }
self.k8s_client.create_namespaced_pod(project, pod_body) self.k8s_client.create_namespaced_pod(project, pod_body)
def waitForPod(self, project, pod_name): def waitForPod(self, project, pod_name):

View File

@ -57,6 +57,7 @@ class OpenshiftPodsProviderConfig(OpenshiftProviderConfig):
'memory': int, 'memory': int,
'python-path': str, 'python-path': str,
'env': [env_var], 'env': [env_var],
'node-selector': dict
} }
pool = ConfigPool.getCommonSchemaDict() pool = ConfigPool.getCommonSchemaDict()

View File

@ -143,6 +143,8 @@ providers:
value: hello value: hello
- name: BAR - name: BAR
value: world value: world
node-selector:
storageType: ssd
- name: openshift - name: openshift
driver: openshift driver: openshift
@ -163,6 +165,8 @@ providers:
value: hello value: hello
- name: BAR - name: BAR
value: world value: world
node-selector:
storageType: ssd
- name: ec2-us-east-2 - name: ec2-us-east-2
driver: aws driver: aws

View File

@ -0,0 +1,6 @@
---
features:
- |
Basic support for specifying k8s/OpenShift `nodeSelectors` on Pod node
labels. This allows to schedule a Pod on k8s nodes with specific labels,
e.g., having certain capabilities.