diff options
95 files changed, 3654 insertions, 470 deletions
diff --git a/.tito/packages/openshift-ansible b/.tito/packages/openshift-ansible index 3b7826d31..9e7b18195 100644 --- a/.tito/packages/openshift-ansible +++ b/.tito/packages/openshift-ansible @@ -1 +1 @@ -3.5.3-1 ./ +3.6.5-1 ./ diff --git a/.tito/releasers.conf b/.tito/releasers.conf index 032212b24..b52e4fd87 100644 --- a/.tito/releasers.conf +++ b/.tito/releasers.conf @@ -32,6 +32,10 @@ releaser = tito.release.DistGitReleaser branches = rhaos-3.5-rhel-7 srpm_disttag = .el7aos +[aos-3.6] +releaser = tito.release.DistGitReleaser +branches = rhaos-3.6-rhel-7 +srpm_disttag = .el7aos [copr-openshift-ansible] releaser = tito.release.CoprReleaser diff --git a/.travis.yml b/.travis.yml index 0698b0280..245202139 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,21 +20,3 @@ script: after_success: - coveralls - -notifications: - email: - recipients: - - jdetiber@redhat.com - - sdodson@redhat.com - on_success: change - on_failure: always - irc: - channels: - - chat.freenode.net#openshift-dev - on_success: change - on_failure: always - template: - - "%{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message}" - - "Change view : %{compare_url}" - - "Build details : %{build_url}" - - "sdodson jdetiber: ^" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 12f3efc09..50bb09470 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -75,7 +75,10 @@ See the [RPM build instructions](BUILD.md). We use [tox](http://readthedocs.org/docs/tox/) to manage virtualenvs and run tests. Alternatively, tests can be run using [detox](https://pypi.python.org/pypi/detox/) which allows for running tests in -parallel +parallel. + +Note: while `detox` may be useful in development to make use of multiple cores, +it can be buggy at times and produce flakes, thus we do not use it in our CI. ``` @@ -95,43 +98,90 @@ by `tox`: $ find . -path '*/bin/python' | grep -vF .tox ``` -Extraneous virtualenvs cause tools such as `pylint` to take a very long time -going through files that are part of the virtualenv. +The reason for this recommendation is that extraneous virtualenvs cause tools +such as `pylint` to take a very long time going through files that are part of +the virtualenv, and test discovery to go through lots of irrelevant files and +potentially fail. --- List the test environments available: + ``` tox -l ``` -Run all of the tests with: +Run all of the tests and linters with: + ``` tox ``` -Run all of the tests in parallel with detox: +Run all of the tests linters in parallel (may flake): + ``` detox ``` -Running a particular test environment (python 2.7 flake8 tests in this case): +### Run only unit tests or some specific linter + +Run a particular test environment (`flake8` on Python 2.7 in this case): + ``` tox -e py27-flake8 ``` -Running a particular test environment in a clean virtualenv (python 3.5 pylint -tests in this case): +Run a particular test environment in a clean virtualenv (`pylint` on Python 3.5 +in this case): + ``` -tox -r -e py35-pylint +tox -re py35-pylint ``` -If you want to enter the virtualenv created by tox to do additional +### Tricks + +#### Activating a virtualenv managed by tox + +If you want to enter a virtualenv created by tox to do additional testing/debugging (py27-flake8 env in this case): + ``` source .tox/py27-flake8/bin/activate ``` +#### Limiting the unit tests that are run + +During development, it might be useful to constantly run just a single test file +or test method, or to pass custom arguments to `pytest`: + +``` +tox -e py27-unit -- path/to/test/file.py +``` + +Anything after `--` is passed directly to `pytest`. To learn more about what +other flags you can use, try: + +``` +tox -e py27-unit -- -h +``` + +As a practical example, the snippet below shows how to list all tests in a +certain file, and then execute only one test of interest: + +``` +$ tox -e py27-unit -- roles/lib_openshift/src/test/unit/test_oc_project.py --collect-only --no-cov +... +collected 1 items +<Module 'roles/lib_openshift/src/test/unit/test_oc_project.py'> + <UnitTestCase 'OCProjectTest'> + <TestCaseFunction 'test_adding_a_project'> +... +$ tox -e py27-unit -- roles/lib_openshift/src/test/unit/test_oc_project.py -k test_adding_a_project +``` + +Among other things, this can be used for instance to see the coverage levels of +individual modules as we work on improving tests. + ## Submitting contributions 1. Go through the guides from the [introduction](#Introduction). diff --git a/callback_plugins/openshift_quick_installer.py b/callback_plugins/openshift_quick_installer.py index b4c7edd38..c0fdbc650 100644 --- a/callback_plugins/openshift_quick_installer.py +++ b/callback_plugins/openshift_quick_installer.py @@ -54,6 +54,12 @@ class CallbackModule(CallbackBase): plays_count = 0 plays_total_ran = 0 + def __init__(self): + """Constructor, ensure standard self.*s are set""" + self._play = None + self._last_task_banner = None + super(CallbackModule, self).__init__() + def banner(self, msg, color=None): '''Prints a header-looking line with stars taking up to 80 columns of width (3 columns, minimum) @@ -68,6 +74,29 @@ class CallbackModule(CallbackBase): stars = "*" * star_len self._display.display("\n%s %s" % (msg, stars), color=color, log_only=True) + def _print_task_banner(self, task): + """Imported from the upstream 'default' callback""" + # args can be specified as no_log in several places: in the task or in + # the argument spec. We can check whether the task is no_log but the + # argument spec can't be because that is only run on the target + # machine and we haven't run it thereyet at this time. + # + # So we give people a config option to affect display of the args so + # that they can secure this if they feel that their stdout is insecure + # (shoulder surfing, logging stdout straight to a file, etc). + args = '' + if not task.no_log and C.DISPLAY_ARGS_TO_STDOUT: + args = ', '.join('%s=%s' % a for a in task.args.items()) + args = ' %s' % args + + self.banner(u"TASK [%s%s]" % (task.get_name().strip(), args)) + if self._display.verbosity >= 2: + path = task.get_path() + if path: + self._display.display(u"task path: %s" % path, color=C.COLOR_DEBUG, log_only=True) + + self._last_task_banner = task._uuid + def v2_playbook_on_start(self, playbook): """This is basically the start of it all""" self.plays_count = len(playbook.get_plays()) @@ -236,6 +265,60 @@ The only thing we change here is adding `log_only=True` to the """ self._display.display("skipping: no hosts matched", color=C.COLOR_SKIP, log_only=True) + ###################################################################### + # So we can bubble up errors to the top + def v2_runner_on_failed(self, result, ignore_errors=False): + """I guess this is when an entire task has failed?""" + + if self._play.strategy == 'free' and self._last_task_banner != result._task._uuid: + self._print_task_banner(result._task) + + delegated_vars = result._result.get('_ansible_delegated_vars', None) + if 'exception' in result._result: + if self._display.verbosity < 3: + # extract just the actual error message from the exception text + error = result._result['exception'].strip().split('\n')[-1] + msg = "An exception occurred during task execution. To see the full traceback, use -vvv. The error was: %s" % error + else: + msg = "An exception occurred during task execution. The full traceback is:\n" + result._result['exception'] + + self._display.display(msg, color=C.COLOR_ERROR) + + if result._task.loop and 'results' in result._result: + self._process_items(result) + + else: + if delegated_vars: + self._display.display("fatal: [%s -> %s]: FAILED! => %s" % (result._host.get_name(), delegated_vars['ansible_host'], self._dump_results(result._result)), color=C.COLOR_ERROR) + else: + self._display.display("fatal: [%s]: FAILED! => %s" % (result._host.get_name(), self._dump_results(result._result)), color=C.COLOR_ERROR) + + if ignore_errors: + self._display.display("...ignoring", color=C.COLOR_SKIP) + + def v2_runner_item_on_failed(self, result): + """When an item in a task fails.""" + delegated_vars = result._result.get('_ansible_delegated_vars', None) + if 'exception' in result._result: + if self._display.verbosity < 3: + # extract just the actual error message from the exception text + error = result._result['exception'].strip().split('\n')[-1] + msg = "An exception occurred during task execution. To see the full traceback, use -vvv. The error was: %s" % error + else: + msg = "An exception occurred during task execution. The full traceback is:\n" + result._result['exception'] + + self._display.display(msg, color=C.COLOR_ERROR) + + msg = "failed: " + if delegated_vars: + msg += "[%s -> %s]" % (result._host.get_name(), delegated_vars['ansible_host']) + else: + msg += "[%s]" % (result._host.get_name()) + + self._display.display(msg + " (item=%s) => %s" % (self._get_item(result._result), self._dump_results(result._result)), color=C.COLOR_ERROR) + self._handle_warnings(result._result) + + ###################################################################### def v2_playbook_on_stats(self, stats): """Print the final playbook run stats""" self._display.display("", screen_only=True) diff --git a/inventory/byo/hosts.origin.example b/inventory/byo/hosts.origin.example index bb9f4706a..033ce8a82 100644 --- a/inventory/byo/hosts.origin.example +++ b/inventory/byo/hosts.origin.example @@ -30,17 +30,17 @@ deployment_type=origin # use this to lookup the latest exact version of the container images, which is the tag actually used to configure # the cluster. For RPM installations we just verify the version detected in your configured repos matches this # release. -openshift_release=v1.4 +openshift_release=v1.5 # Specify an exact container image tag to install or configure. # WARNING: This value will be used for all hosts in containerized environments, even those that have another version installed. # This could potentially trigger an upgrade and downtime, so be careful with modifying this value after the cluster is set up. -#openshift_image_tag=v1.2.0 +#openshift_image_tag=v1.5.0 # Specify an exact rpm version to install or configure. # WARNING: This value will be used for all hosts in RPM based environments, even those that have another version installed. # This could potentially trigger an upgrade and downtime, so be careful with modifying this value after the cluster is set up. -#openshift_pkg_version=-1.2.0 +#openshift_pkg_version=-1.5.0 # Install the openshift examples #openshift_install_examples=true @@ -494,6 +494,9 @@ openshift_master_identity_providers=[{'name': 'htpasswd_auth', 'login': 'true', # your cloud platform use this. #openshift_hosted_metrics_storage_kind=dynamic # +# Other Metrics Options -- Common items you may wish to reconfigure, for the complete +# list of options please see roles/openshift_metrics/README.md +# # Override metricsPublicURL in the master config for cluster metrics # Defaults to https://hawkular-metrics.{{openshift_master_default_subdomain}}/hawkular/metrics # Currently, you may only alter the hostname portion of the url, alterting the @@ -539,15 +542,14 @@ openshift_master_identity_providers=[{'name': 'htpasswd_auth', 'login': 'true', # list of options please see roles/openshift_logging/README.md # # Configure loggingPublicURL in the master config for aggregate logging, defaults -# to https://kibana.{{ openshift_master_default_subdomain }} -#openshift_master_logging_public_url=https://kibana.example.com +# to kibana.{{ openshift_master_default_subdomain }} +#openshift_hosted_logging_hostname=logging.apps.example.com # Configure the number of elastic search nodes, unless you're using dynamic provisioning # this value must be 1 #openshift_hosted_logging_elasticsearch_cluster_size=1 -#openshift_hosted_logging_hostname=logging.apps.example.com -# Configure the prefix and version for the deployer image -#openshift_hosted_logging_deployer_prefix=registry.example.com:8888/openshift3/ -#openshift_hosted_logging_deployer_version=3.3.0 +# Configure the prefix and version for the component images +#openshift_hosted_logging_deployer_prefix=docker.io/openshift/origin- +#openshift_hosted_logging_deployer_version=1.5.0 # Configure the multi-tenant SDN plugin (default is 'redhat/openshift-ovs-subnet') # os_sdn_network_plugin_name='redhat/openshift-ovs-multitenant' diff --git a/inventory/byo/hosts.ose.example b/inventory/byo/hosts.ose.example index 12a1b3991..49bcb7405 100644 --- a/inventory/byo/hosts.ose.example +++ b/inventory/byo/hosts.ose.example @@ -30,17 +30,17 @@ deployment_type=openshift-enterprise # use this to lookup the latest exact version of the container images, which is the tag actually used to configure # the cluster. For RPM installations we just verify the version detected in your configured repos matches this # release. -openshift_release=v3.4 +openshift_release=v3.5 # Specify an exact container image tag to install or configure. # WARNING: This value will be used for all hosts in containerized environments, even those that have another version installed. # This could potentially trigger an upgrade and downtime, so be careful with modifying this value after the cluster is set up. -#openshift_image_tag=v3.2.0.46 +#openshift_image_tag=v3.5.0 # Specify an exact rpm version to install or configure. # WARNING: This value will be used for all hosts in RPM based environments, even those that have another version installed. # This could potentially trigger an upgrade and downtime, so be careful with modifying this value after the cluster is set up. -#openshift_pkg_version=-3.2.0.46 +#openshift_pkg_version=-3.5.0 # Install the openshift examples #openshift_install_examples=true @@ -495,6 +495,9 @@ openshift_master_identity_providers=[{'name': 'htpasswd_auth', 'login': 'true', # your cloud platform use this. #openshift_hosted_metrics_storage_kind=dynamic # +# Other Metrics Options -- Common items you may wish to reconfigure, for the complete +# list of options please see roles/openshift_metrics/README.md +# # Override metricsPublicURL in the master config for cluster metrics # Defaults to https://hawkular-metrics.{{openshift_master_default_subdomain}}/hawkular/metrics # Currently, you may only alter the hostname portion of the url, alterting the @@ -540,15 +543,14 @@ openshift_master_identity_providers=[{'name': 'htpasswd_auth', 'login': 'true', # list of options please see roles/openshift_logging/README.md # # Configure loggingPublicURL in the master config for aggregate logging, defaults -# to https://kibana.{{ openshift_master_default_subdomain }} -#openshift_master_logging_public_url=https://kibana.example.com +# to kibana.{{ openshift_master_default_subdomain }} +#openshift_hosted_logging_hostname=logging.apps.example.com # Configure the number of elastic search nodes, unless you're using dynamic provisioning # this value must be 1 #openshift_hosted_logging_elasticsearch_cluster_size=1 -#openshift_hosted_logging_hostname=logging.apps.example.com -# Configure the prefix and version for the deployer image +# Configure the prefix and version for the component images #openshift_hosted_logging_deployer_prefix=registry.example.com:8888/openshift3/ -#openshift_hosted_logging_deployer_version=3.3.0 +#openshift_hosted_logging_deployer_version=3.5.0 # Configure the multi-tenant SDN plugin (default is 'redhat/openshift-ovs-subnet') # os_sdn_network_plugin_name='redhat/openshift-ovs-multitenant' diff --git a/openshift-ansible.spec b/openshift-ansible.spec index c7b4544be..221740ff3 100644 --- a/openshift-ansible.spec +++ b/openshift-ansible.spec @@ -9,7 +9,7 @@ %global __requires_exclude ^/usr/bin/ansible-playbook$ Name: openshift-ansible -Version: 3.5.3 +Version: 3.6.5 Release: 1%{?dist} Summary: Openshift and Atomic Enterprise Ansible License: ASL 2.0 @@ -270,6 +270,560 @@ Atomic OpenShift Utilities includes %changelog +* Tue Mar 21 2017 Jenkins CD Merge Bot <tdawson@redhat.com> 3.6.5-1 +- Attempt to match version of excluders to target version (sdodson@redhat.com) +- Get rid of adjust.yml (sdodson@redhat.com) +- Protect against missing commands (sdodson@redhat.com) +- Simplify excluder enablement logic a bit more (sdodson@redhat.com) +- Add tito releaser for 3.6 (smunilla@redhat.com) +- Adding oc_group to lib_openshift (kwoodson@redhat.com) +- preflight checks: improve user output from checks (lmeyer@redhat.com) +- preflight checks: bypass RPM excludes (lmeyer@redhat.com) +- acceptschema2 default: true (aweiteka@redhat.com) +- Do not require python-six via openshift_facts (rhcarvalho@gmail.com) + +* Sat Mar 18 2017 Jenkins CD Merge Bot <tdawson@redhat.com> 3.6.4-1 +- Cherry picking from #3689 (ewolinet@redhat.com) +- Moving projects task within openshift_hosted (rteague@redhat.com) +- Refactor openshift_projects role (rteague@redhat.com) +- Add unit tests for existing health checks (rhcarvalho@gmail.com) +- Do not update when properties when not passed. (kwoodson@redhat.com) +- change shell to bash in generate_jks.sh (l@lmello.eu.org) + +* Fri Mar 17 2017 Jenkins CD Merge Bot <tdawson@redhat.com> 3.6.3-1 +- enable docker excluder since the time it is installed (jchaloup@redhat.com) + +* Thu Mar 16 2017 Jenkins CD Merge Bot <tdawson@redhat.com> 3.6.2-1 +- enable excluders during node/master scaling up (jchaloup@redhat.com) +- Fixing variable naming for 35 scoping. (kwoodson@redhat.com) +- Fix get_router_replicas infrastructure node count. (abutcher@redhat.com) +- Fix containerized openvswitch race (sdodson@redhat.com) + +* Thu Mar 16 2017 Jenkins CD Merge Bot <tdawson@redhat.com> 3.6.1-1 +- Bump version to 3.6.0 (smunilla@redhat.com) +- Improve CONTRIBUTING guide with testing tricks (rhcarvalho@gmail.com) +- Update versions in example inventories (sdodson@redhat.com) +- Only call excluder playbooks on masters and nodes (sdodson@redhat.com) +- Since we've decided that we're no longer paying attention to current status + remove this as it was toggling things (sdodson@redhat.com) +- Remove travis notifications (jdetiber@redhat.com) +- Removing dependency on master facts for master_public_url default + (ewolinet@redhat.com) +- don't assume openshift_upgrade_target is in a form d.d (jchaloup@redhat.com) +- Cherry picked from #3657 (ewolinet@redhat.com) +- Revert "Enable docker during installation and upgrade by default" + (skuznets@redhat.com) +- Nuage service account handling by single master + (vishal.patil@nuagenetworks.net) +- Add router svcacct cluster-reader role (rteague@redhat.com) +- Cherry picking from #3644 (ewolinet@redhat.com) +- Revert module_utils six for openshift_health_checker (jdetiber@redhat.com) +- Refactor and remove openshift_serviceaccount (rteague@redhat.com) +- Fix typo (sdodson@redhat.com) +- Force to use TLSv1.2 (related to https://github.com/openshift/openshift- + ansible/pull/2707) (olivier@openkumo.fr) +- Raise on dry-run failures. (kwoodson@redhat.com) +- validate excluders on non-atomic hosts only (jchaloup@redhat.com) +- enable docker excluder since the time it is installed (jchaloup@redhat.com) +- cherry picking from #3621 #3614 #3627 (ewolinet@redhat.com) +- Renaming oadm_manage_node to oc_adm_manage_node (rteague@redhat.com) +- add 'hawkular/metrics' when updating config (jcantril@redhat.com) +- update all the masters (jcantril@redhat.com) +- bug 1430661. Update masterConfig metricsPublicURL on install + (jcantril@redhat.com) +- nuage: Move role back to config (smilner@redhat.com) +- Fix incorrect comparison when detecting petsets (tbielawa@redhat.com) +- Removed unused, unwanted, incorrectly committed code. (kwoodson@redhat.com) +- Minor updates to README_CONTAINER_IMAGE.md (pep@redhat.com) +- Fix references to openshift_set_node_ip in inventory examples + (gskgoskk@gmail.com) +- Bug 1428711 - [IntService_public_324] ES pod is unable to read + searchguard.truststore after upgarde logging from 3.3.1 to 3.5.0 + (rmeggins@redhat.com) +- bug 1428249. Use ES hostmount storage if it exists (jcantril@redhat.com) +- Use ansible.compat.six where possible (jdetiber@redhat.com) +- Remove debug task (tbielawa@redhat.com) +- Use six from ansible.module_utils for remote hosts (jdetiber@redhat.com) +- re-enable excluders if they are enabled after openshift version detection + (jchaloup@redhat.com) +- Allow overriding minTLSVersion and cipherSuites (meggen@redhat.com) +- extend the excluders to containerized deployment (jchaloup@redhat.com) +- Fixing the way policies are found. The old method was unreliable. This + method searches all and matches on properties. (kwoodson@redhat.com) +- openshift_excluders depends on openshift_repos (sdodson@redhat.com) +- add ability to specify an etcd version (mmckinst@umich.edu) +- Lowering test coverage percentage. (kwoodson@redhat.com) +- Removing ordereddict. Replaced with sorted keys. (kwoodson@redhat.com) +- New role (tbielawa@redhat.com) +- Fixed for linting. (kwoodson@redhat.com) +- enable excluders by default (jchaloup@redhat.com) +- ignore the docker excluder status if it is not enabled by a user + (jchaloup@redhat.com) +- Fix pylint/pyflakes errors on master (sdodson@redhat.com) +- Identify PetSets in 3.4 clusters and fail if any are detected + (tbielawa@redhat.com) +- More logging fixes (ewolinet@redhat.com) +- Fix for issue 3541 (srampal@cisco.com) +- Fix to OpenshiftCLIConfig to support an ordereddict. This was breaking test + cases. (kwoodson@redhat.com) +- - update excluders to latest, in non-upgrade scenarios do not update - check + both available excluder versions are at most of upgrade target version - get + excluder status through status command - make excluders enablement + configurable (jchaloup@redhat.com) +- Adding scripts for building and pushing images (bleanhar@redhat.com) +- Adding test_oc_adm_router. (kwoodson@redhat.com) +- Loosely couple docker to iptables service (rteague@redhat.com) +- Generic message directing people to contact support (sdodson@redhat.com) +- Fixing plugin, nodeselectors, and secret pull check (ewolinet@redhat.com) +- Adding into the origin inventory doc. (kwoodson@redhat.com) +- Add oc_objectvalidator to upgrade check (sdodson@redhat.com) +- Augmenting documentation for router sharding. (kwoodson@redhat.com) +- Adding router test. (kwoodson@redhat.com) +- openshift_facts: ensure system containers deps are installed + (gscrivan@redhat.com) +- Preserve order of Docker registries (eric.mountain@amadeus.com) +- Updating metrics defaults (ewolinet@redhat.com) +- Enable coveralls.io (jdetiber@redhat.com) +- Fix indentation of run_once (sdodson@redhat.com) +- Update docs for test consolidation and remove the Makefile + (jdetiber@redhat.com) +- Consolidate root/utils tests (jdetiber@redhat.com) +- Remove dummy setup/teardown methods (rhcarvalho@gmail.com) +- Clean up test files (rhcarvalho@gmail.com) +- Remove commented-out test code (rhcarvalho@gmail.com) +- Make generic OCObjectValidator from OCSDNValidator (mkhan@redhat.com) +- logging needs openshift_master_facts before openshift_facts + (rmeggins@redhat.com) +- separate out test tool configs from setup.cfg (jdetiber@redhat.com) +- Dockerfile and docs to run containerized playbooks (pep@redhat.com) +- Lower test coverage percentage. (kwoodson@redhat.com) +- Mock runs differntly on travis. Fix the mock test params to be ANY. + (kwoodson@redhat.com) +- Fixed the none namespace. Fixed tests with latest loc_oc_binary call. + (kwoodson@redhat.com) +- Updating the namespace param to None. (kwoodson@redhat.com) +- Regenerated code with latest yedit changes. (kwoodson@redhat.com) +- Fixed tests to align with new naming. (kwoodson@redhat.com) +- Fixed docs. Added check for delete failures. Updated namespace to None. + (kwoodson@redhat.com) +- Fixing linters (kwoodson@redhat.com) +- Adding integration test. Fixed issue with node_selector. + (kwoodson@redhat.com) +- Adding oc_project to lib_openshift. (kwoodson@redhat.com) +- Remove old commented-out tests (rhcarvalho@gmail.com) +- Remove redundant assertion (rhcarvalho@gmail.com) +- Fix test (rhcarvalho@gmail.com) +- Lint utils/test (rhcarvalho@gmail.com) +- Rewrap long lines (rhcarvalho@gmail.com) +- Remove unused argument (rhcarvalho@gmail.com) +- Remove unused Makefile variables (rhcarvalho@gmail.com) +- Adding some more logging defaults (ewolinet@redhat.com) +- node/sdn: make /var/lib/cni persistent to ensure IPAM allocations stick + around across node restart (dcbw@redhat.com) +- BZ1422348 - Don't install python-ruamel-yaml (sdodson@redhat.com) +- Re-generate modules (sdodson@redhat.com) +- Only set ownership to etcd for thirdparty datadir (sdodson@redhat.com) +- Added ports. (kwoodson@redhat.com) +- Fixed router name to produce 2nd router. (kwoodson@redhat.com) +- Updated to work with an array of routers. (kwoodson@redhat.com) +- Adding support for router sharding. (kwoodson@redhat.com) +- Removing the openshift_master_facts dependency (ewolinet@redhat.com) +- bug 1420256. Initialize openshift_logging pvc_facts to empty + (jcantril@redhat.com) +- Add oc_adm_policy_user task cluster-role policy (rteague@redhat.com) +- Correct config for hosted registry (rteague@redhat.com) +- Fixing checkout for bindings with -binding suffix (jupierce@redhat.com) +- Leave an empty contiv role directory (sdodson@redhat.com) +- Updating stdout check for changed_when (ewolinet@redhat.com) +- test fixes for openshift_certificates_expiry (jdetiber@redhat.com) +- oadm_policy_group/adm_policy_user module (jupierce@redhat.com) +- Fail on Atomic if docker is too old (smilner@redhat.com) +- Remove contiv role and playbook from rpm packages (sdodson@redhat.com) +- Resolving yammlint errors (ewolinet@redhat.com) +- Fixed error handling when oc adm ca create-server-cert fails. Fixed a logic + error in secure. (kwoodson@redhat.com) +- removing extra when condition (kwoodson@redhat.com) +- Removing run_once. (kwoodson@redhat.com) +- Adding the activeDeadlineSeconds. Removed debug. (kwoodson@redhat.com) +- Separating routes so logic is simpler. (kwoodson@redhat.com) +- Defaulting variables properly to avoid undefined route in dict error. + (kwoodson@redhat.com) +- Add v1.3 FIS templates (sdodson@redhat.com) +- v1.4 Add FIS templates (sdodson@redhat.com) +- Add FIS templates (sdodson@redhat.com) +- Removed duplicate host param. (kwoodson@redhat.com) +- Fixed failures on create when objects exist. (kwoodson@redhat.com) +- Add ca-bundle.crt to list of certs to synchronize. (abutcher@redhat.com) +- Do not force custom ca cert deployment. (abutcher@redhat.com) +- regenerate lib_openshift with yedit exception changes (jdiaz@redhat.com) +- Adding changed_whens for role, rolebinding, and scc reconciliation based on + output from oadm policy command (ewolinet@redhat.com) +- raise exceptions when walking through object path (jdiaz@redhat.com) +- logging fluentd filter was renamed to viaq (rmeggins@redhat.com) +- Add 'persistentVolumeClaim' to volume_info type (rteague@redhat.com) +- Updating delete/recreate with replace --force. (kwoodson@redhat.com) +- Fixed logic error. Ensure both svc and dc exist. (kwoodson@redhat.com) +- Modified base debug statements. Fixed oc_secret debug/verbose flag. Added + reencrypt for route. (kwoodson@redhat.com) +- Adding support for a route with certs and reencrypt. (kwoodson@redhat.com) +- node: use the new oc_atomic_container module (gscrivan@redhat.com) +- master: use the new oc_atomic_container module (gscrivan@redhat.com) +- etcd: use the new oc_atomic_container module (gscrivan@redhat.com) +- lib_openshift: new module atomic_container (gscrivan@redhat.com) +- Combined (squashed) commit for all changes related to adding Contiv support + into Openshift Ansible. This is the first (beta) release of Contiv with + Openshift and is only supported for Openshift Origin + Bare metal deployments + at the time of this commit. Please refer to the Openshift and Contiv official + documentation for details of the level of support for different features and + modes of operation. (srampal@cisco.com) +- Re-generate lib_openshift (sdodson@redhat.com) +- Make s3_volume_mount available to set_fact call (smilner@redhat.com) +- Correct fact creation for pvc (rteague@redhat.com) +- [oc_obj] Move namespace argument to end of command. (abutcher@redhat.com) +- Create hosted registry service (rteague@redhat.com) +- Correct typo in haproxy router collection. (abutcher@redhat.com) +- Fix issue #3505, add notes about origin upgrade versions support in BYO + upgrade README file (contact@stephane-klein.info) +- Moving replica logic to filter_plugin to fix skipped task variable behavior. + (kwoodson@redhat.com) +- install the latest excluders (jchaloup@redhat.com) +- openshift_hosted: Update tasks to use oc_ modules (rteague@redhat.com) +- Rebased. (kwoodson@redhat.com) +- Fixed indentation (kwoodson@redhat.com) +- Adding get_env_var to deploymentconfig. (kwoodson@redhat.com) +- Fixed default variables. Added a fix to generated secret in env var. + (kwoodson@redhat.com) +- Revert "Add centos paas sig common" (sdodson@redhat.com) +- Fix Quick Installer failed due to a Python method failure + (tbielawa@redhat.com) +- Removed JGroups cert and password generation. (juraci@kroehling.de) +- Fix symlink to lookup_plugins/oo_option.py (jchaloup@redhat.com) +- Use 2 and 3 friendly urlparse in oo_filters (smilner@redhat.com) +- Update v1.5 content (sdodson@redhat.com) +- Update v1.4 content (sdodson@redhat.com) +- xPaaS ose-v1.3.6 (sdodson@redhat.com) +- Prepare for origin moving to OCP version scheme (ccoleman@redhat.com) +- initialize_openshift_version: handle excluder packages (gscrivan@redhat.com) +- Add insecure edge termination policy for kibana. (whearn@redhat.com) +- openshift_logging default to 2 replicas of primary shards + (jcantril@redhat.com) +- Fixing doc for oc_adm_ca_server_cert. (kwoodson@redhat.com) +- Convert selectattr tests to use 'match' (rteague@redhat.com) +- Re-generate lib_openshift and lib_utils libraries (sdodson@redhat.com) +- curator config must be in /etc/curator not /usr/curator (rmeggins@redhat.com) +- Updated for pylint. Fixed create doc. (kwoodson@redhat.com) +- Attempt to handle router preparation errors. (kwoodson@redhat.com) +- Fixing the generate tox tests. (kwoodson@redhat.com) +- BZ1414276 - Quote ansible_ssh_user when determining group id + (sdodson@redhat.com) +- Moving import to local class. (kwoodson@redhat.com) +- Added required_together. Added two minor bug fixes for when data is not + passed. (kwoodson@redhat.com) +- fix up ruamel.yaml/pyyaml no-member lint errors (jdetiber@redhat.com) +- Renamed NotContainerized to NotContainerizedMixin and dropped no-member + (smilner@redhat.com) +- Removed unrequired no-members from yedit and generated code + (smilner@redhat.com) +- Removing reference to oadm. Moved parameter under general params. + (kwoodson@redhat.com) +- adding tag to update_master_config (ewolinet@redhat.com) +- CloudFront oc_secret contents should be a list (smilner@redhat.com) +- lib_openshift oc file lookup improvements (jdetiber@redhat.com) +- roles/lib_openshift: Handle /usr/local/bin/oc with sudo (walters@verbum.org) +- if no key, cert, cacert, or default_cert is passed then do not pass to oc + (kwoodson@redhat.com) +- Added backup feature. Fixed a bug with reading the certificate and verifying + names. Added force option. (kwoodson@redhat.com) +- Add SDNValidator Module (mkhan@redhat.com) +- bug 1425321. Default the master api port based on the facts + (jcantril@redhat.com) +- Bug 1420219 - No log entry can be found in Kibana UI after deploying logging + stacks with ansible (rmeggins@redhat.com) +- Address cert expiry parsing review comments (tbielawa@redhat.com) +- Fix typo (rhcarvalho@gmail.com) +- Update link to project homepage (rhcarvalho@gmail.com) +- Implement fake openssl cert classes (tbielawa@redhat.com) +- Removed oadm_ references in doc. (kwoodson@redhat.com) +- Remove unused plays (jhadvig@redhat.com) +- Remove pytest-related dependencies from setup.py (rhcarvalho@gmail.com) +- Added copy support when modifying cert and key on existence + (kwoodson@redhat.com) +- Small spacing fix. (kwoodson@redhat.com) +- Updated doc and defined defaults for signer_* (kwoodson@redhat.com) +- Removed unused code. Made tests executable. (kwoodson@redhat.com) +- Removing cmd, fixed docs and comments. (kwoodson@redhat.com) +- Rename of oadm_ca to oc_adm_ca. Decided to whittle down to the direct call, + server_cert. (kwoodson@redhat.com) +- Fixing doc. (kwoodson@redhat.com) +- Adding oadm_ca to lib_openshift. (kwoodson@redhat.com) +- Fixing docs. Fixed default_cert suggestion. (kwoodson@redhat.com) +- Renamed modules, fixed docs, renamed variables, and cleaned up logic. + (kwoodson@redhat.com) +- Renaming registry and router roles to oc_adm_ (kwoodson@redhat.com) +- Fixing registry doc and suggestions. (kwoodson@redhat.com) +- Adding router and registry to lib_openshift. (kwoodson@redhat.com) +- bug 142026. Ensure Ops PVC prefix are initialized to empty when ops e… + nabled (jcantril@redhat.com) +- Reverting logic for verify api handler to be uniform with other ways we + verify, will be uniformly updated in future (ewolinet@redhat.com) +- bug 1417261. Quote name and secrets in logging templates + (jcantril@redhat.com) +- openshift_facts: handle 'latest' version (gscrivan@redhat.com) +- Surrounding node selector values with quotes (ewolinet@redhat.com) +- Raise the bar on coverage requirements (rhcarvalho@gmail.com) +- Accept extra positional arguments in tox (rhcarvalho@gmail.com) +- Replace nose with pytest (utils) (rhcarvalho@gmail.com) +- Clean up utils/README.md (rhcarvalho@gmail.com) +- Replace nose with pytest (rhcarvalho@gmail.com) +- Extract assertion common to all tests as function (rhcarvalho@gmail.com) +- Replace nose yield-style tests w/ pytest fixtures (rhcarvalho@gmail.com) +- Configure pytest to run tests and coverage (rhcarvalho@gmail.com) +- Fix validation of generated code (rhcarvalho@gmail.com) +- Make tests run with either nosetests or pytest (rhcarvalho@gmail.com) +- Replace assert_equal with plain assert (rhcarvalho@gmail.com) +- Make usage of short_version/release consistent (rhcarvalho@gmail.com) +- Reorganize tests and helper functions logically (rhcarvalho@gmail.com) +- Remove test duplication (rhcarvalho@gmail.com) +- Move similar test cases together (rhcarvalho@gmail.com) +- Insert paths in the second position of sys.path (rhcarvalho@gmail.com) +- Rename test for consistency (rhcarvalho@gmail.com) +- Replace has_key in new modules (smilner@redhat.com) +- Fix symlink to filter_plugins/oo_filters.py (jchaloup@redhat.com) +- Correct logic test for running pods (rteague@redhat.com) +- Temporarily lower the bar for minimum coverage (rhcarvalho@gmail.com) +- Unset exec bit in tests, add missing requirements (jdetiber@redhat.com) +- Include missing unit tests to test runner config (rhcarvalho@gmail.com) +- Fix tests on Python 3 (rhcarvalho@gmail.com) +- Remove dead code in installer (rhcarvalho@gmail.com) +- Remove dead code (rhcarvalho@gmail.com) +- Document how to find dead Python code (rhcarvalho@gmail.com) +- updating until statments on uri module for api verification + (ewolinet@redhat.com) +- add dependency on openshift_repos (sdodson@redhat.com) +- Fixing a bug by removing default debug (kwoodson@redhat.com) +- Updating to use uri module instead (ewolinet@redhat.com) +- Updating node playbooks to use oc_obj (rteague@redhat.com) +- Add centos paas sig common (sdodson@redhat.com) +- Disentangle openshift_repos from openshift_facts (sdodson@redhat.com) +- Adding missing handler to resolve error that it was not found + (ewolinet@redhat.com) +- String compatibility for python2,3 (kwoodson@redhat.com) +- Fix indenting/ordering in router cert redeploy (sdodson@redhat.com) +- post_control_plane.yml: don't fail on grep (gscrivan@redhat.com) +- facts/main: Require Python 3 for Fedora, Python 2 everywhere else + (walters@verbum.org) +- Fix typo, add symlinks for roles (sdodson@redhat.com) +- Resolve deprecation warning (rteague@redhat.com) +- Revert temporary hack to skip router/registry upgrade. (dgoodwin@redhat.com) +- Don't attempt to install python-ruamel-yaml on atomic (sdodson@redhat.com) +- Pleasing the linting gods. (kwoodson@redhat.com) +- Fixed tests for pyyaml vs ruamel. Added import logic. Fixed safe load. + (kwoodson@redhat.com) +- update example templates+imagestreams (bparees@redhat.com) +- Adding fallback support for pyyaml. (kwoodson@redhat.com) +- bug 1420217. Default ES memory to be compariable to 3.4 deployer + (jcantril@redhat.com) +- Register cloudfront privkey when required (smilner@redhat.com) +- initialize oo_nodes_to_upgrade group when running control plane upgrade only + (jchaloup@redhat.com) +- adding some quotes for safety (ewolinet@redhat.com) +- Revert "Add block+when skip to `openshift_facts` tasks" (abutcher@redhat.com) +- Add missing full hostname for the Hawkular Metrics certificate (BZ1421060) + Fix issue where the signer certificate's name is static, preventing + redeployments from being acceptable. (mwringe@redhat.com) +- fixing use of oc_scale module (ewolinet@redhat.com) +- fixing default for logging (ewolinet@redhat.com) +- Fix some lint (jdetiber@redhat.com) +- Fixed issue where upgrade fails when using daemon sets (e.g. aggregated + logging) (adbaldi+ghub@gmail.com) +- upgrades: fix path to disable_excluder.yml (jchaloup@redhat.com) +- Add upgrade job step after the entire upgrade performs (maszulik@redhat.com) +- Ansible Lint cleanup and making filter/lookup plugins used by + openshift_master_facts available within the role (jdetiber@redhat.com) +- Update variant_version (smilner@redhat.com) +- Add block+when skip to `openshift_facts` tasks (tbielawa@redhat.com) +- Trying to fix up/audit note some changes (tbielawa@redhat.com) +- updating defaults for logging and metrics roles (ewolinet@redhat.com) +- Fix logic for checking docker-registry (rteague@redhat.com) +- node, vars/main.yml: define l_is_ha and l_is_same_version + (gscrivan@redhat.com) +- Modify playbooks to use oc_obj module (rteague@redhat.com) +- master, vars/main.yml: define l_is_ha and l_is_same_version + (gscrivan@redhat.com) +- oc route commands now using the oc_route module (smilner@redhat.com) +- Modify playbooks to use oc_label module (rteague@redhat.com) +- Fix cases where child classes override OpenShiftCLI values + (jdetiber@redhat.com) +- BZ1421860: increase Heapster's metric resolution to 30s (mwringe@redhat.com) +- BZ1421834: increase the Heapster metric resolution to 30s + (mwringe@redhat.com) +- Fix Bug 1419654 Remove legacy config_base fallback to /etc/openshift + (sdodson@redhat.com) +- Modify playbooks to use oadm_manage_node module (rteague@redhat.com) +- Removing trailing spaces (esauer@redhat.com) +- Removed adhoc s3_registry (smilner@redhat.com) +- replace 'oc service' command with its lib_openshift equivalent + (jchaloup@redhat.com) +- Making router pods scale with infra nodes (esauer@redhat.com) +- Provisioning of nfs share and PV for logging ops (efreiber@redhat.com) +- Add libselinux-python dependency for localhost (sdodson@redhat.com) +- oc secrets now done via oc_secret module (smilner@redhat.com) +- More fixes for reboot/wait for hosts. (dgoodwin@redhat.com) +- fix openshift_logging where defaults filter needs quoting + (jcantril@redhat.com) +- Do not hard code package names (rhcarvalho@gmail.com) +- Refactor code to access values from task_vars (rhcarvalho@gmail.com) +- oc serviceaccount now done via oc_serviceaccount module (smilner@redhat.com) +- bug 1420229. Bounce metrics components to recognize changes on updates or + upgrades (jcantril@redhat.com) +- node: simplify when conditionals (gscrivan@redhat.com) +- openvswitch: simplify when conditionals (gscrivan@redhat.com) +- uninstall: delete master-api and master-controllers (gscrivan@redhat.com) +- master: support HA deployments with system containers (gscrivan@redhat.com) +- Ensure etcd client certs are regenerated with embedded etcd. + (abutcher@redhat.com) +- bug 1420425. Allow setting of public facing certs for kibana in + openshift_logging role (jcantril@redhat.com) +- bug 1399523. Ops pvc should have different prefix from non-ops for + openshift_logging (jcantril@redhat.com) +- Include rpm/git paths in expiry README. (tbielawa@redhat.com) +- Fixing docs, linting, and comments. (kwoodson@redhat.com) +- fix bug 1420204. Default openshift_logging_use_journal to empty so fluentd + detects and is consistent with deployer (jcantril@redhat.com) +- Let pylint use as many CPUs as available (rhcarvalho@gmail.com) +- Add note about extraneous virtualenvs (rhcarvalho@gmail.com) +- Document how to create new checks (rhcarvalho@gmail.com) +- Introduce tag notation for checks (rhcarvalho@gmail.com) +- Replace multi-role checks with action plugin (rhcarvalho@gmail.com) +- Removing the /usr/bin/ansible-playbook dependency in in the spec file + (mwoodson@redhat.com) +- use the correct name for the ruamel-yaml python module (jchaloup@redhat.com) +- Reword module documentation (rhcarvalho@gmail.com) +- Separate import groups with a blank line (rhcarvalho@gmail.com) +- Remove commented-out debugging code (rhcarvalho@gmail.com) +- Replace service account secrets handling with oc_serviceaccount_secret module + (jchaloup@redhat.com) +- node: refactor Docker container tasks in a block (gscrivan@redhat.com) +- etcd: use as system container (gscrivan@redhat.com) +- Implement uninstall for system containers (gscrivan@redhat.com) +- system-containers: implement idempotent update (gscrivan@redhat.com) +- atomic-openshift: install as a system container (gscrivan@redhat.com) +- make sure cluster_size is an int for arith. ops (rmeggins@redhat.com) +- Bug 1420234 - illegal_argument_exception in Kibana UI. (rmeggins@redhat.com) +- bug 1420538. Allow users to set supplementalGroup for Cassandra + (jcantril@redhat.com) +- Document openshift_cockpit_deployer_prefix and add + openshift_cockpit_deployer_version (sdodson@redhat.com) +- Make the cert expiry playbooks runnable (tbielawa@redhat.com) +- Ensure embedded etcd config uses CA bundle. (abutcher@redhat.com) +- bug 1420684. On logging upgrade use the correct value for namespace + (jcantril@redhat.com) +- Fixing docs. (kwoodson@redhat.com) +- bug 1419962. fix openshift_metrics pwd issue after reinstall where cassandra + has incorrect pwd exception (jcantril@redhat.com) +- Fixing for linters. (kwoodson@redhat.com) +- Adding test cases. (kwoodson@redhat.com) +- Fixing docs. (kwoodson@redhat.com) +- oc process (ihorvath@redhat.com) +- node: ensure conntrack-tools is installed (gscrivan@redhat.com) +- Updating defaults to pull from previously defined variable names used in + playbooks (ewolinet@redhat.com) +- Pleasing the linting bot. (kwoodson@redhat.com) +- fixup! master: latest use same predicates as last version + (gscrivan@redhat.com) +- fixup! master: latest use same priorities as last version + (gscrivan@redhat.com) +- Adding integration tests. (kwoodson@redhat.com) +- Set image change triggers to auto=true for OCP 3.4 - for v1.5 + (simaishi@redhat.com) +- Reference class instead of self.__class__ within super constructor to avoid + calling self forever. (abutcher@redhat.com) +- Adding oc_env to lib_openshift. (kwoodson@redhat.com) +- Fixing for flake8 spacing. (kwoodson@redhat.com) +- Fixing tests for linters. (kwoodson@redhat.com) +- Adding port support for route. (kwoodson@redhat.com) +- use pvc_size instead of pv_size for openshift_metrics since the role creates + claims (jcantril@redhat.com) +- Added temporary kubeconfig file. Fixed tests to coincide with tmpfile. + (kwoodson@redhat.com) +- Set image change triggers to auto=true for OCP 3.4 + (https://github.com/ManageIQ/manageiq-pods/pull/88) (simaishi@redhat.com) +- fixes 1419839. Install only heapster for openshift_metrics when heapster + standalone flag is set (jcantril@redhat.com) +- Adding code to copy kubeconfig before running oc commands. + (kwoodson@redhat.com) +- master: latest use same predicates as last version (gscrivan@redhat.com) +- master: latest use same priorities as last version (gscrivan@redhat.com) +- Changed lib_openshift to use real temporary files. (twiest@redhat.com) +- Fixed ansible module unit and integration tests and added runners. + (twiest@redhat.com) +- Moving to ansible variable. (kwoodson@redhat.com) +- Specifying port for wait_for call. (kwoodson@redhat.com) +- Reverting commit 3257 and renaming master_url to openshift_logging_master_url + (ewolinet@redhat.com) +- [openshift_ca] Reference client binary from openshift_ca_host. + (abutcher@redhat.com) +- Fix playbooks/byo/openshift_facts.yml include path (sdodson@redhat.com) +- Add missing symlink to roles (rhcarvalho@gmail.com) +- Bump registry-console to 3.5 (sdodson@redhat.com) +- Added oc_serviceaccount_secret to lib_openshift. (twiest@redhat.com) +- fix 1406057. Allow openshift_metrics nodeselectors for components + (jcantril@redhat.com) +- Use service annotations to redeploy router service serving cert signer cert. + (abutcher@redhat.com) +- Move excluder disablement into control plane and node upgrade playbooks + (sdodson@redhat.com) +- Add excluder management to upgrade and config playbooks (sdodson@redhat.com) +- Add openshift_excluder role (sdodson@redhat.com) +- Fix RHEL Subscribe std_include path (tbielawa@redhat.com) +- Copies CloudFront pem file to registry hosts (smilner@redhat.com) +- Remove legacy router/registry certs and client configs from synchronized + master certs. (abutcher@redhat.com) +- Bump registry to 3.4 (sdodson@redhat.com) +- Sync latest image stream content (sdodson@redhat.com) +- Support latest for containerized version (gscrivan@redhat.com) +- Ensure python2-ruamel-yaml is installed (sdodson@redhat.com) +- openshift_logging link pull secret to serviceaccounts fix unlabel when + undeploying (jcantril@redhat.com) +- fixes 1414625. Fix check of keytool in openshift_metrics role + (jcantril@redhat.com) +- Doc enhancements. (kwoodson@redhat.com) +- fixes 1417261. Points playbooks to the correct 3.5 roles for logging and + metrics (jcantril@redhat.com) +- Change default docker log driver from json-file to journald. + (abutcher@redhat.com) +- Add logic to verify patched version of Ansible (rteague@redhat.com) +- Restructure certificate redeploy playbooks (abutcher@redhat.com) +- Temporary hack to skip router/registry upgrade. (dgoodwin@redhat.com) +- Fixing linters. (kwoodson@redhat.com) +- run node upgrade if master is node as part of the control plan upgrade only + (jchaloup@redhat.com) +- Appease yamllint (sdodson@redhat.com) +- Adding include_role to block to resolve when eval (ewolinet@redhat.com) +- Updating oc_apply to use command instead of shell (ewolinet@redhat.com) +- Wrap openshift_hosted_logging include_role within a block. + (abutcher@redhat.com) +- Adding unit test. Fixed redudant calls to get. (kwoodson@redhat.com) +- Fixing doc and generating new label with updated base. (kwoodson@redhat.com) +- oc_label ansible module (jdiaz@redhat.com) +- Fixing copy pasta comments. Fixed required in docs. (kwoodson@redhat.com) +- Fix openshift_hosted_logging bool typo. (abutcher@redhat.com) +- Updating oc_apply changed_when conditions, fixing filter usage for + openshift_hosted_logging playbook (ewolinet@redhat.com) +- Add default ansible.cfg file (rteague@redhat.com) +- Move current node upgrade tasks under openshift_node_upgrade role + (jchaloup@redhat.com) +- Fix host when waiting for a master system restart. (dgoodwin@redhat.com) +- Adding bool filter to when openshift_logging_use_ops evals and updating + oc_apply to handle trying to update immutable fields (ewolinet@redhat.com) +- Fixing for tox tests. (flake8|pylint) (kwoodson@redhat.com) +- Adding unit test for oc_service. Added environment fix for non-standard oc + installs. (kwoodson@redhat.com) +- Adding integration tests. (kwoodson@redhat.com) +- Adding oc_service to lib_openshift. (kwoodson@redhat.com) +- Sync etcd ca certs from etcd_ca_host to other etcd hosts + (jawed.khelil@amadeus.com) + * Tue Jan 31 2017 Scott Dodson <sdodson@redhat.com> 3.5.3-1 - Adding bool filter to ensure that we correctly set ops host for fluentd (ewolinet@redhat.com) diff --git a/playbooks/byo/openshift-cluster/upgrades/docker/docker_upgrade.yml b/playbooks/byo/openshift-cluster/upgrades/docker/docker_upgrade.yml index 4ee6afe2a..304559f6e 100644 --- a/playbooks/byo/openshift-cluster/upgrades/docker/docker_upgrade.yml +++ b/playbooks/byo/openshift-cluster/upgrades/docker/docker_upgrade.yml @@ -28,7 +28,7 @@ tasks: - name: Mark node unschedulable - oadm_manage_node: + oc_adm_manage_node: node: "{{ openshift.node.nodename | lower }}" schedulable: False delegate_to: "{{ groups.oo_first_master.0 }}" @@ -51,7 +51,7 @@ when: l_docker_upgrade is defined and l_docker_upgrade | bool - name: Set node schedulability - oadm_manage_node: + oc_adm_manage_node: node: "{{ openshift.node.nodename | lower }}" schedulable: True delegate_to: "{{ groups.oo_first_master.0 }}" diff --git a/playbooks/common/openshift-cluster/disable_excluder.yml b/playbooks/common/openshift-cluster/disable_excluder.yml index b2e025cb8..f664c51c9 100644 --- a/playbooks/common/openshift-cluster/disable_excluder.yml +++ b/playbooks/common/openshift-cluster/disable_excluder.yml @@ -1,6 +1,6 @@ --- -- name: Record excluder state and disable - hosts: l_oo_all_hosts +- name: Disable excluders + hosts: oo_masters_to_config:oo_nodes_to_config gather_facts: no tasks: diff --git a/playbooks/common/openshift-cluster/initialize_openshift_version.yml b/playbooks/common/openshift-cluster/initialize_openshift_version.yml index f8981c040..1f74e929f 100644 --- a/playbooks/common/openshift-cluster/initialize_openshift_version.yml +++ b/playbooks/common/openshift-cluster/initialize_openshift_version.yml @@ -23,6 +23,9 @@ vars: # the excluders needs to be disabled no matter what status says with_status_check: false + # Only openshift excluder needs to be temporarily disabled + # So ignore the docker one + enable_docker_excluder: false tags: - always when: openshift_upgrade_target is not defined @@ -44,6 +47,10 @@ # Re-enable excluders if they are meant to be enabled (and only during installation, upgrade disables the excluders before this play) - include: reset_excluder.yml + vars: + # Only openshift excluder needs to be re-enabled + # So ignore the docker one + enable_docker_excluder: false tags: - always when: openshift_upgrade_target is not defined diff --git a/playbooks/common/openshift-cluster/reset_excluder.yml b/playbooks/common/openshift-cluster/reset_excluder.yml index 7c544ee32..eaa8ce39c 100644 --- a/playbooks/common/openshift-cluster/reset_excluder.yml +++ b/playbooks/common/openshift-cluster/reset_excluder.yml @@ -1,6 +1,6 @@ --- - name: Re-enable excluder if it was previously enabled - hosts: l_oo_all_hosts + hosts: oo_masters_to_config:oo_nodes_to_config gather_facts: no tasks: - include_role: diff --git a/playbooks/common/openshift-cluster/upgrades/disable_excluder.yml b/playbooks/common/openshift-cluster/upgrades/disable_excluder.yml index 2a85dc92e..d1e431c5e 100644 --- a/playbooks/common/openshift-cluster/upgrades/disable_excluder.yml +++ b/playbooks/common/openshift-cluster/upgrades/disable_excluder.yml @@ -1,6 +1,6 @@ --- - name: Record excluder state and disable - hosts: l_oo_all_hosts + hosts: oo_masters_to_config:oo_nodes_to_config gather_facts: no tasks: - include: pre/validate_excluder.yml diff --git a/playbooks/common/openshift-cluster/upgrades/pre/validate_excluder.yml b/playbooks/common/openshift-cluster/upgrades/pre/validate_excluder.yml index 38d1cd0f8..6de1ed061 100644 --- a/playbooks/common/openshift-cluster/upgrades/pre/validate_excluder.yml +++ b/playbooks/common/openshift-cluster/upgrades/pre/validate_excluder.yml @@ -15,11 +15,15 @@ debug: msg: "{{ excluder }}: {{ excluder_version.stdout }}" + - name: Printing upgrade target version + debug: + msg: "{{ openshift_upgrade_target }}" + - name: Check the available {{ excluder }} version is at most of the upgrade target version fail: - msg: "Available {{ excluder }} version {{ excluder_version.stdout }} is higher than the upgrade target version {{ openshift_upgrade_target }}" + msg: "Available {{ excluder }} version {{ excluder_version.stdout }} is higher than the upgrade target version" when: - "{{ excluder_version.stdout != '' }}" - - "{{ excluder_version.stdout.split('.')[0:2] | join('.') | version_compare(openshift_upgrade_target, '>', strict=True) }}" + - "{{ excluder_version.stdout.split('.')[0:2] | join('.') | version_compare(openshift_upgrade_target.split('.')[0:2] | join('.'), '>', strict=True) }}" when: - not openshift.common.is_atomic | bool diff --git a/playbooks/common/openshift-cluster/upgrades/upgrade_control_plane.yml b/playbooks/common/openshift-cluster/upgrades/upgrade_control_plane.yml index babb7191d..e16a1f6d0 100644 --- a/playbooks/common/openshift-cluster/upgrades/upgrade_control_plane.yml +++ b/playbooks/common/openshift-cluster/upgrades/upgrade_control_plane.yml @@ -262,7 +262,7 @@ # or docker actually needs an upgrade before proceeding. Perhaps best to save this until # we merge upgrade functionality into the base roles and a normal config.yml playbook run. - name: Mark node unschedulable - oadm_manage_node: + oc_adm_manage_node: node: "{{ openshift.node.nodename | lower }}" schedulable: False delegate_to: "{{ groups.oo_first_master.0 }}" @@ -284,7 +284,7 @@ post_tasks: - name: Set node schedulability - oadm_manage_node: + oc_adm_manage_node: node: "{{ openshift.node.nodename | lower }}" schedulable: True delegate_to: "{{ groups.oo_first_master.0 }}" diff --git a/playbooks/common/openshift-cluster/upgrades/upgrade_nodes.yml b/playbooks/common/openshift-cluster/upgrades/upgrade_nodes.yml index 4e1838c71..e9f894942 100644 --- a/playbooks/common/openshift-cluster/upgrades/upgrade_nodes.yml +++ b/playbooks/common/openshift-cluster/upgrades/upgrade_nodes.yml @@ -15,7 +15,7 @@ # or docker actually needs an upgrade before proceeding. Perhaps best to save this until # we merge upgrade functionality into the base roles and a normal config.yml playbook run. - name: Mark node unschedulable - oadm_manage_node: + oc_adm_manage_node: node: "{{ openshift.node.nodename | lower }}" schedulable: False delegate_to: "{{ groups.oo_first_master.0 }}" @@ -37,7 +37,7 @@ post_tasks: - name: Set node schedulability - oadm_manage_node: + oc_adm_manage_node: node: "{{ openshift.node.nodename | lower }}" schedulable: True delegate_to: "{{ groups.oo_first_master.0 }}" diff --git a/playbooks/common/openshift-master/scaleup.yml b/playbooks/common/openshift-master/scaleup.yml index 18e5c665f..c59747081 100644 --- a/playbooks/common/openshift-master/scaleup.yml +++ b/playbooks/common/openshift-master/scaleup.yml @@ -60,8 +60,19 @@ - openshift_facts - openshift_docker +- include: ../openshift-cluster/disable_excluder.yml + vars: + # the excluders needs to be disabled no matter what status says + with_status_check: false + tags: + - always + - include: ../openshift-master/config.yml - include: ../openshift-loadbalancer/config.yml - include: ../openshift-node/config.yml + +- include: ../openshift-cluster/reset_excluder.yml + tags: + - always diff --git a/playbooks/common/openshift-node/scaleup.yml b/playbooks/common/openshift-node/scaleup.yml index bb3b1e780..d81bd152e 100644 --- a/playbooks/common/openshift-node/scaleup.yml +++ b/playbooks/common/openshift-node/scaleup.yml @@ -27,4 +27,15 @@ - openshift_facts - openshift_docker +- include: ../openshift-cluster/disable_excluder.yml + vars: + # the excluders needs to be disabled no matter what status says + with_status_check: false + tags: + - always + - include: ../openshift-node/config.yml + +- include: ../openshift-cluster/reset_excluder.yml + tags: + - always diff --git a/pytest.ini b/pytest.ini index 502fd1f46..1b0d19bb2 100644 --- a/pytest.ini +++ b/pytest.ini @@ -9,6 +9,7 @@ python_files = # is Python unittest's default, while pytest discovers both "test_*.py" and # "*_test.py" by default. test_*.py + *_test.py *_tests.py addopts = --cov=. diff --git a/roles/lib_openshift/library/oc_adm_ca_server_cert.py b/roles/lib_openshift/library/oc_adm_ca_server_cert.py index 4ecfd2bff..af1d13fe1 100644 --- a/roles/lib_openshift/library/oc_adm_ca_server_cert.py +++ b/roles/lib_openshift/library/oc_adm_ca_server_cert.py @@ -1058,9 +1058,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout diff --git a/roles/lib_openshift/library/oadm_manage_node.py b/roles/lib_openshift/library/oc_adm_manage_node.py index 8bb0538c0..0050ccf62 100644 --- a/roles/lib_openshift/library/oadm_manage_node.py +++ b/roles/lib_openshift/library/oc_adm_manage_node.py @@ -54,7 +54,7 @@ from ansible.module_utils.basic import AnsibleModule DOCUMENTATION = ''' --- -module: oadm_manage_node +module: oc_adm_manage_node short_description: Module to manage openshift nodes description: - Manage openshift nodes programmatically. @@ -126,13 +126,13 @@ extends_documentation_fragment: [] EXAMPLES = ''' - name: oadm manage-node --schedulable=true --selector=ops_node=new - oadm_manage_node: + oc_adm_manage_node: selector: ops_node=new schedulable: True register: schedout - name: oadm manage-node my-k8s-node-5 --evacuate - oadm_manage_node: + oc_adm_manage_node: node: my-k8s-node-5 evacuate: True force: True @@ -1050,9 +1050,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout @@ -1369,7 +1369,7 @@ class OpenShiftCLIConfig(object): # -*- -*- -*- End included fragment: lib/base.py -*- -*- -*- -# -*- -*- -*- Begin included fragment: class/oadm_manage_node.py -*- -*- -*- +# -*- -*- -*- Begin included fragment: class/oc_adm_manage_node.py -*- -*- -*- class ManageNodeException(Exception): @@ -1578,9 +1578,9 @@ class ManageNode(OpenShiftCLI): return {'changed': changed, 'results': results, 'state': "present"} -# -*- -*- -*- End included fragment: class/oadm_manage_node.py -*- -*- -*- +# -*- -*- -*- End included fragment: class/oc_adm_manage_node.py -*- -*- -*- -# -*- -*- -*- Begin included fragment: ansible/oadm_manage_node.py -*- -*- -*- +# -*- -*- -*- Begin included fragment: ansible/oc_adm_manage_node.py -*- -*- -*- def main(): @@ -1618,4 +1618,4 @@ def main(): if __name__ == "__main__": main() -# -*- -*- -*- End included fragment: ansible/oadm_manage_node.py -*- -*- -*- +# -*- -*- -*- End included fragment: ansible/oc_adm_manage_node.py -*- -*- -*- diff --git a/roles/lib_openshift/library/oc_adm_policy_group.py b/roles/lib_openshift/library/oc_adm_policy_group.py index 49ff22584..3d1dc1c96 100644 --- a/roles/lib_openshift/library/oc_adm_policy_group.py +++ b/roles/lib_openshift/library/oc_adm_policy_group.py @@ -1036,9 +1036,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout diff --git a/roles/lib_openshift/library/oc_adm_policy_user.py b/roles/lib_openshift/library/oc_adm_policy_user.py index bed05044c..83f2165a3 100644 --- a/roles/lib_openshift/library/oc_adm_policy_user.py +++ b/roles/lib_openshift/library/oc_adm_policy_user.py @@ -1036,9 +1036,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout diff --git a/roles/lib_openshift/library/oc_adm_registry.py b/roles/lib_openshift/library/oc_adm_registry.py index c398c5551..93cf34559 100644 --- a/roles/lib_openshift/library/oc_adm_registry.py +++ b/roles/lib_openshift/library/oc_adm_registry.py @@ -1154,9 +1154,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout @@ -2235,8 +2235,8 @@ class Registry(OpenShiftCLI): ''' prepared_registry property ''' if not self.__prepared_registry: results = self.prepare_registry() - if not results: - raise RegistryException('Could not perform registry preparation.') + if not results or ('returncode' in results and results['returncode'] != 0): + raise RegistryException('Could not perform registry preparation. {}'.format(results)) self.__prepared_registry = results return self.__prepared_registry @@ -2301,8 +2301,8 @@ class Registry(OpenShiftCLI): # probably need to parse this # pylint thinks results is a string # pylint: disable=no-member - if results['returncode'] != 0 and 'items' in results['results']: - return results + if results['returncode'] != 0 and 'items' not in results['results']: + raise RegistryException('Could not perform registry preparation. {}'.format(results)) service = None deploymentconfig = None diff --git a/roles/lib_openshift/library/oc_adm_router.py b/roles/lib_openshift/library/oc_adm_router.py index ab06a5141..e666e0d09 100644 --- a/roles/lib_openshift/library/oc_adm_router.py +++ b/roles/lib_openshift/library/oc_adm_router.py @@ -1179,9 +1179,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout diff --git a/roles/lib_openshift/library/oc_edit.py b/roles/lib_openshift/library/oc_edit.py index 7a7eaf40a..42f50ebe7 100644 --- a/roles/lib_openshift/library/oc_edit.py +++ b/roles/lib_openshift/library/oc_edit.py @@ -1078,9 +1078,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout diff --git a/roles/lib_openshift/library/oc_env.py b/roles/lib_openshift/library/oc_env.py index a1994b0f1..3088ea947 100644 --- a/roles/lib_openshift/library/oc_env.py +++ b/roles/lib_openshift/library/oc_env.py @@ -1045,9 +1045,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout diff --git a/roles/lib_openshift/library/oc_group.py b/roles/lib_openshift/library/oc_group.py new file mode 100644 index 000000000..44611df82 --- /dev/null +++ b/roles/lib_openshift/library/oc_group.py @@ -0,0 +1,1560 @@ +#!/usr/bin/env python +# pylint: disable=missing-docstring +# flake8: noqa: T001 +# ___ ___ _ _ ___ ___ _ _____ ___ ___ +# / __| __| \| | __| _ \ /_\_ _| __| \ +# | (_ | _|| .` | _|| / / _ \| | | _|| |) | +# \___|___|_|\_|___|_|_\/_/_\_\_|_|___|___/_ _____ +# | \ / _ \ | \| |/ _ \_ _| | __| \_ _|_ _| +# | |) | (_) | | .` | (_) || | | _|| |) | | | | +# |___/ \___/ |_|\_|\___/ |_| |___|___/___| |_| +# +# Copyright 2016 Red Hat, Inc. and/or its affiliates +# and other contributors as indicated by the @author tags. +# +# 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. +# + +# -*- -*- -*- Begin included fragment: lib/import.py -*- -*- -*- +''' + OpenShiftCLI class that wraps the oc commands in a subprocess +''' +# pylint: disable=too-many-lines + +from __future__ import print_function +import atexit +import copy +import json +import os +import re +import shutil +import subprocess +import tempfile +# pylint: disable=import-error +try: + import ruamel.yaml as yaml +except ImportError: + import yaml + +from ansible.module_utils.basic import AnsibleModule + +# -*- -*- -*- End included fragment: lib/import.py -*- -*- -*- + +# -*- -*- -*- Begin included fragment: doc/group -*- -*- -*- + +DOCUMENTATION = ''' +--- +module: oc_group +short_description: Modify, and idempotently manage openshift groups. +description: + - Modify openshift groups programmatically. +options: + state: + description: + - Supported states, present, absent, list + - present - will ensure object is created or updated to the value specified + - list - will return a group + - absent - will remove the group + required: False + default: present + choices: ["present", 'absent', 'list'] + aliases: [] + kubeconfig: + description: + - The path for the kubeconfig file to use for authentication + required: false + default: /etc/origin/master/admin.kubeconfig + aliases: [] + debug: + description: + - Turn on debug output. + required: false + default: False + aliases: [] + name: + description: + - Name of the object that is being queried. + required: false + default: None + aliases: [] + namespace: + description: + - The namespace where the object lives. + required: false + default: str + aliases: [] +author: +- "Joel Diaz <jdiaz@redhat.com>" +extends_documentation_fragment: [] +''' + +EXAMPLES = ''' +- name: create group + oc_group: + state: present + name: acme_org + register: group_out +''' + +# -*- -*- -*- End included fragment: doc/group -*- -*- -*- + +# -*- -*- -*- Begin included fragment: ../../lib_utils/src/class/yedit.py -*- -*- -*- +# pylint: disable=undefined-variable,missing-docstring +# noqa: E301,E302 + + +class YeditException(Exception): + ''' Exception class for Yedit ''' + pass + + +# pylint: disable=too-many-public-methods +class Yedit(object): + ''' Class to modify yaml files ''' + re_valid_key = r"(((\[-?\d+\])|([0-9a-zA-Z%s/_-]+)).?)+$" + re_key = r"(?:\[(-?\d+)\])|([0-9a-zA-Z%s/_-]+)" + com_sep = set(['.', '#', '|', ':']) + + # pylint: disable=too-many-arguments + def __init__(self, + filename=None, + content=None, + content_type='yaml', + separator='.', + backup=False): + self.content = content + self._separator = separator + self.filename = filename + self.__yaml_dict = content + self.content_type = content_type + self.backup = backup + self.load(content_type=self.content_type) + if self.__yaml_dict is None: + self.__yaml_dict = {} + + @property + def separator(self): + ''' getter method for yaml_dict ''' + return self._separator + + @separator.setter + def separator(self): + ''' getter method for yaml_dict ''' + return self._separator + + @property + def yaml_dict(self): + ''' getter method for yaml_dict ''' + return self.__yaml_dict + + @yaml_dict.setter + def yaml_dict(self, value): + ''' setter method for yaml_dict ''' + self.__yaml_dict = value + + @staticmethod + def parse_key(key, sep='.'): + '''parse the key allowing the appropriate separator''' + common_separators = list(Yedit.com_sep - set([sep])) + return re.findall(Yedit.re_key % ''.join(common_separators), key) + + @staticmethod + def valid_key(key, sep='.'): + '''validate the incoming key''' + common_separators = list(Yedit.com_sep - set([sep])) + if not re.match(Yedit.re_valid_key % ''.join(common_separators), key): + return False + + return True + + @staticmethod + def remove_entry(data, key, sep='.'): + ''' remove data at location key ''' + if key == '' and isinstance(data, dict): + data.clear() + return True + elif key == '' and isinstance(data, list): + del data[:] + return True + + if not (key and Yedit.valid_key(key, sep)) and \ + isinstance(data, (list, dict)): + return None + + key_indexes = Yedit.parse_key(key, sep) + for arr_ind, dict_key in key_indexes[:-1]: + if dict_key and isinstance(data, dict): + data = data.get(dict_key, None) + elif (arr_ind and isinstance(data, list) and + int(arr_ind) <= len(data) - 1): + data = data[int(arr_ind)] + else: + return None + + # process last index for remove + # expected list entry + if key_indexes[-1][0]: + if isinstance(data, list) and int(key_indexes[-1][0]) <= len(data) - 1: # noqa: E501 + del data[int(key_indexes[-1][0])] + return True + + # expected dict entry + elif key_indexes[-1][1]: + if isinstance(data, dict): + del data[key_indexes[-1][1]] + return True + + @staticmethod + def add_entry(data, key, item=None, sep='.'): + ''' Get an item from a dictionary with key notation a.b.c + d = {'a': {'b': 'c'}}} + key = a#b + return c + ''' + if key == '': + pass + elif (not (key and Yedit.valid_key(key, sep)) and + isinstance(data, (list, dict))): + return None + + key_indexes = Yedit.parse_key(key, sep) + for arr_ind, dict_key in key_indexes[:-1]: + if dict_key: + if isinstance(data, dict) and dict_key in data and data[dict_key]: # noqa: E501 + data = data[dict_key] + continue + + elif data and not isinstance(data, dict): + raise YeditException("Unexpected item type found while going through key " + + "path: {} (at key: {})".format(key, dict_key)) + + data[dict_key] = {} + data = data[dict_key] + + elif (arr_ind and isinstance(data, list) and + int(arr_ind) <= len(data) - 1): + data = data[int(arr_ind)] + else: + raise YeditException("Unexpected item type found while going through key path: {}".format(key)) + + if key == '': + data = item + + # process last index for add + # expected list entry + elif key_indexes[-1][0] and isinstance(data, list) and int(key_indexes[-1][0]) <= len(data) - 1: # noqa: E501 + data[int(key_indexes[-1][0])] = item + + # expected dict entry + elif key_indexes[-1][1] and isinstance(data, dict): + data[key_indexes[-1][1]] = item + + # didn't add/update to an existing list, nor add/update key to a dict + # so we must have been provided some syntax like a.b.c[<int>] = "data" for a + # non-existent array + else: + raise YeditException("Error adding to object at path: {}".format(key)) + + return data + + @staticmethod + def get_entry(data, key, sep='.'): + ''' Get an item from a dictionary with key notation a.b.c + d = {'a': {'b': 'c'}}} + key = a.b + return c + ''' + if key == '': + pass + elif (not (key and Yedit.valid_key(key, sep)) and + isinstance(data, (list, dict))): + return None + + key_indexes = Yedit.parse_key(key, sep) + for arr_ind, dict_key in key_indexes: + if dict_key and isinstance(data, dict): + data = data.get(dict_key, None) + elif (arr_ind and isinstance(data, list) and + int(arr_ind) <= len(data) - 1): + data = data[int(arr_ind)] + else: + return None + + return data + + @staticmethod + def _write(filename, contents): + ''' Actually write the file contents to disk. This helps with mocking. ''' + + tmp_filename = filename + '.yedit' + + with open(tmp_filename, 'w') as yfd: + yfd.write(contents) + + os.rename(tmp_filename, filename) + + def write(self): + ''' write to file ''' + if not self.filename: + raise YeditException('Please specify a filename.') + + if self.backup and self.file_exists(): + shutil.copy(self.filename, self.filename + '.orig') + + # Try to set format attributes if supported + try: + self.yaml_dict.fa.set_block_style() + except AttributeError: + pass + + # Try to use RoundTripDumper if supported. + try: + Yedit._write(self.filename, yaml.dump(self.yaml_dict, Dumper=yaml.RoundTripDumper)) + except AttributeError: + Yedit._write(self.filename, yaml.safe_dump(self.yaml_dict, default_flow_style=False)) + + return (True, self.yaml_dict) + + def read(self): + ''' read from file ''' + # check if it exists + if self.filename is None or not self.file_exists(): + return None + + contents = None + with open(self.filename) as yfd: + contents = yfd.read() + + return contents + + def file_exists(self): + ''' return whether file exists ''' + if os.path.exists(self.filename): + return True + + return False + + def load(self, content_type='yaml'): + ''' return yaml file ''' + contents = self.read() + + if not contents and not self.content: + return None + + if self.content: + if isinstance(self.content, dict): + self.yaml_dict = self.content + return self.yaml_dict + elif isinstance(self.content, str): + contents = self.content + + # check if it is yaml + try: + if content_type == 'yaml' and contents: + # Try to set format attributes if supported + try: + self.yaml_dict.fa.set_block_style() + except AttributeError: + pass + + # Try to use RoundTripLoader if supported. + try: + self.yaml_dict = yaml.safe_load(contents, yaml.RoundTripLoader) + except AttributeError: + self.yaml_dict = yaml.safe_load(contents) + + # Try to set format attributes if supported + try: + self.yaml_dict.fa.set_block_style() + except AttributeError: + pass + + elif content_type == 'json' and contents: + self.yaml_dict = json.loads(contents) + except yaml.YAMLError as err: + # Error loading yaml or json + raise YeditException('Problem with loading yaml file. %s' % err) + + return self.yaml_dict + + def get(self, key): + ''' get a specified key''' + try: + entry = Yedit.get_entry(self.yaml_dict, key, self.separator) + except KeyError: + entry = None + + return entry + + def pop(self, path, key_or_item): + ''' remove a key, value pair from a dict or an item for a list''' + try: + entry = Yedit.get_entry(self.yaml_dict, path, self.separator) + except KeyError: + entry = None + + if entry is None: + return (False, self.yaml_dict) + + if isinstance(entry, dict): + # AUDIT:maybe-no-member makes sense due to fuzzy types + # pylint: disable=maybe-no-member + if key_or_item in entry: + entry.pop(key_or_item) + return (True, self.yaml_dict) + return (False, self.yaml_dict) + + elif isinstance(entry, list): + # AUDIT:maybe-no-member makes sense due to fuzzy types + # pylint: disable=maybe-no-member + ind = None + try: + ind = entry.index(key_or_item) + except ValueError: + return (False, self.yaml_dict) + + entry.pop(ind) + return (True, self.yaml_dict) + + return (False, self.yaml_dict) + + def delete(self, path): + ''' remove path from a dict''' + try: + entry = Yedit.get_entry(self.yaml_dict, path, self.separator) + except KeyError: + entry = None + + if entry is None: + return (False, self.yaml_dict) + + result = Yedit.remove_entry(self.yaml_dict, path, self.separator) + if not result: + return (False, self.yaml_dict) + + return (True, self.yaml_dict) + + def exists(self, path, value): + ''' check if value exists at path''' + try: + entry = Yedit.get_entry(self.yaml_dict, path, self.separator) + except KeyError: + entry = None + + if isinstance(entry, list): + if value in entry: + return True + return False + + elif isinstance(entry, dict): + if isinstance(value, dict): + rval = False + for key, val in value.items(): + if entry[key] != val: + rval = False + break + else: + rval = True + return rval + + return value in entry + + return entry == value + + def append(self, path, value): + '''append value to a list''' + try: + entry = Yedit.get_entry(self.yaml_dict, path, self.separator) + except KeyError: + entry = None + + if entry is None: + self.put(path, []) + entry = Yedit.get_entry(self.yaml_dict, path, self.separator) + if not isinstance(entry, list): + return (False, self.yaml_dict) + + # AUDIT:maybe-no-member makes sense due to loading data from + # a serialized format. + # pylint: disable=maybe-no-member + entry.append(value) + return (True, self.yaml_dict) + + # pylint: disable=too-many-arguments + def update(self, path, value, index=None, curr_value=None): + ''' put path, value into a dict ''' + try: + entry = Yedit.get_entry(self.yaml_dict, path, self.separator) + except KeyError: + entry = None + + if isinstance(entry, dict): + # AUDIT:maybe-no-member makes sense due to fuzzy types + # pylint: disable=maybe-no-member + if not isinstance(value, dict): + raise YeditException('Cannot replace key, value entry in ' + + 'dict with non-dict type. value=[%s] [%s]' % (value, type(value))) # noqa: E501 + + entry.update(value) + return (True, self.yaml_dict) + + elif isinstance(entry, list): + # AUDIT:maybe-no-member makes sense due to fuzzy types + # pylint: disable=maybe-no-member + ind = None + if curr_value: + try: + ind = entry.index(curr_value) + except ValueError: + return (False, self.yaml_dict) + + elif index is not None: + ind = index + + if ind is not None and entry[ind] != value: + entry[ind] = value + return (True, self.yaml_dict) + + # see if it exists in the list + try: + ind = entry.index(value) + except ValueError: + # doesn't exist, append it + entry.append(value) + return (True, self.yaml_dict) + + # already exists, return + if ind is not None: + return (False, self.yaml_dict) + return (False, self.yaml_dict) + + def put(self, path, value): + ''' put path, value into a dict ''' + try: + entry = Yedit.get_entry(self.yaml_dict, path, self.separator) + except KeyError: + entry = None + + if entry == value: + return (False, self.yaml_dict) + + # deepcopy didn't work + # Try to use ruamel.yaml and fallback to pyyaml + try: + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, + default_flow_style=False), + yaml.RoundTripLoader) + except AttributeError: + tmp_copy = copy.deepcopy(self.yaml_dict) + + # set the format attributes if available + try: + tmp_copy.fa.set_block_style() + except AttributeError: + pass + + result = Yedit.add_entry(tmp_copy, path, value, self.separator) + if not result: + return (False, self.yaml_dict) + + self.yaml_dict = tmp_copy + + return (True, self.yaml_dict) + + def create(self, path, value): + ''' create a yaml file ''' + if not self.file_exists(): + # deepcopy didn't work + # Try to use ruamel.yaml and fallback to pyyaml + try: + tmp_copy = yaml.load(yaml.round_trip_dump(self.yaml_dict, + default_flow_style=False), + yaml.RoundTripLoader) + except AttributeError: + tmp_copy = copy.deepcopy(self.yaml_dict) + + # set the format attributes if available + try: + tmp_copy.fa.set_block_style() + except AttributeError: + pass + + result = Yedit.add_entry(tmp_copy, path, value, self.separator) + if result: + self.yaml_dict = tmp_copy + return (True, self.yaml_dict) + + return (False, self.yaml_dict) + + @staticmethod + def get_curr_value(invalue, val_type): + '''return the current value''' + if invalue is None: + return None + + curr_value = invalue + if val_type == 'yaml': + curr_value = yaml.load(invalue) + elif val_type == 'json': + curr_value = json.loads(invalue) + + return curr_value + + @staticmethod + def parse_value(inc_value, vtype=''): + '''determine value type passed''' + true_bools = ['y', 'Y', 'yes', 'Yes', 'YES', 'true', 'True', 'TRUE', + 'on', 'On', 'ON', ] + false_bools = ['n', 'N', 'no', 'No', 'NO', 'false', 'False', 'FALSE', + 'off', 'Off', 'OFF'] + + # It came in as a string but you didn't specify value_type as string + # we will convert to bool if it matches any of the above cases + if isinstance(inc_value, str) and 'bool' in vtype: + if inc_value not in true_bools and inc_value not in false_bools: + raise YeditException('Not a boolean type. str=[%s] vtype=[%s]' + % (inc_value, vtype)) + elif isinstance(inc_value, bool) and 'str' in vtype: + inc_value = str(inc_value) + + # If vtype is not str then go ahead and attempt to yaml load it. + if isinstance(inc_value, str) and 'str' not in vtype: + try: + inc_value = yaml.load(inc_value) + except Exception: + raise YeditException('Could not determine type of incoming ' + + 'value. value=[%s] vtype=[%s]' + % (type(inc_value), vtype)) + + return inc_value + + # pylint: disable=too-many-return-statements,too-many-branches + @staticmethod + def run_ansible(module): + '''perform the idempotent crud operations''' + yamlfile = Yedit(filename=module.params['src'], + backup=module.params['backup'], + separator=module.params['separator']) + + if module.params['src']: + rval = yamlfile.load() + + if yamlfile.yaml_dict is None and \ + module.params['state'] != 'present': + return {'failed': True, + 'msg': 'Error opening file [%s]. Verify that the ' + + 'file exists, that it is has correct' + + ' permissions, and is valid yaml.'} + + if module.params['state'] == 'list': + if module.params['content']: + content = Yedit.parse_value(module.params['content'], + module.params['content_type']) + yamlfile.yaml_dict = content + + if module.params['key']: + rval = yamlfile.get(module.params['key']) or {} + + return {'changed': False, 'result': rval, 'state': "list"} + + elif module.params['state'] == 'absent': + if module.params['content']: + content = Yedit.parse_value(module.params['content'], + module.params['content_type']) + yamlfile.yaml_dict = content + + if module.params['update']: + rval = yamlfile.pop(module.params['key'], + module.params['value']) + else: + rval = yamlfile.delete(module.params['key']) + + if rval[0] and module.params['src']: + yamlfile.write() + + return {'changed': rval[0], 'result': rval[1], 'state': "absent"} + + elif module.params['state'] == 'present': + # check if content is different than what is in the file + if module.params['content']: + content = Yedit.parse_value(module.params['content'], + module.params['content_type']) + + # We had no edits to make and the contents are the same + if yamlfile.yaml_dict == content and \ + module.params['value'] is None: + return {'changed': False, + 'result': yamlfile.yaml_dict, + 'state': "present"} + + yamlfile.yaml_dict = content + + # we were passed a value; parse it + if module.params['value']: + value = Yedit.parse_value(module.params['value'], + module.params['value_type']) + key = module.params['key'] + if module.params['update']: + # pylint: disable=line-too-long + curr_value = Yedit.get_curr_value(Yedit.parse_value(module.params['curr_value']), # noqa: E501 + module.params['curr_value_format']) # noqa: E501 + + rval = yamlfile.update(key, value, module.params['index'], curr_value) # noqa: E501 + + elif module.params['append']: + rval = yamlfile.append(key, value) + else: + rval = yamlfile.put(key, value) + + if rval[0] and module.params['src']: + yamlfile.write() + + return {'changed': rval[0], + 'result': rval[1], 'state': "present"} + + # no edits to make + if module.params['src']: + # pylint: disable=redefined-variable-type + rval = yamlfile.write() + return {'changed': rval[0], + 'result': rval[1], + 'state': "present"} + + return {'failed': True, 'msg': 'Unkown state passed'} + +# -*- -*- -*- End included fragment: ../../lib_utils/src/class/yedit.py -*- -*- -*- + +# -*- -*- -*- Begin included fragment: lib/base.py -*- -*- -*- +# pylint: disable=too-many-lines +# noqa: E301,E302,E303,T001 + + +class OpenShiftCLIError(Exception): + '''Exception class for openshiftcli''' + pass + + +ADDITIONAL_PATH_LOOKUPS = ['/usr/local/bin', os.path.expanduser('~/bin')] + + +def locate_oc_binary(): + ''' Find and return oc binary file ''' + # https://github.com/openshift/openshift-ansible/issues/3410 + # oc can be in /usr/local/bin in some cases, but that may not + # be in $PATH due to ansible/sudo + paths = os.environ.get("PATH", os.defpath).split(os.pathsep) + ADDITIONAL_PATH_LOOKUPS + + oc_binary = 'oc' + + # Use shutil.which if it is available, otherwise fallback to a naive path search + try: + which_result = shutil.which(oc_binary, path=os.pathsep.join(paths)) + if which_result is not None: + oc_binary = which_result + except AttributeError: + for path in paths: + if os.path.exists(os.path.join(path, oc_binary)): + oc_binary = os.path.join(path, oc_binary) + break + + return oc_binary + + +# pylint: disable=too-few-public-methods +class OpenShiftCLI(object): + ''' Class to wrap the command line tools ''' + def __init__(self, + namespace, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False, + all_namespaces=False): + ''' Constructor for OpenshiftCLI ''' + self.namespace = namespace + self.verbose = verbose + self.kubeconfig = Utils.create_tmpfile_copy(kubeconfig) + self.all_namespaces = all_namespaces + self.oc_binary = locate_oc_binary() + + # Pylint allows only 5 arguments to be passed. + # pylint: disable=too-many-arguments + def _replace_content(self, resource, rname, content, force=False, sep='.'): + ''' replace the current object with the content ''' + res = self._get(resource, rname) + if not res['results']: + return res + + fname = Utils.create_tmpfile(rname + '-') + + yed = Yedit(fname, res['results'][0], separator=sep) + changes = [] + for key, value in content.items(): + changes.append(yed.put(key, value)) + + if any([change[0] for change in changes]): + yed.write() + + atexit.register(Utils.cleanup, [fname]) + + return self._replace(fname, force) + + return {'returncode': 0, 'updated': False} + + def _replace(self, fname, force=False): + '''replace the current object with oc replace''' + cmd = ['replace', '-f', fname] + if force: + cmd.append('--force') + return self.openshift_cmd(cmd) + + def _create_from_content(self, rname, content): + '''create a temporary file and then call oc create on it''' + fname = Utils.create_tmpfile(rname + '-') + yed = Yedit(fname, content=content) + yed.write() + + atexit.register(Utils.cleanup, [fname]) + + return self._create(fname) + + def _create(self, fname): + '''call oc create on a filename''' + return self.openshift_cmd(['create', '-f', fname]) + + def _delete(self, resource, rname, selector=None): + '''call oc delete on a resource''' + cmd = ['delete', resource, rname] + if selector: + cmd.append('--selector=%s' % selector) + + return self.openshift_cmd(cmd) + + def _process(self, template_name, create=False, params=None, template_data=None): # noqa: E501 + '''process a template + + template_name: the name of the template to process + create: whether to send to oc create after processing + params: the parameters for the template + template_data: the incoming template's data; instead of a file + ''' + cmd = ['process'] + if template_data: + cmd.extend(['-f', '-']) + else: + cmd.append(template_name) + if params: + param_str = ["%s=%s" % (key, value) for key, value in params.items()] + cmd.append('-v') + cmd.extend(param_str) + + results = self.openshift_cmd(cmd, output=True, input_data=template_data) + + if results['returncode'] != 0 or not create: + return results + + fname = Utils.create_tmpfile(template_name + '-') + yed = Yedit(fname, results['results']) + yed.write() + + atexit.register(Utils.cleanup, [fname]) + + return self.openshift_cmd(['create', '-f', fname]) + + def _get(self, resource, rname=None, selector=None): + '''return a resource by name ''' + cmd = ['get', resource] + if selector: + cmd.append('--selector=%s' % selector) + elif rname: + cmd.append(rname) + + cmd.extend(['-o', 'json']) + + rval = self.openshift_cmd(cmd, output=True) + + # Ensure results are retuned in an array + if 'items' in rval: + rval['results'] = rval['items'] + elif not isinstance(rval['results'], list): + rval['results'] = [rval['results']] + + return rval + + def _schedulable(self, node=None, selector=None, schedulable=True): + ''' perform oadm manage-node scheduable ''' + cmd = ['manage-node'] + if node: + cmd.extend(node) + else: + cmd.append('--selector=%s' % selector) + + cmd.append('--schedulable=%s' % schedulable) + + return self.openshift_cmd(cmd, oadm=True, output=True, output_type='raw') # noqa: E501 + + def _list_pods(self, node=None, selector=None, pod_selector=None): + ''' perform oadm list pods + + node: the node in which to list pods + selector: the label selector filter if provided + pod_selector: the pod selector filter if provided + ''' + cmd = ['manage-node'] + if node: + cmd.extend(node) + else: + cmd.append('--selector=%s' % selector) + + if pod_selector: + cmd.append('--pod-selector=%s' % pod_selector) + + cmd.extend(['--list-pods', '-o', 'json']) + + return self.openshift_cmd(cmd, oadm=True, output=True, output_type='raw') + + # pylint: disable=too-many-arguments + def _evacuate(self, node=None, selector=None, pod_selector=None, dry_run=False, grace_period=None, force=False): + ''' perform oadm manage-node evacuate ''' + cmd = ['manage-node'] + if node: + cmd.extend(node) + else: + cmd.append('--selector=%s' % selector) + + if dry_run: + cmd.append('--dry-run') + + if pod_selector: + cmd.append('--pod-selector=%s' % pod_selector) + + if grace_period: + cmd.append('--grace-period=%s' % int(grace_period)) + + if force: + cmd.append('--force') + + cmd.append('--evacuate') + + return self.openshift_cmd(cmd, oadm=True, output=True, output_type='raw') + + def _version(self): + ''' return the openshift version''' + return self.openshift_cmd(['version'], output=True, output_type='raw') + + def _import_image(self, url=None, name=None, tag=None): + ''' perform image import ''' + cmd = ['import-image'] + + image = '{0}'.format(name) + if tag: + image += ':{0}'.format(tag) + + cmd.append(image) + + if url: + cmd.append('--from={0}/{1}'.format(url, image)) + + cmd.append('-n{0}'.format(self.namespace)) + + cmd.append('--confirm') + return self.openshift_cmd(cmd) + + def _run(self, cmds, input_data): + ''' Actually executes the command. This makes mocking easier. ''' + curr_env = os.environ.copy() + curr_env.update({'KUBECONFIG': self.kubeconfig}) + proc = subprocess.Popen(cmds, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=curr_env) + + stdout, stderr = proc.communicate(input_data) + + return proc.returncode, stdout.decode(), stderr.decode() + + # pylint: disable=too-many-arguments,too-many-branches + def openshift_cmd(self, cmd, oadm=False, output=False, output_type='json', input_data=None): + '''Base command for oc ''' + cmds = [self.oc_binary] + + if oadm: + cmds.append('adm') + + cmds.extend(cmd) + + if self.all_namespaces: + cmds.extend(['--all-namespaces']) + elif self.namespace is not None and self.namespace.lower() not in ['none', 'emtpy']: # E501 + cmds.extend(['-n', self.namespace]) + + rval = {} + results = '' + err = None + + if self.verbose: + print(' '.join(cmds)) + + try: + returncode, stdout, stderr = self._run(cmds, input_data) + except OSError as ex: + returncode, stdout, stderr = 1, '', 'Failed to execute {}: {}'.format(subprocess.list2cmdline(cmds), ex) + + rval = {"returncode": returncode, + "results": results, + "cmd": ' '.join(cmds)} + + if returncode == 0: + if output: + if output_type == 'json': + try: + rval['results'] = json.loads(stdout) + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args + elif output_type == 'raw': + rval['results'] = stdout + + if self.verbose: + print("STDOUT: {0}".format(stdout)) + print("STDERR: {0}".format(stderr)) + + if err: + rval.update({"err": err, + "stderr": stderr, + "stdout": stdout, + "cmd": cmds}) + + else: + rval.update({"stderr": stderr, + "stdout": stdout, + "results": {}}) + + return rval + + +class Utils(object): + ''' utilities for openshiftcli modules ''' + + @staticmethod + def _write(filename, contents): + ''' Actually write the file contents to disk. This helps with mocking. ''' + + with open(filename, 'w') as sfd: + sfd.write(contents) + + @staticmethod + def create_tmp_file_from_contents(rname, data, ftype='yaml'): + ''' create a file in tmp with name and contents''' + + tmp = Utils.create_tmpfile(prefix=rname) + + if ftype == 'yaml': + # AUDIT:no-member makes sense here due to ruamel.YAML/PyYAML usage + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripDumper'): + Utils._write(tmp, yaml.dump(data, Dumper=yaml.RoundTripDumper)) + else: + Utils._write(tmp, yaml.safe_dump(data, default_flow_style=False)) + + elif ftype == 'json': + Utils._write(tmp, json.dumps(data)) + else: + Utils._write(tmp, data) + + # Register cleanup when module is done + atexit.register(Utils.cleanup, [tmp]) + return tmp + + @staticmethod + def create_tmpfile_copy(inc_file): + '''create a temporary copy of a file''' + tmpfile = Utils.create_tmpfile('lib_openshift-') + Utils._write(tmpfile, open(inc_file).read()) + + # Cleanup the tmpfile + atexit.register(Utils.cleanup, [tmpfile]) + + return tmpfile + + @staticmethod + def create_tmpfile(prefix='tmp'): + ''' Generates and returns a temporary file name ''' + + with tempfile.NamedTemporaryFile(prefix=prefix, delete=False) as tmp: + return tmp.name + + @staticmethod + def create_tmp_files_from_contents(content, content_type=None): + '''Turn an array of dict: filename, content into a files array''' + if not isinstance(content, list): + content = [content] + files = [] + for item in content: + path = Utils.create_tmp_file_from_contents(item['path'] + '-', + item['data'], + ftype=content_type) + files.append({'name': os.path.basename(item['path']), + 'path': path}) + return files + + @staticmethod + def cleanup(files): + '''Clean up on exit ''' + for sfile in files: + if os.path.exists(sfile): + if os.path.isdir(sfile): + shutil.rmtree(sfile) + elif os.path.isfile(sfile): + os.remove(sfile) + + @staticmethod + def exists(results, _name): + ''' Check to see if the results include the name ''' + if not results: + return False + + if Utils.find_result(results, _name): + return True + + return False + + @staticmethod + def find_result(results, _name): + ''' Find the specified result by name''' + rval = None + for result in results: + if 'metadata' in result and result['metadata']['name'] == _name: + rval = result + break + + return rval + + @staticmethod + def get_resource_file(sfile, sfile_type='yaml'): + ''' return the service file ''' + contents = None + with open(sfile) as sfd: + contents = sfd.read() + + if sfile_type == 'yaml': + # AUDIT:no-member makes sense here due to ruamel.YAML/PyYAML usage + # pylint: disable=no-member + if hasattr(yaml, 'RoundTripLoader'): + contents = yaml.load(contents, yaml.RoundTripLoader) + else: + contents = yaml.safe_load(contents) + elif sfile_type == 'json': + contents = json.loads(contents) + + return contents + + @staticmethod + def filter_versions(stdout): + ''' filter the oc version output ''' + + version_dict = {} + version_search = ['oc', 'openshift', 'kubernetes'] + + for line in stdout.strip().split('\n'): + for term in version_search: + if not line: + continue + if line.startswith(term): + version_dict[term] = line.split()[-1] + + # horrible hack to get openshift version in Openshift 3.2 + # By default "oc version in 3.2 does not return an "openshift" version + if "openshift" not in version_dict: + version_dict["openshift"] = version_dict["oc"] + + return version_dict + + @staticmethod + def add_custom_versions(versions): + ''' create custom versions strings ''' + + versions_dict = {} + + for tech, version in versions.items(): + # clean up "-" from version + if "-" in version: + version = version.split("-")[0] + + if version.startswith('v'): + versions_dict[tech + '_numeric'] = version[1:].split('+')[0] + # "v3.3.0.33" is what we have, we want "3.3" + versions_dict[tech + '_short'] = version[1:4] + + return versions_dict + + @staticmethod + def openshift_installed(): + ''' check if openshift is installed ''' + import yum + + yum_base = yum.YumBase() + if yum_base.rpmdb.searchNevra(name='atomic-openshift'): + return True + + return False + + # Disabling too-many-branches. This is a yaml dictionary comparison function + # pylint: disable=too-many-branches,too-many-return-statements,too-many-statements + @staticmethod + def check_def_equal(user_def, result_def, skip_keys=None, debug=False): + ''' Given a user defined definition, compare it with the results given back by our query. ''' + + # Currently these values are autogenerated and we do not need to check them + skip = ['metadata', 'status'] + if skip_keys: + skip.extend(skip_keys) + + for key, value in result_def.items(): + if key in skip: + continue + + # Both are lists + if isinstance(value, list): + if key not in user_def: + if debug: + print('User data does not have key [%s]' % key) + print('User data: %s' % user_def) + return False + + if not isinstance(user_def[key], list): + if debug: + print('user_def[key] is not a list key=[%s] user_def[key]=%s' % (key, user_def[key])) + return False + + if len(user_def[key]) != len(value): + if debug: + print("List lengths are not equal.") + print("key=[%s]: user_def[%s] != value[%s]" % (key, len(user_def[key]), len(value))) + print("user_def: %s" % user_def[key]) + print("value: %s" % value) + return False + + for values in zip(user_def[key], value): + if isinstance(values[0], dict) and isinstance(values[1], dict): + if debug: + print('sending list - list') + print(type(values[0])) + print(type(values[1])) + result = Utils.check_def_equal(values[0], values[1], skip_keys=skip_keys, debug=debug) + if not result: + print('list compare returned false') + return False + + elif value != user_def[key]: + if debug: + print('value should be identical') + print(user_def[key]) + print(value) + return False + + # recurse on a dictionary + elif isinstance(value, dict): + if key not in user_def: + if debug: + print("user_def does not have key [%s]" % key) + return False + if not isinstance(user_def[key], dict): + if debug: + print("dict returned false: not instance of dict") + return False + + # before passing ensure keys match + api_values = set(value.keys()) - set(skip) + user_values = set(user_def[key].keys()) - set(skip) + if api_values != user_values: + if debug: + print("keys are not equal in dict") + print(user_values) + print(api_values) + return False + + result = Utils.check_def_equal(user_def[key], value, skip_keys=skip_keys, debug=debug) + if not result: + if debug: + print("dict returned false") + print(result) + return False + + # Verify each key, value pair is the same + else: + if key not in user_def or value != user_def[key]: + if debug: + print("value not equal; user_def does not have key") + print(key) + print(value) + if key in user_def: + print(user_def[key]) + return False + + if debug: + print('returning true') + return True + + +class OpenShiftCLIConfig(object): + '''Generic Config''' + def __init__(self, rname, namespace, kubeconfig, options): + self.kubeconfig = kubeconfig + self.name = rname + self.namespace = namespace + self._options = options + + @property + def config_options(self): + ''' return config options ''' + return self._options + + def to_option_list(self): + '''return all options as a string''' + return self.stringify() + + def stringify(self): + ''' return the options hash as cli params in a string ''' + rval = [] + for key in sorted(self.config_options.keys()): + data = self.config_options[key] + if data['include'] \ + and (data['value'] or isinstance(data['value'], int)): + rval.append('--{}={}'.format(key.replace('_', '-'), data['value'])) + + return rval + + +# -*- -*- -*- End included fragment: lib/base.py -*- -*- -*- + +# -*- -*- -*- Begin included fragment: lib/group.py -*- -*- -*- + + +class GroupConfig(object): + ''' Handle route options ''' + # pylint: disable=too-many-arguments + def __init__(self, + sname, + namespace, + kubeconfig): + ''' constructor for handling group options ''' + self.kubeconfig = kubeconfig + self.name = sname + self.namespace = namespace + self.data = {} + + self.create_dict() + + def create_dict(self): + ''' return a service as a dict ''' + self.data['apiVersion'] = 'v1' + self.data['kind'] = 'Group' + self.data['metadata'] = {} + self.data['metadata']['name'] = self.name + self.data['users'] = None + + +# pylint: disable=too-many-instance-attributes +class Group(Yedit): + ''' Class to wrap the oc command line tools ''' + kind = 'group' + + def __init__(self, content): + '''Group constructor''' + super(Group, self).__init__(content=content) + +# -*- -*- -*- End included fragment: lib/group.py -*- -*- -*- + +# -*- -*- -*- Begin included fragment: class/oc_group.py -*- -*- -*- + + +class OCGroup(OpenShiftCLI): + ''' Class to wrap the oc command line tools ''' + kind = 'group' + + def __init__(self, + config, + verbose=False): + ''' Constructor for OCGroup ''' + super(OCGroup, self).__init__(config.namespace, config.kubeconfig) + self.config = config + self.namespace = config.namespace + self._group = None + + @property + def group(self): + ''' property function service''' + if not self._group: + self.get() + return self._group + + @group.setter + def group(self, data): + ''' setter function for yedit var ''' + self._group = data + + def exists(self): + ''' return whether a group exists ''' + if self.group: + return True + + return False + + def get(self): + '''return group information ''' + result = self._get(self.kind, self.config.name) + if result['returncode'] == 0: + self.group = Group(content=result['results'][0]) + elif 'groups \"{}\" not found'.format(self.config.name) in result['stderr']: + result['returncode'] = 0 + result['results'] = [{}] + + return result + + def delete(self): + '''delete the object''' + return self._delete(self.kind, self.config.name) + + def create(self): + '''create the object''' + return self._create_from_content(self.config.name, self.config.data) + + def update(self): + '''update the object''' + return self._replace_content(self.kind, self.config.name, self.config.data) + + def needs_update(self): + ''' verify an update is needed ''' + return not Utils.check_def_equal(self.config.data, self.group.yaml_dict, skip_keys=[], debug=True) + + # pylint: disable=too-many-return-statements,too-many-branches + @staticmethod + def run_ansible(params, check_mode=False): + '''run the idempotent ansible code''' + + gconfig = GroupConfig(params['name'], + params['namespace'], + params['kubeconfig'], + ) + oc_group = OCGroup(gconfig, verbose=params['debug']) + + state = params['state'] + + api_rval = oc_group.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + ##### + # Get + ##### + if state == 'list': + return {'changed': False, 'results': api_rval['results'], 'state': state} + + ######## + # Delete + ######## + if state == 'absent': + if oc_group.exists(): + + if check_mode: + return {'changed': True, 'msg': 'CHECK_MODE: Would have performed a delete.'} + + api_rval = oc_group.delete() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + return {'changed': False, 'state': state} + + if state == 'present': + ######## + # Create + ######## + if not oc_group.exists(): + + if check_mode: + return {'changed': True, 'msg': 'CHECK_MODE: Would have performed a create.'} + + # Create it here + api_rval = oc_group.create() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + # return the created object + api_rval = oc_group.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + ######## + # Update + ######## + if oc_group.needs_update(): + api_rval = oc_group.update() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + # return the created object + api_rval = oc_group.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + return {'changed': False, 'results': api_rval, 'state': state} + + return {'failed': True, 'msg': 'Unknown state passed. {}'.format(state)} + +# -*- -*- -*- End included fragment: class/oc_group.py -*- -*- -*- + +# -*- -*- -*- Begin included fragment: ansible/oc_group.py -*- -*- -*- + +#pylint: disable=too-many-branches +def main(): + ''' + ansible oc module for group + ''' + + module = AnsibleModule( + argument_spec=dict( + kubeconfig=dict(default='/etc/origin/master/admin.kubeconfig', type='str'), + state=dict(default='present', type='str', + choices=['present', 'absent', 'list']), + debug=dict(default=False, type='bool'), + name=dict(default=None, type='str'), + namespace=dict(default='default', type='str'), + # addind users to a group is handled through the oc_users module + #users=dict(default=None, type='list'), + ), + supports_check_mode=True, + ) + + rval = OCGroup.run_ansible(module.params, module.check_mode) + + if 'failed' in rval: + return module.fail_json(**rval) + + return module.exit_json(**rval) + +if __name__ == '__main__': + main() + +# -*- -*- -*- End included fragment: ansible/oc_group.py -*- -*- -*- diff --git a/roles/lib_openshift/library/oc_label.py b/roles/lib_openshift/library/oc_label.py index 109a78184..cfcb15241 100644 --- a/roles/lib_openshift/library/oc_label.py +++ b/roles/lib_openshift/library/oc_label.py @@ -1054,9 +1054,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout diff --git a/roles/lib_openshift/library/oc_obj.py b/roles/lib_openshift/library/oc_obj.py index bd6e77c2a..f5cba696d 100644 --- a/roles/lib_openshift/library/oc_obj.py +++ b/roles/lib_openshift/library/oc_obj.py @@ -1057,9 +1057,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout diff --git a/roles/lib_openshift/library/oc_objectvalidator.py b/roles/lib_openshift/library/oc_objectvalidator.py index 1d0e4c876..4e1e769cf 100644 --- a/roles/lib_openshift/library/oc_objectvalidator.py +++ b/roles/lib_openshift/library/oc_objectvalidator.py @@ -989,9 +989,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout diff --git a/roles/lib_openshift/library/oc_process.py b/roles/lib_openshift/library/oc_process.py index 14d519e52..cabb2ff29 100644 --- a/roles/lib_openshift/library/oc_process.py +++ b/roles/lib_openshift/library/oc_process.py @@ -1046,9 +1046,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout diff --git a/roles/lib_openshift/library/oc_project.py b/roles/lib_openshift/library/oc_project.py index 4f82abcfe..7700a83a3 100644 --- a/roles/lib_openshift/library/oc_project.py +++ b/roles/lib_openshift/library/oc_project.py @@ -1043,9 +1043,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout @@ -1511,30 +1511,34 @@ class OCProject(OpenShiftCLI): def update(self): '''update a project ''' - self.project.update_annotation('display-name', self.config.config_options['display_name']['value']) - self.project.update_annotation('description', self.config.config_options['description']['value']) + if self.config.config_options['display_name']['value'] is not None: + self.project.update_annotation('display-name', self.config.config_options['display_name']['value']) + + if self.config.config_options['description']['value'] is not None: + self.project.update_annotation('description', self.config.config_options['description']['value']) # work around for immutable project field - if self.config.config_options['node_selector']['value']: + if self.config.config_options['node_selector']['value'] is not None: self.project.update_annotation('node-selector', self.config.config_options['node_selector']['value']) - else: - self.project.update_annotation('node-selector', self.project.find_annotation('node-selector')) return self._replace_content(self.kind, self.config.name, self.project.yaml_dict) def needs_update(self): ''' verify an update is needed ''' - result = self.project.find_annotation("display-name") - if result != self.config.config_options['display_name']['value']: - return True + if self.config.config_options['display_name']['value'] is not None: + result = self.project.find_annotation("display-name") + if result != self.config.config_options['display_name']['value']: + return True - result = self.project.find_annotation("description") - if result != self.config.config_options['description']['value']: - return True + if self.config.config_options['description']['value'] is not None: + result = self.project.find_annotation("description") + if result != self.config.config_options['description']['value']: + return True - result = self.project.find_annotation("node-selector") - if result != self.config.config_options['node_selector']['value']: - return True + if self.config.config_options['node_selector']['value'] is not None: + result = self.project.find_annotation("node-selector") + if result != self.config.config_options['node_selector']['value']: + return True return False @@ -1543,19 +1547,22 @@ class OCProject(OpenShiftCLI): def run_ansible(params, check_mode): '''run the idempotent ansible code''' - _ns = None + node_selector = None if params['node_selector'] is not None: - _ns = ','.join(params['node_selector']) - - pconfig = ProjectConfig(params['name'], - 'None', - params['kubeconfig'], - {'admin': {'value': params['admin'], 'include': True}, - 'admin_role': {'value': params['admin_role'], 'include': True}, - 'description': {'value': params['description'], 'include': True}, - 'display_name': {'value': params['display_name'], 'include': True}, - 'node_selector': {'value': _ns, 'include': True}, - }) + node_selector = ','.join(params['node_selector']) + + pconfig = ProjectConfig( + params['name'], + 'None', + params['kubeconfig'], + { + 'admin': {'value': params['admin'], 'include': True}, + 'admin_role': {'value': params['admin_role'], 'include': True}, + 'description': {'value': params['description'], 'include': True}, + 'display_name': {'value': params['display_name'], 'include': True}, + 'node_selector': {'value': node_selector, 'include': True}, + }, + ) oadm_project = OCProject(pconfig, verbose=params['debug']) diff --git a/roles/lib_openshift/library/oc_route.py b/roles/lib_openshift/library/oc_route.py index 97dd310bc..fe59cca33 100644 --- a/roles/lib_openshift/library/oc_route.py +++ b/roles/lib_openshift/library/oc_route.py @@ -1088,9 +1088,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout diff --git a/roles/lib_openshift/library/oc_scale.py b/roles/lib_openshift/library/oc_scale.py index 56e4e38f7..98f1d94a7 100644 --- a/roles/lib_openshift/library/oc_scale.py +++ b/roles/lib_openshift/library/oc_scale.py @@ -1032,9 +1032,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout diff --git a/roles/lib_openshift/library/oc_secret.py b/roles/lib_openshift/library/oc_secret.py index ad32d4900..deba4ab8a 100644 --- a/roles/lib_openshift/library/oc_secret.py +++ b/roles/lib_openshift/library/oc_secret.py @@ -1078,9 +1078,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout diff --git a/roles/lib_openshift/library/oc_service.py b/roles/lib_openshift/library/oc_service.py index a4d0ca3f3..c2e91e39e 100644 --- a/roles/lib_openshift/library/oc_service.py +++ b/roles/lib_openshift/library/oc_service.py @@ -1084,9 +1084,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout diff --git a/roles/lib_openshift/library/oc_serviceaccount.py b/roles/lib_openshift/library/oc_serviceaccount.py index b6586fca9..a1d8fff14 100644 --- a/roles/lib_openshift/library/oc_serviceaccount.py +++ b/roles/lib_openshift/library/oc_serviceaccount.py @@ -1030,9 +1030,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout diff --git a/roles/lib_openshift/library/oc_serviceaccount_secret.py b/roles/lib_openshift/library/oc_serviceaccount_secret.py index 925a5a088..470043cc6 100644 --- a/roles/lib_openshift/library/oc_serviceaccount_secret.py +++ b/roles/lib_openshift/library/oc_serviceaccount_secret.py @@ -1030,9 +1030,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout diff --git a/roles/lib_openshift/library/oc_version.py b/roles/lib_openshift/library/oc_version.py index 8f59d4d7e..378c2b2e5 100644 --- a/roles/lib_openshift/library/oc_version.py +++ b/roles/lib_openshift/library/oc_version.py @@ -1002,9 +1002,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout diff --git a/roles/lib_openshift/src/ansible/oadm_manage_node.py b/roles/lib_openshift/src/ansible/oc_adm_manage_node.py index b870c1211..b870c1211 100644 --- a/roles/lib_openshift/src/ansible/oadm_manage_node.py +++ b/roles/lib_openshift/src/ansible/oc_adm_manage_node.py diff --git a/roles/lib_openshift/src/ansible/oc_group.py b/roles/lib_openshift/src/ansible/oc_group.py new file mode 100644 index 000000000..9294286d6 --- /dev/null +++ b/roles/lib_openshift/src/ansible/oc_group.py @@ -0,0 +1,32 @@ +# pylint: skip-file +# flake8: noqa + +#pylint: disable=too-many-branches +def main(): + ''' + ansible oc module for group + ''' + + module = AnsibleModule( + argument_spec=dict( + kubeconfig=dict(default='/etc/origin/master/admin.kubeconfig', type='str'), + state=dict(default='present', type='str', + choices=['present', 'absent', 'list']), + debug=dict(default=False, type='bool'), + name=dict(default=None, type='str'), + namespace=dict(default='default', type='str'), + # addind users to a group is handled through the oc_users module + #users=dict(default=None, type='list'), + ), + supports_check_mode=True, + ) + + rval = OCGroup.run_ansible(module.params, module.check_mode) + + if 'failed' in rval: + return module.fail_json(**rval) + + return module.exit_json(**rval) + +if __name__ == '__main__': + main() diff --git a/roles/lib_openshift/src/class/oadm_manage_node.py b/roles/lib_openshift/src/class/oc_adm_manage_node.py index c07320477..c07320477 100644 --- a/roles/lib_openshift/src/class/oadm_manage_node.py +++ b/roles/lib_openshift/src/class/oc_adm_manage_node.py diff --git a/roles/lib_openshift/src/class/oc_adm_registry.py b/roles/lib_openshift/src/class/oc_adm_registry.py index c083cd179..25519c9c9 100644 --- a/roles/lib_openshift/src/class/oc_adm_registry.py +++ b/roles/lib_openshift/src/class/oc_adm_registry.py @@ -87,8 +87,8 @@ class Registry(OpenShiftCLI): ''' prepared_registry property ''' if not self.__prepared_registry: results = self.prepare_registry() - if not results: - raise RegistryException('Could not perform registry preparation.') + if not results or ('returncode' in results and results['returncode'] != 0): + raise RegistryException('Could not perform registry preparation. {}'.format(results)) self.__prepared_registry = results return self.__prepared_registry @@ -153,8 +153,8 @@ class Registry(OpenShiftCLI): # probably need to parse this # pylint thinks results is a string # pylint: disable=no-member - if results['returncode'] != 0 and 'items' in results['results']: - return results + if results['returncode'] != 0 and 'items' not in results['results']: + raise RegistryException('Could not perform registry preparation. {}'.format(results)) service = None deploymentconfig = None diff --git a/roles/lib_openshift/src/class/oc_group.py b/roles/lib_openshift/src/class/oc_group.py new file mode 100644 index 000000000..89fb09ea4 --- /dev/null +++ b/roles/lib_openshift/src/class/oc_group.py @@ -0,0 +1,148 @@ +# pylint: skip-file +# flake8: noqa + + +class OCGroup(OpenShiftCLI): + ''' Class to wrap the oc command line tools ''' + kind = 'group' + + def __init__(self, + config, + verbose=False): + ''' Constructor for OCGroup ''' + super(OCGroup, self).__init__(config.namespace, config.kubeconfig) + self.config = config + self.namespace = config.namespace + self._group = None + + @property + def group(self): + ''' property function service''' + if not self._group: + self.get() + return self._group + + @group.setter + def group(self, data): + ''' setter function for yedit var ''' + self._group = data + + def exists(self): + ''' return whether a group exists ''' + if self.group: + return True + + return False + + def get(self): + '''return group information ''' + result = self._get(self.kind, self.config.name) + if result['returncode'] == 0: + self.group = Group(content=result['results'][0]) + elif 'groups \"{}\" not found'.format(self.config.name) in result['stderr']: + result['returncode'] = 0 + result['results'] = [{}] + + return result + + def delete(self): + '''delete the object''' + return self._delete(self.kind, self.config.name) + + def create(self): + '''create the object''' + return self._create_from_content(self.config.name, self.config.data) + + def update(self): + '''update the object''' + return self._replace_content(self.kind, self.config.name, self.config.data) + + def needs_update(self): + ''' verify an update is needed ''' + return not Utils.check_def_equal(self.config.data, self.group.yaml_dict, skip_keys=[], debug=True) + + # pylint: disable=too-many-return-statements,too-many-branches + @staticmethod + def run_ansible(params, check_mode=False): + '''run the idempotent ansible code''' + + gconfig = GroupConfig(params['name'], + params['namespace'], + params['kubeconfig'], + ) + oc_group = OCGroup(gconfig, verbose=params['debug']) + + state = params['state'] + + api_rval = oc_group.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + ##### + # Get + ##### + if state == 'list': + return {'changed': False, 'results': api_rval['results'], 'state': state} + + ######## + # Delete + ######## + if state == 'absent': + if oc_group.exists(): + + if check_mode: + return {'changed': True, 'msg': 'CHECK_MODE: Would have performed a delete.'} + + api_rval = oc_group.delete() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + return {'changed': False, 'state': state} + + if state == 'present': + ######## + # Create + ######## + if not oc_group.exists(): + + if check_mode: + return {'changed': True, 'msg': 'CHECK_MODE: Would have performed a create.'} + + # Create it here + api_rval = oc_group.create() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + # return the created object + api_rval = oc_group.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + ######## + # Update + ######## + if oc_group.needs_update(): + api_rval = oc_group.update() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + # return the created object + api_rval = oc_group.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + return {'changed': False, 'results': api_rval, 'state': state} + + return {'failed': True, 'msg': 'Unknown state passed. {}'.format(state)} diff --git a/roles/lib_openshift/src/class/oc_project.py b/roles/lib_openshift/src/class/oc_project.py index 7e3984297..9ad8111a8 100644 --- a/roles/lib_openshift/src/class/oc_project.py +++ b/roles/lib_openshift/src/class/oc_project.py @@ -61,30 +61,34 @@ class OCProject(OpenShiftCLI): def update(self): '''update a project ''' - self.project.update_annotation('display-name', self.config.config_options['display_name']['value']) - self.project.update_annotation('description', self.config.config_options['description']['value']) + if self.config.config_options['display_name']['value'] is not None: + self.project.update_annotation('display-name', self.config.config_options['display_name']['value']) + + if self.config.config_options['description']['value'] is not None: + self.project.update_annotation('description', self.config.config_options['description']['value']) # work around for immutable project field - if self.config.config_options['node_selector']['value']: + if self.config.config_options['node_selector']['value'] is not None: self.project.update_annotation('node-selector', self.config.config_options['node_selector']['value']) - else: - self.project.update_annotation('node-selector', self.project.find_annotation('node-selector')) return self._replace_content(self.kind, self.config.name, self.project.yaml_dict) def needs_update(self): ''' verify an update is needed ''' - result = self.project.find_annotation("display-name") - if result != self.config.config_options['display_name']['value']: - return True + if self.config.config_options['display_name']['value'] is not None: + result = self.project.find_annotation("display-name") + if result != self.config.config_options['display_name']['value']: + return True - result = self.project.find_annotation("description") - if result != self.config.config_options['description']['value']: - return True + if self.config.config_options['description']['value'] is not None: + result = self.project.find_annotation("description") + if result != self.config.config_options['description']['value']: + return True - result = self.project.find_annotation("node-selector") - if result != self.config.config_options['node_selector']['value']: - return True + if self.config.config_options['node_selector']['value'] is not None: + result = self.project.find_annotation("node-selector") + if result != self.config.config_options['node_selector']['value']: + return True return False @@ -93,19 +97,22 @@ class OCProject(OpenShiftCLI): def run_ansible(params, check_mode): '''run the idempotent ansible code''' - _ns = None + node_selector = None if params['node_selector'] is not None: - _ns = ','.join(params['node_selector']) - - pconfig = ProjectConfig(params['name'], - 'None', - params['kubeconfig'], - {'admin': {'value': params['admin'], 'include': True}, - 'admin_role': {'value': params['admin_role'], 'include': True}, - 'description': {'value': params['description'], 'include': True}, - 'display_name': {'value': params['display_name'], 'include': True}, - 'node_selector': {'value': _ns, 'include': True}, - }) + node_selector = ','.join(params['node_selector']) + + pconfig = ProjectConfig( + params['name'], + 'None', + params['kubeconfig'], + { + 'admin': {'value': params['admin'], 'include': True}, + 'admin_role': {'value': params['admin_role'], 'include': True}, + 'description': {'value': params['description'], 'include': True}, + 'display_name': {'value': params['display_name'], 'include': True}, + 'node_selector': {'value': node_selector, 'include': True}, + }, + ) oadm_project = OCProject(pconfig, verbose=params['debug']) diff --git a/roles/lib_openshift/src/doc/group b/roles/lib_openshift/src/doc/group new file mode 100644 index 000000000..c5ba6ebd9 --- /dev/null +++ b/roles/lib_openshift/src/doc/group @@ -0,0 +1,56 @@ +# flake8: noqa +# pylint: skip-file + +DOCUMENTATION = ''' +--- +module: oc_group +short_description: Modify, and idempotently manage openshift groups. +description: + - Modify openshift groups programmatically. +options: + state: + description: + - Supported states, present, absent, list + - present - will ensure object is created or updated to the value specified + - list - will return a group + - absent - will remove the group + required: False + default: present + choices: ["present", 'absent', 'list'] + aliases: [] + kubeconfig: + description: + - The path for the kubeconfig file to use for authentication + required: false + default: /etc/origin/master/admin.kubeconfig + aliases: [] + debug: + description: + - Turn on debug output. + required: false + default: False + aliases: [] + name: + description: + - Name of the object that is being queried. + required: false + default: None + aliases: [] + namespace: + description: + - The namespace where the object lives. + required: false + default: str + aliases: [] +author: +- "Joel Diaz <jdiaz@redhat.com>" +extends_documentation_fragment: [] +''' + +EXAMPLES = ''' +- name: create group + oc_group: + state: present + name: acme_org + register: group_out +''' diff --git a/roles/lib_openshift/src/doc/manage_node b/roles/lib_openshift/src/doc/manage_node index 382377f3e..b651ea4e7 100644 --- a/roles/lib_openshift/src/doc/manage_node +++ b/roles/lib_openshift/src/doc/manage_node @@ -3,7 +3,7 @@ DOCUMENTATION = ''' --- -module: oadm_manage_node +module: oc_adm_manage_node short_description: Module to manage openshift nodes description: - Manage openshift nodes programmatically. @@ -75,13 +75,13 @@ extends_documentation_fragment: [] EXAMPLES = ''' - name: oadm manage-node --schedulable=true --selector=ops_node=new - oadm_manage_node: + oc_adm_manage_node: selector: ops_node=new schedulable: True register: schedout - name: oadm manage-node my-k8s-node-5 --evacuate - oadm_manage_node: + oc_adm_manage_node: node: my-k8s-node-5 evacuate: True force: True diff --git a/roles/lib_openshift/src/lib/base.py b/roles/lib_openshift/src/lib/base.py index 334542b97..132c586c9 100644 --- a/roles/lib_openshift/src/lib/base.py +++ b/roles/lib_openshift/src/lib/base.py @@ -283,9 +283,9 @@ class OpenShiftCLI(object): if output_type == 'json': try: rval['results'] = json.loads(stdout) - except ValueError as err: - if "No JSON object could be decoded" in err.args: - err = err.args + except ValueError as verr: + if "No JSON object could be decoded" in verr.args: + err = verr.args elif output_type == 'raw': rval['results'] = stdout diff --git a/roles/lib_openshift/src/lib/group.py b/roles/lib_openshift/src/lib/group.py new file mode 100644 index 000000000..fac5fcbc2 --- /dev/null +++ b/roles/lib_openshift/src/lib/group.py @@ -0,0 +1,36 @@ +# pylint: skip-file +# flake8: noqa + + +class GroupConfig(object): + ''' Handle route options ''' + # pylint: disable=too-many-arguments + def __init__(self, + sname, + namespace, + kubeconfig): + ''' constructor for handling group options ''' + self.kubeconfig = kubeconfig + self.name = sname + self.namespace = namespace + self.data = {} + + self.create_dict() + + def create_dict(self): + ''' return a service as a dict ''' + self.data['apiVersion'] = 'v1' + self.data['kind'] = 'Group' + self.data['metadata'] = {} + self.data['metadata']['name'] = self.name + self.data['users'] = None + + +# pylint: disable=too-many-instance-attributes +class Group(Yedit): + ''' Class to wrap the oc command line tools ''' + kind = 'group' + + def __init__(self, content): + '''Group constructor''' + super(Group, self).__init__(content=content) diff --git a/roles/lib_openshift/src/sources.yml b/roles/lib_openshift/src/sources.yml index f16b3c8de..91ee86750 100644 --- a/roles/lib_openshift/src/sources.yml +++ b/roles/lib_openshift/src/sources.yml @@ -9,15 +9,15 @@ oc_adm_ca_server_cert.py: - class/oc_adm_ca_server_cert.py - ansible/oc_adm_ca_server_cert.py -oadm_manage_node.py: +oc_adm_manage_node.py: - doc/generated - doc/license - lib/import.py - doc/manage_node - ../../lib_utils/src/class/yedit.py - lib/base.py -- class/oadm_manage_node.py -- ansible/oadm_manage_node.py +- class/oc_adm_manage_node.py +- ansible/oc_adm_manage_node.py oc_adm_policy_user.py: - doc/generated @@ -100,6 +100,17 @@ oc_env.py: - class/oc_env.py - ansible/oc_env.py +oc_group.py: +- doc/generated +- doc/license +- lib/import.py +- doc/group +- ../../lib_utils/src/class/yedit.py +- lib/base.py +- lib/group.py +- class/oc_group.py +- ansible/oc_group.py + oc_label.py: - doc/generated - doc/license diff --git a/roles/lib_openshift/src/test/integration/group.yml b/roles/lib_openshift/src/test/integration/group.yml new file mode 100755 index 000000000..25aa5727b --- /dev/null +++ b/roles/lib_openshift/src/test/integration/group.yml @@ -0,0 +1,229 @@ +#!/usr/bin/ansible-playbook +--- +- hosts: "{{ cli_master_test }}" + gather_facts: no + user: root + + vars: + + post_tasks: + - name: delete test group (so future tests work) + oc_group: + state: absent + name: jgroup + + - name: delete 2nd test group (so future tests work) + oc_group: + state: absent + name: jgroup2 + + - name: delete test user (so future tests work) + oc_user: + state: absent + username: jdiaz@redhat.com + + - name: get group list + oc_group: + state: list + name: jgroup + register: group_out + #- debug: var=group_out + - name: assert group 'jgroup' (test group) does not exist + assert: + that: group_out['results'][0] == {} + + - name: get group list + oc_group: + state: list + name: jgroup2 + register: group_out + #- debug: var=group_out + - name: assert group 'jgroup2' (test group) does not exist + assert: + that: group_out['results'][0] == {} + + - name: get user list + oc_user: + state: list + username: 'jdiaz@redhat.com' + register: group_out + #- debug: var=group_out + - name: assert user 'jdiaz@redhat.com' (test user) does not exist + assert: + that: group_out['results'][0] == {} + + - name: create group + oc_group: + state: present + name: jgroup + register: group_out + #- debug: var=group_out + - name: assert creating group marked changed + assert: + that: group_out['changed'] == True + + - name: list group + oc_group: + state: list + name: jgroup + register: group_out + #- debug: var=group_out + - name: assert group actually created + assert: + that: group_out['results'][0]['metadata']['name'] == 'jgroup' + + - name: re-add group + oc_group: + state: present + name: jgroup + register: group_out + #- debug: var=group_out + - name: assert re-adding group marked not changed + assert: + that: group_out['changed'] == False + + + - name: add user with group membership + oc_user: + state: present + username: jdiaz@redhat.com + full_name: Joel Diaz + groups: + - jgroup + register: group_out + #- debug: var=group_out + + - name: get group + oc_group: + state: list + name: jgroup + register: group_out + - name: assert user in group + assert: + that: group_out['results'][0]['users'][0] == 'jdiaz@redhat.com' + + - name: add 2nd group + oc_group: + state: present + name: jgroup2 + + - name: change group membership + oc_user: + state: present + username: jdiaz@redhat.com + full_name: Joel Diaz + groups: + - jgroup2 + register: group_out + - name: assert result changed + assert: + that: group_out['changed'] == True + + - name: check jgroup user membership + oc_group: + state: list + name: jgroup + register: group_out + #- debug: var=group_out + - name: assert user not present in previous group + assert: + that: group_out['results'][0]['users'] == [] + + - name: check jgroup2 user membership + oc_group: + state: list + name: jgroup2 + register: group_out + #- debug: var=group_out + - name: assert user present in new group + assert: + that: group_out['results'][0]['users'][0] == 'jdiaz@redhat.com' + + - name: multi-group membership + oc_user: + state: present + username: jdiaz@redhat.com + full_name: Joel Diaz + groups: + - jgroup + - jgroup2 + register: group_out + - name: assert result changed + assert: + that: group_out['changed'] == True + + - name: check jgroup user membership + oc_group: + state: list + name: jgroup + register: group_out + #- debug: var=group_out + - name: assert user present in group + assert: + that: group_out['results'][0]['users'][0] == 'jdiaz@redhat.com' + + - name: check jgroup2 user membership + oc_group: + state: list + name: jgroup2 + register: group_out + #- debug: var=group_out + - name: assert user still present in group + assert: + that: group_out['results'][0]['users'][0] == 'jdiaz@redhat.com' + + - name: user delete (group cleanup) + oc_user: + state: absent + username: jdiaz@redhat.com + register: group_out + + - name: get user list for jgroup + oc_group: + state: list + name: jgroup + register: group_out + #- debug: var=group_out + - name: assert that group jgroup has no members + assert: + that: group_out['results'][0]['users'] == [] + + - name: get user list for jgroup2 + oc_group: + state: list + name: jgroup2 + register: group_out + #- debug: var=group_out + - name: assert that group jgroup2 has no members + assert: + that: group_out['results'][0]['users'] == [] + + - name: user without groups defined + oc_user: + state: present + username: jdiaz@redhat.com + full_name: Joel Diaz + register: group_out + - name: assert result changed + assert: + that: group_out['changed'] == True + + - name: check jgroup user membership + oc_group: + state: list + name: jgroup + register: group_out + #- debug: var=group_out + - name: assert user not present in group + assert: + that: group_out['results'][0]['users'] == [] + + - name: check jgroup2 user membership + oc_group: + state: list + name: jgroup2 + register: group_out + #- debug: var=group_out + - name: assert user not present in group + assert: + that: group_out['results'][0]['users'] == [] diff --git a/roles/lib_openshift/src/test/integration/oadm_manage_node.yml b/roles/lib_openshift/src/test/integration/oc_adm_manage_node.yml index 3ee13a409..1ed2ef11b 100755 --- a/roles/lib_openshift/src/test/integration/oadm_manage_node.yml +++ b/roles/lib_openshift/src/test/integration/oc_adm_manage_node.yml @@ -1,6 +1,6 @@ #!/usr/bin/ansible-playbook --module-path=../../../library/ # -# ./oadm_manage_node.yml -e "cli_master_test=$OPENSHIFT_MASTER +# ./oc_adm_manage_node.yml -e "cli_master_test=$OPENSHIFT_MASTER --- - hosts: "{{ cli_master_test }}" gather_facts: no @@ -17,7 +17,7 @@ node_to_test: "{{ obj_out['results']['results'][0]['items'][0]['metadata']['name'] }}" - name: list pods from a node - oadm_manage_node: + oc_adm_manage_node: list_pods: True node: - "{{ node_to_test }}" @@ -29,7 +29,7 @@ msg: Pod data was not returned - name: set node to unschedulable - oadm_manage_node: + oc_adm_manage_node: schedulable: False node: - "{{ node_to_test }}" @@ -56,7 +56,7 @@ that: nodeout.results.results[0]['spec']['unschedulable'] - name: set node to schedulable - oadm_manage_node: + oc_adm_manage_node: schedulable: True node: - "{{ node_to_test }}" diff --git a/roles/lib_openshift/src/test/unit/test_oadm_manage_node.py b/roles/lib_openshift/src/test/unit/test_oc_adm_manage_node.py index 27d98b869..312b1ecbb 100755 --- a/roles/lib_openshift/src/test/unit/test_oadm_manage_node.py +++ b/roles/lib_openshift/src/test/unit/test_oc_adm_manage_node.py @@ -1,5 +1,5 @@ ''' - Unit tests for oadm_manage_node + Unit tests for oc_adm_manage_node ''' import os @@ -16,16 +16,16 @@ import mock # place class in our python path module_path = os.path.join('/'.join(os.path.realpath(__file__).split('/')[:-4]), 'library') # noqa: E501 sys.path.insert(0, module_path) -from oadm_manage_node import ManageNode, locate_oc_binary # noqa: E402 +from oc_adm_manage_node import ManageNode, locate_oc_binary # noqa: E402 class ManageNodeTest(unittest.TestCase): ''' - Test class for oadm_manage_node + Test class for oc_adm_manage_node ''' - @mock.patch('oadm_manage_node.Utils.create_tmpfile_copy') - @mock.patch('oadm_manage_node.ManageNode.openshift_cmd') + @mock.patch('oc_adm_manage_node.Utils.create_tmpfile_copy') + @mock.patch('oc_adm_manage_node.ManageNode.openshift_cmd') def test_list_pods(self, mock_openshift_cmd, mock_tmpfile_copy): ''' Testing a get ''' params = {'node': ['ip-172-31-49-140.ec2.internal'], @@ -107,8 +107,8 @@ class ManageNodeTest(unittest.TestCase): # returned 2 pods self.assertTrue(len(results['results']['nodes']['ip-172-31-49-140.ec2.internal']) == 2) - @mock.patch('oadm_manage_node.Utils.create_tmpfile_copy') - @mock.patch('oadm_manage_node.ManageNode.openshift_cmd') + @mock.patch('oc_adm_manage_node.Utils.create_tmpfile_copy') + @mock.patch('oc_adm_manage_node.ManageNode.openshift_cmd') def test_schedulable_false(self, mock_openshift_cmd, mock_tmpfile_copy): ''' Testing a get ''' params = {'node': ['ip-172-31-49-140.ec2.internal'], diff --git a/roles/lib_openshift/src/test/unit/test_oc_group.py b/roles/lib_openshift/src/test/unit/test_oc_group.py new file mode 100755 index 000000000..8eef37810 --- /dev/null +++ b/roles/lib_openshift/src/test/unit/test_oc_group.py @@ -0,0 +1,253 @@ +''' + Unit tests for oc group +''' + +import copy +import os +import six +import sys +import unittest +import mock + +# Removing invalid variable names for tests so that I can +# keep them brief +# pylint: disable=invalid-name,no-name-in-module +# Disable import-error b/c our libraries aren't loaded in jenkins +# pylint: disable=import-error,wrong-import-position +# place class in our python path +module_path = os.path.join('/'.join(os.path.realpath(__file__).split('/')[:-4]), 'library') # noqa: E501 +sys.path.insert(0, module_path) +from oc_group import OCGroup, locate_oc_binary # noqa: E402 + + +class OCGroupTest(unittest.TestCase): + ''' + Test class for OCGroup + ''' + params = {'kubeconfig': '/etc/origin/master/admin.kubeconfig', + 'state': 'present', + 'debug': False, + 'name': 'acme', + 'namespace': 'test'} + + @mock.patch('oc_group.Utils.create_tmpfile_copy') + @mock.patch('oc_group.OCGroup._run') + def test_create_group(self, mock_run, mock_tmpfile_copy): + ''' Testing a group create ''' + params = copy.deepcopy(OCGroupTest.params) + + group = '''{ + "kind": "Group", + "apiVersion": "v1", + "metadata": { + "name": "acme" + }, + "users": [] + }''' + + mock_run.side_effect = [ + (1, '', 'Error from server: groups "acme" not found'), + (1, '', 'Error from server: groups "acme" not found'), + (0, '', ''), + (0, group, ''), + ] + + mock_tmpfile_copy.side_effect = [ + '/tmp/mocked_kubeconfig', + ] + + results = OCGroup.run_ansible(params, False) + + self.assertTrue(results['changed']) + self.assertEqual(results['results']['results'][0]['metadata']['name'], 'acme') + + @mock.patch('oc_group.Utils.create_tmpfile_copy') + @mock.patch('oc_group.OCGroup._run') + def test_failed_get_group(self, mock_run, mock_tmpfile_copy): + ''' Testing a group create ''' + params = copy.deepcopy(OCGroupTest.params) + params['state'] = 'list' + params['name'] = 'noexist' + + mock_run.side_effect = [ + (1, '', 'Error from server: groups "acme" not found'), + ] + + mock_tmpfile_copy.side_effect = [ + '/tmp/mocked_kubeconfig', + ] + + results = OCGroup.run_ansible(params, False) + + self.assertTrue(results['failed']) + + @mock.patch('oc_group.Utils.create_tmpfile_copy') + @mock.patch('oc_group.OCGroup._run') + def test_delete_group(self, mock_run, mock_tmpfile_copy): + ''' Testing a group create ''' + params = copy.deepcopy(OCGroupTest.params) + params['state'] = 'absent' + + group = '''{ + "kind": "Group", + "apiVersion": "v1", + "metadata": { + "name": "acme" + }, + "users": [ + "user1" + ] + }''' + + mock_run.side_effect = [ + (0, group, ''), + (0, '', ''), + ] + + mock_tmpfile_copy.side_effect = [ + '/tmp/mocked_kubeconfig', + ] + + results = OCGroup.run_ansible(params, False) + + self.assertTrue(results['changed']) + + @mock.patch('oc_group.Utils.create_tmpfile_copy') + @mock.patch('oc_group.OCGroup._run') + def test_get_group(self, mock_run, mock_tmpfile_copy): + ''' Testing a group create ''' + params = copy.deepcopy(OCGroupTest.params) + params['state'] = 'list' + + group = '''{ + "kind": "Group", + "apiVersion": "v1", + "metadata": { + "name": "acme" + }, + "users": [ + "user1" + ] + }''' + + mock_run.side_effect = [ + (0, group, ''), + ] + + mock_tmpfile_copy.side_effect = [ + '/tmp/mocked_kubeconfig', + ] + + results = OCGroup.run_ansible(params, False) + + self.assertFalse(results['changed']) + self.assertEqual(results['results'][0]['metadata']['name'], 'acme') + self.assertEqual(results['results'][0]['users'][0], 'user1') + + @unittest.skipIf(six.PY3, 'py2 test only') + @mock.patch('os.path.exists') + @mock.patch('os.environ.get') + def test_binary_lookup_fallback(self, mock_env_get, mock_path_exists): + ''' Testing binary lookup fallback ''' + + mock_env_get.side_effect = lambda _v, _d: '' + + mock_path_exists.side_effect = lambda _: False + + self.assertEqual(locate_oc_binary(), 'oc') + + @unittest.skipIf(six.PY3, 'py2 test only') + @mock.patch('os.path.exists') + @mock.patch('os.environ.get') + def test_binary_lookup_in_path(self, mock_env_get, mock_path_exists): + ''' Testing binary lookup in path ''' + + oc_bin = '/usr/bin/oc' + + mock_env_get.side_effect = lambda _v, _d: '/bin:/usr/bin' + + mock_path_exists.side_effect = lambda f: f == oc_bin + + self.assertEqual(locate_oc_binary(), oc_bin) + + @unittest.skipIf(six.PY3, 'py2 test only') + @mock.patch('os.path.exists') + @mock.patch('os.environ.get') + def test_binary_lookup_in_usr_local(self, mock_env_get, mock_path_exists): + ''' Testing binary lookup in /usr/local/bin ''' + + oc_bin = '/usr/local/bin/oc' + + mock_env_get.side_effect = lambda _v, _d: '/bin:/usr/bin' + + mock_path_exists.side_effect = lambda f: f == oc_bin + + self.assertEqual(locate_oc_binary(), oc_bin) + + @unittest.skipIf(six.PY3, 'py2 test only') + @mock.patch('os.path.exists') + @mock.patch('os.environ.get') + def test_binary_lookup_in_home(self, mock_env_get, mock_path_exists): + ''' Testing binary lookup in ~/bin ''' + + oc_bin = os.path.expanduser('~/bin/oc') + + mock_env_get.side_effect = lambda _v, _d: '/bin:/usr/bin' + + mock_path_exists.side_effect = lambda f: f == oc_bin + + self.assertEqual(locate_oc_binary(), oc_bin) + + @unittest.skipIf(six.PY2, 'py3 test only') + @mock.patch('shutil.which') + @mock.patch('os.environ.get') + def test_binary_lookup_fallback_py3(self, mock_env_get, mock_shutil_which): + ''' Testing binary lookup fallback ''' + + mock_env_get.side_effect = lambda _v, _d: '' + + mock_shutil_which.side_effect = lambda _f, path=None: None + + self.assertEqual(locate_oc_binary(), 'oc') + + @unittest.skipIf(six.PY2, 'py3 test only') + @mock.patch('shutil.which') + @mock.patch('os.environ.get') + def test_binary_lookup_in_path_py3(self, mock_env_get, mock_shutil_which): + ''' Testing binary lookup in path ''' + + oc_bin = '/usr/bin/oc' + + mock_env_get.side_effect = lambda _v, _d: '/bin:/usr/bin' + + mock_shutil_which.side_effect = lambda _f, path=None: oc_bin + + self.assertEqual(locate_oc_binary(), oc_bin) + + @unittest.skipIf(six.PY2, 'py3 test only') + @mock.patch('shutil.which') + @mock.patch('os.environ.get') + def test_binary_lookup_in_usr_local_py3(self, mock_env_get, mock_shutil_which): + ''' Testing binary lookup in /usr/local/bin ''' + + oc_bin = '/usr/local/bin/oc' + + mock_env_get.side_effect = lambda _v, _d: '/bin:/usr/bin' + + mock_shutil_which.side_effect = lambda _f, path=None: oc_bin + + self.assertEqual(locate_oc_binary(), oc_bin) + + @unittest.skipIf(six.PY2, 'py3 test only') + @mock.patch('shutil.which') + @mock.patch('os.environ.get') + def test_binary_lookup_in_home_py3(self, mock_env_get, mock_shutil_which): + ''' Testing binary lookup in ~/bin ''' + + oc_bin = os.path.expanduser('~/bin/oc') + + mock_env_get.side_effect = lambda _v, _d: '/bin:/usr/bin' + + mock_shutil_which.side_effect = lambda _f, path=None: oc_bin + + self.assertEqual(locate_oc_binary(), oc_bin) diff --git a/roles/lib_openshift/src/test/unit/test_oc_project.py b/roles/lib_openshift/src/test/unit/test_oc_project.py index 5155101cb..fa454d035 100755 --- a/roles/lib_openshift/src/test/unit/test_oc_project.py +++ b/roles/lib_openshift/src/test/unit/test_oc_project.py @@ -2,6 +2,7 @@ Unit tests for oc project ''' +import copy import os import sys import unittest @@ -20,9 +21,22 @@ from oc_project import OCProject # noqa: E402 class OCProjectTest(unittest.TestCase): ''' - Test class for OCSecret + Test class for OCProject ''' + # run_ansible input parameters + params = { + 'state': 'present', + 'display_name': 'operations project', + 'name': 'operations', + 'node_selector': ['ops_only=True'], + 'kubeconfig': '/etc/origin/master/admin.kubeconfig', + 'debug': False, + 'admin': None, + 'admin_role': 'admin', + 'description': 'All things operations project', + } + @mock.patch('oc_project.locate_oc_binary') @mock.patch('oc_project.Utils.create_tmpfile_copy') @mock.patch('oc_project.Utils._write') @@ -30,21 +44,9 @@ class OCProjectTest(unittest.TestCase): def test_adding_a_project(self, mock_cmd, mock_write, mock_tmpfile_copy, mock_loc_oc_bin): ''' Testing adding a project ''' - # Arrange + params = copy.deepcopy(OCProjectTest.params) # run_ansible input parameters - params = { - 'state': 'present', - 'display_name': 'operations project', - 'name': 'operations', - 'node_selector': ['ops_only=True'], - 'kubeconfig': '/etc/origin/master/admin.kubeconfig', - 'debug': False, - 'admin': None, - 'admin_role': 'admin', - 'description': 'All things operations project', - } - project_results = '''{ "kind": "Project", "apiVersion": "v1", @@ -90,7 +92,6 @@ class OCProjectTest(unittest.TestCase): ] # Act - results = OCProject.run_ansible(params, False) # Assert @@ -108,3 +109,172 @@ class OCProjectTest(unittest.TestCase): mock.call(['oc', 'get', 'namespace', 'operations', '-o', 'json'], None), ]) + + @mock.patch('oc_project.locate_oc_binary') + @mock.patch('oc_project.Utils.create_tmpfile_copy') + @mock.patch('oc_project.Utils._write') + @mock.patch('oc_project.OCProject._run') + def test_modifying_a_project_no_attributes(self, mock_cmd, mock_write, mock_tmpfile_copy, mock_loc_oc_bin): + ''' Testing adding a project ''' + params = copy.deepcopy(self.params) + params['display_name'] = None + params['node_selector'] = None + params['description'] = None + + # run_ansible input parameters + project_results = '''{ + "kind": "Project", + "apiVersion": "v1", + "metadata": { + "name": "operations", + "selfLink": "/oapi/v1/projects/operations", + "uid": "5e52afb8-ee33-11e6-89f4-0edc441d9666", + "resourceVersion": "1584", + "labels": {}, + "annotations": { + "openshift.io/node-selector": "", + "openshift.io/description: "This is a description", + "openshift.io/sa.initialized-roles": "true", + "openshift.io/sa.scc.mcs": "s0:c3,c2", + "openshift.io/sa.scc.supplemental-groups": "1000010000/10000", + "openshift.io/sa.scc.uid-range": "1000010000/10000" + } + }, + "spec": { + "finalizers": [ + "kubernetes", + "openshift.io/origin" + ] + }, + "status": { + "phase": "Active" + } + }''' + + # Return values of our mocked function call. These get returned once per call. + mock_cmd.side_effect = [ + (0, project_results, ''), + ] + + mock_tmpfile_copy.side_effect = [ + '/tmp/mocked_kubeconfig', + ] + + mock_loc_oc_bin.side_effect = [ + 'oc', + ] + + # Act + results = OCProject.run_ansible(params, False) + + # Assert + self.assertFalse(results['changed']) + + # Making sure our mock was called as we expected + mock_cmd.assert_has_calls([ + mock.call(['oc', 'get', 'namespace', 'operations', '-o', 'json'], None), + ]) + + @mock.patch('oc_project.locate_oc_binary') + @mock.patch('oc_project.Utils.create_tmpfile_copy') + @mock.patch('oc_project.Utils._write') + @mock.patch('oc_project.OCProject._run') + def test_modifying_project_attributes(self, mock_cmd, mock_write, mock_tmpfile_copy, mock_loc_oc_bin): + ''' Testing adding a project ''' + params = copy.deepcopy(self.params) + params['display_name'] = 'updated display name' + params['node_selector'] = 'type=infra' + params['description'] = 'updated description' + + # run_ansible input parameters + project_results = '''{ + "kind": "Project", + "apiVersion": "v1", + "metadata": { + "name": "operations", + "selfLink": "/oapi/v1/projects/operations", + "uid": "5e52afb8-ee33-11e6-89f4-0edc441d9666", + "resourceVersion": "1584", + "labels": {}, + "annotations": { + "openshift.io/node-selector": "", + "openshift.io/description": "This is a description", + "openshift.io/sa.initialized-roles": "true", + "openshift.io/sa.scc.mcs": "s0:c3,c2", + "openshift.io/sa.scc.supplemental-groups": "1000010000/10000", + "openshift.io/sa.scc.uid-range": "1000010000/10000" + } + }, + "spec": { + "finalizers": [ + "kubernetes", + "openshift.io/origin" + ] + }, + "status": { + "phase": "Active" + } + }''' + + mod_project_results = '''{ + "kind": "Project", + "apiVersion": "v1", + "metadata": { + "name": "operations", + "selfLink": "/oapi/v1/projects/operations", + "uid": "5e52afb8-ee33-11e6-89f4-0edc441d9666", + "resourceVersion": "1584", + "labels": {}, + "annotations": { + "openshift.io/node-selector": "type=infra", + "openshift.io/description": "updated description", + "openshift.io/display-name": "updated display name", + "openshift.io/sa.initialized-roles": "true", + "openshift.io/sa.scc.mcs": "s0:c3,c2", + "openshift.io/sa.scc.supplemental-groups": "1000010000/10000", + "openshift.io/sa.scc.uid-range": "1000010000/10000" + } + }, + "spec": { + "finalizers": [ + "kubernetes", + "openshift.io/origin" + ] + }, + "status": { + "phase": "Active" + } + }''' + + # Return values of our mocked function call. These get returned once per call. + mock_cmd.side_effect = [ + (0, project_results, ''), + (0, project_results, ''), + (0, '', ''), + (0, mod_project_results, ''), + ] + + mock_tmpfile_copy.side_effect = [ + '/tmp/mocked_kubeconfig', + ] + + mock_loc_oc_bin.side_effect = [ + 'oc', + ] + + # Act + results = OCProject.run_ansible(params, False) + + # Assert + self.assertTrue(results['changed']) + self.assertEqual(results['results']['returncode'], 0) + self.assertEqual(results['results']['results']['metadata']['annotations']['openshift.io/description'], 'updated description') + self.assertEqual(results['state'], 'present') + + # Making sure our mock was called as we expected + mock_cmd.assert_has_calls([ + mock.call(['oc', 'get', 'namespace', 'operations', '-o', 'json'], None), + mock.call(['oc', 'get', 'namespace', 'operations', '-o', 'json'], None), + mock.call(['oc', 'replace', '-f', mock.ANY], None), + mock.call(['oc', 'get', 'namespace', 'operations', '-o', 'json'], None), + ]) diff --git a/roles/lib_openshift/src/test/unit/test_oc_route.py b/roles/lib_openshift/src/test/unit/test_oc_route.py index 09c52a461..afdb5e4dc 100755 --- a/roles/lib_openshift/src/test/unit/test_oc_route.py +++ b/roles/lib_openshift/src/test/unit/test_oc_route.py @@ -21,7 +21,7 @@ from oc_route import OCRoute, locate_oc_binary # noqa: E402 class OCRouteTest(unittest.TestCase): ''' - Test class for OCServiceAccount + Test class for OCRoute ''' @mock.patch('oc_route.locate_oc_binary') diff --git a/roles/nuage_master/tasks/main.yaml b/roles/nuage_master/tasks/main.yaml index d211d30e8..fefd28bbd 100644 --- a/roles/nuage_master/tasks/main.yaml +++ b/roles/nuage_master/tasks/main.yaml @@ -22,6 +22,15 @@ - nuage.key - nuage.kubeconfig +- name: Copy the certificates and keys + become: yes + copy: src="/tmp/{{ item }}" dest="{{ cert_output_dir }}/{{ item }}" + with_items: + - ca.crt + - nuage.crt + - nuage.key + - nuage.kubeconfig + - include: certificates.yml - name: Create nuage-openshift-monitor.yaml diff --git a/roles/nuage_master/tasks/serviceaccount.yml b/roles/nuage_master/tasks/serviceaccount.yml index 16ea08244..eee448e2c 100644 --- a/roles/nuage_master/tasks/serviceaccount.yml +++ b/roles/nuage_master/tasks/serviceaccount.yml @@ -3,14 +3,20 @@ command: mktemp -u /tmp/openshift-ansible-XXXXXXX.kubeconfig register: nuage_tmp_conf_mktemp changed_when: False + run_once: True + delegate_to: "{{ nuage_ca_master }}" - set_fact: nuage_tmp_conf: "{{ nuage_tmp_conf_mktemp.stdout }}" + run_once: True + delegate_to: "{{ nuage_ca_master }}" - name: Copy Configuration to temporary conf command: > cp {{ openshift.common.config_base }}/master/admin.kubeconfig {{nuage_tmp_conf}} changed_when: false + run_once: True + delegate_to: "{{ nuage_ca_master }}" - name: Create Admin Service Account oc_serviceaccount: @@ -18,6 +24,8 @@ name: nuage namespace: default state: present + run_once: True + delegate_to: "{{ nuage_ca_master }}" - name: Configure role/user permissions command: > @@ -27,6 +35,8 @@ register: osnuage_perm_task failed_when: "'the object has been modified' not in osnuage_perm_task.stderr and osnuage_perm_task.rc != 0" changed_when: osnuage_perm_task.rc == 0 + run_once: True + delegate_to: "{{ nuage_ca_master }}" - name: Generate the node client config command: > @@ -40,8 +50,12 @@ --signer-serial={{ openshift_master_ca_serial }} --basename='nuage' --user={{ nuage_service_account }} + delegate_to: "{{ nuage_ca_master }}" + run_once: True - name: Clean temporary configuration file command: > rm -f {{nuage_tmp_conf}} changed_when: false + delegate_to: "{{ nuage_ca_master }}" + run_once: True diff --git a/roles/openshift_excluder/README.md b/roles/openshift_excluder/README.md index e76a15952..e048bd107 100644 --- a/roles/openshift_excluder/README.md +++ b/roles/openshift_excluder/README.md @@ -18,8 +18,6 @@ Facts | enable_docker_excluder | enable_excluders | Enable docker excluder. If not set, the docker excluder is ignored. | | enable_openshift_excluder | enable_excluders | Enable openshift excluder. If not set, the openshift excluder is ignored. | | enable_excluders | None | Enable all excluders -| enable_docker_excluder_override | None | indication the docker excluder needs to be enabled | -| disable_openshift_excluder_override | None | indication the openshift excluder needs to be disabled | Role Variables -------------- diff --git a/roles/openshift_excluder/tasks/adjust.yml b/roles/openshift_excluder/tasks/adjust.yml deleted file mode 100644 index 2535b9ea6..000000000 --- a/roles/openshift_excluder/tasks/adjust.yml +++ /dev/null @@ -1,23 +0,0 @@ ---- -# Depending on enablement of individual excluders and their status -# some excluders needs to be disabled, resp. enabled -# By default, all excluders are disabled unless overrided. -- block: - - include: init.yml - # All excluders that are to be enabled are enabled - - include: exclude.yml - vars: - # Enable the docker excluder only if it is overrided - enable_docker_excluder: "{{ enable_docker_excluder_override | default(false) | bool }}" - # excluder is to be disabled by default - enable_openshift_excluder: false - # All excluders that are to be disabled are disabled - - include: unexclude.yml - vars: - # If the docker override is not set, default to the generic behaviour - disable_docker_excluder: "{{ not enable_docker_excluder_override | default(not docker_excluder_on) | bool }}" - # disable openshift excluder is never overrided to be enabled - # disable it if the docker excluder is enabled - disable_openshift_excluder: "{{ openshift_excluder_on | bool }}" - when: - - not openshift.common.is_atomic | bool diff --git a/roles/openshift_excluder/tasks/disable.yml b/roles/openshift_excluder/tasks/disable.yml index a8deb3eb1..e23496b3b 100644 --- a/roles/openshift_excluder/tasks/disable.yml +++ b/roles/openshift_excluder/tasks/disable.yml @@ -1,7 +1,6 @@ --- # input variables # - with_status_check -# - with_install # - excluder_package_state # - docker_excluder_package_state - include: init.yml @@ -18,9 +17,24 @@ # it the docker excluder is enabled, we install it and in case its status is non-zero # it is enabled no matter what -# Check the current state of all excluders -- include: status.yml - when: with_status_check | default(docker_excluder_on or openshift_excluder_on) | bool - - # And finally adjust an excluder in order to update host components correctly -- include: adjust.yml +# And finally adjust an excluder in order to update host components correctly. First +# exclude then unexclude +- block: + - include: exclude.yml + vars: + # Enable the docker excluder only if it is overrided + # BZ #1430612: docker excluders should be enabled even during installation and upgrade + exclude_docker_excluder: "{{ docker_excluder_on | bool }}" + # excluder is to be disabled by default + exclude_openshift_excluder: false + # All excluders that are to be disabled are disabled + - include: unexclude.yml + vars: + # If the docker override is not set, default to the generic behaviour + # BZ #1430612: docker excluders should be enabled even during installation and upgrade + unexclude_docker_excluder: false + # disable openshift excluder is never overrided to be enabled + # disable it if the docker excluder is enabled + unexclude_openshift_excluder: true + when: + - not openshift.common.is_atomic | bool diff --git a/roles/openshift_excluder/tasks/enable.yml b/roles/openshift_excluder/tasks/enable.yml index 413c7b5cf..e719325bc 100644 --- a/roles/openshift_excluder/tasks/enable.yml +++ b/roles/openshift_excluder/tasks/enable.yml @@ -1,6 +1,5 @@ --- # input variables: -# - with_install - block: - include: init.yml @@ -8,14 +7,12 @@ vars: install_docker_excluder: "{{ docker_excluder_on | bool }}" install_openshift_excluder: "{{ openshift_excluder_on | bool }}" - when: with_install | default(docker_excluder_on or openshift_excluder_on) | bool + when: docker_excluder_on or openshift_excluder_on | bool - include: exclude.yml vars: - # Enable the docker excluder only if it is overrided, resp. enabled by default (in that order) - enable_docker_excluder: "{{ enable_docker_excluder_override | default(docker_excluder_on) | bool }}" - # Enable the openshift excluder only if it is not overrided, resp. enabled by default (in that order) - enable_openshift_excluder: "{{ not disable_openshift_excluder_override | default(not openshift_excluder_on) | bool }}" + exclude_docker_excluder: "{{ docker_excluder_on | bool }}" + exclude_openshift_excluder: "{{ openshift_excluder_on | bool }}" when: - not openshift.common.is_atomic | bool diff --git a/roles/openshift_excluder/tasks/exclude.yml b/roles/openshift_excluder/tasks/exclude.yml index af9824aae..ca18d343f 100644 --- a/roles/openshift_excluder/tasks/exclude.yml +++ b/roles/openshift_excluder/tasks/exclude.yml @@ -1,20 +1,30 @@ --- # input variables: -# - enable_docker_excluder -# - enable_openshift_excluder +# - exclude_docker_excluder +# - exclude_openshift_excluder - block: + + - name: Check for docker-excluder + stat: + path: /sbin/{{ openshift.common.service_type }}-docker-excluder + register: docker_excluder_stat - name: Enable docker excluder command: "{{ openshift.common.service_type }}-docker-excluder exclude" - # if the docker override is set, it means the docker excluder needs to be enabled no matter what - # if the docker override is not set, the excluder is set based on enable_docker_excluder when: - - enable_docker_excluder | default(false) | bool + - exclude_docker_excluder | default(false) | bool + - docker_excluder_stat.stat.exists + - name: Check for openshift excluder + stat: + path: /sbin/{{ openshift.common.service_type }}-excluder + register: openshift_excluder_stat - name: Enable openshift excluder command: "{{ openshift.common.service_type }}-excluder exclude" # if the openshift override is set, it means the openshift excluder is disabled no matter what # if the openshift override is not set, the excluder is set based on enable_openshift_excluder when: - - enable_openshift_excluder | default(false) | bool + - exclude_openshift_excluder | default(false) | bool + - openshift_excluder_stat.stat.exists + when: - not openshift.common.is_atomic | bool diff --git a/roles/openshift_excluder/tasks/install.yml b/roles/openshift_excluder/tasks/install.yml index dcc8df0cb..3490a613e 100644 --- a/roles/openshift_excluder/tasks/install.yml +++ b/roles/openshift_excluder/tasks/install.yml @@ -6,14 +6,14 @@ - name: Install docker excluder package: - name: "{{ openshift.common.service_type }}-docker-excluder" + name: "{{ openshift.common.service_type }}-docker-excluder{{ openshift_pkg_version | default('') | oo_image_tag_to_rpm_version(include_dash=True) + '*' }}" state: "{{ docker_excluder_package_state }}" when: - install_docker_excluder | default(true) | bool - name: Install openshift excluder package: - name: "{{ openshift.common.service_type }}-excluder" + name: "{{ openshift.common.service_type }}-excluder{{ openshift_pkg_version | default('') | oo_image_tag_to_rpm_version(include_dash=True) + '*' }}" state: "{{ openshift_excluder_package_state }}" when: - install_openshift_excluder | default(true) | bool diff --git a/roles/openshift_excluder/tasks/main.yml b/roles/openshift_excluder/tasks/main.yml deleted file mode 100644 index 78a3d37cb..000000000 --- a/roles/openshift_excluder/tasks/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -include: status.yml diff --git a/roles/openshift_excluder/tasks/status.yml b/roles/openshift_excluder/tasks/status.yml deleted file mode 100644 index 363ccdbea..000000000 --- a/roles/openshift_excluder/tasks/status.yml +++ /dev/null @@ -1,84 +0,0 @@ ---- -- name: Determine if excluder packages are installed - rpm_q: - name: "{{ openshift.common.service_type }}-excluder" - state: present - register: openshift_excluder_installed - failed_when: false - -# docker excluder needs to be enable by default -- name: Determine if docker packages are installed - rpm_q: - name: "{{ openshift.common.service_type }}-docker-excluder" - state: present - register: docker_excluder_installed - failed_when: false - -# The excluder status function returns 0 when everything is excluded -# and 1 if any packages are missing from the exclusions list and outputs a warning to stderr -# # atomic-openshift-excluder status ; echo $? -# exclude -- All packages excluded -# 0 -# # atomic-openshift-excluder unexclude -# # atomic-openshift-excluder status ; echo $? -# unexclude -- At least one package not excluded -# 1 - -- block: - - include: init.yml - - block: - - name: Record openshift excluder status - command: "{{ openshift.common.service_type }}-excluder status" - register: excluder_status - failed_when: false - - # Even though the openshift excluder is enabled - # if the status is non-zero, disabled the excluder - - name: Override openshift excluder enablement if the status is non-zero - set_fact: - disable_openshift_excluder_override: true - when: - - "{{ excluder_status.rc | default(0) != 0 }}" - - - debug: - msg: "Disabling openshift excluder" - when: - - "{{ excluder_status.rc | default(0) != 0 }}" - - when: - - "{{ openshift_excluder_installed.installed_versions | default([]) | length > 0 }}" - - "{{ openshift_excluder_on }}" - - - block: - - name: Record docker excluder status - command: "{{ openshift.common.service_type }}-docker-excluder status" - register: docker_excluder_status - failed_when: false - - # If the docker excluder is installed and the status is non-zero - # always enable the docker excluder - - name: Override docker excluder enablement if the status is non-zero - set_fact: - enable_docker_excluder_override: true - when: - - "{{ docker_excluder_status.rc | default(0) != 0 }}" - - - debug: - msg: "Enabling docker excluder" - when: - - "{{ docker_excluder_status.rc | default(0) != 0 }}" - - # As the docker excluder status is not satisfied, - # re-enable entire docker excluder again - # At the same time keep the override set in a case other task would - - name: Enable docker excluder - command: "{{ openshift.common.service_type }}-docker-excluder exclude" - - # Run the docker excluder status even if the excluder is disabled. - # In order to determine of the excluder needs to be enabled. - when: - - "{{ docker_excluder_installed.installed_versions | default([]) | length > 0 }}" - - "{{ docker_excluder_on }}" - - when: - - not openshift.common.is_atomic | bool diff --git a/roles/openshift_excluder/tasks/unexclude.yml b/roles/openshift_excluder/tasks/unexclude.yml index 196ca25f5..4df7f14b4 100644 --- a/roles/openshift_excluder/tasks/unexclude.yml +++ b/roles/openshift_excluder/tasks/unexclude.yml @@ -1,19 +1,28 @@ --- # input variables: -# - disable_docker_excluder -# - disable_openshift_excluder +# - unexclude_docker_excluder +# - unexclude_openshift_excluder - block: - - include: init.yml + - name: Check for docker-excluder + stat: + path: /sbin/{{ openshift.common.service_type }}-docker-excluder + register: docker_excluder_stat - name: disable docker excluder command: "{{ openshift.common.service_type }}-docker-excluder unexclude" when: - - disable_docker_excluder | default(false) | bool + - unexclude_docker_excluder | default(false) | bool + - docker_excluder_stat.stat.exists + - name: Check for openshift excluder + stat: + path: /sbin/{{ openshift.common.service_type }}-excluder + register: openshift_excluder_stat - name: disable openshift excluder command: "{{ openshift.common.service_type }}-excluder unexclude" when: - - disable_openshift_excluder | default(false) | bool + - unexclude_openshift_excluder | default(false) | bool + - openshift_excluder_stat.stat.exists when: - not openshift.common.is_atomic | bool diff --git a/roles/openshift_facts/vars/main.yml b/roles/openshift_facts/vars/main.yml index 07f5100ad..053a4cfc8 100644 --- a/roles/openshift_facts/vars/main.yml +++ b/roles/openshift_facts/vars/main.yml @@ -2,7 +2,6 @@ required_packages: - iproute - python-dbus - - python-six - PyYAML - yum-utils diff --git a/roles/openshift_health_checker/callback_plugins/zz_failure_summary.py b/roles/openshift_health_checker/callback_plugins/zz_failure_summary.py index 8caefab15..208e81048 100644 --- a/roles/openshift_health_checker/callback_plugins/zz_failure_summary.py +++ b/roles/openshift_health_checker/callback_plugins/zz_failure_summary.py @@ -53,11 +53,11 @@ class CallbackModule(CallbackBase): subsequent_extra_indent = u' ' * (initial_indent_len + 10) for i, failure in enumerate(self.__failures, 1): - lines = _format_failure(failure) - self._display.display(u'\n{}{}'.format(initial_indent_format.format(i), lines[0])) - for line in lines[1:]: - line = line.replace(u'\n', u'\n' + subsequent_extra_indent) - indented = u'{}{}'.format(subsequent_indent, line) + entries = _format_failure(failure) + self._display.display(u'\n{}{}'.format(initial_indent_format.format(i), entries[0])) + for entry in entries[1:]: + entry = entry.replace(u'\n', u'\n' + subsequent_extra_indent) + indented = u'{}{}'.format(subsequent_indent, entry) self._display.display(indented) @@ -66,8 +66,9 @@ class CallbackModule(CallbackBase): # Status: permanently disabled unless Ansible's API changes. # pylint: disable=protected-access def _format_failure(failure): - '''Return a list of pretty-formatted lines describing a failure, including - relevant information about it. Line separators are not included.''' + '''Return a list of pretty-formatted text entries describing a failure, including + relevant information about it. Expect that the list of text entries will be joined + by a newline separator when output to the user.''' result = failure['result'] host = result._host.get_name() play = _get_play(result._task) @@ -75,16 +76,29 @@ def _format_failure(failure): play = play.get_name() task = result._task.get_name() msg = result._result.get('msg', u'???') - rows = ( + fields = ( (u'Host', host), (u'Play', play), (u'Task', task), (u'Message', stringc(msg, C.COLOR_ERROR)), ) if 'checks' in result._result: - rows += ((u'Details', stringc(pformat(result._result['checks']), C.COLOR_ERROR)),) + fields += ((u'Details', _format_failed_checks(result._result['checks'])),) row_format = '{:10}{}' - return [row_format.format(header + u':', body) for header, body in rows] + return [row_format.format(header + u':', body) for header, body in fields] + + +def _format_failed_checks(checks): + '''Return pretty-formatted text describing checks that failed.''' + failed_check_msgs = [] + for check, body in checks.items(): + if body.get('failed', False): # only show the failed checks + msg = body.get('msg', u"Failed without returning a message") + failed_check_msgs.append('check "%s":\n%s' % (check, msg)) + if failed_check_msgs: + return stringc("\n\n".join(failed_check_msgs), C.COLOR_ERROR) + else: # something failed but no checks will admit to it, so dump everything + return stringc(pformat(checks), C.COLOR_ERROR) # Reason: disable pylint protected-access because we need to access _* diff --git a/roles/openshift_health_checker/library/aos_version.py b/roles/openshift_health_checker/library/aos_version.py index 13b7d310b..191a4b107 100755 --- a/roles/openshift_health_checker/library/aos_version.py +++ b/roles/openshift_health_checker/library/aos_version.py @@ -32,6 +32,7 @@ def main(): # pylint: disable=missing-docstring,too-many-branches bail("prefix must not be empty") yb = yum.YumBase() # pylint: disable=invalid-name + yb.conf.disable_excludes = ["all"] # assume the openshift excluder will be managed, ignore current state # search for package versions available for aos pkgs expected_pkgs = [ diff --git a/roles/openshift_health_checker/library/check_yum_update.py b/roles/openshift_health_checker/library/check_yum_update.py index 9bc14fd47..630ebc848 100755 --- a/roles/openshift_health_checker/library/check_yum_update.py +++ b/roles/openshift_health_checker/library/check_yum_update.py @@ -27,6 +27,7 @@ def main(): # pylint: disable=missing-docstring,too-many-branches module.fail_json(msg=error) yb = yum.YumBase() # pylint: disable=invalid-name + yb.conf.disable_excludes = ["all"] # assume the openshift excluder will be managed, ignore current state # determine if the existing yum configuration is valid try: yb.repos.populateSack(mdtype='metadata', cacheonly=1) diff --git a/roles/openshift_health_checker/openshift_checks/__init__.py b/roles/openshift_health_checker/openshift_checks/__init__.py index 8433923ed..93547a2e0 100644 --- a/roles/openshift_health_checker/openshift_checks/__init__.py +++ b/roles/openshift_health_checker/openshift_checks/__init__.py @@ -8,11 +8,8 @@ import os from abc import ABCMeta, abstractmethod, abstractproperty from importlib import import_module -# add_metaclass is not available in the embedded six from module_utils in Ansible 2.2.1 -from six import add_metaclass -# pylint import-error disabled because pylint cannot find the package -# when installed in a virtualenv -from ansible.module_utils.six.moves import reduce # pylint: disable=import-error, redefined-builtin +from ansible.module_utils import six +from ansible.module_utils.six.moves import reduce # pylint: disable=import-error,redefined-builtin class OpenShiftCheckException(Exception): @@ -20,7 +17,7 @@ class OpenShiftCheckException(Exception): pass -@add_metaclass(ABCMeta) +@six.add_metaclass(ABCMeta) class OpenShiftCheck(object): """A base class for defining checks for an OpenShift cluster environment.""" @@ -66,7 +63,8 @@ def get_var(task_vars, *keys, **kwargs): Ansible task_vars structures are Python dicts, often mapping strings to other dicts. This helper makes it easier to get a nested value, raising - OpenShiftCheckException when a key is not found. + OpenShiftCheckException when a key is not found or returning a default value + provided as a keyword argument. """ try: value = reduce(operator.getitem, keys, task_vars) diff --git a/roles/openshift_health_checker/test/conftest.py b/roles/openshift_health_checker/test/conftest.py new file mode 100644 index 000000000..bf717ae85 --- /dev/null +++ b/roles/openshift_health_checker/test/conftest.py @@ -0,0 +1,5 @@ +import os +import sys + +# extend sys.path so that tests can import openshift_checks +sys.path.insert(1, os.path.dirname(os.path.dirname(__file__))) diff --git a/roles/openshift_health_checker/test/openshift_check_test.py b/roles/openshift_health_checker/test/openshift_check_test.py new file mode 100644 index 000000000..c4c8cd1c2 --- /dev/null +++ b/roles/openshift_health_checker/test/openshift_check_test.py @@ -0,0 +1,40 @@ +import pytest + +from openshift_checks import get_var, OpenShiftCheckException + + +# Fixtures + + +@pytest.fixture() +def task_vars(): + return dict(foo=42, bar=dict(baz="openshift")) + + +@pytest.fixture(params=[ + ("notfound",), + ("multiple", "keys", "not", "in", "task_vars"), +]) +def missing_keys(request): + return request.param + + +# Tests + + +@pytest.mark.parametrize("keys,expected", [ + (("foo",), 42), + (("bar", "baz"), "openshift"), +]) +def test_get_var_ok(task_vars, keys, expected): + assert get_var(task_vars, *keys) == expected + + +def test_get_var_error(task_vars, missing_keys): + with pytest.raises(OpenShiftCheckException): + get_var(task_vars, *missing_keys) + + +def test_get_var_default(task_vars, missing_keys): + default = object() + assert get_var(task_vars, *missing_keys, default=default) == default diff --git a/roles/openshift_hosted/filter_plugins/filters.py b/roles/openshift_hosted/filter_plugins/filters.py index cbfadfe9d..7f41529ac 100644 --- a/roles/openshift_hosted/filter_plugins/filters.py +++ b/roles/openshift_hosted/filter_plugins/filters.py @@ -21,14 +21,21 @@ class FilterModule(object): if replicas is not None: return replicas + replicas = 1 + + # Ignore boolean expression limit of 5. + # pylint: disable=too-many-boolean-expressions if (isinstance(router_nodes, dict) and 'results' in router_nodes and 'results' in router_nodes['results'] and - 'items' in router_nodes['results']['results']): + isinstance(router_nodes['results']['results'], list) and + len(router_nodes['results']['results']) > 0 and + 'items' in router_nodes['results']['results'][0]): - return len(router_nodes['results']['results'][0]['items']) + if len(router_nodes['results']['results'][0]['items']) > 0: + replicas = len(router_nodes['results']['results'][0]['items']) - return 1 + return replicas def filters(self): ''' returns a mapping of filters to methods ''' diff --git a/roles/openshift_hosted/meta/main.yml b/roles/openshift_hosted/meta/main.yml index bbbb76414..9626c23c1 100644 --- a/roles/openshift_hosted/meta/main.yml +++ b/roles/openshift_hosted/meta/main.yml @@ -15,5 +15,3 @@ dependencies: - role: openshift_cli - role: openshift_hosted_facts - role: lib_openshift -- role: openshift_projects - openshift_projects: "{{ openshift_additional_projects | default({}) | oo_merge_dicts({'default':{'default_node_selector':''},'openshift-infra':{'default_node_selector':''},'logging':{'default_node_selector':''}}) }}" diff --git a/roles/openshift_hosted/tasks/main.yml b/roles/openshift_hosted/tasks/main.yml index fe254f72d..6efe2f63c 100644 --- a/roles/openshift_hosted/tasks/main.yml +++ b/roles/openshift_hosted/tasks/main.yml @@ -1,4 +1,11 @@ --- +- name: Create projects + oc_project: + name: "{{ item.key }}" + node_selector: + - "{{ item.value.default_node_selector }}" + with_dict: "{{ openshift_projects }}" + - include: router/router.yml when: openshift_hosted_manage_router | default(true) | bool diff --git a/roles/openshift_hosted/templates/registry_config.j2 b/roles/openshift_hosted/templates/registry_config.j2 index f3336334a..ca6a23f21 100644 --- a/roles/openshift_hosted/templates/registry_config.j2 +++ b/roles/openshift_hosted/templates/registry_config.j2 @@ -71,7 +71,7 @@ middleware: - name: openshift options: pullthrough: {{ openshift_hosted_registry_pullthrough | default(true) }} - acceptschema2: {{ openshift_hosted_registry_acceptschema2 | default(false) }} + acceptschema2: {{ openshift_hosted_registry_acceptschema2 | default(true) }} enforcequota: {{ openshift_hosted_registry_enforcequota | default(false) }} {% if openshift_hosted_registry_storage_provider | default('') == 's3' and openshift_hosted_registry_storage_s3_cloudfront_baseurl is defined %} storage: diff --git a/roles/openshift_hosted/vars/main.yml b/roles/openshift_hosted/vars/main.yml index 521578cd0..0821d0e7e 100644 --- a/roles/openshift_hosted/vars/main.yml +++ b/roles/openshift_hosted/vars/main.yml @@ -1,3 +1,13 @@ --- openshift_master_config_dir: "{{ openshift.common.config_base }}/master" registry_config_secret_name: registry-config + +openshift_default_projects: + default: + default_node_selector: '' + logging: + default_node_selector: '' + openshift-infra: + default_node_selector: '' + +openshift_projects: "{{ openshift_additional_projects | default({}) | oo_merge_dicts(openshift_default_projects) }}" diff --git a/roles/openshift_logging/defaults/main.yml b/roles/openshift_logging/defaults/main.yml index ad9c1ce42..04fd42cbf 100644 --- a/roles/openshift_logging/defaults/main.yml +++ b/roles/openshift_logging/defaults/main.yml @@ -3,7 +3,7 @@ openshift_logging_image_prefix: "{{ openshift_hosted_logging_deployer_prefix | d openshift_logging_image_version: "{{ openshift_hosted_logging_deployer_version | default('latest') }}" openshift_logging_use_ops: "{{ openshift_hosted_logging_enable_ops_cluster | default('false') | bool }}" openshift_logging_master_url: "https://kubernetes.default.svc.{{ openshift.common.dns_domain }}" -openshift_logging_master_public_url: "{{ openshift_hosted_logging_master_public_url | default('https://' + openshift.common.public_hostname + ':' + openshift.master.api_port) }}" +openshift_logging_master_public_url: "{{ openshift_hosted_logging_master_public_url | default('https://' + openshift.common.public_hostname + ':' ~ (openshift_master_api_port | default('8443', true))) }}" openshift_logging_namespace: logging openshift_logging_install_logging: True openshift_logging_image_pull_secret: "{{ openshift_hosted_logging_image_pull_secret | default('') }}" @@ -22,7 +22,7 @@ openshift_logging_curator_ops_cpu_limit: 100m openshift_logging_curator_ops_memory_limit: null openshift_logging_curator_ops_nodeselector: "{{ openshift_hosted_logging_curator_ops_nodeselector | default('') | map_from_pairs }}" -openshift_logging_kibana_hostname: "{{ openshift_hosted_logging_hostname | default('kibana.' + openshift.common.dns_domain) }}" +openshift_logging_kibana_hostname: "{{ openshift_hosted_logging_hostname | default('kibana.' ~ (openshift_master_default_subdomain | default('router.default.svc.cluster.local', true))) }}" openshift_logging_kibana_cpu_limit: null openshift_logging_kibana_memory_limit: null openshift_logging_kibana_proxy_debug: false @@ -46,7 +46,7 @@ openshift_logging_kibana_key: "" #for the public facing kibana certs openshift_logging_kibana_ca: "" -openshift_logging_kibana_ops_hostname: "{{ openshift_hosted_logging_ops_hostname | default('kibana-ops.' + openshift.common.dns_domain) }}" +openshift_logging_kibana_ops_hostname: "{{ openshift_hosted_logging_ops_hostname | default('kibana-ops.' ~ (openshift_master_default_subdomain | default('router.default.svc.cluster.local', true))) }}" openshift_logging_kibana_ops_cpu_limit: null openshift_logging_kibana_ops_memory_limit: null openshift_logging_kibana_ops_proxy_debug: false @@ -99,7 +99,7 @@ openshift_logging_es_ops_storage_group: "{{ openshift_hosted_logging_elasticsear openshift_logging_es_ops_nodeselector: "{{ openshift_hosted_logging_elasticsearch_ops_nodeselector | default('') | map_from_pairs }}" # storage related defaults -openshift_logging_storage_access_modes: "{{ openshift_hosted_logging_storage_access_modes | default('ReadWriteOnce') }}" +openshift_logging_storage_access_modes: "{{ openshift_hosted_logging_storage_access_modes | default(['ReadWriteOnce']) }}" # following can be uncommented to provide values for configmaps -- take care when providing file contents as it may cause your cluster to not operate correctly diff --git a/roles/openshift_logging/files/generate-jks.sh b/roles/openshift_logging/files/generate-jks.sh index 9fe557f83..b5ba7f9d1 100644 --- a/roles/openshift_logging/files/generate-jks.sh +++ b/roles/openshift_logging/files/generate-jks.sh @@ -1,4 +1,4 @@ -#! /bin/sh +#! /bin/bash set -ex function usage() { diff --git a/roles/openshift_logging/tasks/generate_pvcs.yaml b/roles/openshift_logging/tasks/generate_pvcs.yaml index e1629908f..fa7a86c27 100644 --- a/roles/openshift_logging/tasks/generate_pvcs.yaml +++ b/roles/openshift_logging/tasks/generate_pvcs.yaml @@ -15,8 +15,7 @@ vars: obj_name: "{{claim_name}}" size: "{{es_pvc_size}}" - access_modes: - - "{{ es_access_modes }}" + access_modes: "{{ es_access_modes | list }}" pv_selector: "{{es_pv_selector}}" with_items: - "{{es_pvc_pool | default([])}}" @@ -35,8 +34,7 @@ annotations: volume.alpha.kubernetes.io/storage-class: "dynamic" size: "{{es_pvc_size}}" - access_modes: - - "{{ es_access_modes }}" + access_modes: "{{ es_access_modes | list }}" pv_selector: "{{es_pv_selector}}" with_items: - "{{es_pvc_pool|default([])}}" diff --git a/roles/openshift_logging/tasks/generate_secrets.yaml b/roles/openshift_logging/tasks/generate_secrets.yaml index 0f8e7ae58..f396bcc6d 100644 --- a/roles/openshift_logging/tasks/generate_secrets.yaml +++ b/roles/openshift_logging/tasks/generate_secrets.yaml @@ -31,8 +31,6 @@ - fluentd loop_control: loop_var: component - when: secret_name not in openshift_logging_facts.{{component}}.secrets or - secret_keys | difference(openshift_logging_facts.{{component}}.secrets["{{secret_name}}"]["keys"]) | length != 0 check_mode: no changed_when: no @@ -50,8 +48,6 @@ kibana_key_file: "{{key_pairs | entry_from_named_pair('kibana_internal_key')| b64decode }}" kibana_cert_file: "{{key_pairs | entry_from_named_pair('kibana_internal_cert')| b64decode }}" server_tls_file: "{{key_pairs | entry_from_named_pair('server_tls')| b64decode }}" - when: secret_name not in openshift_logging_facts.kibana.secrets or - secret_keys | difference(openshift_logging_facts.kibana.secrets["{{secret_name}}"]["keys"]) | length != 0 check_mode: no changed_when: no @@ -66,8 +62,6 @@ secret_name: logging-elasticsearch secret_keys: ["admin-cert", "searchguard.key", "admin-ca", "key", "truststore", "admin-key", "searchguard.truststore"] register: logging_es_secret - when: secret_name not in openshift_logging_facts.elasticsearch.secrets or - secret_keys | difference(openshift_logging_facts.elasticsearch.secrets["{{secret_name}}"]["keys"]) | length != 0 check_mode: no changed_when: no diff --git a/roles/openshift_logging/tasks/install_elasticsearch.yaml b/roles/openshift_logging/tasks/install_elasticsearch.yaml index 086f9e33f..1b750bcbe 100644 --- a/roles/openshift_logging/tasks/install_elasticsearch.yaml +++ b/roles/openshift_logging/tasks/install_elasticsearch.yaml @@ -2,8 +2,13 @@ - name: Getting current ES deployment size set_fact: openshift_logging_current_es_size={{ openshift_logging_facts.elasticsearch.deploymentconfigs.keys() | length }} +- set_fact: openshift_logging_es_pvc_prefix="logging-es" + when: "not openshift_logging_es_pvc_prefix or openshift_logging_es_pvc_prefix == ''" + - set_fact: es_pvc_pool={{[]}} +- set_fact: openshift_logging_es_pvc_prefix="{{ openshift_logging_es_pvc_prefix | default('logging-es') }}" + - name: Generate PersistentVolumeClaims include: "{{ role_path}}/tasks/generate_pvcs.yaml" vars: @@ -58,6 +63,8 @@ - name: Getting current ES deployment size set_fact: openshift_logging_current_es_ops_size={{ openshift_logging_facts.elasticsearch_ops.deploymentconfigs.keys() | length }} +- set_fact: openshift_logging_es_ops_pvc_prefix="{{ openshift_logging_es_ops_pvc_prefix | default('logging-es-ops') }}" + - name: Validate Elasticsearch cluster size for Ops fail: msg="The openshift_logging_es_ops_cluster_size may not be scaled down more than 1 less (or 0) the number of Elasticsearch nodes already deployed" vars: @@ -68,6 +75,9 @@ - "{{es_dcs | length - openshift_logging_es_ops_cluster_size|int | abs > 1}}" check_mode: no +- set_fact: openshift_logging_es_ops_pvc_prefix="logging-es-ops" + when: "not openshift_logging_es_ops_pvc_prefix or openshift_logging_es_ops_pvc_prefix == ''" + - set_fact: es_pvc_pool={{[]}} - name: Generate PersistentVolumeClaims for Ops diff --git a/roles/openshift_logging/templates/pvc.j2 b/roles/openshift_logging/templates/pvc.j2 index f19a3a750..07d81afff 100644 --- a/roles/openshift_logging/templates/pvc.j2 +++ b/roles/openshift_logging/templates/pvc.j2 @@ -1,7 +1,7 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: {{obj_name}} + name: "{{obj_name}}" labels: logging-infra: support {% if annotations is defined %} diff --git a/roles/openshift_manage_node/tasks/main.yml b/roles/openshift_manage_node/tasks/main.yml index 9a883feed..f67aee88b 100644 --- a/roles/openshift_manage_node/tasks/main.yml +++ b/roles/openshift_manage_node/tasks/main.yml @@ -39,7 +39,7 @@ delegate_to: "{{ openshift_master_host }}" - name: Set node schedulability - oadm_manage_node: + oc_adm_manage_node: node: "{{ openshift.node.nodename | lower }}" schedulable: "{{ 'true' if openshift.node.schedulable | bool else 'false' }}" retries: 10 diff --git a/roles/openshift_metrics/defaults/main.yaml b/roles/openshift_metrics/defaults/main.yaml index db4a0e1fc..5921b7bb7 100644 --- a/roles/openshift_metrics/defaults/main.yaml +++ b/roles/openshift_metrics/defaults/main.yaml @@ -47,7 +47,7 @@ openshift_metrics_node_id: nodename openshift_metrics_project: openshift-infra openshift_metrics_cassandra_pvc_prefix: "{{ openshift_hosted_metrics_storage_volume_name | default('metrics-cassandra') }}" -openshift_metrics_cassandra_pvc_access: "{{ openshift_hosted_metrics_storage_access_modes | default('ReadWriteOnce') }}" +openshift_metrics_cassandra_pvc_access: "{{ openshift_hosted_metrics_storage_access_modes | default(['ReadWriteOnce']) }}" openshift_metrics_hawkular_user_write_access: False diff --git a/roles/openshift_metrics/tasks/install_cassandra.yaml b/roles/openshift_metrics/tasks/install_cassandra.yaml index 66c81562b..a467c1a51 100644 --- a/roles/openshift_metrics/tasks/install_cassandra.yaml +++ b/roles/openshift_metrics/tasks/install_cassandra.yaml @@ -22,6 +22,9 @@ with_sequence: count={{ openshift_metrics_cassandra_replicas }} changed_when: false +- set_fact: openshift_metrics_cassandra_pvc_prefix="hawkular-metrics" + when: "not openshift_metrics_cassandra_pvc_prefix or openshift_metrics_cassandra_pvc_prefix == ''" + - name: generate hawkular-cassandra persistent volume claims template: src: pvc.j2 @@ -30,8 +33,7 @@ obj_name: "{{ openshift_metrics_cassandra_pvc_prefix }}-{{ item }}" labels: metrics-infra: hawkular-cassandra - access_modes: - - "{{ openshift_metrics_cassandra_pvc_access }}" + access_modes: "{{ openshift_metrics_cassandra_pvc_access | list }}" size: "{{ openshift_metrics_cassandra_pvc_size }}" with_sequence: count={{ openshift_metrics_cassandra_replicas }} when: @@ -49,8 +51,7 @@ metrics-infra: hawkular-cassandra annotations: volume.alpha.kubernetes.io/storage-class: dynamic - access_modes: - - "{{ openshift_metrics_cassandra_pvc_access }}" + access_modes: "{{ openshift_metrics_cassandra_pvc_access | list }}" size: "{{ openshift_metrics_cassandra_pvc_size }}" with_sequence: count={{ openshift_metrics_cassandra_replicas }} when: openshift_metrics_cassandra_storage_type == 'dynamic' diff --git a/roles/openshift_metrics/templates/pvc.j2 b/roles/openshift_metrics/templates/pvc.j2 index 8fbfa8b5d..885dd368d 100644 --- a/roles/openshift_metrics/templates/pvc.j2 +++ b/roles/openshift_metrics/templates/pvc.j2 @@ -1,7 +1,7 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: {{obj_name}} + name: "{{obj_name}}" {% if labels is not defined %} labels: logging-infra: support diff --git a/roles/openshift_metrics/vars/main.yaml b/roles/openshift_metrics/vars/main.yaml index 4a3724e3f..47aa76dd2 100644 --- a/roles/openshift_metrics/vars/main.yaml +++ b/roles/openshift_metrics/vars/main.yaml @@ -8,3 +8,4 @@ openshift_metrics_cassandra_storage_types: - emptydir - pv - dynamic +- nfs diff --git a/roles/openshift_node/templates/openshift.docker.node.service b/roles/openshift_node/templates/openshift.docker.node.service index 6ec88f85e..b4fd5aeb0 100644 --- a/roles/openshift_node/templates/openshift.docker.node.service +++ b/roles/openshift_node/templates/openshift.docker.node.service @@ -6,6 +6,8 @@ PartOf=docker.service Requires=docker.service {% if openshift.common.use_openshift_sdn %} Requires=openvswitch.service +After=ovsdb-server.service +After=ovs-vswitchd.service {% endif %} Wants={{ openshift.common.service_type }}-master.service Requires={{ openshift.common.service_type }}-node-dep.service diff --git a/roles/openshift_projects/meta/main.yml b/roles/openshift_projects/meta/main.yml deleted file mode 100644 index 107a70b83..000000000 --- a/roles/openshift_projects/meta/main.yml +++ /dev/null @@ -1,15 +0,0 @@ ---- -galaxy_info: - author: Jason DeTiberus - description: OpenShift Projects - company: Red Hat, Inc. - license: Apache License, Version 2.0 - min_ansible_version: 1.9 - platforms: - - name: EL - versions: - - 7 - categories: - - cloud -dependencies: -- { role: openshift_facts } diff --git a/roles/openshift_projects/tasks/main.yml b/roles/openshift_projects/tasks/main.yml deleted file mode 100644 index 30d58afd3..000000000 --- a/roles/openshift_projects/tasks/main.yml +++ /dev/null @@ -1,47 +0,0 @@ ---- -- name: Create temp directory for kubeconfig - command: mktemp -d /tmp/openshift-ansible-XXXXXX - register: mktemp - changed_when: False - -- name: Copy the admin client config(s) - command: > - cp {{ openshift_master_config_dir }}/admin.kubeconfig {{ mktemp.stdout }}/admin.kubeconfig - changed_when: False - -- name: Determine if projects exist - command: > - {{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig - get projects {{ item.key }} -o json - with_dict: "{{ openshift_projects }}" - failed_when: false - changed_when: false - register: project_test - -- name: Create projects - command: > - {{ openshift.common.client_binary }} adm --config={{ mktemp.stdout }}/admin.kubeconfig - new-project {{ item.item.key }} - {% if item.item.value.default_node_selector | default(none) != none %} - {{ '--node-selector=' ~ item.item.value.default_node_selector }} - {% endif %} - when: item.rc == 1 - with_items: - - "{{ project_test.results }}" - -- name: Update project default node selector if necessary - command: > - {{ openshift.common.client_binary }} - --config={{ mktemp.stdout }}/admin.kubeconfig patch namespace {{ item.item.key }} - -p '{"metadata": {"annotations": {"openshift.io/node-selector": "{{ item.item.value.default_node_selector }}"}}}' - when: "{{ item.rc == 0 and item.item.value.default_node_selector | default(none) != none - and item.item.value.default_node_selector | default(none) != (item.stdout | from_json).metadata.annotations['openshift.io/node-selector'] | default(none) }}" - with_items: - - "{{ project_test.results }}" - register: annotate_project - -- name: Delete temp directory - file: - name: "{{ mktemp.stdout }}" - state: absent - changed_when: False diff --git a/roles/openshift_projects/vars/main.yml b/roles/openshift_projects/vars/main.yml deleted file mode 100644 index 9967e26f4..000000000 --- a/roles/openshift_projects/vars/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -openshift_master_config_dir: "{{ openshift.common.config_base }}/master" |