k8s - add label_selectors options (#158)

k8s - add label_selectors options

SUMMARY
k8s now support label_selectors options same as k8s_info

Resolves #43

ISSUE TYPE


Feature Pull Request


COMPONENT NAME

k8s

Reviewed-by: Mike Graves <mgraves@redhat.com>
Reviewed-by: None <None>
This commit is contained in:
abikouo
2021-07-29 11:56:34 +02:00
committed by GitHub
parent abd2abb33e
commit 4b682666f1
11 changed files with 982 additions and 15 deletions

View File

@@ -0,0 +1,3 @@
---
minor_changes:
- k8s - add support for label_selectors options (https://github.com/ansible-collections/kubernetes.core/issues/43).

View File

@@ -164,6 +164,14 @@
tags:
- always
- name: Include label_selectors.yml
include_tasks:
file: tasks/label_selectors.yml
apply:
tags: [ label_selectors, k8s ]
tags:
- always
- name: Include diff.yml
include_tasks:
file: tasks/diff.yml

View File

@@ -0,0 +1,657 @@
---
- block:
- set_fact:
selector_namespace: "selector"
selector_pod_delete: "pod-selector-delete"
selector_pod_apply: "pod-selector-apply"
selector_pod_create:
- "pod-selector-apply-00"
- "pod-selector-apply-01"
- "pod-selector-apply-02"
- "pod-selector-apply-03"
- name: Ensure namespace selector
k8s:
kind: namespace
name: '{{ selector_namespace }}'
# Resource deletion using label selector (equality-based requirement)
- name: Create simple pod
k8s:
namespace: '{{ selector_namespace }}'
definition:
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_delete }}-00'
labels:
ansible.dev/team: "cloud"
ansible.release/version: upstream
ansible.dev/test: "true"
spec:
containers:
- name: c0
image: busybox
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
- name: Delete all resource using selector
k8s:
state: absent
kind: Pod
namespace: '{{ selector_namespace }}'
label_selectors:
- ansible.dev/team=cloud
wait: yes
wait_timeout: 180
- name: Ensure resources have been deleted
k8s_info:
kind: Pod
namespace: '{{ selector_namespace }}'
label_selectors:
- ansible.dev/team=cloud
register: result
- assert:
that:
- result.resources == []
# Resource deletion using label selector (set-based requirement)
- name: Create simple pod
k8s:
namespace: '{{ selector_namespace }}'
definition:
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_delete }}-01'
labels:
environment: production
spec:
containers:
- name: c0
image: alpine:3.14.0
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
- name: Delete all resource using selector
k8s:
state: absent
kind: Pod
namespace: '{{ selector_namespace }}'
label_selectors:
- environment in (test, qa)
wait: yes
wait_timeout: 180
register: result
- name: check that no resources were deleted
assert:
that:
- result is not changed
- name: Ensure resources have not been deleted
k8s_info:
kind: Pod
namespace: '{{ selector_namespace }}'
label_selectors:
- environment in (production)
register: result
- assert:
that:
- result.resources | list | length > 0
- name: Delete all resource using selector
k8s:
state: absent
kind: Pod
namespace: '{{ selector_namespace }}'
label_selectors:
- environment in (production)
wait: yes
wait_timeout: 180
register: result
- name: check result is changed
assert:
that:
- result is changed
- name: Ensure resources have not been deleted
k8s_info:
kind: Pod
namespace: '{{ selector_namespace }}'
label_selectors:
- environment in (production)
register: result
- assert:
that:
- result.resources | list | length == 0
# Resource creation using label selector
- name: Create simple pod using label_selectors option (equality-based requirement)
k8s:
namespace: '{{ selector_namespace }}'
label_selectors:
- container.image=fedora
definition: |
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_create[0] }}'
labels:
container.image: busybox
spec:
containers:
- name: c0
image: busybox
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_create[1] }}'
labels:
container.image: alpine
spec:
containers:
- name: c0
image: alpine
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_create[2] }}'
labels:
container.image: python
release: dev
spec:
containers:
- name: c0
image: python
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_create[3] }}'
labels:
container.image: python
release: dev
spec:
containers:
- name: c0
image: python
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
register: result
- assert:
that:
- result is not changed
- name: Create simple pod using label_selectors option
k8s:
namespace: '{{ selector_namespace }}'
label_selectors:
- container.image==alpine
definition: |
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_create[0] }}'
labels:
container.image: busybox
spec:
containers:
- name: c0
image: busybox
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_create[1] }}'
labels:
container.image: alpine
spec:
containers:
- name: c0
image: alpine
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_create[2] }}'
labels:
container.image: python
environment: test
spec:
containers:
- name: c0
image: python
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_create[3] }}'
labels:
container.image: python
environment: production
spec:
containers:
- name: c0
image: python
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
register: result
- assert:
that:
- result is changed
- name: list pod created
k8s_info:
namespace: '{{ selector_namespace }}'
kind: Pod
label_selectors:
- container.image
register: pod_created
- name: Validate that pod with matching label was created
assert:
that:
- pods_created | length == 1
- selector_pod_create[1] in pods_created
vars:
pods_created: '{{ pod_created.resources | map(attribute="metadata.name") | list }}'
- name: Create simple pod using label_selectors option (set-based requirement)
k8s:
namespace: '{{ selector_namespace }}'
label_selectors:
- "!environment"
definition: |
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_create[0] }}'
labels:
container.image: busybox
spec:
containers:
- name: c0
image: busybox
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_create[1] }}'
labels:
container.image: alpine
spec:
containers:
- name: c0
image: alpine
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_create[2] }}'
labels:
container.image: python
environment: test
spec:
containers:
- name: c0
image: python
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_create[3] }}'
labels:
container.image: python
environment: production
spec:
containers:
- name: c0
image: python
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
register: result
- assert:
that:
- result is changed
- name: list pod created
k8s_info:
namespace: '{{ selector_namespace }}'
kind: Pod
label_selectors:
- container.image
register: pod_created
- name: Validate that pod with matching label was created
assert:
that:
- pods_created | length == 2
- selector_pod_create[0] in pods_created
vars:
pods_created: '{{ pod_created.resources | map(attribute="metadata.name") | list }}'
- name: Create simple pod using label_selectors option (set-based requirement)
k8s:
namespace: '{{ selector_namespace }}'
label_selectors:
- environment in (test)
definition: |
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_create[0] }}'
labels:
container.image: busybox
spec:
containers:
- name: c0
image: busybox
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_create[1] }}'
labels:
container.image: alpine
spec:
containers:
- name: c0
image: alpine
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_create[2] }}'
labels:
container.image: python
environment: test
spec:
containers:
- name: c0
image: python
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_create[3] }}'
labels:
container.image: python
environment: production
spec:
containers:
- name: c0
image: python
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
register: result
- assert:
that:
- result is changed
- name: list pod created
k8s_info:
namespace: '{{ selector_namespace }}'
kind: Pod
label_selectors:
- container.image
register: pod_created
- name: Validate that pod with matching label was created
assert:
that:
- pods_created | length == 3
- selector_pod_create[2] in pods_created
vars:
pods_created: '{{ pod_created.resources | map(attribute="metadata.name") | list }}'
- name: Create simple pod using label_selectors option (set-based requirement)
k8s:
namespace: '{{ selector_namespace }}'
label_selectors:
- environment notin (test)
definition: |
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_create[0] }}'
labels:
container.image: busybox
spec:
containers:
- name: c0
image: busybox
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_create[1] }}'
labels:
container.image: alpine
spec:
containers:
- name: c0
image: alpine
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_create[2] }}'
labels:
container.image: python
environment: test
spec:
containers:
- name: c0
image: python
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
---
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_create[3] }}'
labels:
container.image: python
environment: production
spec:
containers:
- name: c0
image: python
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
register: result
- assert:
that:
- result is changed
- name: list pod created
k8s_info:
namespace: '{{ selector_namespace }}'
kind: Pod
label_selectors:
- container.image
register: pod_created
- name: Validate that pod with matching label was created
assert:
that:
- pods_created | length == 4
- selector_pod_create[3] in pods_created
vars:
pods_created: '{{ pod_created.resources | map(attribute="metadata.name") | list }}'
# Resource update using apply
- name: Create simple pod using apply
k8s:
namespace: '{{ selector_namespace }}'
apply: yes
definition:
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_apply }}'
labels:
environment: test
spec:
containers:
- name: c0
image: busybox:1.31.0
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
- name: Apply new pod definition using label_selectors (no match)
k8s:
namespace: '{{ selector_namespace }}'
apply: yes
definition:
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_apply }}'
labels:
environment: test
spec:
containers:
- name: c0
image: busybox:1.33.0
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
label_selectors:
- environment=qa
register: result
- name: check task output
assert:
that:
- result is not changed
- '"filtered by label_selectors" in result.msg'
- name: Apply new pod definition using label_selectors
k8s:
namespace: '{{ selector_namespace }}'
apply: yes
definition:
apiVersion: v1
kind: Pod
metadata:
name: '{{ selector_pod_apply }}'
labels:
environment: test
spec:
containers:
- name: c0
image: busybox:1.33.0
command:
- /bin/sh
- -c
- while true;do date;sleep 5; done
label_selectors:
- environment!=qa
register: result
- name: check task output
assert:
that:
- result is changed
always:
- name: Ensure namespace is deleted
k8s:
kind: Namespace
name: '{{ selector_namespace }}'
state: absent
ignore_errors: true

