From c85ace9302f1652f2631fc771c3da672dc8d7e88 Mon Sep 17 00:00:00 2001 From: adriant Date: Fri, 29 Jan 2016 16:02:47 +1300 Subject: [PATCH] Lots of cleanup * deletion of unneeded heat code * lots of pep8 cleanup * renaming and shifting some stuff * adding/updating some licenses Change-Id: Idd28a0669d1833d4975f877b601c4b43f76eb5e1 --- AUTHORS~ | 2 + CONTRIBUTING.rst | 11 - MANIFEST.in | 7 +- MANIFEST.in~ | 10 + README.rst | 86 - doc/.gitignore | 2 - doc/Makefile | 90 - doc/source/conf.py | 266 - doc/source/ext/gen_ref.py | 59 - doc/source/index.rst | 75 - doc/source/man/heat.rst | 98 - openstack-common.conf | 8 - .../locale/python-stacktaskclient.pot | 0 run_tests.sh | 121 - setup.cfg | 32 +- setup.cfg~ | 35 + stacktaskclient/common/deployment_utils.py | 147 - stacktaskclient/common/environment_format.py | 52 - stacktaskclient/common/event_utils.py | 128 - stacktaskclient/common/template_format.py | 63 - stacktaskclient/common/template_utils.py | 224 - stacktaskclient/exc.py | 4 +- stacktaskclient/shell.py | 464 +- stacktaskclient/tests/__init__.py | 0 stacktaskclient/tests/functional/__init__.py | 0 stacktaskclient/tests/functional/base.py | 42 - .../tests/functional/hooks/post_test_hook.sh | 50 - .../functional/templates/heat_minimal.yaml | 18 - .../templates/heat_minimal_hot.yaml | 19 - .../tests/functional/test_readonly_heat.py | 102 - stacktaskclient/tests/unit/__init__.py | 0 stacktaskclient/tests/unit/fakes.py | 256 - stacktaskclient/tests/unit/test_actions.py | 108 - stacktaskclient/tests/unit/test_build_info.py | 41 - .../tests/unit/test_common_http.py | 861 ---- .../tests/unit/test_deployment_utils.py | 326 -- .../tests/unit/test_environment_format.py | 80 - .../tests/unit/test_event_utils.py | 130 - stacktaskclient/tests/unit/test_events.py | 153 - .../tests/unit/test_resource_types.py | 91 - stacktaskclient/tests/unit/test_resources.py | 210 - stacktaskclient/tests/unit/test_service.py | 59 - stacktaskclient/tests/unit/test_shell.py | 4581 ----------------- .../tests/unit/test_software_configs.py | 99 - .../tests/unit/test_software_deployments.py | 166 - stacktaskclient/tests/unit/test_stacks.py | 316 -- .../tests/unit/test_template_format.py | 50 - .../tests/unit/test_template_utils.py | 1017 ---- .../tests/unit/test_template_versions.py | 57 - stacktaskclient/tests/unit/test_utils.py | 278 - stacktaskclient/tests/unit/v1/__init__.py | 0 stacktaskclient/tests/unit/v1/test_hooks.py | 318 -- .../tests/unit/var/adopt_stack_data.json | 6 - .../tests/unit/var/minimal.template | 9 - stacktaskclient/v1/client.py | 3 +- stacktaskclient/v1/roles.py | 8 +- stacktaskclient/v1/shell.py | 34 +- stacktaskclient/v1/status.py | 14 + stacktaskclient/v1/tasks.py | 3 +- stacktaskclient/v1/tokens.py | 19 +- stacktaskclient/v1/users.py | 10 +- test-requirements.txt | 19 - tools/heat.bash_completion | 27 - tools/install_venv.py | 74 - tools/install_venv_common.py | 172 - tools/with_venv.sh | 10 - tox.ini | 51 - 67 files changed, 344 insertions(+), 11527 deletions(-) create mode 100644 AUTHORS~ delete mode 100644 CONTRIBUTING.rst create mode 100644 MANIFEST.in~ delete mode 100644 README.rst delete mode 100644 doc/.gitignore delete mode 100644 doc/Makefile delete mode 100644 doc/source/conf.py delete mode 100644 doc/source/ext/gen_ref.py delete mode 100644 doc/source/index.rst delete mode 100644 doc/source/man/heat.rst delete mode 100644 openstack-common.conf delete mode 100644 python-stacktaskclient/locale/python-stacktaskclient.pot delete mode 100755 run_tests.sh create mode 100644 setup.cfg~ delete mode 100644 stacktaskclient/common/deployment_utils.py delete mode 100644 stacktaskclient/common/environment_format.py delete mode 100644 stacktaskclient/common/event_utils.py delete mode 100644 stacktaskclient/common/template_format.py delete mode 100644 stacktaskclient/common/template_utils.py delete mode 100644 stacktaskclient/tests/__init__.py delete mode 100644 stacktaskclient/tests/functional/__init__.py delete mode 100644 stacktaskclient/tests/functional/base.py delete mode 100755 stacktaskclient/tests/functional/hooks/post_test_hook.sh delete mode 100644 stacktaskclient/tests/functional/templates/heat_minimal.yaml delete mode 100644 stacktaskclient/tests/functional/templates/heat_minimal_hot.yaml delete mode 100644 stacktaskclient/tests/functional/test_readonly_heat.py delete mode 100644 stacktaskclient/tests/unit/__init__.py delete mode 100644 stacktaskclient/tests/unit/fakes.py delete mode 100644 stacktaskclient/tests/unit/test_actions.py delete mode 100644 stacktaskclient/tests/unit/test_build_info.py delete mode 100644 stacktaskclient/tests/unit/test_common_http.py delete mode 100644 stacktaskclient/tests/unit/test_deployment_utils.py delete mode 100644 stacktaskclient/tests/unit/test_environment_format.py delete mode 100644 stacktaskclient/tests/unit/test_event_utils.py delete mode 100644 stacktaskclient/tests/unit/test_events.py delete mode 100644 stacktaskclient/tests/unit/test_resource_types.py delete mode 100644 stacktaskclient/tests/unit/test_resources.py delete mode 100644 stacktaskclient/tests/unit/test_service.py delete mode 100644 stacktaskclient/tests/unit/test_shell.py delete mode 100644 stacktaskclient/tests/unit/test_software_configs.py delete mode 100644 stacktaskclient/tests/unit/test_software_deployments.py delete mode 100644 stacktaskclient/tests/unit/test_stacks.py delete mode 100644 stacktaskclient/tests/unit/test_template_format.py delete mode 100644 stacktaskclient/tests/unit/test_template_utils.py delete mode 100644 stacktaskclient/tests/unit/test_template_versions.py delete mode 100644 stacktaskclient/tests/unit/test_utils.py delete mode 100644 stacktaskclient/tests/unit/v1/__init__.py delete mode 100644 stacktaskclient/tests/unit/v1/test_hooks.py delete mode 100644 stacktaskclient/tests/unit/var/adopt_stack_data.json delete mode 100644 stacktaskclient/tests/unit/var/minimal.template delete mode 100644 test-requirements.txt delete mode 100644 tools/heat.bash_completion delete mode 100644 tools/install_venv.py delete mode 100644 tools/install_venv_common.py delete mode 100755 tools/with_venv.sh delete mode 100644 tox.ini diff --git a/AUTHORS~ b/AUTHORS~ new file mode 100644 index 0000000..c2a7e17 --- /dev/null +++ b/AUTHORS~ @@ -0,0 +1,2 @@ +Dale Smith +adriant diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst deleted file mode 100644 index 46249a2..0000000 --- a/CONTRIBUTING.rst +++ /dev/null @@ -1,11 +0,0 @@ -If you would like to contribute to the development of OpenStack, -you must follow the steps documented at: - - http://docs.openstack.org/infra/manual/developers.html#development-workflow - -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 - diff --git a/MANIFEST.in b/MANIFEST.in index 44f7c2e..5f2403e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,10 +1,5 @@ include AUTHORS include babel.cfg include LICENSE -include README.rst +include README.md include ChangeLog -include tox.ini -include .testr.conf -recursive-include doc * -recursive-include tools * -recursive-include python-stacktaskclient *.po *.pot diff --git a/MANIFEST.in~ b/MANIFEST.in~ new file mode 100644 index 0000000..44f7c2e --- /dev/null +++ b/MANIFEST.in~ @@ -0,0 +1,10 @@ +include AUTHORS +include babel.cfg +include LICENSE +include README.rst +include ChangeLog +include tox.ini +include .testr.conf +recursive-include doc * +recursive-include tools * +recursive-include python-stacktaskclient *.po *.pot diff --git a/README.rst b/README.rst deleted file mode 100644 index 12deab6..0000000 --- a/README.rst +++ /dev/null @@ -1,86 +0,0 @@ -================= -python-stacktaskclient -================= - -OpenStack Orchestration API Client Library - -This is a client library for Stacktask built on the Catalyst Stacktask API. It -provides a Python API (the ``stacktaskclient`` module) and a command-line tool -(``stacktask``). - -* Free software: Apache license -* Documentation: http://docs.openstack.org/developer/python-heatclient -* Source: http://git.openstack.org/cgit/openstack/python-heatclient -* Bugs: http://bugs.launchpad.net/python-heatclient - -=================== -Basic setup from source -=================== -python tools/install_venv.py -source .tox/venv/bin/activate -source openrc.sh - -Verify: -'pip list' should give: python-stacktaskclient (0.6.1.dev63, /home/dale/dale/dev/openstack/python-heatclient) -'which stacktask' should give: /home/dale/dale/dev/openstack/python-heatclient/.tox/venv/bin/stacktask -'stacktask user-tenant-list' should give list of users. - -=== -Add the registration endpoint -=== -Either: -keystone endpoint-create --service registration --publicurl http://192.168.122.160:8040/v1 --internalurl http://192.168.122.160:8040/v1 --region RegionOne -Or: -Add '--bypass-url http://192.168.122.160:8040/v1' to all stacktask calls - - -=================== -Usage examples -=== -(NYI) = Not Yet Implemented -(NYI cli) = Not Yet Implemented, client implementation only. -(NYI be) = Not Yet Implemented, backend service feature required. -=================== - -List all current users in your tenant: -stacktask user-list - -Send/Resend invite for a user to use your tenant: -(NYI cli) stacktask user-invite-send --user-email dale@catalyst-eu.net - -List pending invites for members to your tenant: -(NYI be) stacktask user-invite-list - -Cancel invite to a user: -(NYI be) stacktask user-invite-cancel --user-email dale@catalyst-eu.net - -List pending actions(admin only) -(NYI be) stacktask task-list - -Cancel a token(admin only?): -(NYI be) stacktask task-cancel --token-id - -Get information about an email token(returns params required, action type): -(NYI cli) stacktask task-get --token-id - -Submit an action token(include any parameters): -(NYI cli) stacktask task-submit --token-id --parameters password=123456 - -Approve a pending action(admin only): -(NYI be) stacktask task-approve --token-id - -Allow a user to invite others: -stacktask managed-role-list -(NYI be) stacktask user-role-add --role project_moderator --user dale@catalyst-eu.net - -Allow a user to administrate your tenant: -stacktask managed-role-list -(NYI be) stacktask user-role-add --role project_moderator --user dale@catalyst-eu.net -(NYI be) stacktask user-role-add --role project_owner --user dale@catalyst-eu.net - -Remove user from your tenant: -stacktask managed-role-list -(NYI be) stacktask user-role-remove --role project_owner --user dale@catalyst-eu.net -(NYI be) stacktask user-role-remove --role project_moderator --user dale@catalyst-eu.net -(NYI be) stacktask user-role-remove --role _member_ --user dale@catalyst-eu.net -(NYI be) stacktask user-role-remove --role Member --user dale@catalyst-eu.net diff --git a/doc/.gitignore b/doc/.gitignore deleted file mode 100644 index 8e0be80..0000000 --- a/doc/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -build/ -source/ref/ diff --git a/doc/Makefile b/doc/Makefile deleted file mode 100644 index 6239df3..0000000 --- a/doc/Makefile +++ /dev/null @@ -1,90 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SPHINXSOURCE = source -PAPER = -BUILDDIR = build - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SPHINXSOURCE) - -.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/python-heatclient.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/python-heatclient.qhc" - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ - "run these through (pdf)latex." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/doc/source/conf.py b/doc/source/conf.py deleted file mode 100644 index e611dbe..0000000 --- a/doc/source/conf.py +++ /dev/null @@ -1,266 +0,0 @@ -# -*- coding: utf-8 -*- -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# python-heatclient documentation build configuration file, created by -# sphinx-quickstart on Sun Dec 6 14:19:25 2009. -# -# This file is execfile()d with the current directory set to its containing -# dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import os - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.append(os.path.abspath('.')) - -exec(open(os.path.join("ext", "gen_ref.py")).read()) -# -- 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', 'oslosphinx'] - -# Add any paths that contain templates here, relative to this directory. -if os.getenv('HUDSON_PUBLISH_DOCS'): - templates_path = ['_ga', '_templates'] -else: - templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = 'python-heatclient' -copyright = 'OpenStack Contributors' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '2.13' -# The full version, including alpha/beta/rc tags. -release = '2.13.0' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['**/#*', '**~', '**/#*#'] - -# The reST default role (used for this markup: `text`) -# to use for all documents. -#default_role = None - -# 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 - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - -primary_domain = 'py' -nitpicky = False - - -# -- Options for HTML output -------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# html_theme_path = ['.'] -# html_theme = '_theme' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -html_theme_options = { - "nosidebar": "false" -} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' -git_cmd = "git log --pretty=format:'%ad, commit %h' --date=local -n1" -html_last_updated_fmt = os.popen(git_cmd).read() - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'python-heatclientdoc' - - -# -- Options for LaTeX output ------------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - #'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - #'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - #'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]) -latex_documents = [ - ('index', 'python-heatclient.tex', 'python-heatclient Documentation', - u'OpenStack Foundation', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output ------------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('man/heat', 'heat', - u'Command line access to the heat project.', - [u'Heat Developers'], 1), -] - -# If true, show URL addresses after external links. -#man_show_urls = False - - -# -- Options for Texinfo output ----------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'Heat', u'Heat Documentation', - u'Heat Developers', 'Heat', 'One line description of project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' diff --git a/doc/source/ext/gen_ref.py b/doc/source/ext/gen_ref.py deleted file mode 100644 index 15ff818..0000000 --- a/doc/source/ext/gen_ref.py +++ /dev/null @@ -1,59 +0,0 @@ -# -*- coding: utf-8 -*- -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import os -import sys - -BASE_DIR = os.path.dirname(os.path.abspath(__file__)) -ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", "..", "..")) - -sys.path.insert(0, ROOT) -sys.path.insert(0, BASE_DIR) - - -def gen_ref(ver, title, names): - refdir = os.path.join(BASE_DIR, "ref") - pkg = "heatclient" - if ver: - pkg = "%s.%s" % (pkg, ver) - refdir = os.path.join(refdir, ver) - if not os.path.exists(refdir): - os.makedirs(refdir) - idxpath = os.path.join(refdir, "index.rst") - with open(idxpath, "w") as idx: - idx.write(("%(title)s\n" - "%(signs)s\n" - "\n" - ".. toctree::\n" - " :maxdepth: 1\n" - "\n") % {"title": title, "signs": "=" * len(title)}) - for name in names: - idx.write(" %s\n" % name) - rstpath = os.path.join(refdir, "%s.rst" % name) - with open(rstpath, "w") as rst: - rst.write(("%(title)s\n" - "%(signs)s\n" - "\n" - ".. automodule:: %(pkg)s.%(name)s\n" - " :members:\n" - " :undoc-members:\n" - " :show-inheritance:\n" - " :noindex:\n") - % {"title": name.capitalize(), - "signs": "=" * len(name), - "pkg": pkg, "name": name}) - -gen_ref("", "Client Reference", ["client", "exc"]) -gen_ref("v1", "Version 1 API Reference", - ["stacks", "resources", "events", "actions", - "software_configs", "software_deployments"]) diff --git a/doc/source/index.rst b/doc/source/index.rst deleted file mode 100644 index ec0bdc5..0000000 --- a/doc/source/index.rst +++ /dev/null @@ -1,75 +0,0 @@ -Python bindings to the OpenStack Heat API -========================================= - -This is a client for OpenStack Heat API. There's a Python API -(the :mod:`heatclient` module), and a command-line script -(installed as :program:`heat`). - -Python API -========== - -In order to use the python api directly, you must first obtain an auth -token and identify which endpoint you wish to speak to:: - - >>> tenant_id = 'b363706f891f48019483f8bd6503c54b' - >>> heat_url = 'http://heat.example.org:8004/v1/%s' % tenant_id - >>> auth_token = '3bcc3d3a03f44e3d8377f9247b0ad155' - -Once you have done so, you can use the API like so:: - - >>> from heatclient.client import Client - >>> heat = Client('1', endpoint=heat_url, token=auth_token) - -Reference ---------- - -.. toctree:: - :maxdepth: 1 - - ref/index - ref/v1/index - -Command-line Tool -================= - -In order to use the CLI, you must provide your OpenStack username, -password, tenant, and auth endpoint. Use the corresponding -configuration options (``--os-username``, ``--os-password``, -``--os-tenant-id``, and ``--os-auth-url``) or set them in environment -variables:: - - export OS_USERNAME=user - export OS_PASSWORD=pass - export OS_TENANT_ID=b363706f891f48019483f8bd6503c54b - export OS_AUTH_URL=http://auth.example.com:5000/v2.0 - -The command line tool will attempt to reauthenticate using your -provided credentials for every request. You can override this behavior -by manually supplying an auth token using ``--heat-url`` and -``--os-auth-token``. You can alternatively set these environment -variables:: - - export HEAT_URL=http://heat.example.org:8004/v1/b363706f891f48019483f8bd6503c54b - export OS_AUTH_TOKEN=3bcc3d3a03f44e3d8377f9247b0ad155 - -Once you've configured your authentication parameters, you can run -``heat help`` to see a complete listing of available commands. - -Man Pages -========= - -.. toctree:: - :maxdepth: 1 - - man/heat - -Contributing -============ - -Code is hosted `on GitHub`_. Submit bugs to the Heat project on -`Launchpad`_. Submit code to the openstack/python-heatclient project -using `Gerrit`_. - -.. _on GitHub: https://github.com/openstack/python-heatclient -.. _Launchpad: https://launchpad.net/python-heatclient -.. _Gerrit: http://docs.openstack.org/infra/manual/developers.html#development-workflow diff --git a/doc/source/man/heat.rst b/doc/source/man/heat.rst deleted file mode 100644 index 1b29111..0000000 --- a/doc/source/man/heat.rst +++ /dev/null @@ -1,98 +0,0 @@ -==== -heat -==== - -.. program:: heat - -SYNOPSIS -======== - - `heat` [options] [command-options] - - `heat help` - - `heat help` - - -DESCRIPTION -=========== - -`heat` is a command line client for controlling OpenStack Heat. - -Before the `heat` command is issued, ensure the environment contains -the necessary variables so that the CLI can pass user credentials to -the server. -See `Getting Credentials for a CLI` section of `OpenStack CLI Guide` -for more info. - - -OPTIONS -======= - -To get a list of available commands and options run:: - - heat help - -To get usage and options of a command run:: - - heat help - - -EXAMPLES -======== - -Get information about stack-create command:: - - heat help stack-create - -List available stacks:: - - heat stack-list - -List available resources in a stack:: - - heat resource-list - -Create a stack:: - - heat stack-create mystack -f some-template.yaml -P "KeyName=mine" - -View stack information:: - - heat stack-show mystack - -List stack outputs:: - - heat output-list - -Show the value of a single output:: - - heat output-show - -List events:: - - heat event-list mystack - -Delete a stack:: - - heat stack-delete mystack - -Abandon a stack:: - - heat stack-abandon mystack - -Adopt a stack :: - - heat stack-adopt -a mystack - -List heat-engines running status :: - - heat service-list - -Note: stack-adopt and stack-abandon commands are not available by default. -Please ask your Openstack operator to enable this feature. - -BUGS -==== - -Heat client is hosted in Launchpad so you can view current bugs at https://bugs.launchpad.net/python-heatclient/. diff --git a/openstack-common.conf b/openstack-common.conf deleted file mode 100644 index 2902223..0000000 --- a/openstack-common.conf +++ /dev/null @@ -1,8 +0,0 @@ -[DEFAULT] - -# The list of modules to copy from openstack-common -modules=apiclient -module=cliutils - -# The base module to hold the copy of openstack.common -base=stacktaskclient diff --git a/python-stacktaskclient/locale/python-stacktaskclient.pot b/python-stacktaskclient/locale/python-stacktaskclient.pot deleted file mode 100644 index e69de29..0000000 diff --git a/run_tests.sh b/run_tests.sh deleted file mode 100755 index 2981e7a..0000000 --- a/run_tests.sh +++ /dev/null @@ -1,121 +0,0 @@ -#!/bin/bash - -BASE_DIR=`dirname $0` - -function usage { - echo "Usage: $0 [OPTION]..." - echo "Run stacktaskclient test suite(s)" - echo "" - echo " -V, --virtual-env Use virtualenv. Install automatically if not present." - echo " (Default is to run tests in local environment)" - echo " -F, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added." - echo " -f, --func Functional tests have been removed." - echo " -u, --unit Run unit tests (default when nothing specified)" - echo " -p, --pep8 Run pep8 tests" - echo " --all Run pep8 and unit tests" - echo " -c, --coverage Generate coverage report" - echo " -d, --debug Run tests with testtools instead of testr. This allows you to use the debugger." - echo " -h, --help Print this usage message" - exit -} - -# must not assign -a as an option, needed for selecting custom attributes -no_venv=1 -function process_option { - case "$1" in - -V|--virtual-env) no_venv=0;; - -F|--force) force=1;; - -f|--func) test_func=1;; - -u|--unit) test_unit=1;; - -p|--pep8) test_pep8=1;; - --all) test_unit=1; test_pep8=1;; - -c|--coverage) coverage=1;; - -d|--debug) debug=1;; - -h|--help) usage;; - *) args="$args $1"; test_unit=1;; - esac -} - -venv=.venv -with_venv=tools/with_venv.sh -wrapper="" -debug=0 - -function run_tests { - echo 'Running tests' - - if [ $debug -eq 1 ]; then - echo "Debugging..." - if [ "$args" = "" ]; then - # Default to running all tests if specific test is not - # provided. - testrargs="discover ./stacktaskclient/tests" - fi - ${wrapper} python -m testtools.run $args $testrargs - - # Short circuit because all of the testr and coverage stuff - # below does not make sense when running testtools.run for - # debugging purposes. - return $? - fi - - # Just run the test suites in current environment - if [ -n "$args" ] ; then - args="-t $args" - fi - ${wrapper} python setup.py testr --slowest $args -} - -function run_pep8 { - echo "Running flake8..." - bash -c "${wrapper} flake8" -} - -# run unit tests with pep8 when no arguments are specified -# otherwise process CLI options -if [[ $# == 0 ]]; then - test_pep8=1 - test_unit=1 -else - for arg in "$@"; do - process_option $arg - done -fi - -if [ "$no_venv" == 0 ] -then - # Remove the virtual environment if --force used - if [ "$force" == 1 ]; then - echo "Cleaning virtualenv..." - rm -rf ${venv} - fi - if [ -e ${venv} ]; then - wrapper="${with_venv}" - else - # Automatically install the virtualenv - python tools/install_venv.py - wrapper="${with_venv}" - fi -fi - -result=0 - -# If functional or unit tests have been selected, run them -if [ "$test_unit" == 1 ] || [ "$debug" == 1 ] ; then - run_tests - result=$? -fi - -# Run pep8 if it was selected -if [ "$test_pep8" == 1 ]; then - run_pep8 -fi - -# Generate coverage report -if [ "$coverage" == 1 ]; then - echo "Generating coverage report in ./cover" - ${wrapper} python setup.py testr --coverage --slowest - ${wrapper} python -m coverage report --show-missing -fi - -exit $result diff --git a/setup.cfg b/setup.cfg index 0d50166..e11e059 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,11 +1,11 @@ [metadata] name = python-stacktaskclient -summary = OpenStack Orchestration API Client Library +summary = StackTask API Client Library description-file = - README.rst -author = OpenStack -author-email = openstack-dev@lists.openstack.org -home-page = http://www.openstack.org/ + README.md +author = Dale Smith +author-email = dale@catalyst-eu.net +home-page = https://github.com/catalyst/stacktask-client classifier = Environment :: OpenStack Intended Audience :: Information Technology @@ -16,8 +16,6 @@ classifier = Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 2.6 - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.4 [files] packages = @@ -31,27 +29,7 @@ console_scripts = setup-hooks = pbr.hooks.setup_hook -[build_sphinx] -source-dir = doc/source -build-dir = doc/build -all_files = 1 - -[upload_sphinx] -upload-dir = doc/build/html - [wheel] universal = 1 -[extract_messages] -keywords = _ gettext ngettext l_ lazy_gettext -mapping_file = babel.cfg -output_file = python-stacktaskclient/locale/python-stacktaskclient.pot -[compile_catalog] -directory = python-stacktaskclient/locale -domain = python-stacktaskclient - -[update_catalog] -domain = python-stacktaskclient -output_dir = python-stacktaskclient/locale -input_file = python-stacktaskclient/locale/python-stacktaskclient.pot diff --git a/setup.cfg~ b/setup.cfg~ new file mode 100644 index 0000000..e11e059 --- /dev/null +++ b/setup.cfg~ @@ -0,0 +1,35 @@ +[metadata] +name = python-stacktaskclient +summary = StackTask API Client Library +description-file = + README.md +author = Dale Smith +author-email = dale@catalyst-eu.net +home-page = https://github.com/catalyst/stacktask-client +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 + +[files] +packages = + stacktaskclient + +[entry_points] +console_scripts = + stacktask = stacktaskclient.shell:main + +[global] +setup-hooks = + pbr.hooks.setup_hook + +[wheel] +universal = 1 + + diff --git a/stacktaskclient/common/deployment_utils.py b/stacktaskclient/common/deployment_utils.py deleted file mode 100644 index c35691c..0000000 --- a/stacktaskclient/common/deployment_utils.py +++ /dev/null @@ -1,147 +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 copy -import six -from six.moves.urllib import parse as urlparse -from swiftclient import client as sc -from swiftclient import utils as swiftclient_utils -import uuid - -from stacktaskclient import exc -from stacktaskclient.openstack.common._i18n import _ - - -def build_derived_config_params(action, source, name, input_values, - server_id, signal_transport, signal_id=None): - - input_values = input_values or {} - inputs = copy.deepcopy(source.get('inputs')) or [] - - for inp in inputs: - input_key = inp['name'] - inp['value'] = input_values.pop(input_key, inp.get('default')) - - # for any input values that do not have a declared input, add - # a derived declared input so that they can be used as config - # inputs - for inpk, inpv in input_values.items(): - inputs.append({ - 'name': inpk, - 'type': 'String', - 'value': inpv - }) - - inputs.extend([{ - 'name': 'deploy_server_id', - 'description': _('ID of the server being deployed to'), - 'type': 'String', - 'value': server_id - }, { - 'name': 'deploy_action', - 'description': _('Name of the current action being deployed'), - 'type': 'String', - 'value': action - }, { - 'name': 'deploy_signal_transport', - 'description': _('How the server should signal to stacktask with ' - 'the deployment output values.'), - 'type': 'String', - 'value': signal_transport - }]) - - if signal_transport == 'TEMP_URL_SIGNAL': - inputs.append({ - 'name': 'deploy_signal_id', - 'description': _('ID of signal to use for signaling ' - 'output values'), - 'type': 'String', - 'value': signal_id - }) - inputs.append({ - 'name': 'deploy_signal_verb', - 'description': _('HTTP verb to use for signaling ' - 'output values'), - 'type': 'String', - 'value': 'PUT' - }) - elif signal_transport != 'NO_SIGNAL': - raise exc.CommandError( - _('Unsupported signal transport %s') % signal_transport) - - return { - 'group': source.get('group') or 'Heat::Ungrouped', - 'config': source.get('config') or '', - 'options': source.get('options') or {}, - 'inputs': inputs, - 'outputs': source.get('outputs') or [], - 'name': name - } - - -def create_temp_url(swift_client, name, timeout, container=None): - - container = container or '%(name)s-%(uuid)s' % { - 'name': name, 'uuid': uuid.uuid4()} - object_name = str(uuid.uuid4()) - - swift_client.put_container(container) - key_header = 'x-account-meta-temp-url-key' - if key_header not in swift_client.head_account(): - swift_client.post_account({ - key_header: six.text_type(uuid.uuid4())[:32]}) - - key = swift_client.head_account()[key_header] - project_path = swift_client.url.split('/')[-1] - path = '/v1/%s/%s/%s' % (project_path, container, object_name) - timeout_secs = timeout * 60 - tempurl = swiftclient_utils.generate_temp_url(path, timeout_secs, key, - 'PUT') - sw_url = urlparse.urlparse(swift_client.url) - put_url = '%s://%s%s' % (sw_url.scheme, sw_url.netloc, tempurl) - swift_client.put_object(container, object_name, '') - return put_url - - -def build_signal_id(hc, args): - if args.signal_transport != 'TEMP_URL_SIGNAL': - return - - if args.os_no_client_auth: - raise exc.CommandError(_( - 'Cannot use --os-no-client-auth, auth required to create ' - 'a Swift TempURL.')) - swift_client = create_swift_client( - hc.http_client.auth, hc.http_client.session, args) - - return create_temp_url(swift_client, args.name, args.timeout) - - -def create_swift_client(auth, session, args): - auth_token = auth.get_token(session) - endpoint = auth.get_endpoint(session, - service_type='object-store', - region_name=args.os_region_name) - project_name = args.os_project_name or args.os_tenant_name - swift_args = { - 'auth_version': '2.0', - 'tenant_name': project_name, - 'user': args.os_username, - 'key': None, - 'authurl': None, - 'preauthtoken': auth_token, - 'preauthurl': endpoint, - 'cacert': args.os_cacert, - 'insecure': args.insecure - } - - return sc.Connection(**swift_args) diff --git a/stacktaskclient/common/environment_format.py b/stacktaskclient/common/environment_format.py deleted file mode 100644 index cab9299..0000000 --- a/stacktaskclient/common/environment_format.py +++ /dev/null @@ -1,52 +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. - -from stacktaskclient.common import template_format -from stacktaskclient.openstack.common._i18n import _ - -import yaml - - -SECTIONS = (PARAMETER_DEFAULTS, PARAMETERS, RESOURCE_REGISTRY) = \ - ('parameter_defaults', 'parameters', 'resource_registry') - - -def parse(env_str): - '''Takes a string and returns a dict containing the parsed structure. - - This includes determination of whether the string is using the - YAML format. - ''' - try: - env = yaml.load(env_str, Loader=template_format.yaml_loader) - except yaml.YAMLError as yea: - raise ValueError(yea) - else: - if env is None: - env = {} - elif not isinstance(env, dict): - raise ValueError(_('The environment is not a valid ' - 'YAML mapping data type.')) - - for param in env: - if param not in SECTIONS: - raise ValueError(_('environment has wrong section "%s"') % param) - - return env - - -def default_for_missing(env): - '''Checks a parsed environment for missing sections. - ''' - for param in SECTIONS: - if param not in env: - env[param] = {} diff --git a/stacktaskclient/common/event_utils.py b/stacktaskclient/common/event_utils.py deleted file mode 100644 index fde4704..0000000 --- a/stacktaskclient/common/event_utils.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright 2015 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. - -from stacktaskclient.common import utils -import stacktaskclient.exc as exc - -from stacktaskclient.openstack.common._i18n import _ - - -def get_hook_events(hc, stack_id, event_args, nested_depth=0, - hook_type='pre-create'): - if hook_type == 'pre-create': - stack_action_reason = 'Stack CREATE started' - hook_event_reason = 'CREATE paused until Hook pre-create is cleared' - hook_clear_event_reason = 'Hook pre-create is cleared' - elif hook_type == 'pre-update': - stack_action_reason = 'Stack UPDATE started' - hook_event_reason = 'UPDATE paused until Hook pre-update is cleared' - hook_clear_event_reason = 'Hook pre-update is cleared' - else: - raise exc.CommandError(_('Unexpected hook type %s') % hook_type) - - events = get_events(hc, stack_id=stack_id, event_args=event_args, - nested_depth=nested_depth) - - # Get the most recent event associated with this action, which gives us the - # event when we moved into IN_PROGRESS for the hooks we're interested in. - stack_name = stack_id.split("/")[0] - action_start_event = [e for e in enumerate(events) - if e[1].resource_status_reason == stack_action_reason - and e[1].stack_name == stack_name][-1] - # Slice the events with the index from the enumerate - action_start_index = action_start_event[0] - events = events[action_start_index:] - - # Get hook events still pending by some list filtering/comparison - # We build a map hook events per-resource, and remove any event - # for which there is a corresponding hook-clear event. - resource_event_map = {} - for e in events: - stack_resource = (e.stack_name, e.resource_name) - if e.resource_status_reason == hook_event_reason: - resource_event_map[(e.stack_name, e.resource_name)] = e - elif e.resource_status_reason == hook_clear_event_reason: - if resource_event_map.get(stack_resource): - del(resource_event_map[(e.stack_name, e.resource_name)]) - return list(resource_event_map.values()) - - -def get_events(hc, stack_id, event_args, nested_depth=0, - marker=None, limit=None): - events = _get_stack_events(hc, stack_id, event_args) - if nested_depth > 0: - events.extend(_get_nested_events(hc, nested_depth, - stack_id, event_args)) - # Because there have been multiple stacks events mangled into - # one list, we need to sort before passing to print_list - # Note we can't use the prettytable sortby_index here, because - # the "start" option doesn't allow post-sort slicing, which - # will be needed to make "--marker" work for nested_depth lists - events.sort(key=lambda x: x.event_time) - - # Slice the list if marker is specified - if marker: - marker_index = [e.id for e in events].index(marker) - events = events[marker_index:] - - # Slice the list if limit is specified - if limit: - limit_index = min(int(limit), len(events)) - events = events[:limit_index] - return events - - -def _get_nested_ids(hc, stack_id): - nested_ids = [] - try: - resources = hc.resources.list(stack_id=stack_id) - except exc.HTTPNotFound: - raise exc.CommandError(_('Stack not found: %s') % stack_id) - for r in resources: - nested_id = utils.resource_nested_identifier(r) - if nested_id: - nested_ids.append(nested_id) - return nested_ids - - -def _get_nested_events(hc, nested_depth, stack_id, event_args): - # FIXME(shardy): this is very inefficient, we should add nested_depth to - # the event_list API in a future stacktask version, but this will be required - # until kilo stacktask is EOL. - nested_ids = _get_nested_ids(hc, stack_id) - nested_events = [] - for n_id in nested_ids: - stack_events = _get_stack_events(hc, n_id, event_args) - if stack_events: - nested_events.extend(stack_events) - if nested_depth > 1: - next_depth = nested_depth - 1 - nested_events.extend(_get_nested_events( - hc, next_depth, n_id, event_args)) - return nested_events - - -def _get_stack_events(hc, stack_id, event_args): - event_args['stack_id'] = stack_id - try: - events = hc.events.list(**event_args) - except exc.HTTPNotFound as ex: - # it could be the stack or resource that is not found - # just use the message that the server sent us. - raise exc.CommandError(str(ex)) - else: - # Show which stack the event comes from (for nested events) - for e in events: - e.stack_name = stack_id.split("/")[0] - return events diff --git a/stacktaskclient/common/template_format.py b/stacktaskclient/common/template_format.py deleted file mode 100644 index 6ae80fc..0000000 --- a/stacktaskclient/common/template_format.py +++ /dev/null @@ -1,63 +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 json -import yaml - -from stacktaskclient.openstack.common._i18n import _ - -if hasattr(yaml, 'CSafeLoader'): - yaml_loader = yaml.CSafeLoader -else: - yaml_loader = yaml.SafeLoader - -if hasattr(yaml, 'CSafeDumper'): - yaml_dumper = yaml.CSafeDumper -else: - yaml_dumper = yaml.SafeDumper - - -def _construct_yaml_str(self, node): - # Override the default string handling function - # to always return unicode objects - return self.construct_scalar(node) -yaml_loader.add_constructor(u'tag:yaml.org,2002:str', _construct_yaml_str) -# Unquoted dates like 2013-05-23 in yaml files get loaded as objects of type -# datetime.data which causes problems in API layer when being processed by -# openstack.common.jsonutils. Therefore, make unicode string out of timestamps -# until jsonutils can handle dates. -yaml_loader.add_constructor(u'tag:yaml.org,2002:timestamp', - _construct_yaml_str) - - -def parse(tmpl_str): - '''Takes a string and returns a dict containing the parsed structure. - - This includes determination of whether the string is using the - JSON or YAML format. - ''' - if tmpl_str.startswith('{'): - tpl = json.loads(tmpl_str) - else: - try: - tpl = yaml.load(tmpl_str, Loader=yaml_loader) - except yaml.YAMLError as yea: - raise ValueError(yea) - else: - if tpl is None: - tpl = {} - # Looking for supported version keys in the loaded template - if not ('HeatTemplateFormatVersion' in tpl - or 'heat_template_version' in tpl - or 'AWSTemplateFormatVersion' in tpl): - raise ValueError(_("Template format version not found.")) - return tpl diff --git a/stacktaskclient/common/template_utils.py b/stacktaskclient/common/template_utils.py deleted file mode 100644 index 669fa18..0000000 --- a/stacktaskclient/common/template_utils.py +++ /dev/null @@ -1,224 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import collections -from oslo_serialization import jsonutils -import six -from six.moves.urllib import parse -from six.moves.urllib import request - -from stacktaskclient.common import environment_format -from stacktaskclient.common import template_format -from stacktaskclient.common import utils -from stacktaskclient import exc -from stacktaskclient.openstack.common._i18n import _ - - -def get_template_contents(template_file=None, template_url=None, - template_object=None, object_request=None, - files=None, existing=False): - - # Transform a bare file path to a file:// URL. - if template_file: - template_url = utils.normalise_file_path_to_url(template_file) - - if template_url: - tpl = request.urlopen(template_url).read() - - elif template_object: - template_url = template_object - tpl = object_request and object_request('GET', - template_object) - elif existing: - return {}, None - else: - raise exc.CommandError(_('Need to specify exactly one of ' - '%(arg1)s, %(arg2)s or %(arg3)s') % - { - 'arg1': '--template-file', - 'arg2': '--template-url', - 'arg3': '--template-object' - }) - - if not tpl: - raise exc.CommandError(_('Could not fetch template from %s') - % template_url) - - try: - if isinstance(tpl, six.binary_type): - tpl = tpl.decode('utf-8') - template = template_format.parse(tpl) - except ValueError as e: - raise exc.CommandError(_('Error parsing template %(url)s %(error)s') % - {'url': template_url, 'error': e}) - - tmpl_base_url = utils.base_url_for_url(template_url) - if files is None: - files = {} - resolve_template_get_files(template, files, tmpl_base_url) - return files, template - - -def resolve_template_get_files(template, files, template_base_url): - - def ignore_if(key, value): - if key != 'get_file' and key != 'type': - return True - if not isinstance(value, six.string_types): - return True - if (key == 'type' and - not value.endswith(('.yaml', '.template'))): - return True - return False - - def recurse_if(value): - return isinstance(value, (dict, list)) - - get_file_contents(template, files, template_base_url, - ignore_if, recurse_if) - - -def is_template(file_content): - try: - if isinstance(file_content, six.binary_type): - file_content = file_content.decode('utf-8') - template_format.parse(file_content) - except (ValueError, TypeError): - return False - return True - - -def get_file_contents(from_data, files, base_url=None, - ignore_if=None, recurse_if=None): - - if recurse_if and recurse_if(from_data): - if isinstance(from_data, dict): - recurse_data = six.itervalues(from_data) - else: - recurse_data = from_data - for value in recurse_data: - get_file_contents(value, files, base_url, ignore_if, recurse_if) - - if isinstance(from_data, dict): - for key, value in iter(from_data.items()): - if ignore_if and ignore_if(key, value): - continue - - if base_url and not base_url.endswith('/'): - base_url = base_url + '/' - - str_url = parse.urljoin(base_url, value) - if str_url not in files: - file_content = utils.read_url_content(str_url) - if is_template(file_content): - template = get_template_contents( - template_url=str_url, files=files)[1] - file_content = jsonutils.dumps(template) - files[str_url] = file_content - # replace the data value with the normalised absolute URL - from_data[key] = str_url - - -def read_url_content(url): - '''DEPRECATED! Use 'utils.read_url_content' instead.''' - return utils.read_url_content(url) - - -def base_url_for_url(url): - '''DEPRECATED! Use 'utils.base_url_for_url' instead.''' - return utils.base_url_for_url(url) - - -def normalise_file_path_to_url(path): - '''DEPRECATED! Use 'utils.normalise_file_path_to_url' instead.''' - return utils.normalise_file_path_to_url(path) - - -def deep_update(old, new): - '''Merge nested dictionaries.''' - for k, v in new.items(): - if isinstance(v, collections.Mapping): - r = deep_update(old.get(k, {}), v) - old[k] = r - else: - old[k] = new[k] - return old - - -def process_multiple_environments_and_files(env_paths=None, template=None, - template_url=None): - merged_files = {} - merged_env = {} - - if env_paths: - for env_path in env_paths: - files, env = process_environment_and_files(env_path, template, - template_url) - - # 'files' looks like {"filename1": contents, "filename2": contents} - # so a simple update is enough for merging - merged_files.update(files) - - # 'env' can be a deeply nested dictionary, so a simple update is - # not enough - merged_env = deep_update(merged_env, env) - - return merged_files, merged_env - - -def process_environment_and_files(env_path=None, template=None, - template_url=None): - files = {} - env = {} - - if env_path: - env_url = utils.normalise_file_path_to_url(env_path) - env_base_url = utils.base_url_for_url(env_url) - raw_env = request.urlopen(env_url).read() - env = environment_format.parse(raw_env) - - resolve_environment_urls( - env.get('resource_registry'), - files, - env_base_url) - - return files, env - - -def resolve_environment_urls(resource_registry, files, env_base_url): - if resource_registry is None: - return - - rr = resource_registry - base_url = rr.get('base_url', env_base_url) - - def ignore_if(key, value): - if key == 'base_url': - return True - if isinstance(value, dict): - return True - if '::' in value: - # Built in providers like: "X::Compute::Server" - # don't need downloading. - return True - if key == 'hooks': - return True - - get_file_contents(rr, files, base_url, ignore_if) - - for res_name, res_dict in iter(rr.get('resources', {}).items()): - res_base_url = res_dict.get('base_url', base_url) - get_file_contents( - res_dict, files, res_base_url, ignore_if) diff --git a/stacktaskclient/exc.py b/stacktaskclient/exc.py index 6caa26b..97f0fda 100644 --- a/stacktaskclient/exc.py +++ b/stacktaskclient/exc.py @@ -81,7 +81,7 @@ class HTTPMultipleChoices(HTTPException): 'name': self.__class__.__name__, 'code': self.code, 'details': self.details - }) + }) class BadRequest(HTTPException): @@ -167,7 +167,7 @@ class HTTPServiceUnavailable(ServiceUnavailable): pass -#NOTE(bcwaldon): Build a mapping of HTTP codes to corresponding exception +# NOTE(bcwaldon): Build a mapping of HTTP codes to corresponding exception # classes _code_map = {} for obj_name in dir(sys.modules[__name__]): diff --git a/stacktaskclient/shell.py b/stacktaskclient/shell.py index a03c8eb..af1d849 100644 --- a/stacktaskclient/shell.py +++ b/stacktaskclient/shell.py @@ -44,192 +44,191 @@ osprofiler_profiler = importutils.try_import("osprofiler.profiler") class StacktaskShell(object): def _append_global_identity_args(self, parser): - # FIXME(gyee): these are global identity (Keystone) arguments which - # should be consistent and shared by all service clients. Therefore, - # they should be provided by python-keystoneclient. We will need to - # refactor this code once this functionality is avaible in - # python-keystoneclient. - parser.add_argument('-k', '--insecure', - default=False, - action='store_true', - help=_('Explicitly allow this client to perform ' - '\"insecure SSL\" (https) requests. The server\'s ' - 'certificate will not be verified against any ' - 'certificate authorities. This option should ' - 'be used with caution.')) + parser.add_argument( + '-k', '--insecure', + default=False, + action='store_true', + help=_( + 'Explicitly allow this client to perform ' + '\"insecure SSL\" (https) requests. The server\'s ' + 'certificate will not be verified against any ' + 'certificate authorities. This option should ' + 'be used with caution.')) - parser.add_argument('--os-cert', - help=_('Path of certificate file to use in SSL ' - 'connection. This file can optionally be ' - 'prepended with the private key.')) + parser.add_argument( + '--os-cert', + help=_( + 'Path of certificate file to use in SSL ' + 'connection. This file can optionally be ' + 'prepended with the private key.')) - parser.add_argument('--os-key', - help=_('Path of client key to use in SSL ' - 'connection. This option is not necessary ' - 'if your key is prepended to your cert file.')) + parser.add_argument( + '--os-key', + help=_( + 'Path of client key to use in SSL ' + 'connection. This option is not necessary ' + 'if your key is prepended to your cert file.')) - parser.add_argument('--os-cacert', - metavar='', - dest='os_cacert', - default=utils.env('OS_CACERT'), - help=_('Path of CA TLS certificate(s) used to ' - 'verify the remote server\'s certificate. ' - 'Without this option glance looks for the ' - 'default system CA certificates.')) + parser.add_argument( + '--os-cacert', + metavar='', + dest='os_cacert', + default=utils.env('OS_CACERT'), + help=_( + 'Path of CA TLS certificate(s) used to ' + 'verify the remote server\'s certificate. ' + 'Without this option glance looks for the ' + 'default system CA certificates.')) - parser.add_argument('--os-username', - default=utils.env('OS_USERNAME'), - help=_('Defaults to %(value)s.') % { - 'value': 'env[OS_USERNAME]' - }) + parser.add_argument( + '--os-username', + default=utils.env('OS_USERNAME'), + help=_('Defaults to %(value)s.') % {'value': 'env[OS_USERNAME]'}) - parser.add_argument('--os_username', - help=argparse.SUPPRESS) + parser.add_argument('--os_username', help=argparse.SUPPRESS) - parser.add_argument('--os-user-id', - default=utils.env('OS_USER_ID'), - help=_('Defaults to %(value)s.') % { - 'value': 'env[OS_USER_ID]' - }) + parser.add_argument( + '--os-user-id', + default=utils.env('OS_USER_ID'), + help=_('Defaults to %(value)s.') % {'value': 'env[OS_USER_ID]'}) - parser.add_argument('--os_user_id', - help=argparse.SUPPRESS) + parser.add_argument('--os_user_id', help=argparse.SUPPRESS) - parser.add_argument('--os-user-domain-id', - default=utils.env('OS_USER_DOMAIN_ID'), - help=_('Defaults to %(value)s.') % { - 'value': 'env[OS_USER_DOMAIN_ID]' - }) + parser.add_argument( + '--os-user-domain-id', + default=utils.env('OS_USER_DOMAIN_ID'), + help=_('Defaults to %(value)s.') % { + 'value': 'env[OS_USER_DOMAIN_ID]' + }) - parser.add_argument('--os_user_domain_id', - help=argparse.SUPPRESS) + parser.add_argument('--os_user_domain_id', help=argparse.SUPPRESS) - parser.add_argument('--os-user-domain-name', - default=utils.env('OS_USER_DOMAIN_NAME'), - help=_('Defaults to %(value)s.') % { - 'value': 'env[OS_USER_DOMAIN_NAME]' - }) + parser.add_argument( + '--os-user-domain-name', + default=utils.env('OS_USER_DOMAIN_NAME'), + help=_('Defaults to %(value)s.') % { + 'value': 'env[OS_USER_DOMAIN_NAME]' + }) - parser.add_argument('--os_user_domain_name', - help=argparse.SUPPRESS) + parser.add_argument('--os_user_domain_name', help=argparse.SUPPRESS) - parser.add_argument('--os-project-id', - default=utils.env('OS_PROJECT_ID'), - help=(_('Another way to specify tenant ID. ' - 'This option is mutually exclusive with ' - '%(arg)s. Defaults to %(value)s.') % - { - 'arg': '--os-tenant-id', - 'value': 'env[OS_PROJECT_ID]' - })) + parser.add_argument( + '--os-project-id', + default=utils.env('OS_PROJECT_ID'), + help=( + _('Another way to specify tenant ID. ' + 'This option is mutually exclusive with ' + '%(arg)s. Defaults to %(value)s.') % + {'arg': '--os-tenant-id', 'value': 'env[OS_PROJECT_ID]'})) - parser.add_argument('--os_project_id', - help=argparse.SUPPRESS) + parser.add_argument('--os_project_id', help=argparse.SUPPRESS) - parser.add_argument('--os-project-name', - default=utils.env('OS_PROJECT_NAME'), - help=(_('Another way to specify tenant name. ' - 'This option is mutually exclusive with ' - '%(arg)s. Defaults to %(value)s.') % - { - 'arg': '--os-tenant-name', - 'value': 'env[OS_PROJECT_NAME]' - })) + parser.add_argument( + '--os-project-name', + default=utils.env('OS_PROJECT_NAME'), + help=( + _('Another way to specify tenant name. ' + 'This option is mutually exclusive with ' + '%(arg)s. Defaults to %(value)s.') % + {'arg': '--os-tenant-name', 'value': 'env[OS_PROJECT_NAME]'})) - parser.add_argument('--os_project_name', - help=argparse.SUPPRESS) + parser.add_argument('--os_project_name', help=argparse.SUPPRESS) - parser.add_argument('--os-project-domain-id', - default=utils.env('OS_PROJECT_DOMAIN_ID'), - help=_('Defaults to %(value)s.') % { - 'value': 'env[OS_PROJECT_DOMAIN_ID]' - }) + parser.add_argument( + '--os-project-domain-id', + default=utils.env('OS_PROJECT_DOMAIN_ID'), + help=_('Defaults to %(value)s.') % { + 'value': 'env[OS_PROJECT_DOMAIN_ID]' + }) - parser.add_argument('--os_project_domain_id', - help=argparse.SUPPRESS) + parser.add_argument('--os_project_domain_id', help=argparse.SUPPRESS) - parser.add_argument('--os-project-domain-name', - default=utils.env('OS_PROJECT_DOMAIN_NAME'), - help=_('Defaults to %(value)s.') % { - 'value': 'env[OS_PROJECT_DOMAIN_NAME]' - }) + parser.add_argument( + '--os-project-domain-name', + default=utils.env('OS_PROJECT_DOMAIN_NAME'), + help=_('Defaults to %(value)s.') % { + 'value': 'env[OS_PROJECT_DOMAIN_NAME]' + }) - parser.add_argument('--os_project_domain_name', - help=argparse.SUPPRESS) + parser.add_argument('--os_project_domain_name', help=argparse.SUPPRESS) - parser.add_argument('--os-password', - default=utils.env('OS_PASSWORD'), - help=_('Defaults to %(value)s.') % { - 'value': 'env[OS_PASSWORD]' - }) + parser.add_argument( + '--os-password', + default=utils.env('OS_PASSWORD'), + help=_('Defaults to %(value)s.') % { + 'value': 'env[OS_PASSWORD]' + }) - parser.add_argument('--os_password', - help=argparse.SUPPRESS) + parser.add_argument('--os_password', help=argparse.SUPPRESS) - parser.add_argument('--os-tenant-id', - default=utils.env('OS_TENANT_ID'), - help=_('Defaults to %(value)s.') % { - 'value': 'env[OS_TENANT_ID]' - }) + parser.add_argument( + '--os-tenant-id', + default=utils.env('OS_TENANT_ID'), + help=_('Defaults to %(value)s.') % { + 'value': 'env[OS_TENANT_ID]' + }) - parser.add_argument('--os_tenant_id', - default=utils.env('OS_TENANT_ID'), - help=argparse.SUPPRESS) + parser.add_argument( + '--os_tenant_id', + default=utils.env('OS_TENANT_ID'), + help=argparse.SUPPRESS) - parser.add_argument('--os-tenant-name', - default=utils.env('OS_TENANT_NAME'), - help=_('Defaults to %(value)s.') % { - 'value': 'env[OS_TENANT_NAME]' - }) + parser.add_argument( + '--os-tenant-name', + default=utils.env('OS_TENANT_NAME'), + help=_('Defaults to %(value)s.') % { + 'value': 'env[OS_TENANT_NAME]' + }) - parser.add_argument('--os_tenant_name', - default=utils.env('OS_TENANT_NAME'), - help=argparse.SUPPRESS) + parser.add_argument( + '--os_tenant_name', + default=utils.env('OS_TENANT_NAME'), + help=argparse.SUPPRESS) - parser.add_argument('--os-auth-url', - default=utils.env('OS_AUTH_URL'), - help=_('Defaults to %(value)s.') % { - 'value': 'env[OS_AUTH_URL]' - }) + parser.add_argument( + '--os-auth-url', + default=utils.env('OS_AUTH_URL'), + help=_('Defaults to %(value)s.') % { + 'value': 'env[OS_AUTH_URL]' + }) - parser.add_argument('--os_auth_url', - help=argparse.SUPPRESS) + parser.add_argument('--os_auth_url', help=argparse.SUPPRESS) - parser.add_argument('--os-region-name', - default=utils.env('OS_REGION_NAME'), - help=_('Defaults to %(value)s.') % { - 'value': 'env[OS_REGION_NAME]' - }) + parser.add_argument( + '--os-region-name', + default=utils.env('OS_REGION_NAME'), + help=_('Defaults to %(value)s.') % { + 'value': 'env[OS_REGION_NAME]' + }) - parser.add_argument('--os_region_name', - help=argparse.SUPPRESS) + parser.add_argument('--os_region_name', help=argparse.SUPPRESS) - parser.add_argument('--os-auth-token', - default=utils.env('OS_AUTH_TOKEN'), - help=_('Defaults to %(value)s.') % { - 'value': 'env[OS_AUTH_TOKEN]' - }) + parser.add_argument( + '--os-auth-token', + default=utils.env('OS_AUTH_TOKEN'), + help=_('Defaults to %(value)s.') % { + 'value': 'env[OS_AUTH_TOKEN]' + }) - parser.add_argument('--os_auth_token', - help=argparse.SUPPRESS) + parser.add_argument('--os_auth_token', help=argparse.SUPPRESS) - parser.add_argument('--os-service-type', - default=utils.env('OS_SERVICE_TYPE'), - help=_('Defaults to %(value)s.') % { - 'value': 'env[OS_SERVICE_TYPE]' - }) + parser.add_argument( + '--os-service-type', + default=utils.env('OS_SERVICE_TYPE'), + help=_('Defaults to %(value)s.') % { + 'value': 'env[OS_SERVICE_TYPE]' + }) - parser.add_argument('--os_service_type', - help=argparse.SUPPRESS) + parser.add_argument('--os_service_type', help=argparse.SUPPRESS) - parser.add_argument('--os-endpoint-type', - default=utils.env('OS_ENDPOINT_TYPE'), - help=_('Defaults to %(value)s.') % { - 'value': 'env[OS_ENDPOINT_TYPE]' - }) + parser.add_argument( + '--os-endpoint-type', + default=utils.env('OS_ENDPOINT_TYPE'), + help=_('Defaults to %(value)s.') % { + 'value': 'env[OS_ENDPOINT_TYPE]' + }) - parser.add_argument('--os_endpoint_type', - help=argparse.SUPPRESS) + parser.add_argument('--os_endpoint_type', help=argparse.SUPPRESS) def get_base_parser(self): parser = argparse.ArgumentParser( @@ -243,70 +242,74 @@ class StacktaskShell(object): ) # Global arguments - parser.add_argument('-h', '--help', - action='store_true', - help=argparse.SUPPRESS) + parser.add_argument( + '-h', '--help', action='store_true', help=argparse.SUPPRESS) - parser.add_argument('--version', - action='version', - version=stacktaskclient.__version__, - help=_("Shows the client version and exits.")) + parser.add_argument( + '--version', action='version', + version=stacktaskclient.__version__, + help=_("Shows the client version and exits.")) - parser.add_argument('-d', '--debug', - default=bool(utils.env('STACKTASKCLIENT_DEBUG')), - action='store_true', - help=_('Defaults to %(value)s.') % { - 'value': 'env[STACKTASKCLIENT_DEBUG]' - }) + parser.add_argument( + '-d', '--debug', + default=bool(utils.env('STACKTASKCLIENT_DEBUG')), + action='store_true', + help=_('Defaults to %(value)s.') % { + 'value': 'env[STACKTASKCLIENT_DEBUG]' + }) - parser.add_argument('-v', '--verbose', - default=False, action="store_true", - help=_("Print more verbose output.")) + parser.add_argument( + '-v', '--verbose', + default=False, action="store_true", + help=_("Print more verbose output.")) - parser.add_argument('--api-timeout', - help=_('Number of seconds to wait for an ' - 'API response, ' - 'defaults to system socket timeout')) + parser.add_argument( + '--api-timeout', + help=_('Number of seconds to wait for an ' + 'API response, ' + 'defaults to system socket timeout')) # os-no-client-auth tells stacktaskclient to use token, instead of # env[OS_AUTH_URL] - parser.add_argument('--os-no-client-auth', - default=utils.env('OS_NO_CLIENT_AUTH'), - action='store_true', - help=(_("Do not contact keystone for a token. " - "Defaults to %(value)s.") % - {'value': 'env[OS_NO_CLIENT_AUTH]'})) + parser.add_argument( + '--os-no-client-auth', + default=utils.env('OS_NO_CLIENT_AUTH'), + action='store_true', + help=(_("Do not contact keystone for a token. " + "Defaults to %(value)s.") % + {'value': 'env[OS_NO_CLIENT_AUTH]'})) - parser.add_argument('--bypass-url', - default=utils.env('STACKTASK_BYPASS_URL'), - help=_('Defaults to %(value)s.') % { - 'value': 'env[STACKTASK_BYPASS_URL]' - }) + parser.add_argument( + '--bypass-url', + default=utils.env('STACKTASK_BYPASS_URL'), + help=_('Defaults to %(value)s.') % { + 'value': 'env[STACKTASK_BYPASS_URL]' + }) - parser.add_argument('--api-version', - default=utils.env('STACKTASK_API_VERSION', - default='1'), - help=_('Defaults to %(value)s or 1.') % { - 'value': 'env[STACKTASK_API_VERSION]' - }) + parser.add_argument( + '--api-version', + default=utils.env('STACKTASK_API_VERSION', + default='1'), + help=_('Defaults to %(value)s or 1.') % { + 'value': 'env[STACKTASK_API_VERSION]' + }) - # FIXME(gyee): this method should come from python-keystoneclient. - # Will refactor this code once it is available. - # https://bugs.launchpad.net/python-keystoneclient/+bug/1332337 self._append_global_identity_args(parser) if osprofiler_profiler: - parser.add_argument('--profile', - metavar='HMAC_KEY', - help=_('HMAC key to use for encrypting ' - 'context data for performance profiling of ' - 'operation. This key should be the value of ' - 'HMAC key configured in osprofiler middleware ' - 'in heat, it is specified in the paste ' - 'configuration (/etc/heat/api-paste.ini). ' - 'Without the key, profiling will not be ' - 'triggered even if osprofiler is enabled ' - 'on server side.')) + parser.add_argument( + '--profile', + metavar='HMAC_KEY', + help=_( + 'HMAC key to use for encrypting ' + 'context data for performance profiling of ' + 'operation. This key should be the value of ' + 'HMAC key configured in osprofiler middleware ' + 'in heat, it is specified in the paste ' + 'configuration (/etc/heat/api-paste.ini). ' + 'Without the key, profiling will not be ' + 'triggered even if osprofiler is enabled ' + 'on server side.')) return parser def get_subcommand_parser(self, version): @@ -398,18 +401,14 @@ class StacktaskShell(object): # first create a Keystone session cacert = kwargs.pop('cacert', None) cert = kwargs.pop('cert', None) - key = kwargs.pop('key', None) insecure = kwargs.pop('insecure', False) timeout = kwargs.pop('timeout', None) verify = kwargs.pop('verify', None) - # FIXME(gyee): this code should come from keystoneclient if verify is None: if insecure: verify = False else: - # TODO(gyee): should we do - # stacktaskclient.common.http.get_system_ca_fle()? verify = cacert or True return kssession.Session(verify=verify, cert=cert, timeout=timeout) @@ -504,23 +503,26 @@ class StacktaskShell(object): return 0 if not args.os_username and not args.os_auth_token: - raise exc.CommandError(_("You must provide a username via" - " either --os-username or env[OS_USERNAME]" - " or a token via --os-auth-token or" - " env[OS_AUTH_TOKEN]")) + raise exc.CommandError( + _("You must provide a username via" + " either --os-username or env[OS_USERNAME]" + " or a token via --os-auth-token or" + " env[OS_AUTH_TOKEN]")) if not args.os_password and not args.os_auth_token: - raise exc.CommandError(_("You must provide a password via" - " either --os-password or env[OS_PASSWORD]" - " or a token via --os-auth-token or" - " env[OS_AUTH_TOKEN]")) + raise exc.CommandError( + _("You must provide a password via" + " either --os-password or env[OS_PASSWORD]" + " or a token via --os-auth-token or" + " env[OS_AUTH_TOKEN]")) if args.os_no_client_auth: if not args.bypass_url: - raise exc.CommandError(_("If you specify --os-no-client-auth" - " you must also specify a Stacktask API" - " URL via either --bypass-url or" - " env[STACKTASK_BYPASS_URL]")) + raise exc.CommandError( + _("If you specify --os-no-client-auth" + " you must also specify a Stacktask API" + " URL via either --bypass-url or" + " env[STACKTASK_BYPASS_URL]")) else: # Tenant/project name or ID is needed to make keystoneclient # retrieve a service catalog, it's not required if @@ -528,20 +530,22 @@ class StacktaskShell(object): if not (args.os_tenant_id or args.os_tenant_name or args.os_project_id or args.os_project_name): - raise exc.CommandError(_("You must provide a tenant id via" - " either --os-tenant-id or" - " env[OS_TENANT_ID] or a tenant name" - " via either --os-tenant-name or" - " env[OS_TENANT_NAME] or a project id" - " via either --os-project-id or" - " env[OS_PROJECT_ID] or a project" - " name via either --os-project-name or" - " env[OS_PROJECT_NAME]")) + raise exc.CommandError( + _("You must provide a tenant id via" + " either --os-tenant-id or" + " env[OS_TENANT_ID] or a tenant name" + " via either --os-tenant-name or" + " env[OS_TENANT_NAME] or a project id" + " via either --os-project-id or" + " env[OS_PROJECT_ID] or a project" + " name via either --os-project-name or" + " env[OS_PROJECT_NAME]")) if not args.os_auth_url: - raise exc.CommandError(_("You must provide an auth url via" - " either --os-auth-url or via" - " env[OS_AUTH_URL]")) + raise exc.CommandError( + _("You must provide an auth url via" + " either --os-auth-url or via" + " env[OS_AUTH_URL]")) kwargs = { 'insecure': args.insecure, diff --git a/stacktaskclient/tests/__init__.py b/stacktaskclient/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/stacktaskclient/tests/functional/__init__.py b/stacktaskclient/tests/functional/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/stacktaskclient/tests/functional/base.py b/stacktaskclient/tests/functional/base.py deleted file mode 100644 index 2f83341..0000000 --- a/stacktaskclient/tests/functional/base.py +++ /dev/null @@ -1,42 +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 - -from tempest_lib.cli import base - - -class ClientTestBase(base.ClientTestBase): - """This is a first pass at a simple read only python-heatclient test. - This only exercises client commands that are read only. - - This should test commands: - * as a regular user - * as a admin user - * with and without optional parameters - * initially just check return codes, and later test command outputs - """ - - def _get_clients(self): - cli_dir = os.environ.get( - 'OS_HEATCLIENT_EXEC_DIR', - os.path.join(os.path.abspath('.'), '.tox/functional/bin')) - - return base.CLIClient( - username=os.environ.get('OS_USERNAME'), - password=os.environ.get('OS_PASSWORD'), - tenant_name=os.environ.get('OS_TENANT_NAME'), - uri=os.environ.get('OS_AUTH_URL'), - cli_dir=cli_dir) - - def heat(self, *args, **kwargs): - return self.clients.heat(*args, **kwargs) diff --git a/stacktaskclient/tests/functional/hooks/post_test_hook.sh b/stacktaskclient/tests/functional/hooks/post_test_hook.sh deleted file mode 100755 index cc46670..0000000 --- a/stacktaskclient/tests/functional/hooks/post_test_hook.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -xe - -# 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 script is executed inside post_test_hook function in devstack gate. - -function generate_testr_results { - if [ -f .testrepository/0 ]; then - sudo .tox/functional/bin/testr last --subunit > $WORKSPACE/testrepository.subunit - sudo mv $WORKSPACE/testrepository.subunit $BASE/logs/testrepository.subunit - sudo .tox/functional/bin/python /usr/local/jenkins/slave_scripts/subunit2html.py $BASE/logs/testrepository.subunit $BASE/logs/testr_results.html - sudo gzip -9 $BASE/logs/testrepository.subunit - sudo gzip -9 $BASE/logs/testr_results.html - sudo chown jenkins:jenkins $BASE/logs/testrepository.subunit.gz $BASE/logs/testr_results.html.gz - sudo chmod a+r $BASE/logs/testrepository.subunit.gz $BASE/logs/testr_results.html.gz - fi -} - -export HEATCLIENT_DIR="$BASE/new/python-heatclient" - -# Get admin credentials -cd $BASE/new/devstack -source openrc admin admin - -# Go to the heatclient dir -cd $HEATCLIENT_DIR - -sudo chown -R jenkins:stack $HEATCLIENT_DIR - -# Run tests -echo "Running heatclient functional test suite" -set +e -# Preserve env for OS_ credentials -sudo -E -H -u jenkins tox -efunctional -EXIT_CODE=$? -set -e - -# Collect and parse result -generate_testr_results -exit $EXIT_CODE diff --git a/stacktaskclient/tests/functional/templates/heat_minimal.yaml b/stacktaskclient/tests/functional/templates/heat_minimal.yaml deleted file mode 100644 index d85e22c..0000000 --- a/stacktaskclient/tests/functional/templates/heat_minimal.yaml +++ /dev/null @@ -1,18 +0,0 @@ -HeatTemplateFormatVersion: '2012-12-12' -Description: Minimal template to test validation -Parameters: - InstanceImage: - Description: Glance image name - Type: String - InstanceType: - Description: Nova instance type - Type: String - Default: m1.small - AllowedValues: [m1.tiny, m1.small, m1.medium, m1.large, m1.nano, m1.xlarge, m1.micro, m1.heat] - ConstraintDescription: must be a valid nova instance type. -Resources: - InstanceResource: - Type: OS::Nova::Server - Properties: - flavor: {Ref: InstanceType} - image: {Ref: InstanceImage} diff --git a/stacktaskclient/tests/functional/templates/heat_minimal_hot.yaml b/stacktaskclient/tests/functional/templates/heat_minimal_hot.yaml deleted file mode 100644 index c4eb8b4..0000000 --- a/stacktaskclient/tests/functional/templates/heat_minimal_hot.yaml +++ /dev/null @@ -1,19 +0,0 @@ -heat_template_version: 2015-04-30 -description: A minimal HOT test template -parameters: - instance_image: - description: Glance image name - type: string - instance_type: - description: Nova instance type - type: string - default: m1.small - constraints: - - allowed_values: [m1.tiny, m1.small, m1.medium, m1.large, m1.nano, m1.xlarge, m1.micro, m1.heat] - description: must be a valid nova instance type. -resources: - instance: - type: OS::Nova::Server - properties: - image: { get_param: instance_image } - flavor: { get_param: instance_type } diff --git a/stacktaskclient/tests/functional/test_readonly_heat.py b/stacktaskclient/tests/functional/test_readonly_heat.py deleted file mode 100644 index 84fe049..0000000 --- a/stacktaskclient/tests/functional/test_readonly_heat.py +++ /dev/null @@ -1,102 +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 json -import os - -from tempest_lib import exceptions -import yaml - -from heatclient.tests.functional import base - - -class SimpleReadOnlyHeatClientTest(base.ClientTestBase): - """Basic, read-only tests for Heat CLI client. - Basic smoke test for the heat CLI commands which do not require - creating or modifying stacks. - """ - - def test_heat_fake_action(self): - self.assertRaises(exceptions.CommandFailed, - self.heat, - 'this-does-not-exist') - - def test_heat_stack_list(self): - self.heat('stack-list') - - def test_heat_stack_list_debug(self): - self.heat('stack-list', flags='--debug') - - def test_heat_resource_template_fmt_default(self): - ret = self.heat('resource-template OS::Nova::Server') - self.assertIn('Type: OS::Nova::Server', ret) - - def test_heat_resource_template_fmt_arg_short_yaml(self): - ret = self.heat('resource-template -F yaml OS::Nova::Server') - self.assertIn('Type: OS::Nova::Server', ret) - self.assertIsInstance(yaml.safe_load(ret), dict) - - def test_heat_resource_template_fmt_arg_long_json(self): - ret = self.heat('resource-template --format json OS::Nova::Server') - self.assertIn('"Type": "OS::Nova::Server"', ret) - self.assertIsInstance(json.loads(ret), dict) - - def test_heat_resource_type_list(self): - ret = self.heat('resource-type-list') - rsrc_types = self.parser.listing(ret) - self.assertTableStruct(rsrc_types, ['resource_type']) - - def test_heat_resource_type_show(self): - rsrc_schema = self.heat('resource-type-show OS::Heat::RandomString') - # resource-type-show returns a json resource schema - self.assertIsInstance(json.loads(rsrc_schema), dict) - - def _template_validate(self, templ_name): - heat_template_path = os.path.join( - os.path.dirname(os.path.realpath(__file__)), - 'templates/%s' % templ_name) - ret = self.heat('template-validate -f %s' % heat_template_path) - # On success template-validate returns a json representation - # of the template parameters - self.assertIsInstance(json.loads(ret), dict) - - def test_heat_template_validate_yaml(self): - self._template_validate('heat_minimal.yaml') - - def test_heat_template_validate_hot(self): - self._template_validate('heat_minimal_hot.yaml') - - def test_heat_help(self): - self.heat('help') - - def test_heat_bash_completion(self): - self.heat('bash-completion') - - def test_heat_help_cmd(self): - # Check requesting help for a specific command works - help_text = self.heat('help resource-template') - lines = help_text.split('\n') - self.assertFirstLineStartsWith(lines, 'usage: heat resource-template') - - def test_heat_version(self): - self.heat('', flags='--version') - - def test_heat_template_version_list(self): - ret = self.heat('template-version-list') - tmpl_types = self.parser.listing(ret) - self.assertTableStruct(tmpl_types, ['version', 'type']) - - def test_heat_template_function_list(self): - ret = self.heat('template-function-list ' - 'heat_template_version.2013-05-23') - tmpl_functions = self.parser.listing(ret) - self.assertTableStruct(tmpl_functions, ['functions', 'description']) diff --git a/stacktaskclient/tests/unit/__init__.py b/stacktaskclient/tests/unit/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/stacktaskclient/tests/unit/fakes.py b/stacktaskclient/tests/unit/fakes.py deleted file mode 100644 index 2469fad..0000000 --- a/stacktaskclient/tests/unit/fakes.py +++ /dev/null @@ -1,256 +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. - -from oslo_serialization import jsonutils - -from heatclient.common import http -from heatclient import exc - - -def script_heat_list(url=None, show_nested=False, client=http.HTTPClient): - if url is None: - url = '/stacks?' - - resp_dict = {"stacks": [ - { - "id": "1", - "stack_name": "teststack", - "stack_owner": "testowner", - "project": "testproject", - "stack_status": 'CREATE_COMPLETE', - "creation_time": "2012-10-25T01:58:47Z" - }, - { - "id": "2", - "stack_name": "teststack2", - "stack_owner": "testowner", - "project": "testproject", - "stack_status": 'IN_PROGRESS', - "creation_time": "2012-10-25T01:58:47Z" - }] - } - if show_nested: - nested = { - "id": "3", - "stack_name": "teststack_nested", - "stack_status": 'IN_PROGRESS', - "creation_time": "2012-10-25T01:58:47Z", - "parent": "theparentof3" - } - resp_dict["stacks"].append(nested) - resp = FakeHTTPResponse(200, - 'success, you', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - if client == http.SessionClient: - client.request(url, 'GET').AndReturn(resp) - else: - client.json_request('GET', url).AndReturn((resp, resp_dict)) - - -def mock_script_heat_list(show_nested=False): - resp_dict = {"stacks": [ - { - "id": "1", - "stack_name": "teststack", - "stack_owner": "testowner", - "project": "testproject", - "stack_status": 'CREATE_COMPLETE', - "creation_time": "2012-10-25T01:58:47Z" - }, - { - "id": "2", - "stack_name": "teststack2", - "stack_owner": "testowner", - "project": "testproject", - "stack_status": 'IN_PROGRESS', - "creation_time": "2012-10-25T01:58:47Z" - }] - } - if show_nested: - nested = { - "id": "3", - "stack_name": "teststack_nested", - "stack_status": 'IN_PROGRESS', - "creation_time": "2012-10-25T01:58:47Z", - "parent": "theparentof3" - } - resp_dict["stacks"].append(nested) - resp = FakeHTTPResponse(200, - 'success, you', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - return resp, resp_dict - - -def mock_script_event_list( - stack_name="teststack", resource_name=None, - rsrc_eventid1="7fecaeed-d237-4559-93a5-92d5d9111205", - rsrc_eventid2="e953547a-18f8-40a7-8e63-4ec4f509648b", - action="CREATE", final_state="COMPLETE", fakehttp=True): - - resp_dict = {"events": [ - {"event_time": "2013-12-05T14:14:31Z", - "id": rsrc_eventid1, - "links": [{"href": "http://heat.example.com:8004/foo", - "rel": "self"}, - {"href": "http://heat.example.com:8004/foo2", - "rel": "resource"}, - {"href": "http://heat.example.com:8004/foo3", - "rel": "stack"}], - "logical_resource_id": "myDeployment", - "physical_resource_id": None, - "resource_name": resource_name if resource_name else "testresource", - "resource_status": "%s_IN_PROGRESS" % action, - "resource_status_reason": "state changed"}, - {"event_time": "2013-12-05T14:14:32Z", - "id": rsrc_eventid2, - "links": [{"href": "http://heat.example.com:8004/foo", - "rel": "self"}, - {"href": "http://heat.example.com:8004/foo2", - "rel": "resource"}, - {"href": "http://heat.example.com:8004/foo3", - "rel": "stack"}], - "logical_resource_id": "myDeployment", - "physical_resource_id": "bce15ec4-8919-4a02-8a90-680960fb3731", - "resource_name": resource_name if resource_name else "testresource", - "resource_status": "%s_%s" % (action, final_state), - "resource_status_reason": "state changed"}]} - - if resource_name is None: - # if resource_name is not specified, - # then request is made for stack events. Hence include the stack event - stack_event1 = "0159dccd-65e1-46e8-a094-697d20b009e5" - stack_event2 = "8f591a36-7190-4adb-80da-00191fe22388" - resp_dict["events"].insert( - 0, {"event_time": "2013-12-05T14:14:30Z", - "id": stack_event1, - "links": [{"href": "http://heat.example.com:8004/foo", - "rel": "self"}, - {"href": "http://heat.example.com:8004/foo2", - "rel": "resource"}, - {"href": "http://heat.example.com:8004/foo3", - "rel": "stack"}], - "logical_resource_id": "aResource", - "physical_resource_id": None, - "resource_name": stack_name, - "resource_status": "%s_IN_PROGRESS" % action, - "resource_status_reason": "state changed"}) - resp_dict["events"].append( - {"event_time": "2013-12-05T14:14:33Z", - "id": stack_event2, - "links": [{"href": "http://heat.example.com:8004/foo", - "rel": "self"}, - {"href": "http://heat.example.com:8004/foo2", - "rel": "resource"}, - {"href": "http://heat.example.com:8004/foo3", - "rel": "stack"}], - "logical_resource_id": "aResource", - "physical_resource_id": None, - "resource_name": stack_name, - "resource_status": "%s_%s" % (action, final_state), - "resource_status_reason": "state changed"}) - - resp = FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) if fakehttp else None - - return resp, resp_dict - - -def script_heat_normal_error(client=http.HTTPClient): - resp_dict = { - "explanation": "The resource could not be found.", - "code": 404, - "error": { - "message": "The Stack (bad) could not be found.", - "type": "StackNotFound", - "traceback": "", - }, - "title": "Not Found" - } - resp = FakeHTTPResponse(400, - 'The resource could not be found', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - if client == http.SessionClient: - client.request('/stacks/bad', 'GET').AndRaise(exc.from_response(resp)) - else: - client.json_request('GET', - '/stacks/bad').AndRaise(exc.from_response(resp)) - - -def script_heat_error(resp_string, client=http.HTTPClient): - resp = FakeHTTPResponse(400, - 'The resource could not be found', - {'content-type': 'application/json'}, - resp_string) - if client == http.SessionClient: - client.request('/stacks/bad', 'GET').AndRaise(exc.from_response(resp)) - else: - client.json_request('GET', - '/stacks/bad').AndRaise(exc.from_response(resp)) - - -def fake_headers(): - return {'X-Auth-Token': 'abcd1234', - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'} - - -class FakeServiceCatalog(): - def url_for(self, endpoint_type, service_type): - return 'http://192.168.1.5:8004/v1/f14b41234' - - -class FakeKeystone(): - service_catalog = FakeServiceCatalog() - - def __init__(self, auth_token): - self.auth_token = auth_token - - -class FakeRaw(): - version = 110 - - -class FakeHTTPResponse(): - - version = 1.1 - - def __init__(self, status_code, reason, headers, content): - self.headers = headers - self.content = content - self.status_code = status_code - self.reason = reason - self.raw = FakeRaw() - - def getheader(self, name, default=None): - return self.headers.get(name, default) - - def getheaders(self): - return self.headers.items() - - def read(self, amt=None): - b = self.content - self.content = None - return b - - def iter_content(self, chunksize): - return self.content - - def json(self): - return jsonutils.loads(self.content) diff --git a/stacktaskclient/tests/unit/test_actions.py b/stacktaskclient/tests/unit/test_actions.py deleted file mode 100644 index 1fcfe8a..0000000 --- a/stacktaskclient/tests/unit/test_actions.py +++ /dev/null @@ -1,108 +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. - -from heatclient.tests.unit import fakes -from heatclient.v1 import actions - -import testtools - - -class ActionManagerTest(testtools.TestCase): - - def setUp(self): - super(ActionManagerTest, self).setUp() - - def _base_test(self, expect_args, expect_kwargs): - - class FakeAPI(object): - """Fake API and ensure request url is correct.""" - - def json_request(self, *args, **kwargs): - assert expect_args == args - assert expect_kwargs['data'] == kwargs['data'] - return fakes.FakeHTTPResponse( - '200', - '', - {'content-type': 'application/json'}, - {}), {} - - def raw_request(self, *args, **kwargs): - assert expect_args == args - return fakes.FakeHTTPResponse( - '200', - '', - {}, - {}) - - def head(self, url, **kwargs): - resp, body = self.json_request("HEAD", url, **kwargs) - return resp - - def get(self, url, **kwargs): - resp, body = self.json_request("GET", url, **kwargs) - return resp - - def post(self, url, **kwargs): - resp, body = self.json_request("POST", url, **kwargs) - return resp - - def put(self, url, **kwargs): - resp, body = self.json_request("PUT", url, **kwargs) - return resp - - def delete(self, url, **kwargs): - resp, body = self.raw_request("DELETE", url, **kwargs) - return resp - - def patch(self, url, **kwargs): - resp, body = self.json_request("PATCH", url, **kwargs) - return resp - - manager = actions.ActionManager(FakeAPI()) - return manager - - def test_suspend(self): - fields = {'stack_id': 'teststack%2Fabcd1234'} - expect_args = ('POST', - '/stacks/teststack%2Fabcd1234/actions') - expect_kwargs = {'data': {'suspend': None}} - - manager = self._base_test(expect_args, expect_kwargs) - manager.suspend(**fields) - - def test_resume(self): - fields = {'stack_id': 'teststack%2Fabcd1234'} - expect_args = ('POST', - '/stacks/teststack%2Fabcd1234/actions') - expect_kwargs = {'data': {'resume': None}} - - manager = self._base_test(expect_args, expect_kwargs) - manager.resume(**fields) - - def test_cancel_update(self): - fields = {'stack_id': 'teststack%2Fabcd1234'} - expect_args = ('POST', - '/stacks/teststack%2Fabcd1234/actions') - expect_kwargs = {'data': {'cancel_update': None}} - - manager = self._base_test(expect_args, expect_kwargs) - manager.cancel_update(**fields) - - def test_check(self): - fields = {'stack_id': 'teststack%2Fabcd1234'} - expect_args = ('POST', - '/stacks/teststack%2Fabcd1234/actions') - expect_kwargs = {'data': {'check': None}} - - manager = self._base_test(expect_args, expect_kwargs) - manager.check(**fields) diff --git a/stacktaskclient/tests/unit/test_build_info.py b/stacktaskclient/tests/unit/test_build_info.py deleted file mode 100644 index 9ee93da..0000000 --- a/stacktaskclient/tests/unit/test_build_info.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. -# -# 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 -from oslo_serialization import jsonutils -import testtools - -from heatclient.tests.unit import fakes -from heatclient.v1 import build_info - - -class BuildInfoManagerTest(testtools.TestCase): - def setUp(self): - super(BuildInfoManagerTest, self).setUp() - self.client = mock.Mock() - self.client.get.return_value = fakes.FakeHTTPResponse( - 200, - None, - {'content-type': 'application/json'}, - jsonutils.dumps('body') - ) - self.manager = build_info.BuildInfoManager(self.client) - - def test_build_info_makes_a_call_to_the_api(self): - self.manager.build_info() - self.client.get.assert_called_once_with('/build_info') - - def test_build_info_returns_the_response_body(self): - response = self.manager.build_info() - self.assertEqual('body', response) diff --git a/stacktaskclient/tests/unit/test_common_http.py b/stacktaskclient/tests/unit/test_common_http.py deleted file mode 100644 index bb2fa20..0000000 --- a/stacktaskclient/tests/unit/test_common_http.py +++ /dev/null @@ -1,861 +0,0 @@ -# -*- coding:utf-8 -*- -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import logging -import mock -import os -import socket - - -from oslo_serialization import jsonutils -import requests -import six -import testtools - -from heatclient.common import http -from heatclient.common import utils -from heatclient import exc -from heatclient.tests.unit import fakes -from keystoneclient import adapter -from mox3 import mox - - -class HttpClientTest(testtools.TestCase): - - # Patch os.environ to avoid required auth info. - def setUp(self): - super(HttpClientTest, self).setUp() - self.m = mox.Mox() - self.m.StubOutWithMock(requests, 'request') - self.addCleanup(self.m.VerifyAll) - self.addCleanup(self.m.UnsetStubs) - - def test_http_raw_request(self): - headers = {'Content-Type': 'application/octet-stream', - 'User-Agent': 'python-heatclient'} - - # Record a 200 - mock_conn = http.requests.request('GET', 'http://example.com:8004', - allow_redirects=False, - headers=headers) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/octet-stream'}, - '')) - # Replay, create client, assert - self.m.ReplayAll() - client = http.HTTPClient('http://example.com:8004') - resp = client.raw_request('GET', '') - self.assertEqual(200, resp.status_code) - self.assertEqual('', ''.join([x for x in resp.content])) - - def test_token_or_credentials(self): - # Record a 200 - fake200 = fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/octet-stream'}, - '') - - # no token or credentials - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/octet-stream', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn(fake200) - - # credentials - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/octet-stream', - 'User-Agent': 'python-heatclient', - 'X-Auth-Key': 'pass', - 'X-Auth-User': 'user'}) - mock_conn.AndReturn(fake200) - - # token suppresses credentials - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/octet-stream', - 'User-Agent': 'python-heatclient', - 'X-Auth-Token': 'abcd1234'}) - mock_conn.AndReturn(fake200) - - # Replay, create client, assert - self.m.ReplayAll() - client = http.HTTPClient('http://example.com:8004') - resp = client.raw_request('GET', '') - self.assertEqual(200, resp.status_code) - - client.username = 'user' - client.password = 'pass' - resp = client.raw_request('GET', '') - self.assertEqual(200, resp.status_code) - - client.auth_token = 'abcd1234' - resp = client.raw_request('GET', '') - self.assertEqual(200, resp.status_code) - - def test_include_pass(self): - # Record a 200 - fake200 = fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/octet-stream'}, - '') - - # no token or credentials - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/octet-stream', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn(fake200) - - # credentials - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/octet-stream', - 'User-Agent': 'python-heatclient', - 'X-Auth-Key': 'pass', - 'X-Auth-User': 'user'}) - mock_conn.AndReturn(fake200) - - # token suppresses credentials - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/octet-stream', - 'User-Agent': 'python-heatclient', - 'X-Auth-Token': 'abcd1234', - 'X-Auth-Key': 'pass', - 'X-Auth-User': 'user'}) - mock_conn.AndReturn(fake200) - - # Replay, create client, assert - self.m.ReplayAll() - client = http.HTTPClient('http://example.com:8004') - resp = client.raw_request('GET', '') - self.assertEqual(200, resp.status_code) - - client.username = 'user' - client.password = 'pass' - client.include_pass = True - resp = client.raw_request('GET', '') - self.assertEqual(200, resp.status_code) - - client.auth_token = 'abcd1234' - resp = client.raw_request('GET', '') - self.assertEqual(200, resp.status_code) - - def test_not_include_pass(self): - # Record a 200 - fake500 = fakes.FakeHTTPResponse( - 500, 'ERROR', - {'content-type': 'application/octet-stream'}, - '(HTTP 401)') - - # no token or credentials - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/octet-stream', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn(fake500) - - # Replay, create client, assert - self.m.ReplayAll() - client = http.HTTPClient('http://example.com:8004') - e = self.assertRaises(exc.HTTPUnauthorized, - client.raw_request, 'GET', '') - self.assertIn('include-password', str(e)) - - def test_region_name(self): - # Record a 200 - fake200 = fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/octet-stream'}, - '') - - # Specify region name - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/octet-stream', - 'X-Region-Name': 'RegionOne', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn(fake200) - - # Replay, create client, assert - self.m.ReplayAll() - client = http.HTTPClient('http://example.com:8004') - client.region_name = 'RegionOne' - resp = client.raw_request('GET', '') - self.assertEqual(200, resp.status_code) - - def test_http_json_request(self): - # Record a 200 - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/json'}, - '{}')) - # Replay, create client, assert - self.m.ReplayAll() - client = http.HTTPClient('http://example.com:8004') - resp, body = client.json_request('GET', '') - self.assertEqual(200, resp.status_code) - self.assertEqual({}, body) - - def test_http_json_request_argument_passed_to_requests(self): - """Check that we have sent the proper arguments to requests.""" - # Record a 200 - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - cert=('RANDOM_CERT_FILE', 'RANDOM_KEY_FILE'), - verify=True, - data='"text"', - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'X-Auth-Url': 'http://AUTH_URL', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/json'}, - '{}')) - # Replay, create client, assert - self.m.ReplayAll() - client = http.HTTPClient('http://example.com:8004') - client.verify_cert = True - client.cert_file = 'RANDOM_CERT_FILE' - client.key_file = 'RANDOM_KEY_FILE' - client.auth_url = 'http://AUTH_URL' - resp, body = client.json_request('GET', '', data='text') - self.assertEqual(200, resp.status_code) - self.assertEqual({}, body) - - def test_http_json_request_w_req_body(self): - # Record a 200 - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - body='test-body', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/json'}, - '{}')) - # Replay, create client, assert - self.m.ReplayAll() - client = http.HTTPClient('http://example.com:8004') - resp, body = client.json_request('GET', '', body='test-body') - self.assertEqual(200, resp.status_code) - self.assertEqual({}, body) - - def test_http_json_request_non_json_resp_cont_type(self): - # Record a 200 - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', body='test-body', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'not/json'}, - {})) - # Replay, create client, assert - self.m.ReplayAll() - client = http.HTTPClient('http://example.com:8004') - resp, body = client.json_request('GET', '', body='test-body') - self.assertEqual(200, resp.status_code) - self.assertIsNone(body) - - def test_http_json_request_invalid_json(self): - # Record a 200 - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/json'}, - 'invalid-json')) - # Replay, create client, assert - self.m.ReplayAll() - client = http.HTTPClient('http://example.com:8004') - resp, body = client.json_request('GET', '') - self.assertEqual(200, resp.status_code) - self.assertEqual('invalid-json', body) - - def test_http_manual_redirect_delete(self): - mock_conn = http.requests.request( - 'DELETE', 'http://example.com:8004/foo', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 302, 'Found', - {'location': 'http://example.com:8004/foo/bar'}, - '')) - mock_conn = http.requests.request( - 'DELETE', 'http://example.com:8004/foo/bar', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/json'}, - '{}')) - - self.m.ReplayAll() - - client = http.HTTPClient('http://example.com:8004/foo') - resp, body = client.json_request('DELETE', '') - - self.assertEqual(200, resp.status_code) - - def test_http_manual_redirect_post(self): - mock_conn = http.requests.request( - 'POST', 'http://example.com:8004/foo', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 302, 'Found', - {'location': 'http://example.com:8004/foo/bar'}, - '')) - mock_conn = http.requests.request( - 'POST', 'http://example.com:8004/foo/bar', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/json'}, - '{}')) - - self.m.ReplayAll() - - client = http.HTTPClient('http://example.com:8004/foo') - resp, body = client.json_request('POST', '') - - self.assertEqual(200, resp.status_code) - - def test_http_manual_redirect_put(self): - mock_conn = http.requests.request( - 'PUT', 'http://example.com:8004/foo', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 302, 'Found', - {'location': 'http://example.com:8004/foo/bar'}, - '')) - mock_conn = http.requests.request( - 'PUT', 'http://example.com:8004/foo/bar', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/json'}, - '{}')) - - self.m.ReplayAll() - - client = http.HTTPClient('http://example.com:8004/foo') - resp, body = client.json_request('PUT', '') - - self.assertEqual(200, resp.status_code) - - def test_http_manual_redirect_put_uppercase(self): - mock_conn = http.requests.request( - 'PUT', 'http://EXAMPLE.com:8004/foo', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 302, 'Found', - {'location': 'http://example.com:8004/foo/bar'}, - '')) - mock_conn = http.requests.request( - 'PUT', 'http://EXAMPLE.com:8004/foo/bar', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/json'}, - '{}')) - - self.m.ReplayAll() - - client = http.HTTPClient('http://EXAMPLE.com:8004/foo') - resp, body = client.json_request('PUT', '') - - self.assertEqual(200, resp.status_code) - - def test_http_manual_redirect_prohibited(self): - mock_conn = http.requests.request( - 'DELETE', 'http://example.com:8004/foo', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 302, 'Found', - {'location': 'http://example.com:8004/'}, - '')) - self.m.ReplayAll() - client = http.HTTPClient('http://example.com:8004/foo') - self.assertRaises(exc.InvalidEndpoint, - client.json_request, 'DELETE', '') - - def test_http_manual_redirect_error_without_location(self): - mock_conn = http.requests.request( - 'DELETE', 'http://example.com:8004/foo', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 302, 'Found', - {}, - '')) - self.m.ReplayAll() - client = http.HTTPClient('http://example.com:8004/foo') - self.assertRaises(exc.InvalidEndpoint, - client.json_request, 'DELETE', '') - - def test_http_json_request_redirect(self): - # Record the 302 - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 302, 'Found', - {'location': 'http://example.com:8004'}, - '')) - # Record the following 200 - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/json'}, - '{}')) - # Replay, create client, assert - self.m.ReplayAll() - client = http.HTTPClient('http://example.com:8004') - resp, body = client.json_request('GET', '') - self.assertEqual(200, resp.status_code) - self.assertEqual({}, body) - - def test_http_404_json_request(self): - # Record a 404 - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 404, 'OK', {'content-type': 'application/json'}, - '{}')) - # Replay, create client, assert - self.m.ReplayAll() - client = http.HTTPClient('http://example.com:8004') - e = self.assertRaises(exc.HTTPNotFound, client.json_request, 'GET', '') - # Assert that the raised exception can be converted to string - self.assertIsNotNone(str(e)) - - def test_http_300_json_request(self): - # Record a 300 - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 300, 'OK', {'content-type': 'application/json'}, - '{}')) - # Replay, create client, assert - self.m.ReplayAll() - client = http.HTTPClient('http://example.com:8004') - e = self.assertRaises( - exc.HTTPMultipleChoices, client.json_request, 'GET', '') - # Assert that the raised exception can be converted to string - self.assertIsNotNone(str(e)) - - def test_fake_json_request(self): - headers = {'User-Agent': 'python-heatclient'} - mock_conn = http.requests.request('GET', 'fake://example.com:8004/', - allow_redirects=False, - headers=headers) - mock_conn.AndRaise(socket.gaierror) - self.m.ReplayAll() - - client = http.HTTPClient('fake://example.com:8004') - self.assertRaises(exc.InvalidEndpoint, - client._http_request, "/", "GET") - - def test_debug_curl_command(self): - self.m.StubOutWithMock(logging.Logger, 'debug') - - ssl_connection_params = {'ca_file': 'TEST_CA', - 'cert_file': 'TEST_CERT', - 'key_file': 'TEST_KEY', - 'insecure': 'TEST_NSA'} - - headers = {'key': 'value'} - - mock_logging_debug = logging.Logger.debug( - "curl -g -i -X GET -H 'key: value' --key TEST_KEY " - "--cert TEST_CERT --cacert TEST_CA " - "-k -d 'text' http://foo/bar" - ) - mock_logging_debug.AndReturn(None) - self.m.ReplayAll() - - client = http.HTTPClient('http://foo') - client.ssl_connection_params = ssl_connection_params - client.log_curl_request('GET', '/bar', {'headers': headers, - 'data': 'text'}) - - def test_http_request_socket_error(self): - headers = {'User-Agent': 'python-heatclient'} - mock_conn = http.requests.request('GET', 'http://example.com:8004/', - allow_redirects=False, - headers=headers) - mock_conn.AndRaise(socket.error) - self.m.ReplayAll() - - client = http.HTTPClient('http://example.com:8004') - self.assertRaises(exc.CommunicationError, - client._http_request, "/", "GET") - - def test_http_request_socket_timeout(self): - headers = {'User-Agent': 'python-heatclient'} - mock_conn = http.requests.request('GET', 'http://example.com:8004/', - allow_redirects=False, - headers=headers) - mock_conn.AndRaise(socket.timeout) - self.m.ReplayAll() - - client = http.HTTPClient('http://example.com:8004') - self.assertRaises(exc.CommunicationError, - client._http_request, "/", "GET") - - def test_http_request_specify_timeout(self): - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}, - timeout=float(123)) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/json'}, - '{}')) - # Replay, create client, assert - self.m.ReplayAll() - client = http.HTTPClient('http://example.com:8004', timeout='123') - resp, body = client.json_request('GET', '') - self.assertEqual(200, resp.status_code) - self.assertEqual({}, body) - - def test_get_system_ca_file(self): - chosen = '/etc/ssl/certs/ca-certificates.crt' - self.m.StubOutWithMock(os.path, 'exists') - os.path.exists(chosen).AndReturn(chosen) - self.m.ReplayAll() - - ca = http.get_system_ca_file() - self.assertEqual(chosen, ca) - - def test_insecure_verify_cert_None(self): - client = http.HTTPClient('https://foo', insecure=True) - self.assertFalse(client.verify_cert) - - def test_passed_cert_to_verify_cert(self): - client = http.HTTPClient('https://foo', ca_file="NOWHERE") - self.assertEqual("NOWHERE", client.verify_cert) - - self.m.StubOutWithMock(http, 'get_system_ca_file') - http.get_system_ca_file().AndReturn("SOMEWHERE") - self.m.ReplayAll() - client = http.HTTPClient('https://foo') - self.assertEqual("SOMEWHERE", client.verify_cert) - - def test_curl_log_i18n_headers(self): - self.m.StubOutWithMock(logging.Logger, 'debug') - kwargs = {'headers': {'Key': b'foo\xe3\x8a\x8e'}} - - mock_logging_debug = logging.Logger.debug( - u"curl -g -i -X GET -H 'Key: foo㊎' http://somewhere" - ) - mock_logging_debug.AndReturn(None) - - self.m.ReplayAll() - - client = http.HTTPClient('http://somewhere') - client.log_curl_request("GET", '', kwargs=kwargs) - - -class SessionClientTest(testtools.TestCase): - def setUp(self): - super(SessionClientTest, self).setUp() - self.request = mock.patch.object(adapter.LegacyJsonAdapter, - 'request').start() - - def test_session_simple_request(self): - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/octet-stream'}, - '') - self.request.return_value = (resp, '') - - client = http.SessionClient(session=mock.ANY, - auth=mock.ANY) - response = client.request(method='GET', url='') - self.assertEqual(200, response.status_code) - self.assertEqual('', ''.join([x for x in response.content])) - - def test_session_json_request(self): - fake = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps({'some': 'body'})) - self.request.return_value = (fake, {}) - - client = http.SessionClient(session=mock.ANY, - auth=mock.ANY) - - resp = client.request('', 'GET') - self.assertEqual(200, resp.status_code) - self.assertEqual({'some': 'body'}, resp.json()) - - def test_404_error_response(self): - fake = fakes.FakeHTTPResponse( - 404, - 'FAIL', - {'content-type': 'application/octet-stream'}, - '') - self.request.return_value = (fake, '') - - client = http.SessionClient(session=mock.ANY, - auth=mock.ANY) - e = self.assertRaises(exc.HTTPNotFound, - client.request, '', 'GET') - # Assert that the raised exception can be converted to string - self.assertIsNotNone(six.text_type(e)) - - def test_redirect_302_location(self): - fake1 = fakes.FakeHTTPResponse( - 302, - 'OK', - {'location': 'http://no.where/ishere'}, - '' - ) - fake2 = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps({'Mount': 'Fuji'}) - ) - self.request.side_effect = [ - (fake1, ''), (fake2, jsonutils.dumps({'Mount': 'Fuji'}))] - - client = http.SessionClient(session=mock.ANY, - auth=mock.ANY, - endpoint_override='http://no.where/') - resp = client.request('', 'GET', redirect=True) - - self.assertEqual(200, resp.status_code) - self.assertEqual({'Mount': 'Fuji'}, utils.get_response_body(resp)) - - self.assertEqual(('', 'GET'), self.request.call_args_list[0][0]) - self.assertEqual(('ishere', 'GET'), self.request.call_args_list[1][0]) - for call in self.request.call_args_list: - self.assertEqual({'user_agent': 'python-heatclient', - 'raise_exc': False, - 'redirect': True}, call[1]) - - def test_302_location_no_endpoint(self): - fake1 = fakes.FakeHTTPResponse( - 302, - 'OK', - {'location': 'http://no.where/ishere'}, - '' - ) - fake2 = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps({'Mount': 'Fuji'}) - ) - self.request.side_effect = [ - (fake1, ''), (fake2, jsonutils.dumps({'Mount': 'Fuji'}))] - - client = http.SessionClient(session=mock.ANY, - auth=mock.ANY) - resp = client.request('', 'GET', redirect=True) - - self.assertEqual(200, resp.status_code) - self.assertEqual({'Mount': 'Fuji'}, utils.get_response_body(resp)) - - self.assertEqual(('', 'GET'), self.request.call_args_list[0][0]) - self.assertEqual(('http://no.where/ishere', - 'GET'), self.request.call_args_list[1][0]) - for call in self.request.call_args_list: - self.assertEqual({'user_agent': 'python-heatclient', - 'raise_exc': False, - 'redirect': True}, call[1]) - - def test_redirect_302_no_location(self): - fake = fakes.FakeHTTPResponse( - 302, - 'OK', - {}, - '' - ) - self.request.side_effect = [(fake, '')] - - client = http.SessionClient(session=mock.ANY, - auth=mock.ANY) - e = self.assertRaises(exc.InvalidEndpoint, - client.request, '', 'GET', redirect=True) - self.assertEqual("Location not returned with 302", six.text_type(e)) - - def test_no_redirect_302_no_location(self): - fake = fakes.FakeHTTPResponse( - 302, - 'OK', - {'location': 'http://no.where/ishere'}, - '' - ) - self.request.side_effect = [(fake, '')] - - client = http.SessionClient(session=mock.ANY, - auth=mock.ANY) - - self.assertEqual(fake, client.request('', 'GET')) - - def test_300_error_response(self): - fake = fakes.FakeHTTPResponse( - 300, - 'FAIL', - {'content-type': 'application/octet-stream'}, - '') - self.request.return_value = (fake, '') - - client = http.SessionClient(session=mock.ANY, - auth=mock.ANY) - e = self.assertRaises(exc.HTTPMultipleChoices, - client.request, '', 'GET') - # Assert that the raised exception can be converted to string - self.assertIsNotNone(six.text_type(e)) - - def test_kwargs(self): - fake = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - {} - ) - kwargs = dict(endpoint_override='http://no.where/', - data='some_data') - - client = http.SessionClient(mock.ANY) - - self.request.return_value = (fake, {}) - - resp = client.request('', 'GET', **kwargs) - - self.assertEqual({'endpoint_override': 'http://no.where/', - 'json': 'some_data', - 'user_agent': 'python-heatclient', - 'raise_exc': False}, self.request.call_args[1]) - self.assertEqual(200, resp.status_code) - - def test_methods(self): - fake = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - {} - ) - self.request.return_value = (fake, {}) - - client = http.SessionClient(mock.ANY) - methods = [client.get, client.put, client.post, client.patch, - client.delete, client.head] - for method in methods: - resp = method('') - self.assertEqual(200, resp.status_code) - - def test_credentials_headers(self): - client = http.SessionClient(mock.ANY) - self.assertEqual({}, client.credentials_headers()) diff --git a/stacktaskclient/tests/unit/test_deployment_utils.py b/stacktaskclient/tests/unit/test_deployment_utils.py deleted file mode 100644 index 69a2899..0000000 --- a/stacktaskclient/tests/unit/test_deployment_utils.py +++ /dev/null @@ -1,326 +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 mock -import six -import swiftclient.client -import testscenarios -import testtools - -from heatclient.common import deployment_utils -from heatclient import exc -from testtools import matchers - - -load_tests = testscenarios.load_tests_apply_scenarios - - -def mock_sc(group=None, config=None, options=None, - inputs=None, outputs=None): - return { - 'group': group, - 'config': config, - 'options': options or {}, - 'inputs': inputs or [], - 'outputs': outputs or [], - } - - -class DerivedConfigTest(testtools.TestCase): - - scenarios = [ - ('defaults', dict( - action='UPDATE', - source=mock_sc(), - name='s1', - input_values=None, - server_id='1234', - signal_transport='NO_SIGNAL', - signal_id=None, - result={ - 'config': '', - 'group': 'Heat::Ungrouped', - 'inputs': [{ - 'description': 'ID of the server being deployed to', - 'name': 'deploy_server_id', - 'type': 'String', - 'value': '1234' - }, { - 'description': 'Name of the current action ' - 'being deployed', - 'name': 'deploy_action', - 'type': 'String', - 'value': 'UPDATE' - }, { - 'description': 'How the server should signal to ' - 'heat with the deployment output values.', - 'name': 'deploy_signal_transport', - 'type': 'String', - 'value': 'NO_SIGNAL'}], - 'name': 's1', - 'options': {}, - 'outputs': []})), - ('config_values', dict( - action='UPDATE', - source=mock_sc( - group='puppet', - config='do the foo', - inputs=[ - {'name': 'one', 'default': '1'}, - {'name': 'two'}], - options={'option1': 'value'}, - outputs=[ - {'name': 'output1'}, - {'name': 'output2'}], - ), - name='s2', - input_values={'one': 'foo', 'two': 'bar', 'three': 'baz'}, - server_id='1234', - signal_transport='NO_SIGNAL', - signal_id=None, - result={ - 'config': 'do the foo', - 'group': 'puppet', - 'inputs': [{ - 'name': 'one', - 'default': '1', - 'value': 'foo' - }, { - 'name': 'two', - 'value': 'bar' - }, { - 'name': 'three', - 'type': 'String', - 'value': 'baz' - }, { - 'description': 'ID of the server being deployed to', - 'name': 'deploy_server_id', - 'type': 'String', - 'value': '1234' - }, { - 'description': 'Name of the current action ' - 'being deployed', - 'name': 'deploy_action', - 'type': 'String', - 'value': 'UPDATE' - }, { - 'description': 'How the server should signal to ' - 'heat with the deployment output values.', - 'name': 'deploy_signal_transport', - 'type': 'String', - 'value': 'NO_SIGNAL' - }], - 'name': 's2', - 'options': {'option1': 'value'}, - 'outputs': [ - {'name': 'output1'}, - {'name': 'output2'}]})), - ('temp_url', dict( - action='UPDATE', - source=mock_sc(), - name='s1', - input_values=None, - server_id='1234', - signal_transport='TEMP_URL_SIGNAL', - signal_id='http://192.0.2.1:8080/foo', - result={ - 'config': '', - 'group': 'Heat::Ungrouped', - 'inputs': [{ - 'description': 'ID of the server being deployed to', - 'name': 'deploy_server_id', - 'type': 'String', - 'value': '1234' - }, { - 'description': 'Name of the current action ' - 'being deployed', - 'name': 'deploy_action', - 'type': 'String', - 'value': 'UPDATE' - }, { - 'description': 'How the server should signal to ' - 'heat with the deployment output values.', - 'name': 'deploy_signal_transport', - 'type': 'String', - 'value': 'TEMP_URL_SIGNAL' - }, { - 'description': 'ID of signal to use for signaling ' - 'output values', - 'name': 'deploy_signal_id', - 'type': 'String', - 'value': 'http://192.0.2.1:8080/foo' - }, { - 'description': 'HTTP verb to use for signaling ' - 'output values', - 'name': 'deploy_signal_verb', - 'type': 'String', - 'value': 'PUT'}], - 'name': 's1', - 'options': {}, - 'outputs': []})), - ('unsupported', dict( - action='UPDATE', - source=mock_sc(), - name='s1', - input_values=None, - server_id='1234', - signal_transport='ASDF', - signal_id=None, - result_error=exc.CommandError, - result_error_msg='Unsupported signal transport ASDF', - result=None)), - ] - - def test_build_derived_config_params(self): - try: - self.assertEqual( - self.result, - deployment_utils.build_derived_config_params( - action=self.action, - source=self.source, - name=self.name, - input_values=self.input_values, - server_id=self.server_id, - signal_transport=self.signal_transport, - signal_id=self.signal_id)) - except Exception as e: - if not self.result_error: - raise e - self.assertIsInstance(e, self.result_error) - self.assertEqual(self.result_error_msg, six.text_type(e)) - - -class TempURLSignalTest(testtools.TestCase): - - @mock.patch.object(swiftclient.client, 'Connection') - def test_create_swift_client(self, sc_conn): - auth = mock.MagicMock() - auth.get_token.return_value = '1234' - auth.get_endpoint.return_value = 'http://192.0.2.1:8080' - - session = mock.MagicMock() - - args = mock.MagicMock() - args.os_region_name = 'Region1' - args.os_project_name = 'project' - args.os_username = 'user' - args.os_cacert = None - args.insecure = True - - sc_conn.return_value = mock.MagicMock() - - sc = deployment_utils.create_swift_client(auth, session, args) - - self.assertEqual(sc_conn.return_value, sc) - - self.assertEqual( - mock.call(session), - auth.get_token.call_args) - - self.assertEqual( - mock.call( - session, - service_type='object-store', - region_name='Region1'), - auth.get_endpoint.call_args) - - self.assertEqual( - mock.call( - cacert=None, - insecure=True, - key=None, - tenant_name='project', - preauthtoken='1234', - authurl=None, - user='user', - preauthurl='http://192.0.2.1:8080', - auth_version='2.0'), - sc_conn.call_args) - - def test_create_temp_url(self): - swift_client = mock.MagicMock() - swift_client.url = ("http://fake-host.com:8080/v1/AUTH_demo") - swift_client.head_account = mock.Mock(return_value={ - 'x-account-meta-temp-url-key': '123456'}) - swift_client.post_account = mock.Mock() - - uuid_pattern = ('[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB]' - '[a-f0-9]{3}-[a-f0-9]{12}') - url = deployment_utils.create_temp_url(swift_client, 'bar', 60) - self.assertFalse(swift_client.post_account.called) - regexp = ("http://fake-host.com:8080/v1/AUTH_demo/bar-%s" - "/%s\?temp_url_sig=[0-9a-f]{40}&" - "temp_url_expires=[0-9]{10}" % (uuid_pattern, uuid_pattern)) - self.assertThat(url, matchers.MatchesRegex(regexp)) - - timeout = int(url.split('=')[-1]) - self.assertTrue(timeout < 2147483647) - - def test_get_temp_url_no_account_key(self): - swift_client = mock.MagicMock() - swift_client.url = ("http://fake-host.com:8080/v1/AUTH_demo") - head_account = {} - - def post_account(data): - head_account.update(data) - - swift_client.head_account = mock.Mock(return_value=head_account) - swift_client.post_account = post_account - - self.assertNotIn('x-account-meta-temp-url-key', head_account) - deployment_utils.create_temp_url(swift_client, 'bar', 60, 'foo') - self.assertIn('x-account-meta-temp-url-key', head_account) - - def test_build_signal_id_no_signal(self): - hc = mock.MagicMock() - args = mock.MagicMock() - args.signal_transport = 'NO_SIGNAL' - self.assertIsNone(deployment_utils.build_signal_id(hc, args)) - - def test_build_signal_id_no_client_auth(self): - hc = mock.MagicMock() - args = mock.MagicMock() - args.os_no_client_auth = True - args.signal_transport = 'TEMP_URL_SIGNAL' - e = self.assertRaises(exc.CommandError, - deployment_utils.build_signal_id, hc, args) - self.assertEqual(( - 'Cannot use --os-no-client-auth, auth required to create ' - 'a Swift TempURL.'), - six.text_type(e)) - - @mock.patch.object(deployment_utils, 'create_temp_url') - @mock.patch.object(deployment_utils, 'create_swift_client') - def test_build_signal_id(self, csc, ctu): - hc = mock.MagicMock() - args = mock.MagicMock() - args.name = 'foo' - args.timeout = 60 - args.os_no_client_auth = False - args.signal_transport = 'TEMP_URL_SIGNAL' - csc.return_value = mock.MagicMock() - temp_url = ( - 'http://fake-host.com:8080/v1/AUTH_demo/foo/' - 'a81a74d5-c395-4269-9670-ddd0824fd696' - '?temp_url_sig=6a68371d602c7a14aaaa9e3b3a63b8b85bd9a503' - '&temp_url_expires=1425270977') - ctu.return_value = temp_url - - self.assertEqual( - temp_url, deployment_utils.build_signal_id(hc, args)) - self.assertEqual( - mock.call(hc.http_client.auth, hc.http_client.session, args), - csc.call_args) - self.assertEqual( - mock.call(csc.return_value, 'foo', 60), - ctu.call_args) diff --git a/stacktaskclient/tests/unit/test_environment_format.py b/stacktaskclient/tests/unit/test_environment_format.py deleted file mode 100644 index 702619d..0000000 --- a/stacktaskclient/tests/unit/test_environment_format.py +++ /dev/null @@ -1,80 +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. - -from heatclient.common import environment_format - -import mock -import testscenarios -import testtools -import yaml - - -load_tests = testscenarios.load_tests_apply_scenarios - - -class YamlEnvironmentTest(testtools.TestCase): - - def test_minimal_yaml(self): - yaml1 = '' - yaml2 = ''' -parameter_defaults: {} -parameters: {} -resource_registry: {} -''' - tpl1 = environment_format.parse(yaml1) - environment_format.default_for_missing(tpl1) - tpl2 = environment_format.parse(yaml2) - self.assertEqual(tpl2, tpl1) - - def test_wrong_sections(self): - env = ''' -parameters: {} -resource_regis: {} -''' - self.assertRaises(ValueError, environment_format.parse, env) - - def test_bad_yaml(self): - env = ''' -parameters: } -''' - self.assertRaises(ValueError, environment_format.parse, env) - - def test_parse_string_environment(self): - env = 'just string' - expect = 'The environment is not a valid YAML mapping data type.' - e = self.assertRaises(ValueError, environment_format.parse, env) - self.assertIn(expect, str(e)) - - def test_parse_document(self): - env = '["foo", "bar"]' - expect = 'The environment is not a valid YAML mapping data type.' - e = self.assertRaises(ValueError, environment_format.parse, env) - self.assertIn(expect, str(e)) - - -class YamlParseExceptions(testtools.TestCase): - - scenarios = [ - ('scanner', dict(raised_exception=yaml.scanner.ScannerError())), - ('parser', dict(raised_exception=yaml.parser.ParserError())), - ('reader', - dict(raised_exception=yaml.reader.ReaderError('', '', '', '', ''))), - ] - - def test_parse_to_value_exception(self): - text = 'not important' - - with mock.patch.object(yaml, 'load') as yaml_loader: - yaml_loader.side_effect = self.raised_exception - - self.assertRaises(ValueError, - environment_format.parse, text) diff --git a/stacktaskclient/tests/unit/test_event_utils.py b/stacktaskclient/tests/unit/test_event_utils.py deleted file mode 100644 index bba77b4..0000000 --- a/stacktaskclient/tests/unit/test_event_utils.py +++ /dev/null @@ -1,130 +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 mock -import testtools - -from heatclient.common import event_utils -from heatclient.v1 import events as hc_ev -from heatclient.v1 import resources as hc_res - - -class ShellTestEventUtils(testtools.TestCase): - @staticmethod - def _mock_resource(resource_id, nested_id=None): - res_info = {"links": [{"href": "http://heat/foo", "rel": "self"}, - {"href": "http://heat/foo2", "rel": "resource"}], - "logical_resource_id": resource_id, - "physical_resource_id": resource_id, - "resource_status": "CREATE_COMPLETE", - "resource_status_reason": "state changed", - "resource_type": "OS::Nested::Server", - "updated_time": "2014-01-06T16:14:26Z"} - if nested_id: - nested_link = {"href": "http://heat/%s" % nested_id, - "rel": "nested"} - res_info["links"].append(nested_link) - return hc_res.Resource(manager=None, info=res_info) - - @staticmethod - def _mock_event(event_id, resource_id): - ev_info = {"links": [{"href": "http://heat/foo", "rel": "self"}], - "logical_resource_id": resource_id, - "physical_resource_id": resource_id, - "resource_status": "CREATE_COMPLETE", - "resource_status_reason": "state changed", - "event_time": "2014-12-05T14:14:30Z", - "id": event_id} - return hc_ev.Event(manager=None, info=ev_info) - - def test_get_nested_ids(self): - def list_stub(stack_id): - return [self._mock_resource('aresource', 'foo3/3id')] - mock_client = mock.MagicMock() - mock_client.resources.list.side_effect = list_stub - ids = event_utils._get_nested_ids(hc=mock_client, - stack_id='astack/123') - mock_client.resources.list.assert_called_once_with( - stack_id='astack/123') - self.assertEqual(['foo3/3id'], ids) - - def test_get_stack_events(self): - def event_stub(stack_id, argfoo): - return [self._mock_event('event1', 'aresource')] - mock_client = mock.MagicMock() - mock_client.events.list.side_effect = event_stub - ev_args = {'argfoo': 123} - evs = event_utils._get_stack_events(hc=mock_client, - stack_id='astack/123', - event_args=ev_args) - mock_client.events.list.assert_called_once_with( - stack_id='astack/123', argfoo=123) - self.assertEqual(1, len(evs)) - self.assertEqual('event1', evs[0].id) - self.assertEqual('astack', evs[0].stack_name) - - def test_get_nested_events(self): - resources = {'parent': self._mock_resource('resource1', 'foo/child1'), - 'foo/child1': self._mock_resource('res_child1', - 'foo/child2'), - 'foo/child2': self._mock_resource('res_child2', - 'foo/child3'), - 'foo/child3': self._mock_resource('res_child3', - 'foo/END')} - - def resource_list_stub(stack_id): - return [resources[stack_id]] - mock_client = mock.MagicMock() - mock_client.resources.list.side_effect = resource_list_stub - - events = {'foo/child1': self._mock_event('event1', 'res_child1'), - 'foo/child2': self._mock_event('event2', 'res_child2'), - 'foo/child3': self._mock_event('event3', 'res_child3')} - - def event_list_stub(stack_id, argfoo): - return [events[stack_id]] - mock_client.events.list.side_effect = event_list_stub - - ev_args = {'argfoo': 123} - # Check nested_depth=1 (non recursive).. - evs = event_utils._get_nested_events(hc=mock_client, - nested_depth=1, - stack_id='parent', - event_args=ev_args) - - rsrc_calls = [mock.call(stack_id='parent')] - mock_client.resources.list.assert_has_calls(rsrc_calls) - ev_calls = [mock.call(stack_id='foo/child1', argfoo=123)] - mock_client.events.list.assert_has_calls(ev_calls) - self.assertEqual(1, len(evs)) - self.assertEqual('event1', evs[0].id) - - # ..and the recursive case via nested_depth=3 - mock_client.resources.list.reset_mock() - mock_client.events.list.reset_mock() - evs = event_utils._get_nested_events(hc=mock_client, - nested_depth=3, - stack_id='parent', - event_args=ev_args) - - rsrc_calls = [mock.call(stack_id='parent'), - mock.call(stack_id='foo/child1'), - mock.call(stack_id='foo/child2')] - mock_client.resources.list.assert_has_calls(rsrc_calls) - ev_calls = [mock.call(stack_id='foo/child1', argfoo=123), - mock.call(stack_id='foo/child2', argfoo=123), - mock.call(stack_id='foo/child3', argfoo=123)] - mock_client.events.list.assert_has_calls(ev_calls) - self.assertEqual(3, len(evs)) - self.assertEqual('event1', evs[0].id) - self.assertEqual('event2', evs[1].id) - self.assertEqual('event3', evs[2].id) diff --git a/stacktaskclient/tests/unit/test_events.py b/stacktaskclient/tests/unit/test_events.py deleted file mode 100644 index 82e0bb8..0000000 --- a/stacktaskclient/tests/unit/test_events.py +++ /dev/null @@ -1,153 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from heatclient.common import utils -from heatclient.v1 import events - -import mock -from mox3 import mox -import testtools - - -class EventManagerTest(testtools.TestCase): - - def setUp(self): - super(EventManagerTest, self).setUp() - self.m = mox.Mox() - self.addCleanup(self.m.VerifyAll) - self.addCleanup(self.m.UnsetStubs) - - def test_list_event(self): - stack_id = 'teststack', - resource_name = 'testresource' - manager = events.EventManager(None) - self.m.StubOutWithMock(manager, '_resolve_stack_id') - manager._resolve_stack_id(stack_id).AndReturn('teststack/abcd1234') - self.m.ReplayAll() - manager._list = mock.MagicMock() - manager.list(stack_id, resource_name) - # Make sure url is correct. - manager._list.assert_called_once_with('/stacks/teststack%2Fabcd1234/' - 'resources/testresource/events', - "events") - - def test_list_event_with_unicode_resource_name(self): - stack_id = 'teststack', - resource_name = u'\u5de5\u4f5c' - manager = events.EventManager(None) - self.m.StubOutWithMock(manager, '_resolve_stack_id') - manager._resolve_stack_id(stack_id).AndReturn('teststack/abcd1234') - self.m.ReplayAll() - manager._list = mock.MagicMock() - manager.list(stack_id, resource_name) - # Make sure url is correct. - manager._list.assert_called_once_with('/stacks/teststack%2Fabcd1234/' - 'resources/%E5%B7%A5%E4%BD%9C/' - 'events', "events") - - def test_list_event_with_none_resource_name(self): - stack_id = 'teststack', - manager = events.EventManager(None) - manager._list = mock.MagicMock() - manager.list(stack_id) - # Make sure url is correct. - manager._list.assert_called_once_with('/stacks/teststack/' - 'events', "events") - - def test_list_event_with_kwargs(self): - stack_id = 'teststack', - resource_name = 'testresource' - kwargs = {'limit': 2, - 'marker': '6d6935f4-0ae5', - 'filters': { - 'resource_action': 'CREATE', - 'resource_status': 'COMPLETE' - }} - manager = events.EventManager(None) - self.m.StubOutWithMock(manager, '_resolve_stack_id') - manager._resolve_stack_id(stack_id).AndReturn('teststack/abcd1234') - self.m.ReplayAll() - manager._list = mock.MagicMock() - manager.list(stack_id, resource_name, **kwargs) - # Make sure url is correct. - self.assertEqual(1, manager._list.call_count) - args = manager._list.call_args - self.assertEqual(2, len(args[0])) - url, param = args[0] - self.assertEqual("events", param) - base_url, query_params = utils.parse_query_url(url) - expected_base_url = ('/stacks/teststack%2Fabcd1234/' - 'resources/testresource/events') - self.assertEqual(expected_base_url, base_url) - expected_query_dict = {'marker': ['6d6935f4-0ae5'], - 'limit': ['2'], - 'resource_action': ['CREATE'], - 'resource_status': ['COMPLETE']} - self.assertEqual(expected_query_dict, query_params) - - def test_get_event(self): - fields = {'stack_id': 'teststack', - 'resource_name': 'testresource', - 'event_id': '1'} - - class FakeAPI(object): - """Fake API and ensure request url is correct.""" - - def json_request(self, *args, **kwargs): - expect = ('GET', - '/stacks/teststack%2Fabcd1234/resources' - '/testresource/events/1') - assert args == expect - return {}, {'event': []} - - def get(self, *args, **kwargs): - pass - - manager = events.EventManager(FakeAPI()) - with mock.patch('heatclient.v1.events.Event'): - self.m.StubOutWithMock(manager, '_resolve_stack_id') - self.m.StubOutWithMock(utils, 'get_response_body') - utils.get_response_body(mox.IgnoreArg()).AndReturn({'event': []}) - manager._resolve_stack_id('teststack').AndReturn( - 'teststack/abcd1234') - self.m.ReplayAll() - manager.get(**fields) - - def test_get_event_with_unicode_resource_name(self): - fields = {'stack_id': 'teststack', - 'resource_name': u'\u5de5\u4f5c', - 'event_id': '1'} - - class FakeAPI(object): - """Fake API and ensure request url is correct.""" - - def json_request(self, *args, **kwargs): - expect = ('GET', - '/stacks/teststack%2Fabcd1234/resources' - '/%E5%B7%A5%E4%BD%9C/events/1') - assert args == expect - return {}, {'event': []} - - def get(self, *args, **kwargs): - pass - - manager = events.EventManager(FakeAPI()) - with mock.patch('heatclient.v1.events.Event'): - self.m.StubOutWithMock(manager, '_resolve_stack_id') - self.m.StubOutWithMock(utils, 'get_response_body') - utils.get_response_body(mox.IgnoreArg()).AndReturn({'event': []}) - manager._resolve_stack_id('teststack').AndReturn( - 'teststack/abcd1234') - self.m.ReplayAll() - manager.get(**fields) diff --git a/stacktaskclient/tests/unit/test_resource_types.py b/stacktaskclient/tests/unit/test_resource_types.py deleted file mode 100644 index d315303..0000000 --- a/stacktaskclient/tests/unit/test_resource_types.py +++ /dev/null @@ -1,91 +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 mock -import testtools - -from heatclient.common import utils -from heatclient.v1 import resource_types - - -class ResourceTypeManagerTest(testtools.TestCase): - - def _base_test(self, expect, key): - - class FakeAPI(object): - """Fake API and ensure request url is correct.""" - - def get(self, *args, **kwargs): - assert ('GET', args[0]) == expect - - def json_request(self, *args, **kwargs): - assert args == expect - ret = key and {key: []} or {} - return {}, {key: ret} - - def raw_request(self, *args, **kwargs): - assert args == expect - return {} - - def head(self, url, **kwargs): - return self.json_request("HEAD", url, **kwargs) - - def post(self, url, **kwargs): - return self.json_request("POST", url, **kwargs) - - def put(self, url, **kwargs): - return self.json_request("PUT", url, **kwargs) - - def delete(self, url, **kwargs): - return self.raw_request("DELETE", url, **kwargs) - - def patch(self, url, **kwargs): - return self.json_request("PATCH", url, **kwargs) - - manager = resource_types.ResourceTypeManager(FakeAPI()) - return manager - - def test_list_types(self): - key = 'resource_types' - expect = ('GET', '/resource_types') - - class FakeResponse(object): - def json(self): - return {key: {}} - - class FakeClient(object): - def get(self, *args, **kwargs): - assert ('GET', args[0]) == expect - return FakeResponse() - - manager = resource_types.ResourceTypeManager(FakeClient()) - manager.list() - - @mock.patch.object(utils, 'get_response_body') - def test_get(self, mock_utils): - key = 'resource_types' - resource_type = 'OS::Nova::KeyPair' - expect = ('GET', '/resource_types/OS%3A%3ANova%3A%3AKeyPair') - manager = self._base_test(expect, key) - mock_utils.return_value = None - manager.get(resource_type) - - @mock.patch.object(utils, 'get_response_body') - def test_generate_template(self, mock_utils): - key = 'resource_types' - resource_type = 'OS::Nova::KeyPair' - template_type = 'cfn' - expect = ('GET', '/resource_types/OS%3A%3ANova%3A%3AKeyPair/template' - '?template_type=cfn') - manager = self._base_test(expect, key) - mock_utils.return_value = None - manager.generate_template(resource_type, template_type) diff --git a/stacktaskclient/tests/unit/test_resources.py b/stacktaskclient/tests/unit/test_resources.py deleted file mode 100644 index 120b1c4..0000000 --- a/stacktaskclient/tests/unit/test_resources.py +++ /dev/null @@ -1,210 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -from heatclient.common import utils - -from heatclient.v1 import resources -from six.moves.urllib import parse - -from mox3 import mox -import testtools - - -class ResourceManagerTest(testtools.TestCase): - - def setUp(self): - super(ResourceManagerTest, self).setUp() - self.m = mox.Mox() - self.addCleanup(self.m.UnsetStubs) - - def _base_test(self, expect, key): - - class FakeAPI(object): - """Fake API and ensure request url is correct.""" - - def get(self, *args, **kwargs): - assert ('GET', args[0]) == expect - - def json_request(self, *args, **kwargs): - assert args == expect - ret = key and {key: []} or {} - return {}, {key: ret} - - def raw_request(self, *args, **kwargs): - assert args == expect - return {} - - def head(self, url, **kwargs): - return self.json_request("HEAD", url, **kwargs) - - def post(self, url, **kwargs): - return self.json_request("POST", url, **kwargs) - - def put(self, url, **kwargs): - return self.json_request("PUT", url, **kwargs) - - def delete(self, url, **kwargs): - return self.raw_request("DELETE", url, **kwargs) - - def patch(self, url, **kwargs): - return self.json_request("PATCH", url, **kwargs) - - manager = resources.ResourceManager(FakeAPI()) - self.m.StubOutWithMock(manager, '_resolve_stack_id') - self.m.StubOutWithMock(utils, 'get_response_body') - utils.get_response_body(mox.IgnoreArg()).AndReturn( - {key: key and {key: []} or {}}) - manager._resolve_stack_id('teststack').AndReturn('teststack/abcd1234') - self.m.ReplayAll() - - return manager - - def test_get(self): - fields = {'stack_id': 'teststack', - 'resource_name': 'testresource'} - expect = ('GET', - '/stacks/teststack%2Fabcd1234/resources' - '/testresource') - key = 'resource' - - manager = self._base_test(expect, key) - manager.get(**fields) - self.m.VerifyAll() - - def test_get_with_attr(self): - fields = {'stack_id': 'teststack', - 'resource_name': 'testresource', - 'with_attr': ['attr_a', 'attr_b']} - expect = ('GET', - '/stacks/teststack%2Fabcd1234/resources' - '/testresource?with_attr=attr_a&with_attr=attr_b') - key = 'resource' - - manager = self._base_test(expect, key) - manager.get(**fields) - self.m.VerifyAll() - - def test_get_with_unicode_resource_name(self): - fields = {'stack_id': 'teststack', - 'resource_name': u'\u5de5\u4f5c'} - expect = ('GET', - '/stacks/teststack%2Fabcd1234/resources' - '/%E5%B7%A5%E4%BD%9C') - key = 'resource' - - manager = self._base_test(expect, key) - manager.get(**fields) - self.m.VerifyAll() - - def test_list(self): - self._test_list( - fields={'stack_id': 'teststack'}, - expect='/stacks/teststack/resources') - - def test_list_nested(self): - self._test_list( - fields={'stack_id': 'teststack', 'nested_depth': '99'}, - expect='/stacks/teststack/resources?%s' % parse.urlencode({ - 'nested_depth': 99, - }, True) - ) - - def test_list_detail(self): - self._test_list( - fields={'stack_id': 'teststack', 'with_detail': 'True'}, - expect='/stacks/teststack/resources?%s' % parse.urlencode({ - 'with_detail': True, - }, True) - ) - - def _test_list(self, fields, expect): - key = 'resources' - - class FakeResponse(object): - def json(self): - return {key: {}} - - class FakeClient(object): - def get(self, *args, **kwargs): - assert args[0] == expect - return FakeResponse() - - manager = resources.ResourceManager(FakeClient()) - manager.list(**fields) - - def test_metadata(self): - fields = {'stack_id': 'teststack', - 'resource_name': 'testresource'} - expect = ('GET', - '/stacks/teststack%2Fabcd1234/resources' - '/testresource/metadata') - key = 'metadata' - - manager = self._base_test(expect, key) - manager.metadata(**fields) - self.m.VerifyAll() - - def test_generate_template(self): - fields = {'resource_name': 'testresource'} - expect = ('GET', '/resource_types/testresource/template') - key = None - - class FakeAPI(object): - """Fake API and ensure request url is correct.""" - - def get(self, *args, **kwargs): - assert ('GET', args[0]) == expect - - def json_request(self, *args, **kwargs): - assert args == expect - ret = key and {key: []} or {} - return {}, {key: ret} - - manager = resources.ResourceManager(FakeAPI()) - self.m.StubOutWithMock(utils, 'get_response_body') - utils.get_response_body(mox.IgnoreArg()).AndReturn( - {key: key and {key: []} or {}}) - self.m.ReplayAll() - - manager.generate_template(**fields) - self.m.VerifyAll() - - def test_signal(self): - fields = {'stack_id': 'teststack', - 'resource_name': 'testresource', - 'data': 'Some content'} - expect = ('POST', - '/stacks/teststack%2Fabcd1234/resources' - '/testresource/signal') - key = 'signal' - - manager = self._base_test(expect, key) - manager.signal(**fields) - self.m.VerifyAll() - - -class ResourceStackNameTest(testtools.TestCase): - - def test_stack_name(self): - resource = resources.Resource(None, {"links": [{ - "href": "http://heat.example.com:8004/foo/12/resources/foobar", - "rel": "self" - }, { - "href": "http://heat.example.com:8004/foo/12", - "rel": "stack" - }]}) - self.assertEqual('foo', resource.stack_name) - - def test_stack_name_no_links(self): - resource = resources.Resource(None, {}) - self.assertIsNone(resource.stack_name) diff --git a/stacktaskclient/tests/unit/test_service.py b/stacktaskclient/tests/unit/test_service.py deleted file mode 100644 index 4213cab..0000000 --- a/stacktaskclient/tests/unit/test_service.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from heatclient import exc -import testtools - - -from heatclient.v1 import services - - -class ManageServiceTest(testtools.TestCase): - def setUp(self): - super(ManageServiceTest, self).setUp() - - def test_service_list(self): - class FakeResponse(object): - def json(self): - return {'services': []} - - class FakeClient(object): - def get(self, *args, **kwargs): - assert args[0] == ('/services') - return FakeResponse() - - manager = services.ServiceManager(FakeClient()) - self.assertEqual([], manager.list()) - - def test_service_list_403(self): - class FakeClient403(object): - - def get(self, *args, **kwargs): - assert args[0] == ('/services') - raise exc.HTTPForbidden() - - manager = services.ServiceManager(FakeClient403()) - self.assertRaises(exc.HTTPForbidden, - manager.list) - - def test_service_list_503(self): - class FakeClient503(object): - def get(self, *args, **kwargs): - assert args[0] == ('/services') - raise exc.HTTPServiceUnavailable() - - manager = services.ServiceManager(FakeClient503()) - self.assertRaises(exc.HTTPServiceUnavailable, - manager.list) diff --git a/stacktaskclient/tests/unit/test_shell.py b/stacktaskclient/tests/unit/test_shell.py deleted file mode 100644 index d24a5de..0000000 --- a/stacktaskclient/tests/unit/test_shell.py +++ /dev/null @@ -1,4581 +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 fixtures -import os -from oslotest import mockpatch -import re -import requests -import six -from six.moves.urllib import parse -from six.moves.urllib import request -import sys -import tempfile -import testscenarios -import testtools -import uuid -import yaml - -from oslo_serialization import jsonutils -from oslo_utils import encodeutils -from requests_mock.contrib import fixture as rm_fixture - -from keystoneclient import fixture as keystone_fixture - -from mox3 import mox - -from heatclient.common import http -from heatclient.common import utils -from heatclient import exc -import heatclient.shell -from heatclient.tests.unit import fakes -import heatclient.v1.shell - -load_tests = testscenarios.load_tests_apply_scenarios -TEST_VAR_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), - 'var')) - -BASE_HOST = 'http://keystone.example.com' -BASE_URL = "%s:5000/" % BASE_HOST -V2_URL = "%sv2.0" % BASE_URL -V3_URL = "%sv3" % BASE_URL - - -FAKE_ENV_KEYSTONE_V2 = { - 'OS_USERNAME': 'username', - 'OS_PASSWORD': 'password', - 'OS_TENANT_NAME': 'tenant_name', - 'OS_AUTH_URL': BASE_URL, -} - -FAKE_ENV_KEYSTONE_V3 = { - 'OS_USERNAME': 'username', - 'OS_PASSWORD': 'password', - 'OS_TENANT_NAME': 'tenant_name', - 'OS_AUTH_URL': BASE_URL, - 'OS_USER_DOMAIN_ID': 'default', - 'OS_PROJECT_DOMAIN_ID': 'default', -} - - -class TestCase(testtools.TestCase): - - tokenid = uuid.uuid4().hex - - def setUp(self): - super(TestCase, self).setUp() - self.requests = self.useFixture(rm_fixture.Fixture()) - # httpretty doesn't work as expected if http proxy environmen - # variable is set. - self.useFixture(fixtures.EnvironmentVariable('http_proxy')) - self.useFixture(fixtures.EnvironmentVariable('https_proxy')) - - def set_fake_env(self, fake_env): - client_env = ('OS_USERNAME', 'OS_PASSWORD', 'OS_TENANT_ID', - 'OS_TENANT_NAME', 'OS_AUTH_URL', 'OS_REGION_NAME', - 'OS_AUTH_TOKEN', 'OS_NO_CLIENT_AUTH', 'OS_SERVICE_TYPE', - 'OS_ENDPOINT_TYPE', 'HEAT_URL') - - for key in client_env: - self.useFixture( - fixtures.EnvironmentVariable(key, fake_env.get(key))) - - # required for testing with Python 2.6 - def assertRegexpMatches(self, text, expected_regexp, msg=None): - """Fail the test unless the text matches the regular expression.""" - if isinstance(expected_regexp, six.string_types): - expected_regexp = re.compile(expected_regexp) - if not expected_regexp.search(text): - msg = msg or "Regexp didn't match" - msg = '%s: %r not found in %r' % ( - msg, expected_regexp.pattern, text) - raise self.failureException(msg) - - # required for testing with Python 2.6 - def assertNotRegexpMatches(self, text, expected_regexp, msg=None): - try: - self.assertRegexpMatches(text, expected_regexp, msg) - except self.failureException: - pass - else: - raise self.failureException(msg) - - def shell_error(self, argstr, error_match): - orig = sys.stderr - sys.stderr = six.StringIO() - _shell = heatclient.shell.HeatShell() - e = self.assertRaises(Exception, _shell.main, argstr.split()) - self.assertRegexpMatches(e.__str__(), error_match) - err = sys.stderr.getvalue() - sys.stderr.close() - sys.stderr = orig - return err - - def register_keystone_v2_token_fixture(self): - v2_token = keystone_fixture.V2Token(token_id=self.tokenid) - service = v2_token.add_service('orchestration') - service.add_endpoint('http://heat.example.com', - admin='http://heat-admin.localdomain', - internal='http://heat.localdomain', - region='RegionOne') - self.requests.post('%s/tokens' % V2_URL, json=v2_token) - - def register_keystone_v3_token_fixture(self): - v3_token = keystone_fixture.V3Token() - service = v3_token.add_service('orchestration') - service.add_standard_endpoints(public='http://heat.example.com', - admin='http://heat-admin.localdomain', - internal='http://heat.localdomain') - self.requests.post('%s/auth/tokens' % V3_URL, - json=v3_token, - headers={'X-Subject-Token': self.tokenid}) - - def register_keystone_auth_fixture(self): - self.register_keystone_v2_token_fixture() - self.register_keystone_v3_token_fixture() - - version_list = keystone_fixture.DiscoveryList(href=BASE_URL) - self.requests.get(BASE_URL, json=version_list) - - # NOTE(tlashchova): this overrides the testtools.TestCase.patch method - # that does simple monkey-patching in favor of mock's patching - def patch(self, target, **kwargs): - mockfixture = self.useFixture(mockpatch.Patch(target, **kwargs)) - return mockfixture.mock - - -class EnvVarTest(TestCase): - - scenarios = [ - ('username', dict( - remove='OS_USERNAME', - err='You must provide a username')), - ('password', dict( - remove='OS_PASSWORD', - err='You must provide a password')), - ('tenant_name', dict( - remove='OS_TENANT_NAME', - err='You must provide a tenant id')), - ('auth_url', dict( - remove='OS_AUTH_URL', - err='You must provide an auth url')), - ] - - def test_missing_auth(self): - - fake_env = { - 'OS_USERNAME': 'username', - 'OS_PASSWORD': 'password', - 'OS_TENANT_NAME': 'tenant_name', - 'OS_AUTH_URL': 'http://no.where', - } - fake_env[self.remove] = None - self.set_fake_env(fake_env) - self.shell_error('stack-list', self.err) - - -class EnvVarTestToken(TestCase): - - scenarios = [ - ('tenant_id', dict( - remove='OS_TENANT_ID', - err='You must provide a tenant id')), - ('auth_url', dict( - remove='OS_AUTH_URL', - err='You must provide an auth url')), - ] - - def test_missing_auth(self): - - fake_env = { - 'OS_AUTH_TOKEN': 'atoken', - 'OS_TENANT_ID': 'tenant_id', - 'OS_AUTH_URL': 'http://no.where', - } - fake_env[self.remove] = None - self.set_fake_env(fake_env) - self.shell_error('stack-list', self.err) - - -class ShellParamValidationTest(TestCase): - - scenarios = [ - ('stack-create', dict( - command='stack-create ts -P "ab"', - err='Malformed parameter')), - ('stack-update', dict( - command='stack-update ts -P "a-b"', - err='Malformed parameter')), - ] - - def setUp(self): - super(ShellParamValidationTest, self).setUp() - self.m = mox.Mox() - self.addCleanup(self.m.VerifyAll) - self.addCleanup(self.m.UnsetStubs) - - def test_bad_parameters(self): - self.register_keystone_auth_fixture() - fake_env = { - 'OS_USERNAME': 'username', - 'OS_PASSWORD': 'password', - 'OS_TENANT_NAME': 'tenant_name', - 'OS_AUTH_URL': BASE_URL, - } - self.set_fake_env(fake_env) - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - cmd = '%s --template-file=%s ' % (self.command, template_file) - self.shell_error(cmd, self.err) - - -class ShellValidationTest(TestCase): - - def setUp(self): - super(ShellValidationTest, self).setUp() - self.m = mox.Mox() - self.addCleanup(self.m.VerifyAll) - self.addCleanup(self.m.UnsetStubs) - - def test_failed_auth(self): - self.register_keystone_auth_fixture() - self.m.StubOutWithMock(http.SessionClient, 'request') - failed_msg = 'Unable to authenticate user with credentials provided' - http.SessionClient.request( - '/stacks?', 'GET').AndRaise(exc.Unauthorized(failed_msg)) - - self.m.ReplayAll() - self.set_fake_env(FAKE_ENV_KEYSTONE_V2) - self.shell_error('stack-list', failed_msg) - - def test_stack_create_validation(self): - self.register_keystone_auth_fixture() - self.set_fake_env(FAKE_ENV_KEYSTONE_V2) - self.shell_error( - 'stack-create teststack ' - '--parameters="InstanceType=m1.large;DBUsername=wp;' - 'DBPassword=verybadpassword;KeyName=heat_key;' - 'LinuxDistribution=F17"', - 'Need to specify exactly one of') - - def test_stack_create_with_paramfile_validation(self): - self.register_keystone_auth_fixture() - self.set_fake_env(FAKE_ENV_KEYSTONE_V2) - self.shell_error( - 'stack-create teststack ' - '--parameter-file private_key=private_key.env ' - '--parameters="InstanceType=m1.large;DBUsername=wp;' - 'DBPassword=verybadpassword;KeyName=heat_key;' - 'LinuxDistribution=F17"', - 'Need to specify exactly one of') - - def test_stack_create_validation_keystone_v3(self): - self.register_keystone_auth_fixture() - self.set_fake_env(FAKE_ENV_KEYSTONE_V3) - self.shell_error( - 'stack-create teststack ' - '--parameters="InstanceType=m1.large;DBUsername=wp;' - 'DBPassword=verybadpassword;KeyName=heat_key;' - 'LinuxDistribution=F17"', - 'Need to specify exactly one of') - - -class ShellBase(TestCase): - - def setUp(self): - super(ShellBase, self).setUp() - self.m = mox.Mox() - self.m.StubOutWithMock(http.HTTPClient, 'json_request') - self.m.StubOutWithMock(http.HTTPClient, 'raw_request') - self.m.StubOutWithMock(http.SessionClient, 'request') - self.client = http.SessionClient - self.addCleanup(self.m.VerifyAll) - self.addCleanup(self.m.UnsetStubs) - - # Some tests set exc.verbose = 1, so reset on cleanup - def unset_exc_verbose(): - exc.verbose = 0 - - self.addCleanup(unset_exc_verbose) - - def shell(self, argstr): - orig = sys.stdout - try: - sys.stdout = six.StringIO() - _shell = heatclient.shell.HeatShell() - _shell.main(argstr.split()) - self.subcommands = _shell.subcommands.keys() - except SystemExit: - exc_type, exc_value, exc_traceback = sys.exc_info() - self.assertEqual(0, exc_value.code) - finally: - out = sys.stdout.getvalue() - sys.stdout.close() - sys.stdout = orig - - return out - - -class ShellTestNoMox(TestCase): - # NOTE(dhu): This class is reserved for no Mox usage. Instead, - # use requests_mock to expose errors from json_request. - def setUp(self): - super(ShellTestNoMox, self).setUp() - self._set_fake_env() - - def _set_fake_env(self): - self.set_fake_env({ - 'OS_USERNAME': 'username', - 'OS_PASSWORD': 'password', - 'OS_TENANT_NAME': 'tenant_name', - 'HEAT_URL': 'http://heat.example.com', - 'OS_AUTH_URL': BASE_URL, - 'OS_NO_CLIENT_AUTH': 'True' - }) - - def shell(self, argstr): - orig = sys.stdout - try: - sys.stdout = six.StringIO() - _shell = heatclient.shell.HeatShell() - _shell.main(argstr.split()) - self.subcommands = _shell.subcommands.keys() - except SystemExit: - exc_type, exc_value, exc_traceback = sys.exc_info() - self.assertEqual(0, exc_value.code) - finally: - out = sys.stdout.getvalue() - sys.stdout.close() - sys.stdout = orig - - return out - - # This function tests err msg handling - def test_stack_create_parameter_missing_err_msg(self): - self.register_keystone_auth_fixture() - - resp_dict = {"error": - {"message": 'The Parameter (key_name) was not provided.', - "type": "UserParameterMissing"}} - - self.requests.post('http://heat.example.com/stacks', - status_code=400, - headers={'Content-Type': 'application/json'}, - json=resp_dict) - - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - - self.shell_error('stack-create -f %s stack' % template_file, - 'The Parameter \(key_name\) was not provided.') - - def test_event_list(self): - eventid1 = uuid.uuid4().hex - eventid2 = uuid.uuid4().hex - self.register_keystone_auth_fixture() - - h = {'Content-Type': 'text/plain; charset=UTF-8', - 'location': 'http://heat.example.com/stacks/myStack/60f83b5e'} - self.requests.get('http://heat.example.com/stacks/myStack', - status_code=302, - headers=h) - - resp, resp_dict = fakes.mock_script_event_list( - resource_name="myDeployment", rsrc_eventid1=eventid1, - rsrc_eventid2=eventid2, fakehttp=False - ) - - self.requests.get('http://heat.example.com/stacks/myStack%2F60f83b5e/' - 'resources/myDeployment/events', - headers={'Content-Type': 'application/json'}, - json=resp_dict) - - list_text = self.shell('event-list -r myDeployment myStack') - - required = [ - 'resource_name', - 'id', - 'resource_status_reason', - 'resource_status', - 'event_time', - 'myDeployment', - eventid1, - eventid2, - 'state changed', - 'CREATE_IN_PROGRESS', - '2013-12-05T14:14:31Z', - '2013-12-05T14:14:32Z', - ] - - for r in required: - self.assertRegexpMatches(list_text, r) - - -class ShellTestNoMoxV3(ShellTestNoMox): - - def _set_fake_env(self): - fake_env_kwargs = {'OS_NO_CLIENT_AUTH': 'True', - 'HEAT_URL': 'http://heat.example.com'} - fake_env_kwargs.update(FAKE_ENV_KEYSTONE_V3) - self.set_fake_env(fake_env_kwargs) - - -class ShellTestEndpointType(TestCase): - - def setUp(self): - super(ShellTestEndpointType, self).setUp() - self.m = mox.Mox() - self.m.StubOutWithMock(http, '_construct_http_client') - self.m.StubOutWithMock(heatclient.v1.shell, 'do_stack_list') - self.addCleanup(self.m.VerifyAll) - self.addCleanup(self.m.UnsetStubs) - self.set_fake_env(FAKE_ENV_KEYSTONE_V2) - - def test_endpoint_type_public_url(self): - self.register_keystone_auth_fixture() - kwargs = { - 'auth_url': 'http://keystone.example.com:5000/', - 'session': mox.IgnoreArg(), - 'auth': mox.IgnoreArg(), - 'service_type': 'orchestration', - 'endpoint_type': 'publicURL', - 'region_name': '', - 'username': 'username', - 'password': 'password', - 'include_pass': False - } - http._construct_http_client(u'http://heat.example.com', **kwargs) - heatclient.v1.shell.do_stack_list(mox.IgnoreArg(), mox.IgnoreArg()) - - self.m.ReplayAll() - heatclient.shell.main(('stack-list',)) - - def test_endpoint_type_admin_url(self): - self.register_keystone_auth_fixture() - kwargs = { - 'auth_url': 'http://keystone.example.com:5000/', - 'session': mox.IgnoreArg(), - 'auth': mox.IgnoreArg(), - 'service_type': 'orchestration', - 'endpoint_type': 'adminURL', - 'region_name': '', - 'username': 'username', - 'password': 'password', - 'include_pass': False - } - http._construct_http_client(u'http://heat-admin.localdomain', **kwargs) - heatclient.v1.shell.do_stack_list(mox.IgnoreArg(), mox.IgnoreArg()) - - self.m.ReplayAll() - heatclient.shell.main(('--os-endpoint-type=adminURL', 'stack-list',)) - - def test_endpoint_type_internal_url(self): - self.register_keystone_auth_fixture() - self.useFixture(fixtures.EnvironmentVariable('OS_ENDPOINT_TYPE', - 'internalURL')) - kwargs = { - 'auth_url': 'http://keystone.example.com:5000/', - 'session': mox.IgnoreArg(), - 'auth': mox.IgnoreArg(), - 'service_type': 'orchestration', - 'endpoint_type': 'internalURL', - 'region_name': '', - 'username': 'username', - 'password': 'password', - 'include_pass': False - } - http._construct_http_client(u'http://heat.localdomain', **kwargs) - heatclient.v1.shell.do_stack_list(mox.IgnoreArg(), mox.IgnoreArg()) - - self.m.ReplayAll() - heatclient.shell.main(('stack-list',)) - - -class ShellTestCommon(ShellBase): - - def setUp(self): - super(ShellTestCommon, self).setUp() - self.client = http.SessionClient - self.set_fake_env(FAKE_ENV_KEYSTONE_V2) - - def test_help_unknown_command(self): - self.assertRaises(exc.CommandError, self.shell, 'help foofoo') - - def test_help(self): - required = [ - '^usage: heat', - '(?m)^See "heat help COMMAND" for help on a specific command', - ] - for argstr in ['--help', 'help']: - help_text = self.shell(argstr) - for r in required: - self.assertRegexpMatches(help_text, r) - - def test_command_help(self): - output = self.shell('help help') - self.assertIn('usage: heat help []', output) - subcommands = list(self.subcommands) - for command in subcommands: - if command.replace('_', '-') == 'bash-completion': - continue - output1 = self.shell('help %s' % command) - output2 = self.shell('%s --help' % command) - self.assertEqual(output1, output2) - self.assertRegexpMatches(output1, '^usage: heat %s' % command) - - def test_debug_switch_raises_error(self): - self.register_keystone_auth_fixture() - if self.client == http.SessionClient: - self.client.request( - '/stacks?', 'GET').AndRaise(exc.Unauthorized("FAIL")) - else: - self.client.json_request( - 'GET', '/stacks?').AndRaise(exc.Unauthorized("FAIL")) - - self.m.ReplayAll() - - args = ['--debug', 'stack-list'] - self.assertRaises(exc.Unauthorized, heatclient.shell.main, args) - - def test_dash_d_switch_raises_error(self): - self.register_keystone_auth_fixture() - if self.client == http.SessionClient: - self.client.request( - '/stacks?', 'GET').AndRaise(exc.CommandError("FAIL")) - else: - self.client.json_request( - 'GET', '/stacks?').AndRaise(exc.CommandError("FAIL")) - - self.m.ReplayAll() - - args = ['-d', 'stack-list'] - self.assertRaises(exc.CommandError, heatclient.shell.main, args) - - def test_no_debug_switch_no_raises_errors(self): - self.register_keystone_auth_fixture() - if self.client == http.SessionClient: - self.client.request( - '/stacks?', 'GET').AndRaise(exc.Unauthorized("FAIL")) - else: - self.client.json_request( - 'GET', '/stacks?').AndRaise(exc.Unauthorized("FAIL")) - - self.m.ReplayAll() - - args = ['stack-list'] - self.assertRaises(SystemExit, heatclient.shell.main, args) - - def test_help_on_subcommand(self): - required = [ - '^usage: heat stack-list', - "(?m)^List the user's stacks", - ] - argstrings = [ - 'help stack-list', - ] - for argstr in argstrings: - help_text = self.shell(argstr) - for r in required: - self.assertRegexpMatches(help_text, r) - - -class ShellTestUserPass(ShellBase): - - def setUp(self): - super(ShellTestUserPass, self).setUp() - if self.client is None: - self.client = http.SessionClient - self._set_fake_env() - - def _set_fake_env(self): - self.set_fake_env(FAKE_ENV_KEYSTONE_V2) - - def test_stack_list(self): - self.register_keystone_auth_fixture() - fakes.script_heat_list(client=self.client) - - self.m.ReplayAll() - - list_text = self.shell('stack-list') - - required = [ - 'id', - 'stack_status', - 'creation_time', - 'teststack', - '1', - 'CREATE_COMPLETE', - 'IN_PROGRESS', - ] - for r in required: - self.assertRegexpMatches(list_text, r) - self.assertNotRegexpMatches(list_text, 'parent') - - def test_stack_list_show_nested(self): - self.register_keystone_auth_fixture() - expected_url = '/stacks?%s' % parse.urlencode({ - 'show_nested': True, - }, True) - fakes.script_heat_list(expected_url, show_nested=True, - client=self.client) - - self.m.ReplayAll() - - list_text = self.shell('stack-list' - ' --show-nested') - - required = [ - 'teststack', - 'teststack2', - 'teststack_nested', - 'parent', - 'theparentof3' - ] - for r in required: - self.assertRegexpMatches(list_text, r) - - def test_stack_list_show_owner(self): - self.register_keystone_auth_fixture() - fakes.script_heat_list(client=self.client) - self.m.ReplayAll() - - list_text = self.shell('stack-list --show-owner') - - required = [ - 'stack_owner', - 'testowner', - ] - for r in required: - self.assertRegexpMatches(list_text, r) - - def test_parsable_error(self): - self.register_keystone_auth_fixture() - message = "The Stack (bad) could not be found." - resp_dict = { - "explanation": "The resource could not be found.", - "code": 404, - "error": { - "message": message, - "type": "StackNotFound", - "traceback": "", - }, - "title": "Not Found" - } - - fakes.script_heat_error(jsonutils.dumps(resp_dict), - client=self.client) - - self.m.ReplayAll() - - e = self.assertRaises(exc.HTTPException, self.shell, "stack-show bad") - self.assertEqual("ERROR: " + message, str(e)) - - def test_parsable_verbose(self): - self.register_keystone_auth_fixture() - message = "The Stack (bad) could not be found." - resp_dict = { - "explanation": "The resource could not be found.", - "code": 404, - "error": { - "message": message, - "type": "StackNotFound", - "traceback": "", - }, - "title": "Not Found" - } - - fakes.script_heat_error(jsonutils.dumps(resp_dict), self.client) - - self.m.ReplayAll() - - exc.verbose = 1 - - e = self.assertRaises(exc.HTTPException, self.shell, "stack-show bad") - self.assertIn(message, str(e)) - - def test_parsable_malformed_error(self): - self.register_keystone_auth_fixture() - invalid_json = "ERROR: {Invalid JSON Error." - fakes.script_heat_error(invalid_json, client=self.client) - self.m.ReplayAll() - e = self.assertRaises(exc.HTTPException, self.shell, "stack-show bad") - self.assertEqual("ERROR: " + invalid_json, str(e)) - - def test_parsable_malformed_error_missing_message(self): - self.register_keystone_auth_fixture() - missing_message = { - "explanation": "The resource could not be found.", - "code": 404, - "error": { - "type": "StackNotFound", - "traceback": "", - }, - "title": "Not Found" - } - - fakes.script_heat_error(jsonutils.dumps(missing_message), - client=self.client) - self.m.ReplayAll() - - e = self.assertRaises(exc.HTTPException, self.shell, "stack-show bad") - self.assertEqual("ERROR: Internal Error", str(e)) - - def test_parsable_malformed_error_missing_traceback(self): - self.register_keystone_auth_fixture() - message = "The Stack (bad) could not be found." - resp_dict = { - "explanation": "The resource could not be found.", - "code": 404, - "error": { - "message": message, - "type": "StackNotFound", - }, - "title": "Not Found" - } - - fakes.script_heat_error(jsonutils.dumps(resp_dict), client=self.client) - self.m.ReplayAll() - - exc.verbose = 1 - - e = self.assertRaises(exc.HTTPException, self.shell, "stack-show bad") - self.assertEqual("ERROR: The Stack (bad) could not be found.\n", - str(e)) - - def test_stack_show(self): - self.register_keystone_auth_fixture() - resp_dict = {"stack": { - "id": "1", - "stack_name": "teststack", - "stack_status": 'CREATE_COMPLETE', - "creation_time": "2012-10-25T01:58:47Z" - }} - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack/1', 'GET').AndReturn(resp) - else: - self.client.json_request( - 'GET', '/stacks/teststack/1').AndReturn((resp, resp_dict)) - - self.m.ReplayAll() - - list_text = self.shell('stack-show teststack/1') - - required = [ - 'id', - 'stack_name', - 'stack_status', - 'creation_time', - 'teststack', - 'CREATE_COMPLETE', - '2012-10-25T01:58:47Z' - ] - for r in required: - self.assertRegexpMatches(list_text, r) - - def _output_fake_response(self): - - resp_dict = {"stack": { - "id": "1", - "stack_name": "teststack", - "stack_status": 'CREATE_COMPLETE', - "creation_time": "2012-10-25T01:58:47Z", - "outputs": [ - { - "output_value": "value1", - "output_key": "output1", - "description": "test output 1", - }, - { - "output_value": ["output", "value", "2"], - "output_key": "output2", - "description": "test output 2", - }, - { - "output_value": u"test\u2665", - "output_key": "output_uni", - "description": "test output unicode", - }, - ], - "creation_time": "2012-10-25T01:58:47Z" - }} - - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack/1', 'GET').MultipleTimes().AndReturn( - resp) - else: - self.client.json_request( - 'GET', '/stacks/teststack/1').MultipleTimes().AndReturn( - (resp, resp_dict)) - - self.m.ReplayAll() - - def _error_output_fake_response(self): - - resp_dict = {"stack": { - "id": "1", - "stack_name": "teststack", - "stack_status": 'CREATE_COMPLETE', - "creation_time": "2012-10-25T01:58:47Z", - "outputs": [ - { - "output_value": "null", - "output_key": "output1", - "description": "test output 1", - "output_error": "The Referenced Attribute (0 PublicIP) " - "is incorrect." - }, - ], - "creation_time": "2012-10-25T01:58:47Z" - }} - - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack/1', 'GET').AndReturn(resp) - else: - self.client.json_request( - 'GET', '/stacks/teststack/1').AndReturn((resp, resp_dict)) - - self.m.ReplayAll() - - def test_output_list(self): - self.register_keystone_auth_fixture() - self._output_fake_response() - list_text = self.shell('output-list teststack/1') - for r in ['output1', 'output2', 'output_uni']: - self.assertRegexpMatches(list_text, r) - - def test_output_show(self): - self.register_keystone_auth_fixture() - - self._output_fake_response() - list_text = self.shell('output-show teststack/1 output1') - self.assertEqual('"value1"\n', list_text) - - list_text = self.shell('output-show -F raw teststack/1 output1') - self.assertEqual('value1\n', list_text) - - list_text = self.shell('output-show -F raw teststack/1 output2') - self.assertEqual('[\n "output", \n "value", \n "2"\n]\n', - list_text) - - list_text = self.shell('output-show -F json teststack/1 output2') - self.assertEqual('[\n "output", \n "value", \n "2"\n]\n', - list_text) - - def test_output_show_unicode(self): - self.register_keystone_auth_fixture() - self._output_fake_response() - list_text = self.shell('output-show teststack/1 output_uni') - self.assertEqual(u'"test\u2665"\n', list_text) - - def test_output_show_all(self): - self.register_keystone_auth_fixture() - self._output_fake_response() - list_text = self.shell('output-show teststack/1 --all') - for r in ['output1', 'value1', 'output2', 'test output unicode']: - self.assertRegexpMatches(list_text, r) - - def test_output_show_missing_arg(self): - self.register_keystone_auth_fixture() - error = self.assertRaises( - exc.CommandError, self.shell, 'output-show teststack/1') - self.assertIn('either or --all argument is needed.', - str(error)) - - def test_output_show_error(self): - self.register_keystone_auth_fixture() - self._error_output_fake_response() - error = self.assertRaises( - exc.CommandError, self.shell, - 'output-show teststack/1 output1') - self.assertIn('The Referenced Attribute (0 PublicIP) is incorrect.', - str(error)) - - def test_template_show_cfn(self): - self.register_keystone_auth_fixture() - template_data = open(os.path.join(TEST_VAR_DIR, - 'minimal.template')).read() - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - template_data) - resp_dict = jsonutils.loads(template_data) - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack/template', 'GET').AndReturn(resp) - else: - self.client.json_request( - 'GET', '/stacks/teststack/template').AndReturn((resp, - resp_dict)) - - self.m.ReplayAll() - - show_text = self.shell('template-show teststack') - required = [ - '{', - ' "AWSTemplateFormatVersion": "2010-09-09"', - ' "Outputs": {}', - ' "Resources": {}', - ' "Parameters": {}', - '}' - ] - for r in required: - self.assertRegexpMatches(show_text, r) - - def test_template_show_cfn_unicode(self): - self.register_keystone_auth_fixture() - resp_dict = {"AWSTemplateFormatVersion": "2010-09-09", - "Description": u"test\u2665", - "Outputs": {}, - "Resources": {}, - "Parameters": {}} - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack/template', 'GET').AndReturn(resp) - else: - self.client.json_request( - 'GET', '/stacks/teststack/template').AndReturn((resp, - resp_dict)) - - self.m.ReplayAll() - - show_text = self.shell('template-show teststack') - required = [ - '{', - ' "AWSTemplateFormatVersion": "2010-09-09"', - ' "Outputs": {}', - ' "Parameters": {}', - u' "Description": "test\u2665"', - ' "Resources": {}', - '}' - ] - for r in required: - self.assertRegexpMatches(show_text, r) - - def test_template_show_hot(self): - self.register_keystone_auth_fixture() - resp_dict = {"heat_template_version": "2013-05-23", - "parameters": {}, - "resources": {}, - "outputs": {}} - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack/template', 'GET').AndReturn(resp) - else: - self.client.json_request( - 'GET', '/stacks/teststack/template').AndReturn((resp, - resp_dict)) - - self.m.ReplayAll() - - show_text = self.shell('template-show teststack') - required = [ - "heat_template_version: '2013-05-23'", - "outputs: {}", - "parameters: {}", - "resources: {}" - ] - for r in required: - self.assertRegexpMatches(show_text, r) - - def _test_stack_preview(self, timeout=None, enable_rollback=False): - self.register_keystone_auth_fixture() - resp_dict = {"stack": { - "id": "1", - "stack_name": "teststack", - "stack_status": 'CREATE_COMPLETE', - "resources": {'1': {'name': 'r1'}}, - "creation_time": "2012-10-25T01:58:47Z", - "timeout_mins": timeout, - "disable_rollback": not(enable_rollback) - }} - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'location': 'http://no.where/v1/tenant_id/stacks/teststack2/2', - 'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - if self.client is http.HTTPClient: - headers = {'X-Auth-Key': 'password', 'X-Auth-User': 'username'} - else: - headers = {} - if self.client == http.SessionClient: - self.client.request( - '/stacks/preview', 'POST', data=mox.IgnoreArg(), - headers=headers - ).AndReturn(resp) - else: - self.client.json_request( - 'POST', '/stacks/preview', data=mox.IgnoreArg(), - headers=headers - ).AndReturn((resp, resp_dict)) - - self.m.ReplayAll() - - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - cmd = ('stack-preview teststack ' - '--template-file=%s ' - '--parameters="InstanceType=m1.large;DBUsername=wp;' - 'DBPassword=verybadpassword;KeyName=heat_key;' - 'LinuxDistribution=F17" ' % template_file) - if enable_rollback: - cmd += '-r ' - if timeout: - cmd += '--timeout=%d ' % timeout - preview_text = self.shell(cmd) - - required = [ - 'stack_name', - 'id', - 'teststack', - '1', - 'resources', - 'timeout_mins', - 'disable_rollback' - ] - - for r in required: - self.assertRegexpMatches(preview_text, r) - - def test_stack_preview(self): - self._test_stack_preview() - - def test_stack_preview_timeout(self): - self._test_stack_preview(300, True) - - def test_stack_create(self): - self.register_keystone_auth_fixture() - resp = fakes.FakeHTTPResponse( - 201, - 'Created', - {'location': 'http://no.where/v1/tenant_id/stacks/teststack2/2'}, - None) - if self.client is http.HTTPClient: - headers = {'X-Auth-Key': 'password', 'X-Auth-User': 'username'} - else: - headers = {} - if self.client == http.SessionClient: - self.client.request( - '/stacks', 'POST', data=mox.IgnoreArg(), - headers=headers).AndReturn(resp) - else: - self.client.json_request( - 'POST', '/stacks', data=mox.IgnoreArg(), - headers=headers - ).AndReturn((resp, None)) - fakes.script_heat_list(client=self.client) - self.m.ReplayAll() - - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - create_text = self.shell( - 'stack-create teststack ' - '--template-file=%s ' - '--parameters="InstanceType=m1.large;DBUsername=wp;' - 'DBPassword=verybadpassword;KeyName=heat_key;' - 'LinuxDistribution=F17"' % template_file) - - required = [ - 'stack_name', - 'id', - 'teststack', - '1' - ] - - for r in required: - self.assertRegexpMatches(create_text, r) - - def test_create_success_with_poll(self): - self.register_keystone_auth_fixture() - - stack_create_resp_dict = {"stack": { - "id": "teststack2/2", - "stack_name": "teststack2", - "stack_status": 'CREATE_IN_PROGRESS', - "creation_time": "2012-10-25T01:58:47Z" - }} - stack_create_resp = fakes.FakeHTTPResponse( - 201, - 'Created', - {'location': 'http://no.where/v1/tenant_id/stacks/teststack2/2'}, - jsonutils.dumps(stack_create_resp_dict)) - if self.client == http.SessionClient: - headers = {} - self.client.request( - '/stacks', 'POST', data=mox.IgnoreArg(), - headers=headers).AndReturn(stack_create_resp) - else: - headers = {'X-Auth-Key': 'password', 'X-Auth-User': 'username'} - self.client.json_request( - 'POST', '/stacks', data=mox.IgnoreArg(), - headers=headers - ).AndReturn((stack_create_resp, None)) - fakes.script_heat_list(client=self.client) - - stack_show_resp_dict = {"stack": { - "id": "1", - "stack_name": "teststack", - "stack_status": 'CREATE_COMPLETE', - "creation_time": "2012-10-25T01:58:47Z" - }} - stack_show_resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(stack_show_resp_dict)) - - event_list_resp, event_list_resp_dict = fakes.mock_script_event_list( - stack_name="teststack2") - stack_id = 'teststack2' - - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack2', 'GET').MultipleTimes().AndReturn( - stack_show_resp) - self.client.request( - '/stacks/%s/events?sort_dir=asc' % stack_id, 'GET' - ).MultipleTimes().AndReturn(event_list_resp) - else: - self.client.json_request( - 'GET', '/stacks/teststack2').MultipleTimes().AndReturn( - (stack_show_resp, stack_show_resp_dict)) - http.HTTPClient.json_request( - 'GET', '/stacks/%s/events?sort_dir=asc' % stack_id - ).MultipleTimes().AndReturn((event_list_resp, - event_list_resp_dict)) - self.m.ReplayAll() - - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - create_text = self.shell( - 'stack-create teststack2 ' - '--poll 4 ' - '--template-file=%s ' - '--parameters="InstanceType=m1.large;DBUsername=wp;' - 'DBPassword=verybadpassword;KeyName=heat_key;' - 'LinuxDistribution=F17"' % template_file) - - required = [ - 'id', - 'stack_name', - 'stack_status', - '2', - 'teststack2', - 'IN_PROGRESS', - '14:14:30', '2013-12-05', '0159dccd-65e1-46e8-a094-697d20b009e5', - 'CREATE_IN_PROGRESS', 'state changed', - '14:14:31', '7fecaeed-d237-4559-93a5-92d5d9111205', - 'testresource', - '14:14:32', 'e953547a-18f8-40a7-8e63-4ec4f509648b', - 'CREATE_COMPLETE', - '14:14:33', '8f591a36-7190-4adb-80da-00191fe22388' - ] - - for r in required: - self.assertRegexpMatches(create_text, r) - - def test_create_failed_with_poll(self): - self.register_keystone_auth_fixture() - stack_create_resp_dict = {"stack": { - "id": "teststack2/2", - "stack_name": "teststack2", - "stack_status": 'CREATE_IN_PROGRESS', - "creation_time": "2012-10-25T01:58:47Z" - }} - stack_create_resp = fakes.FakeHTTPResponse( - 201, - 'Created', - {'location': 'http://no.where/v1/tenant_id/stacks/teststack2/2'}, - jsonutils.dumps(stack_create_resp_dict)) - if self.client == http.SessionClient: - headers = {} - self.client.request( - '/stacks', 'POST', data=mox.IgnoreArg(), - headers=headers).AndReturn(stack_create_resp) - else: - headers = {'X-Auth-Key': 'password', 'X-Auth-User': 'username'} - self.client.json_request( - 'POST', '/stacks', data=mox.IgnoreArg(), - headers=headers - ).AndReturn((stack_create_resp, None)) - fakes.script_heat_list(client=self.client) - - stack_show_resp_dict = {"stack": { - "id": "1", - "stack_name": "teststack", - "stack_status": 'CREATE_COMPLETE', - "creation_time": "2012-10-25T01:58:47Z" - }} - stack_show_resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(stack_show_resp_dict)) - - event_list_resp, event_list_resp_dict = fakes.mock_script_event_list( - stack_name="teststack2", action="CREATE", final_state="FAILED") - stack_id = 'teststack2' - - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack2', 'GET').MultipleTimes().AndReturn( - stack_show_resp) - self.client.request( - '/stacks/%s/events?sort_dir=asc' % stack_id, 'GET' - ).MultipleTimes().AndReturn(event_list_resp) - else: - self.client.json_request( - 'GET', '/stacks/teststack2').MultipleTimes().AndReturn( - (stack_show_resp, stack_show_resp_dict)) - http.HTTPClient.json_request( - 'GET', '/stacks/%s/events?sort_dir=asc' % stack_id - ).MultipleTimes().AndReturn((event_list_resp, - event_list_resp_dict)) - - self.m.ReplayAll() - - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - - e = self.assertRaises(exc.StackFailure, self.shell, - 'stack-create teststack2 --poll ' - '--template-file=%s --parameters="InstanceType=' - 'm1.large;DBUsername=wp;DBPassword=password;' - 'KeyName=heat_key;LinuxDistribution=F17' % - template_file) - self.assertEqual("\n Stack teststack2 CREATE_FAILED \n", - str(e)) - - def test_stack_create_param_file(self): - self.register_keystone_auth_fixture() - resp = fakes.FakeHTTPResponse( - 201, - 'Created', - {'location': 'http://no.where/v1/tenant_id/stacks/teststack2/2'}, - None) - if self.client is http.HTTPClient: - headers = {'X-Auth-Key': 'password', 'X-Auth-User': 'username'} - else: - headers = {} - if self.client == http.SessionClient: - self.client.request( - '/stacks', 'POST', data=mox.IgnoreArg(), - headers=headers - ).AndReturn(resp) - else: - self.client.json_request( - 'POST', '/stacks', data=mox.IgnoreArg(), - headers=headers - ).AndReturn((resp, None)) - fakes.script_heat_list(client=self.client) - - self.m.StubOutWithMock(utils, 'read_url_content') - url = 'file://%s/private_key.env' % TEST_VAR_DIR - utils.read_url_content(url).AndReturn('xxxxxx') - self.m.ReplayAll() - - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - create_text = self.shell( - 'stack-create teststack ' - '--template-file=%s ' - '--parameter-file private_key=private_key.env ' - '--parameters="InstanceType=m1.large;DBUsername=wp;' - 'DBPassword=verybadpassword;KeyName=heat_key;' - 'LinuxDistribution=F17"' % template_file) - - required = [ - 'stack_name', - 'id', - 'teststack', - '1' - ] - - for r in required: - self.assertRegexpMatches(create_text, r) - - def test_stack_create_only_param_file(self): - self.register_keystone_auth_fixture() - resp = fakes.FakeHTTPResponse( - 201, - 'Created', - {'location': 'http://no.where/v1/tenant_id/stacks/teststack2/2'}, - None) - if self.client is http.HTTPClient: - headers = {'X-Auth-Key': 'password', 'X-Auth-User': 'username'} - else: - headers = {} - if self.client == http.SessionClient: - self.client.request( - '/stacks', 'POST', data=mox.IgnoreArg(), - headers=headers - ).AndReturn(resp) - else: - self.client.json_request( - 'POST', '/stacks', data=mox.IgnoreArg(), - headers=headers - ).AndReturn((resp, None)) - fakes.script_heat_list(client=self.client) - - self.m.StubOutWithMock(utils, 'read_url_content') - url = 'file://%s/private_key.env' % TEST_VAR_DIR - utils.read_url_content(url).AndReturn('xxxxxx') - self.m.ReplayAll() - - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - create_text = self.shell( - 'stack-create teststack ' - '--template-file=%s ' - '--parameter-file private_key=private_key.env ' - % template_file) - - required = [ - 'stack_name', - 'id', - 'teststack', - '1' - ] - - for r in required: - self.assertRegexpMatches(create_text, r) - - def test_stack_create_timeout(self): - self.register_keystone_auth_fixture() - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - template_data = open(template_file).read() - resp = fakes.FakeHTTPResponse( - 201, - 'Created', - {'location': 'http://no.where/v1/tenant_id/stacks/teststack2/2'}, - None) - expected_data = { - 'files': {}, - 'disable_rollback': True, - 'parameters': {'DBUsername': 'wp', - 'KeyName': 'heat_key', - 'LinuxDistribution': 'F17"', - '"InstanceType': 'm1.large', - 'DBPassword': 'verybadpassword'}, - 'stack_name': 'teststack', - 'environment': {}, - 'template': jsonutils.loads(template_data), - 'timeout_mins': 123} - if self.client is http.HTTPClient: - headers = {'X-Auth-Key': 'password', 'X-Auth-User': 'username'} - else: - headers = {} - if self.client == http.SessionClient: - self.client.request( - '/stacks', 'POST', data=expected_data, - headers=headers - ).AndReturn(resp) - else: - self.client.json_request( - 'POST', '/stacks', data=expected_data, - headers=headers - ).AndReturn((resp, None)) - fakes.script_heat_list(client=self.client) - - self.m.ReplayAll() - - create_text = self.shell( - 'stack-create teststack ' - '--template-file=%s ' - '--timeout=123 ' - '--parameters="InstanceType=m1.large;DBUsername=wp;' - 'DBPassword=verybadpassword;KeyName=heat_key;' - 'LinuxDistribution=F17"' % template_file) - - required = [ - 'stack_name', - 'id', - 'teststack', - '1' - ] - - for r in required: - self.assertRegexpMatches(create_text, r) - - def test_stack_update_timeout(self): - self.register_keystone_auth_fixture() - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - template_data = open(template_file).read() - resp = fakes.FakeHTTPResponse( - 202, - 'Accepted', - {}, - 'The request is accepted for processing.') - - expected_data = { - 'files': {}, - 'environment': {}, - 'template': jsonutils.loads(template_data), - 'parameters': {'DBUsername': 'wp', - 'KeyName': 'heat_key', - 'LinuxDistribution': 'F17"', - '"InstanceType': 'm1.large', - 'DBPassword': 'verybadpassword'}, - 'timeout_mins': 123, - 'disable_rollback': True} - if self.client is http.HTTPClient: - headers = {'X-Auth-Key': 'password', 'X-Auth-User': 'username'} - else: - headers = {} - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack2/2', 'PUT', - data=expected_data, - headers=headers - ).AndReturn(resp) - else: - self.client.json_request( - 'PUT', '/stacks/teststack2/2', - data=expected_data, - headers=headers - ).AndReturn((resp, None)) - fakes.script_heat_list(client=self.client) - - self.m.ReplayAll() - - update_text = self.shell( - 'stack-update teststack2/2 ' - '--template-file=%s ' - '--timeout 123 ' - '--rollback off ' - '--parameters="InstanceType=m1.large;DBUsername=wp;' - 'DBPassword=verybadpassword;KeyName=heat_key;' - 'LinuxDistribution=F17"' % template_file) - - required = [ - 'stack_name', - 'id', - 'teststack2', - '1' - ] - for r in required: - self.assertRegexpMatches(update_text, r) - - def test_stack_create_url(self): - self.register_keystone_auth_fixture() - resp = fakes.FakeHTTPResponse( - 201, - 'Created', - {'location': 'http://no.where/v1/tenant_id/stacks/teststack2/2'}, - None) - self.m.StubOutWithMock(request, 'urlopen') - request.urlopen('http://no.where/minimal.template').AndReturn( - six.StringIO('{"AWSTemplateFormatVersion" : "2010-09-09"}')) - - expected_data = { - 'files': {}, - 'disable_rollback': True, - 'stack_name': 'teststack', - 'environment': {}, - 'template': {"AWSTemplateFormatVersion": "2010-09-09"}, - 'parameters': {'DBUsername': 'wp', - 'KeyName': 'heat_key', - 'LinuxDistribution': 'F17"', - '"InstanceType': 'm1.large', - 'DBPassword': 'verybadpassword'}} - - if self.client is http.HTTPClient: - headers = {'X-Auth-Key': 'password', 'X-Auth-User': 'username'} - else: - headers = {} - if self.client == http.SessionClient: - self.client.request( - '/stacks', 'POST', data=expected_data, - headers=headers).AndReturn(resp) - else: - self.client.json_request( - 'POST', '/stacks', data=expected_data, - headers=headers - ).AndReturn((resp, None)) - fakes.script_heat_list(client=self.client) - - self.m.ReplayAll() - - create_text = self.shell( - 'stack-create teststack ' - '--template-url=http://no.where/minimal.template ' - '--parameters="InstanceType=m1.large;DBUsername=wp;' - 'DBPassword=verybadpassword;KeyName=heat_key;' - 'LinuxDistribution=F17"') - - required = [ - 'stack_name', - 'id', - 'teststack2', - '2' - ] - for r in required: - self.assertRegexpMatches(create_text, r) - - def test_stack_create_object(self): - self.register_keystone_auth_fixture() - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - template_data = open(template_file).read() - - raw_resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {}, - template_data) - - if self.client == http.SessionClient: - self.client.request( - 'http://no.where/container/minimal.template', - 'GET' - ).AndReturn(raw_resp) - else: - self.client.raw_request( - 'GET', - 'http://no.where/container/minimal.template', - ).AndReturn(raw_resp) - - resp = fakes.FakeHTTPResponse( - 201, - 'Created', - {'location': 'http://no.where/v1/tenant_id/stacks/teststack2/2'}, - None) - if self.client is http.HTTPClient: - headers = {'X-Auth-Key': 'password', 'X-Auth-User': 'username'} - else: - headers = {} - if self.client == http.SessionClient: - self.client.request( - '/stacks', 'POST', data=mox.IgnoreArg(), - headers=headers - ).AndReturn(resp) - else: - self.client.json_request( - 'POST', '/stacks', data=mox.IgnoreArg(), - headers=headers - ).AndReturn((resp, None)) - - fakes.script_heat_list(client=self.client) - - self.m.ReplayAll() - - create_text = self.shell( - 'stack-create teststack2 ' - '--template-object=http://no.where/container/minimal.template ' - '--parameters="InstanceType=m1.large;DBUsername=wp;' - 'DBPassword=verybadpassword;KeyName=heat_key;' - 'LinuxDistribution=F17"') - - required = [ - 'stack_name', - 'id', - 'teststack2', - '2' - ] - for r in required: - self.assertRegexpMatches(create_text, r) - - def test_stack_create_with_tags(self): - self.register_keystone_auth_fixture() - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - template_data = open(template_file).read() - resp = fakes.FakeHTTPResponse( - 201, - 'Created', - {'location': 'http://no.where/v1/tenant_id/stacks/teststack2/2'}, - None) - expected_data = { - 'files': {}, - 'disable_rollback': True, - 'parameters': {'DBUsername': 'wp', - 'KeyName': 'heat_key', - 'LinuxDistribution': 'F17"', - '"InstanceType': 'm1.large', - 'DBPassword': 'verybadpassword'}, - 'stack_name': 'teststack', - 'environment': {}, - 'template': jsonutils.loads(template_data), - 'tags': 'tag1,tag2'} - if self.client == http.SessionClient: - self.client.request( - '/stacks', 'POST', data=expected_data, - headers={}).AndReturn(resp) - else: - http.HTTPClient.json_request( - 'POST', '/stacks', data=expected_data, - headers={'X-Auth-Key': 'password', 'X-Auth-User': 'username'} - ).AndReturn((resp, None)) - fakes.script_heat_list(client=self.client) - - self.m.ReplayAll() - - create_text = self.shell( - 'stack-create teststack ' - '--template-file=%s ' - '--tags=tag1,tag2 ' - '--parameters="InstanceType=m1.large;DBUsername=wp;' - 'DBPassword=verybadpassword;KeyName=heat_key;' - 'LinuxDistribution=F17"' % template_file) - - required = [ - 'stack_name', - 'id', - 'teststack', - '1' - ] - - for r in required: - self.assertRegexpMatches(create_text, r) - - def test_stack_abandon(self): - self.register_keystone_auth_fixture() - - resp_dict = {"stack": { - "id": "1", - "stack_name": "teststack", - "stack_status": 'CREATE_COMPLETE', - "creation_time": "2012-10-25T01:58:47Z" - }} - - abandoned_stack = { - "action": "CREATE", - "status": "COMPLETE", - "name": "teststack", - "id": "1", - "resources": { - "foo": { - "name": "foo", - "resource_id": "test-res-id", - "action": "CREATE", - "status": "COMPLETE", - "resource_data": {}, - "metadata": {}, - } - } - } - - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - abandoned_resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(abandoned_stack)) - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack/1', 'GET').AndReturn(resp) - self.client.request( - '/stacks/teststack/1/abandon', - 'DELETE').AndReturn(abandoned_resp) - else: - http.HTTPClient.json_request( - 'GET', '/stacks/teststack/1').AndReturn((resp, resp_dict)) - http.HTTPClient.raw_request( - 'DELETE', '/stacks/teststack/1/abandon').AndReturn( - abandoned_resp) - - self.m.ReplayAll() - abandon_resp = self.shell('stack-abandon teststack/1') - self.assertEqual(abandoned_stack, jsonutils.loads(abandon_resp)) - - def test_stack_abandon_with_outputfile(self): - self.register_keystone_auth_fixture() - - resp_dict = {"stack": { - "id": "1", - "stack_name": "teststack", - "stack_status": 'CREATE_COMPLETE', - "creation_time": "2012-10-25T01:58:47Z" - }} - - abandoned_stack = { - "action": "CREATE", - "status": "COMPLETE", - "name": "teststack", - "id": "1", - "resources": { - "foo": { - "name": "foo", - "resource_id": "test-res-id", - "action": "CREATE", - "status": "COMPLETE", - "resource_data": {}, - "metadata": {}, - } - } - } - - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - abandoned_resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(abandoned_stack)) - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack/1', 'GET').AndReturn(resp) - self.client.request( - '/stacks/teststack/1/abandon', - 'DELETE').AndReturn(abandoned_resp) - else: - http.HTTPClient.json_request( - 'GET', '/stacks/teststack/1').AndReturn((resp, resp_dict)) - http.HTTPClient.raw_request( - 'DELETE', '/stacks/teststack/1/abandon').AndReturn( - abandoned_resp) - - self.m.ReplayAll() - - with tempfile.NamedTemporaryFile() as file_obj: - self.shell('stack-abandon teststack/1 -O %s' % file_obj.name) - result = jsonutils.loads(file_obj.read().decode()) - self.assertEqual(abandoned_stack, result) - - def test_stack_adopt(self): - self.register_keystone_auth_fixture() - resp = fakes.FakeHTTPResponse( - 201, - 'Created', - {'location': 'http://no.where/v1/tenant_id/stacks/teststack/1'}, - None) - if self.client is http.HTTPClient: - headers = {'X-Auth-Key': 'password', 'X-Auth-User': 'username'} - else: - headers = {} - if self.client == http.SessionClient: - self.client.request( - '/stacks', 'POST', data=mox.IgnoreArg(), - headers=headers).AndReturn(resp) - else: - self.client.json_request( - 'POST', '/stacks', data=mox.IgnoreArg(), - headers=headers - ).AndReturn((resp, None)) - fakes.script_heat_list(client=self.client) - - self.m.ReplayAll() - - adopt_data_file = os.path.join(TEST_VAR_DIR, 'adopt_stack_data.json') - adopt_text = self.shell( - 'stack-adopt teststack ' - '--adopt-file=%s ' - '--parameters="InstanceType=m1.large;DBUsername=wp;' - 'DBPassword=verybadpassword;KeyName=heat_key;' - 'LinuxDistribution=F17"' % (adopt_data_file)) - - required = [ - 'stack_name', - 'id', - 'teststack', - '1' - ] - - for r in required: - self.assertRegexpMatches(adopt_text, r) - - def test_stack_adopt_without_data(self): - self.register_keystone_auth_fixture() - failed_msg = 'Need to specify --adopt-file' - self.m.ReplayAll() - self.shell_error('stack-adopt teststack ', failed_msg) - - def test_stack_update_enable_rollback(self): - self.register_keystone_auth_fixture() - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - with open(template_file, 'rb') as f: - template_data = jsonutils.load(f) - expected_data = {'files': {}, - 'environment': {}, - 'template': template_data, - 'disable_rollback': False, - 'parameters': mox.IgnoreArg() - } - resp = fakes.FakeHTTPResponse( - 202, - 'Accepted', - {}, - 'The request is accepted for processing.') - if self.client is http.HTTPClient: - headers = {'X-Auth-Key': 'password', 'X-Auth-User': 'username'} - else: - headers = {} - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack2/2', 'PUT', - data=expected_data, - headers=headers - ).AndReturn((resp, None)) - else: - self.client.json_request( - 'PUT', '/stacks/teststack2/2', - data=expected_data, - headers=headers - ).AndReturn((resp, None)) - fakes.script_heat_list(client=self.client) - self.m.ReplayAll() - - update_text = self.shell( - 'stack-update teststack2/2 ' - '--rollback on ' - '--template-file=%s ' - '--parameters="InstanceType=m1.large;DBUsername=wp;' - 'DBPassword=verybadpassword;KeyName=heat_key;' - 'LinuxDistribution=F17"' % template_file) - - required = [ - 'stack_name', - 'id', - 'teststack2', - '1' - ] - for r in required: - self.assertRegexpMatches(update_text, r) - - def test_stack_update_disable_rollback(self): - self.register_keystone_auth_fixture() - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - with open(template_file, 'rb') as f: - template_data = jsonutils.load(f) - expected_data = {'files': {}, - 'environment': {}, - 'template': template_data, - 'disable_rollback': True, - 'parameters': mox.IgnoreArg() - } - resp = fakes.FakeHTTPResponse( - 202, - 'Accepted', - {}, - 'The request is accepted for processing.') - - if self.client is http.HTTPClient: - headers = {'X-Auth-Key': 'password', 'X-Auth-User': 'username'} - else: - headers = {} - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack2', 'PUT', - data=expected_data, - headers=headers - ).AndReturn(resp) - else: - self.client.json_request( - 'PUT', '/stacks/teststack2', - data=expected_data, - headers=headers - ).AndReturn((resp, None)) - fakes.script_heat_list(client=self.client) - self.m.ReplayAll() - - update_text = self.shell( - 'stack-update teststack2 ' - '--template-file=%s ' - '--rollback off ' - '--parameters="InstanceType=m1.large;DBUsername=wp;' - 'DBPassword=verybadpassword;KeyName=heat_key;' - 'LinuxDistribution=F17"' % template_file) - - required = [ - 'stack_name', - 'id', - 'teststack2', - '1' - ] - for r in required: - self.assertRegexpMatches(update_text, r) - - def test_stack_update_fault_rollback_value(self): - self.register_keystone_auth_fixture() - self.m.ReplayAll() - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - self.shell_error('stack-update teststack2/2 ' - '--rollback Foo ' - '--template-file=%s' % template_file, - "Unrecognized value 'Foo', acceptable values are:" - ) - - def test_stack_update_rollback_default(self): - self.register_keystone_auth_fixture() - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - with open(template_file, 'rb') as f: - template_data = jsonutils.load(f) - expected_data = {'files': {}, - 'environment': {}, - 'template': template_data, - 'parameters': mox.IgnoreArg() - } - resp_update = fakes.FakeHTTPResponse( - 202, - 'Accepted', - {}, - 'The request is accepted for processing.') - if self.client is http.HTTPClient: - headers = {'X-Auth-Key': 'password', 'X-Auth-User': 'username'} - else: - headers = {} - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack2', 'PUT', - data=expected_data, - headers=headers - ).AndReturn(resp_update) - else: - self.client.json_request( - 'PUT', '/stacks/teststack2', - data=expected_data, - headers=headers - ).AndReturn((resp_update, None)) - fakes.script_heat_list(client=self.client) - self.m.ReplayAll() - - update_text = self.shell( - 'stack-update teststack2 ' - '--template-file=%s ' - '--parameters="InstanceType=m1.large;DBUsername=wp;' - 'DBPassword=verybadpassword;KeyName=heat_key;' - 'LinuxDistribution=F17"' % template_file) - - required = [ - 'stack_name', - 'id', - 'teststack2', - '2' - ] - for r in required: - self.assertRegexpMatches(update_text, r) - - def test_stack_update_with_existing_parameters(self): - self.register_keystone_auth_fixture() - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - template_data = open(template_file).read() - resp = fakes.FakeHTTPResponse( - 202, - 'Accepted', - {}, - 'The request is accepted for processing.') - expected_data = { - 'files': {}, - 'environment': {}, - 'template': jsonutils.loads(template_data), - 'parameters': {}, - 'disable_rollback': False} - if self.client is http.HTTPClient: - headers = {'X-Auth-Key': 'password', 'X-Auth-User': 'username'} - else: - headers = {} - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack2/2', 'PATCH', - data=expected_data, - headers=headers - ).AndReturn(resp) - else: - self.client.json_request( - 'PATCH', '/stacks/teststack2/2', - data=expected_data, - headers=headers - ).AndReturn((resp, None)) - fakes.script_heat_list(client=self.client) - - self.m.ReplayAll() - - update_text = self.shell( - 'stack-update teststack2/2 ' - '--template-file=%s ' - '--enable-rollback ' - '--existing' % template_file) - - required = [ - 'stack_name', - 'id', - 'teststack2', - '1' - ] - for r in required: - self.assertRegexpMatches(update_text, r) - - def test_stack_update_with_patched_existing_parameters(self): - self.register_keystone_auth_fixture() - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - template_data = open(template_file).read() - resp = fakes.FakeHTTPResponse( - 202, - 'Accepted', - {}, - 'The request is accepted for processing.') - expected_data = { - 'files': {}, - 'environment': {}, - 'template': jsonutils.loads(template_data), - 'parameters': {'"KeyPairName': 'updated_key"'}, - 'disable_rollback': False} - if self.client is http.HTTPClient: - headers = {'X-Auth-Key': 'password', 'X-Auth-User': 'username'} - else: - headers = {} - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack2/2', 'PATCH', - data=expected_data, - headers=headers - ).AndReturn(resp) - else: - self.client.json_request( - 'PATCH', '/stacks/teststack2/2', - data=expected_data, - headers=headers - ).AndReturn((resp, None)) - fakes.script_heat_list(client=self.client) - - self.m.ReplayAll() - - update_text = self.shell( - 'stack-update teststack2/2 ' - '--template-file=%s ' - '--enable-rollback ' - '--parameters="KeyPairName=updated_key" ' - '--existing' % template_file) - - required = [ - 'stack_name', - 'id', - 'teststack2', - '1' - ] - for r in required: - self.assertRegexpMatches(update_text, r) - - def test_stack_update_with_existing_and_default_parameters(self): - self.register_keystone_auth_fixture() - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - template_data = open(template_file).read() - resp = fakes.FakeHTTPResponse( - 202, - 'Accepted', - {}, - 'The request is accepted for processing.') - expected_data = { - 'files': {}, - 'environment': {}, - 'template': jsonutils.loads(template_data), - 'parameters': {}, - 'clear_parameters': ['InstanceType', 'DBUsername', - 'DBPassword', 'KeyPairName', - 'LinuxDistribution'], - 'disable_rollback': False} - if self.client is http.HTTPClient: - headers = {'X-Auth-Key': 'password', 'X-Auth-User': 'username'} - else: - headers = {} - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack2/2', 'PATCH', - data=expected_data, - headers=headers - ).AndReturn(resp) - else: - self.client.json_request( - 'PATCH', '/stacks/teststack2/2', - data=expected_data, - headers=headers - ).AndReturn((resp, None)) - fakes.script_heat_list(client=self.client) - - self.m.ReplayAll() - - update_text = self.shell( - 'stack-update teststack2/2 ' - '--template-file=%s ' - '--enable-rollback ' - '--existing ' - '--clear-parameter=InstanceType ' - '--clear-parameter=DBUsername ' - '--clear-parameter=DBPassword ' - '--clear-parameter=KeyPairName ' - '--clear-parameter=LinuxDistribution' % template_file) - - required = [ - 'stack_name', - 'id', - 'teststack2', - '1' - ] - for r in required: - self.assertRegexpMatches(update_text, r) - - def test_stack_update_with_patched_and_default_parameters(self): - self.register_keystone_auth_fixture() - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - template_data = open(template_file).read() - resp = fakes.FakeHTTPResponse( - 202, - 'Accepted', - {}, - 'The request is accepted for processing.') - expected_data = { - 'files': {}, - 'environment': {}, - 'template': jsonutils.loads(template_data), - 'parameters': {'"KeyPairName': 'updated_key"'}, - 'clear_parameters': ['InstanceType', 'DBUsername', - 'DBPassword', 'KeyPairName', - 'LinuxDistribution'], - 'disable_rollback': False} - if self.client is http.HTTPClient: - headers = {'X-Auth-Key': 'password', 'X-Auth-User': 'username'} - else: - headers = {} - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack2/2', 'PATCH', - data=expected_data, - headers=headers - ).AndReturn(resp) - else: - self.client.json_request( - 'PATCH', '/stacks/teststack2/2', - data=expected_data, - headers=headers - ).AndReturn((resp, None)) - fakes.script_heat_list(client=self.client) - - self.m.ReplayAll() - - update_text = self.shell( - 'stack-update teststack2/2 ' - '--template-file=%s ' - '--enable-rollback ' - '--existing ' - '--parameters="KeyPairName=updated_key" ' - '--clear-parameter=InstanceType ' - '--clear-parameter=DBUsername ' - '--clear-parameter=DBPassword ' - '--clear-parameter=KeyPairName ' - '--clear-parameter=LinuxDistribution' % template_file) - - required = [ - 'stack_name', - 'id', - 'teststack2', - '1' - ] - for r in required: - self.assertRegexpMatches(update_text, r) - - def test_stack_update_with_tags(self): - self.register_keystone_auth_fixture() - template_file = os.path.join(TEST_VAR_DIR, 'minimal.template') - template_data = open(template_file).read() - resp = fakes.FakeHTTPResponse( - 202, - 'Accepted', - {}, - 'The request is accepted for processing.') - expected_data = { - 'files': {}, - 'environment': {}, - 'template': jsonutils.loads(template_data), - 'parameters': {'"KeyPairName': 'updated_key"'}, - 'disable_rollback': False, - 'tags': 'tag1,tag2'} - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack2/2', 'PATCH', - data=expected_data, headers={}).AndReturn(resp) - else: - http.HTTPClient.json_request( - 'PATCH', '/stacks/teststack2/2', - data=expected_data, - headers={'X-Auth-Key': 'password', 'X-Auth-User': 'username'} - ).AndReturn((resp, None)) - fakes.script_heat_list(client=self.client) - - self.m.ReplayAll() - - update_text = self.shell( - 'stack-update teststack2/2 ' - '--template-file=%s ' - '--enable-rollback ' - '--existing ' - '--parameters="KeyPairName=updated_key" ' - '--tags=tag1,tag2 ' % template_file) - - required = [ - 'stack_name', - 'id', - 'teststack2', - '1' - ] - for r in required: - self.assertRegexpMatches(update_text, r) - - def test_stack_delete(self): - self.register_keystone_auth_fixture() - resp = fakes.FakeHTTPResponse( - 204, - 'No Content', - {}, - None) - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack2/2', 'DELETE').AndReturn(resp) - else: - self.client.raw_request( - 'DELETE', '/stacks/teststack2/2').AndReturn((resp, None)) - fakes.script_heat_list(client=self.client) - - self.m.ReplayAll() - - delete_text = self.shell('stack-delete teststack2/2') - - required = [ - 'stack_name', - 'id', - 'teststack', - '1' - ] - for r in required: - self.assertRegexpMatches(delete_text, r) - - def test_stack_delete_multiple(self): - self.register_keystone_auth_fixture() - resp = fakes.FakeHTTPResponse( - 204, - 'No Content', - {}, - None) - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack1/1', 'DELETE').AndReturn(resp) - self.client.request( - '/stacks/teststack2/2', 'DELETE').AndReturn(resp) - else: - self.client.raw_request( - 'DELETE', '/stacks/teststack1/1').AndReturn((resp, None)) - self.client.raw_request( - 'DELETE', '/stacks/teststack2/2').AndReturn((resp, None)) - fakes.script_heat_list(client=self.client) - - self.m.ReplayAll() - - delete_text = self.shell('stack-delete teststack1/1 teststack2/2') - - required = [ - 'stack_name', - 'id', - 'teststack', - '1' - ] - for r in required: - self.assertRegexpMatches(delete_text, r) - - def test_build_info(self): - self.register_keystone_auth_fixture() - resp_dict = { - 'build_info': { - 'api': {'revision': 'api_revision'}, - 'engine': {'revision': 'engine_revision'} - } - } - resp_string = jsonutils.dumps(resp_dict) - headers = {'content-type': 'application/json'} - http_resp = fakes.FakeHTTPResponse(200, 'OK', headers, resp_string) - response = (http_resp, resp_dict) - if self.client == http.SessionClient: - self.client.request('/build_info', 'GET').AndReturn(http_resp) - else: - self.client.json_request('GET', '/build_info').AndReturn(response) - - self.m.ReplayAll() - - build_info_text = self.shell('build-info') - - required = [ - 'api', - 'engine', - 'revision', - 'api_revision', - 'engine_revision', - ] - for r in required: - self.assertRegexpMatches(build_info_text, r) - - def test_stack_snapshot(self): - self.register_keystone_auth_fixture() - - stack_dict = {"stack": { - "id": "1", - "stack_name": "teststack", - "stack_status": 'CREATE_COMPLETE', - "creation_time": "2012-10-25T01:58:47Z" - }} - - resp_dict = {"snapshot": { - "id": "1", - "creation_time": "2012-10-25T01:58:47Z" - }} - - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - if self.client == http.SessionClient: - self.client.request('/stacks/teststack/1', 'GET').AndReturn( - fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(stack_dict))) - self.client.request( - '/stacks/teststack/1/snapshots', - 'POST', - data={}).AndReturn(resp) - else: - http.HTTPClient.json_request( - 'GET', '/stacks/teststack/1').AndReturn( - (fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(stack_dict)), stack_dict)) - http.HTTPClient.json_request( - 'POST', - '/stacks/teststack/1/snapshots', - data={}).AndReturn((resp, resp_dict)) - - self.m.ReplayAll() - resp = self.shell('stack-snapshot teststack/1') - self.assertEqual(resp_dict, jsonutils.loads(resp)) - - def test_snapshot_list(self): - self.register_keystone_auth_fixture() - - stack_dict = {"stack": { - "id": "1", - "stack_name": "teststack", - "stack_status": 'CREATE_COMPLETE', - "creation_time": "2012-10-25T01:58:47Z" - }} - - resp_dict = {"snapshots": [{ - "id": "2", - "name": "snap1", - "status": "COMPLETE", - "status_reason": "", - "data": {}, - "creation_time": "2014-12-05T01:25:52Z" - }]} - - stack_resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(stack_dict)) - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack/1', - 'GET').AndReturn(stack_resp) - self.client.request( - '/stacks/teststack/1/snapshots', - 'GET').AndReturn(resp) - else: - http.HTTPClient.json_request( - 'GET', '/stacks/teststack/1').AndReturn((stack_resp, - stack_dict)) - http.HTTPClient.json_request( - 'GET', - '/stacks/teststack/1/snapshots').AndReturn((resp, resp_dict)) - - self.m.ReplayAll() - list_text = self.shell('snapshot-list teststack/1') - - required = [ - 'id', - 'name', - 'status', - 'status_reason', - 'data', - 'creation_time', - '2', - 'COMPLETE', - '{}', - '2014-12-05T01:25:52Z', - ] - for r in required: - self.assertRegexpMatches(list_text, r) - - def test_snapshot_show(self): - self.register_keystone_auth_fixture() - - stack_dict = {"stack": { - "id": "1", - "stack_name": "teststack", - "stack_status": 'CREATE_COMPLETE', - "creation_time": "2012-10-25T01:58:47Z" - }} - - resp_dict = {"snapshot": { - "id": "2", - "creation_time": "2012-10-25T01:58:47Z" - }} - - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - if self.client == http.SessionClient: - self.client.request('/stacks/teststack/1', 'GET').AndReturn( - fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(stack_dict))) - self.client.request( - '/stacks/teststack/1/snapshots/2', - 'GET').AndReturn(resp) - else: - http.HTTPClient.json_request( - 'GET', '/stacks/teststack/1').AndReturn(( - fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(stack_dict)), stack_dict)) - http.HTTPClient.json_request( - 'GET', - '/stacks/teststack/1/snapshots/2').AndReturn((resp, resp_dict)) - - self.m.ReplayAll() - resp = self.shell('snapshot-show teststack/1 2') - self.assertEqual(resp_dict, jsonutils.loads(resp)) - - def test_snapshot_delete(self): - self.register_keystone_auth_fixture() - - stack_dict = {"stack": { - "id": "1", - "stack_name": "teststack", - "stack_status": 'CREATE_COMPLETE', - "creation_time": "2012-10-25T01:58:47Z" - }} - - resp_dict = {"snapshot": { - "id": "2", - "creation_time": "2012-10-25T01:58:47Z" - }} - - resp = fakes.FakeHTTPResponse( - 204, - 'No Content', - {'content-type': 'application/json'}, - jsonutils.dumps(stack_dict)) - second_resp = fakes.FakeHTTPResponse( - 204, - 'No Content', - {}, - jsonutils.dumps(resp_dict)) - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack/1', 'GET').AndReturn(resp) - self.client.request( - '/stacks/teststack/1/snapshots/2', - 'DELETE').AndReturn(second_resp) - else: - http.HTTPClient.json_request( - 'GET', '/stacks/teststack/1').AndReturn((resp, stack_dict)) - http.HTTPClient.raw_request( - 'DELETE', - '/stacks/teststack/1/snapshots/2').AndReturn(second_resp) - - self.m.ReplayAll() - resp = self.shell('snapshot-delete teststack/1 2') - self.assertEqual("", resp) - - def test_stack_restore(self): - self.register_keystone_auth_fixture() - - stack_dict = {"stack": { - "id": "1", - "stack_name": "teststack", - "stack_status": 'CREATE_COMPLETE', - "creation_time": "2012-10-25T01:58:47Z" - }} - - stack_resp = fakes.FakeHTTPResponse( - 204, - 'No Content', - {'content-type': 'application/json'}, - jsonutils.dumps(stack_dict)) - no_resp = fakes.FakeHTTPResponse( - 204, - 'No Content', - {'content-type': 'application/json'}, - jsonutils.dumps({})) - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack/1', 'GET').AndReturn(stack_resp) - self.client.request( - '/stacks/teststack/1/snapshots/2/restore', - 'POST').AndReturn(no_resp) - else: - http.HTTPClient.json_request( - 'GET', '/stacks/teststack/1').AndReturn((stack_resp, - stack_dict)) - http.HTTPClient.json_request( - 'POST', - '/stacks/teststack/1/snapshots/2/restore').AndReturn((no_resp, - {})) - - self.m.ReplayAll() - resp = self.shell('stack-restore teststack/1 2') - self.assertEqual("", resp) - - -class ShellTestActions(ShellBase): - - def setUp(self): - super(ShellTestActions, self).setUp() - self.set_fake_env(FAKE_ENV_KEYSTONE_V2) - - def test_stack_cancel_update(self): - self.register_keystone_auth_fixture() - expected_data = {'cancel_update': None} - resp = fakes.FakeHTTPResponse( - 202, - 'Accepted', - {}, - 'The request is accepted for processing.') - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack2/actions', 'POST', - data=expected_data).AndReturn(resp) - fakes.script_heat_list(client=self.client) - else: - http.HTTPClient.json_request( - 'POST', '/stacks/teststack2/actions', - data=expected_data - ).AndReturn((resp, None)) - fakes.script_heat_list() - - self.m.ReplayAll() - - update_text = self.shell('stack-cancel-update teststack2') - - required = [ - 'stack_name', - 'id', - 'teststack2', - '1' - ] - for r in required: - self.assertRegexpMatches(update_text, r) - - def test_stack_check(self): - self.register_keystone_auth_fixture() - expected_data = {'check': None} - resp = fakes.FakeHTTPResponse( - 202, - 'Accepted', - {}, - 'The request is accepted for processing.') - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack2/actions', 'POST', - data=expected_data).AndReturn(resp) - fakes.script_heat_list(client=self.client) - else: - http.HTTPClient.json_request( - 'POST', '/stacks/teststack2/actions', - data=expected_data - ).AndReturn((resp, None)) - fakes.script_heat_list() - - self.m.ReplayAll() - - check_text = self.shell('action-check teststack2') - - required = [ - 'stack_name', - 'id', - 'teststack2', - '1' - ] - for r in required: - self.assertRegexpMatches(check_text, r) - - def test_stack_suspend(self): - self.register_keystone_auth_fixture() - expected_data = {'suspend': None} - resp = fakes.FakeHTTPResponse( - 202, - 'Accepted', - {}, - 'The request is accepted for processing.') - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack2/actions', 'POST', - data=expected_data - ).AndReturn(resp) - fakes.script_heat_list(client=self.client) - else: - http.HTTPClient.json_request( - 'POST', '/stacks/teststack2/actions', - data=expected_data - ).AndReturn((resp, None)) - fakes.script_heat_list() - - self.m.ReplayAll() - - suspend_text = self.shell('action-suspend teststack2') - - required = [ - 'stack_name', - 'id', - 'teststack2', - '1' - ] - for r in required: - self.assertRegexpMatches(suspend_text, r) - - def test_stack_resume(self): - self.register_keystone_auth_fixture() - expected_data = {'resume': None} - resp = fakes.FakeHTTPResponse( - 202, - 'Accepted', - {}, - 'The request is accepted for processing.') - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack2/actions', 'POST', - data=expected_data - ).AndReturn(resp) - fakes.script_heat_list(client=self.client) - else: - http.HTTPClient.json_request( - 'POST', '/stacks/teststack2/actions', - data=expected_data - ).AndReturn((resp, None)) - fakes.script_heat_list() - - self.m.ReplayAll() - - resume_text = self.shell('action-resume teststack2') - - required = [ - 'stack_name', - 'id', - 'teststack2', - '1' - ] - for r in required: - self.assertRegexpMatches(resume_text, r) - - -class ShellTestEvents(ShellBase): - - def setUp(self): - super(ShellTestEvents, self).setUp() - self.set_fake_env(FAKE_ENV_KEYSTONE_V2) - - scenarios = [ - ('integer_id', dict( - event_id_one='24', - event_id_two='42')), - ('uuid_id', dict( - event_id_one='3d68809e-c4aa-4dc9-a008-933823d2e44f', - event_id_two='43b68bae-ed5d-4aed-a99f-0b3d39c2418a'))] - - def test_event_list(self): - self.register_keystone_auth_fixture() - resp, resp_dict = fakes.mock_script_event_list( - resource_name="aResource", - rsrc_eventid1=self.event_id_one, - rsrc_eventid2=self.event_id_two - ) - stack_id = 'teststack/1' - resource_name = 'testresource/1' - http.SessionClient.request( - '/stacks/%s/resources/%s/events?sort_dir=asc' % ( - parse.quote(stack_id, ''), - parse.quote(encodeutils.safe_encode( - resource_name), '')), 'GET').AndReturn(resp) - - self.m.ReplayAll() - - event_list_text = self.shell('event-list {0} --resource {1}'.format( - stack_id, resource_name)) - - required = [ - 'resource_name', - 'id', - 'resource_status_reason', - 'resource_status', - 'event_time', - 'aResource', - self.event_id_one, - self.event_id_two, - 'state changed', - 'CREATE_IN_PROGRESS', - 'CREATE_COMPLETE', - '2013-12-05T14:14:31Z', - '2013-12-05T14:14:32Z', - ] - for r in required: - self.assertRegexpMatches(event_list_text, r) - - def test_stack_event_list_log(self): - self.register_keystone_auth_fixture() - resp, resp_dict = fakes.mock_script_event_list( - resource_name="aResource", - rsrc_eventid1=self.event_id_one, - rsrc_eventid2=self.event_id_two - ) - - stack_id = 'teststack/1' - if self.client == http.SessionClient: - self.client.request( - '/stacks/%s/events?sort_dir=asc' % stack_id, - 'GET').AndReturn(resp) - else: - http.HTTPClient.json_request( - 'GET', - '/stacks/%s/events?sort_dir=asc' % - stack_id).AndReturn((resp, resp_dict)) - - self.m.ReplayAll() - - event_list_text = self.shell('event-list {0} --format log'.format( - stack_id)) - - expected = '14:14:31 2013-12-05 %s [aResource]: ' \ - 'CREATE_IN_PROGRESS state changed\n' \ - '14:14:32 2013-12-05 %s [aResource]: CREATE_COMPLETE ' \ - 'state changed\n' % (self.event_id_one, self.event_id_two) - - self.assertEqual(expected, event_list_text) - - def test_event_show(self): - self.register_keystone_auth_fixture() - resp_dict = {"event": - {"event_time": "2013-12-05T14:14:30Z", - "id": self.event_id_one, - "links": [{"href": "http://heat.example.com:8004/foo", - "rel": "self"}, - {"href": "http://heat.example.com:8004/foo2", - "rel": "resource"}, - {"href": "http://heat.example.com:8004/foo3", - "rel": "stack"}], - "logical_resource_id": "aResource", - "physical_resource_id": None, - "resource_name": "aResource", - "resource_properties": {"admin_user": "im_powerful", - "availability_zone": "nova"}, - "resource_status": "CREATE_IN_PROGRESS", - "resource_status_reason": "state changed", - "resource_type": "OS::Nova::Server" - }} - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - stack_id = 'teststack/1' - resource_name = 'testresource/1' - http.SessionClient.request( - '/stacks/%s/resources/%s/events/%s' % - ( - parse.quote(stack_id, ''), - parse.quote(encodeutils.safe_encode( - resource_name), ''), - parse.quote(self.event_id_one, '') - ), 'GET').AndReturn(resp) - - self.m.ReplayAll() - - event_list_text = self.shell('event-show {0} {1} {2}'.format( - stack_id, resource_name, - self.event_id_one)) - - required = [ - 'Property', - 'Value', - 'event_time', - '2013-12-05T14:14:30Z', - 'id', - self.event_id_one, - 'links', - 'http://heat.example.com:8004/foo[0-9]', - 'logical_resource_id', - 'physical_resource_id', - 'resource_name', - 'aResource', - 'resource_properties', - 'admin_user', - 'availability_zone', - 'resource_status', - 'CREATE_IN_PROGRESS', - 'resource_status_reason', - 'state changed', - 'resource_type', - 'OS::Nova::Server', - ] - for r in required: - self.assertRegexpMatches(event_list_text, r) - - -class ShellTestEventsNested(ShellBase): - def setUp(self): - super(ShellTestEventsNested, self).setUp() - self.set_fake_env(FAKE_ENV_KEYSTONE_V2) - - def test_shell_nested_depth_invalid_xor(self): - self.register_keystone_auth_fixture() - stack_id = 'teststack/1' - resource_name = 'aResource' - - self.m.ReplayAll() - - error = self.assertRaises( - exc.CommandError, self.shell, - 'event-list {0} --resource {1} --nested-depth 5'.format( - stack_id, resource_name)) - self.assertIn('--nested-depth cannot be specified with --resource', - str(error)) - - def test_shell_nested_depth_invalid_value(self): - self.register_keystone_auth_fixture() - stack_id = 'teststack/1' - resource_name = 'aResource' - error = self.assertRaises( - exc.CommandError, self.shell, - 'event-list {0} --nested-depth Z'.format( - stack_id, resource_name)) - self.assertIn('--nested-depth invalid value Z', str(error)) - - def test_shell_nested_depth_zero(self): - self.register_keystone_auth_fixture() - resp_dict = {"events": [{"id": 'eventid1'}, - {"id": 'eventid2'}]} - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - stack_id = 'teststack/1' - - if self.client == http.SessionClient: - self.client.request( - '/stacks/%s/events?sort_dir=asc' % stack_id, - 'GET' - ).AndReturn(resp) - else: - http.HTTPClient.json_request( - 'GET', - '/stacks/%s/events?sort_dir=asc' % stack_id - ).AndReturn((resp, resp_dict)) - self.m.ReplayAll() - list_text = self.shell('event-list %s --nested-depth 0' % stack_id) - required = ['id', 'eventid1', 'eventid2'] - for r in required: - self.assertRegexpMatches(list_text, r) - - def _stub_event_list_response(self, stack_id, nested_id, timestamps): - # Stub events for parent stack - ev_resp_dict = {"events": [{"id": "p_eventid1", - "event_time": timestamps[0]}, - {"id": "p_eventid2", - "event_time": timestamps[3]}]} - ev_resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(ev_resp_dict)) - if self.client == http.SessionClient: - self.client.request('/stacks/%s/events?sort_dir=asc' % stack_id, - 'GET').AndReturn(ev_resp) - else: - http.HTTPClient.json_request( - 'GET', '/stacks/%s/events?sort_dir=asc' % ( - stack_id)).AndReturn((ev_resp, ev_resp_dict)) - - # Stub resources for parent, including one nested - res_resp_dict = {"resources": [ - {"links": [{"href": "http://heat/foo", "rel": "self"}, - {"href": "http://heat/foo2", - "rel": "resource"}, - {"href": "http://heat/%s" % nested_id, - "rel": "nested"}], - "resource_type": "OS::Nested::Foo"}]} - res_resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(res_resp_dict)) - if self.client == http.SessionClient: - self.client.request( - '/stacks/%s/resources' % ( - stack_id), 'GET').AndReturn(res_resp) - - else: - http.HTTPClient.json_request( - 'GET', '/stacks/%s/resources' % ( - stack_id)).AndReturn((res_resp, res_resp_dict)) - - # Stub the events for the nested stack - nev_resp_dict = {"events": [{"id": 'n_eventid1', - "event_time": timestamps[1]}, - {"id": 'n_eventid2', - "event_time": timestamps[2]}]} - nev_resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(nev_resp_dict)) - if self.client == http.SessionClient: - self.client.request( - '/stacks/%s/events?sort_dir=asc' % ( - nested_id), 'GET').AndReturn(nev_resp) - else: - http.HTTPClient.json_request( - 'GET', '/stacks/%s/events?sort_dir=asc' % ( - nested_id)).AndReturn((nev_resp, nev_resp_dict)) - - def test_shell_nested_depth(self): - self.register_keystone_auth_fixture() - stack_id = 'teststack/1' - nested_id = 'nested/2' - timestamps = ("2014-01-06T16:14:00Z", # parent p_eventid1 - "2014-01-06T16:15:00Z", # nested n_eventid1 - "2014-01-06T16:16:00Z", # nested n_eventid2 - "2014-01-06T16:17:00Z") # parent p_eventid2 - self._stub_event_list_response(stack_id, nested_id, timestamps) - self.m.ReplayAll() - list_text = self.shell('event-list %s --nested-depth 1' % stack_id) - required = ['id', 'p_eventid1', 'p_eventid2', 'n_eventid1', - 'n_eventid2', 'stack_name', 'teststack', 'nested'] - for r in required: - self.assertRegexpMatches(list_text, r) - - # Check event time sort/ordering - self.assertRegexpMatches(list_text, - "%s.*\n.*%s.*\n.*%s.*\n.*%s" % timestamps) - - def test_shell_nested_depth_marker(self): - self.register_keystone_auth_fixture() - stack_id = 'teststack/1' - nested_id = 'nested/2' - timestamps = ("2014-01-06T16:14:00Z", # parent p_eventid1 - "2014-01-06T16:15:00Z", # nested n_eventid1 - "2014-01-06T16:16:00Z", # nested n_eventid2 - "2014-01-06T16:17:00Z") # parent p_eventid2 - self._stub_event_list_response(stack_id, nested_id, timestamps) - self.m.ReplayAll() - list_text = self.shell( - 'event-list %s --nested-depth 1 --marker n_eventid1' % stack_id) - required = ['id', 'p_eventid2', 'n_eventid1', 'n_eventid2', - 'stack_name', 'teststack', 'nested'] - for r in required: - self.assertRegexpMatches(list_text, r) - - self.assertNotRegexpMatches(list_text, 'p_eventid1') - - self.assertRegexpMatches(list_text, - "%s.*\n.*%s.*\n.*%s.*" % timestamps[1:]) - - def test_shell_nested_depth_limit(self): - self.register_keystone_auth_fixture() - stack_id = 'teststack/1' - nested_id = 'nested/2' - timestamps = ("2014-01-06T16:14:00Z", # parent p_eventid1 - "2014-01-06T16:15:00Z", # nested n_eventid1 - "2014-01-06T16:16:00Z", # nested n_eventid2 - "2014-01-06T16:17:00Z") # parent p_eventid2 - self._stub_event_list_response(stack_id, nested_id, timestamps) - self.m.ReplayAll() - list_text = self.shell( - 'event-list %s --nested-depth 1 --limit 2' % stack_id) - required = ['id', 'p_eventid1', 'n_eventid1', - 'stack_name', 'teststack', 'nested'] - for r in required: - self.assertRegexpMatches(list_text, r) - self.assertNotRegexpMatches(list_text, 'p_eventid2') - self.assertNotRegexpMatches(list_text, 'n_eventid2') - - self.assertRegexpMatches(list_text, - "%s.*\n.*%s.*\n" % timestamps[:2]) - - -class ShellTestHookFunctions(ShellBase): - def setUp(self): - super(ShellTestHookFunctions, self).setUp() - self.set_fake_env(FAKE_ENV_KEYSTONE_V2) - - def _stub_stack_response(self, stack_id, action='CREATE', - status='IN_PROGRESS'): - # Stub parent stack show for status - resp_dict = {"stack": { - "id": stack_id.split("/")[1], - "stack_name": stack_id.split("/")[0], - "stack_status": '%s_%s' % (action, status), - "creation_time": "2014-01-06T16:14:00Z", - }} - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - if self.client == http.SessionClient: - self.client.request( - '/stacks/teststack/1', 'GET').AndReturn(resp) - else: - self.client.json_request( - 'GET', '/stacks/teststack/1').AndReturn((resp, resp_dict)) - - def _stub_responses(self, stack_id, nested_id, action='CREATE'): - action_reason = 'Stack %s started' % action - hook_reason = ('%s paused until Hook pre-%s is cleared' % - (action, action.lower())) - hook_clear_reason = 'Hook pre-%s is cleared' % action.lower() - - self._stub_stack_response(stack_id, action) - - # Stub events for parent stack - ev_resp_dict = {"events": [{"id": "p_eventid1", - "event_time": "2014-01-06T16:14:00Z", - "resource_name": None, - "resource_status_reason": action_reason}, - {"id": "p_eventid2", - "event_time": "2014-01-06T16:17:00Z", - "resource_name": "p_res", - "resource_status_reason": hook_reason}]} - ev_resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(ev_resp_dict)) - if self.client == http.SessionClient: - self.client.request( - '/stacks/%s/events?sort_dir=asc' % (stack_id), - 'GET').AndReturn(ev_resp) - else: - self.client.json_request( - 'GET', '/stacks/%s/events?sort_dir=asc' % ( - stack_id)).AndReturn((ev_resp, ev_resp_dict)) - - # Stub resources for parent, including one nested - res_resp_dict = {"resources": [ - {"links": [{"href": "http://heat/foo", "rel": "self"}, - {"href": "http://heat/foo2", - "rel": "resource"}, - {"href": "http://heat/%s" % nested_id, - "rel": "nested"}], - "resource_type": "OS::Nested::Foo"}]} - res_resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(res_resp_dict)) - if self.client == http.SessionClient: - self.client.request( - '/stacks/%s/resources' % (stack_id), - 'GET').AndReturn(res_resp) - else: - self.client.json_request( - 'GET', '/stacks/%s/resources' % ( - stack_id)).AndReturn((res_resp, res_resp_dict)) - - # Stub the events for the nested stack - nev_resp_dict = {"events": [{"id": 'n_eventid1', - "event_time": "2014-01-06T16:15:00Z", - "resource_name": "n_res", - "resource_status_reason": hook_reason}, - {"id": 'n_eventid2', - "event_time": "2014-01-06T16:16:00Z", - "resource_name": "n_res", - "resource_status_reason": - hook_clear_reason}]} - nev_resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(nev_resp_dict)) - if self.client == http.SessionClient: - self.client.request( - '/stacks/%s/events?sort_dir=asc' % (nested_id), - 'GET').AndReturn(nev_resp) - else: - self.client.json_request( - 'GET', '/stacks/%s/events?sort_dir=asc' % ( - nested_id)).AndReturn((nev_resp, nev_resp_dict)) - - def test_hook_poll_pre_create(self): - self.register_keystone_auth_fixture() - stack_id = 'teststack/1' - nested_id = 'nested/2' - self._stub_responses(stack_id, nested_id, 'CREATE') - self.m.ReplayAll() - list_text = self.shell('hook-poll %s --nested-depth 1' % stack_id) - hook_reason = 'CREATE paused until Hook pre-create is cleared' - required = ['id', 'p_eventid2', 'stack_name', 'teststack', hook_reason] - for r in required: - self.assertRegexpMatches(list_text, r) - self.assertNotRegexpMatches(list_text, 'p_eventid1') - self.assertNotRegexpMatches(list_text, 'n_eventid1') - self.assertNotRegexpMatches(list_text, 'n_eventid2') - - def test_hook_poll_pre_update(self): - self.register_keystone_auth_fixture() - stack_id = 'teststack/1' - nested_id = 'nested/2' - self._stub_responses(stack_id, nested_id, 'UPDATE') - self.m.ReplayAll() - list_text = self.shell('hook-poll %s --nested-depth 1' % stack_id) - hook_reason = 'UPDATE paused until Hook pre-update is cleared' - required = ['id', 'p_eventid2', 'stack_name', 'teststack', hook_reason] - for r in required: - self.assertRegexpMatches(list_text, r) - self.assertNotRegexpMatches(list_text, 'p_eventid1') - self.assertNotRegexpMatches(list_text, 'n_eventid1') - self.assertNotRegexpMatches(list_text, 'n_eventid2') - - def test_hook_poll_bad_status(self): - self.register_keystone_auth_fixture() - stack_id = 'teststack/1' - self._stub_stack_response(stack_id, status='COMPLETE') - self.m.ReplayAll() - error = self.assertRaises( - exc.CommandError, self.shell, - 'hook-poll %s --nested-depth 1' % stack_id) - self.assertIn('Stack status CREATE_COMPLETE not IN_PROGRESS', - str(error)) - - def test_shell_nested_depth_invalid_value(self): - self.register_keystone_auth_fixture() - stack_id = 'teststack/1' - self.m.ReplayAll() - error = self.assertRaises( - exc.CommandError, self.shell, - 'hook-poll %s --nested-depth Z' % stack_id) - self.assertIn('--nested-depth invalid value Z', str(error)) - - def test_hook_poll_clear_bad_status(self): - self.register_keystone_auth_fixture() - stack_id = 'teststack/1' - self._stub_stack_response(stack_id, status='COMPLETE') - self.m.ReplayAll() - error = self.assertRaises( - exc.CommandError, self.shell, - 'hook-clear %s aresource' % stack_id) - self.assertIn('Stack status CREATE_COMPLETE not IN_PROGRESS', - str(error)) - - def test_hook_poll_clear_bad_action(self): - self.register_keystone_auth_fixture() - stack_id = 'teststack/1' - self._stub_stack_response(stack_id, action='DELETE') - self.m.ReplayAll() - error = self.assertRaises( - exc.CommandError, self.shell, - 'hook-clear %s aresource' % stack_id) - self.assertIn('Unexpected stack status DELETE_IN_PROGRESS', - str(error)) - - -class ShellTestResources(ShellBase): - - def setUp(self): - super(ShellTestResources, self).setUp() - self.set_fake_env(FAKE_ENV_KEYSTONE_V2) - - def _test_resource_list(self, with_resource_name): - self.register_keystone_auth_fixture() - resp_dict = {"resources": [ - {"links": [{"href": "http://heat.example.com:8004/foo", - "rel": "self"}, - {"href": "http://heat.example.com:8004/foo2", - "rel": "resource"}], - "logical_resource_id": "aLogicalResource", - "physical_resource_id": - "43b68bae-ed5d-4aed-a99f-0b3d39c2418a", - "resource_status": "CREATE_COMPLETE", - "resource_status_reason": "state changed", - "resource_type": "OS::Nova::Server", - "updated_time": "2014-01-06T16:14:26Z"}]} - if with_resource_name: - resp_dict["resources"][0]["resource_name"] = "aResource" - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - stack_id = 'teststack/1' - http.SessionClient.request( - '/stacks/%s/resources' % ( - stack_id), 'GET').AndReturn(resp) - - self.m.ReplayAll() - - resource_list_text = self.shell('resource-list {0}'.format(stack_id)) - - required = [ - 'physical_resource_id', - 'resource_type', - 'resource_status', - 'updated_time', - '43b68bae-ed5d-4aed-a99f-0b3d39c2418a', - 'OS::Nova::Server', - 'CREATE_COMPLETE', - '2014-01-06T16:14:26Z' - ] - if with_resource_name: - required.append('resource_name') - required.append('aResource') - else: - required.append('logical_resource_id') - required.append("aLogicalResource") - - for r in required: - self.assertRegexpMatches(resource_list_text, r) - - def test_resource_list(self): - self._test_resource_list(True) - - def test_resource_list_no_resource_name(self): - self._test_resource_list(False) - - def test_resource_list_empty(self): - self.register_keystone_auth_fixture() - resp_dict = {"resources": []} - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - stack_id = 'teststack/1' - http.SessionClient.request( - '/stacks/%s/resources' % ( - stack_id), 'GET').AndReturn(resp) - - self.m.ReplayAll() - - resource_list_text = self.shell('resource-list {0}'.format(stack_id)) - - self.assertEqual('''\ -+---------------+----------------------+---------------+-----------------+\ ---------------+ -| resource_name | physical_resource_id | resource_type | resource_status |\ - updated_time | -+---------------+----------------------+---------------+-----------------+\ ---------------+ -+---------------+----------------------+---------------+-----------------+\ ---------------+ -''', resource_list_text) - - def test_resource_list_nested(self): - self.register_keystone_auth_fixture() - resp_dict = {"resources": [{ - "resource_name": "foobar", - "links": [{ - "href": "http://heat.example.com:8004/foo/12/resources/foobar", - "rel": "self" - }, { - "href": "http://heat.example.com:8004/foo/12", - "rel": "stack" - }], - }]} - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - stack_id = 'teststack/1' - http.SessionClient.request( - '/stacks/%s/resources?nested_depth=99' % ( - stack_id), 'GET').AndReturn(resp) - - self.m.ReplayAll() - - shell_cmd = 'resource-list {0} --nested-depth {1}'.format(stack_id, 99) - resource_list_text = self.shell(shell_cmd) - - required = [ - 'resource_name', 'foobar', - 'stack_name', 'foo', - ] - for field in required: - self.assertRegexpMatches(resource_list_text, field) - - def test_resource_list_detail(self): - self.register_keystone_auth_fixture() - resp_dict = {"resources": [{ - "resource_name": "foobar", - "links": [{ - "href": "http://heat.example.com:8004/foo/12/resources/foobar", - "rel": "self" - }, { - "href": "http://heat.example.com:8004/foo/12", - "rel": "stack" - }], - }]} - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - stack_id = 'teststack/1' - http.SessionClient.request('/stacks/%s/resources?%s' % ( - stack_id, - parse.urlencode({'with_detail': True}, True) - ), 'GET').AndReturn(resp) - - self.m.ReplayAll() - - shell_cmd = 'resource-list {0} --with-detail'.format(stack_id) - resource_list_text = self.shell(shell_cmd) - - required = [ - 'resource_name', 'foobar', - 'stack_name', 'foo', - ] - for field in required: - self.assertRegexpMatches(resource_list_text, field) - - def test_resource_show_with_attrs(self): - self.register_keystone_auth_fixture() - resp_dict = {"resource": - {"description": "", - "links": [{"href": "http://heat.example.com:8004/foo", - "rel": "self"}, - {"href": "http://heat.example.com:8004/foo2", - "rel": "resource"}], - "logical_resource_id": "aResource", - "physical_resource_id": - "43b68bae-ed5d-4aed-a99f-0b3d39c2418a", - "required_by": [], - "resource_name": "aResource", - "resource_status": "CREATE_COMPLETE", - "resource_status_reason": "state changed", - "resource_type": "OS::Nova::Server", - "updated_time": "2014-01-06T16:14:26Z", - "creation_time": "2014-01-06T16:14:26Z", - "attributes": { - "attr_a": "value_of_attr_a", - "attr_b": "value_of_attr_b"}}} - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - stack_id = 'teststack/1' - resource_name = 'aResource' - http.SessionClient.request( - '/stacks/%s/resources/%s?with_attr=attr_a&with_attr=attr_b' % - ( - parse.quote(stack_id, ''), - parse.quote(encodeutils.safe_encode( - resource_name), '') - ), 'GET').AndReturn(resp) - - self.m.ReplayAll() - - resource_show_text = self.shell( - 'resource-show {0} {1} --with-attr attr_a ' - '--with-attr attr_b'.format( - stack_id, resource_name)) - - required = [ - 'description', - 'links', - 'http://heat.example.com:8004/foo[0-9]', - 'logical_resource_id', - 'aResource', - 'physical_resource_id', - '43b68bae-ed5d-4aed-a99f-0b3d39c2418a', - 'required_by', - 'resource_name', - 'aResource', - 'resource_status', - 'CREATE_COMPLETE', - 'resource_status_reason', - 'state changed', - 'resource_type', - 'OS::Nova::Server', - 'updated_time', - '2014-01-06T16:14:26Z', - ] - for r in required: - self.assertRegexpMatches(resource_show_text, r) - - def test_resource_signal(self): - self.register_keystone_auth_fixture() - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {}, - '') - stack_id = 'teststack/1' - resource_name = 'aResource' - http.SessionClient.request( - '/stacks/%s/resources/%s/signal' % - ( - parse.quote(stack_id, ''), - parse.quote(encodeutils.safe_encode( - resource_name), '') - ), - 'POST', - data={'message': 'Content'}).AndReturn(resp) - - self.m.ReplayAll() - - text = self.shell( - 'resource-signal {0} {1} -D {{"message":"Content"}}'.format( - stack_id, resource_name)) - self.assertEqual("", text) - - def test_resource_signal_no_data(self): - self.register_keystone_auth_fixture() - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {}, - '') - stack_id = 'teststack/1' - resource_name = 'aResource' - http.SessionClient.request( - '/stacks/%s/resources/%s/signal' % - ( - parse.quote(stack_id, ''), - parse.quote(encodeutils.safe_encode( - resource_name), '') - ), 'POST', data=None).AndReturn(resp) - - self.m.ReplayAll() - - text = self.shell( - 'resource-signal {0} {1}'.format(stack_id, resource_name)) - self.assertEqual("", text) - - def test_resource_signal_no_json(self): - self.register_keystone_auth_fixture() - stack_id = 'teststack/1' - resource_name = 'aResource' - - self.m.ReplayAll() - - error = self.assertRaises( - exc.CommandError, self.shell, - 'resource-signal {0} {1} -D [2'.format( - stack_id, resource_name)) - self.assertIn('Data should be in JSON format', str(error)) - - def test_resource_signal_no_dict(self): - self.register_keystone_auth_fixture() - stack_id = 'teststack/1' - resource_name = 'aResource' - - self.m.ReplayAll() - - error = self.assertRaises( - exc.CommandError, self.shell, - 'resource-signal {0} {1} -D "message"'.format( - stack_id, resource_name)) - self.assertEqual('Data should be a JSON dict', str(error)) - - def test_resource_signal_both_data(self): - self.register_keystone_auth_fixture() - stack_id = 'teststack/1' - resource_name = 'aResource' - - self.m.ReplayAll() - - error = self.assertRaises( - exc.CommandError, self.shell, - 'resource-signal {0} {1} -D "message" -f foo'.format( - stack_id, resource_name)) - self.assertEqual('Can only specify one of data and data-file', - str(error)) - - def test_resource_signal_data_file(self): - self.register_keystone_auth_fixture() - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {}, - '') - stack_id = 'teststack/1' - resource_name = 'aResource' - http.SessionClient.request( - '/stacks/%s/resources/%s/signal' % - ( - parse.quote(stack_id, ''), - parse.quote(encodeutils.safe_encode( - resource_name), '') - ), - 'POST', - data={'message': 'Content'}).AndReturn(resp) - - self.m.ReplayAll() - - with tempfile.NamedTemporaryFile() as data_file: - data_file.write(b'{"message":"Content"}') - data_file.flush() - text = self.shell( - 'resource-signal {0} {1} -f {2}'.format( - stack_id, resource_name, data_file.name)) - self.assertEqual("", text) - - -class ShellTestResourceTypes(ShellBase): - def setUp(self): - super(ShellTestResourceTypes, self).setUp() - self.set_fake_env(FAKE_ENV_KEYSTONE_V3) - - def test_resource_type_template_yaml(self): - self.register_keystone_auth_fixture() - resp_dict = {"heat_template_version": "2013-05-23", - "parameters": {}, - "resources": {}, - "outputs": {}} - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - - http.SessionClient.request( - '/resource_types/OS%3A%3ANova%3A%3AKeyPair/template' - '?template_type=hot', 'GET' - ).AndReturn(resp) - - self.m.ReplayAll() - - show_text = self.shell( - 'resource-type-template -F yaml -t hot OS::Nova::KeyPair') - required = [ - "heat_template_version: '2013-05-23'", - "outputs: {}", - "parameters: {}", - "resources: {}" - ] - for r in required: - self.assertRegexpMatches(show_text, r) - - def test_resource_type_template_json(self): - self.register_keystone_auth_fixture() - resp_dict = {"AWSTemplateFormatVersion": "2013-05-23", - "Parameters": {}, - "Resources": {}, - "Outputs": {}} - resp = fakes.FakeHTTPResponse( - 200, - 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict)) - - http.SessionClient.request( - '/resource_types/OS%3A%3ANova%3A%3AKeyPair/template' - '?template_type=cfn', 'GET' - ).AndReturn(resp) - - self.m.ReplayAll() - - show_text = self.shell( - 'resource-type-template -F json OS::Nova::KeyPair') - required = [ - '{', - ' "AWSTemplateFormatVersion": "2013-05-23"', - ' "Outputs": {}', - ' "Resources": {}', - ' "Parameters": {}', - '}' - ] - for r in required: - self.assertRegexpMatches(show_text, r) - - -class ShellTestConfig(ShellBase): - - def setUp(self): - super(ShellTestConfig, self).setUp() - self._set_fake_env() - - def _set_fake_env(self): - '''Patch os.environ to avoid required auth info.''' - self.set_fake_env(FAKE_ENV_KEYSTONE_V2) - - def test_config_create(self): - self.register_keystone_auth_fixture() - - definition = { - 'inputs': [ - {'name': 'foo'}, - {'name': 'bar'}, - ], - 'outputs': [ - {'name': 'result'} - ], - 'options': {'a': 'b'} - } - validate_template = {'template': { - 'heat_template_version': '2013-05-23', - 'resources': { - 'config_name': { - 'type': 'OS::Heat::SoftwareConfig', - 'properties': { - 'config': 'the config script', - 'group': 'script', - 'inputs': [ - {'name': 'foo'}, - {'name': 'bar'}, - ], - 'outputs': [ - {'name': 'result'} - ], - 'options': {'a': 'b'}, - 'config': 'the config script' - } - } - } - }} - - create_dict = { - 'group': 'script', - 'name': 'config_name', - 'inputs': [ - {'name': 'foo'}, - {'name': 'bar'}, - ], - 'outputs': [ - {'name': 'result'} - ], - 'options': {'a': 'b'}, - 'config': 'the config script' - } - - resp_dict = {'software_config': { - 'group': 'script', - 'name': 'config_name', - 'inputs': [ - {'name': 'foo'}, - {'name': 'bar'}, - ], - 'outputs': [ - {'name': 'result'} - ], - 'options': {'a': 'b'}, - 'config': 'the config script', - 'id': 'abcd' - }} - resp_string = jsonutils.dumps(resp_dict) - headers = {'content-type': 'application/json'} - http_resp = fakes.FakeHTTPResponse(200, 'OK', headers, resp_string) - - self.m.StubOutWithMock(request, 'urlopen') - request.urlopen('file:///tmp/defn').AndReturn( - six.StringIO(yaml.safe_dump(definition, indent=2))) - request.urlopen('file:///tmp/config_script').AndReturn( - six.StringIO('the config script')) - - http.SessionClient.request( - '/validate', 'POST', data=validate_template).AndReturn(http_resp) - http.SessionClient.request( - '/software_configs', 'POST', data=create_dict).AndReturn(http_resp) - - self.m.ReplayAll() - - text = self.shell('config-create -c /tmp/config_script ' - '-g script -f /tmp/defn config_name') - - self.assertEqual(resp_dict['software_config'], jsonutils.loads(text)) - - def test_config_show(self): - self.register_keystone_auth_fixture() - resp_dict = {'software_config': { - 'inputs': [], - 'group': 'script', - 'name': 'config_name', - 'outputs': [], - 'options': {}, - 'config': 'the config script', - 'id': 'abcd'}} - resp_string = jsonutils.dumps(resp_dict) - headers = {'content-type': 'application/json'} - http_resp = fakes.FakeHTTPResponse(200, 'OK', headers, resp_string) - http.SessionClient.request( - '/software_configs/abcd', 'GET').AndReturn(http_resp) - http.SessionClient.request( - '/software_configs/abcd', 'GET').AndReturn(http_resp) - http.SessionClient.request( - '/software_configs/abcde', 'GET').AndRaise(exc.HTTPNotFound()) - - self.m.ReplayAll() - - text = self.shell('config-show abcd') - - required = [ - 'inputs', - 'group', - 'name', - 'outputs', - 'options', - 'config', - 'id', - ] - for r in required: - self.assertRegexpMatches(text, r) - - self.assertEqual( - 'the config script\n', - self.shell('config-show --config-only abcd')) - self.assertRaises(exc.CommandError, self.shell, 'config-show abcde') - - def test_config_delete(self): - self.register_keystone_auth_fixture() - headers = {'content-type': 'application/json'} - http_resp = fakes.FakeHTTPResponse(204, 'OK', headers, None) - http.SessionClient.request( - '/software_configs/abcd', 'DELETE').AndReturn(http_resp) - http.SessionClient.request( - '/software_configs/qwer', 'DELETE').AndReturn(http_resp) - http.SessionClient.request( - '/software_configs/abcd', 'DELETE').AndRaise(exc.HTTPNotFound()) - http.SessionClient.request( - '/software_configs/qwer', 'DELETE').AndRaise(exc.HTTPNotFound()) - - self.m.ReplayAll() - - self.assertEqual('', self.shell('config-delete abcd qwer')) - self.assertRaises( - exc.CommandError, self.shell, 'config-delete abcd qwer') - - -class ShellTestDeployment(ShellBase): - - def setUp(self): - super(ShellTestDeployment, self).setUp() - self.client = http.SessionClient - self._set_fake_env() - - def _set_fake_env(self): - '''Patch os.environ to avoid required auth info.''' - self.set_fake_env(FAKE_ENV_KEYSTONE_V2) - - def test_deploy_create(self): - self.register_keystone_auth_fixture() - self.patch( - 'heatclient.common.deployment_utils.build_derived_config_params') - self.patch( - 'heatclient.common.deployment_utils.build_signal_id') - resp_dict = {'software_deployment': { - 'status': 'INPROGRESS', - 'server_id': '700115e5-0100-4ecc-9ef7-9e05f27d8803', - 'config_id': '18c4fc03-f897-4a1d-aaad-2b7622e60257', - 'output_values': { - 'deploy_stdout': '', - 'deploy_stderr': '', - 'deploy_status_code': 0, - 'result': 'The result value' - }, - 'input_values': {}, - 'action': 'UPDATE', - 'status_reason': 'Outputs received', - 'id': 'abcd' - }} - - config_dict = {'software_config': { - 'inputs': [], - 'group': 'script', - 'name': 'config_name', - 'outputs': [], - 'options': {}, - 'config': 'the config script', - 'id': 'defg'}} - - derived_dict = {'software_config': { - 'inputs': [], - 'group': 'script', - 'name': 'config_name', - 'outputs': [], - 'options': {}, - 'config': 'the config script', - 'id': 'abcd'}} - - deploy_data = {'action': 'UPDATE', - 'config_id': u'abcd', - 'server_id': 'inst01', - 'status': 'IN_PROGRESS', - 'tenant_id': 'asdf'} - - config_string = jsonutils.dumps(config_dict) - headers = {'content-type': 'application/json'} - http_resp = fakes.FakeHTTPResponse(200, 'OK', headers, config_string) - response = (http_resp, config_dict) - if self.client == http.SessionClient: - http.SessionClient.request( - '/software_configs/defg', 'GET').AndReturn(http_resp) - else: - self.client.json_request( - 'GET', '/software_configs/defg').AndReturn(response) - - derived_string = jsonutils.dumps(derived_dict) - http_resp = fakes.FakeHTTPResponse(200, 'OK', headers, derived_string) - response = (http_resp, derived_dict) - if self.client == http.SessionClient: - http.SessionClient.request( - '/software_configs', 'POST', data={}).AndReturn(http_resp) - else: - self.client.json_request( - 'POST', '/software_configs', data={}).AndReturn(response) - - resp_string = jsonutils.dumps(resp_dict) - http_resp = fakes.FakeHTTPResponse(200, 'OK', headers, resp_string) - response = (http_resp, resp_dict) - if self.client == http.SessionClient: - self.client.request( - '/software_deployments', 'POST', - data=deploy_data).AndReturn(http_resp) - else: - self.client.json_request( - 'POST', - '/software_deployments', data=deploy_data).AndReturn(response) - - http_resp = fakes.FakeHTTPResponse(200, 'OK', headers, derived_string) - response = (http_resp, derived_dict) - if self.client == http.SessionClient: - http.SessionClient.request( - '/software_configs', 'POST', data={}).AndReturn(http_resp) - else: - self.client.json_request( - 'POST', '/software_configs', data={}).AndReturn(response) - - http_resp = fakes.FakeHTTPResponse(200, 'OK', headers, resp_string) - response = (http_resp, resp_dict) - if self.client == http.SessionClient: - self.client.request( - '/software_deployments', 'POST', - data=deploy_data).AndReturn(http_resp) - self.client.request( - '/software_configs/defgh', 'GET').AndRaise( - exc.HTTPNotFound()) - else: - self.client.json_request( - 'POST', '/software_deployments').AndReturn(response) - self.client.json_request( - 'GET', '/software_configs/defgh').AndRaise( - exc.HTTPNotFound()) - - self.m.ReplayAll() - - text = self.shell('deployment-create -c defg -sinst01 xxx') - - required = [ - 'status', - 'server_id', - 'config_id', - 'output_values', - 'input_values', - 'action', - 'status_reason', - 'id', - ] - for r in required: - self.assertRegexpMatches(text, r) - - text = self.shell('deployment-create -sinst01 xxx') - for r in required: - self.assertRegexpMatches(text, r) - - self.assertRaises(exc.CommandError, self.shell, - 'deployment-create -c defgh -s inst01 yyy') - self.m.VerifyAll() - - def test_deploy_list(self): - self.register_keystone_auth_fixture() - - resp_dict = { - 'software_deployments': - [{'status': 'COMPLETE', - 'server_id': '123', - 'config_id': '18c4fc03-f897-4a1d-aaad-2b7622e60257', - 'output_values': { - 'deploy_stdout': '', - 'deploy_stderr': '', - 'deploy_status_code': 0, - 'result': 'The result value' - }, - 'input_values': {}, - 'action': 'CREATE', - 'status_reason': 'Outputs received', - 'id': 'defg'}, ] - } - resp_string = jsonutils.dumps(resp_dict) - headers = {'content-type': 'application/json'} - http_resp = fakes.FakeHTTPResponse(200, 'OK', headers, resp_string) - response = (http_resp, resp_dict) - if self.client == http.SessionClient: - self.client.request( - '/software_deployments?', 'GET').AndReturn(http_resp) - self.client.request( - '/software_deployments?server_id=123', - 'GET').AndReturn(http_resp) - else: - self.client.json_request( - 'GET', '/software_deployments?').AndReturn(response) - self.client.json_request( - 'GET', - '/software_deployments?server_id=123').AndReturn(response) - - self.m.ReplayAll() - - list_text = self.shell('deployment-list') - - required = [ - 'id', - 'config_id', - 'server_id', - 'action', - 'status', - 'creation_time', - 'status_reason', - ] - for r in required: - self.assertRegexpMatches(list_text, r) - self.assertNotRegexpMatches(list_text, 'parent') - - list_text = self.shell('deployment-list -s 123') - - for r in required: - self.assertRegexpMatches(list_text, r) - self.assertNotRegexpMatches(list_text, 'parent') - - def test_deploy_show(self): - self.register_keystone_auth_fixture() - resp_dict = {'software_deployment': { - 'status': 'COMPLETE', - 'server_id': '700115e5-0100-4ecc-9ef7-9e05f27d8803', - 'config_id': '18c4fc03-f897-4a1d-aaad-2b7622e60257', - 'output_values': { - 'deploy_stdout': '', - 'deploy_stderr': '', - 'deploy_status_code': 0, - 'result': 'The result value' - }, - 'input_values': {}, - 'action': 'CREATE', - 'status_reason': 'Outputs received', - 'id': 'defg' - }} - - resp_string = jsonutils.dumps(resp_dict) - headers = {'content-type': 'application/json'} - http_resp = fakes.FakeHTTPResponse(200, 'OK', headers, resp_string) - response = (http_resp, resp_dict) - if self.client == http.SessionClient: - self.client.request( - '/software_deployments/defg', 'GET').AndReturn(http_resp) - self.client.request( - '/software_deployments/defgh', 'GET').AndRaise( - exc.HTTPNotFound()) - else: - self.client.json_request( - 'GET', '/software_deployments/defg').AndReturn(response) - self.client.json_request( - 'GET', '/software_deployments/defgh').AndRaise( - exc.HTTPNotFound()) - - self.m.ReplayAll() - - text = self.shell('deployment-show defg') - - required = [ - 'status', - 'server_id', - 'config_id', - 'output_values', - 'input_values', - 'action', - 'status_reason', - 'id', - ] - for r in required: - self.assertRegexpMatches(text, r) - self.assertRaises(exc.CommandError, self.shell, - 'deployment-show defgh') - - def test_deploy_delete(self): - self.register_keystone_auth_fixture() - headers = {'content-type': 'application/json'} - http_resp = fakes.FakeHTTPResponse(204, 'OK', headers, None) - response = (http_resp, '') - if self.client == http.SessionClient: - self.client.request( - '/software_deployments/defg', - 'DELETE').AndReturn(http_resp) - self.client.request( - '/software_deployments/qwer', - 'DELETE').AndReturn(http_resp) - self.client.request( - '/software_deployments/defg', - 'DELETE').AndRaise(exc.HTTPNotFound()) - self.client.request( - '/software_deployments/qwer', - 'DELETE').AndRaise(exc.HTTPNotFound()) - else: - self.client.raw_request( - 'DELETE', '/software_deployments/defg').AndReturn(response) - self.client.raw_request( - 'DELETE', '/software_deployments/qwer').AndReturn(response) - self.client.raw_request( - 'DELETE', - '/software_deployments/defg').AndRaise(exc.HTTPNotFound()) - self.client.raw_request( - 'DELETE', - '/software_deployments/qwer').AndRaise(exc.HTTPNotFound()) - - self.m.ReplayAll() - - self.assertEqual('', self.shell('deployment-delete defg qwer')) - self.assertRaises(exc.CommandError, self.shell, - 'deployment-delete defg qwer') - - def test_deploy_metadata(self): - self.register_keystone_auth_fixture() - resp_dict = {'metadata': [ - {'id': 'abcd'}, - {'id': 'defg'} - ]} - - resp_string = jsonutils.dumps(resp_dict) - headers = {'content-type': 'application/json'} - http_resp = fakes.FakeHTTPResponse(200, 'OK', headers, resp_string) - response = (http_resp, resp_dict) - if self.client == http.SessionClient: - self.client.request( - '/software_deployments/metadata/aaaa', - 'GET').AndReturn(http_resp) - else: - self.client.json_request( - 'GET', '/software_deployments/metadata/aaaa').AndReturn( - response) - - self.m.ReplayAll() - - build_info_text = self.shell('deployment-metadata-show aaaa') - - required = [ - 'abcd', - 'defg', - 'id', - ] - for r in required: - self.assertRegexpMatches(build_info_text, r) - - def test_deploy_output_show(self): - self.register_keystone_auth_fixture() - resp_dict = {'software_deployment': { - 'status': 'COMPLETE', - 'server_id': '700115e5-0100-4ecc-9ef7-9e05f27d8803', - 'config_id': '18c4fc03-f897-4a1d-aaad-2b7622e60257', - 'output_values': { - 'deploy_stdout': '', - 'deploy_stderr': '', - 'deploy_status_code': 0, - 'result': 'The result value', - 'dict_output': {'foo': 'bar'}, - 'list_output': ['foo', 'bar'] - }, - 'input_values': {}, - 'action': 'CREATE', - 'status_reason': 'Outputs received', - 'id': 'defg' - }} - - resp_string = jsonutils.dumps(resp_dict) - headers = {'content-type': 'application/json'} - http_resp = fakes.FakeHTTPResponse(200, 'OK', headers, resp_string) - response = (http_resp, resp_dict) - if self.client == http.SessionClient: - self.client.request( - '/software_deployments/defgh', 'GET').AndRaise( - exc.HTTPNotFound()) - self.client.request( - '/software_deployments/defg', 'GET').MultipleTimes().AndReturn( - http_resp) - else: - self.client.json_request( - 'GET', '/software_deployments/defgh').AndRaise( - exc.HTTPNotFound()) - self.client.json_request( - 'GET', '/software_deployments/defg').MultipleTimes().AndReturn( - response) - - self.m.ReplayAll() - - self.assertRaises(exc.CommandError, self.shell, - 'deployment-output-show defgh result') - self.assertEqual( - 'The result value\n', - self.shell('deployment-output-show defg result')) - self.assertEqual( - '"The result value"\n', - self.shell('deployment-output-show --format json defg result')) - - self.assertEqual( - '{\n "foo": "bar"\n}\n', - self.shell('deployment-output-show defg dict_output')) - self.assertEqual( - self.shell( - 'deployment-output-show --format raw defg dict_output'), - self.shell( - 'deployment-output-show --format json defg dict_output')) - - self.assertEqual( - '[\n "foo", \n "bar"\n]\n', - self.shell('deployment-output-show defg list_output')) - self.assertEqual( - self.shell( - 'deployment-output-show --format raw defg list_output'), - self.shell( - 'deployment-output-show --format json defg list_output')) - - self.assertEqual({ - 'deploy_stdout': '', - 'deploy_stderr': '', - 'deploy_status_code': 0, - 'result': 'The result value', - 'dict_output': {'foo': 'bar'}, - 'list_output': ['foo', 'bar']}, - jsonutils.loads(self.shell( - 'deployment-output-show --format json defg --all')) - ) - - -class ShellTestBuildInfo(ShellBase): - - def setUp(self): - super(ShellTestBuildInfo, self).setUp() - self._set_fake_env() - - def _set_fake_env(self): - '''Patch os.environ to avoid required auth info.''' - self.set_fake_env(FAKE_ENV_KEYSTONE_V2) - - def test_build_info(self): - self.register_keystone_auth_fixture() - resp_dict = { - 'build_info': { - 'api': {'revision': 'api_revision'}, - 'engine': {'revision': 'engine_revision'} - } - } - resp_string = jsonutils.dumps(resp_dict) - headers = {'content-type': 'application/json'} - http_resp = fakes.FakeHTTPResponse(200, 'OK', headers, resp_string) - response = http_resp - http.SessionClient.request( - '/build_info', 'GET').AndReturn(response) - - self.m.ReplayAll() - - build_info_text = self.shell('build-info') - - required = [ - 'api', - 'engine', - 'revision', - 'api_revision', - 'engine_revision', - ] - for r in required: - self.assertRegexpMatches(build_info_text, r) - - -class ShellTestToken(ShellTestUserPass): - - # Rerun all ShellTestUserPass test with token auth - def setUp(self): - self.token = 'a_token' - super(ShellTestToken, self).setUp() - - def _set_fake_env(self): - fake_env = { - 'OS_AUTH_TOKEN': self.token, - 'OS_TENANT_ID': 'tenant_id', - 'OS_AUTH_URL': BASE_URL, - # Note we also set username/password, because create/update - # pass them even if we have a token to support storing credentials - # Hopefully at some point we can remove this and move to only - # storing trust id's in heat-engine instead.. - 'OS_USERNAME': 'username', - 'OS_PASSWORD': 'password' - } - self.set_fake_env(fake_env) - - -class ShellTestUserPassKeystoneV3(ShellTestUserPass): - - def _set_fake_env(self): - self.set_fake_env(FAKE_ENV_KEYSTONE_V3) - - -class ShellTestStandaloneToken(ShellTestUserPass): - - # Rerun all ShellTestUserPass test in standalone mode, where we - # specify --os-no-client-auth, a token and Heat endpoint - def setUp(self): - self.token = 'a_token' - super(ShellTestStandaloneToken, self).setUp() - self.client = http.HTTPClient - - def _set_fake_env(self): - fake_env = { - 'OS_AUTH_TOKEN': self.token, - 'OS_NO_CLIENT_AUTH': 'True', - 'HEAT_URL': 'http://no.where', - 'OS_AUTH_URL': BASE_URL, - # Note we also set username/password, because create/update - # pass them even if we have a token to support storing credentials - # Hopefully at some point we can remove this and move to only - # storing trust id's in heat-engine instead.. - 'OS_USERNAME': 'username', - 'OS_PASSWORD': 'password' - } - self.set_fake_env(fake_env) - - def test_bad_template_file(self): - self.register_keystone_auth_fixture() - failed_msg = 'Error parsing template ' - - with tempfile.NamedTemporaryFile() as bad_json_file: - bad_json_file.write(b"{foo:}") - bad_json_file.flush() - self.shell_error("stack-create ts -f %s" % bad_json_file.name, - failed_msg) - - with tempfile.NamedTemporaryFile() as bad_json_file: - bad_json_file.write(b'{"foo": None}') - bad_json_file.flush() - self.shell_error("stack-create ts -f %s" % bad_json_file.name, - failed_msg) - - def test_commandline_args_passed_to_requests(self): - """Check that we have sent the proper arguments to requests.""" - self.register_keystone_auth_fixture() - - # we need a mock for 'request' to check whether proper arguments - # sent to request in the form of HTTP headers. So unset - # stubs(json_request, raw_request) and create a new mock for request. - self.m.UnsetStubs() - self.m.StubOutWithMock(requests, 'request') - - # Record a 200 - mock_conn = http.requests.request( - 'GET', 'http://no.where/stacks?', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'X-Auth-Token': self.token, - 'X-Auth-Url': BASE_URL, - 'User-Agent': 'python-heatclient'}) - resp_dict = {"stacks": [ - { - "id": "1", - "stack_name": "teststack", - "stack_owner": "testowner", - "project": "testproject", - "stack_status": 'CREATE_COMPLETE', - "creation_time": "2014-10-15T01:58:47Z" - }]} - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/json'}, - jsonutils.dumps(resp_dict))) - - # Replay, create client, assert - self.m.ReplayAll() - list_text = self.shell('stack-list') - required = [ - 'id', - 'stack_status', - 'creation_time', - 'teststack', - '1', - 'CREATE_COMPLETE', - ] - for r in required: - self.assertRegexpMatches(list_text, r) - self.assertNotRegexpMatches(list_text, 'parent') - - -class MockShellBase(TestCase): - - def setUp(self): - super(MockShellBase, self).setUp() - self.jreq_mock = self.patch( - 'heatclient.common.http.HTTPClient.json_request') - self.session_jreq_mock = self.patch( - 'heatclient.common.http.SessionClient.request') - - # Some tests set exc.verbose = 1, so reset on cleanup - def unset_exc_verbose(): - exc.verbose = 0 - - self.addCleanup(unset_exc_verbose) - - def shell(self, argstr): - orig = sys.stdout - try: - sys.stdout = six.StringIO() - _shell = heatclient.shell.HeatShell() - _shell.main(argstr.split()) - self.subcommands = _shell.subcommands.keys() - except SystemExit: - exc_type, exc_value, exc_traceback = sys.exc_info() - self.assertEqual(0, exc_value.code) - finally: - out = sys.stdout.getvalue() - sys.stdout.close() - sys.stdout = orig - - return out - - -class MockShellTestUserPass(MockShellBase): - - def setUp(self): - super(MockShellTestUserPass, self).setUp() - self._set_fake_env() - - def _set_fake_env(self): - self.set_fake_env(FAKE_ENV_KEYSTONE_V2) - - def test_stack_list_with_args(self): - self.register_keystone_auth_fixture() - self.jreq_mock.return_value = fakes.mock_script_heat_list() - self.session_jreq_mock.return_value = fakes.mock_script_heat_list()[0] - - list_text = self.shell('stack-list' - ' --limit 2' - ' --marker fake_id' - ' --filters=status=COMPLETE' - ' --filters=status=FAILED' - ' --tags=tag1,tag2' - ' --tags-any=tag3,tag4' - ' --not-tags=tag5,tag6' - ' --not-tags-any=tag7,tag8' - ' --global-tenant' - ' --show-deleted' - ' --show-hidden') - - required = [ - 'stack_owner', - 'project', - 'testproject', - 'teststack', - 'teststack2', - ] - for r in required: - self.assertRegexpMatches(list_text, r) - self.assertNotRegexpMatches(list_text, 'parent') - - if self.jreq_mock.call_args is None: - self.assertEqual(1, self.session_jreq_mock.call_count) - url, method = self.session_jreq_mock.call_args[0] - else: - self.assertEqual(1, self.jreq_mock.call_count) - method, url = self.jreq_mock.call_args[0] - self.assertEqual('GET', method) - base_url, query_params = utils.parse_query_url(url) - self.assertEqual('/stacks', base_url) - expected_query_dict = {'limit': ['2'], - 'status': ['COMPLETE', 'FAILED'], - 'marker': ['fake_id'], - 'tags': ['tag1,tag2'], - 'tags_any': ['tag3,tag4'], - 'not_tags': ['tag5,tag6'], - 'not_tags_any': ['tag7,tag8'], - 'global_tenant': ['True'], - 'show_deleted': ['True'], - 'show_hidden': ['True']} - self.assertEqual(expected_query_dict, query_params) - - -class MockShellTestToken(MockShellTestUserPass): - - # Rerun all ShellTestUserPass test with token auth - def setUp(self): - self.token = 'a_token' - super(MockShellTestToken, self).setUp() - - def _set_fake_env(self): - fake_env = { - 'OS_AUTH_TOKEN': self.token, - 'OS_TENANT_ID': 'tenant_id', - 'OS_AUTH_URL': BASE_URL, - # Note we also set username/password, because create/update - # pass them even if we have a token to support storing credentials - # Hopefully at some point we can remove this and move to only - # storing trust id's in heat-engine instead.. - 'OS_USERNAME': 'username', - 'OS_PASSWORD': 'password' - } - self.set_fake_env(fake_env) - - -class MockShellTestUserPassKeystoneV3(MockShellTestUserPass): - - def _set_fake_env(self): - self.set_fake_env(FAKE_ENV_KEYSTONE_V3) - - -class MockShellTestStandaloneToken(MockShellTestUserPass): - - # Rerun all ShellTestUserPass test in standalone mode, where we - # specify --os-no-client-auth, a token and Heat endpoint - def setUp(self): - self.token = 'a_token' - super(MockShellTestStandaloneToken, self).setUp() - - def _set_fake_env(self): - fake_env = { - 'OS_AUTH_TOKEN': self.token, - 'OS_NO_CLIENT_AUTH': 'True', - 'HEAT_URL': 'http://no.where', - 'OS_AUTH_URL': BASE_URL, - # Note we also set username/password, because create/update - # pass them even if we have a token to support storing credentials - # Hopefully at some point we can remove this and move to only - # storing trust id's in heat-engine instead.. - 'OS_USERNAME': 'username', - 'OS_PASSWORD': 'password' - } - self.set_fake_env(fake_env) - - -class ShellTestManageService(ShellBase): - - def setUp(self): - super(ShellTestManageService, self).setUp() - self.set_fake_env(FAKE_ENV_KEYSTONE_V2) - - def _set_fake_env(self): - '''Patch os.environ to avoid required auth info.''' - self.set_fake_env(FAKE_ENV_KEYSTONE_V2) - - def _test_error_case(self, code, message): - self.register_keystone_auth_fixture() - - resp_dict = { - 'explanation': '', - 'code': code, - 'error': { - 'message': message, - 'type': '', - 'traceback': '', - }, - 'title': 'test title' - } - resp_string = jsonutils.dumps(resp_dict) - resp = fakes.FakeHTTPResponse( - code, - 'test reason', - {'content-type': 'application/json'}, - resp_string) - (http.SessionClient.request('/services', 'GET'). - AndRaise(exc.from_response(resp))) - - exc.verbose = 1 - - self.m.ReplayAll() - e = self.assertRaises(exc.HTTPException, - self.shell, "service-list") - self.assertIn(message, str(e)) - - def test_service_list(self): - self.register_keystone_auth_fixture() - resp_dict = { - 'services': [ - { - "status": "up", - "binary": "heat-engine", - "engine_id": "9d9242c3-4b9e-45e1-9e74-7615fbf20e5d", - "hostname": "mrkanag", - "updated_at": "2015-02-03T05:57:59.000000", - "topic": "engine", - "host": "engine-1" - } - ] - } - resp_string = jsonutils.dumps(resp_dict) - headers = {} - http_resp = fakes.FakeHTTPResponse(200, 'OK', headers, resp_string) - http.SessionClient.request('/services', 'GET').AndReturn(http_resp) - - self.m.ReplayAll() - services_text = self.shell('service-list') - - required = [ - 'hostname', 'binary', 'engine_id', 'host', - 'topic', 'updated_at', 'status' - ] - for r in required: - self.assertRegexpMatches(services_text, r) - - def test_service_list_503(self): - self._test_error_case( - message='All heat engines are down', - code=503) - - def test_service_list_403(self): - self._test_error_case( - message=('You are not authorized to ' - 'complete this action'), - code=403) diff --git a/stacktaskclient/tests/unit/test_software_configs.py b/stacktaskclient/tests/unit/test_software_configs.py deleted file mode 100644 index 936ed50..0000000 --- a/stacktaskclient/tests/unit/test_software_configs.py +++ /dev/null @@ -1,99 +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 mock -import testtools - -from heatclient.common import utils -from heatclient.v1 import software_configs - - -class SoftwareConfigTest(testtools.TestCase): - - def setUp(self): - super(SoftwareConfigTest, self).setUp() - config_id = 'bca6871d-86c0-4aff-b792-58a1f6947b57' - self.config = software_configs.SoftwareConfig(mock.MagicMock(), - info={'id': config_id}) - self.config_id = config_id - - def test_delete(self): - self.config.manager.delete.return_value = None - self.assertIsNone(self.config.delete()) - kwargs = self.config.manager.delete.call_args[1] - self.assertEqual(self.config_id, kwargs['config_id']) - - def test_data(self): - self.assertEqual( - "" % self.config_id, str(self.config)) - self.config.manager.data.return_value = None - self.config.data(name='config_mysql') - kwargs = self.config.manager.data.call_args[1] - self.assertEqual('config_mysql', kwargs['name']) - - -class SoftwareConfigManagerTest(testtools.TestCase): - - def setUp(self): - super(SoftwareConfigManagerTest, self).setUp() - self.manager = software_configs.SoftwareConfigManager(mock.MagicMock()) - - @mock.patch.object(utils, 'get_response_body') - def test_get(self, mock_body): - config_id = 'bca6871d-86c0-4aff-b792-58a1f6947b57' - data = { - 'id': config_id, - 'name': 'config_mysql', - 'group': 'Heat::Shell', - 'config': '#!/bin/bash', - 'inputs': [], - 'ouputs': [], - 'options': []} - - self.manager.client.json_request.return_value = ( - {}, {'software_config': data}) - mock_body.return_value = {'software_config': data} - result = self.manager.get(config_id=config_id) - self.assertEqual(software_configs.SoftwareConfig(self.manager, data), - result) - call_args = self.manager.client.get.call_args - self.assertEqual( - ('/software_configs/%s' % config_id,), *call_args) - - @mock.patch.object(utils, 'get_response_body') - def test_create(self, mock_body): - config_id = 'bca6871d-86c0-4aff-b792-58a1f6947b57' - body = { - 'name': 'config_mysql', - 'group': 'Heat::Shell', - 'config': '#!/bin/bash', - 'inputs': [], - 'ouputs': [], - 'options': []} - data = body.copy() - data['id'] = config_id - self.manager.client.json_request.return_value = ( - {}, {'software_config': data}) - mock_body.return_value = {'software_config': data} - result = self.manager.create(**body) - self.assertEqual(software_configs.SoftwareConfig(self.manager, data), - result) - args, kargs = self.manager.client.post.call_args - self.assertEqual('/software_configs', args[0]) - self.assertEqual({'data': body}, kargs) - - def test_delete(self): - config_id = 'bca6871d-86c0-4aff-b792-58a1f6947b57' - self.manager.delete(config_id) - call_args = self.manager.client.delete.call_args - self.assertEqual( - ('/software_configs/%s' % config_id,), *call_args) diff --git a/stacktaskclient/tests/unit/test_software_deployments.py b/stacktaskclient/tests/unit/test_software_deployments.py deleted file mode 100644 index 47a446d..0000000 --- a/stacktaskclient/tests/unit/test_software_deployments.py +++ /dev/null @@ -1,166 +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 mock -import testtools - -from heatclient.common import utils -from heatclient.v1 import software_deployments - - -class SoftwareDeploymentTest(testtools.TestCase): - - def setUp(self): - super(SoftwareDeploymentTest, self).setUp() - deployment_id = 'bca6871d-86c0-4aff-b792-58a1f6947b57' - self.deployment = software_deployments.SoftwareDeployment( - mock.MagicMock(), info={'id': deployment_id}) - self.deployment_id = deployment_id - - def test_delete(self): - self.deployment.manager.delete.return_value = None - self.assertIsNone(self.deployment.delete()) - kwargs = self.deployment.manager.delete.call_args[1] - self.assertEqual(self.deployment_id, kwargs['deployment_id']) - - def test_update(self): - self.assertEqual( - "" % self.deployment_id, - str(self.deployment)) - self.deployment.manager.update.return_value = None - config_id = 'd00ba4aa-db33-42e1-92f4-2a6469260107' - self.assertIsNone(self.deployment.update(config_id=config_id)) - kwargs = self.deployment.manager.update.call_args[1] - self.assertEqual(self.deployment_id, kwargs['deployment_id']) - self.assertEqual(config_id, kwargs['config_id']) - - -class SoftwareDeploymentManagerTest(testtools.TestCase): - - def setUp(self): - super(SoftwareDeploymentManagerTest, self).setUp() - self.manager = software_deployments.SoftwareDeploymentManager( - mock.MagicMock()) - - def test_list(self): - server_id = 'fc01f89f-e151-4dc5-9c28-543c0d20ed6a' - self.manager.client.json_request.return_value = ( - {}, - {'software_deployments': []}) - result = self.manager.list(server_id=server_id) - self.assertEqual([], result) - call_args = self.manager.client.get.call_args - self.assertEqual( - ('/software_deployments?server_id=%s' % server_id,), - *call_args) - - @mock.patch.object(utils, 'get_response_body') - def test_metadata(self, mock_utils): - server_id = 'fc01f89f-e151-4dc5-9c28-543c0d20ed6a' - metadata = { - 'group1': [{'foo': 'bar'}], - 'group2': [{'foo': 'bar'}, {'bar': 'baz'}], - } - self.manager.client.get.return_value = {} - mock_utils.return_value = {'metadata': metadata} - result = self.manager.metadata(server_id=server_id) - self.assertEqual(metadata, result) - call_args = self.manager.client.get.call_args - self.assertEqual( - '/software_deployments/metadata/%s' % server_id, - call_args[0][0]) - - @mock.patch.object(utils, 'get_response_body') - def test_get(self, mock_utils): - deployment_id = 'bca6871d-86c0-4aff-b792-58a1f6947b57' - config_id = 'd00ba4aa-db33-42e1-92f4-2a6469260107' - server_id = 'fb322564-7927-473d-8aad-68ae7fbf2abf' - data = { - 'id': deployment_id, - 'server_id': server_id, - 'input_values': {}, - 'output_values': {}, - 'action': 'INIT', - 'status': 'COMPLETE', - 'status_reason': None, - 'signal_id': None, - 'config_id': config_id, - 'config': '#!/bin/bash', - 'name': 'config_mysql', - 'group': 'Heat::Shell', - 'inputs': [], - 'outputs': [], - 'options': []} - - self.manager.client.get.return_value = {} - mock_utils.return_value = {'software_deployment': data} - result = self.manager.get(deployment_id=deployment_id) - self.assertEqual(software_deployments.SoftwareDeployment( - self.manager, data), result) - call_args = self.manager.client.get.call_args - self.assertEqual( - ('/software_deployments/%s' % deployment_id,), *call_args) - - @mock.patch.object(utils, 'get_response_body') - def test_create(self, mock_utils): - deployment_id = 'bca6871d-86c0-4aff-b792-58a1f6947b57' - config_id = 'd00ba4aa-db33-42e1-92f4-2a6469260107' - server_id = 'fb322564-7927-473d-8aad-68ae7fbf2abf' - body = { - 'server_id': server_id, - 'input_values': {}, - 'action': 'INIT', - 'status': 'COMPLETE', - 'status_reason': None, - 'signal_id': None, - 'config_id': config_id} - data = body.copy() - data['id'] = deployment_id - self.manager.client.post.return_value = {} - mock_utils.return_value = {'software_deployment': data} - result = self.manager.create(**body) - self.assertEqual(software_deployments.SoftwareDeployment( - self.manager, data), result) - args, kwargs = self.manager.client.post.call_args - self.assertEqual('/software_deployments', args[0]) - self.assertEqual({'data': body}, kwargs) - - def test_delete(self): - deployment_id = 'bca6871d-86c0-4aff-b792-58a1f6947b57' - self.manager.delete(deployment_id) - call_args = self.manager.client.delete.call_args - self.assertEqual( - ('/software_deployments/%s' % deployment_id,), *call_args) - - @mock.patch.object(utils, 'get_response_body') - def test_update(self, mock_utils): - deployment_id = 'bca6871d-86c0-4aff-b792-58a1f6947b57' - config_id = 'd00ba4aa-db33-42e1-92f4-2a6469260107' - server_id = 'fb322564-7927-473d-8aad-68ae7fbf2abf' - body = { - 'server_id': server_id, - 'input_values': {}, - 'action': 'DEPLOYED', - 'status': 'COMPLETE', - 'status_reason': None, - 'signal_id': None, - 'config_id': config_id} - data = body.copy() - data['id'] = deployment_id - self.manager.client.put.return_value = {} - mock_utils.return_value = {'software_deployment': data} - result = self.manager.update(deployment_id, **body) - self.assertEqual(software_deployments.SoftwareDeployment( - self.manager, data), result) - args, kwargs = self.manager.client.put.call_args - self.assertEqual('/software_deployments/%s' % deployment_id, args[0]) - self.assertEqual({'data': body}, kwargs) diff --git a/stacktaskclient/tests/unit/test_stacks.py b/stacktaskclient/tests/unit/test_stacks.py deleted file mode 100644 index 12ebdc0..0000000 --- a/stacktaskclient/tests/unit/test_stacks.py +++ /dev/null @@ -1,316 +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. -from heatclient.v1 import stacks - -import mock -import testscenarios -from testscenarios import scenarios as scnrs -import testtools - -load_tests = testscenarios.load_tests_apply_scenarios - - -def mock_stack(manager, stack_name, stack_id): - return stacks.Stack(manager, { - "id": stack_id, - "stack_name": stack_name, - "links": [{ - "href": "http://192.0.2.1:8004/v1/1234/stacks/%s/%s" % ( - stack_name, stack_id), - "rel": "self"}], - "description": "No description", - "stack_status_reason": "Stack create completed successfully", - "creation_time": "2013-08-04T20:57:55Z", - "updated_time": "2013-08-04T20:57:55Z", - "stack_status": "CREATE_COMPLETE" - }) - - -class StackStatusActionTest(testtools.TestCase): - - scenarios = scnrs.multiply_scenarios([ - ('CREATE', dict(action='CREATE')), - ('DELETE', dict(action='DELETE')), - ('UPDATE', dict(action='UPDATE')), - ('ROLLBACK', dict(action='ROLLBACK')), - ('SUSPEND', dict(action='SUSPEND')), - ('RESUME', dict(action='RESUME')), - ('CHECK', dict(action='CHECK')) - ], [ - ('IN_PROGRESS', dict(status='IN_PROGRESS')), - ('FAILED', dict(status='FAILED')), - ('COMPLETE', dict(status='COMPLETE')) - ]) - - def test_status_action(self): - stack_status = '%s_%s' % (self.action, self.status) - stack = mock_stack(None, 'stack_1', 'abcd1234') - stack.stack_status = stack_status - self.assertEqual(self.action, stack.action) - self.assertEqual(self.status, stack.status) - - -class StackIdentifierTest(testtools.TestCase): - - def test_stack_identifier(self): - stack = mock_stack(None, 'the_stack', 'abcd1234') - self.assertEqual('the_stack/abcd1234', stack.identifier) - - -class StackOperationsTest(testtools.TestCase): - - def test_delete_stack(self): - manager = mock.MagicMock() - stack = mock_stack(manager, 'the_stack', 'abcd1234') - stack.delete() - manager.delete.assert_called_once_with('the_stack/abcd1234') - - def test_abandon_stack(self): - manager = mock.MagicMock() - stack = mock_stack(manager, 'the_stack', 'abcd1234') - stack.abandon() - manager.abandon.assert_called_once_with('the_stack/abcd1234') - - def test_get_stack(self): - manager = mock.MagicMock() - stack = mock_stack(manager, 'the_stack', 'abcd1234') - stack.get() - manager.get.assert_called_once_with('the_stack/abcd1234') - - def test_update_stack(self): - manager = mock.MagicMock() - stack = mock_stack(manager, 'the_stack', 'abcd1234') - stack.update() - manager.update.assert_called_once_with('the_stack/abcd1234') - - def test_create_stack(self): - manager = mock.MagicMock() - stack = mock_stack(manager, 'the_stack', 'abcd1234') - stack = stack.create() - manager.create.assert_called_once_with('the_stack/abcd1234') - - def test_preview_stack(self): - manager = mock.MagicMock() - stack = mock_stack(manager, 'the_stack', 'abcd1234') - stack = stack.preview() - manager.preview.assert_called_once_with() - - def test_snapshot(self): - manager = mock.MagicMock() - stack = mock_stack(manager, 'the_stack', 'abcd1234') - stack.snapshot('foo') - manager.snapshot.assert_called_once_with('the_stack/abcd1234', 'foo') - - def test_snapshot_show(self): - manager = mock.MagicMock() - stack = mock_stack(manager, 'the_stack', 'abcd1234') - stack.snapshot_show('snap1234') - manager.snapshot_show.assert_called_once_with( - 'the_stack/abcd1234', 'snap1234') - - def test_snapshot_delete(self): - manager = mock.MagicMock() - stack = mock_stack(manager, 'the_stack', 'abcd1234') - stack.snapshot_delete('snap1234') - manager.snapshot_delete.assert_called_once_with( - 'the_stack/abcd1234', 'snap1234') - - def test_restore(self): - manager = mock.MagicMock() - stack = mock_stack(manager, 'the_stack', 'abcd1234') - stack.restore('snap1234') - manager.restore.assert_called_once_with( - 'the_stack/abcd1234', 'snap1234') - - def test_snapshot_list(self): - manager = mock.MagicMock() - stack = mock_stack(manager, 'the_stack', 'abcd1234') - stack.snapshot_list() - manager.snapshot_list.assert_called_once_with('the_stack/abcd1234') - - -class StackManagerNoPaginationTest(testtools.TestCase): - - scenarios = [ - ('total_0', dict(total=0)), - ('total_1', dict(total=1)), - ('total_9', dict(total=9)), - ('total_10', dict(total=10)), - ('total_11', dict(total=11)), - ('total_19', dict(total=19)), - ('total_20', dict(total=20)), - ('total_21', dict(total=21)), - ('total_49', dict(total=49)), - ('total_50', dict(total=50)), - ('total_51', dict(total=51)), - ('total_95', dict(total=95)), - ] - - # absolute limit for results returned - limit = 50 - - def mock_manager(self): - manager = stacks.StackManager(None) - manager._list = mock.MagicMock() - - def mock_list(*args, **kwargs): - def results(): - for i in range(0, self.total): - stack_name = 'stack_%s' % (i + 1) - stack_id = 'abcd1234-%s' % (i + 1) - yield mock_stack(manager, stack_name, stack_id) - - return list(results()) - - manager._list.side_effect = mock_list - return manager - - def test_stack_list_no_pagination(self): - manager = self.mock_manager() - - results = list(manager.list()) - manager._list.assert_called_once_with( - '/stacks?', 'stacks') - - # paginate is not specified, so the total - # results is always returned - self.assertEqual(self.total, len(results)) - - if self.total > 0: - self.assertEqual('stack_1', results[0].stack_name) - self.assertEqual('stack_%s' % self.total, results[-1].stack_name) - - -class StackManagerPaginationTest(testtools.TestCase): - - scenarios = [ - ('0_offset_0', dict( - offset=0, - total=0, - results=((0, 0),) - )), - ('1_offset_0', dict( - offset=0, - total=1, - results=((0, 1),) - )), - ('9_offset_0', dict( - offset=0, - total=9, - results=((0, 9),) - )), - ('10_offset_0', dict( - offset=0, - total=10, - results=((0, 10), (10, 10)) - )), - ('11_offset_0', dict( - offset=0, - total=11, - results=((0, 10), (10, 11)) - )), - ('11_offset_10', dict( - offset=10, - total=11, - results=((10, 11),) - )), - ('19_offset_10', dict( - offset=10, - total=19, - results=((10, 19),) - )), - ('20_offset_10', dict( - offset=10, - total=20, - results=((10, 20), (20, 20)) - )), - ('21_offset_10', dict( - offset=10, - total=21, - results=((10, 20), (20, 21)) - )), - ('21_offset_0', dict( - offset=0, - total=21, - results=((0, 10), (10, 20), (20, 21)) - )), - ('21_offset_20', dict( - offset=20, - total=21, - results=((20, 21),) - )), - ('95_offset_90', dict( - offset=90, - total=95, - results=((90, 95),) - )), - ] - - # absolute limit for results returned - limit = 50 - - def mock_manager(self): - manager = stacks.StackManager(None) - manager._list = mock.MagicMock() - - def mock_list(arg_url, arg_response_key): - try: - result = self.results[self.result_index] - except IndexError: - return [] - self.result_index = self.result_index + 1 - - limit_string = 'limit=%s' % self.limit - self.assertIn(limit_string, arg_url) - - offset = result[0] - if offset > 0: - offset_string = 'marker=abcd1234-%s' % offset - self.assertIn(offset_string, arg_url) - - def results(): - - for i in range(*result): - self.limit -= 1 - stack_name = 'stack_%s' % (i + 1) - stack_id = 'abcd1234-%s' % (i + 1) - yield mock_stack(manager, stack_name, stack_id) - - return list(results()) - - manager._list.side_effect = mock_list - return manager - - def test_stack_list_pagination(self): - manager = self.mock_manager() - - list_params = {'limit': self.limit} - - if self.offset > 0: - marker = 'abcd1234-%s' % self.offset - list_params['marker'] = marker - - self.result_index = 0 - results = list(manager.list(**list_params)) - - # assert that the list method has been called enough times - self.assertEqual(len(self.results), self.result_index) - - last_result = min(self.limit, self.total - self.offset) - # one or more list calls have been recomposed into a single list - self.assertEqual(last_result, len(results)) - - if last_result > 0: - self.assertEqual('stack_%s' % (self.offset + 1), - results[0].stack_name) - self.assertEqual('stack_%s' % (self.offset + last_result), - results[-1].stack_name) diff --git a/stacktaskclient/tests/unit/test_template_format.py b/stacktaskclient/tests/unit/test_template_format.py deleted file mode 100644 index ca8d1fc..0000000 --- a/stacktaskclient/tests/unit/test_template_format.py +++ /dev/null @@ -1,50 +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 mock -import testscenarios -import testtools -import yaml - -from heatclient.common import template_format - - -load_tests = testscenarios.load_tests_apply_scenarios - - -class YamlParseExceptions(testtools.TestCase): - - scenarios = [ - ('scanner', dict(raised_exception=yaml.scanner.ScannerError())), - ('parser', dict(raised_exception=yaml.parser.ParserError())), - ('reader', - dict(raised_exception=yaml.reader.ReaderError('', '', '', '', ''))), - ] - - def test_parse_to_value_exception(self): - text = 'not important' - - with mock.patch.object(yaml, 'load') as yaml_loader: - yaml_loader.side_effect = self.raised_exception - - self.assertRaises(ValueError, - template_format.parse, text) - - def test_parse_no_version_format(self): - yaml = '' - self.assertRaises(ValueError, template_format.parse, yaml) - yaml2 = '''Parameters: {} -Mappings: {} -Resources: {} -Outputs: {} -''' - self.assertRaises(ValueError, template_format.parse, yaml2) diff --git a/stacktaskclient/tests/unit/test_template_utils.py b/stacktaskclient/tests/unit/test_template_utils.py deleted file mode 100644 index 9f917b3..0000000 --- a/stacktaskclient/tests/unit/test_template_utils.py +++ /dev/null @@ -1,1017 +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 base64 -import json -from mox3 import mox -import six -from six.moves.urllib import request -import tempfile -import testtools -from testtools import matchers -import yaml - -from heatclient.common import template_utils -from heatclient.common import utils -from heatclient import exc - - -class ShellEnvironmentTest(testtools.TestCase): - - template_a = b'{"heat_template_version": "2013-05-23"}' - - def setUp(self): - super(ShellEnvironmentTest, self).setUp() - self.m = mox.Mox() - - self.addCleanup(self.m.VerifyAll) - self.addCleanup(self.m.UnsetStubs) - - def collect_links(self, env, content, url, env_base_url=''): - - jenv = yaml.safe_load(env) - files = {} - if url: - self.m.StubOutWithMock(request, 'urlopen') - request.urlopen(url).AndReturn(six.BytesIO(content)) - request.urlopen(url).AndReturn(six.BytesIO(content)) - self.m.ReplayAll() - - template_utils.resolve_environment_urls( - jenv.get('resource_registry'), files, env_base_url) - if url: - self.assertEqual(content.decode('utf-8'), files[url]) - - def test_process_environment_file(self): - - self.m.StubOutWithMock(request, 'urlopen') - env_file = '/home/my/dir/env.yaml' - env = b''' - resource_registry: - "OS::Thingy": "file:///home/b/a.yaml" - ''' - - request.urlopen('file://%s' % env_file).AndReturn( - six.BytesIO(env)) - request.urlopen('file:///home/b/a.yaml').AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/b/a.yaml').AndReturn( - six.BytesIO(self.template_a)) - self.m.ReplayAll() - - files, env_dict = template_utils.process_environment_and_files( - env_file) - self.assertEqual( - {'resource_registry': { - 'OS::Thingy': 'file:///home/b/a.yaml'}}, - env_dict) - self.assertEqual(self.template_a.decode('utf-8'), - files['file:///home/b/a.yaml']) - - def test_process_environment_relative_file(self): - - self.m.StubOutWithMock(request, 'urlopen') - env_file = '/home/my/dir/env.yaml' - env_url = 'file:///home/my/dir/env.yaml' - env = b''' - resource_registry: - "OS::Thingy": a.yaml - ''' - - request.urlopen(env_url).AndReturn( - six.BytesIO(env)) - request.urlopen('file:///home/my/dir/a.yaml').AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/my/dir/a.yaml').AndReturn( - six.BytesIO(self.template_a)) - self.m.ReplayAll() - - self.assertEqual( - env_url, - utils.normalise_file_path_to_url(env_file)) - self.assertEqual( - 'file:///home/my/dir', - utils.base_url_for_url(env_url)) - - files, env_dict = template_utils.process_environment_and_files( - env_file) - - self.assertEqual( - {'resource_registry': { - 'OS::Thingy': 'file:///home/my/dir/a.yaml'}}, - env_dict) - self.assertEqual(self.template_a.decode('utf-8'), - files['file:///home/my/dir/a.yaml']) - - def test_process_environment_relative_file_up(self): - - self.m.StubOutWithMock(request, 'urlopen') - env_file = '/home/my/dir/env.yaml' - env_url = 'file:///home/my/dir/env.yaml' - env = b''' - resource_registry: - "OS::Thingy": ../bar/a.yaml - ''' - - request.urlopen(env_url).AndReturn( - six.BytesIO(env)) - request.urlopen('file:///home/my/bar/a.yaml').AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/my/bar/a.yaml').AndReturn( - six.BytesIO(self.template_a)) - self.m.ReplayAll() - - env_url = 'file://%s' % env_file - self.assertEqual( - env_url, - utils.normalise_file_path_to_url(env_file)) - self.assertEqual( - 'file:///home/my/dir', - utils.base_url_for_url(env_url)) - - files, env_dict = template_utils.process_environment_and_files( - env_file) - - self.assertEqual( - {'resource_registry': { - 'OS::Thingy': 'file:///home/my/bar/a.yaml'}}, - env_dict) - self.assertEqual(self.template_a.decode('utf-8'), - files['file:///home/my/bar/a.yaml']) - - def test_process_environment_url(self): - env = b''' - resource_registry: - "OS::Thingy": "a.yaml" - ''' - url = 'http://no.where/some/path/to/file.yaml' - tmpl_url = 'http://no.where/some/path/to/a.yaml' - - self.m.StubOutWithMock(request, 'urlopen') - request.urlopen(url).AndReturn(six.BytesIO(env)) - request.urlopen(tmpl_url).AndReturn(six.BytesIO(self.template_a)) - request.urlopen(tmpl_url).AndReturn(six.BytesIO(self.template_a)) - self.m.ReplayAll() - - files, env_dict = template_utils.process_environment_and_files( - url) - - self.assertEqual({'resource_registry': {'OS::Thingy': tmpl_url}}, - env_dict) - self.assertEqual(self.template_a.decode('utf-8'), files[tmpl_url]) - - def test_process_environment_empty_file(self): - - self.m.StubOutWithMock(request, 'urlopen') - env_file = '/home/my/dir/env.yaml' - env = b'' - - request.urlopen('file://%s' % env_file).AndReturn(six.BytesIO(env)) - self.m.ReplayAll() - - files, env_dict = template_utils.process_environment_and_files( - env_file) - - self.assertEqual({}, env_dict) - self.assertEqual({}, files) - - def test_no_process_environment_and_files(self): - files, env = template_utils.process_environment_and_files() - self.assertEqual({}, env) - self.assertEqual({}, files) - - def test_process_multiple_environments_and_files(self): - - self.m.StubOutWithMock(request, 'urlopen') - env_file1 = '/home/my/dir/env1.yaml' - env_file2 = '/home/my/dir/env2.yaml' - - env1 = b''' - parameters: - "param1": "value1" - resource_registry: - "OS::Thingy1": "file:///home/b/a.yaml" - ''' - env2 = b''' - parameters: - "param2": "value2" - resource_registry: - "OS::Thingy2": "file:///home/b/b.yaml" - ''' - - request.urlopen('file://%s' % env_file1).AndReturn( - six.BytesIO(env1)) - request.urlopen('file:///home/b/a.yaml').AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/b/a.yaml').AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file://%s' % env_file2).AndReturn( - six.BytesIO(env2)) - request.urlopen('file:///home/b/b.yaml').AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/b/b.yaml').AndReturn( - six.BytesIO(self.template_a)) - self.m.ReplayAll() - - files, env = template_utils.process_multiple_environments_and_files( - [env_file1, env_file2]) - self.assertEqual( - { - 'resource_registry': { - 'OS::Thingy1': 'file:///home/b/a.yaml', - 'OS::Thingy2': 'file:///home/b/b.yaml'}, - 'parameters': { - 'param1': 'value1', - 'param2': 'value2'} - }, - env) - self.assertEqual(self.template_a.decode('utf-8'), - files['file:///home/b/a.yaml']) - self.assertEqual(self.template_a.decode('utf-8'), - files['file:///home/b/b.yaml']) - - def test_process_multiple_environments_default_resources(self): - - self.m.StubOutWithMock(request, 'urlopen') - env_file1 = '/home/my/dir/env1.yaml' - env_file2 = '/home/my/dir/env2.yaml' - - env1 = b''' - resource_registry: - resources: - resource1: - "OS::Thingy1": "file:///home/b/a.yaml" - resource2: - "OS::Thingy2": "file:///home/b/b.yaml" - ''' - env2 = b''' - resource_registry: - resources: - resource1: - "OS::Thingy3": "file:///home/b/a.yaml" - resource2: - "OS::Thingy4": "file:///home/b/b.yaml" - ''' - - request.urlopen('file://%s' % env_file1).InAnyOrder().AndReturn( - six.BytesIO(env1)) - request.urlopen('file:///home/b/a.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/b/b.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/b/a.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/b/b.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file://%s' % env_file2).InAnyOrder().AndReturn( - six.BytesIO(env2)) - request.urlopen('file:///home/b/a.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/b/b.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/b/a.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/b/b.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.template_a)) - self.m.ReplayAll() - - files, env = template_utils.process_multiple_environments_and_files( - [env_file1, env_file2]) - self.assertEqual( - { - 'resource_registry': { - 'resources': { - 'resource1': { - 'OS::Thingy1': 'file:///home/b/a.yaml', - 'OS::Thingy3': 'file:///home/b/a.yaml' - }, - 'resource2': { - 'OS::Thingy2': 'file:///home/b/b.yaml', - 'OS::Thingy4': 'file:///home/b/b.yaml' - } - } - } - }, - env) - self.assertEqual(self.template_a.decode('utf-8'), - files['file:///home/b/a.yaml']) - self.assertEqual(self.template_a.decode('utf-8'), - files['file:///home/b/b.yaml']) - - def test_no_process_multiple_environments_and_files(self): - files, env = template_utils.process_multiple_environments_and_files() - self.assertEqual({}, env) - self.assertEqual({}, files) - - def test_global_files(self): - url = 'file:///home/b/a.yaml' - env = ''' - resource_registry: - "OS::Thingy": "%s" - ''' % url - self.collect_links(env, self.template_a, url) - - def test_nested_files(self): - url = 'file:///home/b/a.yaml' - env = ''' - resource_registry: - resources: - freddy: - "OS::Thingy": "%s" - ''' % url - self.collect_links(env, self.template_a, url) - - def test_http_url(self): - url = 'http://no.where/container/a.yaml' - env = ''' - resource_registry: - "OS::Thingy": "%s" - ''' % url - self.collect_links(env, self.template_a, url) - - def test_with_base_url(self): - url = 'ftp://no.where/container/a.yaml' - env = ''' - resource_registry: - base_url: "ftp://no.where/container/" - resources: - server_for_me: - "OS::Thingy": a.yaml - ''' - self.collect_links(env, self.template_a, url) - - def test_with_built_in_provider(self): - env = ''' - resource_registry: - resources: - server_for_me: - "OS::Thingy": OS::Compute::Server - ''' - self.collect_links(env, self.template_a, None) - - def test_with_env_file_base_url_file(self): - url = 'file:///tmp/foo/a.yaml' - env = ''' - resource_registry: - resources: - server_for_me: - "OS::Thingy": a.yaml - ''' - env_base_url = 'file:///tmp/foo' - self.collect_links(env, self.template_a, url, env_base_url) - - def test_with_env_file_base_url_http(self): - url = 'http://no.where/path/to/a.yaml' - env = ''' - resource_registry: - resources: - server_for_me: - "OS::Thingy": to/a.yaml - ''' - env_base_url = 'http://no.where/path' - self.collect_links(env, self.template_a, url, env_base_url) - - def test_unsupported_protocol(self): - env = ''' - resource_registry: - "OS::Thingy": "sftp://no.where/dev/null/a.yaml" - ''' - jenv = yaml.safe_load(env) - fields = {'files': {}} - self.assertRaises(exc.CommandError, - template_utils.get_file_contents, - jenv['resource_registry'], - fields) - - -class TestGetTemplateContents(testtools.TestCase): - - def setUp(self): - super(TestGetTemplateContents, self).setUp() - self.m = mox.Mox() - - self.addCleanup(self.m.VerifyAll) - self.addCleanup(self.m.UnsetStubs) - - def test_get_template_contents_file(self): - with tempfile.NamedTemporaryFile() as tmpl_file: - tmpl = b'{"AWSTemplateFormatVersion" : "2010-09-09",' \ - b' "foo": "bar"}' - tmpl_file.write(tmpl) - tmpl_file.flush() - - files, tmpl_parsed = template_utils.get_template_contents( - tmpl_file.name) - self.assertEqual({"AWSTemplateFormatVersion": "2010-09-09", - "foo": "bar"}, tmpl_parsed) - self.assertEqual({}, files) - - def test_get_template_contents_file_empty(self): - with tempfile.NamedTemporaryFile() as tmpl_file: - - ex = self.assertRaises( - exc.CommandError, - template_utils.get_template_contents, - tmpl_file.name) - self.assertEqual( - 'Could not fetch template from file://%s' % tmpl_file.name, - str(ex)) - - def test_get_template_contents_file_none(self): - ex = self.assertRaises( - exc.CommandError, - template_utils.get_template_contents) - self.assertEqual( - ('Need to specify exactly one of --template-file, ' - '--template-url or --template-object'), - str(ex)) - - def test_get_template_contents_parse_error(self): - with tempfile.NamedTemporaryFile() as tmpl_file: - - tmpl = b'{"foo": "bar"' - tmpl_file.write(tmpl) - tmpl_file.flush() - - ex = self.assertRaises( - exc.CommandError, - template_utils.get_template_contents, - tmpl_file.name) - self.assertThat( - str(ex), - matchers.MatchesRegex( - 'Error parsing template file://%s ' % tmpl_file.name)) - - def test_get_template_contents_url(self): - tmpl = b'{"AWSTemplateFormatVersion" : "2010-09-09", "foo": "bar"}' - url = 'http://no.where/path/to/a.yaml' - self.m.StubOutWithMock(request, 'urlopen') - request.urlopen(url).AndReturn(six.BytesIO(tmpl)) - self.m.ReplayAll() - - files, tmpl_parsed = template_utils.get_template_contents( - template_url=url) - self.assertEqual({"AWSTemplateFormatVersion": "2010-09-09", - "foo": "bar"}, tmpl_parsed) - self.assertEqual({}, files) - - def test_get_template_contents_object(self): - tmpl = '{"AWSTemplateFormatVersion" : "2010-09-09", "foo": "bar"}' - url = 'http://no.where/path/to/a.yaml' - self.m.ReplayAll() - - self.object_requested = False - - def object_request(method, object_url): - self.object_requested = True - self.assertEqual('GET', method) - self.assertEqual('http://no.where/path/to/a.yaml', object_url) - return tmpl - - files, tmpl_parsed = template_utils.get_template_contents( - template_object=url, - object_request=object_request) - - self.assertEqual({"AWSTemplateFormatVersion": "2010-09-09", - "foo": "bar"}, tmpl_parsed) - self.assertEqual({}, files) - self.assertTrue(self.object_requested) - - def check_non_utf8_content(self, filename, content): - base_url = 'file:///tmp' - url = '%s/%s' % (base_url, filename) - template = {'resources': - {'one_init': - {'type': 'OS::Heat::CloudConfig', - 'properties': - {'cloud_config': - {'write_files': - [{'path': '/tmp/%s' % filename, - 'content': {'get_file': url}, - 'encoding': 'b64'}]}}}}} - self.m.StubOutWithMock(request, 'urlopen') - raw_content = base64.decodestring(content) - response = six.BytesIO(raw_content) - request.urlopen(url).AndReturn(response) - self.m.ReplayAll() - files = {} - template_utils.resolve_template_get_files( - template, files, base_url) - self.assertEqual({url: content}, files) - - def test_get_zip_content(self): - filename = 'heat.zip' - content = b'''\ -UEsDBAoAAAAAAEZZWkRbOAuBBQAAAAUAAAAIABwAaGVhdC50eHRVVAkAAxRbDVNYh\ -t9SdXgLAAEE\n6AMAAATpAwAAaGVhdApQSwECHgMKAAAAAABGWVpEWzgLgQUAAAAF\ -AAAACAAYAAAAAAABAAAApIEA\nAAAAaGVhdC50eHRVVAUAAxRbDVN1eAsAAQToAwA\ -ABOkDAABQSwUGAAAAAAEAAQBOAAAARwAAAAAA\n''' - # zip has '\0' in stream - self.assertIn(b'\0', base64.decodestring(content)) - decoded_content = base64.decodestring(content) - if six.PY3: - self.assertRaises(UnicodeDecodeError, decoded_content.decode) - else: - self.assertRaises( - UnicodeDecodeError, - json.dumps, - {'content': decoded_content}) - self.check_non_utf8_content( - filename=filename, content=content) - - def test_get_utf16_content(self): - filename = 'heat.utf16' - content = b'//4tTkhTCgA=\n' - # utf6 has '\0' in stream - self.assertIn(b'\0', base64.decodestring(content)) - decoded_content = base64.decodestring(content) - if six.PY3: - self.assertRaises(UnicodeDecodeError, decoded_content.decode) - else: - self.assertRaises( - UnicodeDecodeError, - json.dumps, - {'content': decoded_content}) - self.check_non_utf8_content(filename=filename, content=content) - - def test_get_gb18030_content(self): - filename = 'heat.gb18030' - content = b'1tDO5wo=\n' - # gb18030 has no '\0' in stream - self.assertNotIn('\0', base64.decodestring(content)) - decoded_content = base64.decodestring(content) - if six.PY3: - self.assertRaises(UnicodeDecodeError, decoded_content.decode) - else: - self.assertRaises( - UnicodeDecodeError, - json.dumps, - {'content': decoded_content}) - self.check_non_utf8_content(filename=filename, content=content) - - -class TestTemplateGetFileFunctions(testtools.TestCase): - - hot_template = b'''heat_template_version: 2013-05-23 -resources: - resource1: - type: OS::type1 - properties: - foo: {get_file: foo.yaml} - bar: - get_file: - 'http://localhost/bar.yaml' - resource2: - type: OS::type1 - properties: - baz: - - {get_file: baz/baz1.yaml} - - {get_file: baz/baz2.yaml} - - {get_file: baz/baz3.yaml} - ignored_list: {get_file: [ignore, me]} - ignored_dict: {get_file: {ignore: me}} - ignored_none: {get_file: } - ''' - - def setUp(self): - super(TestTemplateGetFileFunctions, self).setUp() - self.m = mox.Mox() - - self.addCleanup(self.m.VerifyAll) - self.addCleanup(self.m.UnsetStubs) - - def test_hot_template(self): - self.m.StubOutWithMock(request, 'urlopen') - - tmpl_file = '/home/my/dir/template.yaml' - url = 'file:///home/my/dir/template.yaml' - request.urlopen(url).AndReturn( - six.BytesIO(self.hot_template)) - request.urlopen( - 'http://localhost/bar.yaml').InAnyOrder().AndReturn( - six.BytesIO(b'bar contents')) - request.urlopen( - 'file:///home/my/dir/foo.yaml').InAnyOrder().AndReturn( - six.BytesIO(b'foo contents')) - request.urlopen( - 'file:///home/my/dir/baz/baz1.yaml').InAnyOrder().AndReturn( - six.BytesIO(b'baz1 contents')) - request.urlopen( - 'file:///home/my/dir/baz/baz2.yaml').InAnyOrder().AndReturn( - six.BytesIO(b'baz2 contents')) - request.urlopen( - 'file:///home/my/dir/baz/baz3.yaml').InAnyOrder().AndReturn( - six.BytesIO(b'baz3 contents')) - - self.m.ReplayAll() - - files, tmpl_parsed = template_utils.get_template_contents( - template_file=tmpl_file) - - self.assertEqual({ - 'http://localhost/bar.yaml': b'bar contents', - 'file:///home/my/dir/foo.yaml': b'foo contents', - 'file:///home/my/dir/baz/baz1.yaml': b'baz1 contents', - 'file:///home/my/dir/baz/baz2.yaml': b'baz2 contents', - 'file:///home/my/dir/baz/baz3.yaml': b'baz3 contents', - }, files) - self.assertEqual({ - 'heat_template_version': '2013-05-23', - 'resources': { - 'resource1': { - 'type': 'OS::type1', - 'properties': { - 'bar': {'get_file': 'http://localhost/bar.yaml'}, - 'foo': {'get_file': 'file:///home/my/dir/foo.yaml'}, - }, - }, - 'resource2': { - 'type': 'OS::type1', - 'properties': { - 'baz': [ - {'get_file': 'file:///home/my/dir/baz/baz1.yaml'}, - {'get_file': 'file:///home/my/dir/baz/baz2.yaml'}, - {'get_file': 'file:///home/my/dir/baz/baz3.yaml'}, - ], - 'ignored_list': {'get_file': ['ignore', 'me']}, - 'ignored_dict': {'get_file': {'ignore': 'me'}}, - 'ignored_none': {'get_file': None}, - }, - } - } - }, tmpl_parsed) - - def test_hot_template_outputs(self): - self.m.StubOutWithMock(request, 'urlopen') - tmpl_file = '/home/my/dir/template.yaml' - url = 'file://%s' % tmpl_file - foo_url = 'file:///home/my/dir/foo.yaml' - contents = b''' -heat_template_version: 2013-05-23\n\ -outputs:\n\ - contents:\n\ - value:\n\ - get_file: foo.yaml\n''' - request.urlopen(url).AndReturn(six.BytesIO(contents)) - request.urlopen(foo_url).AndReturn(six.BytesIO(b'foo contents')) - self.m.ReplayAll() - files = template_utils.get_template_contents( - template_file=tmpl_file)[0] - self.assertEqual({foo_url: b'foo contents'}, files) - - def test_hot_template_same_file(self): - self.m.StubOutWithMock(request, 'urlopen') - tmpl_file = '/home/my/dir/template.yaml' - url = 'file://%s' % tmpl_file - foo_url = 'file:///home/my/dir/foo.yaml' - contents = b''' -heat_template_version: 2013-05-23\n -outputs:\n\ - contents:\n\ - value:\n\ - get_file: foo.yaml\n\ - template:\n\ - value:\n\ - get_file: foo.yaml\n''' - request.urlopen(url).AndReturn(six.BytesIO(contents)) - # asserts that is fetched only once even though it is - # referenced in the template twice - request.urlopen(foo_url).AndReturn(six.BytesIO(b'foo contents')) - self.m.ReplayAll() - files = template_utils.get_template_contents( - template_file=tmpl_file)[0] - self.assertEqual({foo_url: b'foo contents'}, files) - - -class TestTemplateTypeFunctions(testtools.TestCase): - - hot_template = b'''heat_template_version: 2013-05-23 -parameters: - param1: - type: string -resources: - resource1: - type: foo.yaml - properties: - foo: bar - resource2: - type: OS::Heat::ResourceGroup - properties: - resource_def: - type: spam/egg.yaml - ''' - - foo_template = b'''heat_template_version: "2013-05-23" -parameters: - foo: - type: string - ''' - - egg_template = b'''heat_template_version: "2013-05-23" -parameters: - egg: - type: string - ''' - - def setUp(self): - super(TestTemplateTypeFunctions, self).setUp() - self.m = mox.Mox() - - self.addCleanup(self.m.VerifyAll) - self.addCleanup(self.m.UnsetStubs) - - def test_hot_template(self): - self.m.StubOutWithMock(request, 'urlopen') - tmpl_file = '/home/my/dir/template.yaml' - url = 'file:///home/my/dir/template.yaml' - request.urlopen( - 'file:///home/my/dir/foo.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.foo_template)) - request.urlopen( - 'file:///home/my/dir/foo.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.foo_template)) - request.urlopen(url).InAnyOrder().AndReturn( - six.BytesIO(self.hot_template)) - request.urlopen( - 'file:///home/my/dir/spam/egg.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.egg_template)) - request.urlopen( - 'file:///home/my/dir/spam/egg.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.egg_template)) - self.m.ReplayAll() - - files, tmpl_parsed = template_utils.get_template_contents( - template_file=tmpl_file) - - self.assertEqual(yaml.load(self.foo_template.decode('utf-8')), - json.loads(files.get('file:///home/my/dir/foo.yaml'))) - self.assertEqual( - yaml.load(self.egg_template.decode('utf-8')), - json.loads(files.get('file:///home/my/dir/spam/egg.yaml'))) - - self.assertEqual({ - u'heat_template_version': u'2013-05-23', - u'parameters': { - u'param1': { - u'type': u'string' - } - }, - u'resources': { - u'resource1': { - u'type': u'file:///home/my/dir/foo.yaml', - u'properties': {u'foo': u'bar'} - }, - u'resource2': { - u'type': u'OS::Heat::ResourceGroup', - u'properties': { - u'resource_def': { - u'type': u'file:///home/my/dir/spam/egg.yaml' - } - } - } - } - }, tmpl_parsed) - - -class TestTemplateInFileFunctions(testtools.TestCase): - - hot_template = b'''heat_template_version: 2013-05-23 -resources: - resource1: - type: OS::Heat::Stack - properties: - template: {get_file: foo.yaml} - ''' - - foo_template = b'''heat_template_version: "2013-05-23" -resources: - foo: - type: OS::Type1 - properties: - config: {get_file: bar.yaml} - ''' - - bar_template = b'''heat_template_version: "2013-05-23" -parameters: - bar: - type: string - ''' - - def setUp(self): - super(TestTemplateInFileFunctions, self).setUp() - self.m = mox.Mox() - - self.addCleanup(self.m.VerifyAll) - self.addCleanup(self.m.UnsetStubs) - - def test_hot_template(self): - self.m.StubOutWithMock(request, 'urlopen') - tmpl_file = '/home/my/dir/template.yaml' - url = 'file:///home/my/dir/template.yaml' - foo_url = 'file:///home/my/dir/foo.yaml' - bar_url = 'file:///home/my/dir/bar.yaml' - request.urlopen(url).InAnyOrder().AndReturn( - six.BytesIO(self.hot_template)) - request.urlopen(foo_url).InAnyOrder().AndReturn( - six.BytesIO(self.foo_template)) - request.urlopen(foo_url).InAnyOrder().AndReturn( - six.BytesIO(self.foo_template)) - request.urlopen(bar_url).InAnyOrder().AndReturn( - six.BytesIO(self.bar_template)) - request.urlopen(bar_url).InAnyOrder().AndReturn( - six.BytesIO(self.bar_template)) - self.m.ReplayAll() - - files, tmpl_parsed = template_utils.get_template_contents( - template_file=tmpl_file) - - self.assertEqual(yaml.load(self.bar_template.decode('utf-8')), - json.loads(files.get('file:///home/my/dir/bar.yaml'))) - - self.assertEqual({ - u'heat_template_version': u'2013-05-23', - u'resources': { - u'foo': { - u'type': u'OS::Type1', - u'properties': { - u'config': { - u'get_file': u'file:///home/my/dir/bar.yaml' - } - } - } - } - }, json.loads(files.get('file:///home/my/dir/foo.yaml'))) - - self.assertEqual({ - u'heat_template_version': u'2013-05-23', - u'resources': { - u'resource1': { - u'type': u'OS::Heat::Stack', - u'properties': { - u'template': { - u'get_file': u'file:///home/my/dir/foo.yaml' - } - } - } - } - }, tmpl_parsed) - - -class TestNestedIncludes(testtools.TestCase): - - hot_template = b'''heat_template_version: 2013-05-23 -parameters: - param1: - type: string -resources: - resource1: - type: foo.yaml - properties: - foo: bar - resource2: - type: OS::Heat::ResourceGroup - properties: - resource_def: - type: spam/egg.yaml - with: {get_file: spam/ham.yaml} - ''' - - egg_template = b'''heat_template_version: 2013-05-23 -parameters: - param1: - type: string -resources: - resource1: - type: one.yaml - properties: - foo: bar - resource2: - type: OS::Heat::ResourceGroup - properties: - resource_def: - type: two.yaml - with: {get_file: three.yaml} - ''' - - foo_template = b'''heat_template_version: "2013-05-23" -parameters: - foo: - type: string - ''' - - def setUp(self): - super(TestNestedIncludes, self).setUp() - self.m = mox.Mox() - - self.addCleanup(self.m.VerifyAll) - self.addCleanup(self.m.UnsetStubs) - - def test_env_nested_includes(self): - self.m.StubOutWithMock(request, 'urlopen') - env_file = '/home/my/dir/env.yaml' - env_url = 'file:///home/my/dir/env.yaml' - env = b''' - resource_registry: - "OS::Thingy": template.yaml - ''' - template_url = u'file:///home/my/dir/template.yaml' - foo_url = u'file:///home/my/dir/foo.yaml' - egg_url = u'file:///home/my/dir/spam/egg.yaml' - ham_url = u'file:///home/my/dir/spam/ham.yaml' - one_url = u'file:///home/my/dir/spam/one.yaml' - two_url = u'file:///home/my/dir/spam/two.yaml' - three_url = u'file:///home/my/dir/spam/three.yaml' - - request.urlopen(env_url).AndReturn( - six.BytesIO(env)) - request.urlopen(template_url).AndReturn( - six.BytesIO(self.hot_template)) - request.urlopen(template_url).AndReturn( - six.BytesIO(self.hot_template)) - - request.urlopen(foo_url).InAnyOrder().AndReturn( - six.BytesIO(self.foo_template)) - request.urlopen(egg_url).InAnyOrder().AndReturn( - six.BytesIO(self.egg_template)) - request.urlopen(ham_url).InAnyOrder().AndReturn( - six.BytesIO(b'ham contents')) - request.urlopen(one_url).InAnyOrder().AndReturn( - six.BytesIO(self.foo_template)) - request.urlopen(two_url).InAnyOrder().AndReturn( - six.BytesIO(self.foo_template)) - request.urlopen(three_url).InAnyOrder().AndReturn( - six.BytesIO(b'three contents')) - request.urlopen(foo_url).InAnyOrder().AndReturn( - six.BytesIO(self.foo_template)) - request.urlopen(egg_url).InAnyOrder().AndReturn( - six.BytesIO(self.egg_template)) - request.urlopen(one_url).InAnyOrder().AndReturn( - six.BytesIO(self.foo_template)) - request.urlopen(two_url).InAnyOrder().AndReturn( - six.BytesIO(self.foo_template)) - - self.m.ReplayAll() - - files, env_dict = template_utils.process_environment_and_files( - env_file) - - self.assertEqual( - {'resource_registry': { - 'OS::Thingy': template_url}}, - env_dict) - - self.assertEqual({ - u'heat_template_version': u'2013-05-23', - u'parameters': {u'param1': {u'type': u'string'}}, - u'resources': { - u'resource1': { - u'properties': {u'foo': u'bar'}, - u'type': foo_url - }, - u'resource2': { - u'type': u'OS::Heat::ResourceGroup', - u'properties': { - u'resource_def': { - u'type': egg_url}, - u'with': {u'get_file': ham_url} - } - } - } - }, json.loads(files.get(template_url))) - - self.assertEqual(yaml.load(self.foo_template.decode('utf-8')), - json.loads(files.get(foo_url))) - self.assertEqual({ - u'heat_template_version': u'2013-05-23', - u'parameters': {u'param1': {u'type': u'string'}}, - u'resources': { - u'resource1': { - u'properties': {u'foo': u'bar'}, - u'type': one_url}, - u'resource2': { - u'type': u'OS::Heat::ResourceGroup', - u'properties': { - u'resource_def': {u'type': two_url}, - u'with': {u'get_file': three_url} - } - } - } - }, json.loads(files.get(egg_url))) - self.assertEqual(b'ham contents', - files.get(ham_url)) - self.assertEqual(yaml.load(self.foo_template.decode('utf-8')), - json.loads(files.get(one_url))) - self.assertEqual(yaml.load(self.foo_template.decode('utf-8')), - json.loads(files.get(two_url))) - self.assertEqual(b'three contents', - files.get(three_url)) diff --git a/stacktaskclient/tests/unit/test_template_versions.py b/stacktaskclient/tests/unit/test_template_versions.py deleted file mode 100644 index fe3cd09..0000000 --- a/stacktaskclient/tests/unit/test_template_versions.py +++ /dev/null @@ -1,57 +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 testtools - -from heatclient.v1 import template_versions - - -class TemplateVersionManagerTest(testtools.TestCase): - - def setUp(self): - super(TemplateVersionManagerTest, self).setUp() - - def test_list_versions(self): - expect = ('GET', '/template_versions') - - class FakeResponse(object): - def json(self): - return {'template_versions': [{'version': '2013-05-23', - 'type': 'hot'}]} - - class FakeClient(object): - def get(self, *args, **kwargs): - assert ('GET', args[0]) == expect - return FakeResponse() - - manager = template_versions.TemplateVersionManager(FakeClient()) - versions = manager.list() - self.assertEqual('2013-05-23', getattr(versions[0], 'version')) - self.assertEqual('hot', getattr(versions[0], 'type')) - - def test_get(self): - expect = ('GET', '/template_versions/heat_template_version.2015-04-30' - '/functions') - - class FakeResponse(object): - def json(self): - return {'template_functions': [{'function': 'get_attr'}]} - - class FakeClient(object): - def get(self, *args, **kwargs): - assert ('GET', args[0]) == expect - return FakeResponse() - - manager = template_versions.TemplateVersionManager(FakeClient()) - functions = manager.get('heat_template_version.2015-04-30') - self.assertEqual('get_attr', getattr(functions[0], 'function')) diff --git a/stacktaskclient/tests/unit/test_utils.py b/stacktaskclient/tests/unit/test_utils.py deleted file mode 100644 index 10946eb..0000000 --- a/stacktaskclient/tests/unit/test_utils.py +++ /dev/null @@ -1,278 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -from heatclient.common import utils -from heatclient import exc -from heatclient.v1 import resources as hc_res -import mock -import os -import testtools - - -class ShellTest(testtools.TestCase): - - def test_format_parameter_none(self): - self.assertEqual({}, utils.format_parameters(None)) - - def test_format_parameters(self): - p = utils.format_parameters([ - 'InstanceType=m1.large;DBUsername=wp;' - 'DBPassword=verybadpassword;KeyName=heat_key;' - 'LinuxDistribution=F17']) - self.assertEqual({'InstanceType': 'm1.large', - 'DBUsername': 'wp', - 'DBPassword': 'verybadpassword', - 'KeyName': 'heat_key', - 'LinuxDistribution': 'F17' - }, p) - - def test_format_parameters_split(self): - p = utils.format_parameters([ - 'KeyName=heat_key;' - 'DnsSecKey=hsgx1m31PbamNF4WEcHlwjIlCGgifOdoB58/wwC7a4oAONQ/fDV5ct' - 'qrYBoLlKHhTfkyQEw9iVScKYZbbMtMNg==;' - 'UpstreamDNS=8.8.8.8']) - self.assertEqual({'KeyName': 'heat_key', - 'DnsSecKey': 'hsgx1m31PbamNF4WEcHlwjIlCGgifOdoB58/ww' - 'C7a4oAONQ/fDV5ctqrYBoLlKHhTfkyQEw9iVScKYZbbMtMNg==', - 'UpstreamDNS': '8.8.8.8'}, p) - - def test_format_parameters_multiple(self): - p = utils.format_parameters([ - 'KeyName=heat_key', - 'DnsSecKey=hsgx1m31PbamNF4WEcHlwjIlCGgifOdoB58/wwC7a4oAONQ/fDV5ct' - 'qrYBoLlKHhTfkyQEw9iVScKYZbbMtMNg==', - 'UpstreamDNS=8.8.8.8']) - self.assertEqual({'KeyName': 'heat_key', - 'DnsSecKey': 'hsgx1m31PbamNF4WEcHlwjIlCGgifOdoB58/ww' - 'C7a4oAONQ/fDV5ctqrYBoLlKHhTfkyQEw9iVScKYZbbMtMNg==', - 'UpstreamDNS': '8.8.8.8'}, p) - - def test_format_parameters_multiple_semicolon_values(self): - p = utils.format_parameters([ - 'KeyName=heat_key', - 'DnsSecKey=hsgx1m31;PbaNF4WEcHlwj;IlCGgfOdoB;58/ww7a4oAO;NQ/fD==', - 'UpstreamDNS=8.8.8.8']) - self.assertEqual({'KeyName': 'heat_key', - 'DnsSecKey': 'hsgx1m31;PbaNF4WEcHlwj;IlCGgfOdoB;58/' - 'ww7a4oAO;NQ/fD==', - 'UpstreamDNS': '8.8.8.8'}, p) - - def test_format_parameters_parse_semicolon_false(self): - p = utils.format_parameters( - ['KeyName=heat_key;UpstreamDNS=8.8.8.8;a=b'], - parse_semicolon=False) - self.assertEqual({'KeyName': 'heat_key;UpstreamDNS=8.8.8.8;a=b'}, p) - - def test_format_parameters_multiple_values_per_pamaters(self): - p = utils.format_parameters([ - 'status=COMPLETE', - 'status=FAILED']) - self.assertIn('status', p) - self.assertIn('COMPLETE', p['status']) - self.assertIn('FAILED', p['status']) - - def test_format_parameter_bad_parameter(self): - params = ['KeyName=heat_key;UpstreamDNS8.8.8.8'] - ex = self.assertRaises(exc.CommandError, - utils.format_parameters, params) - self.assertEqual('Malformed parameter(UpstreamDNS8.8.8.8). ' - 'Use the key=value format.', str(ex)) - - def test_format_multiple_bad_parameter(self): - params = ['KeyName=heat_key', 'UpstreamDNS8.8.8.8'] - ex = self.assertRaises(exc.CommandError, - utils.format_parameters, params) - self.assertEqual('Malformed parameter(UpstreamDNS8.8.8.8). ' - 'Use the key=value format.', str(ex)) - - def test_link_formatter(self): - self.assertEqual('', utils.link_formatter(None)) - self.assertEqual('', utils.link_formatter([])) - self.assertEqual( - 'http://foo.example.com\nhttp://bar.example.com', - utils.link_formatter([ - {'href': 'http://foo.example.com'}, - {'href': 'http://bar.example.com'}])) - self.assertEqual( - 'http://foo.example.com (a)\nhttp://bar.example.com (b)', - utils.link_formatter([ - {'href': 'http://foo.example.com', 'rel': 'a'}, - {'href': 'http://bar.example.com', 'rel': 'b'}])) - self.assertEqual( - '\n', - utils.link_formatter([ - {'hrf': 'http://foo.example.com'}, - {}])) - - def test_resource_nested_identifier(self): - rsrc_info = {'resource_name': 'aresource', - 'links': [{'href': u'http://foo/name/id/resources/0', - 'rel': u'self'}, - {'href': u'http://foo/name/id', - 'rel': u'stack'}, - {'href': u'http://foo/n_name/n_id', - 'rel': u'nested'}]} - rsrc = hc_res.Resource(manager=None, info=rsrc_info) - self.assertEqual('n_name/n_id', utils.resource_nested_identifier(rsrc)) - - def test_resource_nested_identifier_none(self): - rsrc_info = {'resource_name': 'aresource', - 'links': [{'href': u'http://foo/name/id/resources/0', - 'rel': u'self'}, - {'href': u'http://foo/name/id', - 'rel': u'stack'}]} - rsrc = hc_res.Resource(manager=None, info=rsrc_info) - self.assertIsNone(utils.resource_nested_identifier(rsrc)) - - def test_json_formatter(self): - self.assertEqual('null', utils.json_formatter(None)) - self.assertEqual('{}', utils.json_formatter({})) - self.assertEqual('{\n "foo": "bar"\n}', - utils.json_formatter({"foo": "bar"})) - self.assertEqual(u'{\n "Uni": "test\u2665"\n}', - utils.json_formatter({"Uni": u"test\u2665"})) - - def test_text_wrap_formatter(self): - self.assertEqual('', utils.text_wrap_formatter(None)) - self.assertEqual('', utils.text_wrap_formatter('')) - self.assertEqual('one two three', - utils.text_wrap_formatter('one two three')) - self.assertEqual( - 'one two three four five six seven eight nine ten eleven\ntwelve', - utils.text_wrap_formatter( - ('one two three four five six seven ' - 'eight nine ten eleven twelve'))) - - def test_newline_list_formatter(self): - self.assertEqual('', utils.newline_list_formatter(None)) - self.assertEqual('', utils.newline_list_formatter([])) - self.assertEqual('one\ntwo', - utils.newline_list_formatter(['one', 'two'])) - - -class ShellTestParameterFiles(testtools.TestCase): - - def test_format_parameter_file_none(self): - self.assertEqual({}, utils.format_parameter_file(None)) - - def test_format_parameter_file(self): - tmpl_file = '/opt/stack/template.yaml' - contents = 'DBUsername=wp\nDBPassword=verybadpassword' - utils.read_url_content = mock.MagicMock() - utils.read_url_content.return_value = 'DBUsername=wp\n' \ - 'DBPassword=verybadpassword' - - p = utils.format_parameter_file([ - 'env_file1=test_file1'], tmpl_file) - self.assertEqual({'env_file1': contents - }, p) - - def test_format_parameter_file_no_template(self): - tmpl_file = None - contents = 'DBUsername=wp\nDBPassword=verybadpassword' - utils.read_url_content = mock.MagicMock() - utils.read_url_content.return_value = 'DBUsername=wp\n' \ - 'DBPassword=verybadpassword' - p = utils.format_parameter_file([ - 'env_file1=test_file1'], tmpl_file) - self.assertEqual({'env_file1': contents - }, p) - - def test_format_all_parameters(self): - tmpl_file = '/opt/stack/template.yaml' - contents = 'DBUsername=wp\nDBPassword=verybadpassword' - params = ['KeyName=heat_key;UpstreamDNS=8.8.8.8'] - utils.read_url_content = mock.MagicMock() - utils.read_url_content.return_value = 'DBUsername=wp\n' \ - 'DBPassword=verybadpassword' - p = utils.format_all_parameters(params, [ - 'env_file1=test_file1'], template_file=tmpl_file) - self.assertEqual({'KeyName': 'heat_key', - 'UpstreamDNS': '8.8.8.8', - 'env_file1': contents}, p) - - -class TestURLFunctions(testtools.TestCase): - - def setUp(self): - super(TestURLFunctions, self).setUp() - self.m = mock.MagicMock() - - self.addCleanup(self.m.UnsetStubs) - - def test_normalise_file_path_to_url_relative(self): - self.assertEqual( - 'file://%s/foo' % os.getcwd(), - utils.normalise_file_path_to_url( - 'foo')) - - def test_normalise_file_path_to_url_absolute(self): - self.assertEqual( - 'file:///tmp/foo', - utils.normalise_file_path_to_url( - '/tmp/foo')) - - def test_normalise_file_path_to_url_file(self): - self.assertEqual( - 'file:///tmp/foo', - utils.normalise_file_path_to_url( - 'file:///tmp/foo')) - - def test_normalise_file_path_to_url_http(self): - self.assertEqual( - 'http://localhost/foo', - utils.normalise_file_path_to_url( - 'http://localhost/foo')) - - def test_get_template_url(self): - tmpl_file = '/opt/stack/template.yaml' - tmpl_url = 'file:///opt/stack/template.yaml' - self.assertEqual(utils.get_template_url(tmpl_file, None), - tmpl_url) - self.assertEqual(utils.get_template_url(None, tmpl_url), - tmpl_url) - self.assertEqual(utils.get_template_url(None, None), - None) - - def test_base_url_for_url(self): - self.assertEqual( - 'file:///foo/bar', - utils.base_url_for_url( - 'file:///foo/bar/baz')) - self.assertEqual( - 'file:///foo/bar', - utils.base_url_for_url( - 'file:///foo/bar/baz.txt')) - self.assertEqual( - 'file:///foo/bar', - utils.base_url_for_url( - 'file:///foo/bar/')) - self.assertEqual( - 'file:///', - utils.base_url_for_url( - 'file:///')) - self.assertEqual( - 'file:///', - utils.base_url_for_url( - 'file:///foo')) - - self.assertEqual( - 'http://foo/bar', - utils.base_url_for_url( - 'http://foo/bar/')) - self.assertEqual( - 'http://foo/bar', - utils.base_url_for_url( - 'http://foo/bar/baz.template')) diff --git a/stacktaskclient/tests/unit/v1/__init__.py b/stacktaskclient/tests/unit/v1/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/stacktaskclient/tests/unit/v1/test_hooks.py b/stacktaskclient/tests/unit/v1/test_hooks.py deleted file mode 100644 index 2b0724c..0000000 --- a/stacktaskclient/tests/unit/v1/test_hooks.py +++ /dev/null @@ -1,318 +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 mock -import testtools - -import heatclient.v1.shell as shell - - -class TestHooks(testtools.TestCase): - def setUp(self): - super(TestHooks, self).setUp() - self.client = mock.Mock() - nested_stack = mock.Mock() - self.client.resources.get = mock.Mock(name='thingy', - return_value=nested_stack) - type(nested_stack).physical_resource_id = mock.PropertyMock( - return_value='nested_id') - self.args = mock.Mock() - stack_name_p = mock.PropertyMock(return_value="mystack") - type(self.args).name = stack_name_p - type(self.args).id = stack_name_p - shell.template_utils.get_template_contents = mock.Mock( - return_value=({}, "")) - shell.template_utils.process_multiple_environments_and_files = \ - mock.Mock(return_value=({}, {})) - shell.utils.format_all_parameters = mock.Mock(return_value=[]) - shell.do_stack_list = mock.Mock() - shell.logger = mock.Mock() - type(self.args).clear_parameter = mock.PropertyMock(return_value=[]) - type(self.args).rollback = mock.PropertyMock(return_value=None) - type(self.args).pre_create = mock.PropertyMock(return_value=False) - type(self.args).pre_update = mock.PropertyMock(return_value=False) - type(self.args).poll = mock.PropertyMock(return_value=None) - - def test_create_hooks_in_args(self): - type(self.args).pre_create = mock.PropertyMock( - return_value=['bp', 'another_bp']) - - shell.do_stack_create(self.client, self.args) - self.assertEqual(1, self.client.stacks.create.call_count) - expected_hooks = { - 'bp': {'hooks': 'pre-create'}, - 'another_bp': {'hooks': 'pre-create'} - } - actual_hooks = self.client.stacks.create.call_args[1][ - 'environment']['resource_registry']['resources'] - self.assertEqual(expected_hooks, actual_hooks) - - def test_create_nested_hooks_in_args(self): - type(self.args).pre_create = mock.PropertyMock( - return_value=['nested/bp', 'super/nested/bp']) - - shell.do_stack_create(self.client, self.args) - self.assertEqual(1, self.client.stacks.create.call_count) - expected_hooks = { - 'nested': { - 'bp': {'hooks': 'pre-create'}, - }, - 'super': { - 'nested': { - 'bp': {'hooks': 'pre-create'}, - } - } - } - actual_hooks = self.client.stacks.create.call_args[1][ - 'environment']['resource_registry']['resources'] - self.assertEqual(expected_hooks, actual_hooks) - - def test_create_hooks_in_env_and_args(self): - type(self.args).pre_create = mock.PropertyMock(return_value=[ - 'nested_a/bp', - 'bp_a', - 'another_bp_a', - 'super_a/nested/bp', - ]) - env = { - 'resource_registry': { - 'resources': { - 'bp_e': {'hooks': 'pre-create'}, - 'another_bp_e': {'hooks': 'pre-create'}, - 'nested_e': { - 'bp': {'hooks': 'pre-create'} - }, - 'super_e': { - 'nested': { - 'bp': {'hooks': 'pre-create'} - } - } - } - } - } - shell.template_utils.process_multiple_environments_and_files = \ - mock.Mock(return_value=({}, env)) - - shell.do_stack_create(self.client, self.args) - self.assertEqual(1, self.client.stacks.create.call_count) - actual_hooks = self.client.stacks.create.call_args[1][ - 'environment']['resource_registry']['resources'] - expected_hooks = { - 'bp_e': {'hooks': 'pre-create'}, - 'another_bp_e': {'hooks': 'pre-create'}, - 'nested_e': { - 'bp': {'hooks': 'pre-create'} - }, - 'super_e': { - 'nested': { - 'bp': {'hooks': 'pre-create'} - } - }, - 'bp_a': {'hooks': 'pre-create'}, - 'another_bp_a': {'hooks': 'pre-create'}, - 'nested_a': { - 'bp': {'hooks': 'pre-create'} - }, - 'super_a': { - 'nested': { - 'bp': {'hooks': 'pre-create'} - } - }, - } - self.assertEqual(expected_hooks, actual_hooks) - - def test_update_hooks_in_args(self): - type(self.args).pre_update = mock.PropertyMock( - return_value=['bp', 'another_bp']) - - shell.do_stack_update(self.client, self.args) - self.assertEqual(1, self.client.stacks.update.call_count) - expected_hooks = { - 'bp': {'hooks': 'pre-update'}, - 'another_bp': {'hooks': 'pre-update'}, - } - actual_hooks = self.client.stacks.update.call_args[1][ - 'environment']['resource_registry']['resources'] - self.assertEqual(expected_hooks, actual_hooks) - - def test_update_nested_hooks_in_args(self): - type(self.args).pre_update = mock.PropertyMock( - return_value=['nested/bp', 'super/nested/bp']) - - shell.do_stack_update(self.client, self.args) - self.assertEqual(1, self.client.stacks.update.call_count) - expected_hooks = { - 'nested': { - 'bp': {'hooks': 'pre-update'} - }, - 'super': { - 'nested': { - 'bp': {'hooks': 'pre-update'} - } - } - } - actual_hooks = self.client.stacks.update.call_args[1][ - 'environment']['resource_registry']['resources'] - self.assertEqual(expected_hooks, actual_hooks) - - def test_update_hooks_in_env_and_args(self): - type(self.args).pre_update = mock.PropertyMock(return_value=[ - 'nested_a/bp', - 'bp_a', - 'another_bp_a', - 'super_a/nested/bp', - ]) - env = { - 'resource_registry': { - 'resources': { - 'bp_e': {'hooks': 'pre-update'}, - 'another_bp_e': {'hooks': 'pre-update'}, - 'nested_e': { - 'bp': {'hooks': 'pre-update'} - }, - 'super_e': { - 'nested': { - 'bp': {'hooks': 'pre-update'} - } - } - } - } - } - shell.template_utils.process_multiple_environments_and_files = \ - mock.Mock(return_value=({}, env)) - - shell.do_stack_update(self.client, self.args) - self.assertEqual(1, self.client.stacks.update.call_count) - actual_hooks = self.client.stacks.update.call_args[1][ - 'environment']['resource_registry']['resources'] - expected_hooks = { - 'bp_e': {'hooks': 'pre-update'}, - 'another_bp_e': {'hooks': 'pre-update'}, - 'nested_e': { - 'bp': {'hooks': 'pre-update'} - }, - 'super_e': { - 'nested': { - 'bp': {'hooks': 'pre-update'} - } - }, - 'bp_a': {'hooks': 'pre-update'}, - 'another_bp_a': {'hooks': 'pre-update'}, - 'nested_a': { - 'bp': {'hooks': 'pre-update'} - }, - 'super_a': { - 'nested': { - 'bp': {'hooks': 'pre-update'} - } - }, - } - self.assertEqual(expected_hooks, actual_hooks) - - def test_clear_all_hooks(self): - shell._get_hook_type_via_status =\ - mock.Mock(return_value='pre-create') - type(self.args).hook = mock.PropertyMock( - return_value=['bp']) - type(self.args).pre_create = mock.PropertyMock(return_value=True) - bp = mock.Mock() - type(bp).resource_name = 'bp' - self.client.resources.list = mock.Mock(return_value=[bp]) - - shell.do_hook_clear(self.client, self.args) - self.assertEqual(1, self.client.resources.signal.call_count) - payload_pre_create = self.client.resources.signal.call_args_list[0][1] - self.assertEqual({'unset_hook': 'pre-create'}, - payload_pre_create['data']) - self.assertEqual('bp', payload_pre_create['resource_name']) - self.assertEqual('mystack', payload_pre_create['stack_id']) - - def test_clear_pre_create_hooks(self): - type(self.args).hook = mock.PropertyMock( - return_value=['bp']) - type(self.args).pre_create = mock.PropertyMock(return_value=True) - bp = mock.Mock() - type(bp).resource_name = 'bp' - self.client.resources.list = mock.Mock(return_value=[bp]) - - shell.do_hook_clear(self.client, self.args) - self.assertEqual(1, self.client.resources.signal.call_count) - payload = self.client.resources.signal.call_args_list[0][1] - self.assertEqual({'unset_hook': 'pre-create'}, payload['data']) - self.assertEqual('bp', payload['resource_name']) - self.assertEqual('mystack', payload['stack_id']) - - def test_clear_pre_update_hooks(self): - type(self.args).hook = mock.PropertyMock( - return_value=['bp']) - type(self.args).pre_update = mock.PropertyMock(return_value=True) - bp = mock.Mock() - type(bp).resource_name = 'bp' - self.client.resources.list = mock.Mock(return_value=[bp]) - - shell.do_hook_clear(self.client, self.args) - self.assertEqual(1, self.client.resources.signal.call_count) - payload = self.client.resources.signal.call_args_list[0][1] - self.assertEqual({'unset_hook': 'pre-update'}, payload['data']) - self.assertEqual('bp', payload['resource_name']) - self.assertEqual('mystack', payload['stack_id']) - - def test_clear_nested_hook(self): - type(self.args).hook = mock.PropertyMock( - return_value=['a/b/bp']) - type(self.args).pre_create = mock.PropertyMock(return_value=True) - - a = mock.Mock() - type(a).resource_name = 'a' - b = mock.Mock() - type(b).resource_name = 'b' - bp = mock.Mock() - type(bp).resource_name = 'bp' - self.client.resources.list = mock.Mock( - side_effect=[[a], [b], [bp]]) - m1 = mock.Mock() - m2 = mock.Mock() - type(m2).physical_resource_id = 'nested_id' - self.client.resources.get = mock.Mock( - side_effect=[m1, m2]) - - shell.do_hook_clear(self.client, self.args) - payload = self.client.resources.signal.call_args_list[0][1] - self.assertEqual({'unset_hook': 'pre-create'}, payload['data']) - self.assertEqual('bp', payload['resource_name']) - self.assertEqual('nested_id', payload['stack_id']) - - def test_clear_wildcard_hooks(self): - type(self.args).hook = mock.PropertyMock( - return_value=['a/*b/bp*']) - type(self.args).pre_create = mock.PropertyMock(return_value=True) - a = mock.Mock() - type(a).resource_name = 'a' - b = mock.Mock() - type(b).resource_name = 'matcthis_b' - bp = mock.Mock() - type(bp).resource_name = 'bp_matchthis' - self.client.resources.list = mock.Mock( - side_effect=[[a], [b], [bp]]) - m1 = mock.Mock() - m2 = mock.Mock() - type(m2).physical_resource_id = 'nested_id' - self.client.resources.get = mock.Mock( - side_effect=[m1, m2]) - - shell.do_hook_clear(self.client, self.args) - payload = self.client.resources.signal.call_args_list[0][1] - self.assertEqual({'unset_hook': 'pre-create'}, - payload['data']) - self.assertEqual('bp_matchthis', payload['resource_name']) - self.assertEqual('nested_id', payload['stack_id']) diff --git a/stacktaskclient/tests/unit/var/adopt_stack_data.json b/stacktaskclient/tests/unit/var/adopt_stack_data.json deleted file mode 100644 index efb998a..0000000 --- a/stacktaskclient/tests/unit/var/adopt_stack_data.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "action": "CREATE", - "status": "COMPLETE", - "name": "teststack", - "resources": {} -} \ No newline at end of file diff --git a/stacktaskclient/tests/unit/var/minimal.template b/stacktaskclient/tests/unit/var/minimal.template deleted file mode 100644 index 67331ac..0000000 --- a/stacktaskclient/tests/unit/var/minimal.template +++ /dev/null @@ -1,9 +0,0 @@ -{ - "AWSTemplateFormatVersion" : "2010-09-09", - "Parameters" : { - }, - "Resources" : { - }, - "Outputs" : { - } -} diff --git a/stacktaskclient/v1/client.py b/stacktaskclient/v1/client.py index 7ffc022..d343a27 100644 --- a/stacktaskclient/v1/client.py +++ b/stacktaskclient/v1/client.py @@ -1,5 +1,4 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. +# Copyright (C) 2016 Catalyst IT Ltd # # 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 diff --git a/stacktaskclient/v1/roles.py b/stacktaskclient/v1/roles.py index 7d4bbfd..e3c0e4a 100644 --- a/stacktaskclient/v1/roles.py +++ b/stacktaskclient/v1/roles.py @@ -1,5 +1,4 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. +# Copyright (C) 2016 Catalyst IT Ltd # # 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 @@ -12,6 +11,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. + from six.moves.urllib import parse from stacktaskclient.openstack.common.apiclient import base @@ -65,9 +65,9 @@ class UserRolesManager(base.BaseManager): def add(self, user, role, tenant=None): """Add a role to a user""" # TODO: resolve the roles and users into id's - #user_id = base.getid(user) + # user_id = base.getid(user) user_id = user - #role_id = role + # role_id = role params = { 'roles': [role] } diff --git a/stacktaskclient/v1/shell.py b/stacktaskclient/v1/shell.py index 74f46d5..a39ac4f 100644 --- a/stacktaskclient/v1/shell.py +++ b/stacktaskclient/v1/shell.py @@ -1,5 +1,4 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. +# Copyright (C) 2016 Catalyst IT Ltd # # 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 @@ -17,6 +16,7 @@ import logging import json from stacktaskclient.common import utils +from stacktaskclient.common import http from stacktaskclient.openstack.common._i18n import _ @@ -38,8 +38,8 @@ def _authenticated_fetcher(hc): return _do -#@utils.arg('--tenant-id', metavar='', -# help=_('Specify a particular tenant')) +# @utils.arg('--tenant-id', metavar='', +# help=_('Specify a particular tenant')) def do_user_list(hc, args): """List all users in tenant""" kwargs = {} @@ -212,17 +212,17 @@ def do_user_password_reset(rc, args): print "NOT YET IMPLEMENTED." pass -# -#@utils.arg('--user', '--user-id', metavar='', required=True, -# help=_('Name or ID of user.')) -#@utils.arg('--roles', '--role-id', metavar='', required=True, -# help=_('List of role ids')) -#@utils.arg('--tenant', '--tenant-id', metavar='', -# help=_('Name or ID of tenant.')) -#def do_user_role_set(hc, args): -# """Set the roles of a user. May be empty""" -# print("do_user_role_set") -# pass + +# @utils.arg('--user', '--user-id', metavar='', required=True, +# help=_('Name or ID of user.')) +# @utils.arg('--roles', '--role-id', metavar='', required=True, +# help=_('List of role ids')) +# @utils.arg('--tenant', '--tenant-id', metavar='', +# help=_('Name or ID of tenant.')) +# def do_user_role_set(hc, args): +# """Set the roles of a user. May be empty""" +# print("do_user_role_set") +# pass @utils.arg('--tenant', metavar='', @@ -241,4 +241,6 @@ def do_status(rc, args): if status.status_code != 200: print "Failed: %s" % status.reason return - print json.dumps(json.loads(status.content), sort_keys=True, indent=4, separators=(',', ': ')) + print json.dumps( + json.loads(status.content), sort_keys=True, + indent=4, separators=(',', ': ')) diff --git a/stacktaskclient/v1/status.py b/stacktaskclient/v1/status.py index 2fd4706..c42d508 100644 --- a/stacktaskclient/v1/status.py +++ b/stacktaskclient/v1/status.py @@ -1,3 +1,17 @@ +# Copyright (C) 2016 Catalyst IT Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + from stacktaskclient.openstack.common.apiclient import base diff --git a/stacktaskclient/v1/tasks.py b/stacktaskclient/v1/tasks.py index b9f606b..c59b729 100644 --- a/stacktaskclient/v1/tasks.py +++ b/stacktaskclient/v1/tasks.py @@ -1,5 +1,4 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. +# Copyright (C) 2016 Catalyst IT Ltd # # 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 diff --git a/stacktaskclient/v1/tokens.py b/stacktaskclient/v1/tokens.py index 77600a3..971b78d 100644 --- a/stacktaskclient/v1/tokens.py +++ b/stacktaskclient/v1/tokens.py @@ -1,9 +1,16 @@ -#tokens class - -from stacktaskclient.common import utils - -import six -from six.moves.urllib import parse +# Copyright (C) 2016 Catalyst IT Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. from stacktaskclient.openstack.common.apiclient import base diff --git a/stacktaskclient/v1/users.py b/stacktaskclient/v1/users.py index 0d34348..a3abbd3 100644 --- a/stacktaskclient/v1/users.py +++ b/stacktaskclient/v1/users.py @@ -1,5 +1,4 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. +# Copyright (C) 2016 Catalyst IT Ltd # # 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 @@ -12,7 +11,6 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from stacktaskclient.common import utils import six from six.moves.urllib import parse @@ -73,9 +71,9 @@ class UsersManager(base.ManagerWithFind): yield stack params = {} - #if 'filters' in kwargs: - # filters = kwargs.pop('filters') - # params.update(filters) + # if 'filters' in kwargs: + # filters = kwargs.pop('filters') + # params.update(filters) for key, value in six.iteritems(kwargs): if value: diff --git a/test-requirements.txt b/test-requirements.txt deleted file mode 100644 index 49e38ee..0000000 --- a/test-requirements.txt +++ /dev/null @@ -1,19 +0,0 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. - -# Hacking already pins down pep8, pyflakes and flake8 -hacking<0.11,>=0.10.0 -coverage>=3.6 -discover -fixtures>=1.3.1 -requests-mock>=0.6.0 # Apache-2.0 -mock>=1.2 -mox3>=0.7.0 -oslosphinx>=2.5.0 # Apache-2.0 -oslotest>=1.10.0 # Apache-2.0 -sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 -tempest-lib>=0.6.1 -testrepository>=0.0.18 -testscenarios>=0.4 -testtools>=1.4.0 diff --git a/tools/heat.bash_completion b/tools/heat.bash_completion deleted file mode 100644 index a04de1a..0000000 --- a/tools/heat.bash_completion +++ /dev/null @@ -1,27 +0,0 @@ -# bash completion for openstack heat - -_heat_opts="" # lazy init -_heat_flags="" # lazy init -_heat_opts_exp="" # lazy init -_heat() -{ - local cur prev kbc - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - - if [ "x$_heat_opts" == "x" ] ; then - kbc="`heat bash-completion | sed -e "s/ -h / /"`" - _heat_opts="`echo "$kbc" | sed -e "s/--[a-z0-9_-]*//g" -e "s/[ ][ ]*/ /g"`" - _heat_flags="`echo " $kbc" | sed -e "s/ [^-][^-][a-z0-9_-]*//g" -e "s/[ ][ ]*/ /g"`" - _heat_opts_exp="`echo $_heat_opts | sed -e "s/[ ]/|/g"`" - fi - - if [[ " ${COMP_WORDS[@]} " =~ " "($_heat_opts_exp)" " && "$prev" != "help" ]] ; then - COMPREPLY=($(compgen -W "${_heat_flags}" -- ${cur})) - else - COMPREPLY=($(compgen -W "${_heat_opts}" -- ${cur})) - fi - return 0 -} -complete -o default -F _heat heat diff --git a/tools/install_venv.py b/tools/install_venv.py deleted file mode 100644 index cc21843..0000000 --- a/tools/install_venv.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Copyright 2010 OpenStack Foundation -# Copyright 2013 IBM Corp. -# 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 ConfigParser -import os -import sys - -import install_venv_common as install_venv # flake8: noqa - - -def print_help(project, venv, root): - help = """ - %(project)s development environment setup is complete. - - %(project)s development uses virtualenv to track and manage Python - dependencies while in development and testing. - - To activate the %(project)s virtualenv for the extent of your current - shell session you can run: - - $ source %(venv)s/bin/activate - - Or, if you prefer, you can run commands in the virtualenv on a case by - case basis by running: - - $ %(root)s/tools/with_venv.sh - """ - print help % dict(project=project, venv=venv, root=root) - - -def main(argv): - root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - - if os.environ.get('tools_path'): - root = os.environ['tools_path'] - venv = os.path.join(root, '.venv') - if os.environ.get('venv'): - venv = os.environ['venv'] - - pip_requires = os.path.join(root, 'requirements.txt') - test_requires = os.path.join(root, 'test-requirements.txt') - py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1]) - setup_cfg = ConfigParser.ConfigParser() - setup_cfg.read('setup.cfg') - project = setup_cfg.get('metadata', 'name') - - install = install_venv.InstallVenv( - root, venv, pip_requires, test_requires, py_version, project) - options = install.parse_args(argv) - install.check_python_version() - install.check_dependencies() - install.create_virtualenv(no_site_packages=options.no_site_packages) - install.install_dependencies() - print_help(project, venv, root) - -if __name__ == '__main__': - main(sys.argv) diff --git a/tools/install_venv_common.py b/tools/install_venv_common.py deleted file mode 100644 index 46822e3..0000000 --- a/tools/install_venv_common.py +++ /dev/null @@ -1,172 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# Copyright 2013 IBM Corp. -# -# 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. - -"""Provides methods needed by installation script for OpenStack development -virtual environments. - -Since this script is used to bootstrap a virtualenv from the system's Python -environment, it should be kept strictly compatible with Python 2.6. - -Synced in from openstack-common -""" - -from __future__ import print_function - -import optparse -import os -import subprocess -import sys - - -class InstallVenv(object): - - def __init__(self, root, venv, requirements, - test_requirements, py_version, - project): - self.root = root - self.venv = venv - self.requirements = requirements - self.test_requirements = test_requirements - self.py_version = py_version - self.project = project - - def die(self, message, *args): - print(message % args, file=sys.stderr) - sys.exit(1) - - def check_python_version(self): - if sys.version_info < (2, 6): - self.die("Need Python Version >= 2.6") - - def run_command_with_code(self, cmd, redirect_output=True, - check_exit_code=True): - """Runs a command in an out-of-process shell. - - Returns the output of that command. Working directory is self.root. - """ - if redirect_output: - stdout = subprocess.PIPE - else: - stdout = None - - proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout) - output = proc.communicate()[0] - if check_exit_code and proc.returncode != 0: - self.die('Command "%s" failed.\n%s', ' '.join(cmd), output) - return (output, proc.returncode) - - def run_command(self, cmd, redirect_output=True, check_exit_code=True): - return self.run_command_with_code(cmd, redirect_output, - check_exit_code)[0] - - def get_distro(self): - if (os.path.exists('/etc/fedora-release') or - os.path.exists('/etc/redhat-release')): - return Fedora( - self.root, self.venv, self.requirements, - self.test_requirements, self.py_version, self.project) - else: - return Distro( - self.root, self.venv, self.requirements, - self.test_requirements, self.py_version, self.project) - - def check_dependencies(self): - self.get_distro().install_virtualenv() - - def create_virtualenv(self, no_site_packages=True): - """Creates the virtual environment and installs PIP. - - Creates the virtual environment and installs PIP only into the - virtual environment. - """ - if not os.path.isdir(self.venv): - print('Creating venv...', end=' ') - if no_site_packages: - self.run_command(['virtualenv', '-q', '--no-site-packages', - self.venv]) - else: - self.run_command(['virtualenv', '-q', self.venv]) - print('done.') - else: - print("venv already exists...") - pass - - def pip_install(self, *args): - self.run_command(['tools/with_venv.sh', - 'pip', 'install', '--upgrade'] + list(args), - redirect_output=False) - - def install_dependencies(self): - print('Installing dependencies with pip (this can take a while)...') - - # First things first, make sure our venv has the latest pip and - # setuptools and pbr - self.pip_install('pip>=1.4') - self.pip_install('setuptools') - self.pip_install('pbr') - - self.pip_install('-r', self.requirements, '-r', self.test_requirements) - - def parse_args(self, argv): - """Parses command-line arguments.""" - parser = optparse.OptionParser() - parser.add_option('-n', '--no-site-packages', - action='store_true', - help="Do not inherit packages from global Python " - "install") - return parser.parse_args(argv[1:])[0] - - -class Distro(InstallVenv): - - def check_cmd(self, cmd): - return bool(self.run_command(['which', cmd], - check_exit_code=False).strip()) - - def install_virtualenv(self): - if self.check_cmd('virtualenv'): - return - - if self.check_cmd('easy_install'): - print('Installing virtualenv via easy_install...', end=' ') - if self.run_command(['easy_install', 'virtualenv']): - print('Succeeded') - return - else: - print('Failed') - - self.die('ERROR: virtualenv not found.\n\n%s development' - ' requires virtualenv, please install it using your' - ' favorite package management tool' % self.project) - - -class Fedora(Distro): - """This covers all Fedora-based distributions. - - Includes: Fedora, RHEL, CentOS, Scientific Linux - """ - - def check_pkg(self, pkg): - return self.run_command_with_code(['rpm', '-q', pkg], - check_exit_code=False)[1] == 0 - - def install_virtualenv(self): - if self.check_cmd('virtualenv'): - return - - if not self.check_pkg('python-virtualenv'): - self.die("Please install 'python-virtualenv'.") - - super(Fedora, self).install_virtualenv() diff --git a/tools/with_venv.sh b/tools/with_venv.sh deleted file mode 100755 index 1b09ad7..0000000 --- a/tools/with_venv.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -command -v tox > /dev/null 2>&1 -if [ $? -ne 0 ]; then - echo 'This script requires "tox" to run.' - echo 'You can install it with "pip install tox".' - exit 1; -fi - -tox -evenv -- $@ diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 7674f25..0000000 --- a/tox.ini +++ /dev/null @@ -1,51 +0,0 @@ -[tox] -envlist = py26,py27,pypy,pep8 -minversion = 1.6 -skipsdist = True - -[testenv] -setenv = VIRTUAL_ENV={envdir} -usedevelop = True -install_command = pip install -U {opts} {packages} -deps = -r{toxinidir}/requirements.txt - -r{toxinidir}/test-requirements.txt -commands = python setup.py testr --slowest --testr-args='{posargs}' - -[testenv:pypy] -deps = setuptools<3.2 - -r{toxinidir}/requirements.txt - -r{toxinidir}/test-requirements.txt - -[testenv:pep8] -commands = - flake8 - # Check that .po and .pot files are valid: - bash -c "find python-registrationclient -type f -regex '.*\.pot?' -print0|xargs -0 -n 1 msgfmt --check-format -o /dev/null" -whitelist_externals = bash - -[testenv:venv] -commands = {posargs} - -[testenv:functional] -setenv = - OS_TEST_PATH = ./registrationclient/tests/functional -passenv = OS_* - -[testenv:cover] -commands = python setup.py testr --coverage --testr-args='{posargs}' - -[testenv:docs] -commands= - python setup.py build_sphinx - -[tox:jenkins] -downloadcache = ~/cache/pip - -[flake8] -ignore = E123,E126,E128,E241,E265,E713,H202,H405,H238 -show-source = True -exclude=.venv,.git,.tox,dist,*openstack/common*,*lib/python*,*egg,build -max-complexity=20 - -[hacking] -import_exceptions = registrationclient.openstack.common._i18n