Zack Williams | 0149cd2 | 2018-05-17 16:19:48 -0700 | [diff] [blame] | 1 | #!/usr/bin/env bash |
| 2 | |
| 3 | # Copyright 2018-present Open Networking Foundation |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | |
| 17 | # service_scaffold.sh |
| 18 | # creates directories and scaffolding for a service |
| 19 | |
| 20 | set -e -o pipefail |
| 21 | |
| 22 | LICENSE_TEXT=$(cat <<EOF |
| 23 | # Copyright $(date +%Y)-present Open Networking Foundation |
| 24 | # |
| 25 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 26 | # you may not use this file except in compliance with the License. |
| 27 | # You may obtain a copy of the License at |
| 28 | # |
| 29 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 30 | # |
| 31 | # Unless required by applicable law or agreed to in writing, software |
| 32 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 33 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 34 | # See the License for the specific language governing permissions and |
| 35 | # limitations under the License. |
| 36 | EOF |
| 37 | ) |
| 38 | |
| 39 | TPL_LICENSE_TEXT=$(cat <<EOF |
| 40 | {{/* |
| 41 | Copyright $(date +%Y)-present Open Networking Foundation |
| 42 | |
| 43 | Licensed under the Apache License, Version 2.0 (the "License"); |
| 44 | you may not use this file except in compliance with the License. |
| 45 | You may obtain a copy of the License at |
| 46 | |
| 47 | http://www.apache.org/licenses/LICENSE-2.0 |
| 48 | |
| 49 | Unless required by applicable law or agreed to in writing, software |
| 50 | distributed under the License is distributed on an "AS IS" BASIS, |
| 51 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 52 | See the License for the specific language governing permissions and |
| 53 | limitations under the License. |
| 54 | */}} |
| 55 | EOF |
| 56 | ) |
| 57 | |
| 58 | if [ -z "${SERVICE_NAME}" ]; |
| 59 | then |
| 60 | SERVICE_NAME=$(basename "${PWD}") |
| 61 | echo "SERVICE_NAME undefined, using parent directory name: ${SERVICE_NAME}" |
| 62 | fi |
| 63 | |
| 64 | if [ -z "${SERVICE_VERSION}" ]; |
| 65 | then |
| 66 | SERVICE_VERSION="0.0.1" |
| 67 | echo "SERVICE_VERSION undefined, using default: ${SERVICE_VERSION}" |
| 68 | fi |
| 69 | |
| 70 | echo "Creating service named: ${SERVICE_NAME}, version: ${SERVICE_VERSION}" |
| 71 | |
| 72 | echo "${SERVICE_VERSION}" > VERSION |
| 73 | |
| 74 | # create uppercase version of SERVICE_NAME in a naive fashion |
| 75 | UC_SERVICE_NAME=$(echo "${SERVICE_NAME}" | cut -c1 | tr '[:lower:]' '[:upper:]')$(echo "${SERVICE_NAME}" | cut -c2-) |
| 76 | |
| 77 | echo "Uppercase service name: ${UC_SERVICE_NAME}" |
| 78 | |
| 79 | echo "Creating README.md" |
| 80 | echo "# ${UC_SERVICE_NAME} Service" > README.md |
| 81 | |
| 82 | echo "Creating directories" |
| 83 | mkdir -p xos/synchronizer/model_policies/ |
| 84 | mkdir -p xos/synchronizer/models/ |
| 85 | mkdir -p xos/synchronizer/pull_steps/ |
| 86 | mkdir -p xos/synchronizer/steps/ |
| 87 | mkdir -p xos/synchronizer/tests/ |
| 88 | mkdir -p "helm-charts/${SERVICE_NAME}/templates" |
| 89 | |
| 90 | echo "Creating empty model-deps" |
| 91 | echo "{}" > xos/synchronizer/model-deps |
| 92 | |
| 93 | echo "Creating xproto scaffold" |
| 94 | cat << EOF > "xos/synchronizer/models/${SERVICE_NAME}.xproto" |
| 95 | option app_label = "${SERVICE_NAME}"; |
| 96 | option name = "${SERVICE_NAME}"; |
| 97 | |
| 98 | message ${UC_SERVICE_NAME}Service (Service){ |
| 99 | option verbose_name = "${UC_SERVICE_NAME} Service"; |
| 100 | } |
| 101 | |
| 102 | message ${UC_SERVICE_NAME}ServiceInstance (ServiceInstance){ |
| 103 | option owner_class_name = "${UC_SERVICE_NAME}Service"; |
| 104 | option verbose_name = "${UC_SERVICE_NAME} Service Instance"; |
| 105 | } |
| 106 | EOF |
| 107 | |
| 108 | echo "Creating unittest.cfg" |
| 109 | cat << EOF > "xos/unittest.cfg" |
| 110 | [unittest] |
| 111 | plugins=nose2.plugins.junitxml |
| 112 | code-directories=synchronizer |
| 113 | model_policies |
| 114 | steps |
| 115 | pull_steps |
| 116 | event_steps |
| 117 | EOF |
| 118 | |
| 119 | echo "Creating service config.yaml" |
| 120 | cat << EOF > "xos/synchronizer/config.yaml" |
| 121 | ${LICENSE_TEXT} |
| 122 | |
| 123 | name: ${SERVICE_NAME} |
| 124 | required_models: |
| 125 | - ${UC_SERVICE_NAME}Service |
| 126 | - ${UC_SERVICE_NAME}ServiceInstance |
| 127 | dependency_graph: "/opt/xos/synchronizers/${SERVICE_NAME}/model-deps" |
| 128 | model_policies_dir: "/opt/xos/synchronizers/${SERVICE_NAME}/model_policies" |
| 129 | models_dir: "/opt/xos/synchronizers/${SERVICE_NAME}/models" |
| 130 | pull_steps_dir: "/opt/xos/synchronizers/${SERVICE_NAME}/pull_steps" |
| 131 | steps_dir: "/opt/xos/synchronizers/${SERVICE_NAME}/steps" |
| 132 | sys_dir: "/opt/xos/synchronizers/${SERVICE_NAME}/sys" |
| 133 | EOF |
| 134 | |
| 135 | echo "Creating test_config.yaml" |
| 136 | cat << EOF > "xos/synchronizer/test_config.yaml" |
| 137 | ${LICENSE_TEXT} |
| 138 | |
| 139 | name: ${SERVICE_NAME}-testconfig |
| 140 | accessor: |
| 141 | username: xosadmin@opencord.org |
| 142 | password: "sample" |
| 143 | kind: "testframework" |
| 144 | logging: |
| 145 | version: 1 |
| 146 | handlers: |
| 147 | console: |
| 148 | class: logging.StreamHandler |
| 149 | loggers: |
| 150 | 'multistructlog': |
| 151 | handlers: |
| 152 | - console |
| 153 | EOF |
| 154 | |
| 155 | echo "Creating synchronizer.py" |
| 156 | cat << EOF > "xos/synchronizer/${SERVICE_NAME}-synchronizer.py" |
| 157 | #!/usr/bin/env python |
| 158 | |
| 159 | ${LICENSE_TEXT} |
| 160 | |
| 161 | """ |
| 162 | ${SERVICE_NAME}-synchronizer.py |
| 163 | This is the main entrypoint for the synchronizer. It loads the config file, and |
| 164 | then starts the synchronizer. |
| 165 | """ |
| 166 | |
| 167 | import importlib |
| 168 | import os |
| 169 | import sys |
| 170 | from xosconfig import Config |
| 171 | |
| 172 | config_file = os.path.abspath(os.path.dirname( |
| 173 | os.path.realpath(__file__)) + '/config.yaml') |
| 174 | |
| 175 | base_config_file = os.path.abspath(os.path.dirname( |
| 176 | os.path.realpath(__file__)) + '/config.yaml') |
| 177 | mounted_config_file = os.path.abspath(os.path.dirname( |
| 178 | os.path.realpath(__file__)) + '/mounted_config.yaml') |
| 179 | |
| 180 | if os.path.isfile(mounted_config_file): |
| 181 | Config.init(base_config_file, 'synchronizer-config-schema.yaml', |
| 182 | mounted_config_file) |
| 183 | else: |
| 184 | Config.init(base_config_file, 'synchronizer-config-schema.yaml') |
| 185 | |
| 186 | synchronizer_path = os.path.join(os.path.dirname( |
| 187 | os.path.realpath(__file__)), "../../synchronizers/new_base") |
| 188 | |
| 189 | sys.path.append(synchronizer_path) |
| 190 | mod = importlib.import_module("xos-synchronizer") |
| 191 | mod.main() |
| 192 | EOF |
| 193 | |
| 194 | chmod +x "xos/synchronizer/${SERVICE_NAME}-synchronizer.py" |
| 195 | |
| 196 | echo "Creating synchronizer Dockerfile" |
| 197 | cat << EOF > Dockerfile.synchronizer |
| 198 | ${LICENSE_TEXT} |
| 199 | |
| 200 | # xosproject/${SERVICE_NAME} |
| 201 | |
| 202 | FROM xosproject/xos-synchronizer-base:2.0.0 |
| 203 | |
| 204 | COPY xos/synchronizer /opt/xos/synchronizers/${SERVICE_NAME} |
| 205 | COPY VERSION /opt/xos/synchronizers/${SERVICE_NAME}/ |
| 206 | |
| 207 | ENTRYPOINT [] |
| 208 | |
| 209 | WORKDIR "/opt/xos/synchronizers/${SERVICE_NAME}" |
| 210 | |
| 211 | # Label image |
| 212 | ARG org_label_schema_schema_version=1.0 |
| 213 | ARG org_label_schema_name=${SERVICE_NAME} |
| 214 | ARG org_label_schema_version=unknown |
| 215 | ARG org_label_schema_vcs_url=unknown |
| 216 | ARG org_label_schema_vcs_ref=unknown |
| 217 | ARG org_label_schema_build_date=unknown |
| 218 | ARG org_opencord_vcs_commit_date=unknown |
| 219 | ARG org_opencord_component_chameleon_version=unknown |
| 220 | ARG org_opencord_component_chameleon_vcs_url=unknown |
| 221 | ARG org_opencord_component_chameleon_vcs_ref=unknown |
| 222 | ARG org_opencord_component_xos_version=unknown |
| 223 | ARG org_opencord_component_xos_vcs_url=unknown |
| 224 | ARG org_opencord_component_xos_vcs_ref=unknown |
| 225 | |
| 226 | LABEL org.label-schema.schema-version=\$org_label_schema_schema_version \\ |
| 227 | org.label-schema.name=\$org_label_schema_name \\ |
| 228 | org.label-schema.version=\$org_label_schema_version \\ |
| 229 | org.label-schema.vcs-url=\$org_label_schema_vcs_url \\ |
| 230 | org.label-schema.vcs-ref=\$org_label_schema_vcs_ref \\ |
| 231 | org.label-schema.build-date=\$org_label_schema_build_date \\ |
| 232 | org.opencord.vcs-commit-date=\$org_opencord_vcs_commit_date \\ |
| 233 | org.opencord.component.chameleon.version=\$org_opencord_component_chameleon_version \\ |
| 234 | org.opencord.component.chameleon.vcs-url=\$org_opencord_component_chameleon_vcs_url \\ |
| 235 | org.opencord.component.chameleon.vcs-ref=\$org_opencord_component_chameleon_vcs_ref \\ |
| 236 | org.opencord.component.xos.version=\$org_opencord_component_xos_version \\ |
| 237 | org.opencord.component.xos.vcs-url=\$org_opencord_component_xos_vcs_url \\ |
| 238 | org.opencord.component.xos.vcs-ref=\$org_opencord_component_xos_vcs_ref |
| 239 | |
| 240 | CMD ["/usr/bin/python", "/opt/xos/synchronizers/${SERVICE_NAME}/${SERVICE_NAME}-synchronizer.py"] |
| 241 | EOF |
| 242 | |
| 243 | echo "Creating sync step" |
| 244 | cat << EOF > "xos/synchronizer/steps/sync_${SERVICE_NAME}_serviceinstance.py" |
| 245 | ${LICENSE_TEXT} |
| 246 | |
| 247 | from synchronizers.new_base.syncstep import SyncStep |
| 248 | from synchronizers.new_base.modelaccessor import ${UC_SERVICE_NAME}ServiceInstance |
| 249 | |
| 250 | from xosconfig import Config |
| 251 | from multistructlog import create_logger |
| 252 | |
| 253 | log = create_logger(Config().get('logging')) |
| 254 | |
| 255 | |
| 256 | class Sync${UC_SERVICE_NAME}ServiceInstance(SyncStep): |
| 257 | """ |
| 258 | Sync${UC_SERVICE_NAME}ServiceInstance |
| 259 | Implements sync step for syncing ${UC_SERVICE_NAME} Services |
| 260 | """ |
| 261 | |
| 262 | provides = [${UC_SERVICE_NAME}ServiceInstance] |
| 263 | observes = [${UC_SERVICE_NAME}ServiceInstance] |
| 264 | requested_interval = 0 |
| 265 | |
| 266 | def sync_record(self, model): |
| 267 | log.info("Synchronizing ${UC_SERVICE_NAME}ServiceInstance", |
| 268 | object=str(model)) |
| 269 | # TODO: Implement sync step |
| 270 | |
| 271 | # Verify that the name is not empty, used in tests |
| 272 | if model.name != "": |
| 273 | model.save() |
| 274 | else: |
| 275 | raise Exception("Empty names aren't allowed") |
| 276 | |
| 277 | def delete_record(self, model): |
| 278 | log.info("Deleting ${UC_SERVICE_NAME}ServiceInstance", |
| 279 | object=str(model)) |
| 280 | # TODO: Implement delete step |
| 281 | EOF |
| 282 | |
| 283 | echo "Creating test for synchronizer of service instance" |
| 284 | cat << EOF > "xos/synchronizer/steps/test_sync_${SERVICE_NAME}_serviceinstance.py" |
| 285 | ${LICENSE_TEXT} |
| 286 | |
| 287 | import os |
| 288 | import sys |
| 289 | import unittest |
| 290 | from mock import Mock |
| 291 | |
| 292 | # Hack to load synchronizer framework |
| 293 | test_path = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) |
| 294 | |
| 295 | xos_dir = os.path.join(test_path, "../../..") |
| 296 | |
| 297 | if not os.path.exists(os.path.join(test_path, "new_base")): |
| 298 | xos_dir = os.path.join(test_path, "../../../../../../orchestration/xos/xos") |
Matteo Scandolo | e157680 | 2019-03-06 15:23:40 -0800 | [diff] [blame^] | 299 | services_dir = os.path.join(xos_dir, "../../xos-services") |
Zack Williams | 0149cd2 | 2018-05-17 16:19:48 -0700 | [diff] [blame] | 300 | |
| 301 | sys.path.append(xos_dir) |
| 302 | sys.path.append(os.path.join(xos_dir, 'synchronizers', 'new_base')) |
| 303 | # END Hack to load synchronizer framework |
| 304 | |
| 305 | |
| 306 | # generate model from xproto |
| 307 | def get_models_fn(service_name, xproto_name): |
| 308 | name = os.path.join(service_name, "xos", xproto_name) |
| 309 | if os.path.exists(os.path.join(services_dir, name)): |
| 310 | return name |
| 311 | else: |
| 312 | name = os.path.join(service_name, "xos", "synchronizer", "models", xproto_name) |
| 313 | if os.path.exists(os.path.join(services_dir, name)): |
| 314 | return name |
| 315 | raise Exception("Unable to find service=%s xproto=%s" % (service_name, xproto_name)) |
| 316 | # END generate model from xproto |
| 317 | |
| 318 | |
| 319 | class TestSync${UC_SERVICE_NAME}ServiceInstance(unittest.TestCase): |
| 320 | |
| 321 | def setUp(self): |
| 322 | |
| 323 | self.sys_path_save = sys.path |
| 324 | sys.path.append(xos_dir) |
| 325 | sys.path.append(os.path.join(xos_dir, 'synchronizers', 'new_base')) |
| 326 | |
| 327 | # Setting up the config module |
| 328 | from xosconfig import Config |
| 329 | config = os.path.join(test_path, "../test_config.yaml") |
| 330 | Config.clear() |
| 331 | Config.init(config, "synchronizer-config-schema.yaml") |
| 332 | # END Setting up the config module |
| 333 | |
| 334 | from synchronizers.new_base.mock_modelaccessor_build import build_mock_modelaccessor |
| 335 | build_mock_modelaccessor(xos_dir, services_dir, [ |
| 336 | get_models_fn("${SERVICE_NAME}", "${SERVICE_NAME}.xproto"), |
| 337 | ]) |
| 338 | |
| 339 | from synchronizers.new_base.modelaccessor import model_accessor |
| 340 | from sync_${SERVICE_NAME}_serviceinstance import Sync${UC_SERVICE_NAME}ServiceInstance |
| 341 | # import all class names to globals |
| 342 | for (k, v) in model_accessor.all_model_classes.items(): |
| 343 | globals()[k] = v |
| 344 | |
| 345 | self.sync_step = Sync${UC_SERVICE_NAME}ServiceInstance |
| 346 | |
| 347 | # create a mock instance instance |
| 348 | self.model = Mock() |
| 349 | self.model.name = "Example" |
| 350 | |
| 351 | def tearDown(self): |
| 352 | self.model = None |
| 353 | sys.path = self.sys_path_save |
| 354 | |
| 355 | # TODO - The following two tests are very simple, replace with more meaningful ones |
| 356 | |
| 357 | def test_save(self): |
| 358 | # Tests that the model can be saved |
| 359 | |
| 360 | self.model.name = "${SERVICE_NAME}_test" |
| 361 | self.sync_step().sync_record(self.model) |
| 362 | self.model.save.assert_called() |
| 363 | |
| 364 | def test_sync_rejected(self): |
| 365 | # Tests that an empty name raises an exception |
| 366 | |
| 367 | self.model.name = "" |
| 368 | with self.assertRaises(Exception): |
| 369 | self.sync_step().sync_record(self.model) |
| 370 | |
| 371 | |
| 372 | if __name__ == '__main__': |
| 373 | unittest.main() |
| 374 | EOF |
| 375 | |
| 376 | echo "Creating model policy for instance" |
| 377 | cat << EOF > "xos/synchronizer/model_policies/model_policy_${SERVICE_NAME}_serviceinstance.py" |
| 378 | ${LICENSE_TEXT} |
| 379 | |
| 380 | from synchronizers.new_base.policy import Policy |
| 381 | |
| 382 | |
| 383 | class ${UC_SERVICE_NAME}ServiceInstancePolicy(Policy): |
| 384 | """ |
| 385 | ${UC_SERVICE_NAME}ServiceInstancePolicy |
| 386 | Implements model policy for ${UC_SERVICE_NAME}Instance |
| 387 | """ |
| 388 | |
| 389 | model_name = "${UC_SERVICE_NAME}ServiceInstance" |
| 390 | |
| 391 | def handle_create(self, si): |
| 392 | self.logger.debug( |
| 393 | "MODEL_POLICY: enter handle_create for ${UC_SERVICE_NAME}ServiceInstance %s" % |
| 394 | si.id) |
| 395 | self.handle_update(si) |
| 396 | # TODO: Implement creation policy, if it differs from update policy |
| 397 | |
| 398 | def handle_update(self, si): |
| 399 | self.logger.debug( |
| 400 | "MODEL_POLICY: enter handle_update for ${UC_SERVICE_NAME}ServiceInstance %s, valid=%s" % |
| 401 | (si.id, si.valid)) |
| 402 | |
| 403 | if (si.backend_code != 1): |
| 404 | raise Exception( |
| 405 | "MODEL_POLICY: ${UC_SERVICE_NAME}ServiceInstance %s has not been synced yet" % |
| 406 | si.id) |
| 407 | |
| 408 | # TODO: Implement update policy |
| 409 | |
| 410 | def handle_delete(self, si): |
| 411 | self.logger.debug( |
| 412 | "MODEL_POLICY: enter handle_delete for ${UC_SERVICE_NAME}ServiceInstance %s" % |
| 413 | si.id) |
| 414 | # TODO: Implement delete policy |
| 415 | EOF |
| 416 | |
| 417 | echo "Creating test for model policy of service instance" |
| 418 | cat << EOF > "xos/synchronizer/model_policies/test_model_policy_${SERVICE_NAME}_serviceinstance.py" |
| 419 | ${LICENSE_TEXT} |
| 420 | |
| 421 | import os |
| 422 | import sys |
| 423 | import unittest |
| 424 | from mock import Mock |
| 425 | |
| 426 | # Hack to load synchronizer framework |
| 427 | test_path = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) |
| 428 | |
| 429 | xos_dir = os.path.join(test_path, "../../..") |
| 430 | |
| 431 | if not os.path.exists(os.path.join(test_path, "new_base")): |
| 432 | xos_dir = os.path.join(test_path, "../../../../../../orchestration/xos/xos") |
Matteo Scandolo | e157680 | 2019-03-06 15:23:40 -0800 | [diff] [blame^] | 433 | services_dir = os.path.join(xos_dir, "../../xos-services") |
Zack Williams | 0149cd2 | 2018-05-17 16:19:48 -0700 | [diff] [blame] | 434 | |
| 435 | sys.path.append(xos_dir) |
| 436 | sys.path.append(os.path.join(xos_dir, 'synchronizers', 'new_base')) |
| 437 | # END Hack to load synchronizer framework |
| 438 | |
| 439 | |
| 440 | # generate model from xproto |
| 441 | def get_models_fn(service_name, xproto_name): |
| 442 | name = os.path.join(service_name, "xos", xproto_name) |
| 443 | if os.path.exists(os.path.join(services_dir, name)): |
| 444 | return name |
| 445 | else: |
| 446 | name = os.path.join(service_name, "xos", "synchronizer", "models", xproto_name) |
| 447 | if os.path.exists(os.path.join(services_dir, name)): |
| 448 | return name |
| 449 | raise Exception("Unable to find service=%s xproto=%s" % (service_name, xproto_name)) |
| 450 | # END generate model from xproto |
| 451 | |
| 452 | |
| 453 | class TestModelPolicy${UC_SERVICE_NAME}ServiceInstance(unittest.TestCase): |
| 454 | |
| 455 | def setUp(self): |
| 456 | |
| 457 | self.sys_path_save = sys.path |
| 458 | sys.path.append(xos_dir) |
| 459 | sys.path.append(os.path.join(xos_dir, 'synchronizers', 'new_base')) |
| 460 | |
| 461 | # Setting up the config module |
| 462 | from xosconfig import Config |
| 463 | config = os.path.join(test_path, "../test_config.yaml") |
| 464 | Config.clear() |
| 465 | Config.init(config, "synchronizer-config-schema.yaml") |
| 466 | # END Setting up the config module |
| 467 | |
| 468 | from synchronizers.new_base.mock_modelaccessor_build import build_mock_modelaccessor |
| 469 | build_mock_modelaccessor(xos_dir, services_dir, [ |
| 470 | get_models_fn("${SERVICE_NAME}", "${SERVICE_NAME}.xproto"), |
| 471 | ]) |
| 472 | |
| 473 | from synchronizers.new_base.modelaccessor import model_accessor |
| 474 | from model_policy_${SERVICE_NAME}_serviceinstance import ${UC_SERVICE_NAME}ServiceInstancePolicy |
| 475 | # import all class names to globals |
| 476 | for (k, v) in model_accessor.all_model_classes.items(): |
| 477 | globals()[k] = v |
| 478 | |
| 479 | # Some of the functions we call have side-effects, reset the world. |
| 480 | model_accessor.reset_all_object_stores() |
| 481 | |
| 482 | self.policy = ${UC_SERVICE_NAME}ServiceInstancePolicy() |
| 483 | self.si = Mock() |
| 484 | |
| 485 | def tearDown(self): |
| 486 | sys.path = self.sys_path_save |
| 487 | self.si = None |
| 488 | |
| 489 | def test_not_synced(self): |
| 490 | self.si.valid = "awaiting" |
| 491 | self.si.backend_code = 0 |
| 492 | |
| 493 | with self.assertRaises(Exception) as e: |
| 494 | self.policy.handle_update(self.si) |
| 495 | |
| 496 | self.assertIn("has not been synced yet", e.exception.message) |
| 497 | |
| 498 | def test_skip_update(self): |
| 499 | self.si.valid = "awaiting" |
| 500 | self.si.backend_code = 1 |
| 501 | |
| 502 | self.policy.handle_update(self.si) |
| 503 | |
| 504 | |
| 505 | if __name__ == '__main__': |
| 506 | unittest.main() |
| 507 | EOF |
| 508 | |
| 509 | |
| 510 | echo "Creating helm chart for service" |
| 511 | cat << EOF > "helm-charts/${SERVICE_NAME}/Chart.yaml" |
| 512 | --- |
| 513 | ${LICENSE_TEXT} |
| 514 | |
| 515 | name: ${SERVICE_NAME} |
| 516 | version: ${SERVICE_VERSION} |
| 517 | EOF |
| 518 | |
| 519 | cat << EOF > "helm-charts/${SERVICE_NAME}/values.yaml" |
| 520 | --- |
| 521 | ${LICENSE_TEXT} |
| 522 | |
| 523 | nameOverride: "" |
| 524 | fullnameOverride: "" |
| 525 | |
| 526 | ${SERVICE_NAME}_synchronizerImage: "xosproject/${SERVICE_NAME}-synchronizer:{{ .Chart.Version }}" |
| 527 | |
| 528 | imagePullPolicy: 'IfNotPresent' |
| 529 | |
| 530 | xosAdminUser: "admin@opencord.org" |
| 531 | xosAdminPassword: "letmein" |
| 532 | |
| 533 | affinity: {} |
| 534 | nodeSelector: {} |
| 535 | replicaCount: 1 |
| 536 | resources: {} |
| 537 | tolerations: [] |
| 538 | EOF |
| 539 | |
| 540 | cat << EOF > "helm-charts/${SERVICE_NAME}/templates/_helpers.tpl" |
| 541 | {{/* vim: set filetype=mustache: */}} |
| 542 | ${TPL_LICENSE_TEXT} |
| 543 | {{/* |
| 544 | Expand the name of the chart. |
| 545 | */}} |
| 546 | {{- define "${SERVICE_NAME}.name" -}} |
| 547 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} |
| 548 | {{- end -}} |
| 549 | |
| 550 | {{/* |
| 551 | Create a default fully qualified app name. |
| 552 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). |
| 553 | If release name contains chart name it will be used as a full name. |
| 554 | */}} |
| 555 | {{- define "${SERVICE_NAME}.fullname" -}} |
| 556 | {{- if .Values.fullnameOverride -}} |
| 557 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} |
| 558 | {{- else -}} |
| 559 | {{- \$name := default .Chart.Name .Values.nameOverride -}} |
| 560 | {{- if contains \$name .Release.Name -}} |
| 561 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} |
| 562 | {{- else -}} |
| 563 | {{- printf "%s-%s" .Release.Name \$name | trunc 63 | trimSuffix "-" -}} |
| 564 | {{- end -}} |
| 565 | {{- end -}} |
| 566 | {{- end -}} |
| 567 | |
| 568 | {{/* |
| 569 | Create chart name and version as used by the chart label. |
| 570 | */}} |
| 571 | {{- define "${SERVICE_NAME}.chart" -}} |
| 572 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} |
| 573 | {{- end -}} |
| 574 | |
| 575 | {{- define "${SERVICE_NAME}.serviceConfig" -}} |
| 576 | name: ${SERVICE_NAME} |
| 577 | accessor: |
| 578 | username: {{ .Values.xosAdminUser | quote }} |
| 579 | password: {{ .Values.xosAdminPassword | quote }} |
| 580 | endpoint: xos-core:50051 |
| 581 | required_models: |
| 582 | - ${UC_SERVICE_NAME}Service |
| 583 | - ${UC_SERVICE_NAME}ServiceInstance |
| 584 | dependency_graph: "/opt/xos/synchronizers/${SERVICE_NAME}/model-deps" |
| 585 | model_policies_dir: "/opt/xos/synchronizers/${SERVICE_NAME}/model_policies" |
| 586 | models_dir: "/opt/xos/synchronizers/${SERVICE_NAME}/models" |
| 587 | steps_dir: "/opt/xos/synchronizers/${SERVICE_NAME}/steps" |
| 588 | logging: |
| 589 | version: 1 |
| 590 | handlers: |
| 591 | console: |
| 592 | class: logging.StreamHandler |
| 593 | file: |
| 594 | class: logging.handlers.RotatingFileHandler |
| 595 | filename: /var/log/xos.log |
| 596 | maxBytes: 10485760 |
| 597 | backupCount: 5 |
| 598 | loggers: |
| 599 | 'multistructlog': |
| 600 | handlers: |
| 601 | - console |
| 602 | - file |
| 603 | level: DEBUG |
| 604 | {{- end -}} |
| 605 | EOF |
| 606 | |
| 607 | cat << EOF > "helm-charts/${SERVICE_NAME}/templates/_tosca.tpl" |
| 608 | {{/* vim: set filetype=mustache: */}} |
| 609 | ${TPL_LICENSE_TEXT} |
| 610 | {{- define "${SERVICE_NAME}.serviceTosca" -}} |
| 611 | tosca_definitions_version: tosca_simple_yaml_1_0 |
| 612 | description: Set up ${SERVICE_NAME} service |
| 613 | imports: |
| 614 | - custom_types/${SERVICE_NAME}service.yaml |
| 615 | |
| 616 | topology_template: |
| 617 | node_templates: |
| 618 | service#${SERVICE_NAME}: |
| 619 | type: tosca.nodes.${UC_SERVICE_NAME}Service |
| 620 | properties: |
| 621 | name: ${SERVICE_NAME} |
| 622 | kind: ${UC_SERVICE_NAME} |
| 623 | {{- end -}} |
| 624 | EOF |
| 625 | |
| 626 | cat << EOF > "helm-charts/${SERVICE_NAME}/templates/configmap.yaml" |
| 627 | --- |
| 628 | ${LICENSE_TEXT} |
| 629 | |
| 630 | apiVersion: v1 |
| 631 | kind: ConfigMap |
| 632 | metadata: |
| 633 | name: ${SERVICE_NAME} |
| 634 | data: |
| 635 | serviceConfig: | |
| 636 | {{ include "${SERVICE_NAME}.serviceConfig" . | indent 4 }} |
| 637 | EOF |
| 638 | |
| 639 | cat << EOF > "helm-charts/${SERVICE_NAME}/templates/deployment.yaml" |
| 640 | --- |
| 641 | ${LICENSE_TEXT} |
| 642 | |
| 643 | apiVersion: apps/v1beta2 |
| 644 | kind: Deployment |
| 645 | metadata: |
| 646 | name: {{ template "${SERVICE_NAME}.fullname" . }} |
| 647 | labels: |
| 648 | app: {{ template "${SERVICE_NAME}.name" . }} |
| 649 | chart: {{ template "${SERVICE_NAME}.chart" . }} |
| 650 | release: {{ .Release.Name }} |
| 651 | heritage: {{ .Release.Service }} |
| 652 | spec: |
| 653 | replicas: {{ .Values.replicaCount }} |
| 654 | selector: |
| 655 | matchLabels: |
| 656 | app: {{ template "${SERVICE_NAME}.name" . }} |
| 657 | release: {{ .Release.Name }} |
| 658 | template: |
| 659 | metadata: |
| 660 | labels: |
| 661 | app: {{ template "${SERVICE_NAME}.name" . }} |
| 662 | release: {{ .Release.Name }} |
| 663 | annotations: |
| 664 | checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} |
| 665 | spec: |
| 666 | containers: |
| 667 | - name: {{ .Chart.Name }} |
| 668 | image: {{ tpl .Values.${SERVICE_NAME}_synchronizerImage . | quote }} |
| 669 | imagePullPolicy: {{ .Values.imagePullPolicy }} |
| 670 | resources: |
| 671 | {{ toYaml .Values.resources | indent 12 }} |
| 672 | volumeMounts: |
| 673 | - name: ${SERVICE_NAME}-config |
| 674 | mountPath: /opt/xos/synchronizers/${SERVICE_NAME}/config.yaml |
| 675 | subPath: config.yaml |
| 676 | - name: certchain-volume |
| 677 | mountPath: /usr/local/share/ca-certificates/local_certs.crt |
| 678 | subPath: config/ca_cert_chain.pem |
| 679 | volumes: |
| 680 | - name: ${SERVICE_NAME}-config |
| 681 | configMap: |
| 682 | name: ${SERVICE_NAME} |
| 683 | items: |
| 684 | - key: serviceConfig |
| 685 | path: config.yaml |
| 686 | - name: certchain-volume |
| 687 | configMap: |
| 688 | name: ca-certificates |
| 689 | items: |
| 690 | - key: chain |
| 691 | path: config/ca_cert_chain.pem |
| 692 | {{- with .Values.nodeSelector }} |
| 693 | nodeSelector: |
| 694 | {{ toYaml . | indent 8 }} |
| 695 | {{- end }} |
| 696 | {{- with .Values.affinity }} |
| 697 | affinity: |
| 698 | {{ toYaml . | indent 8 }} |
| 699 | {{- end }} |
| 700 | {{- with .Values.tolerations }} |
| 701 | tolerations: |
| 702 | {{ toYaml . | indent 8 }} |
| 703 | {{- end }} |
| 704 | EOF |
| 705 | |
| 706 | echo 'Done! Check the newly created service repo with service_lint.sh' |