View File

@@ -29,6 +29,7 @@ from distutils.version import LooseVersion
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (AUTH_ARG_MAP, AUTH_ARG_SPEC, AUTH_PROXY_HEADERS_SPEC)
from ansible_collections.kubernetes.core.plugins.module_utils.hashes import generate_hash
from ansible_collections.kubernetes.core.plugins.module_utils.selector import LabelSelectorFilter
from ansible.module_utils.basic import missing_required_lib
from ansible.module_utils.six import iteritems, string_types
@@ -349,7 +350,7 @@ class K8sAnsibleMixin(object):
def fail(self, msg=None):
self.fail_json(msg=msg)
def _wait_for(self, resource, name, namespace, predicate, sleep, timeout, state):
def _wait_for(self, resource, name, namespace, predicate, sleep, timeout, state, label_selectors):
start = datetime.now()
def _wait_for_elapsed():
@@ -358,7 +359,10 @@ class K8sAnsibleMixin(object):
response = None
while _wait_for_elapsed() < timeout:
try:
response = resource.get(name=name, namespace=namespace)
params = dict(name=name, namespace=namespace)
if label_selectors:
params['label_selector'] = ','.join(label_selectors)
response = resource.get(**params)
if predicate(response):
if response:
return True, response.to_dict(), _wait_for_elapsed()
@@ -371,7 +375,7 @@ class K8sAnsibleMixin(object):
response = response.to_dict()
return False, response, _wait_for_elapsed()
def wait(self, resource, definition, sleep, timeout, state='present', condition=None):
def wait(self, resource, definition, sleep, timeout, state='present', condition=None, label_selectors=None):
def _deployment_ready(deployment):
# FIXME: frustratingly bool(deployment.status) is True even if status is empty
@@ -420,7 +424,7 @@ class K8sAnsibleMixin(object):
return False
def _resource_absent(resource):
return not resource
return not resource or (resource.kind.endswith('List') and resource.items == [])
waiter = dict(
Deployment=_deployment_ready,
@@ -428,13 +432,13 @@ class K8sAnsibleMixin(object):
Pod=_pod_ready
)
kind = definition['kind']
if state == 'present' and not condition:
predicate = waiter.get(kind, lambda x: x)
elif state == 'present' and condition:
predicate = _custom_condition
if state == 'present':
predicate = waiter.get(kind, lambda x: x) if not condition else _custom_condition
else:
predicate = _resource_absent
return self._wait_for(resource, definition['metadata']['name'], definition['metadata'].get('namespace'), predicate, sleep, timeout, state)
name = definition['metadata']['name']
namespace = definition['metadata'].get('namespace')
return self._wait_for(resource, name, namespace, predicate, sleep, timeout, state, label_selectors)
def set_resource_definitions(self, module):
resource_definition = module.params.get('resource_definition')
@@ -575,6 +579,7 @@ class K8sAnsibleMixin(object):
wait_timeout = self.params.get('wait_timeout')
wait_condition = None
continue_on_error = self.params.get('continue_on_error')
label_selectors = self.params.get('label_selectors')
if self.params.get('wait_condition') and self.params['wait_condition'].get('type'):
wait_condition = self.params['wait_condition']
@@ -591,6 +596,8 @@ class K8sAnsibleMixin(object):
params = dict(name=name)
if namespace:
params['namespace'] = namespace
if label_selectors:
params['label_selector'] = ','.join(label_selectors)
existing = resource.get(**params)
except (NotFoundError, MethodNotAllowedError):
# Remove traceback so that it doesn't show up in later failures
@@ -625,7 +632,13 @@ class K8sAnsibleMixin(object):
if state == 'absent':
result['method'] = "delete"
if not existing:
def _empty_resource_list():
if existing and existing.kind.endswith('List'):
return existing.items == []
return False
if not existing or _empty_resource_list():
# The object already does not exist
return result
else:
@@ -651,7 +664,7 @@ class K8sAnsibleMixin(object):
else:
self.fail_json(msg=build_error_msg(definition['kind'], origin_name, msg), error=exc.status, status=exc.status, reason=exc.reason)
if wait:
success, resource, duration = self.wait(resource, definition, wait_sleep, wait_timeout, 'absent')
success, resource, duration = self.wait(resource, definition, wait_sleep, wait_timeout, 'absent', label_selectors=label_selectors)
result['duration'] = duration
if not success:
msg = "Resource deletion timed out"
@@ -663,6 +676,13 @@ class K8sAnsibleMixin(object):
return result
else:
if label_selectors:
filter_selector = LabelSelectorFilter(label_selectors)
if not filter_selector.isMatching(definition):
result['changed'] = False
result['msg'] = "resource 'kind={kind},name={name},namespace={namespace}' filtered by label_selectors.".format(
kind=definition['kind'], name=origin_name, namespace=namespace)
return result
if apply:
if self.check_mode:
ignored, patch = apply_object(resource, _encode_stringdata(definition))

