Retire dox repository
This change removes all content from the repository and adds the usual README file to point out that the repository is retired following the process from https://docs.openstack.org/infra/manual/drivers.html#retiring-a-project Depends-On: https://review.opendev.org/701446 Change-Id: I49ddca0f6c4a5d0e546e22a5515d405f0e869eab
This commit is contained in:
parent
3087d31254
commit
06e67f317d
@ -1,7 +0,0 @@
|
|||||||
[run]
|
|
||||||
branch = True
|
|
||||||
source = dox
|
|
||||||
omit = dox/tests/*,dox/openstack/*
|
|
||||||
|
|
||||||
[report]
|
|
||||||
ignore_errors = True
|
|
55
.gitignore
vendored
55
.gitignore
vendored
@ -1,55 +0,0 @@
|
|||||||
*.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
|
|
||||||
.coverage
|
|
||||||
.tox
|
|
||||||
nosetests.xml
|
|
||||||
.testrepository
|
|
||||||
cover
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# Autogenerated by ourseleves when testing the dox.yml
|
|
||||||
.dox
|
|
3
.mailmap
3
.mailmap
@ -1,3 +0,0 @@
|
|||||||
# Format is:
|
|
||||||
# <preferred e-mail> <other e-mail 1>
|
|
||||||
# <preferred e-mail> <other e-mail 2>
|
|
@ -1,7 +0,0 @@
|
|||||||
[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
|
|
@ -1,16 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
||||||
Once those steps have been completed, changes to OpenStack
|
|
||||||
should be submitted for review via the Gerrit tool, following
|
|
||||||
the workflow documented at:
|
|
||||||
|
|
||||||
http://docs.openstack.org/infra/manual/developers.html#development-workflow
|
|
||||||
|
|
||||||
Pull requests submitted through GitHub will be ignored.
|
|
||||||
|
|
||||||
Bugs should be filed on Storyboard, not GitHub:
|
|
||||||
|
|
||||||
https://storyboard.openstack.org
|
|
@ -1,4 +0,0 @@
|
|||||||
dox Style Commandments
|
|
||||||
===============================================
|
|
||||||
|
|
||||||
Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/
|
|
175
LICENSE
175
LICENSE
@ -1,175 +0,0 @@
|
|||||||
|
|
||||||
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.
|
|
@ -1,6 +0,0 @@
|
|||||||
include AUTHORS
|
|
||||||
include ChangeLog
|
|
||||||
exclude .gitignore
|
|
||||||
exclude .gitreview
|
|
||||||
|
|
||||||
global-exclude *.pyc
|
|
155
README.rst
155
README.rst
@ -1,149 +1,10 @@
|
|||||||
===
|
This project is no longer maintained.
|
||||||
dox
|
|
||||||
===
|
|
||||||
|
|
||||||
dox is a tool for using docker containers to run local tests, inspired by
|
The contents of this repository are still available in the Git
|
||||||
tox and virtualenv for python. There are two elements to its configuration:
|
source code management system. To see the contents of this
|
||||||
|
repository before it reached its end of life, please check out the
|
||||||
|
previous commit with "git checkout HEAD^1".
|
||||||
|
|
||||||
* What commands should be run?
|
For any further questions, please email
|
||||||
|
openstack-discuss@lists.openstack.org or join #openstack-dev on
|
||||||
* In what image should they be run?
|
Freenode.
|
||||||
|
|
||||||
If there is a dox.yml file, you're set. You want to specify what image to
|
|
||||||
use and what commands to run. You win::
|
|
||||||
|
|
||||||
image: ubuntu:trusty
|
|
||||||
commands: |
|
|
||||||
pip install . -r test-requirements.txt
|
|
||||||
python setup.py test
|
|
||||||
|
|
||||||
You might either not be willing to commit to dox as a way of life yet, or you
|
|
||||||
may want to use dox in a project that similarly has not done so.
|
|
||||||
|
|
||||||
What commands should be run
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
dox.yml wins.
|
|
||||||
|
|
||||||
If there is a tox.ini file, the commands specified in the base [testenv]
|
|
||||||
will be used.
|
|
||||||
|
|
||||||
If there is a .travis.yml file, the script section will be used.
|
|
||||||
|
|
||||||
If there are none of those things, dox will do its best to infer what
|
|
||||||
should be done. Examining the directory can often provide hints if you
|
|
||||||
haven't been too clever. For instance, if you have a Gruntfile, you probably
|
|
||||||
want to run grunt. If you have a Makefile, then make && make test is probably
|
|
||||||
your bag. If you have a Makefile.am, you probably want to run autotools first.
|
|
||||||
If you have a setup.py file, python setup.py test is a likely choice (although
|
|
||||||
in that case, you probably haven't done it right because setuptools support
|
|
||||||
for this is quite awful.)
|
|
||||||
|
|
||||||
After all of that, if we still can't figure out what you want - it's probably
|
|
||||||
easiest to just edit a file called dox.yml and put in a section telling us
|
|
||||||
what to do.
|
|
||||||
|
|
||||||
In what image should they be run
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
Again, dox.yml wins, and thanks for making things easy!
|
|
||||||
|
|
||||||
If there is a tox.ini file, and it contains a [docker] section, the value in
|
|
||||||
"image" will be used::
|
|
||||||
|
|
||||||
[docker]
|
|
||||||
image=ubuntu:trusty
|
|
||||||
|
|
||||||
If there is not an image key in the docker section or an image key in the
|
|
||||||
dox.yml but there is a Dockerfile in the repo, an new image will be built
|
|
||||||
using the Dockerfile and the test commands will be run inside of the image.
|
|
||||||
|
|
||||||
If all of that fails, tests are going to run in a bare ubuntu image. Good luck!
|
|
||||||
|
|
||||||
Image Caching
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Every run actually has two images associated with it: The `base` image and
|
|
||||||
the `test` image.
|
|
||||||
|
|
||||||
The image referenced above is the `base` image. When you execute `dox`,
|
|
||||||
it will search docker for the named image. If it exists, it will do nothing.
|
|
||||||
If it does not, it will either pull it, or, it will build it from the
|
|
||||||
Dockerfile. In the case of building from a Dockerfile, dox will make an image
|
|
||||||
named for the source directory. So if you're in ~/src/openstack/nova, it'll
|
|
||||||
make an image called "dox/nova/base".
|
|
||||||
|
|
||||||
The `test` image is an image made by dox for the test run. Similar to the
|
|
||||||
base run, it'll have a generated name, such as "dox/nova/test", and similar
|
|
||||||
to the base image, it will get generated once and then left alone.
|
|
||||||
|
|
||||||
If you want to regenerate the `test` image, you can run dox with the `-r`
|
|
||||||
option. If you want to regenerate/repull everything, you can run it with the
|
|
||||||
`--rebuild-all` option.
|
|
||||||
|
|
||||||
The reasoning behind this is that the base image should really be the
|
|
||||||
substrata which doesn't have a lot to do with the repo itself ... it shouldn't
|
|
||||||
really expect to change much based on day to day changes in the repo. The
|
|
||||||
test image on the other hand is built a bit more based on the repo itself.
|
|
||||||
So, for instance, in the base image you might want to do things like::
|
|
||||||
|
|
||||||
apt-get install libxml-dev
|
|
||||||
|
|
||||||
and in the test image, you'd want things like::
|
|
||||||
|
|
||||||
pip install -U requirements.txt
|
|
||||||
|
|
||||||
Neither change frequently, but the second it more likely to change day to day
|
|
||||||
than the first.
|
|
||||||
|
|
||||||
Additional information
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Regardless, dox will mount the current source dir as a volume at `/src` in
|
|
||||||
the container and will run commands in that context.
|
|
||||||
|
|
||||||
dox will attempt to reuse containers. Since the source is bind-mounted into
|
|
||||||
the container, things that might be expensive like copying source dirs or
|
|
||||||
re-installing the source into the system can be minimized.
|
|
||||||
|
|
||||||
Boot2Docker support
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
To get support for non Linux OSes that doesn't support natively docker
|
|
||||||
there is a tool called `boot2docker <http://boot2docker.io/>`_ which
|
|
||||||
allows you to run a remote docker server in a VirtualBox VM and the
|
|
||||||
client running on the non Linux desktop.
|
|
||||||
|
|
||||||
Volume support started to be supported by `boot2docker` since docker version
|
|
||||||
1.3. You will need to be at least on that version to have volume mount support.
|
|
||||||
|
|
||||||
If you username id is different from the docker username on the `boot2docker` vm,
|
|
||||||
you will need to specify it on the command line, like this::
|
|
||||||
|
|
||||||
dox --user-map=docker:1000:10
|
|
||||||
|
|
||||||
If you use VirtualBox guest additions to mount your osx onto the `boot2docker` vm,
|
|
||||||
example /Users/your_name/openstack/oslo-incubator as /home/your_name/openstack/oslo-incubator,
|
|
||||||
then you can add the following mapping::
|
|
||||||
|
|
||||||
dos --path-map=/Users/your_name:/home/your_name
|
|
||||||
|
|
||||||
|
|
||||||
Advanced
|
|
||||||
--------
|
|
||||||
It is possible to specify multiple images to be used in a dox run.
|
|
||||||
Images can be provided on the command line, via the dox.yml file, or the
|
|
||||||
tox.ini file.
|
|
||||||
|
|
||||||
For the command line, images should be provided via the --images option,
|
|
||||||
in a comma-separated list.
|
|
||||||
|
|
||||||
The tox.ini file should also use a comma-separated list.
|
|
||||||
|
|
||||||
The dox.yml file should list images in an array.
|
|
||||||
|
|
||||||
The same prep and command instructions will be executed on each image sequentially.
|
|
||||||
|
|
||||||
dox does not currently allow for multiple images executing different tasks
|
|
||||||
at this time. However, it is a goal to allow for such test scenarios in
|
|
||||||
the future.
|
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
# This is a cross-platform list tracking distribution packages needed by tests;
|
|
||||||
# see http://docs.openstack.org/infra/bindep/ for additional information.
|
|
||||||
|
|
||||||
docker.io [platform:dpkg test]
|
|
||||||
python-dev [platform:dpkg test]
|
|
||||||
python-devel [platform:rpm test]
|
|
@ -1,74 +0,0 @@
|
|||||||
# 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',
|
|
||||||
'oslo.sphinx'
|
|
||||||
]
|
|
||||||
|
|
||||||
# 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'dox'
|
|
||||||
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}
|
|
@ -1 +0,0 @@
|
|||||||
.. include:: ../../CONTRIBUTING.rst
|
|
@ -1,37 +0,0 @@
|
|||||||
.. dox 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 dox's documentation!
|
|
||||||
========================================================
|
|
||||||
|
|
||||||
Contents:
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
readme
|
|
||||||
installation
|
|
||||||
usage
|
|
||||||
contributing
|
|
||||||
|
|
||||||
|
|
||||||
Release Notes
|
|
||||||
==============
|
|
||||||
|
|
||||||
0.2.0
|
|
||||||
-----
|
|
||||||
|
|
||||||
dffe391 Don't prefix the cached image with `_`
|
|
||||||
ea141b9 Don't run useradd for root
|
|
||||||
d3971df Add Centos 7 Dockerfile
|
|
||||||
d0d15f9 More informative error when command fails
|
|
||||||
|
|
||||||
|
|
||||||
Indices and tables
|
|
||||||
==================
|
|
||||||
|
|
||||||
* :ref:`genindex`
|
|
||||||
* :ref:`modindex`
|
|
||||||
* :ref:`search`
|
|
@ -1,12 +0,0 @@
|
|||||||
============
|
|
||||||
Installation
|
|
||||||
============
|
|
||||||
|
|
||||||
At the command line::
|
|
||||||
|
|
||||||
$ pip install dox
|
|
||||||
|
|
||||||
Or, if you have virtualenvwrapper installed::
|
|
||||||
|
|
||||||
$ mkvirtualenv dox
|
|
||||||
$ pip install dox
|
|
@ -1 +0,0 @@
|
|||||||
.. include:: ../README.rst
|
|
@ -1,7 +0,0 @@
|
|||||||
========
|
|
||||||
Usage
|
|
||||||
========
|
|
||||||
|
|
||||||
To use dox in a project::
|
|
||||||
|
|
||||||
import dox
|
|
@ -1,9 +0,0 @@
|
|||||||
FROM centos:7
|
|
||||||
MAINTAINER OpenStack <openstack-dev@lists.openstack.org>
|
|
||||||
|
|
||||||
RUN yum -y update
|
|
||||||
RUN yum -y groupinstall 'Development Tools'
|
|
||||||
RUN yum -y install wget git python
|
|
||||||
|
|
||||||
RUN wget https://bootstrap.pypa.io/get-pip.py && python get-pip.py && rm get-pip.py
|
|
||||||
RUN pip install -U setuptools
|
|
@ -1,9 +0,0 @@
|
|||||||
FROM fedora:20
|
|
||||||
MAINTAINER OpenStack <openstack-dev@lists.openstack.org>
|
|
||||||
|
|
||||||
RUN yum -y update
|
|
||||||
RUN yum -y groupinstall 'Development Tools'
|
|
||||||
RUN yum -y install wget git python
|
|
||||||
|
|
||||||
RUN wget https://bootstrap.pypa.io/get-pip.py && python get-pip.py && rm get-pip.py
|
|
||||||
RUN pip install -U setuptools
|
|
@ -1,8 +0,0 @@
|
|||||||
FROM ubuntu:trusty
|
|
||||||
MAINTAINER OpenStack <openstack-dev@lists.openstack.org>
|
|
||||||
|
|
||||||
RUN apt-get update --fix-missing
|
|
||||||
RUN apt-get install -y build-essential wget git python python-dev
|
|
||||||
|
|
||||||
RUN wget https://bootstrap.pypa.io/get-pip.py && python get-pip.py && rm get-pip.py
|
|
||||||
RUN pip install -U setuptools
|
|
@ -1,8 +0,0 @@
|
|||||||
FROM ubuntu:xenial
|
|
||||||
MAINTAINER OpenStack <openstack-dev@lists.openstack.org>
|
|
||||||
|
|
||||||
RUN apt-get update --fix-missing
|
|
||||||
RUN apt-get install -y build-essential wget git python python-dev
|
|
||||||
|
|
||||||
RUN wget https://bootstrap.pypa.io/get-pip.py && python get-pip.py && rm get-pip.py
|
|
||||||
RUN pip install -U setuptools
|
|
19
dox.yml
19
dox.yml
@ -1,19 +0,0 @@
|
|||||||
testing:
|
|
||||||
images:
|
|
||||||
- infra/trusty
|
|
||||||
add:
|
|
||||||
- requirements.txt
|
|
||||||
- test-requirements.txt
|
|
||||||
prep:
|
|
||||||
- pip install -U -r requirements.txt -r test-requirements.txt
|
|
||||||
commands:
|
|
||||||
- export PYTHONHASHSEED=$RANDOM
|
|
||||||
- python setup.py testr --slowest
|
|
||||||
|
|
||||||
testing:pep8:
|
|
||||||
commands:
|
|
||||||
- flake8
|
|
||||||
|
|
||||||
testing:cover:
|
|
||||||
commands:
|
|
||||||
- /bin/echo "hello"
|
|
@ -1,16 +0,0 @@
|
|||||||
# 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 pbr.version
|
|
||||||
|
|
||||||
|
|
||||||
__version__ = pbr.version.VersionInfo('dox').version_string()
|
|
140
dox/cmd.py
140
dox/cmd.py
@ -1,140 +0,0 @@
|
|||||||
# Copyright (c) 2014 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.
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import functools
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import dox.commands
|
|
||||||
import dox.config.cmdline
|
|
||||||
import dox.images
|
|
||||||
import dox.runner
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def get_log_level(args):
|
|
||||||
if args.debug:
|
|
||||||
return logging.DEBUG
|
|
||||||
if args.quiet:
|
|
||||||
return logging.WARN
|
|
||||||
return logging.INFO
|
|
||||||
|
|
||||||
|
|
||||||
def setup_logging(level):
|
|
||||||
handler = logging.StreamHandler()
|
|
||||||
formatter = logging.Formatter('%(levelname)s|%(name)s|%(message)s')
|
|
||||||
handler.setFormatter(formatter)
|
|
||||||
logger = logging.getLogger('dox')
|
|
||||||
logger.setLevel(level)
|
|
||||||
logger.addHandler(handler)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
|
||||||
parser = argparse.ArgumentParser(description='Run tests in docker.')
|
|
||||||
parser.add_argument(dest='extra_args', nargs='*',
|
|
||||||
help='args to append to command, or command to run'
|
|
||||||
' if -c is given')
|
|
||||||
parser.add_argument('-i', '--images', dest='images',
|
|
||||||
help='Base images to use')
|
|
||||||
parser.add_argument('-e', '--environment', dest='environment',
|
|
||||||
default=None,
|
|
||||||
help='Run target environment.')
|
|
||||||
parser.add_argument('-c', '--command', dest='command', default=False,
|
|
||||||
action='store_true',
|
|
||||||
help='Treat arguments as the entire command to run')
|
|
||||||
parser.add_argument('-r', '--rebuild', dest='rebuild', default=False,
|
|
||||||
action='store_true',
|
|
||||||
help='Rebuild the test image')
|
|
||||||
parser.add_argument('--rebuild-all', dest='rebuild_all', default=False,
|
|
||||||
action='store_true', help='Rebuild all images')
|
|
||||||
parser.add_argument('-d', '--debug', dest='debug', default=False,
|
|
||||||
action='store_true', help='Debug mode')
|
|
||||||
parser.add_argument('-q', '--quiet', dest='quiet', default=False,
|
|
||||||
action='store_true', help='Quiet output')
|
|
||||||
parser.add_argument('-n', '--noop', dest='noop', default=False,
|
|
||||||
action='store_true',
|
|
||||||
help="Don't actually execute commands")
|
|
||||||
parser.add_argument('--user-map', default=os.environ.get("DOX_USER_MAP"),
|
|
||||||
help='User to run the container to '
|
|
||||||
'format is user:uid:gid, with boot2docker use '
|
|
||||||
'docker:1000:10 (default to your current user)')
|
|
||||||
parser.add_argument('--path-map', default=os.environ.get("DOX_PATH_MAP"),
|
|
||||||
help='with boot2docker, specify how osx path maps to '
|
|
||||||
'linux path. example --path-map /Users:/home, '
|
|
||||||
'means /Users is available as /home on docker '
|
|
||||||
'container')
|
|
||||||
parser.add_argument('-k', '--keep', dest='keep_image', default=False,
|
|
||||||
action='store_true',
|
|
||||||
help="Keep test container after command finishes")
|
|
||||||
return parser.parse_args()
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
args = parse_args()
|
|
||||||
setup_logging(get_log_level(args))
|
|
||||||
|
|
||||||
return runner(args)
|
|
||||||
|
|
||||||
|
|
||||||
def run_dox(args, images, command, image_name):
|
|
||||||
# Run
|
|
||||||
try:
|
|
||||||
run = functools.partial(dox.runner.Runner(args, image_name).run,
|
|
||||||
command=command)
|
|
||||||
map(run, images)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error("Operation failed, aborting dox.", exc_info=e)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
|
|
||||||
def runner(args):
|
|
||||||
options = {}
|
|
||||||
args_images = None
|
|
||||||
|
|
||||||
if not dox.runner.Runner(args).is_docker_installed():
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# Get Image
|
|
||||||
if args.images:
|
|
||||||
args_images = [x.strip() for x in args.images.split(',')]
|
|
||||||
|
|
||||||
if args.command:
|
|
||||||
command = dox.config.cmdline.CommandLine(args.extra_args)
|
|
||||||
logger.debug("Command source is the command line")
|
|
||||||
return run_dox(args, args_images, command, image_name="commandline")
|
|
||||||
|
|
||||||
if args.environment:
|
|
||||||
sections = args.environment.split(',')
|
|
||||||
else:
|
|
||||||
sections = ['_default']
|
|
||||||
|
|
||||||
for section in sections:
|
|
||||||
options['section'] = section
|
|
||||||
|
|
||||||
if args_images:
|
|
||||||
images = args_images
|
|
||||||
else:
|
|
||||||
images = dox.images.get_images(options)
|
|
||||||
|
|
||||||
command = dox.commands.Commands(args.extra_args, options)
|
|
||||||
logger.debug("Command source is %s, section %s" % (
|
|
||||||
command.source.source_name(), section))
|
|
||||||
|
|
||||||
# TODO(chmouel): add to section a proper image_name that include the
|
|
||||||
# type of backend i.e: dox_yaml/tox_init
|
|
||||||
run_dox(args, images, command, image_name=section)
|
|
@ -1,96 +0,0 @@
|
|||||||
# Copyright (c) 2014 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.
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'get_commands',
|
|
||||||
'Commands',
|
|
||||||
]
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
|
|
||||||
import dox.config.dox_yaml
|
|
||||||
import dox.config.tox_ini
|
|
||||||
import dox.config.travis_yaml
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def get_commands(options):
|
|
||||||
'''Examine the local environment and figure out what we should run.'''
|
|
||||||
|
|
||||||
dox_yaml = dox.config.dox_yaml.get_dox_yaml(options)
|
|
||||||
tox_ini = dox.config.tox_ini.get_tox_ini(options)
|
|
||||||
travis_yaml = dox.config.travis_yaml.get_travis_yaml(options)
|
|
||||||
|
|
||||||
for source in (dox_yaml, tox_ini, travis_yaml):
|
|
||||||
if source.exists():
|
|
||||||
return source
|
|
||||||
raise Exception("dox cannot figure out what command to run")
|
|
||||||
|
|
||||||
|
|
||||||
class Commands(object):
|
|
||||||
|
|
||||||
def __init__(self, extra_args=[], options=None):
|
|
||||||
self.options = options or {}
|
|
||||||
self.source = get_commands(options)
|
|
||||||
self.args = []
|
|
||||||
self.extra_args = extra_args
|
|
||||||
|
|
||||||
def _test_command_as_script(self, commands, shell='/bin/sh'):
|
|
||||||
"""Combine test commands into a master script file.
|
|
||||||
|
|
||||||
The script, using the given shell, will be created in the .dox
|
|
||||||
subdirectory of the current directory.
|
|
||||||
|
|
||||||
:param commands: A list of commands to execute.
|
|
||||||
:param shell: Path to the OS shell to run the commands.
|
|
||||||
"""
|
|
||||||
dox_dir = '.dox'
|
|
||||||
master_script = os.path.join(dox_dir, 'master_script.sh')
|
|
||||||
|
|
||||||
if not os.path.exists(dox_dir):
|
|
||||||
os.mkdir(dox_dir, 0o755)
|
|
||||||
|
|
||||||
with open(master_script, "w") as f:
|
|
||||||
f.write("#!" + shell + "\n")
|
|
||||||
f.write("\n".join(commands))
|
|
||||||
f.write("\n")
|
|
||||||
|
|
||||||
os.chmod(master_script, 0o700)
|
|
||||||
return master_script
|
|
||||||
|
|
||||||
def test_command(self):
|
|
||||||
"""Return the command to execute in the container.
|
|
||||||
|
|
||||||
If there is more than one command, we combine them into a master
|
|
||||||
script to execute. Otherwise, we just issue the command normally
|
|
||||||
on the docker command line.
|
|
||||||
"""
|
|
||||||
commands = self.source.get_commands(self.extra_args)
|
|
||||||
if len(commands) > 1:
|
|
||||||
return self._test_command_as_script(commands)
|
|
||||||
|
|
||||||
ret = commands[0] + ' ' + ' '.join(self.args)
|
|
||||||
return ret.strip()
|
|
||||||
|
|
||||||
def prep_commands(self):
|
|
||||||
return self.source.get_prep_commands()
|
|
||||||
|
|
||||||
def get_add_files(self):
|
|
||||||
return self.source.get_add_files()
|
|
||||||
|
|
||||||
def append(self, args):
|
|
||||||
self.args = args
|
|
@ -1,81 +0,0 @@
|
|||||||
# Copyright (c) 2014 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.
|
|
||||||
|
|
||||||
import abc
|
|
||||||
import six
|
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(abc.ABCMeta)
|
|
||||||
class ConfigBase(object):
|
|
||||||
"""Configuration file reader base class."""
|
|
||||||
|
|
||||||
def __init__(self, options):
|
|
||||||
self.options = options
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def exists(self):
|
|
||||||
"""Check if the configuration file is present.
|
|
||||||
|
|
||||||
:returns: True if the file is present.
|
|
||||||
:returns: False if the file is not present.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def get_images(self):
|
|
||||||
"""Get list of docker source images used to build the test image.
|
|
||||||
|
|
||||||
:returns: List of image names in <user>/<repo>[:<tag>] format.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def get_commands(self):
|
|
||||||
"""Get list of commands to execute with the final test image.
|
|
||||||
|
|
||||||
These commands will be executed via the 'docker run' command
|
|
||||||
with the final test image.
|
|
||||||
|
|
||||||
:returns: List of executable commands.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def get_prep_commands(self):
|
|
||||||
"""Get list of commands to run while building the test image.
|
|
||||||
|
|
||||||
These commands will be executed while building the test image,
|
|
||||||
thus allowing for any software installation, configuration, etc.
|
|
||||||
to be built into the test image.
|
|
||||||
|
|
||||||
:returns: List of executable commands.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def get_add_files(self):
|
|
||||||
"""Get list of files to add to the test image.
|
|
||||||
|
|
||||||
The named files will be placed in the working directory of the
|
|
||||||
built test image.
|
|
||||||
|
|
||||||
:returns: List of file names.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def source_name(self):
|
|
||||||
"""The source of the configuration.
|
|
||||||
|
|
||||||
This identifies the source of the configuration values. E.g.,
|
|
||||||
a file name, or other descriptive text.
|
|
||||||
|
|
||||||
:returns: String describing the source.
|
|
||||||
"""
|
|
@ -1,33 +0,0 @@
|
|||||||
# Copyright (c) 2014 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.
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'CommandLine',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class CommandLine(object):
|
|
||||||
|
|
||||||
def __init__(self, args):
|
|
||||||
self.args = args
|
|
||||||
|
|
||||||
def test_command(self):
|
|
||||||
return " ".join(self.args)
|
|
||||||
|
|
||||||
def prep_commands(self):
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def get_add_files(self):
|
|
||||||
return []
|
|
@ -1,35 +0,0 @@
|
|||||||
# Copyright (c) 2014 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.
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'get_dockerfile',
|
|
||||||
]
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
_dockerfile = None
|
|
||||||
|
|
||||||
|
|
||||||
def get_dockerfile():
|
|
||||||
global _dockerfile
|
|
||||||
if _dockerfile is None:
|
|
||||||
_dockerfile = Dockerfile()
|
|
||||||
return _dockerfile
|
|
||||||
|
|
||||||
|
|
||||||
class Dockerfile(object):
|
|
||||||
|
|
||||||
def exists(self):
|
|
||||||
return os.path.exists('Dockerfile')
|
|
@ -1,107 +0,0 @@
|
|||||||
# Copyright (c) 2014 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.
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
import dox.config.base as base
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'get_dox_yaml',
|
|
||||||
]
|
|
||||||
|
|
||||||
_dox_yaml = None
|
|
||||||
|
|
||||||
|
|
||||||
class DoxYamlSectionNotFound(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def get_dox_yaml(options):
|
|
||||||
global _dox_yaml
|
|
||||||
if _dox_yaml is None:
|
|
||||||
_dox_yaml = DoxYaml(options)
|
|
||||||
return _dox_yaml
|
|
||||||
|
|
||||||
|
|
||||||
class DoxYaml(base.ConfigBase):
|
|
||||||
|
|
||||||
_yaml = None
|
|
||||||
dox_file = 'dox.yml'
|
|
||||||
default_section = 'testing'
|
|
||||||
default_keys_of_section = ['images', 'commands', 'add', 'prep']
|
|
||||||
|
|
||||||
def _parse_parent_child_section(self, section, _yaml):
|
|
||||||
ret = {}
|
|
||||||
if ':' not in section:
|
|
||||||
return _yaml
|
|
||||||
|
|
||||||
parent, child = section.split(':')
|
|
||||||
|
|
||||||
if parent in self._yaml.keys():
|
|
||||||
ret = self._yaml.get(parent)
|
|
||||||
ret.update(_yaml)
|
|
||||||
else:
|
|
||||||
raise DoxYamlSectionNotFound("Parent %s was not found" % parent)
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def get_section(self, _yaml, section):
|
|
||||||
if section == '_default':
|
|
||||||
section = self.default_section
|
|
||||||
|
|
||||||
# NOTE(chmou): This is for compatibility mode with dox.yml with no
|
|
||||||
# sections, probably need to be removed in the future
|
|
||||||
if (section is None and
|
|
||||||
(all(i in _yaml.keys() for i in self.default_keys_of_section)
|
|
||||||
or all(i in self.default_keys_of_section
|
|
||||||
for i in _yaml.keys()))):
|
|
||||||
return _yaml
|
|
||||||
elif not section:
|
|
||||||
if self.default_section in _yaml.keys():
|
|
||||||
return self._parse_parent_child_section(
|
|
||||||
self.default_section, _yaml.get(self.default_section))
|
|
||||||
raise DoxYamlSectionNotFound("You need to specify a section.")
|
|
||||||
elif section not in _yaml.keys():
|
|
||||||
raise DoxYamlSectionNotFound(section)
|
|
||||||
|
|
||||||
return self._parse_parent_child_section(section,
|
|
||||||
_yaml.get(section))
|
|
||||||
|
|
||||||
def _open_dox_yaml(self):
|
|
||||||
if self._yaml is None:
|
|
||||||
with open(self.dox_file, 'r') as f:
|
|
||||||
self._yaml = yaml.load(f)
|
|
||||||
return self.get_section(self._yaml,
|
|
||||||
self.options.get('section'))
|
|
||||||
|
|
||||||
def source_name(self):
|
|
||||||
return self.dox_file
|
|
||||||
|
|
||||||
def exists(self):
|
|
||||||
return os.path.exists(self.dox_file)
|
|
||||||
|
|
||||||
def get_images(self):
|
|
||||||
return self._open_dox_yaml().get('images', [])
|
|
||||||
|
|
||||||
def get_commands(self, extra_args):
|
|
||||||
return self._open_dox_yaml().get('commands', [])
|
|
||||||
|
|
||||||
def get_prep_commands(self):
|
|
||||||
return self._open_dox_yaml().get('prep', [])
|
|
||||||
|
|
||||||
def get_add_files(self):
|
|
||||||
return self._open_dox_yaml().get('add', [])
|
|
@ -1,101 +0,0 @@
|
|||||||
# Copyright (c) 2014 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.
|
|
||||||
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from six.moves.configparser import ConfigParser
|
|
||||||
|
|
||||||
import dox.config.base as base
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'get_tox_ini',
|
|
||||||
]
|
|
||||||
|
|
||||||
_tox_ini = None
|
|
||||||
|
|
||||||
|
|
||||||
def get_tox_ini(options):
|
|
||||||
global _tox_ini
|
|
||||||
if _tox_ini is None:
|
|
||||||
_tox_ini = ToxIni(options)
|
|
||||||
return _tox_ini
|
|
||||||
|
|
||||||
|
|
||||||
class ToxIni(base.ConfigBase):
|
|
||||||
|
|
||||||
_ini = None
|
|
||||||
tox_ini_file = 'tox.ini'
|
|
||||||
default_section = 'testenv'
|
|
||||||
|
|
||||||
def _open_tox_ini(self):
|
|
||||||
if self._ini is None:
|
|
||||||
self._ini = ConfigParser()
|
|
||||||
self._ini.read(self.tox_ini_file)
|
|
||||||
return self._ini
|
|
||||||
|
|
||||||
def source_name(self):
|
|
||||||
return self.tox_ini_file
|
|
||||||
|
|
||||||
def exists(self):
|
|
||||||
return os.path.exists(self.tox_ini_file)
|
|
||||||
|
|
||||||
def get_images(self):
|
|
||||||
ini = self._open_tox_ini()
|
|
||||||
if ini.has_option('docker', 'images'):
|
|
||||||
return ini.get('docker', 'images').split(',')
|
|
||||||
|
|
||||||
def get_commands(self, extra_args):
|
|
||||||
"""Get commands to run from the config file.
|
|
||||||
|
|
||||||
If any of the commands contain the string '{posargs}', then this
|
|
||||||
is replaced with the extra_args value.
|
|
||||||
"""
|
|
||||||
section = self.options.get('section',
|
|
||||||
self.default_section)
|
|
||||||
|
|
||||||
if section == '_default':
|
|
||||||
section = self.default_section
|
|
||||||
|
|
||||||
ini = self._open_tox_ini()
|
|
||||||
commands = ini.get(section, 'commands').split("\n")
|
|
||||||
extra_args = " ".join(extra_args)
|
|
||||||
|
|
||||||
scrubbed = []
|
|
||||||
for cmd in commands:
|
|
||||||
if '{posargs}' in cmd:
|
|
||||||
scrubbed.append(cmd.replace('{posargs}', extra_args))
|
|
||||||
else:
|
|
||||||
scrubbed.append(cmd)
|
|
||||||
return scrubbed
|
|
||||||
|
|
||||||
def get_prep_commands(self):
|
|
||||||
ini = self._open_tox_ini()
|
|
||||||
deps = ""
|
|
||||||
if ini.has_option('testenv', 'deps'):
|
|
||||||
deps = ini.get('testenv', 'deps')
|
|
||||||
deps = deps.replace('{toxinidir}', '/dox').replace('\n', ' ')
|
|
||||||
if deps.strip() == '':
|
|
||||||
return []
|
|
||||||
install_command = "pip install -U"
|
|
||||||
if ini.has_option('testenv', 'install_command'):
|
|
||||||
install_command = ini.get('testenv', 'install_command')
|
|
||||||
install_command = install_command.replace('{opts}', '')
|
|
||||||
install_command = install_command.replace('{packages}', deps)
|
|
||||||
return [install_command]
|
|
||||||
|
|
||||||
def get_add_files(self):
|
|
||||||
return [d for d in os.listdir('.') if d.endswith('requirements.txt')]
|
|
@ -1,75 +0,0 @@
|
|||||||
# Copyright (c) 2014 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.
|
|
||||||
|
|
||||||
import os
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
import dox.config.base as base
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'get_travis_yaml',
|
|
||||||
]
|
|
||||||
|
|
||||||
_travis_yaml = None
|
|
||||||
|
|
||||||
|
|
||||||
def get_travis_yaml(options):
|
|
||||||
global _travis_yaml
|
|
||||||
if _travis_yaml is None:
|
|
||||||
_travis_yaml = TravisYaml(options)
|
|
||||||
return _travis_yaml
|
|
||||||
|
|
||||||
|
|
||||||
class TravisYaml(base.ConfigBase):
|
|
||||||
|
|
||||||
_yaml = None
|
|
||||||
_travis_file = 'travis.yml'
|
|
||||||
|
|
||||||
def _open_travis_yaml(self):
|
|
||||||
if self._yaml is None:
|
|
||||||
with open('travis.yml', 'r') as f:
|
|
||||||
self._yaml = yaml.load(f)
|
|
||||||
return self._yaml
|
|
||||||
|
|
||||||
def source_name(self):
|
|
||||||
return self._travis_file
|
|
||||||
|
|
||||||
def exists(self):
|
|
||||||
return os.path.exists(self._travis_file)
|
|
||||||
|
|
||||||
def get_commands(self, command):
|
|
||||||
return self._open_travis_yaml().get('script', command)
|
|
||||||
|
|
||||||
def get_prep_commands(self):
|
|
||||||
travis_yaml = self._open_travis_yaml()
|
|
||||||
prep = []
|
|
||||||
|
|
||||||
for key in ('before_install', 'install', 'before_script'):
|
|
||||||
if key in travis_yaml:
|
|
||||||
val = travis_yaml[key]
|
|
||||||
if hasattr(val, 'append'):
|
|
||||||
prep.extend(val)
|
|
||||||
else:
|
|
||||||
prep.append(val)
|
|
||||||
return []
|
|
||||||
|
|
||||||
# TODO(Shrews): Implement this
|
|
||||||
def get_add_files(self):
|
|
||||||
return []
|
|
||||||
|
|
||||||
# TODO(Shrews): Implement this
|
|
||||||
def get_images(self):
|
|
||||||
return []
|
|
@ -1,45 +0,0 @@
|
|||||||
# Copyright (c) 2014 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.
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'get_images',
|
|
||||||
]
|
|
||||||
|
|
||||||
import dox.config.dockerfile
|
|
||||||
import dox.config.dox_yaml
|
|
||||||
import dox.config.tox_ini
|
|
||||||
|
|
||||||
|
|
||||||
def get_images(options):
|
|
||||||
'''Examine the local environment and figure out where we should run.'''
|
|
||||||
|
|
||||||
dockerfile = dox.config.dockerfile.get_dockerfile()
|
|
||||||
dox_yaml = dox.config.dox_yaml.get_dox_yaml(options)
|
|
||||||
tox_ini = dox.config.tox_ini.get_tox_ini(options)
|
|
||||||
|
|
||||||
if dockerfile.exists():
|
|
||||||
default_images = []
|
|
||||||
else:
|
|
||||||
# NOTE(flaper87): We should probably raise
|
|
||||||
# `RuntimeError` if no image was specified
|
|
||||||
default_images = ['ubuntu']
|
|
||||||
|
|
||||||
images = []
|
|
||||||
if dox_yaml.exists():
|
|
||||||
images = dox_yaml.get_images()
|
|
||||||
elif tox_ini.exists():
|
|
||||||
images = tox_ini.get_images()
|
|
||||||
|
|
||||||
return images or default_images
|
|
211
dox/runner.py
211
dox/runner.py
@ -1,211 +0,0 @@
|
|||||||
# Copyright (c) 2014 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.
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'Runner',
|
|
||||||
]
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import pwd
|
|
||||||
import shlex
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import tempfile
|
|
||||||
import textwrap
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class Runner(object):
|
|
||||||
|
|
||||||
def __init__(self, args, image_name=None):
|
|
||||||
image_name = image_name and "_" + image_name or ""
|
|
||||||
self.args = args
|
|
||||||
self.project = os.path.basename(os.path.abspath('.'))
|
|
||||||
self.base_image_name = 'dox/%s%s_base' % (self.project,
|
|
||||||
image_name)
|
|
||||||
self.test_image_name = 'dox/%s%s_test' % (self.project,
|
|
||||||
image_name)
|
|
||||||
self.user_map = self._get_user_mapping()
|
|
||||||
self.path_map = self._get_path_mapping()
|
|
||||||
|
|
||||||
def _get_user_mapping(self):
|
|
||||||
"""Get user mapping from command line or current user."""
|
|
||||||
if self.args.user_map:
|
|
||||||
username, uid, gid = self.args.user_map.split(':')
|
|
||||||
else:
|
|
||||||
username, uid, gid = (pwd.getpwuid(os.getuid())[0],
|
|
||||||
os.getuid(),
|
|
||||||
os.getgid())
|
|
||||||
return {'username': username, 'uid': int(uid), 'gid': int(gid)}
|
|
||||||
|
|
||||||
def _get_path_mapping(self):
|
|
||||||
"""Get path mapping from command line."""
|
|
||||||
if not self.args.path_map:
|
|
||||||
return None
|
|
||||||
local, remote = self.args.path_map.split(':', 1)
|
|
||||||
return {'local': local, 'remote': remote}
|
|
||||||
|
|
||||||
def is_docker_installed(self):
|
|
||||||
try:
|
|
||||||
self._docker_cmd("version")
|
|
||||||
except OSError as e:
|
|
||||||
msg = 'docker does not seem installed'
|
|
||||||
if e.errno == 2 and not self.args.debug:
|
|
||||||
logger.error(msg)
|
|
||||||
else:
|
|
||||||
logger.exception(msg)
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _docker_build(self, image, image_dir='.'):
|
|
||||||
logger.info('Building image %s' % image)
|
|
||||||
self._docker_cmd('build', '-t', image, image_dir)
|
|
||||||
|
|
||||||
def _docker_run(self, *args):
|
|
||||||
logger.info('Running docker')
|
|
||||||
self._docker_cmd('run', *args)
|
|
||||||
|
|
||||||
def _docker_cmd(self, *args):
|
|
||||||
base_docker = ['docker']
|
|
||||||
try:
|
|
||||||
self._run_shell_command(base_docker + list(args))
|
|
||||||
except Exception as e:
|
|
||||||
logger.error("docker failed")
|
|
||||||
logger.info(e)
|
|
||||||
raise
|
|
||||||
|
|
||||||
def _run_shell_command(self, cmd):
|
|
||||||
|
|
||||||
logger.debug('shell: ' + ' '.join(cmd))
|
|
||||||
if self.args.noop:
|
|
||||||
return
|
|
||||||
|
|
||||||
process = subprocess.Popen(
|
|
||||||
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
||||||
|
|
||||||
while True:
|
|
||||||
output = process.stdout.read(1)
|
|
||||||
|
|
||||||
if output == '' and process.poll() is not None:
|
|
||||||
break
|
|
||||||
|
|
||||||
if output != '' and not self.args.quiet:
|
|
||||||
sys.stdout.write(output)
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
if process.returncode:
|
|
||||||
raise Exception(
|
|
||||||
"%s returned %d" % (cmd, process.returncode))
|
|
||||||
|
|
||||||
def _indent(self, text):
|
|
||||||
wrapper = textwrap.TextWrapper(
|
|
||||||
initial_indent=' ', subsequent_indent=' ')
|
|
||||||
return '\n'.join([wrapper.fill(line) for line in text.split('\n')])
|
|
||||||
|
|
||||||
def _get_image_list(self):
|
|
||||||
|
|
||||||
process = subprocess.Popen(
|
|
||||||
shlex.split('docker images'),
|
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
||||||
|
|
||||||
stdout, _ = process.communicate()
|
|
||||||
out_text = stdout.strip().decode('utf-8') if stdout else ""
|
|
||||||
return dict([f.split()[:2] for f in out_text.split('\n')])
|
|
||||||
|
|
||||||
def have_test_image(self):
|
|
||||||
if self.args.rebuild or self.args.rebuild_all:
|
|
||||||
return False
|
|
||||||
if self.test_image_name in self._get_image_list():
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def build_test_image(self, image, commands):
|
|
||||||
logger.debug(
|
|
||||||
"Want test image %(image)s with %(prep_commands)s" % dict(
|
|
||||||
image=self.test_image_name,
|
|
||||||
prep_commands=commands.prep_commands()))
|
|
||||||
if self.have_test_image():
|
|
||||||
return
|
|
||||||
|
|
||||||
dockerfile = []
|
|
||||||
dockerfile.append("FROM %s" % image)
|
|
||||||
try:
|
|
||||||
tempd = tempfile.mkdtemp()
|
|
||||||
if not self.user_map['username'] == 'root':
|
|
||||||
dockerfile.append(
|
|
||||||
"RUN useradd -M -U -d /src -u %(uid)s %(user)s" % dict(
|
|
||||||
uid=self.user_map['uid'],
|
|
||||||
gid=self.user_map['gid'],
|
|
||||||
user=self.user_map['username']))
|
|
||||||
|
|
||||||
for add_file in commands.get_add_files():
|
|
||||||
shutil.copy(add_file, os.path.join(tempd, add_file))
|
|
||||||
dockerfile.append("ADD %s /dox/" % add_file)
|
|
||||||
dockerfile.append("WORKDIR /dox")
|
|
||||||
for command in commands.prep_commands():
|
|
||||||
dockerfile.append("RUN %s\n" % command)
|
|
||||||
dockerfile = '\n'.join(dockerfile)
|
|
||||||
with open(os.path.join(tempd, 'Dockerfile'), 'w') as f:
|
|
||||||
f.write(dockerfile)
|
|
||||||
logger.debug("Dockerfile:\n" + self._indent(dockerfile))
|
|
||||||
self._docker_build(self.test_image_name, tempd)
|
|
||||||
finally:
|
|
||||||
shutil.rmtree(tempd)
|
|
||||||
|
|
||||||
def run_commands(self, command):
|
|
||||||
path = os.path.abspath('.')
|
|
||||||
if self.path_map:
|
|
||||||
path = path.replace(self.path_map['local'],
|
|
||||||
self.path_map['remote'])
|
|
||||||
docker_args = ['--privileged=true',
|
|
||||||
'--user=%s' % self.user_map['username'],
|
|
||||||
'-v', "%s:/src" % path,
|
|
||||||
'-w', '/src']
|
|
||||||
if not self.args.keep_image:
|
|
||||||
docker_args.append('--rm')
|
|
||||||
docker_args.append(self.test_image_name)
|
|
||||||
for c in command:
|
|
||||||
docker_args.append(c)
|
|
||||||
self._docker_run(*docker_args)
|
|
||||||
|
|
||||||
def have_base_image(self):
|
|
||||||
if self.args.rebuild_all:
|
|
||||||
return False
|
|
||||||
if self.base_image_name in self._get_image_list():
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def build_base_image(self):
|
|
||||||
|
|
||||||
logger.debug("Want base image")
|
|
||||||
if self.have_base_image():
|
|
||||||
return
|
|
||||||
self._docker_build(self.base_image_name)
|
|
||||||
|
|
||||||
def run(self, image, command):
|
|
||||||
logger.debug(
|
|
||||||
"Going to run %(command)s in %(image)s" % dict(
|
|
||||||
command=command.test_command(), image=image))
|
|
||||||
if self.args.rebuild:
|
|
||||||
logger.debug("Need to rebuild")
|
|
||||||
|
|
||||||
if image is None:
|
|
||||||
self.build_base_image()
|
|
||||||
self.build_test_image(image, command)
|
|
||||||
|
|
||||||
self.run_commands(shlex.split(command.test_command()))
|
|
@ -1,69 +0,0 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
import fixtures
|
|
||||||
import testtools
|
|
||||||
|
|
||||||
_TRUE_VALUES = ('true', '1', 'yes')
|
|
||||||
|
|
||||||
TESTDIR = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
SAMPLEDIR = os.path.join(TESTDIR, 'samples')
|
|
||||||
|
|
||||||
|
|
||||||
class TestCase(testtools.TestCase):
|
|
||||||
|
|
||||||
"""Test case base class for all unit tests."""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
"""Run before each test method to initialize test environment."""
|
|
||||||
|
|
||||||
super(TestCase, self).setUp()
|
|
||||||
test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
|
|
||||||
try:
|
|
||||||
test_timeout = int(test_timeout)
|
|
||||||
except ValueError:
|
|
||||||
# If timeout value is invalid do not set a timeout.
|
|
||||||
test_timeout = 0
|
|
||||||
if test_timeout > 0:
|
|
||||||
self.useFixture(fixtures.Timeout(test_timeout, gentle=True))
|
|
||||||
|
|
||||||
self.useFixture(fixtures.NestedTempfile())
|
|
||||||
self.useFixture(fixtures.TempHomeDir())
|
|
||||||
|
|
||||||
if os.environ.get('OS_STDOUT_CAPTURE') in _TRUE_VALUES:
|
|
||||||
stdout = self.useFixture(fixtures.StringStream('stdout')).stream
|
|
||||||
self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
|
|
||||||
if os.environ.get('OS_STDERR_CAPTURE') in _TRUE_VALUES:
|
|
||||||
stderr = self.useFixture(fixtures.StringStream('stderr')).stream
|
|
||||||
self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
|
|
||||||
|
|
||||||
self.log_fixture = self.useFixture(fixtures.FakeLogger())
|
|
||||||
|
|
||||||
|
|
||||||
def fake_does_exist(self):
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def fake_does_not_exist(self):
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def bool_to_fake(val):
|
|
||||||
if val:
|
|
||||||
return fake_does_exist
|
|
||||||
else:
|
|
||||||
return fake_does_not_exist
|
|
@ -1,84 +0,0 @@
|
|||||||
# Copyright (c) 2014 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.
|
|
||||||
|
|
||||||
"""
|
|
||||||
test_commands
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
Tests for `dox.commands` module.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import fixtures
|
|
||||||
import testscenarios
|
|
||||||
|
|
||||||
from dox import commands
|
|
||||||
from dox.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
def get_fake_command(value):
|
|
||||||
def fake_value(self, args):
|
|
||||||
return value
|
|
||||||
return fake_value
|
|
||||||
|
|
||||||
|
|
||||||
class TestCommands(base.TestCase):
|
|
||||||
|
|
||||||
scenarios = [
|
|
||||||
('dox_yaml', dict(
|
|
||||||
dox_yaml=True, tox_ini=False, travis_yaml=False,
|
|
||||||
dox_value=["testr run"], tox_value=None, travis_value=None,
|
|
||||||
commands="testr run")),
|
|
||||||
('dox_yaml_ignore_others', dict(
|
|
||||||
dox_yaml=True, tox_ini=True, travis_yaml=True,
|
|
||||||
dox_value=["testr run"], tox_value=["setup.py test"],
|
|
||||||
travis_value=["gem test"],
|
|
||||||
commands="testr run")),
|
|
||||||
('tox_ini', dict(
|
|
||||||
dox_yaml=False, tox_ini=True, travis_yaml=False,
|
|
||||||
dox_value=None, tox_value=["setup.py test"], travis_value=None,
|
|
||||||
commands="setup.py test")),
|
|
||||||
('travis_yaml', dict(
|
|
||||||
dox_yaml=False, tox_ini=False, travis_yaml=True,
|
|
||||||
dox_value=["testr run"], tox_value=None, travis_value=["ruby"],
|
|
||||||
commands="ruby")),
|
|
||||||
]
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestCommands, self).setUp()
|
|
||||||
self.useFixture(fixtures.MonkeyPatch(
|
|
||||||
'dox.config.dox_yaml.DoxYaml.exists',
|
|
||||||
base.bool_to_fake(self.dox_yaml)))
|
|
||||||
self.useFixture(fixtures.MonkeyPatch(
|
|
||||||
'dox.config.tox_ini.ToxIni.exists',
|
|
||||||
base.bool_to_fake(self.tox_ini)))
|
|
||||||
self.useFixture(fixtures.MonkeyPatch(
|
|
||||||
'dox.config.travis_yaml.TravisYaml.exists',
|
|
||||||
base.bool_to_fake(self.travis_yaml)))
|
|
||||||
self.useFixture(fixtures.MonkeyPatch(
|
|
||||||
'dox.config.dox_yaml.DoxYaml.get_commands',
|
|
||||||
get_fake_command(self.dox_value)))
|
|
||||||
self.useFixture(fixtures.MonkeyPatch(
|
|
||||||
'dox.config.tox_ini.ToxIni.get_commands',
|
|
||||||
get_fake_command(self.tox_value)))
|
|
||||||
self.useFixture(fixtures.MonkeyPatch(
|
|
||||||
'dox.config.travis_yaml.TravisYaml.get_commands',
|
|
||||||
get_fake_command(self.travis_value)))
|
|
||||||
|
|
||||||
def test_commands(self):
|
|
||||||
p = commands.Commands()
|
|
||||||
self.assertEqual(p.test_command(), self.commands)
|
|
||||||
|
|
||||||
|
|
||||||
def load_tests(loader, in_tests, pattern):
|
|
||||||
return testscenarios.load_tests_apply_scenarios(loader, in_tests, pattern)
|
|
@ -1,62 +0,0 @@
|
|||||||
# 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 dox.config.base as cfg_base
|
|
||||||
|
|
||||||
from dox.config import dox_yaml
|
|
||||||
from dox.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
class TestDoxYaml(base.TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestDoxYaml, self).setUp()
|
|
||||||
self.doxyaml = dox_yaml.DoxYaml({})
|
|
||||||
self.doxyaml.dox_file = os.path.join(base.SAMPLEDIR,
|
|
||||||
'dox.yaml')
|
|
||||||
|
|
||||||
def test_base_class(self):
|
|
||||||
self.assertIsInstance(self.doxyaml, cfg_base.ConfigBase)
|
|
||||||
|
|
||||||
def test_dox_yaml_old_parsing(self):
|
|
||||||
self.doxyaml = dox_yaml.DoxYaml({})
|
|
||||||
self.doxyaml.dox_file = os.path.join(base.SAMPLEDIR,
|
|
||||||
'dox-old.yaml')
|
|
||||||
for key in self.doxyaml.default_keys_of_section:
|
|
||||||
self.assertIn(
|
|
||||||
key, self.doxyaml._open_dox_yaml().keys())
|
|
||||||
|
|
||||||
def test_dox_yaml_not_finding_section(self):
|
|
||||||
self.doxyaml = dox_yaml.DoxYaml({'section': 'foobar'})
|
|
||||||
self.doxyaml.dox_file = os.path.join(base.SAMPLEDIR,
|
|
||||||
'dox.yaml')
|
|
||||||
self.assertRaises(
|
|
||||||
dox_yaml.DoxYamlSectionNotFound,
|
|
||||||
self.doxyaml._open_dox_yaml)
|
|
||||||
|
|
||||||
def test_dox_yaml_with_default_session(self):
|
|
||||||
self.doxyaml = dox_yaml.DoxYaml({})
|
|
||||||
self.doxyaml.dox_file = os.path.join(base.SAMPLEDIR,
|
|
||||||
'dox.yaml')
|
|
||||||
|
|
||||||
for key in self.doxyaml.default_keys_of_section:
|
|
||||||
self.assertIn(
|
|
||||||
key, self.doxyaml._open_dox_yaml().keys())
|
|
||||||
|
|
||||||
def test_dox_yaml_new_parsing(self):
|
|
||||||
for key in self.doxyaml.default_keys_of_section:
|
|
||||||
self.assertIn(
|
|
||||||
key, self.doxyaml._open_dox_yaml().keys())
|
|
||||||
|
|
||||||
# TOOD(chmou): Finish tests of dox_yaml.py
|
|
@ -1,65 +0,0 @@
|
|||||||
# Author: Chmouel Boudjnah <chmouel@enovance.com>
|
|
||||||
#
|
|
||||||
# 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 mock
|
|
||||||
import os
|
|
||||||
|
|
||||||
import dox.config.base as cfg_base
|
|
||||||
|
|
||||||
from dox.config import tox_ini
|
|
||||||
from dox.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
class TestToxIni(base.TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestToxIni, self).setUp()
|
|
||||||
|
|
||||||
self.toxini = tox_ini.ToxIni({})
|
|
||||||
self.toxini.tox_ini_file = os.path.join(base.SAMPLEDIR,
|
|
||||||
'tox.ini')
|
|
||||||
|
|
||||||
def test_get_tox_ini(self):
|
|
||||||
tox_ini_new = tox_ini.ToxIni({})
|
|
||||||
with mock.patch.object(tox_ini, '_tox_ini', tox_ini_new):
|
|
||||||
self.assertEqual(tox_ini.get_tox_ini({}),
|
|
||||||
tox_ini_new)
|
|
||||||
|
|
||||||
def test_base_class(self):
|
|
||||||
self.assertIsInstance(self.toxini, cfg_base.ConfigBase)
|
|
||||||
|
|
||||||
def test_exists_ini_file(self):
|
|
||||||
self.assertTrue(self.toxini.exists())
|
|
||||||
|
|
||||||
def test_open_tox_ini(self):
|
|
||||||
self.assertIn('tox', self.toxini._open_tox_ini().sections())
|
|
||||||
|
|
||||||
def test_get_images(self):
|
|
||||||
self.assertEqual(['foo', 'bar'],
|
|
||||||
self.toxini.get_images())
|
|
||||||
|
|
||||||
def test_get_commands(self):
|
|
||||||
self.toxini = tox_ini.ToxIni({'section': 'testenv2'})
|
|
||||||
self.toxini.tox_ini_file = os.path.join(base.SAMPLEDIR,
|
|
||||||
'tox.ini')
|
|
||||||
self.assertEqual(['foobar -c blah'],
|
|
||||||
self.toxini.get_commands(
|
|
||||||
['-c']))
|
|
||||||
|
|
||||||
def test_get_prep_commands(self):
|
|
||||||
cmd = ['pip install -U -r/dox/requirements.txt '
|
|
||||||
'-r/dox/test-requirements.txt']
|
|
||||||
self.assertEqual(
|
|
||||||
self.toxini.get_prep_commands(),
|
|
||||||
cmd)
|
|
@ -1,26 +0,0 @@
|
|||||||
# 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 dox.config.base as cfg_base
|
|
||||||
|
|
||||||
from dox.config import travis_yaml
|
|
||||||
from dox.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
class TestTravisYaml(base.TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestTravisYaml, self).setUp()
|
|
||||||
self.travisyaml = travis_yaml.TravisYaml({})
|
|
||||||
|
|
||||||
def test_base_class(self):
|
|
||||||
self.assertIsInstance(self.travisyaml, cfg_base.ConfigBase)
|
|
@ -1,9 +0,0 @@
|
|||||||
images:
|
|
||||||
- infra/trusty
|
|
||||||
add:
|
|
||||||
- requirements.txt
|
|
||||||
- test-requirements.txt
|
|
||||||
prep:
|
|
||||||
- pip install -U -r requirements.txt -r test-requirements.txt
|
|
||||||
commands:
|
|
||||||
- python setup.py testr --slowest
|
|
@ -1,21 +0,0 @@
|
|||||||
testing:
|
|
||||||
images:
|
|
||||||
- infra/trusty
|
|
||||||
add:
|
|
||||||
- requirements.txt
|
|
||||||
- test-requirements.txt
|
|
||||||
prep:
|
|
||||||
- pip install -U -r requirements.txt -r test-requirements.txt
|
|
||||||
commands:
|
|
||||||
- python setup.py testr --slowest
|
|
||||||
|
|
||||||
pep8:
|
|
||||||
images:
|
|
||||||
- infra/trusty
|
|
||||||
add:
|
|
||||||
- requirements.txt
|
|
||||||
- test-requirements.txt
|
|
||||||
prep:
|
|
||||||
- pip install -U -r requirements.txt -r test-requirements.txt
|
|
||||||
commands:
|
|
||||||
- pep8
|
|
@ -1,14 +0,0 @@
|
|||||||
[tox]
|
|
||||||
minversion = 1.6
|
|
||||||
|
|
||||||
[docker]
|
|
||||||
images = foo,bar
|
|
||||||
|
|
||||||
[testenv]
|
|
||||||
commands = foobar
|
|
||||||
deps = -r{toxinidir}/requirements.txt
|
|
||||||
-r{toxinidir}/test-requirements.txt
|
|
||||||
install_command = pip install -U {opts} {packages}
|
|
||||||
|
|
||||||
[testenv2]
|
|
||||||
commands = foobar {posargs} blah
|
|
@ -1,79 +0,0 @@
|
|||||||
# Author: Chmouel Boudjnah <chmouel@chmouel.com>
|
|
||||||
#
|
|
||||||
# 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 argparse
|
|
||||||
|
|
||||||
import mock
|
|
||||||
|
|
||||||
import dox.cmd
|
|
||||||
from dox.tests import base
|
|
||||||
|
|
||||||
default_argp = argparse.Namespace(user_map=None, command=None,
|
|
||||||
environment=None, extra_args=None,
|
|
||||||
debug=None, noop=True,
|
|
||||||
images=None, path_map=None)
|
|
||||||
|
|
||||||
|
|
||||||
class TestCmd(base.TestCase):
|
|
||||||
|
|
||||||
@mock.patch('dox.runner.Runner.is_docker_installed',
|
|
||||||
return_value=False)
|
|
||||||
def test_runner(self, installed_mock):
|
|
||||||
self.assertRaises(SystemExit, dox.cmd.runner, default_argp)
|
|
||||||
|
|
||||||
@mock.patch('dox.cmd.run_dox')
|
|
||||||
@mock.patch('dox.config.cmdline.CommandLine')
|
|
||||||
def test_multiple_images_one_command(self, m_cmdline, m_run_dox):
|
|
||||||
argp = default_argp
|
|
||||||
argp.images = 'foo, bar'
|
|
||||||
argp.command = '/bin/true'
|
|
||||||
dox.cmd.runner(argp)
|
|
||||||
|
|
||||||
self.assertTrue(m_cmdline.called)
|
|
||||||
# silly but i'm not sure how to test that in a proper way
|
|
||||||
self.assertEqual(['foo', 'bar'],
|
|
||||||
m_run_dox.call_args_list[0][0][1])
|
|
||||||
|
|
||||||
@mock.patch('dox.cmd.run_dox')
|
|
||||||
@mock.patch('dox.images.get_images')
|
|
||||||
def test_multiple_environments(self, m_get_images, m_run_dox):
|
|
||||||
argp = default_argp
|
|
||||||
argp.environment = 'env1, env2'
|
|
||||||
dox.cmd.runner(argp)
|
|
||||||
|
|
||||||
self.assertEqual(2, m_get_images.call_count)
|
|
||||||
self.assertEqual(2, m_run_dox.call_count)
|
|
||||||
|
|
||||||
@mock.patch('dox.cmd.run_dox')
|
|
||||||
@mock.patch('dox.images.get_images')
|
|
||||||
def test_multiple_environments_images(self, m_get_images, m_run_dox):
|
|
||||||
argp = default_argp
|
|
||||||
argp.images = 'foo, bar'
|
|
||||||
argp.environment = 'env1, env2'
|
|
||||||
dox.cmd.runner(argp)
|
|
||||||
|
|
||||||
self.assertEqual(0, m_get_images.call_count)
|
|
||||||
self.assertEqual(2, m_run_dox.call_count)
|
|
||||||
|
|
||||||
@mock.patch('dox.cmd.run_dox')
|
|
||||||
@mock.patch('dox.images.get_images')
|
|
||||||
def test_default(self, m_get_images, m_run_dox):
|
|
||||||
dox.cmd.runner(default_argp)
|
|
||||||
|
|
||||||
self.assertEqual('_default',
|
|
||||||
m_get_images.call_args_list[0][0][0].get('section'))
|
|
||||||
|
|
||||||
@mock.patch('dox.runner.Runner')
|
|
||||||
def test_run_dox(self, m_runner):
|
|
||||||
dox.cmd.run_dox(default_argp, ['1', '2', '3'], '/bin/echo', "name")
|
|
||||||
self.assertEqual(1, m_runner.call_count)
|
|
@ -1,26 +0,0 @@
|
|||||||
# 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_dox
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
Tests for `dox` module.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from dox.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
class TestDox(base.TestCase):
|
|
||||||
|
|
||||||
def test_something(self):
|
|
||||||
pass
|
|
@ -1,102 +0,0 @@
|
|||||||
# Copyright (c) 2014 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.
|
|
||||||
|
|
||||||
"""
|
|
||||||
test_images
|
|
||||||
--------------
|
|
||||||
|
|
||||||
Tests for `dox.images` module.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import fixtures
|
|
||||||
import testscenarios
|
|
||||||
|
|
||||||
from dox import images
|
|
||||||
from dox.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
def get_fake_image(value):
|
|
||||||
if value is not None:
|
|
||||||
def fake_value(self):
|
|
||||||
return value
|
|
||||||
else:
|
|
||||||
def fake_value(self):
|
|
||||||
return ['ubuntu']
|
|
||||||
return fake_value
|
|
||||||
|
|
||||||
|
|
||||||
class TestImages(base.TestCase):
|
|
||||||
|
|
||||||
scenarios = [
|
|
||||||
('have_dockerfile', dict(
|
|
||||||
dockerfile=True, tox_ini=False, dox_yaml=False,
|
|
||||||
tox_value=[], dox_value=[], images=[])),
|
|
||||||
('no_dockerfile', dict(
|
|
||||||
dockerfile=False, tox_ini=False, dox_yaml=False,
|
|
||||||
tox_value=[], dox_value=[], images=['ubuntu'])),
|
|
||||||
('tox_no_docker', dict(
|
|
||||||
dockerfile=False, tox_ini=True, dox_yaml=False,
|
|
||||||
tox_value=[], dox_value=[], images=['ubuntu'])),
|
|
||||||
('tox_docker', dict(
|
|
||||||
dockerfile=False, tox_ini=True, dox_yaml=False,
|
|
||||||
tox_value=['tox_docker'], dox_value=[], images=['tox_docker'])),
|
|
||||||
('dox_image', dict(
|
|
||||||
dockerfile=False, tox_ini=False, dox_yaml=True,
|
|
||||||
tox_value=[], dox_value=[], images=['ubuntu'])),
|
|
||||||
('dox_no_image', dict(
|
|
||||||
dockerfile=False, tox_ini=False, dox_yaml=True,
|
|
||||||
tox_value=[], dox_value=['dox_value'], images=['dox_value'])),
|
|
||||||
('both_dox_wins', dict(
|
|
||||||
dockerfile=False, tox_ini=True, dox_yaml=True,
|
|
||||||
tox_value=['tox_wins'], dox_value=['dox_wins'],
|
|
||||||
images=['dox_wins'])),
|
|
||||||
('both_no_dox', dict(
|
|
||||||
dockerfile=False, tox_ini=True, dox_yaml=True,
|
|
||||||
tox_value=['tox_wins'], dox_value=[], images=['ubuntu'])),
|
|
||||||
('both_dockerfile_passthru', dict(
|
|
||||||
dockerfile=True, tox_ini=True, dox_yaml=True,
|
|
||||||
tox_value=[], dox_value=[], images=[])),
|
|
||||||
('all_dockerfile_dox_override', dict(
|
|
||||||
dockerfile=True, tox_ini=True, dox_yaml=True,
|
|
||||||
tox_value=[], dox_value=['dox_wins'], images=['dox_wins'])),
|
|
||||||
('all_dockerfile_tox_loses', dict(
|
|
||||||
dockerfile=True, tox_ini=True, dox_yaml=True,
|
|
||||||
tox_value=['tox_wins'], dox_value=[], images=[])),
|
|
||||||
]
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestImages, self).setUp()
|
|
||||||
self.useFixture(fixtures.MonkeyPatch(
|
|
||||||
'dox.config.dockerfile.Dockerfile.exists',
|
|
||||||
base.bool_to_fake(self.dockerfile)))
|
|
||||||
self.useFixture(fixtures.MonkeyPatch(
|
|
||||||
'dox.config.dox_yaml.DoxYaml.exists',
|
|
||||||
base.bool_to_fake(self.dox_yaml)))
|
|
||||||
self.useFixture(fixtures.MonkeyPatch(
|
|
||||||
'dox.config.tox_ini.ToxIni.exists',
|
|
||||||
base.bool_to_fake(self.tox_ini)))
|
|
||||||
self.useFixture(fixtures.MonkeyPatch(
|
|
||||||
'dox.config.dox_yaml.DoxYaml.get_images',
|
|
||||||
get_fake_image(self.dox_value)))
|
|
||||||
self.useFixture(fixtures.MonkeyPatch(
|
|
||||||
'dox.config.tox_ini.ToxIni.get_images',
|
|
||||||
get_fake_image(self.tox_value)))
|
|
||||||
|
|
||||||
def test_images(self):
|
|
||||||
image = images.get_images({})
|
|
||||||
self.assertEqual(image, self.images)
|
|
||||||
|
|
||||||
|
|
||||||
def load_tests(loader, in_tests, pattern):
|
|
||||||
return testscenarios.load_tests_apply_scenarios(loader, in_tests, pattern)
|
|
@ -1,232 +0,0 @@
|
|||||||
# Author: Chmouel Boudjnah <chmouel@enovance.com>
|
|
||||||
#
|
|
||||||
# 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 argparse
|
|
||||||
import os
|
|
||||||
import shutil # noqa
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
import mock
|
|
||||||
|
|
||||||
import dox.runner as doxrunner
|
|
||||||
from dox.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
class FakeCommands(object):
|
|
||||||
def __init__(self, commands=None, files_to_add=None):
|
|
||||||
self.commands = commands or ["command1", "command2"]
|
|
||||||
self.files_to_add = files_to_add or ["file1"]
|
|
||||||
|
|
||||||
def prep_commands(self):
|
|
||||||
return self.commands
|
|
||||||
|
|
||||||
def get_add_files(self):
|
|
||||||
return self.files_to_add
|
|
||||||
|
|
||||||
|
|
||||||
class TestRunner(base.TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestRunner, self).setUp()
|
|
||||||
|
|
||||||
def test_user_mapping(self):
|
|
||||||
dr = doxrunner.Runner(argparse.Namespace(user_map='foo:100:10',
|
|
||||||
path_map=None))
|
|
||||||
self.assertEqual('foo', dr.user_map['username'])
|
|
||||||
self.assertEqual(100, dr.user_map['uid'])
|
|
||||||
self.assertEqual(10, dr.user_map['gid'])
|
|
||||||
|
|
||||||
@mock.patch('os.getuid', return_value=12345)
|
|
||||||
@mock.patch('os.getgid', return_value=67890)
|
|
||||||
@mock.patch('pwd.getpwuid', return_value=['toto'])
|
|
||||||
def test_user_mapping_default(self, os_uid, os_gid, os_username):
|
|
||||||
dr = doxrunner.Runner(argparse.Namespace(user_map=None,
|
|
||||||
path_map=None))
|
|
||||||
self.assertEqual('toto', dr.user_map['username'])
|
|
||||||
self.assertEqual(12345, dr.user_map['uid'])
|
|
||||||
self.assertEqual(67890, dr.user_map['gid'])
|
|
||||||
|
|
||||||
def test_path_mapping(self):
|
|
||||||
dr = doxrunner.Runner(argparse.Namespace(path_map='/Users:/home',
|
|
||||||
user_map=None))
|
|
||||||
self.assertEqual('/Users', dr.path_map['local'])
|
|
||||||
self.assertEqual('/home', dr.path_map['remote'])
|
|
||||||
|
|
||||||
def test_path_mapping_extra_colon(self):
|
|
||||||
dr = doxrunner.Runner(argparse.Namespace(path_map='/Users:/home:foo',
|
|
||||||
user_map=None))
|
|
||||||
self.assertEqual('/Users', dr.path_map['local'])
|
|
||||||
self.assertEqual('/home:foo', dr.path_map['remote'])
|
|
||||||
|
|
||||||
def test_path_mapping_default(self):
|
|
||||||
dr = doxrunner.Runner(argparse.Namespace(path_map=None,
|
|
||||||
user_map=None))
|
|
||||||
self.assertIsNone(dr.path_map)
|
|
||||||
|
|
||||||
def test_is_docker_installed(self):
|
|
||||||
dr = doxrunner.Runner(argparse.Namespace(
|
|
||||||
path_map=None,
|
|
||||||
user_map=None))
|
|
||||||
|
|
||||||
def mydocker_cmd(dr, *args):
|
|
||||||
raise OSError
|
|
||||||
dr._docker_cmd = mydocker_cmd
|
|
||||||
self.assertFalse(dr.is_docker_installed())
|
|
||||||
|
|
||||||
def mydocker_cmd(dr, *args):
|
|
||||||
return True
|
|
||||||
dr._docker_cmd = mydocker_cmd
|
|
||||||
self.assertTrue(dr.is_docker_installed())
|
|
||||||
|
|
||||||
def test_docker_cmd(self):
|
|
||||||
dr = doxrunner.Runner(argparse.Namespace(user_map=None,
|
|
||||||
path_map=None,
|
|
||||||
debug=False))
|
|
||||||
dr._run_shell_command = mock.MagicMock()
|
|
||||||
dr._docker_cmd("version")
|
|
||||||
dr._run_shell_command.assert_called_with(
|
|
||||||
['docker', 'version']
|
|
||||||
)
|
|
||||||
|
|
||||||
dr = doxrunner.Runner(argparse.Namespace(user_map=None,
|
|
||||||
path_map=None,
|
|
||||||
debug=True))
|
|
||||||
dr._run_shell_command = mock.MagicMock()
|
|
||||||
dr._docker_cmd("version")
|
|
||||||
dr._run_shell_command.assert_called_with(
|
|
||||||
['docker', 'version']
|
|
||||||
)
|
|
||||||
|
|
||||||
dr = doxrunner.Runner(argparse.Namespace(user_map=None,
|
|
||||||
path_map=None,
|
|
||||||
debug=True))
|
|
||||||
dr._run_shell_command = mock.Mock()
|
|
||||||
dr._run_shell_command.side_effect = OSError("Boom")
|
|
||||||
self.assertRaises(OSError, dr._docker_cmd, "version")
|
|
||||||
|
|
||||||
def test_build_images_pass(self):
|
|
||||||
dr = doxrunner.Runner(argparse.Namespace(path_map=None,
|
|
||||||
user_map=None))
|
|
||||||
dr.have_test_image = mock.Mock()
|
|
||||||
dr.return_value = True
|
|
||||||
self.assertIsNone(dr.build_test_image("image", FakeCommands()))
|
|
||||||
|
|
||||||
@mock.patch.multiple("shutil", rmtree=mock.DEFAULT,
|
|
||||||
copy=mock.DEFAULT)
|
|
||||||
def test_build_images(self, rmtree, copy):
|
|
||||||
my_temp_file = tempfile.mkdtemp()
|
|
||||||
docker_written = os.path.join(my_temp_file, "Dockerfile")
|
|
||||||
fk = FakeCommands(["toto1", "toto2"],
|
|
||||||
["blah3", "blah4"])
|
|
||||||
|
|
||||||
dr = doxrunner.Runner(argparse.Namespace(
|
|
||||||
quiet=False, noop=False,
|
|
||||||
rebuild=True, debug=True,
|
|
||||||
path_map=None,
|
|
||||||
user_map=None))
|
|
||||||
m_docker_build = dr._docker_build = mock.Mock()
|
|
||||||
with mock.patch.object(tempfile, "mkdtemp", return_value=my_temp_file):
|
|
||||||
dr.build_test_image("myimage", fk)
|
|
||||||
|
|
||||||
m_docker_build.assert_called_once_with(
|
|
||||||
dr.test_image_name, my_temp_file)
|
|
||||||
|
|
||||||
# Only the last one
|
|
||||||
copy.assert_called_with('blah4',
|
|
||||||
os.path.join(my_temp_file, "blah4"))
|
|
||||||
self.assertTrue(copy.called)
|
|
||||||
self.assertTrue(rmtree.called)
|
|
||||||
self.assertTrue(os.path.exists(docker_written))
|
|
||||||
um = dr.user_map
|
|
||||||
|
|
||||||
expected = """FROM %s
|
|
||||||
RUN useradd -M -U -d /src -u %s %s
|
|
||||||
ADD blah3 /dox/
|
|
||||||
ADD blah4 /dox/
|
|
||||||
WORKDIR /dox
|
|
||||||
RUN toto1
|
|
||||||
|
|
||||||
RUN toto2
|
|
||||||
""""" % ("myimage", um['uid'], um['username'])
|
|
||||||
|
|
||||||
self.assertEqual(expected, open(docker_written, 'r').read())
|
|
||||||
|
|
||||||
def test_have_base_image(self):
|
|
||||||
dr = doxrunner.Runner(argparse.Namespace(
|
|
||||||
rebuild=True,
|
|
||||||
rebuild_all=False,
|
|
||||||
path_map=None,
|
|
||||||
user_map=None))
|
|
||||||
dr._get_image_list = mock.MagicMock()
|
|
||||||
self.assertFalse(dr.have_base_image())
|
|
||||||
|
|
||||||
dr = doxrunner.Runner(argparse.Namespace(
|
|
||||||
rebuild=False,
|
|
||||||
rebuild_all=True,
|
|
||||||
path_map=None,
|
|
||||||
user_map=None))
|
|
||||||
dr._get_image_list = mock.MagicMock()
|
|
||||||
self.assertFalse(dr.have_base_image())
|
|
||||||
|
|
||||||
dr = doxrunner.Runner(argparse.Namespace(
|
|
||||||
rebuild=False,
|
|
||||||
rebuild_all=False,
|
|
||||||
path_map=None,
|
|
||||||
user_map=None))
|
|
||||||
dr._get_image_list = mock.MagicMock()
|
|
||||||
dr._get_image_list.return_value = [dr.base_image_name]
|
|
||||||
self.assertTrue(dr.have_base_image())
|
|
||||||
|
|
||||||
dr = doxrunner.Runner(argparse.Namespace(
|
|
||||||
rebuild=False,
|
|
||||||
rebuild_all=False,
|
|
||||||
path_map=None,
|
|
||||||
user_map=None))
|
|
||||||
dr._get_image_list = mock.MagicMock()
|
|
||||||
dr._get_image_list.return_value = []
|
|
||||||
self.assertFalse(dr.have_base_image())
|
|
||||||
|
|
||||||
def test_have_test_image(self):
|
|
||||||
# NOTE(chmou): this probably would need some refactoring with
|
|
||||||
# have_base_image
|
|
||||||
dr = doxrunner.Runner(argparse.Namespace(
|
|
||||||
rebuild=True,
|
|
||||||
rebuild_all=False,
|
|
||||||
path_map=None,
|
|
||||||
user_map=None))
|
|
||||||
self.assertFalse(dr.have_test_image())
|
|
||||||
|
|
||||||
dr = doxrunner.Runner(argparse.Namespace(
|
|
||||||
rebuild=False,
|
|
||||||
rebuild_all=True,
|
|
||||||
path_map=None,
|
|
||||||
user_map=None))
|
|
||||||
self.assertFalse(dr.have_test_image())
|
|
||||||
|
|
||||||
dr = doxrunner.Runner(argparse.Namespace(
|
|
||||||
rebuild=False,
|
|
||||||
rebuild_all=False,
|
|
||||||
path_map=None,
|
|
||||||
user_map=None))
|
|
||||||
dr._get_image_list = mock.MagicMock()
|
|
||||||
dr._get_image_list.return_value = [dr.test_image_name]
|
|
||||||
self.assertTrue(dr.have_test_image())
|
|
||||||
|
|
||||||
dr = doxrunner.Runner(argparse.Namespace(
|
|
||||||
rebuild=False,
|
|
||||||
rebuild_all=False,
|
|
||||||
path_map=None,
|
|
||||||
user_map=None))
|
|
||||||
dr._get_image_list = mock.MagicMock()
|
|
||||||
dr._get_image_list.return_value = []
|
|
||||||
self.assertFalse(dr.have_test_image())
|
|
@ -1,4 +0,0 @@
|
|||||||
pbr>=0.5.21,<1.0
|
|
||||||
|
|
||||||
PyYAML
|
|
||||||
six
|
|
46
setup.cfg
46
setup.cfg
@ -1,46 +0,0 @@
|
|||||||
[metadata]
|
|
||||||
name = dox
|
|
||||||
summary = dox runs tox descriptions in docker containers
|
|
||||||
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 :: 2.6
|
|
||||||
Programming Language :: Python :: 3
|
|
||||||
Programming Language :: Python :: 3.3
|
|
||||||
|
|
||||||
[entry_points]
|
|
||||||
console_scripts =
|
|
||||||
dox = dox.cmd:main
|
|
||||||
|
|
||||||
[build_sphinx]
|
|
||||||
source-dir = doc/source
|
|
||||||
build-dir = doc/build
|
|
||||||
all_files = 1
|
|
||||||
|
|
||||||
[upload_sphinx]
|
|
||||||
upload-dir = doc/build/html
|
|
||||||
|
|
||||||
[compile_catalog]
|
|
||||||
directory = dox/locale
|
|
||||||
domain = dox
|
|
||||||
|
|
||||||
[update_catalog]
|
|
||||||
domain = dox
|
|
||||||
output_dir = dox/locale
|
|
||||||
input_file = dox/locale/dox.pot
|
|
||||||
|
|
||||||
[extract_messages]
|
|
||||||
keywords = _ gettext ngettext l_ lazy_gettext
|
|
||||||
mapping_file = babel.cfg
|
|
||||||
output_file = dox/locale/dox.pot
|
|
22
setup.py
22
setup.py
@ -1,22 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# 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
|
|
||||||
|
|
||||||
setuptools.setup(
|
|
||||||
setup_requires=['pbr'],
|
|
||||||
pbr=True)
|
|
@ -1,12 +0,0 @@
|
|||||||
hacking>=0.5.6,<0.8
|
|
||||||
|
|
||||||
coverage>=3.6
|
|
||||||
discover
|
|
||||||
fixtures>=0.3.14
|
|
||||||
python-subunit
|
|
||||||
sphinx>=1.1.2
|
|
||||||
oslo.sphinx
|
|
||||||
testrepository>=0.0.17
|
|
||||||
testscenarios>=0.4,<0.5
|
|
||||||
testtools>=0.9.32
|
|
||||||
mock
|
|
@ -1,38 +0,0 @@
|
|||||||
#!/bin/bash -ex
|
|
||||||
# Copyright 2017 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
# implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
GROUP=docker
|
|
||||||
if [ $(id -gn) != ${GROUP} ]; then
|
|
||||||
exec sg ${GROUP} "$0 $*"
|
|
||||||
fi
|
|
||||||
|
|
||||||
### Build image with docker
|
|
||||||
IMAGES="infra/centos7 infra/trusty infra/xenial"
|
|
||||||
for IMAGE in $IMAGES; do
|
|
||||||
docker build dockerfiles/$IMAGE -t $IMAGE
|
|
||||||
done
|
|
||||||
|
|
||||||
docker images
|
|
||||||
|
|
||||||
# NOTE(pabelanger): Make sure we hash by ZUUL_COMMIT, so we know which tarball
|
|
||||||
# to download from secure worker.
|
|
||||||
DIST=$WORKSPACE/dist/$ZUUL_COMMIT
|
|
||||||
mkdir -p $DIST
|
|
||||||
|
|
||||||
### Save docker image for upload to tarballs.o.o
|
|
||||||
FILENAME=images.tar.gz
|
|
||||||
docker save $IMAGES | gzip -9 > $DIST/$FILENAME
|
|
||||||
shasum $DIST/$FILENAME > $DIST/$FILENAME.sha256
|
|
@ -1,18 +0,0 @@
|
|||||||
#!/bin/bash -ex
|
|
||||||
# Copyright 2017 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
# implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
sudo gpasswd -a ${USER} docker
|
|
||||||
sudo service docker restart
|
|
33
tox.ini
33
tox.ini
@ -1,33 +0,0 @@
|
|||||||
[tox]
|
|
||||||
minversion = 1.6
|
|
||||||
envlist = py27,py33,pep8
|
|
||||||
skipsdist = True
|
|
||||||
|
|
||||||
[testenv]
|
|
||||||
usedevelop = True
|
|
||||||
install_command = pip install -U {opts} {packages}
|
|
||||||
setenv =
|
|
||||||
VIRTUAL_ENV={envdir}
|
|
||||||
LANG=en_US.UTF-8
|
|
||||||
LANGUAGE=en_US:en
|
|
||||||
LC_ALL=C
|
|
||||||
deps = -r{toxinidir}/requirements.txt
|
|
||||||
-r{toxinidir}/test-requirements.txt
|
|
||||||
commands = python setup.py testr --slowest --testr-args='{posargs}'
|
|
||||||
|
|
||||||
[testenv:pep8]
|
|
||||||
commands = flake8
|
|
||||||
|
|
||||||
[testenv:venv]
|
|
||||||
commands = {posargs}
|
|
||||||
|
|
||||||
[testenv:cover]
|
|
||||||
commands = python setup.py testr --coverage --testr-args='{posargs}'
|
|
||||||
|
|
||||||
[flake8]
|
|
||||||
show-source = True
|
|
||||||
builtins = _
|
|
||||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build
|
|
||||||
|
|
||||||
[docker]
|
|
||||||
images = infra/trusty
|
|
Loading…
x
Reference in New Issue
Block a user