|
| 1 | +import datetime |
1 | 2 | import logging
|
2 | 3 | import unittest
|
| 4 | +from typing import Tuple, Callable, Union |
3 | 5 | from unittest.mock import Mock
|
4 | 6 |
|
5 | 7 | from kubernetes.client import V1ObjectMeta
|
6 |
| -from kubernetes_utils import get_ns_list |
| 8 | + |
| 9 | +from consts import CREATE_BY_ANNOTATION, LAST_SYNC_ANNOTATION, VERSION_ANNOTATION, BLACK_LISTED_ANNOTATIONS, \ |
| 10 | + BLACK_LISTED_LABELS, CREATE_BY_AUTHOR, CLUSTER_SECRET_LABEL |
| 11 | +from kubernetes_utils import get_ns_list, create_secret_metadata |
| 12 | +from os_utils import get_version |
7 | 13 |
|
8 | 14 | USER_NAMESPACE_COUNT = 10
|
9 | 15 | initial_namespaces = ['default', 'kube-node-lease', 'kube-public', 'kube-system']
|
10 | 16 | user_namespaces = [f'example-{index}' for index in range(USER_NAMESPACE_COUNT)]
|
11 | 17 |
|
12 | 18 |
|
| 19 | +def is_iso_format(date: str) -> bool: |
| 20 | + """check whether a date string parses correctly as ISO 8601 format""" |
| 21 | + try: |
| 22 | + datetime.datetime.fromisoformat(date) |
| 23 | + return True |
| 24 | + except (TypeError, ValueError): |
| 25 | + return False |
| 26 | + |
| 27 | + |
13 | 28 | class TestClusterSecret(unittest.TestCase):
|
14 | 29 |
|
15 | 30 | def test_get_ns_list(self):
|
@@ -69,3 +84,87 @@ def test_get_ns_list(self):
|
69 | 84 | )),
|
70 | 85 | msg=case['name'],
|
71 | 86 | )
|
| 87 | + |
| 88 | + def test_create_secret_metadata(self) -> None: |
| 89 | + |
| 90 | + expected_base_label_key = CLUSTER_SECRET_LABEL |
| 91 | + expected_base_label_value = 'true' |
| 92 | + |
| 93 | + # key, value pairs, where the value can be a string or validation function |
| 94 | + expected_base_annotations: list[Tuple[str, Union[str, Callable[[str], bool]]]] = [ |
| 95 | + (CREATE_BY_ANNOTATION, CREATE_BY_AUTHOR), |
| 96 | + (VERSION_ANNOTATION, get_version()), |
| 97 | + # Since LAST_SYNC_ANNOTATION is a date string which isn't easily validated by string equality |
| 98 | + # have the function 'is_iso_format' validate the value of this annotation. |
| 99 | + (LAST_SYNC_ANNOTATION, is_iso_format) |
| 100 | + ] |
| 101 | + |
| 102 | + attributes_black_lists = dict( |
| 103 | + labels=BLACK_LISTED_LABELS, |
| 104 | + annotations=BLACK_LISTED_ANNOTATIONS, |
| 105 | + ) |
| 106 | + |
| 107 | + test_cases: list[Tuple[dict[str, str], dict[str, str]]] = [ |
| 108 | + # Annotations, Labels |
| 109 | + ( |
| 110 | + {}, |
| 111 | + {} |
| 112 | + ), |
| 113 | + ( |
| 114 | + {}, |
| 115 | + {"modifiedAt": "1692462880", |
| 116 | + "name": "prometheus-operator", |
| 117 | + "owner": "helm", |
| 118 | + "status": "superseded", |
| 119 | + "version": "1"} |
| 120 | + ), |
| 121 | + ( |
| 122 | + {"managed-by": "argocd.argoproj.io"}, |
| 123 | + {"argocd.argoproj.io/secret-type": "repository"} |
| 124 | + ), |
| 125 | + ( |
| 126 | + {"argocd.argoproj.io/compare-options": "ServerSideDiff=true", |
| 127 | + "argocd.argoproj.io/sync-wave": "4"}, |
| 128 | + {"app.kubernetes.io/instance": "cluster-secret"} |
| 129 | + ) |
| 130 | + ] |
| 131 | + |
| 132 | + for annotations, labels in test_cases: |
| 133 | + |
| 134 | + subject: V1ObjectMeta = create_secret_metadata( |
| 135 | + name='test_secret', |
| 136 | + namespace='test_namespace', |
| 137 | + annotations=annotations, |
| 138 | + labels=labels |
| 139 | + ) |
| 140 | + |
| 141 | + self.assertIsInstance(obj=subject, cls=V1ObjectMeta, msg='returned value has correct type') |
| 142 | + |
| 143 | + for attribute, black_list in attributes_black_lists.items(): |
| 144 | + attribute_object = subject.__getattribute__(attribute) |
| 145 | + self.assertIsNotNone(obj=attribute_object, msg=f'attribute "{attribute}" is not None') |
| 146 | + |
| 147 | + for key in attribute_object.keys(): |
| 148 | + self.assertIsInstance(obj=key, cls=str, msg=f'the {attribute} key is a string') |
| 149 | + for black_listed_label_prefix in black_list: |
| 150 | + self.assertFalse( |
| 151 | + expr=key.startswith(black_listed_label_prefix), |
| 152 | + msg=f'{attribute} key does not match black listed prefix' |
| 153 | + ) |
| 154 | + |
| 155 | + # This tells mypy that those attributes are not None |
| 156 | + assert subject.labels is not None |
| 157 | + assert subject.annotations is not None |
| 158 | + |
| 159 | + self.assertEqual( |
| 160 | + first=subject.labels[expected_base_label_key], |
| 161 | + second=expected_base_label_value, |
| 162 | + msg='expected base label is present' |
| 163 | + ) |
| 164 | + for key, value_expectation in expected_base_annotations: |
| 165 | + validator = value_expectation if callable(value_expectation) else value_expectation.__eq__ |
| 166 | + value = subject.annotations[key] |
| 167 | + self.assertTrue( |
| 168 | + expr=validator(value), |
| 169 | + msg=f'expected base annotation with key {key} is present and its value {value} is as expected' |
| 170 | + ) |
0 commit comments