View File

@@ -0,0 +1,71 @@
# Copyright [2021] [Red Hat, Inc.]
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import re
class Selector(object):
equality_based_operators = ('==', '!=', '=')
def __init__(self, data):
self._operator = None
self._data = None
if not self.parse_set_based_requirement(data):
no_whitespace_data = data.replace(" ", "")
for op in self.equality_based_operators:
idx = no_whitespace_data.find(op)
if idx != -1:
self._operator = "in" if op == '==' or op == '=' else "notin"
self._key = no_whitespace_data[0:idx]
self._data = [no_whitespace_data[idx + len(op):]]
break
def parse_set_based_requirement(self, data):
m = re.match(r'( *)([a-z0-9A-Z][a-z0-9A-Z\._-]*[a-z0-9A-Z])( +)(notin|in)( +)\((.*)\)( *)', data)
if m:
self._set_based_requirement = True
self._key = m.group(2)
self._operator = m.group(4)
self._data = [x.replace(' ', '') for x in m.group(6).split(',') if x != '']
return True
elif all([x not in data for x in self.equality_based_operators]):
self._key = data.rstrip(" ").lstrip(" ")
if self._key.startswith("!"):
self._key = self._key[1:].lstrip(" ")
self._operator = "!"
return True
return False
def isMatch(self, labels):
if self._operator == "in":
return self._key in labels and labels.get(self._key) in self._data
elif self._operator == "notin":
return self._key not in labels or labels.get(self._key) not in self._data
else:
return self._key not in labels if self._operator == "!" else self._key in labels
class LabelSelectorFilter(object):
def __init__(self, label_selectors):
self.selectors = [Selector(data) for data in label_selectors]
def isMatching(self, definition):
if "metadata" not in definition or "labels" not in definition['metadata']:
return False
labels = definition['metadata']['labels']
if not isinstance(labels, dict):
return None
return all([sel.isMatch(labels) for sel in self.selectors])

