Initial Ccotyledon Commit.
This commit is contained in:
		
							
								
								
									
										7
									
								
								.coveragerc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.coveragerc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					[run]
 | 
				
			||||||
 | 
					branch = True
 | 
				
			||||||
 | 
					source = cotyledon
 | 
				
			||||||
 | 
					omit = cotyledon/openstack/*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[report]
 | 
				
			||||||
 | 
					ignore_errors = True
 | 
				
			||||||
							
								
								
									
										55
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					*.py[cod]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# C extensions
 | 
				
			||||||
 | 
					*.so
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Packages
 | 
				
			||||||
 | 
					*.egg*
 | 
				
			||||||
 | 
					*.egg-info
 | 
				
			||||||
 | 
					dist
 | 
				
			||||||
 | 
					build
 | 
				
			||||||
 | 
					eggs
 | 
				
			||||||
 | 
					parts
 | 
				
			||||||
 | 
					bin
 | 
				
			||||||
 | 
					var
 | 
				
			||||||
 | 
					sdist
 | 
				
			||||||
 | 
					develop-eggs
 | 
				
			||||||
 | 
					.installed.cfg
 | 
				
			||||||
 | 
					lib
 | 
				
			||||||
 | 
					lib64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Installer logs
 | 
				
			||||||
 | 
					pip-log.txt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Unit test / coverage reports
 | 
				
			||||||
 | 
					cover/
 | 
				
			||||||
 | 
					.coverage*
 | 
				
			||||||
 | 
					!.coveragerc
 | 
				
			||||||
 | 
					.tox
 | 
				
			||||||
 | 
					nosetests.xml
 | 
				
			||||||
 | 
					.testrepository
 | 
				
			||||||
 | 
					.venv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Translations
 | 
				
			||||||
 | 
					*.mo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Mr Developer
 | 
				
			||||||
 | 
					.mr.developer.cfg
 | 
				
			||||||
 | 
					.project
 | 
				
			||||||
 | 
					.pydevproject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Complexity
 | 
				
			||||||
 | 
					output/*.html
 | 
				
			||||||
 | 
					output/*/index.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Sphinx
 | 
				
			||||||
 | 
					doc/build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# pbr generates these
 | 
				
			||||||
 | 
					AUTHORS
 | 
				
			||||||
 | 
					ChangeLog
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Editors
 | 
				
			||||||
 | 
					*~
 | 
				
			||||||
 | 
					.*.swp
 | 
				
			||||||
 | 
					.*sw?
 | 
				
			||||||
							
								
								
									
										4
									
								
								.gitreview
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.gitreview
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					[gerrit]
 | 
				
			||||||
 | 
					host=review.openstack.org
 | 
				
			||||||
 | 
					port=29418
 | 
				
			||||||
 | 
					project=openstack/cotyledon.git
 | 
				
			||||||
							
								
								
									
										3
									
								
								.mailmap
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.mailmap
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					# Format is:
 | 
				
			||||||
 | 
					# <preferred e-mail> <other e-mail 1>
 | 
				
			||||||
 | 
					# <preferred e-mail> <other e-mail 2>
 | 
				
			||||||
							
								
								
									
										7
									
								
								.testr.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.testr.conf
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					[DEFAULT]
 | 
				
			||||||
 | 
					test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
 | 
				
			||||||
 | 
					             OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
 | 
				
			||||||
 | 
					             OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
 | 
				
			||||||
 | 
					             ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION
 | 
				
			||||||
 | 
					test_id_option=--load-list $IDFILE
 | 
				
			||||||
 | 
					test_list_option=--list
 | 
				
			||||||
							
								
								
									
										17
									
								
								CONTRIBUTING.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								CONTRIBUTING.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					If you would like to contribute to the development of OpenStack, you must
 | 
				
			||||||
 | 
					follow the steps in this page:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   http://docs.openstack.org/infra/manual/developers.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you already have a good understanding of how the system works and your
 | 
				
			||||||
 | 
					OpenStack accounts are set up, you can skip to the development workflow
 | 
				
			||||||
 | 
					section of this documentation to learn how changes to OpenStack should be
 | 
				
			||||||
 | 
					submitted for review via the Gerrit tool:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   http://docs.openstack.org/infra/manual/developers.html#development-workflow
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Pull requests submitted through GitHub will be ignored.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Bugs should be filed on Launchpad, not GitHub:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   https://bugs.launchpad.net/cotyledon
 | 
				
			||||||
							
								
								
									
										4
									
								
								HACKING.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								HACKING.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					cotyledon Style Commandments
 | 
				
			||||||
 | 
					===============================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/
 | 
				
			||||||
							
								
								
									
										176
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,176 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					                                 Apache License
 | 
				
			||||||
 | 
					                           Version 2.0, January 2004
 | 
				
			||||||
 | 
					                        http://www.apache.org/licenses/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   1. Definitions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "License" shall mean the terms and conditions for use, reproduction,
 | 
				
			||||||
 | 
					      and distribution as defined by Sections 1 through 9 of this document.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Licensor" shall mean the copyright owner or entity authorized by
 | 
				
			||||||
 | 
					      the copyright owner that is granting the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Legal Entity" shall mean the union of the acting entity and all
 | 
				
			||||||
 | 
					      other entities that control, are controlled by, or are under common
 | 
				
			||||||
 | 
					      control with that entity. For the purposes of this definition,
 | 
				
			||||||
 | 
					      "control" means (i) the power, direct or indirect, to cause the
 | 
				
			||||||
 | 
					      direction or management of such entity, whether by contract or
 | 
				
			||||||
 | 
					      otherwise, or (ii) ownership of fifty percent (50%) or more of the
 | 
				
			||||||
 | 
					      outstanding shares, or (iii) beneficial ownership of such entity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "You" (or "Your") shall mean an individual or Legal Entity
 | 
				
			||||||
 | 
					      exercising permissions granted by this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Source" form shall mean the preferred form for making modifications,
 | 
				
			||||||
 | 
					      including but not limited to software source code, documentation
 | 
				
			||||||
 | 
					      source, and configuration files.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Object" form shall mean any form resulting from mechanical
 | 
				
			||||||
 | 
					      transformation or translation of a Source form, including but
 | 
				
			||||||
 | 
					      not limited to compiled object code, generated documentation,
 | 
				
			||||||
 | 
					      and conversions to other media types.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Work" shall mean the work of authorship, whether in Source or
 | 
				
			||||||
 | 
					      Object form, made available under the License, as indicated by a
 | 
				
			||||||
 | 
					      copyright notice that is included in or attached to the work
 | 
				
			||||||
 | 
					      (an example is provided in the Appendix below).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Derivative Works" shall mean any work, whether in Source or Object
 | 
				
			||||||
 | 
					      form, that is based on (or derived from) the Work and for which the
 | 
				
			||||||
 | 
					      editorial revisions, annotations, elaborations, or other modifications
 | 
				
			||||||
 | 
					      represent, as a whole, an original work of authorship. For the purposes
 | 
				
			||||||
 | 
					      of this License, Derivative Works shall not include works that remain
 | 
				
			||||||
 | 
					      separable from, or merely link (or bind by name) to the interfaces of,
 | 
				
			||||||
 | 
					      the Work and Derivative Works thereof.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Contribution" shall mean any work of authorship, including
 | 
				
			||||||
 | 
					      the original version of the Work and any modifications or additions
 | 
				
			||||||
 | 
					      to that Work or Derivative Works thereof, that is intentionally
 | 
				
			||||||
 | 
					      submitted to Licensor for inclusion in the Work by the copyright owner
 | 
				
			||||||
 | 
					      or by an individual or Legal Entity authorized to submit on behalf of
 | 
				
			||||||
 | 
					      the copyright owner. For the purposes of this definition, "submitted"
 | 
				
			||||||
 | 
					      means any form of electronic, verbal, or written communication sent
 | 
				
			||||||
 | 
					      to the Licensor or its representatives, including but not limited to
 | 
				
			||||||
 | 
					      communication on electronic mailing lists, source code control systems,
 | 
				
			||||||
 | 
					      and issue tracking systems that are managed by, or on behalf of, the
 | 
				
			||||||
 | 
					      Licensor for the purpose of discussing and improving the Work, but
 | 
				
			||||||
 | 
					      excluding communication that is conspicuously marked or otherwise
 | 
				
			||||||
 | 
					      designated in writing by the copyright owner as "Not a Contribution."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      "Contributor" shall mean Licensor and any individual or Legal Entity
 | 
				
			||||||
 | 
					      on behalf of whom a Contribution has been received by Licensor and
 | 
				
			||||||
 | 
					      subsequently incorporated within the Work.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   2. Grant of Copyright License. Subject to the terms and conditions of
 | 
				
			||||||
 | 
					      this License, each Contributor hereby grants to You a perpetual,
 | 
				
			||||||
 | 
					      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
				
			||||||
 | 
					      copyright license to reproduce, prepare Derivative Works of,
 | 
				
			||||||
 | 
					      publicly display, publicly perform, sublicense, and distribute the
 | 
				
			||||||
 | 
					      Work and such Derivative Works in Source or Object form.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   3. Grant of Patent License. Subject to the terms and conditions of
 | 
				
			||||||
 | 
					      this License, each Contributor hereby grants to You a perpetual,
 | 
				
			||||||
 | 
					      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
				
			||||||
 | 
					      (except as stated in this section) patent license to make, have made,
 | 
				
			||||||
 | 
					      use, offer to sell, sell, import, and otherwise transfer the Work,
 | 
				
			||||||
 | 
					      where such license applies only to those patent claims licensable
 | 
				
			||||||
 | 
					      by such Contributor that are necessarily infringed by their
 | 
				
			||||||
 | 
					      Contribution(s) alone or by combination of their Contribution(s)
 | 
				
			||||||
 | 
					      with the Work to which such Contribution(s) was submitted. If You
 | 
				
			||||||
 | 
					      institute patent litigation against any entity (including a
 | 
				
			||||||
 | 
					      cross-claim or counterclaim in a lawsuit) alleging that the Work
 | 
				
			||||||
 | 
					      or a Contribution incorporated within the Work constitutes direct
 | 
				
			||||||
 | 
					      or contributory patent infringement, then any patent licenses
 | 
				
			||||||
 | 
					      granted to You under this License for that Work shall terminate
 | 
				
			||||||
 | 
					      as of the date such litigation is filed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   4. Redistribution. You may reproduce and distribute copies of the
 | 
				
			||||||
 | 
					      Work or Derivative Works thereof in any medium, with or without
 | 
				
			||||||
 | 
					      modifications, and in Source or Object form, provided that You
 | 
				
			||||||
 | 
					      meet the following conditions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (a) You must give any other recipients of the Work or
 | 
				
			||||||
 | 
					          Derivative Works a copy of this License; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (b) You must cause any modified files to carry prominent notices
 | 
				
			||||||
 | 
					          stating that You changed the files; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (c) You must retain, in the Source form of any Derivative Works
 | 
				
			||||||
 | 
					          that You distribute, all copyright, patent, trademark, and
 | 
				
			||||||
 | 
					          attribution notices from the Source form of the Work,
 | 
				
			||||||
 | 
					          excluding those notices that do not pertain to any part of
 | 
				
			||||||
 | 
					          the Derivative Works; and
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      (d) If the Work includes a "NOTICE" text file as part of its
 | 
				
			||||||
 | 
					          distribution, then any Derivative Works that You distribute must
 | 
				
			||||||
 | 
					          include a readable copy of the attribution notices contained
 | 
				
			||||||
 | 
					          within such NOTICE file, excluding those notices that do not
 | 
				
			||||||
 | 
					          pertain to any part of the Derivative Works, in at least one
 | 
				
			||||||
 | 
					          of the following places: within a NOTICE text file distributed
 | 
				
			||||||
 | 
					          as part of the Derivative Works; within the Source form or
 | 
				
			||||||
 | 
					          documentation, if provided along with the Derivative Works; or,
 | 
				
			||||||
 | 
					          within a display generated by the Derivative Works, if and
 | 
				
			||||||
 | 
					          wherever such third-party notices normally appear. The contents
 | 
				
			||||||
 | 
					          of the NOTICE file are for informational purposes only and
 | 
				
			||||||
 | 
					          do not modify the License. You may add Your own attribution
 | 
				
			||||||
 | 
					          notices within Derivative Works that You distribute, alongside
 | 
				
			||||||
 | 
					          or as an addendum to the NOTICE text from the Work, provided
 | 
				
			||||||
 | 
					          that such additional attribution notices cannot be construed
 | 
				
			||||||
 | 
					          as modifying the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      You may add Your own copyright statement to Your modifications and
 | 
				
			||||||
 | 
					      may provide additional or different license terms and conditions
 | 
				
			||||||
 | 
					      for use, reproduction, or distribution of Your modifications, or
 | 
				
			||||||
 | 
					      for any such Derivative Works as a whole, provided Your use,
 | 
				
			||||||
 | 
					      reproduction, and distribution of the Work otherwise complies with
 | 
				
			||||||
 | 
					      the conditions stated in this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   5. Submission of Contributions. Unless You explicitly state otherwise,
 | 
				
			||||||
 | 
					      any Contribution intentionally submitted for inclusion in the Work
 | 
				
			||||||
 | 
					      by You to the Licensor shall be under the terms and conditions of
 | 
				
			||||||
 | 
					      this License, without any additional terms or conditions.
 | 
				
			||||||
 | 
					      Notwithstanding the above, nothing herein shall supersede or modify
 | 
				
			||||||
 | 
					      the terms of any separate license agreement you may have executed
 | 
				
			||||||
 | 
					      with Licensor regarding such Contributions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   6. Trademarks. This License does not grant permission to use the trade
 | 
				
			||||||
 | 
					      names, trademarks, service marks, or product names of the Licensor,
 | 
				
			||||||
 | 
					      except as required for reasonable and customary use in describing the
 | 
				
			||||||
 | 
					      origin of the Work and reproducing the content of the NOTICE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   7. Disclaimer of Warranty. Unless required by applicable law or
 | 
				
			||||||
 | 
					      agreed to in writing, Licensor provides the Work (and each
 | 
				
			||||||
 | 
					      Contributor provides its Contributions) on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 | 
				
			||||||
 | 
					      implied, including, without limitation, any warranties or conditions
 | 
				
			||||||
 | 
					      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 | 
				
			||||||
 | 
					      PARTICULAR PURPOSE. You are solely responsible for determining the
 | 
				
			||||||
 | 
					      appropriateness of using or redistributing the Work and assume any
 | 
				
			||||||
 | 
					      risks associated with Your exercise of permissions under this License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   8. Limitation of Liability. In no event and under no legal theory,
 | 
				
			||||||
 | 
					      whether in tort (including negligence), contract, or otherwise,
 | 
				
			||||||
 | 
					      unless required by applicable law (such as deliberate and grossly
 | 
				
			||||||
 | 
					      negligent acts) or agreed to in writing, shall any Contributor be
 | 
				
			||||||
 | 
					      liable to You for damages, including any direct, indirect, special,
 | 
				
			||||||
 | 
					      incidental, or consequential damages of any character arising as a
 | 
				
			||||||
 | 
					      result of this License or out of the use or inability to use the
 | 
				
			||||||
 | 
					      Work (including but not limited to damages for loss of goodwill,
 | 
				
			||||||
 | 
					      work stoppage, computer failure or malfunction, or any and all
 | 
				
			||||||
 | 
					      other commercial damages or losses), even if such Contributor
 | 
				
			||||||
 | 
					      has been advised of the possibility of such damages.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   9. Accepting Warranty or Additional Liability. While redistributing
 | 
				
			||||||
 | 
					      the Work or Derivative Works thereof, You may choose to offer,
 | 
				
			||||||
 | 
					      and charge a fee for, acceptance of support, warranty, indemnity,
 | 
				
			||||||
 | 
					      or other liability obligations and/or rights consistent with this
 | 
				
			||||||
 | 
					      License. However, in accepting such obligations, You may act only
 | 
				
			||||||
 | 
					      on Your own behalf and on Your sole responsibility, not on behalf
 | 
				
			||||||
 | 
					      of any other Contributor, and only if You agree to indemnify,
 | 
				
			||||||
 | 
					      defend, and hold each Contributor harmless for any liability
 | 
				
			||||||
 | 
					      incurred by, or claims asserted against, such Contributor by reason
 | 
				
			||||||
 | 
					      of your accepting any such warranty or additional liability.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										6
									
								
								MANIFEST.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								MANIFEST.in
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					include AUTHORS
 | 
				
			||||||
 | 
					include ChangeLog
 | 
				
			||||||
 | 
					exclude .gitignore
 | 
				
			||||||
 | 
					exclude .gitreview
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					global-exclude *.pyc
 | 
				
			||||||
							
								
								
									
										10
									
								
								README.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								README.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					===============================
 | 
				
			||||||
 | 
					Cotyledon
 | 
				
			||||||
 | 
					===============================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Cotyledon provides a framework for defining long-running services.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Free software: Apache license
 | 
				
			||||||
 | 
					* Documentation: http://docs.openstack.org/developer/cotyledon
 | 
				
			||||||
 | 
					* Source: http://git.openstack.org/cgit/openstack/cotyledon
 | 
				
			||||||
 | 
					* Bugs: http://bugs.launchpad.net/replace with the name of the project on launchpad
 | 
				
			||||||
							
								
								
									
										381
									
								
								cotyledon/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										381
									
								
								cotyledon/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,381 @@
 | 
				
			|||||||
 | 
					# Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
				
			||||||
 | 
					# not use this file except in compliance with the License. You may obtain
 | 
				
			||||||
 | 
					# a copy of the License at
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#      http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
				
			||||||
 | 
					# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
				
			||||||
 | 
					# License for the specific language governing permissions and limitations
 | 
				
			||||||
 | 
					# under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import collections
 | 
				
			||||||
 | 
					import contextlib
 | 
				
			||||||
 | 
					import errno
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import random
 | 
				
			||||||
 | 
					import resource
 | 
				
			||||||
 | 
					import signal
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					import threading
 | 
				
			||||||
 | 
					import time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import setproctitle
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOG = logging.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAXFD = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
 | 
				
			||||||
 | 
					if MAXFD == resource.RLIM_INFINITY:
 | 
				
			||||||
 | 
					    MAXFD = 2048
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SIGNAL_TO_NAME = dict((getattr(signal, name), name) for name in dir(signal)
 | 
				
			||||||
 | 
					                      if name.startswith("SIG") and name not in ('SIG_DFL',
 | 
				
			||||||
 | 
					                                                                 'SIG_IGN'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_ServiceConfig = collections.namedtuple("ServiceConfig", ["service",
 | 
				
			||||||
 | 
					                                                          "workers"])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@contextlib.contextmanager
 | 
				
			||||||
 | 
					def _catch_exception_and_exit():
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        yield
 | 
				
			||||||
 | 
					    except SystemExit as exc:
 | 
				
			||||||
 | 
					        os._exit(exc.code)
 | 
				
			||||||
 | 
					    except BaseException:
 | 
				
			||||||
 | 
					        LOG.exception('Unhandled exception')
 | 
				
			||||||
 | 
					        os._exit(2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ServiceMethodNotImplementedError(NotImplementedError):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Service(object):
 | 
				
			||||||
 | 
					    """Base class for a service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This class will be executed in a new child process of a
 | 
				
			||||||
 | 
					    :py:class:`ServiceRunner`. It registers signals to manager the reloading
 | 
				
			||||||
 | 
					    and the ending of the process.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Methods :py:meth:`run`, :py:meth:`terminate` and :py:meth:`reload` are
 | 
				
			||||||
 | 
					    optional.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    name = None
 | 
				
			||||||
 | 
					    """Service name used in the process title and the log messages in additionnal
 | 
				
			||||||
 | 
					    of the worker_id."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, worker_id):
 | 
				
			||||||
 | 
					        """Create a new Service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param worker_id: the identifier of this service instance
 | 
				
			||||||
 | 
					        :param worker_id: int
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if self.name is None:
 | 
				
			||||||
 | 
					            self.name = self.__class__.__name__
 | 
				
			||||||
 | 
					        self.worker_id = worker_id
 | 
				
			||||||
 | 
					        self.pid = os.getpid()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        pname = os.path.basename(sys.argv[0])
 | 
				
			||||||
 | 
					        self._title = "%(name)s(%(worker_id)d) [%(pid)d]" % dict(
 | 
				
			||||||
 | 
					            name=self.name, worker_id=self.worker_id, pid=self.pid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Set process title
 | 
				
			||||||
 | 
					        setproctitle.setproctitle(
 | 
				
			||||||
 | 
					            "%(pname)s - %(name)s(%(worker_id)d)" % dict(
 | 
				
			||||||
 | 
					                pname=pname, name=self.name,
 | 
				
			||||||
 | 
					                worker_id=self.worker_id))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Setup signals
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGHUP, self._reload)
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGTERM, self._clean_exit)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def terminate(self):
 | 
				
			||||||
 | 
					        """Gracefully shutdown the service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This method will be executed when the Service have to
 | 
				
			||||||
 | 
					        shutdown cleanly.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        If not implemented the process will just end with status 0.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        To customize the exit code SystemExit exception can be used.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        raise ServiceMethodNotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def reload(self):
 | 
				
			||||||
 | 
					        """Reloading of the service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This method will be executed when the Service receive a SIGHUP.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        If not implemented the process will just end with status 0 and
 | 
				
			||||||
 | 
					        :py:class:`ServiceRunner` will start a new fresh process for this
 | 
				
			||||||
 | 
					        service with the same worker_id.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        raise ServiceMethodNotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def run(self):
 | 
				
			||||||
 | 
					        """Method representing the service activity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        If not implemented the process will just wait to receive an ending
 | 
				
			||||||
 | 
					        signal.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        raise ServiceMethodNotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _run(self):
 | 
				
			||||||
 | 
					        LOG.debug("Run service %s" % self._title)
 | 
				
			||||||
 | 
					        with _catch_exception_and_exit():
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                self.run()
 | 
				
			||||||
 | 
					            except ServiceMethodNotImplementedError:
 | 
				
			||||||
 | 
					                pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _reload(self, sig, frame):
 | 
				
			||||||
 | 
					        with _catch_exception_and_exit():
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                self.reload()
 | 
				
			||||||
 | 
					            except ServiceMethodNotImplementedError:
 | 
				
			||||||
 | 
					                try:
 | 
				
			||||||
 | 
					                    self.terminate()
 | 
				
			||||||
 | 
					                except ServiceMethodNotImplementedError:
 | 
				
			||||||
 | 
					                    pass
 | 
				
			||||||
 | 
					        os._exit(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _clean_exit(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        LOG.info('Caught SIGTERM signal, '
 | 
				
			||||||
 | 
					                 'graceful exiting of service %s' % self._title)
 | 
				
			||||||
 | 
					        with _catch_exception_and_exit():
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                self.terminate()
 | 
				
			||||||
 | 
					            except ServiceMethodNotImplementedError:
 | 
				
			||||||
 | 
					                pass
 | 
				
			||||||
 | 
					        os._exit(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ServiceManager(object):
 | 
				
			||||||
 | 
					    """Manage lifetimes of services
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :py:class:`ServiceManager` acts as a master process that controls the
 | 
				
			||||||
 | 
					    lifetime of children processes and restart them if they die unexpectedly.
 | 
				
			||||||
 | 
					    It also propagate some signals (SIGTERM, SIGALRM, SIGINT and SIGHUP) to
 | 
				
			||||||
 | 
					    them.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Each child process runs an instance of a :py:class:`Service`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    An application must create only one :py:class:ServiceManager` class
 | 
				
			||||||
 | 
					    and use :py:meth:ServiceManager.run()` as main loop of the application.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Usage::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class MyService(Service):
 | 
				
			||||||
 | 
					            def __init__(self):
 | 
				
			||||||
 | 
					                preparing_my_job()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            def run(self):
 | 
				
			||||||
 | 
					                do_my_job()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            def terminate(self):
 | 
				
			||||||
 | 
					                gracefully_stop_my_job()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            def reload(self):
 | 
				
			||||||
 | 
					                restart_my_job()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sr = ServiceManager()
 | 
				
			||||||
 | 
					        sr.add("my_service", MyService, 5)
 | 
				
			||||||
 | 
					        sr.run()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This will create 5 children processes running the service MyService.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _marker = object()
 | 
				
			||||||
 | 
					    _process_runner_already_created = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, wait_interval=0.01):
 | 
				
			||||||
 | 
					        """Creates the ServiceManager object
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param wait_interval: time between each new process spawn
 | 
				
			||||||
 | 
					        :type wait_interval: float
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self._process_runner_already_created:
 | 
				
			||||||
 | 
					            raise RuntimeError("Only one instance of ProcessRunner per "
 | 
				
			||||||
 | 
					                               "application is allowed")
 | 
				
			||||||
 | 
					        ServiceManager._process_runner_already_created = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._wait_interval = wait_interval
 | 
				
			||||||
 | 
					        self._shutdown = threading.Event()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._running_services = collections.defaultdict(dict)
 | 
				
			||||||
 | 
					        self._services = []
 | 
				
			||||||
 | 
					        self._forktimes = []
 | 
				
			||||||
 | 
					        self._current_process = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.readpipe, self.writepipe = os.pipe()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGTERM, self._clean_exit)
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGINT, self._fast_exit)
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGALRM, self._alarm_exit)
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGHUP, self._reload_services)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def add(self, service, workers=1):
 | 
				
			||||||
 | 
					        """Add a new service to the ServiceManager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param service: callable that return an instance of :py:class:`Service`
 | 
				
			||||||
 | 
					        :type service: callable
 | 
				
			||||||
 | 
					        :param workers: number of processes/workers for this service
 | 
				
			||||||
 | 
					        :type workers: int
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self._services.append(_ServiceConfig(service, workers))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def run(self):
 | 
				
			||||||
 | 
					        """Start and supervise services
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This method will start and supervise all children processes
 | 
				
			||||||
 | 
					        until the master process asked to shutdown by a SIGTERM.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        All spawned processes are part of the same unix process group.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while not self._shutdown.is_set():
 | 
				
			||||||
 | 
					            info = self._wait_service()
 | 
				
			||||||
 | 
					            if info is not None:
 | 
				
			||||||
 | 
					                # Restart this particular service
 | 
				
			||||||
 | 
					                conf, worker_id = info
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                for conf in self._services:
 | 
				
			||||||
 | 
					                    if len(self._running_services[conf]) < conf.workers:
 | 
				
			||||||
 | 
					                        worker_id = len(self._running_services[conf]) + 1
 | 
				
			||||||
 | 
					                        break
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    time.sleep(self._wait_interval)
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            pid = self._start_service(conf, worker_id)
 | 
				
			||||||
 | 
					            self._running_services[conf][pid] = worker_id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LOG.debug("Killing services with signal SIGTERM")
 | 
				
			||||||
 | 
					        os.killpg(0, signal.SIGTERM)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LOG.debug("Waiting services to terminate")
 | 
				
			||||||
 | 
					        os.waitpid(-1, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LOG.debug("Shutdown finish")
 | 
				
			||||||
 | 
					        os._exit(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _wait_service(self):
 | 
				
			||||||
 | 
					        """Return the last died service or None"""
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            # Don't block if no child processes have exited
 | 
				
			||||||
 | 
					            pid, status = os.waitpid(0, os.WNOHANG)
 | 
				
			||||||
 | 
					            if not pid:
 | 
				
			||||||
 | 
					                return None
 | 
				
			||||||
 | 
					        except OSError as exc:
 | 
				
			||||||
 | 
					            if exc.errno not in (errno.EINTR, errno.ECHILD):
 | 
				
			||||||
 | 
					                raise
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if os.WIFSIGNALED(status):
 | 
				
			||||||
 | 
					            sig = SIGNAL_TO_NAME.get(os.WTERMSIG(status))
 | 
				
			||||||
 | 
					            LOG.info('Child %(pid)d killed by signal %(sig)s',
 | 
				
			||||||
 | 
					                     dict(pid=pid, sig=sig))
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            code = os.WEXITSTATUS(status)
 | 
				
			||||||
 | 
					            LOG.info('Child %(pid)d exited with status %(code)d',
 | 
				
			||||||
 | 
					                     dict(pid=pid, code=code))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for conf in self._running_services:
 | 
				
			||||||
 | 
					            if pid in self._running_services[conf]:
 | 
				
			||||||
 | 
					                return conf, self._running_services[conf].pop(pid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LOG.warning('pid %d not in service list', pid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _reload_services(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        if self._shutdown.is_set():
 | 
				
			||||||
 | 
					            # NOTE(sileht): We are in shutdown process no need
 | 
				
			||||||
 | 
					            # to reload anything
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Reset forktimes to respawn services quickly
 | 
				
			||||||
 | 
					        self._forktimes = []
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGHUP, signal.SIG_IGN)
 | 
				
			||||||
 | 
					        os.killpg(0, signal.SIGHUP)
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGHUP, self._reload_services)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _clean_exit(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        # Don't need to be called more.
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGTERM, signal.SIG_IGN)
 | 
				
			||||||
 | 
					        LOG.info('Caught SIGTERM signal, graceful exiting of master process')
 | 
				
			||||||
 | 
					        self._shutdown.set()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _fast_exit(self, signo, frame):
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGINT, signal.SIG_IGN)
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGALRM, signal.SIG_IGN)
 | 
				
			||||||
 | 
					        LOG.info('Caught SIGINT signal, instantaneous exiting of '
 | 
				
			||||||
 | 
					                 'master process')
 | 
				
			||||||
 | 
					        os.killpg(0, signal.SIGINT)
 | 
				
			||||||
 | 
					        os._exit(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _alarm_exit(self, signo, frame):
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGINT, signal.SIG_IGN)
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGALRM, signal.SIG_IGN)
 | 
				
			||||||
 | 
					        LOG.info('Graceful shutdown timeout exceeded, '
 | 
				
			||||||
 | 
					                 'instantaneous exiting of master process')
 | 
				
			||||||
 | 
					        os.killpg(0, signal.SIGINT)
 | 
				
			||||||
 | 
					        os._exit(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _slowdown_respawn_if_needed(self):
 | 
				
			||||||
 | 
					        # Limit ourselves to one process a second (over the period of
 | 
				
			||||||
 | 
					        # number of workers * 1 second). This will allow workers to
 | 
				
			||||||
 | 
					        # start up quickly but ensure we don't fork off children that
 | 
				
			||||||
 | 
					        # die instantly too quickly.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expected_children = sum(s.workers for s in self._services)
 | 
				
			||||||
 | 
					        if len(self._forktimes) > expected_children:
 | 
				
			||||||
 | 
					            if time.time() - self._forktimes[0] < expected_children:
 | 
				
			||||||
 | 
					                LOG.info('Forking too fast, sleeping')
 | 
				
			||||||
 | 
					                time.sleep(1)
 | 
				
			||||||
 | 
					                self._forktimes.pop(0)
 | 
				
			||||||
 | 
					                self._forktimes.append(time.time())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _start_service(self, config, worker_id):
 | 
				
			||||||
 | 
					        self._slowdown_respawn_if_needed()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        pid = os.fork()
 | 
				
			||||||
 | 
					        if pid != 0:
 | 
				
			||||||
 | 
					            return pid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # reset parent signals
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGINT, signal.SIG_DFL)
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGALRM, signal.SIG_DFL)
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGTERM, signal.SIG_DFL)
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGHUP, signal.SIG_DFL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Close write to ensure only parent has it open
 | 
				
			||||||
 | 
					        os.close(self.writepipe)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Reseed random number generator
 | 
				
			||||||
 | 
					        random.seed()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Create and run a new service
 | 
				
			||||||
 | 
					        with _catch_exception_and_exit():
 | 
				
			||||||
 | 
					            p = config.service(worker_id)
 | 
				
			||||||
 | 
					            t = threading.Thread(target=p._run)
 | 
				
			||||||
 | 
					            t.daemon = True
 | 
				
			||||||
 | 
					            t.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # This will block until the write end is closed when the parent
 | 
				
			||||||
 | 
					        # dies unexpectedly
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            os.read(self.readpipe, 1)
 | 
				
			||||||
 | 
					        except EnvironmentError:
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LOG.info('Parent process has died unexpectedly, exiting')
 | 
				
			||||||
 | 
					        p._clean_exit()
 | 
				
			||||||
							
								
								
									
										0
									
								
								cotyledon/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								cotyledon/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										23
									
								
								cotyledon/tests/base.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								cotyledon/tests/base.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Copyright 2010-2011 OpenStack Foundation
 | 
				
			||||||
 | 
					# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
				
			||||||
 | 
					# not use this file except in compliance with the License. You may obtain
 | 
				
			||||||
 | 
					# a copy of the License at
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#      http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
				
			||||||
 | 
					# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
				
			||||||
 | 
					# License for the specific language governing permissions and limitations
 | 
				
			||||||
 | 
					# under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from oslotest import base
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestCase(base.BaseTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """Test case base class for all unit tests."""
 | 
				
			||||||
							
								
								
									
										30
									
								
								cotyledon/tests/examples.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								cotyledon/tests/examples.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					# Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
				
			||||||
 | 
					# not use this file except in compliance with the License. You may obtain
 | 
				
			||||||
 | 
					# a copy of the License at
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#      http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
				
			||||||
 | 
					# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
				
			||||||
 | 
					# License for the specific language governing permissions and limitations
 | 
				
			||||||
 | 
					# under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import cotyledon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOG = logging.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestService(cotyledon.Service):
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def example_app():
 | 
				
			||||||
 | 
					    logging.basicConfig(level=logging.DEBUG)
 | 
				
			||||||
 | 
					    p = cotyledon.ServiceManager()
 | 
				
			||||||
 | 
					    p.add_service("processing", TestService, 4)
 | 
				
			||||||
 | 
					    p.add_service("reporting", TestService, 1)
 | 
				
			||||||
 | 
					    p.run()
 | 
				
			||||||
							
								
								
									
										28
									
								
								cotyledon/tests/test_cotyledon.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								cotyledon/tests/test_cotyledon.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					test_cotyledon
 | 
				
			||||||
 | 
					----------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Tests for `cotyledon` module.
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from cotyledon.tests import base
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestCotyledon(base.TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_something(self):
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
							
								
								
									
										11
									
								
								doc/source/api.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								doc/source/api.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					========
 | 
				
			||||||
 | 
					API
 | 
				
			||||||
 | 
					========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. autoclass:: cotyledon.Service
 | 
				
			||||||
 | 
					   :members:
 | 
				
			||||||
 | 
					   :special-members: __init__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. autoclass:: cotyledon.ServiceManager
 | 
				
			||||||
 | 
					   :members:
 | 
				
			||||||
 | 
					   :special-members: __init__
 | 
				
			||||||
							
								
								
									
										75
									
								
								doc/source/conf.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										75
									
								
								doc/source/conf.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,75 @@
 | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					# Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					# you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					# You may obtain a copy of the License at
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					# distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 | 
				
			||||||
 | 
					# implied.
 | 
				
			||||||
 | 
					# See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					# limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sys.path.insert(0, os.path.abspath('../..'))
 | 
				
			||||||
 | 
					# -- General configuration ----------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Add any Sphinx extension module names here, as strings. They can be
 | 
				
			||||||
 | 
					# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
 | 
				
			||||||
 | 
					extensions = [
 | 
				
			||||||
 | 
					    'sphinx.ext.autodoc',
 | 
				
			||||||
 | 
					    #'sphinx.ext.intersphinx',
 | 
				
			||||||
 | 
					    'oslosphinx'
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# autodoc generation is a bit aggressive and a nuisance when doing heavy
 | 
				
			||||||
 | 
					# text edit cycles.
 | 
				
			||||||
 | 
					# execute "export SPHINX_DEBUG=1" in your terminal to disable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# The suffix of source filenames.
 | 
				
			||||||
 | 
					source_suffix = '.rst'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# The master toctree document.
 | 
				
			||||||
 | 
					master_doc = 'index'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# General information about the project.
 | 
				
			||||||
 | 
					project = u'cotyledon'
 | 
				
			||||||
 | 
					copyright = u'2013, OpenStack Foundation'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# If true, '()' will be appended to :func: etc. cross-reference text.
 | 
				
			||||||
 | 
					add_function_parentheses = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# If true, the current module name will be prepended to all description
 | 
				
			||||||
 | 
					# unit titles (such as .. function::).
 | 
				
			||||||
 | 
					add_module_names = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# The name of the Pygments (syntax highlighting) style to use.
 | 
				
			||||||
 | 
					pygments_style = 'sphinx'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# -- Options for HTML output --------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# The theme to use for HTML and HTML Help pages.  Major themes that come with
 | 
				
			||||||
 | 
					# Sphinx are currently 'default' and 'sphinxdoc'.
 | 
				
			||||||
 | 
					# html_theme_path = ["."]
 | 
				
			||||||
 | 
					# html_theme = '_theme'
 | 
				
			||||||
 | 
					# html_static_path = ['static']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Output file base name for HTML help builder.
 | 
				
			||||||
 | 
					htmlhelp_basename = '%sdoc' % project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Grouping the document tree into LaTeX files. List of tuples
 | 
				
			||||||
 | 
					# (source start file, target name, title, author, documentclass
 | 
				
			||||||
 | 
					# [howto/manual]).
 | 
				
			||||||
 | 
					latex_documents = [
 | 
				
			||||||
 | 
					    ('index',
 | 
				
			||||||
 | 
					     '%s.tex' % project,
 | 
				
			||||||
 | 
					     u'%s Documentation' % project,
 | 
				
			||||||
 | 
					     u'OpenStack Foundation', 'manual'),
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Example configuration for intersphinx: refer to the Python standard library.
 | 
				
			||||||
 | 
					#intersphinx_mapping = {'http://docs.python.org/': None}
 | 
				
			||||||
							
								
								
									
										4
									
								
								doc/source/contributing.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								doc/source/contributing.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					============
 | 
				
			||||||
 | 
					Contributing
 | 
				
			||||||
 | 
					============
 | 
				
			||||||
 | 
					.. include:: ../../CONTRIBUTING.rst
 | 
				
			||||||
							
								
								
									
										25
									
								
								doc/source/index.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								doc/source/index.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					.. cotyledon documentation master file, created by
 | 
				
			||||||
 | 
					   sphinx-quickstart on Tue Jul  9 22:26:36 2013.
 | 
				
			||||||
 | 
					   You can adapt this file completely to your liking, but it should at least
 | 
				
			||||||
 | 
					   contain the root `toctree` directive.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Welcome to cotyledon's documentation!
 | 
				
			||||||
 | 
					========================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Contents:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. toctree::
 | 
				
			||||||
 | 
					   :maxdepth: 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   readme
 | 
				
			||||||
 | 
					   installation
 | 
				
			||||||
 | 
					   api
 | 
				
			||||||
 | 
					   contributing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Indices and tables
 | 
				
			||||||
 | 
					==================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* :ref:`genindex`
 | 
				
			||||||
 | 
					* :ref:`modindex`
 | 
				
			||||||
 | 
					* :ref:`search`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										12
									
								
								doc/source/installation.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								doc/source/installation.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					============
 | 
				
			||||||
 | 
					Installation
 | 
				
			||||||
 | 
					============
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					At the command line::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $ pip install cotyledon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Or, if you have virtualenvwrapper installed::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $ mkvirtualenv cotyledon
 | 
				
			||||||
 | 
					    $ pip install cotyledon
 | 
				
			||||||
							
								
								
									
										1
									
								
								doc/source/readme.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								doc/source/readme.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					.. include:: ../../README.rst
 | 
				
			||||||
							
								
								
									
										34
									
								
								release.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										34
									
								
								release.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					#!/bin/bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					set -e
 | 
				
			||||||
 | 
					set -x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					version=$(python setup.py --version | sed 's/\.dev.*//')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					status=$(git status -sz)
 | 
				
			||||||
 | 
					[ -z "$status" ] || false
 | 
				
			||||||
 | 
					git checkout master
 | 
				
			||||||
 | 
					git push 
 | 
				
			||||||
 | 
					git tag -s $version -m "Release version ${version}"
 | 
				
			||||||
 | 
					git checkout $version
 | 
				
			||||||
 | 
					git clean -fdx
 | 
				
			||||||
 | 
					python setup.py --version
 | 
				
			||||||
 | 
					python setup.py sdist
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					set +x
 | 
				
			||||||
 | 
					echo
 | 
				
			||||||
 | 
					echo "release: Cotyledon ${version}"
 | 
				
			||||||
 | 
					echo
 | 
				
			||||||
 | 
					echo "SHA1sum: "
 | 
				
			||||||
 | 
					sha1sum dist/*
 | 
				
			||||||
 | 
					echo "MD5sum: "
 | 
				
			||||||
 | 
					md5sum dist/*
 | 
				
			||||||
 | 
					echo
 | 
				
			||||||
 | 
					echo "uploading..."
 | 
				
			||||||
 | 
					echo
 | 
				
			||||||
 | 
					set -x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					read
 | 
				
			||||||
 | 
					git push --tags
 | 
				
			||||||
 | 
					twine upload -r pypi -s dist/cotyledo-${version}.tar.gz dist/cotyledo-${version}-py2.py3-none-any.whl
 | 
				
			||||||
 | 
					git checkout master
 | 
				
			||||||
							
								
								
									
										6
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					# The order of packages is significant, because pip processes them in the order
 | 
				
			||||||
 | 
					# of appearance. Changing the order has an impact on the overall integration
 | 
				
			||||||
 | 
					# process, which may cause wedges in the gate later.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pbr>=1.6
 | 
				
			||||||
 | 
					setproctitle
 | 
				
			||||||
							
								
								
									
										50
									
								
								setup.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								setup.cfg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					[metadata]
 | 
				
			||||||
 | 
					name = cotyledon
 | 
				
			||||||
 | 
					summary = Cotyledon provides a framework for defining long-running services.
 | 
				
			||||||
 | 
					description-file =
 | 
				
			||||||
 | 
					    README.rst
 | 
				
			||||||
 | 
					author = OpenStack
 | 
				
			||||||
 | 
					author-email = openstack-dev@lists.openstack.org
 | 
				
			||||||
 | 
					home-page = http://www.openstack.org/
 | 
				
			||||||
 | 
					classifier =
 | 
				
			||||||
 | 
					    Environment :: OpenStack
 | 
				
			||||||
 | 
					    Intended Audience :: Information Technology
 | 
				
			||||||
 | 
					    Intended Audience :: System Administrators
 | 
				
			||||||
 | 
					    License :: OSI Approved :: Apache Software License
 | 
				
			||||||
 | 
					    Operating System :: POSIX :: Linux
 | 
				
			||||||
 | 
					    Programming Language :: Python
 | 
				
			||||||
 | 
					    Programming Language :: Python :: 2
 | 
				
			||||||
 | 
					    Programming Language :: Python :: 2.7
 | 
				
			||||||
 | 
					    Programming Language :: Python :: 3
 | 
				
			||||||
 | 
					    Programming Language :: Python :: 3.3
 | 
				
			||||||
 | 
					    Programming Language :: Python :: 3.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[files]
 | 
				
			||||||
 | 
					packages =
 | 
				
			||||||
 | 
					    cotyledon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[entry_points]
 | 
				
			||||||
 | 
					console_scripts =
 | 
				
			||||||
 | 
					    cotyledon-example = cotyledon.tests.examples:example_app
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[build_sphinx]
 | 
				
			||||||
 | 
					source-dir = doc/source
 | 
				
			||||||
 | 
					build-dir = doc/build
 | 
				
			||||||
 | 
					all_files = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[upload_sphinx]
 | 
				
			||||||
 | 
					upload-dir = doc/build/html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[compile_catalog]
 | 
				
			||||||
 | 
					directory = cotyledon/locale
 | 
				
			||||||
 | 
					domain = cotyledon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[update_catalog]
 | 
				
			||||||
 | 
					domain = cotyledon
 | 
				
			||||||
 | 
					output_dir = cotyledon/locale
 | 
				
			||||||
 | 
					input_file = cotyledon/locale/cotyledon.pot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[extract_messages]
 | 
				
			||||||
 | 
					keywords = _ gettext ngettext l_ lazy_gettext
 | 
				
			||||||
 | 
					mapping_file = babel.cfg
 | 
				
			||||||
 | 
					output_file = cotyledon/locale/cotyledon.pot
 | 
				
			||||||
							
								
								
									
										29
									
								
								setup.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								setup.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
 | 
				
			||||||
 | 
					import setuptools
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# In python < 2.7.4, a lazy loading of package `pbr` will break
 | 
				
			||||||
 | 
					# setuptools if some other modules registered functions in `atexit`.
 | 
				
			||||||
 | 
					# solution from: http://bugs.python.org/issue15881#msg170215
 | 
				
			||||||
 | 
					try:
 | 
				
			||||||
 | 
					    import multiprocessing  # noqa
 | 
				
			||||||
 | 
					except ImportError:
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					setuptools.setup(
 | 
				
			||||||
 | 
					    setup_requires=['pbr'],
 | 
				
			||||||
 | 
					    pbr=True)
 | 
				
			||||||
							
								
								
									
										14
									
								
								test-requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								test-requirements.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					# The order of packages is significant, because pip processes them in the order
 | 
				
			||||||
 | 
					# of appearance. Changing the order has an impact on the overall integration
 | 
				
			||||||
 | 
					# process, which may cause wedges in the gate later.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					hacking<0.11,>=0.10.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					coverage>=3.6
 | 
				
			||||||
 | 
					python-subunit>=0.0.18
 | 
				
			||||||
 | 
					sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2
 | 
				
			||||||
 | 
					oslosphinx>=2.5.0 # Apache-2.0
 | 
				
			||||||
 | 
					oslotest>=1.10.0 # Apache-2.0
 | 
				
			||||||
 | 
					testrepository>=0.0.18
 | 
				
			||||||
 | 
					testscenarios>=0.4
 | 
				
			||||||
 | 
					testtools>=1.4.0
 | 
				
			||||||
							
								
								
									
										60
									
								
								tox.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								tox.ini
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					[tox]
 | 
				
			||||||
 | 
					minversion = 2.0
 | 
				
			||||||
 | 
					envlist = py34-constraints,py27-constraints,pypy-constraints,pep8-constraints
 | 
				
			||||||
 | 
					skipsdist = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[testenv]
 | 
				
			||||||
 | 
					usedevelop = True
 | 
				
			||||||
 | 
					install_command =
 | 
				
			||||||
 | 
					                  constraints: {[testenv:common-constraints]install_command}
 | 
				
			||||||
 | 
					                  pip install -U {opts} {packages}
 | 
				
			||||||
 | 
					setenv =
 | 
				
			||||||
 | 
					   VIRTUAL_ENV={envdir}
 | 
				
			||||||
 | 
					deps = -r{toxinidir}/test-requirements.txt
 | 
				
			||||||
 | 
					commands = python setup.py test --slowest --testr-args='{posargs}'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[testenv:common-constraints]
 | 
				
			||||||
 | 
					install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[testenv:pep8]
 | 
				
			||||||
 | 
					commands = flake8 {posargs}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[testenv:pep8-constraints]
 | 
				
			||||||
 | 
					install_command = {[testenv:common-constraints]install_command}
 | 
				
			||||||
 | 
					commands = flake8 {posargs}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[testenv:venv]
 | 
				
			||||||
 | 
					commands = {posargs}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[testenv:venv-constraints]
 | 
				
			||||||
 | 
					install_command = {[testenv:common-constraints]install_command}
 | 
				
			||||||
 | 
					commands = {posargs}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[testenv:cover]
 | 
				
			||||||
 | 
					commands = python setup.py test --coverage --testr-args='{posargs}'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[testenv:cover-constraints]
 | 
				
			||||||
 | 
					install_command = {[testenv:common-constraints]install_command}
 | 
				
			||||||
 | 
					commands = python setup.py test --coverage --testr-args='{posargs}'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[testenv:docs]
 | 
				
			||||||
 | 
					commands = python setup.py build_sphinx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[testenv:docs-constraints]
 | 
				
			||||||
 | 
					install_command = {[testenv:common-constraints]install_command}
 | 
				
			||||||
 | 
					commands = python setup.py build_sphinx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[testenv:debug]
 | 
				
			||||||
 | 
					commands = oslo_debug_helper {posargs}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[testenv:debug-constraints]
 | 
				
			||||||
 | 
					install_command = {[testenv:common-constraints]install_command}
 | 
				
			||||||
 | 
					commands = oslo_debug_helper {posargs}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[flake8]
 | 
				
			||||||
 | 
					# E123, E125 skipped as they are invalid PEP-8.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					show-source = True
 | 
				
			||||||
 | 
					ignore = E123,E125
 | 
				
			||||||
 | 
					builtins = _
 | 
				
			||||||
 | 
					exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build
 | 
				
			||||||
		Reference in New Issue
	
	Block a user