View File

@@ -135,6 +135,12 @@ options:
type: bool
default: False
version_added: 2.0.0
label_selectors:
description:
- Selector (label query) to filter on.
type: list
elements: str
version_added: 2.2.0
requirements:
- "python >= 3.6"
@@ -335,6 +341,7 @@ def argspec():
argument_spec['continue_on_error'] = dict(type='bool', default=False)
argument_spec['state'] = dict(default='present', choices=['present', 'absent', 'patched'])
argument_spec['force'] = dict(type='bool', default=False)
argument_spec['label_selectors'] = dict(type='list', elements='str')
return argument_spec

View File

@@ -240,4 +240,8 @@ plugins/modules/k8s_cp.py import-2.6!skip
plugins/modules/k8s_cp.py import-2.7!skip
molecule/default/roles/k8scopy/library/kubectl_file_compare.py compile-2.6!skip
molecule/default/roles/k8scopy/library/kubectl_file_compare.py compile-2.7!skip
molecule/default/roles/k8scopy/library/kubectl_file_compare.py compile-3.5!skip
molecule/default/roles/k8scopy/library/kubectl_file_compare.py compile-3.5!skip
tests/unit/module_utils/test_selector.py future-import-boilerplate!skip
tests/unit/module_utils/test_selector.py metaclass-boilerplate!skip
plugins/module_utils/selector.py future-import-boilerplate!skip
plugins/module_utils/selector.py metaclass-boilerplate!skip

View File

@@ -240,4 +240,8 @@ plugins/modules/k8s_cp.py import-2.6!skip
plugins/modules/k8s_cp.py import-2.7!skip
molecule/default/roles/k8scopy/library/kubectl_file_compare.py compile-2.6!skip
molecule/default/roles/k8scopy/library/kubectl_file_compare.py compile-2.7!skip
molecule/default/roles/k8scopy/library/kubectl_file_compare.py compile-3.5!skip
molecule/default/roles/k8scopy/library/kubectl_file_compare.py compile-3.5!skip
tests/unit/module_utils/test_selector.py future-import-boilerplate!skip
tests/unit/module_utils/test_selector.py metaclass-boilerplate!skip
plugins/module_utils/selector.py future-import-boilerplate!skip
plugins/module_utils/selector.py metaclass-boilerplate!skip

View File

@@ -238,4 +238,6 @@ tests/sanity/refresh_ignore_files shebang!skip
plugins/modules/k8s_cp.py compile-2.6!skip
plugins/modules/k8s_cp.py compile-2.7!skip
plugins/modules/k8s_cp.py import-2.6!skip
plugins/modules/k8s_cp.py import-2.7!skip
plugins/modules/k8s_cp.py import-2.7!skip
plugins/module_utils/selector.py future-import-boilerplate!skip
plugins/module_utils/selector.py metaclass-boilerplate!skip

View File

@@ -234,4 +234,8 @@ plugins/modules/k8s_cp.py import-2.6!skip
plugins/modules/k8s_cp.py import-2.7!skip
molecule/default/roles/k8scopy/library/kubectl_file_compare.py compile-2.6!skip
molecule/default/roles/k8scopy/library/kubectl_file_compare.py compile-2.7!skip
molecule/default/roles/k8scopy/library/kubectl_file_compare.py compile-3.5!skip
molecule/default/roles/k8scopy/library/kubectl_file_compare.py compile-3.5!skip
tests/unit/module_utils/test_selector.py future-import-boilerplate!skip
tests/unit/module_utils/test_selector.py metaclass-boilerplate!skip
plugins/module_utils/selector.py future-import-boilerplate!skip
plugins/module_utils/selector.py metaclass-boilerplate!skip

View File

@@ -0,0 +1,187 @@
# Copyright [2021] [Red Hat, Inc.]
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from ansible_collections.kubernetes.core.plugins.module_utils.selector import LabelSelectorFilter, Selector
prod_definition = {
'apiVersion': 'v1',
'kind': 'Pod',
'metadata': {
'name': 'test',
'labels': {
'environment': 'production',
'app': 'nginx',
}
},
'spec': {
'containers': [
{'name': 'nginx', 'image': 'nginx:1.14.2', 'command': ['/bin/sh', '-c', 'sleep 10']}
]
}
}
no_label_definition = {
'apiVersion': 'v1',
'kind': 'Pod',
'metadata': {
'name': 'test',
'labels': {}
},
'spec': {
'containers': [
{'name': 'nginx', 'image': 'nginx:1.14.2', 'command': ['/bin/sh', '-c', 'sleep 10']}
]
}
}
test_definition = {
'apiVersion': 'v1',
'kind': 'Pod',
'metadata': {
'name': 'test',
'labels': {
'environment': 'test',
'app': 'nginx',
}
},
'spec': {
'containers': [
{'name': 'nginx', 'image': 'nginx:1.15.2', 'command': ['/bin/sh', '-c', 'sleep 10']}
]
}
}
def test_selector_parser():
f_selector = "environment==true"
sel = Selector(f_selector)
assert sel._operator == "in" and sel._data == ["true"] and sel._key == "environment"
f_selector = "environment=true"
sel = Selector(f_selector)
assert sel._operator == "in" and sel._data == ["true"] and sel._key == "environment"
f_selector = " environment == true "
sel = Selector(f_selector)
assert sel._operator == "in" and sel._data == ["true"] and sel._key == "environment"
f_selector = "environment!=false"
sel = Selector(f_selector)
assert sel._operator == "notin" and sel._data == ["false"] and sel._key == "environment"
f_selector = "environment notin (true, false)"
sel = Selector(f_selector)
assert sel._operator == "notin" and "true" in sel._data and "false" in sel._data and sel._key == "environment"
f_selector = "environment in (true, false)"
sel = Selector(f_selector)
assert sel._operator == "in" and "true" in sel._data and "false" in sel._data and sel._key == "environment"
f_selector = "environmentin(true, false)"
sel = Selector(f_selector)
assert not sel._operator and not sel._data and sel._key == f_selector
f_selector = "environment notin (true, false"
sel = Selector(f_selector)
assert not sel._operator and not sel._data and sel._key == f_selector
f_selector = "!environment"
sel = Selector(f_selector)
assert sel._operator == "!" and not sel._data and sel._key == "environment"
f_selector = "! environment "
sel = Selector(f_selector)
assert sel._operator == "!" and not sel._data and sel._key == "environment"
def test_label_selector_without_operator():
label_selector = ['environment', 'app']
assert LabelSelectorFilter(label_selector).isMatching(prod_definition)
assert not LabelSelectorFilter(label_selector).isMatching(no_label_definition)
assert LabelSelectorFilter(label_selector).isMatching(test_definition)
def test_label_selector_equal_operator():
label_selector = ['environment==test']
assert not LabelSelectorFilter(label_selector).isMatching(prod_definition)
assert not LabelSelectorFilter(label_selector).isMatching(no_label_definition)
assert LabelSelectorFilter(label_selector).isMatching(test_definition)
label_selector = ['environment=production']
assert LabelSelectorFilter(label_selector).isMatching(prod_definition)
assert not LabelSelectorFilter(label_selector).isMatching(no_label_definition)
assert not LabelSelectorFilter(label_selector).isMatching(test_definition)
label_selector = ['environment=production', 'app==mongodb']
assert not LabelSelectorFilter(label_selector).isMatching(prod_definition)
assert not LabelSelectorFilter(label_selector).isMatching(no_label_definition)
assert not LabelSelectorFilter(label_selector).isMatching(test_definition)
label_selector = ['environment=production', 'app==nginx']
assert LabelSelectorFilter(label_selector).isMatching(prod_definition)
assert not LabelSelectorFilter(label_selector).isMatching(no_label_definition)
assert not LabelSelectorFilter(label_selector).isMatching(test_definition)
label_selector = ['environment', 'app==nginx']
assert LabelSelectorFilter(label_selector).isMatching(prod_definition)
assert not LabelSelectorFilter(label_selector).isMatching(no_label_definition)
assert LabelSelectorFilter(label_selector).isMatching(test_definition)
def test_label_selector_notequal_operator():
label_selector = ['environment!=test']
assert LabelSelectorFilter(label_selector).isMatching(prod_definition)
assert LabelSelectorFilter(label_selector).isMatching(no_label_definition)
assert not LabelSelectorFilter(label_selector).isMatching(test_definition)
label_selector = ['environment!=production']
assert not LabelSelectorFilter(label_selector).isMatching(prod_definition)
assert LabelSelectorFilter(label_selector).isMatching(no_label_definition)
assert LabelSelectorFilter(label_selector).isMatching(test_definition)
label_selector = ['environment=production', 'app!=mongodb']
assert LabelSelectorFilter(label_selector).isMatching(prod_definition)
assert not LabelSelectorFilter(label_selector).isMatching(no_label_definition)
assert not LabelSelectorFilter(label_selector).isMatching(test_definition)
label_selector = ['environment=production', 'app!=nginx']
assert not LabelSelectorFilter(label_selector).isMatching(prod_definition)
assert not LabelSelectorFilter(label_selector).isMatching(no_label_definition)
assert not LabelSelectorFilter(label_selector).isMatching(test_definition)
label_selector = ['environment', 'app!=nginx']
assert not LabelSelectorFilter(label_selector).isMatching(prod_definition)
assert not LabelSelectorFilter(label_selector).isMatching(no_label_definition)
assert not LabelSelectorFilter(label_selector).isMatching(test_definition)
def test_label_selector_conflicting_definition():
label_selector = ['environment==test', 'environment!=test']
assert not LabelSelectorFilter(label_selector).isMatching(prod_definition)
assert not LabelSelectorFilter(label_selector).isMatching(no_label_definition)
assert not LabelSelectorFilter(label_selector).isMatching(test_definition)
label_selector = ['environment==test', 'environment==production']
assert not LabelSelectorFilter(label_selector).isMatching(prod_definition)
assert not LabelSelectorFilter(label_selector).isMatching(no_label_definition)
assert not LabelSelectorFilter(label_selector).isMatching(test_definition)
def test_set_based_requirement():
label_selector = ['environment in (production)']
assert LabelSelectorFilter(label_selector).isMatching(prod_definition)
assert not LabelSelectorFilter(label_selector).isMatching(no_label_definition)
assert not LabelSelectorFilter(label_selector).isMatching(test_definition)
label_selector = ['environment in (production, test)']
assert LabelSelectorFilter(label_selector).isMatching(prod_definition)
assert not LabelSelectorFilter(label_selector).isMatching(no_label_definition)
assert LabelSelectorFilter(label_selector).isMatching(test_definition)
label_selector = ['environment notin (production)']
assert not LabelSelectorFilter(label_selector).isMatching(prod_definition)
assert LabelSelectorFilter(label_selector).isMatching(no_label_definition)
assert LabelSelectorFilter(label_selector).isMatching(test_definition)
label_selector = ['environment notin (production, test)']
assert not LabelSelectorFilter(label_selector).isMatching(prod_definition)
assert LabelSelectorFilter(label_selector).isMatching(no_label_definition)
assert not LabelSelectorFilter(label_selector).isMatching(test_definition)
label_selector = ['environment']
assert LabelSelectorFilter(label_selector).isMatching(prod_definition)
assert not LabelSelectorFilter(label_selector).isMatching(no_label_definition)
assert LabelSelectorFilter(label_selector).isMatching(test_definition)
label_selector = ['!environment']
assert not LabelSelectorFilter(label_selector).isMatching(prod_definition)
assert LabelSelectorFilter(label_selector).isMatching(no_label_definition)
assert not LabelSelectorFilter(label_selector).isMatching(test_definition)