├── .bowerrc ├── .dockerignore ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .gitreview ├── .testr.conf ├── .zuul.yaml ├── LICENSE ├── README.rst ├── bin ├── refstack-api └── refstack-manage ├── bindep.txt ├── bower.json ├── doc ├── requirements.txt └── source │ ├── README.rst │ ├── conf.py │ ├── index.rst │ ├── refstack.rst │ ├── run_in_docker.rst │ ├── test_result_management.rst │ ├── uploading_private_results.rst │ └── vendor_product.rst ├── docker ├── Dockerfile ├── nginx │ ├── refstack-site.conf │ ├── refstack_dev.crt │ └── refstack_dev.key ├── scripts │ ├── api-db-version │ ├── api-init-db │ ├── api-sync │ ├── api-up │ └── start.sh └── templates │ ├── config.json.tmpl │ └── refstack.conf.tmpl ├── etc └── refstack.conf.sample ├── package.json ├── refstack-ui ├── app │ ├── app.js │ ├── assets │ │ ├── css │ │ │ └── style.css │ │ └── img │ │ │ ├── OpenStack_Project_Refstack_mascot_90x90.png │ │ │ ├── openstack-logo.png │ │ │ └── refstack-logo.png │ ├── components │ │ ├── about │ │ │ ├── about.html │ │ │ └── aboutController.js │ │ ├── auth-failure │ │ │ └── authFailureController.js │ │ ├── guidelines │ │ │ ├── guidelines.html │ │ │ ├── guidelinesController.js │ │ │ └── partials │ │ │ │ ├── guidelineDetails.html │ │ │ │ └── testListModal.html │ │ ├── home │ │ │ └── home.html │ │ ├── logout │ │ │ ├── logout.html │ │ │ └── logoutController.js │ │ ├── products │ │ │ ├── cloud.html │ │ │ ├── distro.html │ │ │ ├── partials │ │ │ │ ├── management.html │ │ │ │ ├── productEditModal.html │ │ │ │ ├── testsTable.html │ │ │ │ ├── versions.html │ │ │ │ └── versionsModal.html │ │ │ ├── productController.js │ │ │ ├── products.html │ │ │ └── productsController.js │ │ ├── profile │ │ │ ├── importPubKeyModal.html │ │ │ ├── profile.html │ │ │ ├── profileController.js │ │ │ └── showPubKeyModal.html │ │ ├── results-report │ │ │ ├── partials │ │ │ │ ├── editTestModal.html │ │ │ │ ├── fullTestListModal.html │ │ │ │ └── reportDetails.html │ │ │ ├── resultsReport.html │ │ │ └── resultsReportController.js │ │ ├── results │ │ │ ├── results.html │ │ │ └── resultsController.js │ │ └── vendors │ │ │ ├── partials │ │ │ └── vendorEditModal.html │ │ │ ├── vendor.html │ │ │ ├── vendorController.js │ │ │ ├── vendors.html │ │ │ └── vendorsController.js │ ├── config.json.sample │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── index.html │ ├── robots.txt │ └── shared │ │ ├── alerts │ │ ├── alertModal.html │ │ ├── alertModalFactory.js │ │ ├── confirmModal.html │ │ └── confirmModalFactory.js │ │ ├── filters.js │ │ └── header │ │ ├── header.html │ │ └── headerController.js └── tests │ ├── karma.conf.js │ └── unit │ ├── AuthSpec.js │ ├── ControllerSpec.js │ └── FilterSpec.js ├── refstack ├── __init__.py ├── api │ ├── __init__.py │ ├── app.py │ ├── app.wsgi │ ├── config.py │ ├── constants.py │ ├── controllers │ │ ├── __init__.py │ │ ├── auth.py │ │ ├── guidelines.py │ │ ├── products.py │ │ ├── results.py │ │ ├── root.py │ │ ├── user.py │ │ ├── v1.py │ │ ├── validation.py │ │ └── vendors.py │ ├── exceptions.py │ ├── guidelines.py │ ├── utils.py │ └── validators.py ├── db │ ├── __init__.py │ ├── api.py │ ├── migration.py │ ├── migrations │ │ ├── __init__.py │ │ ├── alembic.ini │ │ └── alembic │ │ │ ├── README │ │ │ ├── __init__.py │ │ │ ├── env.py │ │ │ ├── migration.py │ │ │ ├── script.py.mako │ │ │ ├── utils.py │ │ │ └── versions │ │ │ ├── 19fded785b8c_create_organization_table.py │ │ │ ├── 23843be3da52_add_product_version_id.py │ │ │ ├── 2f178b0bf762_create_user_table.py │ │ │ ├── 319ee8fe47c7_create_group_table.py │ │ │ ├── 35bf54e2c13c_add_product_version.py │ │ │ ├── 42278d6179b9_init.py │ │ │ ├── 428e5aef5534_associate_test_result.py │ │ │ ├── 434be17a6ec3_fix_openids_with_space.py │ │ │ ├── 534e20be9964_create_pubkey_table.py │ │ │ ├── 59df512e82f_add_verification_status.py │ │ │ ├── 7092392cbb8e_create_product_table.py │ │ │ ├── 7093ca478d35_product_table_make_product_id_nullable.py │ │ │ └── 709452f38a5c_product_table_rename_product_id.py │ ├── sqlalchemy │ │ ├── __init__.py │ │ ├── api.py │ │ └── models.py │ └── utils.py ├── opts.py └── tests │ ├── __init__.py │ ├── api │ ├── __init__.py │ ├── test_guidelines.py │ ├── test_products.py │ ├── test_profile.py │ ├── test_results.py │ └── test_vendors.py │ └── unit │ ├── __init__.py │ ├── test_api.py │ ├── test_api_utils.py │ ├── test_app.py │ ├── test_db.py │ ├── test_guidelines.py │ ├── test_migration.py │ └── test_validators.py ├── requirements.txt ├── run-in-docker ├── setup-mysql-tests.sh ├── setup.cfg ├── setup.py ├── specs ├── README.rst ├── mitaka │ └── implemented │ │ ├── product-registration-api.rst │ │ ├── rsa-key-existence-check.rst │ │ ├── use-url-as-cpid.rst │ │ ├── user-group.rst │ │ ├── vendor-registration-data-model.rst │ │ └── vendor-user-management-api.rst ├── newton │ └── implemented │ │ ├── defcore-guideline-api.rst │ │ ├── product-version-datamodel-api.rst │ │ └── vendor-registration-api.rst ├── ocata │ └── implemented │ │ ├── associate-test-result-to-product.rst │ │ └── mark-test-result-as-verified.rst ├── pike │ └── approved │ │ ├── add-refstack-docs.rst │ │ └── upload-subunit-tests.rst ├── prior │ ├── approved │ │ ├── ability_to_upload_a_complete_tempest_config.rst │ │ ├── refstack-org-defcore-reports.rst │ │ ├── refstack-org-gearman-tester.rst │ │ ├── refstack-org-result-visualization.rst │ │ └── refstack-org-tcup-base.rst │ └── implemented │ │ ├── api-v1.md │ │ ├── coretest-testid.rst │ │ ├── identify-code-to-deprecate.rst │ │ ├── refstack-org-api-cloud-uuid.rst │ │ ├── refstack-org-test-result-json-schema.rst │ │ ├── seperate_refstack_tester_from_refstack.rst │ │ ├── simplify-uploads-by-only-sending-pass-results.rst │ │ └── test-record-api.rst ├── queens │ └── approved │ │ └── subunit-data-api.rst └── template.rst ├── test-requirements.txt ├── tools ├── convert-docs.py ├── cover.sh ├── install-js-tools.sh ├── test-setup.sh ├── update-rs-db.py └── update-rs-db.rst ├── tox.ini └── yarn.lock /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "refstack-ui/app/assets/lib" 3 | } 4 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | *.egg* 2 | *.py[cod] 3 | .coverage 4 | .testrepository/ 5 | .tox/ 6 | .venv/ 7 | AUTHORS 8 | ChangeLog 9 | build/ 10 | cover/ 11 | dist 12 | .git/ -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | refstack-ui/app/assets/lib -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | // For a detailed list of all options, please see here: 3 | // http://eslint.org/docs/configuring/ 4 | "ecmaFeatures": { 5 | "arrowFunctions": false, 6 | "binaryLiterals": false, 7 | "blockBindings": false, 8 | "defaultParams": false, 9 | "forOf": false, 10 | "generators": false, 11 | "objectLiteralComputedProperties": false, 12 | "objectLiteralDuplicateProperties": false, 13 | "objectLiteralShorthandProperties": false, 14 | "octalLiterals": false, 15 | "regexUFlag": false, 16 | "superInFunctions": false, 17 | "templateStrings": false, 18 | "unicodeCodePointEscapes": false, 19 | "globalReturn": false, 20 | "jsx": false 21 | }, 22 | 23 | "env": { 24 | "browser": true, 25 | "node": false, 26 | "amd": false, 27 | "mocha": false, 28 | "jasmine": true, 29 | "phantomjs": false, 30 | "jquery": false, 31 | "prototypejs": false, 32 | "shelljs": false, 33 | "es6": true 34 | }, 35 | 36 | "extends": "openstack", 37 | 38 | "globals": { 39 | "require": false, 40 | "exports": false, 41 | "angular": false, // AngularJS 42 | "module": false, 43 | "inject": false, 44 | "element": false, 45 | "by": false, 46 | "browser": false 47 | }, 48 | 49 | "plugins": [ 50 | "angular" 51 | ], 52 | 53 | "rules": { 54 | "quotes": [2, "single"], 55 | "eol-last": 2, 56 | "no-trailing-spaces": 2, 57 | "camelcase": 0, 58 | "no-extra-boolean-cast": 0, 59 | "operator-linebreak": 0, 60 | "require-jsdoc": 2, 61 | "quote-props": 0, 62 | "valid-jsdoc": 0, 63 | 64 | // Stylistic 65 | "indent": [2, 4, {SwitchCase: 1}], 66 | "max-len": [2, 80], 67 | "no-undefined": 2, 68 | 69 | // Angular Plugin 70 | "angular/controller-as-vm": [1, "ctrl"] 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.egg* 2 | *.py[cod] 3 | .coverage 4 | .testrepository/ 5 | .tox/ 6 | .venv/ 7 | AUTHORS 8 | ChangeLog 9 | build/ 10 | cover/ 11 | dist 12 | 13 | .tmp 14 | node_modules 15 | npm-debug.log 16 | refstack-ui/app/assets/lib 17 | refstack-ui/app/config.json 18 | -------------------------------------------------------------------------------- /.gitreview: -------------------------------------------------------------------------------- 1 | [gerrit] 2 | host=review.opendev.org 3 | port=29418 4 | project=openstack/refstack 5 | -------------------------------------------------------------------------------- /.testr.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./refstack -s ${SUBUNIT_TEST_PATH:-./refstack/tests/unit} $LISTOPT $IDOPTION 3 | test_id_option=--load-list $IDFILE 4 | test_list_option=--list 5 | -------------------------------------------------------------------------------- /.zuul.yaml: -------------------------------------------------------------------------------- 1 | - project: 2 | templates: 3 | - nodejs6-jobs 4 | - publish-openstack-docs-pti 5 | - openstack-cover-jobs 6 | - openstack-python-jobs 7 | - openstack-python35-jobs 8 | - openstack-python36-jobs 9 | check: 10 | jobs: 11 | - refstack-tox-py27-func-mysql 12 | - refstack-tox-py36-func-mysql 13 | gate: 14 | jobs: 15 | - refstack-tox-py27-func-mysql 16 | - refstack-tox-py36-func-mysql 17 | 18 | - job: 19 | name: refstack-tox-py27-func-mysql 20 | parent: openstack-tox 21 | description: | 22 | Run unit tests for an OpenStack Python project under cPython version 2.7. 23 | 24 | Uses tox with the ``py27-func-mysql`` environment. 25 | irrelevant-files: 26 | - ^.*\.rst$ 27 | - ^doc/.*$ 28 | - ^releasenotes/.*$ 29 | vars: 30 | tox_envlist: py27-func-mysql 31 | 32 | - job: 33 | name: refstack-tox-py36-func-mysql 34 | parent: openstack-tox 35 | description: | 36 | Run unit tests for an OpenStack Python project under cPython version 3.6. 37 | 38 | Uses tox with the ``py36-func-mysql`` environment. 39 | irrelevant-files: 40 | - ^.*\.rst$ 41 | - ^doc/.*$ 42 | - ^releasenotes/.*$ 43 | vars: 44 | tox_envlist: py36-func-mysql 45 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | RefStack 3 | ======== 4 | 5 | RefStack team and repository tags 6 | ################################# 7 | .. image:: https://governance.openstack.org/badges/refstack.svg 8 | :target: https://governance.openstack.org/reference/tags/index.html 9 | 10 | 11 | What is RefStack? 12 | ################# 13 | 14 | - Toolset for testing interoperability between OpenStack clouds. 15 | - Database backed website supporting collection and publication of 16 | Community Test results for OpenStack. 17 | - User interface to display individual test run results. 18 | 19 | Overview 20 | ######## 21 | 22 | RefStack intends on being THE source of tools for interoperability testing 23 | of OpenStack clouds. 24 | 25 | RefStack provides users in the OpenStack community with a Tempest wrapper, 26 | refstack-client, that helps to verify interoperability of their cloud 27 | with other OpenStack clouds. It does so by validating any cloud 28 | implementation against the OpenStack Tempest API tests. 29 | 30 | **RefStack and Interop Working Group** - The prototypical use case for RefStack 31 | provides the Interop Working Group - formerly known as DefCore committee - the 32 | tools for vendors and other users to run API tests against their clouds to 33 | provide the WG with a reliable overview of what APIs and capabilities are 34 | being used in the marketplace. This will help to guide the Interop 35 | Working Group defined capabilities and help ensure interoperability across 36 | the entire OpenStack ecosystem. It can also be used to validate clouds 37 | against existing capability lists, giving you assurance that your cloud 38 | faithfully implements OpenStack standards. 39 | 40 | **Value add for vendors** - Vendors can use RefStack to demonstrate that 41 | their distros, and/or their customers' installed clouds remain with OpenStack 42 | after their software has been incorporated into the distro or cloud. 43 | 44 | **RefStack consists of two parts:** 45 | 46 | * **refstack-api** 47 | Our API isn't just for us to collect data from private and public cloud 48 | vendors. It can be used by vendors in house to compare interoperability 49 | data over time. 50 | 51 | * API and UI install docs: http://github.com/openstack/refstack/blob/master/doc/source/refstack.rst 52 | * repository: http://git.openstack.org/cgit/openstack/refstack 53 | * reviews: https://review.opendev.org/#/q/status:open+project:openstack/refstack 54 | 55 | * **refstack-client** 56 | refstack-client contains the tools you will need to run the 57 | Interop Working Group tests. 58 | 59 | * repository: http://git.openstack.org/cgit/openstack/refstack-client 60 | * reviews: https://review.opendev.org/#/q/status:open+project:openstack/refstack-client 61 | 62 | Get involved! 63 | ############# 64 | 65 | * Mailing List: openstack-discuss@lists.openstack.org 66 | * IRC: #refstack on Freenode 67 | * Dev Meetings: Tuesdays @ 19:00 UTC in #openstack-meeting-alt on Freenode 68 | * Web-site: https://refstack.openstack.org 69 | * Wiki: https://wiki.OpenStack.org/wiki/RefStack 70 | -------------------------------------------------------------------------------- /bin/refstack-api: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2015 Mirantis, Inc. 4 | # All Rights Reserved. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | # not use this file except in compliance with the License. You may obtain 8 | # a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | # License for the specific language governing permissions and limitations 16 | # under the License. 17 | 18 | """ 19 | Command-line launcher for Refstack API 20 | """ 21 | 22 | import sys 23 | 24 | from pecan.commands import serve 25 | 26 | from refstack.api import config as api_config 27 | 28 | 29 | def get_pecan_config(): 30 | """Get path to pecan configuration file""" 31 | filename = api_config.__file__.replace('.pyc', '.py') 32 | return filename 33 | 34 | 35 | if __name__ == '__main__': 36 | config_path = get_pecan_config() 37 | sys.argv.append(config_path) 38 | serve.gunicorn_run() 39 | -------------------------------------------------------------------------------- /bin/refstack-manage: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2015 Mirantis, Inc. 4 | # All Rights Reserved. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | # not use this file except in compliance with the License. You may obtain 8 | # a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | # License for the specific language governing permissions and limitations 16 | # under the License. 17 | """ 18 | Command-line utility for database manage 19 | """ 20 | 21 | import sys 22 | 23 | from oslo_config import cfg 24 | from oslo_log import log 25 | 26 | from refstack.db import migration 27 | 28 | CONF = cfg.CONF 29 | 30 | log.register_options(CONF) 31 | 32 | 33 | class DatabaseManager(object): 34 | 35 | def version(self): 36 | print(migration.version()) 37 | 38 | def upgrade(self): 39 | migration.upgrade(CONF.command.revision) 40 | 41 | def downgrade(self): 42 | migration.downgrade(CONF.command.revision) 43 | 44 | def stamp(self): 45 | migration.stamp(CONF.command.revision) 46 | 47 | def revision(self): 48 | migration.revision(CONF.command.message, CONF.command.autogenerate) 49 | 50 | 51 | def add_command_parsers(subparsers): 52 | db_manager = DatabaseManager() 53 | 54 | parser = subparsers.add_parser('version', 55 | help='show current database version') 56 | parser.set_defaults(func=db_manager.version) 57 | 58 | parser = subparsers.add_parser('upgrade', 59 | help='upgrade database to ' 60 | 'the specified version') 61 | parser.set_defaults(func=db_manager.upgrade) 62 | parser.add_argument('--revision', nargs='?', 63 | help='desired database version') 64 | 65 | parser = subparsers.add_parser('downgrade', 66 | help='downgrade database ' 67 | 'to the specified version') 68 | parser.set_defaults(func=db_manager.downgrade) 69 | parser.add_argument('--revision', nargs='?', 70 | help='desired database version') 71 | 72 | parser = subparsers.add_parser('stamp', 73 | help='stamp database with provided ' 74 | 'revision. Don\'t run any migrations') 75 | parser.add_argument('--revision', nargs='?', 76 | help='should match one from repository or head - ' 77 | 'to stamp database with most recent revision') 78 | parser.set_defaults(func=db_manager.stamp) 79 | 80 | parser = subparsers.add_parser('revision', 81 | help='create template for migration') 82 | parser.add_argument('-m', '--message', 83 | help='text that will be used for migration title') 84 | parser.add_argument('--autogenerate', action='store_true', 85 | help='if True - generates diff based ' 86 | 'on current database state (True by default)') 87 | parser.set_defaults(func=db_manager.revision) 88 | 89 | command_opt = cfg.SubCommandOpt('command', 90 | title='Available commands', 91 | handler=add_command_parsers) 92 | 93 | CONF.register_cli_opt(command_opt) 94 | 95 | if __name__ == '__main__': 96 | CONF(sys.argv[1:], project='refstack') 97 | log.setup(CONF, 'refstack') 98 | CONF.command.func() 99 | -------------------------------------------------------------------------------- /bindep.txt: -------------------------------------------------------------------------------- 1 | # This is a cross-platform list tracking distribution packages needed for install and tests; 2 | # see https://docs.openstack.org/infra/bindep/ for additional information. 3 | 4 | gcc [compile test] 5 | mysql-client [test platform:dpkg] 6 | mysql-server [test platform:dpkg] 7 | postgresql [test] 8 | postgresql-client [test platform:dpkg] 9 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "refstack-ui", 3 | "version": "0.0.1", 4 | "description": "Refstack user interface", 5 | "dependencies": { 6 | "angular": "1.3.15", 7 | "angular-ui-router": "0.2.13", 8 | "angular-resource": "1.3.15", 9 | "angular-bootstrap": "0.14.3", 10 | "angular-busy": "4.1.3", 11 | "angular-confirm-modal": "1.2.3", 12 | "bootstrap": "3.3.2" 13 | }, 14 | "devDependencies": { 15 | "angular-mocks": "1.3.15" 16 | }, 17 | "resolutions": { 18 | "angular": "1.3.15" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /doc/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx>=1.6.2 2 | openstackdocstheme>=1.11.0 # Apache-2.0 3 | -------------------------------------------------------------------------------- /doc/source/README.rst: -------------------------------------------------------------------------------- 1 | ../../README.rst -------------------------------------------------------------------------------- /doc/source/index.rst: -------------------------------------------------------------------------------- 1 | .. Refstack documentation master file, created by 2 | sphinx-quickstart on Fri Aug 5 01:41:59 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | ==================================== 7 | Welcome to RefStack's documentation! 8 | ==================================== 9 | 10 | -------------- 11 | About RefStack 12 | -------------- 13 | 14 | .. toctree:: 15 | :maxdepth: 2 16 | 17 | README 18 | 19 | -------------------------- 20 | Installing RefStack Server 21 | -------------------------- 22 | 23 | .. toctree:: 24 | :maxdepth: 2 25 | 26 | refstack 27 | run_in_docker 28 | 29 | -------------- 30 | Using RefStack 31 | -------------- 32 | 33 | .. toctree:: 34 | :maxdepth: 2 35 | 36 | uploading_private_results 37 | vendor_product 38 | test_result_management 39 | 40 | ------------------ 41 | Indices and tables 42 | ------------------ 43 | 44 | * :ref:`search` 45 | 46 | -------------------------------------------------------------------------------- /doc/source/run_in_docker.rst: -------------------------------------------------------------------------------- 1 | Run-in-docker manual 2 | ==================== 3 | 4 | The main purpose of the ``run-in-docker`` script is to provide a 5 | convenient way to create a local setup of RefStack inside a Docker 6 | container. It should be helpful for new developers and also for testing 7 | new features. 8 | 9 | Requirements: 10 | ^^^^^^^^^^^^^ 11 | 12 | - ``Docker >= 1.6`` (How to update on 13 | `Ubuntu `__) 14 | 15 | How to use: 16 | ^^^^^^^^^^^ 17 | 18 | Just run the ``run-in-docker`` script, but is important to first set 19 | env[REFSTACK\_HOST] with the public host/IP for your local API. If you 20 | want to test RefStack with OpenStackid you should point a valid local 21 | alias here. For example: 22 | 23 | ``export REFSTACK_HOST=myrefstack.com`` 24 | 25 | By default 127.0.0.1 is used. 26 | 27 | After it completes, check that the site is running on https://127.0.0.1. 28 | 29 | The script will build a RefStack docker image with all dependencies, and 30 | will run a container from this image. By default, RefStack will run 31 | inside this container. You also can run ``run-in-docker bash`` to get 32 | access into the container. If you stop the RefStack server by pressing 33 | 'Ctrl-C', the container is kept alive and will be re-used next time. 34 | 35 | You can customize the RefStack API config by editing 36 | ``docker/templates/refstack.conf.tmpl``. It is a bash template, so you 37 | can use ${SOME\_ENV\_VARIABLE} in it. 38 | 39 | This script can make the reviewing process much easier because it 40 | creates separate containers for each review. Containers get names in the 41 | form refstack\_{REVIEW-TOPIC}. Database schema changes are automatically 42 | handled, too, where the script creates a data container for each 43 | database revision (refstack\_data\_{DATA-BASE-REVISON}) and reuses it 44 | where possible. For example, if a new review uses an existing database 45 | revision, that database container will be used. 46 | 47 | Available script options: 48 | ^^^^^^^^^^^^^^^^^^^^^^^^^ 49 | 50 | - ``-r`` Force delete the RefStack container and run it again. This 51 | will update the RefStack config from template noted above. 52 | - ``-i`` Run a container with isolated MySQL data. By default MySQL 53 | data is stored in a refstack\_data\_{DATA-BASE-REVISON} container. It 54 | reuses this container if such one exists. If you want to drop the DB 55 | data, just execute 56 | ``sudo docker rm refstack_data_{DATA-BASE-REVISON}``. 57 | - ``-b`` Force delete RefStack image and build it again. This rebuilds 58 | the Python and JS environment for RefStack. 59 | - ``-d`` Turn on debug information. 60 | - ``-h`` Print usage message. 61 | 62 | Useful in-container commands/aliases: 63 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 64 | 65 | - ``api-up`` - sync project and run the RefStack API 66 | - ``api-init-db`` - initialize the RefStack database 67 | - ``api-db-version`` - get current migration version of the RefStack 68 | database 69 | - ``api-sync`` - sync project files in the container with the project 70 | files on the host 71 | - ``activate`` - activate the python virtual env 72 | - ``mysql`` - open the MySQL console 73 | 74 | -------------------------------------------------------------------------------- /doc/source/test_result_management.rst: -------------------------------------------------------------------------------- 1 | Test result management 2 | ====================== 3 | 4 | Test result to product version association 5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | 7 | Test results uploaded by users can be associated to a version of a product. To 8 | perform this association, the user must be both the one who uploaded the result 9 | and also an admin of the vendor which owns the product. Once a test result is 10 | associated to a product, all admins of the vendor which owns the product can 11 | manage the test result. 12 | 13 | Mark or unmark a test results as verified 14 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 15 | 16 | Only Foundation admins can mark and un-mark a test as verified. A verified 17 | test result can not be updated or deleted. 18 | -------------------------------------------------------------------------------- /doc/source/vendor_product.rst: -------------------------------------------------------------------------------- 1 | Vendor and product management 2 | ============================= 3 | 4 | RefStack has implemented a vendor and product registration process so that test 5 | results can be associated to products of vendors. The creation and management 6 | of vendor and product entities can be done using the RefStack Server UI or 7 | RefStack APIs. The following is a quick guide outlining the information related 8 | to the creation and management of those entities. 9 | 10 | Vendor entity 11 | ^^^^^^^^^^^^^ 12 | 13 | Any user who has successfully authenticated to the RefStack server can create 14 | vendor entities. The minimum required information to create a vendor is the 15 | vendor name. Users can update the rest of the vendor related information at a 16 | later time. 17 | 18 | Vendor admin 19 | ~~~~~~~~~~~~~ 20 | 21 | Whenever a user creates a vendor, this user will be added as the vendor's first 22 | vendor admin. Subsequently, any admin of the vendor can add additional users to 23 | the vendor. In RefStack, the "OpenStack User ID" of users are used as the 24 | identities for adding users to vendors. At the time this document is written, 25 | RefStack has not implemented user roles, and as such, all users of a vendor are 26 | admin users. 27 | 28 | Vendor types 29 | ~~~~~~~~~~~~~ 30 | 31 | There are four types of vendor entities in RefStack: 32 | 33 | - Foundation: 34 | 35 | This is a special entity representing the OpenStack Foundation. Users belong 36 | to this entity are the Foundation admins. Foundation admins have visibility 37 | to all vendors and products. 38 | 39 | - Private: 40 | 41 | A vendor will always be created with type "private". Vendors of this type 42 | are only visible to their own users and Foundation admins. Vendor users can 43 | initiate a registration request to the Foundation to change its type from 44 | "private" to "official". 45 | 46 | - Pending 47 | 48 | Once a registration request is submitted, the vendor type will be changed 49 | automatically from type "private" to "pending". Vendors of this type are 50 | still only visible to their own users and Foundation admins. 51 | 52 | - Official 53 | 54 | Once a vendor registration request is approved by the Foundation. The vendor 55 | type will be changed from "pending" to "official". Official vendors are 56 | visible to all RefStack users. 57 | 58 | Product entity 59 | ^^^^^^^^^^^^^^ 60 | 61 | Any user who has successfully authenticated to the RefStack server can create 62 | product entities. The minimum information needed to create a product entity is 63 | as follows: 64 | 65 | - Name 66 | 67 | This is the name of the product entity being created. 68 | 69 | - Product type: 70 | 71 | Product types are defined by OpenStack as shown on the OpenStack Marketplace 72 | ( https://www.openstack.org/marketplace/ ). Currently, there are three types 73 | of products, namely: Distro & Appliances, Hosted Private Clouds and Public 74 | Clouds. 75 | 76 | - Vendor 77 | 78 | This is the vendor which owns the product. A default vendor will be created 79 | for the user if no vendor entity exists for this user. 80 | 81 | Whenever a product is created, by default, it is a private product and is only 82 | visible to its vendor users. Vendor users can make a product publicly visible 83 | as needed later. However, only products that are owned by official vendors can 84 | be made publicly visible. 85 | 86 | Product version 87 | ~~~~~~~~~~~~~~~ 88 | 89 | A default version is created whenever a product is created. The name of the 90 | default version is blank. The default version is used for products that have 91 | no version. Users can add new product versions to the product as needed. 92 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | EXPOSE 443 3 | 4 | ENV DEBIAN_FRONTEND noninteractive 5 | ENV PYTHONPATH=/home/dev/refstack \ 6 | SQL_DIR=/home/dev/mysql 7 | ENV REFSTACK_MYSQL_URL="mysql+pymysql://root@localhost/refstack?unix_socket=${SQL_DIR}/mysql.socket&charset=utf8" 8 | 9 | ADD /docker/scripts/* /usr/bin/ 10 | ADD . /refstack 11 | 12 | RUN apt update -y \ 13 | && apt upgrade -y 14 | 15 | RUN apt install -y curl \ 16 | sudo \ 17 | && groupadd dev \ 18 | && useradd -g dev -s /bin/bash -d /home/dev -m dev \ 19 | && ( umask 226 && echo "dev ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/50_dev ) \ 20 | && curl -sL https://deb.nodesource.com/setup_8.x -o /tmp/setup_8.x.sh \ 21 | && sudo bash /tmp/setup_8.x.sh \ 22 | && apt install -y git \ 23 | libffi-dev \ 24 | libmysqlclient-dev \ 25 | mysql-client \ 26 | mysql-server \ 27 | nginx \ 28 | nodejs \ 29 | python-dev \ 30 | python-pip \ 31 | python3-dev \ 32 | sudo \ 33 | vim \ 34 | wget \ 35 | && rm -rf /var/lib/apt/lists/* \ 36 | && rm -rf /var/lib/mysql/* \ 37 | && rm -rf /etc/nginx/sites-enabled/default \ 38 | && npm install -g yarn \ 39 | && pip install virtualenv tox httpie 40 | 41 | USER dev 42 | 43 | RUN echo "cd /home/dev/refstack" >> /home/dev/.bashrc \ 44 | && echo "alias activate='source /home/dev/refstack/.venv/bin/activate'" >> /home/dev/.bashrc \ 45 | && echo "alias mysql='mysql --no-defaults -S ${SQL_DIR}/mysql.socket'" >> /home/dev/.bashrc \ 46 | && start.sh \ 47 | && api-init-db 48 | 49 | CMD start.sh -s 50 | -------------------------------------------------------------------------------- /docker/nginx/refstack-site.conf: -------------------------------------------------------------------------------- 1 | server { 2 | proxy_connect_timeout 600; 3 | proxy_send_timeout 600; 4 | proxy_read_timeout 600; 5 | send_timeout 600; 6 | server_name 127.0.0.1; 7 | listen 443 ssl; 8 | 9 | ssl on; 10 | ssl_certificate /etc/nginx/certificates/refstack_dev.crt; 11 | ssl_certificate_key /etc/nginx/certificates/refstack_dev.key; 12 | ssl_protocols TLSv1.1 TLSv1.2; 13 | ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AES:RSA+3DES:!ADH:!AECDH:!MD5:!DSS:!RC4; 14 | ssl_prefer_server_ciphers on; 15 | ssl_session_timeout 5m; 16 | location / { 17 | access_log off; 18 | proxy_pass http://127.0.0.1:8000; 19 | proxy_set_header X-Real-IP $remote_addr; 20 | proxy_set_header Host $host; 21 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 22 | } 23 | } -------------------------------------------------------------------------------- /docker/nginx/refstack_dev.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICpzCCAhACCQCjqL+hsTaQ6zANBgkqhkiG9w0BAQsFADCBlzELMAkGA1UEBhMC 3 | VUExEDAOBgNVBAgMB0toYXJraXYxEDAOBgNVBAcMB0toYXJraXYxHTAbBgNVBAoM 4 | FE9wZW5zdGFjayBGb3VuZGF0aW9uMRswGQYDVQQDDBJyZWZzdGFjay5sb2NhbC5v 5 | cmcxKDAmBgkqhkiG9w0BCQEWGXNzbHlwdXNoZW5rb0BtaXJhbnRpcy5jb20wHhcN 6 | MTUwNTIxMTYyNDQ1WhcNMjUwNTE4MTYyNDQ1WjCBlzELMAkGA1UEBhMCVUExEDAO 7 | BgNVBAgMB0toYXJraXYxEDAOBgNVBAcMB0toYXJraXYxHTAbBgNVBAoMFE9wZW5z 8 | dGFjayBGb3VuZGF0aW9uMRswGQYDVQQDDBJyZWZzdGFjay5sb2NhbC5vcmcxKDAm 9 | BgkqhkiG9w0BCQEWGXNzbHlwdXNoZW5rb0BtaXJhbnRpcy5jb20wgZ8wDQYJKoZI 10 | hvcNAQEBBQADgY0AMIGJAoGBAMKfF4KpXa+/Ju5SM/oEuEKxffXh6WnA/eG4FQoP 11 | 1JMAKKn4wIsn4umKDHebKBDDIT/nlEuDQC9+dour1UxFhba8kJGh5QCmtp+qiWXj 12 | H2f+U0RwBacHgjZoNOqJ+V88PW949IhD91v/lDYmDNtVUHt7BJw7nrnd5MLJAmBe 13 | 3S15AgMBAAEwDQYJKoZIhvcNAQELBQADgYEAU0WxG2amQsEv8qq4Vgps4zUTtnec 14 | Vr6KMU06IrvNF0MEODJhasoQFmr2J6dy90abSqPPEdW76cxi1J6wtIEtNvW81elS 15 | 9OvdKwL+BoPFo+4G2VvT5Fj8DEl8goyIRGiK7+gpflS4jDRX57DVujgpVd5Omu7L 16 | 7F+OgXFZceBNBJw= 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /docker/nginx/refstack_dev.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICWwIBAAKBgQDCnxeCqV2vvybuUjP6BLhCsX314elpwP3huBUKD9STACip+MCL 3 | J+Lpigx3mygQwyE/55RLg0AvfnaLq9VMRYW2vJCRoeUAprafqoll4x9n/lNEcAWn 4 | B4I2aDTqiflfPD1vePSIQ/db/5Q2JgzbVVB7ewScO5653eTCyQJgXt0teQIDAQAB 5 | AoGAaPWDqGPOsslUJZMPlPaWqOEwHTsIto/uW5z7O8Ht0plzVLdin6mTJn/c2WRD 6 | 50ZU2DH8N/1A0FxTcl/pWIjl4wZPDOQ3W8EVcQ30gqV1vunXOi5jDGulCv0nsDXK 7 | YifHxRDehr+ND20IqsQFv+k4PBBTcOMJ+7YpM+DrLubNAkECQQDmOsKF1jumAMP9 8 | CIkJ8wzXIzAk07w4QXLK1DMoSQVHI0Zz0KjCyJNNbR1w5J7c3QD4KWbIt/PSWuz/ 9 | L+/G6YTjAkEA2Gf7AhFRv1cLg/l/1SwXtVb9MOh7Gf27XuTZeKyV202Fq0y6FhK/ 10 | AQPPQfWQYcsrLkKwegIERtaY34ALLQPu8wJAUBsz4cOH35u2lc0peXfDCPwqXTX6 11 | 8Iv9OAubfTHjDzx74AJDJfsKHc+Qhd5WVDzlgHNPWxl+UbvnaGcyg8BuxwJAXVAA 12 | wPR83leHRKH5yA6aLnxS8prcMenhuFpPl6Q7ffOgdqu/9bKhn6tn3BYp6rEzbmAd 13 | Po7OD0mLY5wPtZpjlwJAShmD4/1gjmV1aYAxQs6gPDDCr5oycn7jyta59gcwKdAv 14 | zO5eKW1jMd+gg4jk3TiuLECdorUoGGbvIxEeP1gGBA== 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /docker/scripts/api-db-version: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | /home/dev/refstack/.venv/bin/python /home/dev/refstack/bin/refstack-manage --log-file /dev/null version 2>/dev/null 3 | -------------------------------------------------------------------------------- /docker/scripts/api-init-db: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | [[ ${DEBUG_MODE} ]] && set -x 3 | mysql --no-defaults -S ${SQL_DIR}/mysql.socket -e 'CREATE DATABASE refstack;' 4 | mysql --no-defaults -S ${SQL_DIR}/mysql.socket -e 'set @@global.show_compatibility_56=ON;' 5 | 6 | cd /home/dev/refstack 7 | .venv/bin/python bin/refstack-manage upgrade --revision head 8 | -------------------------------------------------------------------------------- /docker/scripts/api-sync: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | [[ ${DEBUG_MODE} ]] && set -x 3 | echo "Syncing project files..." 4 | rsync -avr /refstack /home/dev --exclude-from '/refstack/.gitignore' > /dev/null && \ 5 | echo "Done!" -------------------------------------------------------------------------------- /docker/scripts/api-up: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | [[ ${DEBUG_MODE} ]] && set -x 3 | api-sync 4 | cd /home/dev/refstack 5 | .venv/bin/pecan serve refstack/api/config.py 6 | -------------------------------------------------------------------------------- /docker/scripts/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | wait_for_line () { 4 | while read line; do 5 | echo "$line" | grep -q "$1" && break 6 | done < "$2" 7 | # Read the fifo for ever otherwise process would block 8 | cat "$2" >/dev/null & 9 | } 10 | 11 | build_tmpl () { 12 | TEMPLATE="$1" 13 | TARGET="$2" 14 | cat ${TEMPLATE} | \ 15 | while read LINE; do 16 | NEWLINE=$(eval echo ${LINE}) 17 | [[ ! -z "$NEWLINE" ]] && echo ${NEWLINE} 18 | done > ${TARGET} 19 | } 20 | 21 | start_mysql () { 22 | # Start MySQL process for tests 23 | [ ! -d ${SQL_DIR} ] && mkdir ${SQL_DIR} 24 | sudo chown dev:dev ${SQL_DIR} 25 | rm -rf ${SQL_DIR}/out && mkfifo ${SQL_DIR}/out 26 | rm -rf ${SQL_DIR}/mysql.socket 27 | # On systems like Fedora here's where mysqld can be found 28 | PATH=$PATH:/usr/libexec 29 | mysqld --no-defaults --datadir=${SQL_DIR} --pid-file=${SQL_DIR}/mysql.pid \ 30 | --socket=${SQL_DIR}/mysql.socket --skip-networking \ 31 | --skip-grant-tables &> ${SQL_DIR}/out & 32 | # Wait for MySQL to start listening to connections 33 | wait_for_line "mysqld: ready for connections." ${SQL_DIR}/out 34 | } 35 | 36 | build_refstack_env () { 37 | api-sync 38 | cd /home/dev/refstack 39 | [ ! -d .venv ] && virtualenv .venv 40 | .venv/bin/pip install -r requirements.txt 41 | #Install some dev tools 42 | .venv/bin/pip install pymysql httpie 43 | cd /home/dev/refstack 44 | yarn 45 | 46 | build_tmpl /refstack/docker/templates/config.json.tmpl /home/dev/refstack/refstack-ui/app/config.json 47 | build_tmpl /refstack/docker/templates/refstack.conf.tmpl /home/dev/refstack.conf 48 | sudo cp /home/dev/refstack.conf /etc 49 | } 50 | 51 | start_nginx () { 52 | [ ! -d /etc/nginx/certificates ] && sudo mkdir /etc/nginx/certificates 53 | sudo cp /refstack/docker/nginx/refstack_dev.key /etc/nginx/certificates 54 | sudo cp /refstack/docker/nginx/refstack_dev.crt /etc/nginx/certificates 55 | sudo cp /refstack/docker/nginx/refstack-site.conf /etc/nginx/sites-enabled/ 56 | sudo nginx 57 | } 58 | 59 | while getopts ":s" opt; do 60 | case ${opt} in 61 | s) SLEEP=true;; 62 | esac 63 | done 64 | 65 | [[ ${DEBUG_MODE} ]] && set -x 66 | 67 | touch /tmp/is-not-ready 68 | 69 | start_mysql 70 | start_nginx 71 | build_refstack_env 72 | 73 | rm -rf /tmp/is-not-ready 74 | 75 | if [[ ${SLEEP} ]]; then 76 | set +x 77 | sleep 1024d 78 | fi 79 | -------------------------------------------------------------------------------- /docker/templates/config.json.tmpl: -------------------------------------------------------------------------------- 1 | {\\"refstackApiUrl\\": \\"https://${REFSTACK_HOST:-127.0.0.1}/v1\\"} 2 | -------------------------------------------------------------------------------- /docker/templates/refstack.conf.tmpl: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | debug = true 3 | verbose = true 4 | ui_url = https://${REFSTACK_HOST:-127.0.0.1} 5 | 6 | [api] 7 | static_root = /home/dev/refstack/refstack-ui/app 8 | template_path = /home/dev/refstack/refstack-ui/app 9 | app_dev_mode = true 10 | api_url = https://${REFSTACK_HOST:-127.0.0.1} 11 | 12 | [database] 13 | connection = ${REFSTACK_MYSQL_URL} 14 | 15 | [osid] 16 | openstack_openid_endpoint = https://172.17.42.1:8443/accounts/openid2 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.5.0", 3 | "private": true, 4 | "name": "refstack-ui", 5 | "description": "A user interface for RefStack", 6 | "license": "Apache2", 7 | "devDependencies": { 8 | "eslint": "^3.0.0", 9 | "eslint-config-openstack": "4.0.1", 10 | "eslint-plugin-angular": "1.4.0", 11 | "jasmine-core": "2.8.0", 12 | "karma": "^1.7.1", 13 | "karma-chrome-launcher": "^2.2.0", 14 | "karma-cli": "1.0.1", 15 | "karma-jasmine": "^1.1.0", 16 | "angular-mocks": "^1.3.15" 17 | }, 18 | "scripts": { 19 | "prestart": "yarn", 20 | "pretest": "yarn", 21 | "test": "if [ -z $CHROME_BIN ];then export CHROME_BIN=/usr/bin/chromium-browser;fi && karma start ./refstack-ui/tests/karma.conf.js --single-run", 22 | "test-auto-watch": "if [ -z $CHROME_BIN ];then export CHROME_BIN=/usr/bin/chromium-browser;fi && karma start ./refstack-ui/tests/karma.conf.js --auto-watch", 23 | "lint": "eslint -c ./.eslintrc --no-color ./refstack-ui", 24 | "postinstall": "node -e \"try { require('fs').symlinkSync(require('path').resolve('node_modules/@bower_components'), 'refstack-ui/app/assets/lib', 'junction') } catch (e) { }\"" 25 | }, 26 | "dependencies": { 27 | "bower-away": "^1.1.2", 28 | "@bower_components/angular": "angular/bower-angular#1.3.15", 29 | "@bower_components/angular-animate": "angular/bower-angular-animate#~1.3", 30 | "@bower_components/angular-bootstrap": "angular-ui/bootstrap-bower#0.14.3", 31 | "@bower_components/angular-busy": "cgross/angular-busy#4.1.3", 32 | "@bower_components/angular-confirm-modal": "Schlogen/angular-confirm#1.2.3", 33 | "@bower_components/angular-mocks": "angular/bower-angular-mocks#1.3.15", 34 | "@bower_components/angular-resource": "angular/bower-angular-resource#1.3.15", 35 | "@bower_components/angular-ui-router": "angular-ui/angular-ui-router-bower#0.2.13", 36 | "@bower_components/bootstrap": "twbs/bootstrap#3.3.2", 37 | "@bower_components/jquery": "jquery/jquery-dist#>= 1.9.1" 38 | }, 39 | "engines": { 40 | "yarn": ">= 1.0.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /refstack-ui/app/assets/img/OpenStack_Project_Refstack_mascot_90x90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack-archive/refstack/0af3a46e16037d13695ef74d35d2ef5909186d97/refstack-ui/app/assets/img/OpenStack_Project_Refstack_mascot_90x90.png -------------------------------------------------------------------------------- /refstack-ui/app/assets/img/openstack-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack-archive/refstack/0af3a46e16037d13695ef74d35d2ef5909186d97/refstack-ui/app/assets/img/openstack-logo.png -------------------------------------------------------------------------------- /refstack-ui/app/assets/img/refstack-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack-archive/refstack/0af3a46e16037d13695ef74d35d2ef5909186d97/refstack-ui/app/assets/img/refstack-logo.png -------------------------------------------------------------------------------- /refstack-ui/app/components/about/about.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
7 | {{data.title}} 8 |
9 |
10 |
11 |
12 |
13 |
14 | -------------------------------------------------------------------------------- /refstack-ui/app/components/about/aboutController.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | (function () { 16 | 'use strict'; 17 | 18 | angular 19 | .module('refstackApp') 20 | .controller('AboutController', AboutController); 21 | 22 | AboutController.$inject = ['$location']; 23 | 24 | /** 25 | * RefStack About Controller 26 | * This controller handles the about page and the multiple templates 27 | * associated to the page. 28 | */ 29 | function AboutController($location) { 30 | var ctrl = this; 31 | 32 | ctrl.selectOption = selectOption; 33 | ctrl.getHash = getHash; 34 | 35 | ctrl.options = { 36 | 'about' : { 37 | 'title': 'About RefStack', 38 | 'template': 'components/about/templates/README.html', 39 | 'order': 1 40 | }, 41 | 'uploading-your-results': { 42 | 'title': 'Uploading Your Results', 43 | 'template': 'components/about/templates/' + 44 | 'uploading_private_results.html', 45 | 'order': 2 46 | }, 47 | 'managing-results': { 48 | 'title': 'Managing Results', 49 | 'template': 'components/about/templates/' + 50 | 'test_result_management.html', 51 | 'order': 3 52 | }, 53 | 'vendors-and-products': { 54 | 'title': 'Vendors and Products', 55 | 'template': 'components/about/templates/vendor_product.html', 56 | 'order': 4 57 | } 58 | }; 59 | 60 | /** 61 | * Given an option key, mark it as selected and set the corresponding 62 | * template and URL hash. 63 | */ 64 | function selectOption(key) { 65 | ctrl.selected = key; 66 | ctrl.template = ctrl.options[key].template; 67 | $location.hash(key); 68 | } 69 | 70 | /** 71 | * Get the hash in the URL and select it if possible. 72 | */ 73 | function getHash() { 74 | var hash = $location.hash(); 75 | if (hash && hash in ctrl.options) { 76 | ctrl.selectOption(hash); 77 | } else { 78 | ctrl.selectOption('about'); 79 | } 80 | } 81 | 82 | ctrl.getHash(); 83 | } 84 | })(); 85 | -------------------------------------------------------------------------------- /refstack-ui/app/components/auth-failure/authFailureController.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | (function () { 16 | 'use strict'; 17 | 18 | angular 19 | .module('refstackApp') 20 | .controller('AuthFailureController', AuthFailureController); 21 | 22 | AuthFailureController.$inject = ['$location', '$state', 'raiseAlert']; 23 | /** 24 | * Refstack Auth Failure Controller 25 | * This controller handles messages from Refstack API if user auth fails. 26 | */ 27 | function AuthFailureController($location, $state, raiseAlert) { 28 | var ctrl = this; 29 | ctrl.message = $location.search().message; 30 | raiseAlert('danger', 'Authentication Failure:', ctrl.message); 31 | $state.go('home'); 32 | } 33 | })(); 34 | -------------------------------------------------------------------------------- /refstack-ui/app/components/guidelines/guidelines.html: -------------------------------------------------------------------------------- 1 |

OpenStack Powered™ Guidelines

2 | 3 | 4 |
5 |
6 | Version: 7 | 8 | 13 |
14 |
15 | Target Program: 16 | About 17 | 24 |
25 |
26 | 27 |
28 |
29 | Guideline Status: 30 | {{ctrl.guidelineStatus | capitalize}} 31 |
32 | 33 |
34 | Corresponding OpenStack Releases: 35 | 40 |
41 | 42 | Capability Status: 43 |
44 | 48 | 52 | 56 | 60 | 63 | 64 | Test List 65 | 66 |
67 | 68 | 69 |

Tests marked with are tests flagged by Interop Working Group.

70 | 71 | 72 |
73 |
74 | 75 | 76 |
77 | 78 | 83 | -------------------------------------------------------------------------------- /refstack-ui/app/components/guidelines/partials/guidelineDetails.html: -------------------------------------------------------------------------------- 1 | 6 | 7 |
    8 |
  1. 9 | {{capability.id}}
    10 | {{capability.description}}
    11 | Status: {{ctrl.targetCapabilities[capability.id]}}
    12 | Project: {{capability.project | capitalize}}
    13 | Achievements ({{capability.achievements.length}})
    14 |
      15 |
    1. 16 | {{achievement}} 17 |
    2. 18 |
    19 | 20 | Tests ({{ctrl.getObjectLength(capability.tests)}}) 21 |
      22 |
    • 23 | 24 | {{test}} 25 |
    • 26 |
    • 27 | 28 | {{testName}} 29 |
      30 | Aliases: 31 |
      • {{alias}}
      32 |
      33 |
    • 34 |
    35 |
  2. 36 |
37 | 38 |
39 |
40 |

Criteria

41 |
42 |
    43 |
  • 44 | {{criterion.name}}
    45 | {{criterion.Description || criterion.description}}
    46 | Weight: {{criterion.weight}} 47 |
  • 48 |
49 |
50 |
51 | -------------------------------------------------------------------------------- /refstack-ui/app/components/guidelines/partials/testListModal.html: -------------------------------------------------------------------------------- 1 | 47 | -------------------------------------------------------------------------------- /refstack-ui/app/components/home/home.html: -------------------------------------------------------------------------------- 1 |
2 | 5 |
6 |

OpenStack Interoperability

7 |

RefStack is a source of tools for 8 | OpenStack interoperability 9 | testing.

10 |
11 |
12 |
13 | 14 |
15 |
16 |

What is RefStack?

17 |
    18 |
  • Toolset for testing interoperability between OpenStack clouds.
  • 19 |
  • Database backed website supporting collection and publication of 20 | community test results for OpenStack.
  • 21 |
  • User interface to display individual test run results.
  • 22 |
23 |
24 | 25 |
26 |

OpenStack Marketing Programs

27 |
    28 |
  • OpenStack Powered Platform
  • 29 |
  • OpenStack Powered Compute
  • 30 |
  • OpenStack Powered Object Storage
  • 31 |
  • OpenStack with DNS
  • 32 |
  • OpenStack with Orchestration
  • 33 |
34 |
35 |
36 | -------------------------------------------------------------------------------- /refstack-ui/app/components/logout/logout.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /refstack-ui/app/components/logout/logoutController.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | (function () { 16 | 'use strict'; 17 | 18 | angular 19 | .module('refstackApp') 20 | .controller('LogoutController', LogoutController); 21 | 22 | LogoutController.$inject = [ 23 | '$location', '$window', '$timeout' 24 | ]; 25 | 26 | /** 27 | * Refstack Logout Controller 28 | * This controller handles logging out. In order to fully logout, the 29 | * openstackid_session cookie must also be removed. The way to do that 30 | * is to have the user's browser make a request to the openstackid logout 31 | * page. We do this by placing the logout link as the src for an html 32 | * image. After some time, the user is redirected home. 33 | */ 34 | function LogoutController($location, $window, $timeout) { 35 | var ctrl = this; 36 | 37 | ctrl.openid_logout_url = $location.search().openid_logout; 38 | var img = new Image(0, 0); 39 | img.src = ctrl.openid_logout_url; 40 | ctrl.redirectWait = $timeout(function() { 41 | $window.location.href = '/'; 42 | }, 500); 43 | } 44 | })(); 45 | -------------------------------------------------------------------------------- /refstack-ui/app/components/products/cloud.html: -------------------------------------------------------------------------------- 1 |

Cloud Product

2 |
3 |
4 |
5 |
6 |
7 | Name: {{ctrl.product.name}}
8 | Product ID: {{ctrl.id}}
9 | Description: {{ctrl.product.description}}
10 | CPID: {{ctrl.nullVersion.cpid}}
11 | Publicity: {{ctrl.product.public ? 'Public' : 'Private'}}
12 | Vendor Name: {{ctrl.vendor.name}}
13 |
14 | Properties: 15 |
    16 |
  • 17 | {{key}}: {{value}} 18 |
  • 19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | 35 | -------------------------------------------------------------------------------- /refstack-ui/app/components/products/distro.html: -------------------------------------------------------------------------------- 1 |

Distro Product

2 |
3 |
4 |
5 |
6 |
7 | Name: {{ctrl.product.name}}
8 | Product ID: {{ctrl.id}}
9 | Description: {{ctrl.product.description}}
10 | CPID: {{ctrl.nullVersion.cpid}}
11 | Publicity: {{ctrl.product.public ? 'Public' : 'Private'}}
12 | Vendor Name: {{ctrl.vendor.name}}
13 |
14 | Properties: 15 |
    16 |
  • 17 | {{key}}: {{value}} 18 |
  • 19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | 35 | -------------------------------------------------------------------------------- /refstack-ui/app/components/products/partials/management.html: -------------------------------------------------------------------------------- 1 |
2 | 5 | Edit 6 |
7 | 11 | Delete 12 |
13 | 16 | Make Product PrivatePublic 17 |
18 |
19 | -------------------------------------------------------------------------------- /refstack-ui/app/components/products/partials/productEditModal.html: -------------------------------------------------------------------------------- 1 | 76 | -------------------------------------------------------------------------------- /refstack-ui/app/components/products/partials/versions.html: -------------------------------------------------------------------------------- 1 | Version(s) Available: 2 | 3 | 4 | {{item.version}} 5 | 6 | {{item.version}} 7 | 8 |   9 | 12 | 13 | 14 |
15 |
16 |
17 | 19 | 20 | 26 | 27 |
28 |
29 |
30 | -------------------------------------------------------------------------------- /refstack-ui/app/components/products/partials/versionsModal.html: -------------------------------------------------------------------------------- 1 | 52 | -------------------------------------------------------------------------------- /refstack-ui/app/components/products/products.html: -------------------------------------------------------------------------------- 1 |

{{ctrl.pageHeader}}

2 |

{{ctrl.pageParagraph}}

3 | 4 |
5 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
NameProduct TypeDescriptionVendorVisibility
{{product.name}}{{product.name}}{{product.name}}{{ctrl.getProductTypeDescription(product.product_type)}}{{product.description}}{{ctrl.allVendors[product.organization_id].name}}{{product.public ? 'Public' : 'Private'}}
32 |
33 | 34 |
35 |
36 |

Add New Product

37 |
38 |
39 | 40 |

41 | 42 |

43 |
44 |
45 | 46 |

47 | 49 |

50 |
51 |
52 | 53 | 58 |
59 |
60 | 61 | 64 |
65 |
66 | 67 |
68 |
69 |
70 | 71 | Success: 72 | Product successfully created. 73 |
74 |
75 |
76 | 77 |
78 |
79 |
80 | 81 | 86 | -------------------------------------------------------------------------------- /refstack-ui/app/components/profile/importPubKeyModal.html: -------------------------------------------------------------------------------- 1 | 10 | 28 | -------------------------------------------------------------------------------- /refstack-ui/app/components/profile/profile.html: -------------------------------------------------------------------------------- 1 |

User profile

2 |
3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
User name {{auth.currentUser.fullname}}
User OpenId {{auth.currentUser.openid}}
Email {{auth.currentUser.email}}
11 |
12 |
13 |
14 |
15 |
16 |

User Public Keys

17 |
18 |
19 | 22 |
23 |
24 |
25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
{{pubKey.format}}{{pubKey.shortKey}}{{pubKey.comment}}
36 |
37 |
38 | -------------------------------------------------------------------------------- /refstack-ui/app/components/profile/showPubKeyModal.html: -------------------------------------------------------------------------------- 1 | 4 | 12 | -------------------------------------------------------------------------------- /refstack-ui/app/components/results-report/partials/editTestModal.html: -------------------------------------------------------------------------------- 1 | 68 | -------------------------------------------------------------------------------- /refstack-ui/app/components/results-report/partials/fullTestListModal.html: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /refstack-ui/app/components/vendors/partials/vendorEditModal.html: -------------------------------------------------------------------------------- 1 | 63 | -------------------------------------------------------------------------------- /refstack-ui/app/components/vendors/vendors.html: -------------------------------------------------------------------------------- 1 |

{{ctrl.pageHeader}}

2 |

{{ctrl.pageParagraph}}

3 | 4 |
5 |
6 | 7 |
8 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 |
NameDescriptionType
{{vendor.name}}{{vendor.name}}{{vendor.description || '-'}} 26 |  OpenStack 27 |  Private 28 |  Pending Approval 29 |  Official 30 |
34 |
35 | 36 |
37 |
38 |

Add New Vendor

39 |

Creating a vendor allows you to associate test results to specific vendors/products. 40 | Created vendors are private, but vendors can be registered with the Foundation to become public and official. 41 | This will require approval by a Foundation administrator.

42 |
43 |
44 | 45 |

46 | 48 |

49 |
50 |
51 | 52 |

53 | 55 |

56 |
57 |
58 | 59 |
60 |
61 |
62 | 63 | Success: 64 | Vendor successfully created. 65 |
66 |
67 |
68 | 69 |
70 | 71 | 76 | -------------------------------------------------------------------------------- /refstack-ui/app/config.json.sample: -------------------------------------------------------------------------------- 1 | {"refstackApiUrl": "https://refstack.openstack.org/api/v1"} 2 | -------------------------------------------------------------------------------- /refstack-ui/app/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack-archive/refstack/0af3a46e16037d13695ef74d35d2ef5909186d97/refstack-ui/app/favicon-16x16.png -------------------------------------------------------------------------------- /refstack-ui/app/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack-archive/refstack/0af3a46e16037d13695ef74d35d2ef5909186d97/refstack-ui/app/favicon-32x32.png -------------------------------------------------------------------------------- /refstack-ui/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack-archive/refstack/0af3a46e16037d13695ef74d35d2ef5909186d97/refstack-ui/app/favicon.ico -------------------------------------------------------------------------------- /refstack-ui/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | Refstack 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
62 | 63 |
64 | 65 | 66 | -------------------------------------------------------------------------------- /refstack-ui/app/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | 5 | -------------------------------------------------------------------------------- /refstack-ui/app/shared/alerts/alertModal.html: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /refstack-ui/app/shared/alerts/alertModalFactory.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | (function () { 16 | 'use strict'; 17 | 18 | angular 19 | .module('refstackApp') 20 | .factory('raiseAlert', raiseAlert); 21 | 22 | raiseAlert.$inject = ['$uibModal']; 23 | 24 | /** 25 | * This allows alert pop-ups to be raised. Just inject it as a dependency 26 | * in the calling controller. 27 | */ 28 | function raiseAlert($uibModal) { 29 | return function(mode, title, text) { 30 | $uibModal.open({ 31 | templateUrl: '/shared/alerts/alertModal.html', 32 | controller: 'RaiseAlertModalController as alert', 33 | backdrop: true, 34 | keyboard: true, 35 | backdropClick: true, 36 | size: 'md', 37 | resolve: { 38 | data: function () { 39 | return { 40 | mode: mode, 41 | title: title, 42 | text: text 43 | }; 44 | } 45 | } 46 | }); 47 | }; 48 | } 49 | 50 | angular 51 | .module('refstackApp') 52 | .controller('RaiseAlertModalController', RaiseAlertModalController); 53 | 54 | RaiseAlertModalController.$inject = ['$uibModalInstance', 'data']; 55 | 56 | /** 57 | * This is the controller for the alert pop-up. 58 | */ 59 | function RaiseAlertModalController($uibModalInstance, data) { 60 | var ctrl = this; 61 | 62 | ctrl.close = close; 63 | ctrl.data = data; 64 | 65 | /** 66 | * This method will close the alert modal. The modal will close 67 | * when the user clicks the close button or clicks outside of the 68 | * modal. 69 | */ 70 | function close() { 71 | $uibModalInstance.close(); 72 | } 73 | } 74 | })(); 75 | -------------------------------------------------------------------------------- /refstack-ui/app/shared/alerts/confirmModal.html: -------------------------------------------------------------------------------- 1 | 2 | 10 | 14 | -------------------------------------------------------------------------------- /refstack-ui/app/shared/alerts/confirmModalFactory.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular 5 | .module('refstackApp') 6 | .factory('confirmModal', confirmModal); 7 | 8 | confirmModal.$inject = ['$uibModal']; 9 | 10 | /** 11 | * Opens confirm modal dialog with input textbox 12 | */ 13 | function confirmModal($uibModal) { 14 | return function(text, successHandler) { 15 | $uibModal.open({ 16 | templateUrl: '/shared/alerts/confirmModal.html', 17 | controller: 'CustomConfirmModalController as confirmModal', 18 | size: 'md', 19 | resolve: { 20 | data: function () { 21 | return { 22 | text: text, 23 | successHandler: successHandler 24 | }; 25 | } 26 | } 27 | }); 28 | }; 29 | } 30 | 31 | angular 32 | .module('refstackApp') 33 | .controller('CustomConfirmModalController', 34 | CustomConfirmModalController); 35 | 36 | CustomConfirmModalController.$inject = ['$uibModalInstance', 'data']; 37 | 38 | /** 39 | * This is the controller for the alert pop-up. 40 | */ 41 | function CustomConfirmModalController($uibModalInstance, data) { 42 | var ctrl = this; 43 | 44 | ctrl.confirm = confirm; 45 | ctrl.cancel = cancel; 46 | 47 | ctrl.data = angular.copy(data); 48 | 49 | /** 50 | * Initiate confirmation and call the success handler with the 51 | * input text. 52 | */ 53 | function confirm() { 54 | $uibModalInstance.close(); 55 | if (angular.isDefined(ctrl.data.successHandler)) { 56 | ctrl.data.successHandler(ctrl.inputText); 57 | } 58 | } 59 | 60 | /** 61 | * Close the confirm modal without initiating changes. 62 | */ 63 | function cancel() { 64 | $uibModalInstance.dismiss('cancel'); 65 | } 66 | } 67 | })(); 68 | -------------------------------------------------------------------------------- /refstack-ui/app/shared/filters.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | (function () { 16 | 'use strict'; 17 | 18 | /** 19 | * Convert an object of objects to an array of objects to use with ng-repeat 20 | * filters. 21 | */ 22 | angular 23 | .module('refstackApp') 24 | .filter('arrayConverter', arrayConverter); 25 | 26 | /** 27 | * Convert an object of objects to an array of objects to use with ng-repeat 28 | * filters. 29 | */ 30 | function arrayConverter() { 31 | return function (objects) { 32 | var array = []; 33 | angular.forEach(objects, function (object, key) { 34 | if (!('id' in object)) { 35 | object.id = key; 36 | } 37 | array.push(object); 38 | }); 39 | return array; 40 | }; 41 | } 42 | 43 | angular 44 | .module('refstackApp') 45 | .filter('capitalize', capitalize); 46 | 47 | /** 48 | * Angular filter that will capitalize the first letter of a string. 49 | */ 50 | function capitalize() { 51 | return function (string) { 52 | return string.substring(0, 1).toUpperCase() + string.substring(1); 53 | }; 54 | } 55 | })(); 56 | -------------------------------------------------------------------------------- /refstack-ui/app/shared/header/header.html: -------------------------------------------------------------------------------- 1 |
RefStack 2 | RefStack 3 |
4 | 52 | -------------------------------------------------------------------------------- /refstack-ui/app/shared/header/headerController.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | (function () { 16 | 'use strict'; 17 | 18 | angular 19 | .module('refstackApp') 20 | .controller('HeaderController', HeaderController); 21 | 22 | HeaderController.$inject = ['$location']; 23 | 24 | /** 25 | * Refstack Header Controller 26 | * This controller is for the header template which contains the site 27 | * navigation. 28 | */ 29 | function HeaderController($location) { 30 | var ctrl = this; 31 | 32 | ctrl.isActive = isActive; 33 | ctrl.isCatalogActive = isCatalogActive; 34 | 35 | /** Whether the Navbar is collapsed for small displays. */ 36 | ctrl.navbarCollapsed = true; 37 | 38 | /** 39 | * This determines whether a button should be in the active state based 40 | * on the URL. 41 | */ 42 | function isActive(viewLocation) { 43 | var path = $location.path().substr(0, viewLocation.length); 44 | if (path === viewLocation) { 45 | // Make sure "/" only matches when viewLocation is "/". 46 | if (!($location.path().substr(0).length > 1 && 47 | viewLocation.length === 1)) { 48 | return true; 49 | } 50 | } 51 | return false; 52 | } 53 | 54 | /** This determines the active state for the catalog dropdown. Type 55 | * parameter should be passed in to specify if the catalog is the 56 | * public or user one. 57 | */ 58 | function isCatalogActive(type) { 59 | return ctrl.isActive('/' + type + '_vendors') 60 | || ctrl.isActive('/' + type + '_products'); 61 | } 62 | } 63 | })(); 64 | -------------------------------------------------------------------------------- /refstack-ui/tests/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function (config) { 2 | 'use strict'; 3 | 4 | config.set({ 5 | 6 | basePath: '../', 7 | 8 | files: [ 9 | // Angular libraries. 10 | 'app/assets/lib/angular/angular.js', 11 | 'app/assets/lib/angular-ui-router/release/angular-ui-router.js', 12 | 'app/assets/lib/angular-mocks/angular-mocks.js', 13 | 'app/assets/lib/angular-bootstrap/ui-bootstrap-tpls.min.js', 14 | 'app/assets/lib/angular-busy/dist/angular-busy.min.js', 15 | 'app/assets/lib/angular-resource/angular-resource.min.js', 16 | 'app/assets/lib/angular-confirm-modal/angular-confirm.js', 17 | // JS files. 18 | 'app/app.js', 19 | 'app/components/**/*.js', 20 | 'app/shared/*.js', 21 | 'app/shared/**/*.js', 22 | 23 | // Test Specs. 24 | 'tests/unit/*.js' 25 | ], 26 | 27 | autoWatch: true, 28 | 29 | frameworks: ['jasmine'], 30 | 31 | browsers: ['Chrome'], 32 | 33 | plugins: [ 34 | 'karma-chrome-launcher', 35 | 'karma-jasmine' 36 | ], 37 | 38 | junitReporter: { 39 | outputFile: 'test_out/unit.xml', 40 | suite: 'unit' 41 | } 42 | 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /refstack-ui/tests/unit/AuthSpec.js: -------------------------------------------------------------------------------- 1 | describe('Auth', function () { 2 | 'use strict'; 3 | 4 | var fakeApiUrl = 'http://foo.bar/v1'; 5 | var $window, $rootScope, $httpBackend; 6 | beforeEach(function () { 7 | $window = {location: { href: jasmine.createSpy()} }; 8 | module(function ($provide) { 9 | $provide.constant('refstackApiUrl', fakeApiUrl); 10 | $provide.value('$window', $window); 11 | }); 12 | module('refstackApp'); 13 | inject(function (_$httpBackend_, _$rootScope_) { 14 | $httpBackend = _$httpBackend_; 15 | $rootScope = _$rootScope_; 16 | }); 17 | $httpBackend.whenGET('/components/home/home.html') 18 | .respond('
mock template
'); 19 | }); 20 | it('should show signin url for signed user', function () { 21 | $httpBackend.expectGET(fakeApiUrl + 22 | '/profile').respond({'openid': 'foo@bar.com', 23 | 'email': 'foo@bar.com', 24 | 'fullname': 'foo' }); 25 | $httpBackend.flush(); 26 | $rootScope.auth.doSignIn(); 27 | expect($window.location.href).toBe(fakeApiUrl + '/auth/signin'); 28 | expect($rootScope.auth.isAuthenticated).toBe(true); 29 | }); 30 | 31 | it('should show signout url for not signed user', function () { 32 | $httpBackend.expectGET(fakeApiUrl + 33 | '/profile').respond(401); 34 | $httpBackend.flush(); 35 | $rootScope.auth.doSignOut(); 36 | expect($window.location.href).toBe(fakeApiUrl + '/auth/signout'); 37 | expect($rootScope.auth.isAuthenticated).toBe(false); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /refstack-ui/tests/unit/FilterSpec.js: -------------------------------------------------------------------------------- 1 | /** Jasmine specs for Refstack filters */ 2 | describe('Refstack filters', function () { 3 | 'use strict'; 4 | 5 | var fakeApiUrl = 'http://foo.bar/v1'; 6 | beforeEach(function () { 7 | module(function ($provide) { 8 | $provide.constant('refstackApiUrl', fakeApiUrl); 9 | }); 10 | module('refstackApp'); 11 | }); 12 | 13 | describe('Filter: arrayConverter', function () { 14 | var $filter; 15 | beforeEach(inject(function (_$filter_) { 16 | $filter = _$filter_('arrayConverter'); 17 | })); 18 | 19 | it('should convert dict to array of dict values', function () { 20 | var object = {'id1': {'key1': 'value1'}, 'id2': {'key2': 'value2'}}; 21 | var expected = [{'key1': 'value1', 'id': 'id1'}, 22 | {'key2': 'value2', 'id': 'id2'}]; 23 | expect($filter(object)).toEqual(expected); 24 | }); 25 | }); 26 | 27 | describe('Filter: capitalize', function() { 28 | var $filter; 29 | beforeEach(inject(function(_$filter_) { 30 | $filter = _$filter_('capitalize'); 31 | })); 32 | 33 | it('should capitalize the first letter', function () { 34 | var string1 = 'somestring'; 35 | var string2 = 'someString'; 36 | var string3 = 'SOMESTRING'; 37 | expect($filter(string1)).toEqual('Somestring'); 38 | expect($filter(string2)).toEqual('SomeString'); 39 | expect($filter(string3)).toEqual(string3); 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /refstack/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | """Refstack package.""" 16 | -------------------------------------------------------------------------------- /refstack/api/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | """Refstack API package.""" 16 | -------------------------------------------------------------------------------- /refstack/api/app.wsgi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from refstack.api import app 17 | from refstack.api import config as api_config 18 | 19 | application = app.setup_app(api_config) -------------------------------------------------------------------------------- /refstack/api/config.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | """Configuration for running API. 17 | 18 | Custom Configurations must be in Python dictionary format: 19 | 20 | foo = {'bar':'baz'} 21 | 22 | All configurations are accessible at: 23 | pecan.conf 24 | """ 25 | 26 | # Server Specific Configurations 27 | server = { 28 | 'port': '8000', 29 | 'host': '0.0.0.0', 30 | 'protocol': 'http' 31 | } 32 | 33 | # Pecan Application Configurations 34 | app = { 35 | 'root': 'refstack.api.controllers.root.RootController', 36 | 'modules': ['refstack.api'], 37 | } 38 | -------------------------------------------------------------------------------- /refstack/api/constants.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | """Constants for Refstack API.""" 16 | 17 | # Names of input parameters for request 18 | START_DATE = 'start_date' 19 | END_DATE = 'end_date' 20 | CPID = 'cpid' 21 | PAGE = 'page' 22 | SIGNED = 'signed' 23 | VERIFICATION_STATUS = 'verification_status' 24 | PRODUCT_ID = 'product_id' 25 | ALL_PRODUCT_TESTS = 'all_product_tests' 26 | OPENID = 'openid' 27 | USER_PUBKEYS = 'pubkeys' 28 | 29 | # Guidelines tests requests parameters 30 | ALIAS = 'alias' 31 | FLAG = 'flag' 32 | TYPE = 'type' 33 | TARGET = 'target' 34 | 35 | # OpenID parameters 36 | OPENID_MODE = 'openid.mode' 37 | OPENID_NS = 'openid.ns' 38 | OPENID_RETURN_TO = 'openid.return_to' 39 | OPENID_CLAIMED_ID = 'openid.claimed_id' 40 | OPENID_IDENTITY = 'openid.identity' 41 | OPENID_REALM = 'openid.realm' 42 | OPENID_NS_SREG = 'openid.ns.sreg' 43 | OPENID_NS_SREG_REQUIRED = 'openid.sreg.required' 44 | OPENID_NS_SREG_EMAIL = 'openid.sreg.email' 45 | OPENID_NS_SREG_FULLNAME = 'openid.sreg.fullname' 46 | OPENID_ERROR = 'openid.error' 47 | 48 | # User session parameters 49 | CSRF_TOKEN = 'csrf_token' 50 | USER_OPENID = 'user_openid' 51 | 52 | # Test metadata fields 53 | USER = 'user' 54 | SHARED_TEST_RUN = 'shared' 55 | 56 | # Test verification statuses 57 | TEST_NOT_VERIFIED = 0 58 | TEST_VERIFIED = 1 59 | 60 | # Roles 61 | ROLE_USER = 'user' 62 | ROLE_OWNER = 'owner' 63 | ROLE_FOUNDATION = 'foundation' 64 | 65 | # Organization types. 66 | # OpenStack Foundation 67 | FOUNDATION = 0 68 | # User's private unofficial Vendor (allows creation and testing 69 | # of user's products) 70 | PRIVATE_VENDOR = 1 71 | # Vendor applied and waiting for official status. 72 | PENDING_VENDOR = 2 73 | # Official Vendor approved by the Foundation. 74 | OFFICIAL_VENDOR = 3 75 | 76 | # Product object types. 77 | CLOUD = 0 78 | SOFTWARE = 1 79 | 80 | # Product specific types. 81 | DISTRO = 0 82 | PUBLIC_CLOUD = 1 83 | HOSTED_PRIVATE_CLOUD = 2 84 | 85 | JWT_TOKEN_HEADER = 'Authorization' 86 | JWT_TOKEN_ENV = 'jwt.token' 87 | JWT_VALIDATION_LEEWAY = 42 88 | -------------------------------------------------------------------------------- /refstack/api/controllers/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | """API controllers package.""" 17 | 18 | from oslo_config import cfg 19 | 20 | from refstack.api import constants as const 21 | 22 | CTRLS_OPTS = [ 23 | cfg.IntOpt('results_per_page', 24 | default=20, 25 | help='Number of results for one page'), 26 | cfg.StrOpt('input_date_format', 27 | default='%Y-%m-%d %H:%M:%S', 28 | help='The format for %(start)s and %(end)s parameters' % { 29 | 'start': const.START_DATE, 30 | 'end': const.END_DATE 31 | }) 32 | ] 33 | 34 | CONF = cfg.CONF 35 | 36 | CONF.register_opts(CTRLS_OPTS, group='api') 37 | -------------------------------------------------------------------------------- /refstack/api/controllers/guidelines.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2015 Mirantis, Inc. 3 | # All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | """Interop WG guidelines controller.""" 18 | 19 | import pecan 20 | from pecan import rest 21 | 22 | from refstack.api import constants as const 23 | from refstack.api import guidelines 24 | from refstack.api import utils as api_utils 25 | 26 | 27 | class TestsController(rest.RestController): 28 | """v1/guidelines//tests handler. 29 | 30 | This will allow users to retrieve specific test lists from specific 31 | guidelines for use with refstack-client. 32 | """ 33 | 34 | @pecan.expose(content_type='text/plain') 35 | def get(self, version): 36 | """Get the plain-text test list of the specified guideline version.""" 37 | # Remove the .json from version if it is there. 38 | version.replace('.json', '') 39 | g = guidelines.Guidelines() 40 | json = g.get_guideline_contents(version) 41 | 42 | if not json: 43 | return 'Error getting JSON content for version: ' + version 44 | 45 | if pecan.request.GET.get(const.TYPE): 46 | types = pecan.request.GET.get(const.TYPE).split(',') 47 | else: 48 | types = None 49 | 50 | if pecan.request.GET.get('alias'): 51 | alias = api_utils.str_to_bool(pecan.request.GET.get('alias')) 52 | else: 53 | alias = True 54 | 55 | if pecan.request.GET.get('flag'): 56 | flag = api_utils.str_to_bool(pecan.request.GET.get('flag')) 57 | else: 58 | flag = True 59 | 60 | target = pecan.request.GET.get('target', 'platform') 61 | try: 62 | target_caps = g.get_target_capabilities(json, types, target) 63 | test_list = g.get_test_list(json, target_caps, alias, flag) 64 | except KeyError: 65 | return 'Invalid target: ' + target 66 | 67 | return '\n'.join(test_list) 68 | 69 | 70 | class GuidelinesController(rest.RestController): 71 | """/v1/guidelines handler. 72 | 73 | This acts as a proxy for retrieving guideline files 74 | from the openstack/interop Github repository. 75 | """ 76 | 77 | tests = TestsController() 78 | 79 | @pecan.expose('json') 80 | def get(self): 81 | """Get a list of all available guidelines.""" 82 | g = guidelines.Guidelines() 83 | version_list = g.get_guideline_list() 84 | if version_list is None: 85 | pecan.abort(500, 'The server was unable to get a list of ' 86 | 'guidelines from the external source.') 87 | else: 88 | return version_list 89 | 90 | @pecan.expose('json') 91 | def get_one(self, file_name): 92 | """Handler for getting contents of specific guideline file.""" 93 | g = guidelines.Guidelines() 94 | json = g.get_guideline_contents(file_name) 95 | if json: 96 | return json 97 | else: 98 | pecan.abort(500, 'The server was unable to get the JSON ' 99 | 'content for the specified guideline file.') 100 | -------------------------------------------------------------------------------- /refstack/api/controllers/root.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | """Root controller.""" 17 | 18 | from pecan import expose 19 | from oslo_config import cfg 20 | 21 | from refstack.api.controllers import v1 22 | 23 | CONF = cfg.CONF 24 | 25 | 26 | class RootController(object): 27 | """Root handler.""" 28 | 29 | v1 = v1.V1Controller() 30 | 31 | if CONF.api.app_dev_mode: 32 | @expose(generic=True, template='index.html') 33 | def index(self): 34 | """Return index.html in development mode. 35 | 36 | It allows to run both API and UI with pecan serve. 37 | Template path should point into UI app folder 38 | """ 39 | return dict() 40 | -------------------------------------------------------------------------------- /refstack/api/controllers/user.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | """User profile controller.""" 17 | 18 | import pecan 19 | from pecan import rest 20 | from pecan.secure import secure 21 | 22 | from refstack.api import utils as api_utils 23 | from refstack.api import validators 24 | from refstack.api.controllers import validation 25 | from refstack import db 26 | 27 | 28 | class PublicKeysController(validation.BaseRestControllerWithValidation): 29 | """/v1/profile/pubkeys handler.""" 30 | 31 | __validator__ = validators.PubkeyValidator 32 | 33 | @secure(api_utils.is_authenticated) 34 | @pecan.expose('json') 35 | def post(self, ): 36 | """Handler for uploading public pubkeys.""" 37 | return super(PublicKeysController, self).post() 38 | 39 | def store_item(self, body): 40 | """Handler for storing item.""" 41 | pubkey = {'openid': api_utils.get_user_id()} 42 | parts = body['raw_key'].strip().split() 43 | if len(parts) == 2: 44 | parts.append('') 45 | pubkey['format'], pubkey['pubkey'], pubkey['comment'] = parts 46 | pubkey_id = db.store_pubkey(pubkey) 47 | return pubkey_id 48 | 49 | @secure(api_utils.is_authenticated) 50 | @pecan.expose('json') 51 | def get(self): 52 | """Retrieve all user's public pubkeys.""" 53 | return api_utils.get_user_public_keys() 54 | 55 | @secure(api_utils.is_authenticated) 56 | @pecan.expose('json') 57 | def delete(self, pubkey_id): 58 | """Delete public key.""" 59 | pubkeys = api_utils.get_user_public_keys() 60 | for key in pubkeys: 61 | if key['id'] == pubkey_id: 62 | db.delete_pubkey(pubkey_id) 63 | pecan.response.status = 204 64 | return 65 | else: 66 | pecan.abort(404) 67 | 68 | 69 | class ProfileController(rest.RestController): 70 | """Controller provides user information in OpenID 2.0 IdP. 71 | 72 | /v1/profile handler 73 | """ 74 | 75 | pubkeys = PublicKeysController() 76 | 77 | @secure(api_utils.is_authenticated) 78 | @pecan.expose('json') 79 | def get(self): 80 | """Handle get request on user info.""" 81 | user = api_utils.get_user() 82 | return { 83 | "openid": user.openid, 84 | "email": user.email, 85 | "fullname": user.fullname, 86 | "is_admin": api_utils.check_user_is_foundation_admin() 87 | } 88 | -------------------------------------------------------------------------------- /refstack/api/controllers/v1.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | """Version 1 of the API.""" 17 | 18 | from refstack.api.controllers import auth 19 | from refstack.api.controllers import guidelines 20 | from refstack.api.controllers import products 21 | from refstack.api.controllers import results 22 | from refstack.api.controllers import user 23 | from refstack.api.controllers import vendors 24 | 25 | 26 | class V1Controller(object): 27 | """Version 1 API controller root.""" 28 | 29 | results = results.ResultsController() 30 | guidelines = guidelines.GuidelinesController() 31 | auth = auth.AuthController() 32 | profile = user.ProfileController() 33 | products = products.ProductsController() 34 | vendors = vendors.VendorsController() 35 | -------------------------------------------------------------------------------- /refstack/api/controllers/validation.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | """Base for controllers with validation.""" 17 | 18 | import json 19 | 20 | import pecan 21 | from pecan import rest 22 | 23 | 24 | class BaseRestControllerWithValidation(rest.RestController): 25 | """Rest controller with validation. 26 | 27 | Controller provides validation for POSTed data 28 | exposed endpoints: 29 | POST base_url/ 30 | GET base_url/ 31 | GET base_url/schema 32 | """ 33 | 34 | __validator__ = None 35 | 36 | _custom_actions = { 37 | "schema": ["GET"], 38 | } 39 | 40 | def __init__(self): # pragma: no cover 41 | """Init.""" 42 | if self.__validator__: 43 | self.validator = self.__validator__() 44 | else: 45 | raise ValueError("__validator__ is not defined") 46 | 47 | def store_item(self, item_in_json): # pragma: no cover 48 | """Handler for storing item. Should return new item id.""" 49 | raise NotImplementedError 50 | 51 | @pecan.expose('json') 52 | def schema(self): 53 | """Return validation schema.""" 54 | return self.validator.schema 55 | 56 | @pecan.expose('json') 57 | def post(self, ): 58 | """POST handler.""" 59 | self.validator.validate(pecan.request) 60 | item = json.loads(pecan.request.body.decode('utf-8')) 61 | item_id = self.store_item(item) 62 | pecan.response.status = 201 63 | return item_id 64 | -------------------------------------------------------------------------------- /refstack/api/exceptions.py: -------------------------------------------------------------------------------- 1 | # 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | """Refstack API exceptions.""" 17 | 18 | 19 | class ParseInputsError(Exception): 20 | """Raise if input params are invalid.""" 21 | 22 | pass 23 | 24 | 25 | class ValidationError(Exception): 26 | """Raise if request doesn't pass trough validation process.""" 27 | 28 | def __init__(self, title, exc=None): 29 | """Init.""" 30 | super(ValidationError, self).__init__(title) 31 | self.exc = exc 32 | self.title = title 33 | self.details = "%s(%s: %s)" % (self.title, 34 | self.exc.__class__.__name__, 35 | str(self.exc)) \ 36 | if self.exc else self.title 37 | 38 | def __repr__(self): 39 | """Repr method.""" 40 | return self.details 41 | 42 | def __str__(self): 43 | """Str method.""" 44 | return self.__repr__() 45 | -------------------------------------------------------------------------------- /refstack/db/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | """DB abstraction for Refstack.""" 16 | 17 | from refstack.db.api import * # noqa 18 | -------------------------------------------------------------------------------- /refstack/db/migration.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | """Database setup and migration commands.""" 17 | 18 | from refstack.db import utils as db_utils 19 | 20 | 21 | IMPL = db_utils.PluggableBackend( 22 | 'db_backend', sqlalchemy='refstack.db.migrations.alembic.migration') 23 | 24 | 25 | def version(): 26 | """Display the current database version.""" 27 | return IMPL.version() 28 | 29 | 30 | def upgrade(version): 31 | """Upgrade database to 'version' or the most recent version.""" 32 | return IMPL.upgrade(version) 33 | 34 | 35 | def downgrade(version): 36 | """Downgrade database to 'version' or to initial state.""" 37 | return IMPL.downgrade(version) 38 | 39 | 40 | def stamp(version): 41 | """Stamp database with 'version' or the most recent version.""" 42 | return IMPL.stamp(version) 43 | 44 | 45 | def revision(message, autogenerate): 46 | """Generate new migration script.""" 47 | return IMPL.revision(message, autogenerate) 48 | -------------------------------------------------------------------------------- /refstack/db/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | """Migrations.""" 16 | -------------------------------------------------------------------------------- /refstack/db/migrations/alembic.ini: -------------------------------------------------------------------------------- 1 | # A generic, single database configuration. 2 | 3 | [alembic] 4 | # path to migration scripts 5 | script_location = %(here)s/alembic 6 | 7 | # template used to generate migration files 8 | # file_template = %%(rev)s_%%(slug)s 9 | 10 | # set to 'true' to run the environment during 11 | # the 'revision' command, regardless of autogenerate 12 | # revision_environment = false 13 | 14 | #sqlalchemy.url = driver://user:pass@127.0.0.1/dbname 15 | 16 | # Logging configuration 17 | [loggers] 18 | keys = root,sqlalchemy,alembic 19 | 20 | [handlers] 21 | keys = console 22 | 23 | [formatters] 24 | keys = generic 25 | 26 | [logger_root] 27 | level = WARN 28 | handlers = console 29 | qualname = 30 | 31 | [logger_sqlalchemy] 32 | level = WARN 33 | handlers = 34 | qualname = sqlalchemy.engine 35 | 36 | [logger_alembic] 37 | level = INFO 38 | handlers = 39 | qualname = alembic 40 | 41 | [handler_console] 42 | class = StreamHandler 43 | args = (sys.stderr,) 44 | level = NOTSET 45 | formatter = generic 46 | 47 | [formatter_generic] 48 | format = %(levelname)-5.5s [%(name)s] %(message)s 49 | datefmt = %H:%M:%S 50 | -------------------------------------------------------------------------------- /refstack/db/migrations/alembic/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /refstack/db/migrations/alembic/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | """Alembic backend for migrations.""" 16 | -------------------------------------------------------------------------------- /refstack/db/migrations/alembic/env.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2013 Piston Cloud Computing, Inc. 4 | # All Rights Reserved. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | # not use this file except in compliance with the License. You may obtain 8 | # a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | # License for the specific language governing permissions and limitations 16 | # under the License. 17 | """Alembic environment script.""" 18 | 19 | from __future__ import with_statement 20 | 21 | from alembic import context 22 | from oslo_config import cfg 23 | 24 | from refstack.db.sqlalchemy import api as db_api 25 | from refstack.db.sqlalchemy import models as db_models 26 | 27 | CONF = cfg.CONF 28 | 29 | 30 | def run_migrations_online(): 31 | """Run migrations in 'online' mode. 32 | 33 | In this scenario we need to create an Engine 34 | and associate a connection with the context. 35 | """ 36 | engine = db_api.get_engine() 37 | connection = engine.connect() 38 | target_metadata = db_models.RefStackBase.metadata 39 | context.configure(connection=connection, 40 | target_metadata=target_metadata, 41 | version_table=getattr(CONF, 'version_table')) 42 | 43 | try: 44 | with context.begin_transaction(): 45 | context.run_migrations() 46 | finally: 47 | connection.close() 48 | 49 | 50 | run_migrations_online() 51 | -------------------------------------------------------------------------------- /refstack/db/migrations/alembic/migration.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | """Implementation of Alembic commands.""" 16 | import alembic 17 | import alembic.migration as alembic_migration 18 | from oslo_config import cfg 19 | from refstack.db.sqlalchemy import api as db_api 20 | from refstack.db.migrations.alembic import utils 21 | 22 | CONF = cfg.CONF 23 | 24 | 25 | def version(): 26 | """Current database version. 27 | 28 | :returns: Database version 29 | :type: string 30 | """ 31 | engine = db_api.get_engine() 32 | with engine.connect() as conn: 33 | conf_table = getattr(CONF, 'version_table') 34 | utils.recheck_alembic_table(conn) 35 | context = alembic_migration.MigrationContext.configure( 36 | conn, opts={'version_table': conf_table}) 37 | return context.get_current_revision() 38 | 39 | 40 | def upgrade(revision): 41 | """Upgrade database. 42 | 43 | :param version: Desired database version 44 | :type version: string 45 | """ 46 | return alembic.command.upgrade(utils.alembic_config(), revision or 'head') 47 | 48 | 49 | def downgrade(revision): 50 | """Downgrade database. 51 | 52 | :param version: Desired database version 53 | :type version: string 54 | """ 55 | return alembic.command.downgrade(utils.alembic_config(), 56 | revision or 'base') 57 | 58 | 59 | def stamp(revision): 60 | """Stamp database with provided revision. 61 | 62 | Don't run any migrations. 63 | 64 | :param revision: Should match one from repository or head - to stamp 65 | database with most recent revision 66 | :type revision: string 67 | """ 68 | return alembic.command.stamp(utils.alembic_config(), revision or 'head') 69 | 70 | 71 | def revision(message=None, autogenerate=False): 72 | """Create template for migration. 73 | 74 | :param message: Text that will be used for migration title 75 | :type message: string 76 | :param autogenerate: If True - generates diff based on current database 77 | state 78 | :type autogenerate: bool 79 | """ 80 | return alembic.command.revision(utils.alembic_config(), 81 | message, autogenerate) 82 | -------------------------------------------------------------------------------- /refstack/db/migrations/alembic/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | 9 | # revision identifiers, used by Alembic. 10 | revision = ${repr(up_revision)} 11 | down_revision = ${repr(down_revision)} 12 | MYSQL_CHARSET = 'utf8' 13 | 14 | from alembic import op 15 | import sqlalchemy as sa 16 | ${imports if imports else ""} 17 | 18 | def upgrade(): 19 | """Upgrade DB.""" 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | """Downgrade DB.""" 25 | ${downgrades if downgrades else "pass"} 26 | -------------------------------------------------------------------------------- /refstack/db/migrations/alembic/versions/19fded785b8c_create_organization_table.py: -------------------------------------------------------------------------------- 1 | """Create organization table. 2 | 3 | Revision ID: 19fded785b8c 4 | Revises: 319ee8fe47c7 5 | Create Date: 2016-01-18 14:40:00 6 | 7 | """ 8 | 9 | # revision identifiers, used by Alembic. 10 | revision = '19fded785b8c' 11 | down_revision = '319ee8fe47c7' 12 | MYSQL_CHARSET = 'utf8' 13 | 14 | from alembic import op 15 | import sqlalchemy as sa 16 | 17 | 18 | def upgrade(): 19 | """Upgrade DB.""" 20 | op.create_table( 21 | 'organization', 22 | sa.Column('updated_at', sa.DateTime()), 23 | sa.Column('deleted_at', sa.DateTime()), 24 | sa.Column('deleted', sa.Integer, default=0), 25 | sa.Column('created_at', sa.DateTime(), nullable=False), 26 | sa.Column('id', sa.String(36), nullable=False), 27 | sa.Column('type', sa.Integer(), nullable=False), 28 | sa.Column('name', sa.String(length=80), nullable=False), 29 | sa.Column('description', sa.Text()), 30 | sa.Column('group_id', sa.String(36), nullable=False), 31 | sa.Column('created_by_user', sa.String(128), nullable=False), 32 | sa.Column('properties', sa.Text()), 33 | sa.PrimaryKeyConstraint('id'), 34 | sa.ForeignKeyConstraint(['group_id'], ['group.id'], ), 35 | sa.ForeignKeyConstraint(['created_by_user'], ['user.openid'], ), 36 | mysql_charset=MYSQL_CHARSET 37 | ) 38 | 39 | 40 | def downgrade(): 41 | """Downgrade DB.""" 42 | op.drop_table('organization') 43 | -------------------------------------------------------------------------------- /refstack/db/migrations/alembic/versions/23843be3da52_add_product_version_id.py: -------------------------------------------------------------------------------- 1 | """Add product_version_id column to test. 2 | 3 | Revision ID: 23843be3da52 4 | Revises: 35bf54e2c13c 5 | Create Date: 2016-07-30 18:15:52.429610 6 | """ 7 | 8 | # revision identifiers, used by Alembic. 9 | revision = '23843be3da52' 10 | down_revision = '35bf54e2c13c' 11 | MYSQL_CHARSET = 'utf8' 12 | 13 | from alembic import op 14 | import sqlalchemy as sa 15 | 16 | 17 | def upgrade(): 18 | """Upgrade DB.""" 19 | op.add_column('test', sa.Column('product_version_id', sa.String(36), 20 | nullable=True)) 21 | op.create_foreign_key('fk_test_prod_version_id', 'test', 'product_version', 22 | ['product_version_id'], ['id']) 23 | 24 | 25 | def downgrade(): 26 | """Downgrade DB.""" 27 | op.drop_constraint('fk_test_prod_version_id', 'test', type_="foreignkey") 28 | op.drop_column('test', 'product_version_id') 29 | -------------------------------------------------------------------------------- /refstack/db/migrations/alembic/versions/2f178b0bf762_create_user_table.py: -------------------------------------------------------------------------------- 1 | """Create user table. 2 | 3 | Revision ID: 2f178b0bf762 4 | Revises: 42278d6179b9 5 | Create Date: 2015-05-12 12:15:43.810938 6 | 7 | """ 8 | 9 | # revision identifiers, used by Alembic. 10 | revision = '2f178b0bf762' 11 | down_revision = '42278d6179b9' 12 | MYSQL_CHARSET = 'utf8' 13 | 14 | from alembic import op 15 | import sqlalchemy as sa 16 | 17 | 18 | def upgrade(): 19 | """Upgrade DB.""" 20 | op.create_table( 21 | 'user', 22 | sa.Column('updated_at', sa.DateTime()), 23 | sa.Column('deleted_at', sa.DateTime()), 24 | sa.Column('deleted', sa.Integer, default=0), 25 | sa.Column('_id', sa.Integer(), nullable=False), 26 | sa.Column('created_at', sa.DateTime(), nullable=False), 27 | sa.Column('openid', sa.String(length=128), 28 | nullable=False, unique=True), 29 | sa.Column('email', sa.String(length=128)), 30 | sa.Column('fullname', sa.String(length=128)), 31 | sa.PrimaryKeyConstraint('_id'), 32 | mysql_charset=MYSQL_CHARSET 33 | ) 34 | 35 | 36 | def downgrade(): 37 | """Downgrade DB.""" 38 | op.drop_table('user') 39 | -------------------------------------------------------------------------------- /refstack/db/migrations/alembic/versions/319ee8fe47c7_create_group_table.py: -------------------------------------------------------------------------------- 1 | """Create group table and group-user links table. 2 | 3 | Revision ID: 319ee8fe47c7 4 | Revises: 428e5aef5534 5 | Create Date: 2016-01-15 16:34:00 6 | 7 | """ 8 | 9 | # revision identifiers, used by Alembic. 10 | revision = '319ee8fe47c7' 11 | down_revision = '428e5aef5534' 12 | MYSQL_CHARSET = 'utf8' 13 | 14 | from alembic import op 15 | import sqlalchemy as sa 16 | 17 | 18 | def upgrade(): 19 | """Upgrade DB.""" 20 | op.create_table( 21 | 'group', 22 | sa.Column('created_at', sa.DateTime(), nullable=False), 23 | sa.Column('updated_at', sa.DateTime()), 24 | sa.Column('deleted_at', sa.DateTime()), 25 | sa.Column('deleted', sa.Integer, default=0), 26 | sa.Column('id', sa.String(36), nullable=False), 27 | sa.Column('name', sa.String(length=80), nullable=False), 28 | sa.Column('description', sa.Text()), 29 | sa.PrimaryKeyConstraint('id'), 30 | mysql_charset=MYSQL_CHARSET 31 | ) 32 | op.create_table( 33 | 'user_to_group', 34 | sa.Column('created_at', sa.DateTime(), nullable=False), 35 | sa.Column('updated_at', sa.DateTime()), 36 | sa.Column('deleted_at', sa.DateTime()), 37 | sa.Column('deleted', sa.Integer, default=0), 38 | sa.Column('created_by_user', sa.String(length=128), nullable=False), 39 | sa.Column('_id', sa.Integer(), nullable=False), 40 | sa.Column('group_id', sa.String(36), nullable=False), 41 | sa.Column('user_openid', sa.String(length=128), nullable=False), 42 | sa.PrimaryKeyConstraint('_id'), 43 | sa.ForeignKeyConstraint(['user_openid'], ['user.openid'], ), 44 | sa.ForeignKeyConstraint(['group_id'], ['group.id'], ), 45 | mysql_charset=MYSQL_CHARSET 46 | ) 47 | 48 | 49 | def downgrade(): 50 | """Downgrade DB.""" 51 | op.drop_table('user_to_group') 52 | op.drop_table('group') 53 | -------------------------------------------------------------------------------- /refstack/db/migrations/alembic/versions/35bf54e2c13c_add_product_version.py: -------------------------------------------------------------------------------- 1 | """Add Product version table. 2 | 3 | Also product_ref_id is removed from the product table. 4 | 5 | Revision ID: 35bf54e2c13c 6 | Revises: 709452f38a5c 7 | Create Date: 2016-07-30 17:59:57.912306 8 | 9 | """ 10 | 11 | # revision identifiers, used by Alembic. 12 | revision = '35bf54e2c13c' 13 | down_revision = '709452f38a5c' 14 | MYSQL_CHARSET = 'utf8' 15 | 16 | from alembic import op 17 | import sqlalchemy as sa 18 | 19 | 20 | def upgrade(): 21 | """Upgrade DB.""" 22 | op.create_table( 23 | 'product_version', 24 | sa.Column('updated_at', sa.DateTime()), 25 | sa.Column('deleted_at', sa.DateTime()), 26 | sa.Column('deleted', sa.Integer, default=0), 27 | sa.Column('created_at', sa.DateTime(), nullable=False), 28 | sa.Column('created_by_user', sa.String(128), nullable=False), 29 | sa.Column('id', sa.String(36), nullable=False), 30 | sa.Column('product_id', sa.String(36), nullable=False), 31 | sa.Column('version', sa.String(length=36), nullable=True), 32 | sa.Column('cpid', sa.String(length=36)), 33 | sa.PrimaryKeyConstraint('id'), 34 | sa.ForeignKeyConstraint(['product_id'], ['product.id'], ), 35 | sa.ForeignKeyConstraint(['created_by_user'], ['user.openid'], ), 36 | sa.UniqueConstraint('product_id', 'version', name='prod_ver_uc'), 37 | mysql_charset=MYSQL_CHARSET 38 | ) 39 | op.drop_column('product', 'product_ref_id') 40 | 41 | 42 | def downgrade(): 43 | """Downgrade DB.""" 44 | op.drop_table('product_version') 45 | op.add_column('product', 46 | sa.Column('product_ref_id', sa.String(36), nullable=True)) 47 | -------------------------------------------------------------------------------- /refstack/db/migrations/alembic/versions/42278d6179b9_init.py: -------------------------------------------------------------------------------- 1 | """Init. 2 | 3 | Revision ID: 42278d6179b9 4 | Revises: None 5 | Create Date: 2015-01-09 15:00:11.385580 6 | 7 | """ 8 | 9 | # revision identifiers, used by Alembic. 10 | revision = '42278d6179b9' 11 | down_revision = None 12 | MYSQL_CHARSET = 'utf8' 13 | 14 | from alembic import op 15 | import sqlalchemy as sa 16 | 17 | 18 | def upgrade(): 19 | """Upgrade DB.""" 20 | op.create_table( 21 | 'test', 22 | sa.Column('updated_at', sa.DateTime()), 23 | sa.Column('deleted_at', sa.DateTime()), 24 | sa.Column('deleted', sa.Integer, default=0), 25 | sa.Column('id', sa.String(length=36), nullable=False), 26 | sa.Column('created_at', sa.DateTime(), nullable=False), 27 | sa.Column('cpid', sa.String(length=128), nullable=False), 28 | sa.Column('duration_seconds', sa.Integer(), nullable=False), 29 | sa.PrimaryKeyConstraint('id'), 30 | mysql_charset=MYSQL_CHARSET, 31 | ) 32 | op.create_table( 33 | 'meta', 34 | sa.Column('updated_at', sa.DateTime()), 35 | sa.Column('deleted_at', sa.DateTime()), 36 | sa.Column('deleted', sa.Integer, default=0), 37 | sa.Column('_id', sa.Integer(), nullable=False), 38 | sa.Column('created_at', sa.DateTime(), nullable=False), 39 | sa.Column('test_id', sa.String(length=36), nullable=False), 40 | sa.Column('meta_key', sa.String(length=64), nullable=False), 41 | sa.Column('value', sa.Text(), nullable=True), 42 | sa.ForeignKeyConstraint(['test_id'], ['test.id'], ), 43 | sa.PrimaryKeyConstraint('_id'), 44 | sa.UniqueConstraint('test_id', 'meta_key'), 45 | mysql_charset=MYSQL_CHARSET 46 | ) 47 | op.create_table( 48 | 'results', 49 | sa.Column('updated_at', sa.DateTime()), 50 | sa.Column('deleted_at', sa.DateTime()), 51 | sa.Column('deleted', sa.Integer, default=0), 52 | sa.Column('_id', sa.Integer(), nullable=False), 53 | sa.Column('created_at', sa.DateTime(), nullable=False), 54 | sa.Column('test_id', sa.String(length=36), nullable=False), 55 | sa.Column('name', 56 | sa.String(length=512, collation='latin1_swedish_ci'), 57 | nullable=True), 58 | sa.Column('uuid', sa.String(length=36), nullable=True), 59 | sa.ForeignKeyConstraint(['test_id'], ['test.id'], ), 60 | sa.PrimaryKeyConstraint('_id'), 61 | sa.UniqueConstraint('test_id', 'name'), 62 | # TODO(sslypushenko) 63 | # Constraint should turned on after duplication test uuids issue 64 | # will be fixed 65 | # sa.UniqueConstraint('test_id', 'uuid') 66 | mysql_charset=MYSQL_CHARSET 67 | ) 68 | 69 | 70 | def downgrade(): 71 | """Downgrade DB.""" 72 | op.drop_table('results') 73 | op.drop_table('meta') 74 | op.drop_table('test') 75 | -------------------------------------------------------------------------------- /refstack/db/migrations/alembic/versions/428e5aef5534_associate_test_result.py: -------------------------------------------------------------------------------- 1 | """Associate test results to users. 2 | 3 | Revision ID: 428e5aef5534 4 | Revises: 534e20be9964 5 | Create Date: 2015-11-03 00:51:34.096598 6 | 7 | """ 8 | 9 | # revision identifiers, used by Alembic. 10 | revision = '428e5aef5534' 11 | down_revision = '534e20be9964' 12 | MYSQL_CHARSET = 'utf8' 13 | 14 | from alembic import op 15 | import sqlalchemy as sa 16 | 17 | 18 | def upgrade(): 19 | """Upgrade DB.""" 20 | conn = op.get_bind() 21 | res = conn.execute("select openid,format,pubkey from pubkeys") 22 | results = res.fetchall() 23 | 24 | # Get public key to user mappings. 25 | pubkeys = {} 26 | for result in results: 27 | pubkeys[result[1] + " " + result[2]] = result[0] 28 | 29 | res = conn.execute("select test_id,value from meta where " 30 | "meta_key='public_key'") 31 | results = res.fetchall() 32 | 33 | for result in results: 34 | test_id = result[0] 35 | if result[1] in pubkeys: 36 | openid = pubkeys[result[1]] 37 | conn.execute(sa.text("update meta set meta_key='user', " 38 | "value=:value where " 39 | "test_id=:testid and meta_key='public_key'" 40 | ), 41 | value=openid, testid=test_id) 42 | 43 | 44 | def downgrade(): 45 | """Downgrade DB.""" 46 | pass 47 | -------------------------------------------------------------------------------- /refstack/db/migrations/alembic/versions/434be17a6ec3_fix_openids_with_space.py: -------------------------------------------------------------------------------- 1 | """Fix openids with spaces. 2 | 3 | A change in the openstackid naming made is so IDs with spaces 4 | are trimmed, so %20 are no longer in the openid url. This migration 5 | will replace any '%20' with a '.' in each openid. 6 | 7 | Revision ID: 434be17a6ec3 8 | Revises: 59df512e82f 9 | Create Date: 2017-03-23 12:20:08.219294 10 | 11 | """ 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = '434be17a6ec3' 15 | down_revision = '59df512e82f' 16 | MYSQL_CHARSET = 'utf8' 17 | 18 | from alembic import op 19 | 20 | 21 | def upgrade(): 22 | """Upgrade DB.""" 23 | conn = op.get_bind() 24 | # Need to disable FOREIGN_KEY_CHECKS as a lot of tables reference the 25 | # openid in the user table. 26 | conn.execute("SET FOREIGN_KEY_CHECKS=0") 27 | res = conn.execute("select * from user where openid LIKE '%%\%%20%%'") 28 | results = res.fetchall() 29 | for user in results: 30 | old_openid = user[5] 31 | new_openid = user[5].replace('%20', '.') 32 | 33 | # Remove instances of the new openid so the old one can take 34 | # its place. 35 | query = "delete from user where openid='%s'" % (new_openid) 36 | conn.execute(query.replace('%', '%%')) 37 | 38 | # Update the openid. 39 | query = ("update user set openid='%s' where openid='%s'" % 40 | (new_openid, old_openid)) 41 | conn.execute(query.replace('%', '%%')) 42 | 43 | # Update all usage of %20 in all openid references using MySQL Replace. 44 | conn.execute("update meta set value = " 45 | "REPLACE (value, '%%20', '.')") 46 | conn.execute("update pubkeys set openid = " 47 | "REPLACE (openid, '%%20', '.')") 48 | conn.execute("update organization set created_by_user = " 49 | "REPLACE (created_by_user, '%%20', '.')") 50 | conn.execute("update product set created_by_user = " 51 | "REPLACE (created_by_user, '%%20', '.')") 52 | conn.execute("update product_version set created_by_user = " 53 | "REPLACE (created_by_user, '%%20', '.')") 54 | conn.execute("update user_to_group set created_by_user = " 55 | "REPLACE (created_by_user, '%%20', '.')") 56 | conn.execute("update user_to_group set user_openid = " 57 | "REPLACE (user_openid, '%%20', '.')") 58 | 59 | conn.execute("SET FOREIGN_KEY_CHECKS=1") 60 | 61 | 62 | def downgrade(): 63 | """Downgrade DB.""" 64 | pass 65 | -------------------------------------------------------------------------------- /refstack/db/migrations/alembic/versions/534e20be9964_create_pubkey_table.py: -------------------------------------------------------------------------------- 1 | """Create user metadata table. 2 | 3 | Revision ID: 534e20be9964 4 | Revises: 2f178b0bf762 5 | Create Date: 2015-07-03 13:26:29.138416 6 | 7 | """ 8 | 9 | # revision identifiers, used by Alembic. 10 | revision = '534e20be9964' 11 | down_revision = '2f178b0bf762' 12 | MYSQL_CHARSET = 'utf8' 13 | 14 | from alembic import op 15 | import sqlalchemy as sa 16 | 17 | 18 | def upgrade(): 19 | """Upgrade DB.""" 20 | op.create_table( 21 | 'pubkeys', 22 | sa.Column('updated_at', sa.DateTime()), 23 | sa.Column('deleted_at', sa.DateTime()), 24 | sa.Column('deleted', sa.Integer, default=0), 25 | sa.Column('id', sa.String(length=36), primary_key=True), 26 | sa.Column('created_at', sa.DateTime(), nullable=False), 27 | sa.Column('openid', sa.String(length=128), 28 | nullable=False, index=True), 29 | sa.Column('format', sa.String(length=24), nullable=False), 30 | sa.Column('pubkey', sa.Text(), nullable=False), 31 | sa.Column('md5_hash', sa.String(length=32), 32 | nullable=False, index=True), 33 | sa.Column('comment', sa.String(length=128)), 34 | sa.ForeignKeyConstraint(['openid'], ['user.openid'], ), 35 | mysql_charset=MYSQL_CHARSET 36 | ) 37 | op.create_index('indx_meta_value', 'meta', ['value'], mysql_length=32) 38 | 39 | 40 | def downgrade(): 41 | """Downgrade DB.""" 42 | op.drop_table('pubkeys') 43 | -------------------------------------------------------------------------------- /refstack/db/migrations/alembic/versions/59df512e82f_add_verification_status.py: -------------------------------------------------------------------------------- 1 | """Add verification_status field to test. 2 | 3 | Revision ID: 59df512e82f 4 | Revises: 23843be3da52 5 | Create Date: 2016-09-26 11:51:08.955006 6 | 7 | """ 8 | 9 | # revision identifiers, used by Alembic. 10 | revision = '59df512e82f' 11 | down_revision = '23843be3da52' 12 | MYSQL_CHARSET = 'utf8' 13 | 14 | from alembic import op 15 | import sqlalchemy as sa 16 | 17 | 18 | def upgrade(): 19 | """Upgrade DB.""" 20 | op.add_column('test', sa.Column('verification_status', 21 | sa.Integer, 22 | nullable=False, 23 | default=0)) 24 | 25 | 26 | def downgrade(): 27 | """Downgrade DB.""" 28 | op.drop_column('test', 'verification_status') 29 | -------------------------------------------------------------------------------- /refstack/db/migrations/alembic/versions/7092392cbb8e_create_product_table.py: -------------------------------------------------------------------------------- 1 | """Create product table. 2 | 3 | Revision ID: 7092392cbb8e 4 | Revises: 19fded785b8c 5 | Create Date: 2016-01-18 16:10:00 6 | 7 | """ 8 | 9 | # revision identifiers, used by Alembic. 10 | revision = '7092392cbb8e' 11 | down_revision = '19fded785b8c' 12 | MYSQL_CHARSET = 'utf8' 13 | 14 | from alembic import op 15 | import sqlalchemy as sa 16 | 17 | 18 | def upgrade(): 19 | """Upgrade DB.""" 20 | op.create_table( 21 | 'product', 22 | sa.Column('updated_at', sa.DateTime()), 23 | sa.Column('deleted_at', sa.DateTime()), 24 | sa.Column('deleted', sa.Integer, default=0), 25 | sa.Column('created_at', sa.DateTime(), nullable=False), 26 | sa.Column('created_by_user', sa.String(128), nullable=False), 27 | sa.Column('id', sa.String(36), nullable=False), 28 | sa.Column('name', sa.String(length=80), nullable=False), 29 | sa.Column('description', sa.Text()), 30 | sa.Column('product_id', sa.String(36), nullable=False), 31 | sa.Column('type', sa.Integer(), nullable=False), 32 | sa.Column('product_type', sa.Integer(), nullable=False), 33 | sa.Column('public', sa.Boolean(), nullable=False), 34 | sa.Column('organization_id', sa.String(36), nullable=False), 35 | sa.Column('properties', sa.Text()), 36 | sa.PrimaryKeyConstraint('id'), 37 | sa.ForeignKeyConstraint(['organization_id'], ['organization.id'], ), 38 | sa.ForeignKeyConstraint(['created_by_user'], ['user.openid'], ), 39 | mysql_charset=MYSQL_CHARSET 40 | ) 41 | 42 | 43 | def downgrade(): 44 | """Downgrade DB.""" 45 | op.drop_table('product') 46 | -------------------------------------------------------------------------------- /refstack/db/migrations/alembic/versions/7093ca478d35_product_table_make_product_id_nullable.py: -------------------------------------------------------------------------------- 1 | """Make product_id nullable in product table. 2 | 3 | Revision ID: 7093ca478d35 4 | Revises: 7092392cbb8e 5 | Create Date: 2016-05-12 13:10:00 6 | 7 | """ 8 | 9 | # revision identifiers, used by Alembic. 10 | revision = '7093ca478d35' 11 | down_revision = '7092392cbb8e' 12 | MYSQL_CHARSET = 'utf8' 13 | 14 | from alembic import op 15 | import sqlalchemy as sa 16 | 17 | 18 | def upgrade(): 19 | """Upgrade DB.""" 20 | op.alter_column('product', 'product_id', nullable=True, 21 | type_=sa.String(36)) 22 | 23 | 24 | def downgrade(): 25 | """Downgrade DB.""" 26 | pass 27 | -------------------------------------------------------------------------------- /refstack/db/migrations/alembic/versions/709452f38a5c_product_table_rename_product_id.py: -------------------------------------------------------------------------------- 1 | """Rename product_id to product_ref_id. 2 | 3 | Revision ID: 709452f38a5c 4 | Revises: 7093ca478d35 5 | Create Date: 2016-06-27 13:10:00 6 | 7 | """ 8 | 9 | # revision identifiers, used by Alembic. 10 | revision = '709452f38a5c' 11 | down_revision = '7093ca478d35' 12 | MYSQL_CHARSET = 'utf8' 13 | 14 | from alembic import op 15 | import sqlalchemy as sa 16 | 17 | 18 | def upgrade(): 19 | """Upgrade DB.""" 20 | op.alter_column('product', 'product_id', new_column_name='product_ref_id', 21 | type_=sa.String(36)) 22 | 23 | 24 | def downgrade(): 25 | """Downgrade DB.""" 26 | op.alter_column('product', 'product_ref_id', new_column_name='product_id', 27 | type_=sa.String(36)) 28 | -------------------------------------------------------------------------------- /refstack/db/sqlalchemy/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | """SQLAlchemy backend.""" 16 | -------------------------------------------------------------------------------- /refstack/db/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | """Utilities for database.""" 17 | from oslo_config import cfg 18 | from oslo_log import log 19 | 20 | CONF = cfg.CONF 21 | LOG = log.getLogger(__name__) 22 | 23 | 24 | class PluggableBackend(object): 25 | """A pluggable backend loaded lazily based on some value.""" 26 | 27 | def __init__(self, pivot, **backends): 28 | """Init.""" 29 | self.__backends = backends 30 | self.__pivot = pivot 31 | self.__backend = None 32 | 33 | def __get_backend(self): 34 | """Get backend.""" 35 | if not self.__backend: 36 | backend_name = CONF[self.__pivot] 37 | if backend_name not in self.__backends: # pragma: no cover 38 | raise Exception('Invalid backend: %s' % backend_name) 39 | 40 | backend = self.__backends[backend_name] 41 | if isinstance(backend, tuple): # pragma: no cover 42 | name = backend[0] 43 | fromlist = backend[1] 44 | else: 45 | name = backend 46 | fromlist = backend 47 | 48 | self.__backend = __import__(name, None, None, fromlist) 49 | LOG.debug('backend %s', self.__backend) 50 | return self.__backend 51 | 52 | def __getattr__(self, key): 53 | """Proxy interface to backend.""" 54 | backend = self.__get_backend() 55 | return getattr(backend, key) 56 | -------------------------------------------------------------------------------- /refstack/opts.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | """Function list_opts intended for oslo-config-generator. 16 | 17 | this tool used for generate config file with help info and default values 18 | for options defined anywhere in application. 19 | All new options must be imported here and must be returned from 20 | list_opts function as list that contain tuple. 21 | Use itertools.chain if config section contain more than one imported module 22 | with options. For example: 23 | 24 | ... 25 | def list_opts(): 26 | return [ 27 | ('DEFAULT', refstack.db.api.db_opts), 28 | ('api', 29 | itertools.chain(refstack.api.first.module.opts, 30 | refstack.api.second.modulei.opts,)), 31 | ] 32 | ... 33 | """ 34 | import itertools 35 | 36 | import refstack.api.app 37 | import refstack.api.controllers.v1 38 | import refstack.api.controllers.auth 39 | import refstack.db.api 40 | 41 | 42 | def list_opts(): 43 | """List oslo config options. 44 | 45 | Keep a list in alphabetical order 46 | """ 47 | return [ 48 | # 49 | ('DEFAULT', itertools.chain(refstack.api.app.UI_OPTS, 50 | refstack.db.api.db_opts)), 51 | ('api', itertools.chain(refstack.api.app.API_OPTS, 52 | refstack.api.controllers.CTRLS_OPTS)), 53 | ('osid', refstack.api.controllers.auth.OPENID_OPTS), 54 | ] 55 | -------------------------------------------------------------------------------- /refstack/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | """Refstack tests.""" 16 | -------------------------------------------------------------------------------- /refstack/tests/api/test_profile.py: -------------------------------------------------------------------------------- 1 | # All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | import binascii 16 | import json 17 | 18 | from cryptography.hazmat.backends import default_backend 19 | from cryptography.hazmat.primitives.asymmetric import padding 20 | from cryptography.hazmat.primitives.asymmetric import rsa 21 | from cryptography.hazmat.primitives import hashes 22 | from cryptography.hazmat.primitives import serialization 23 | import mock 24 | import webtest.app 25 | 26 | from refstack.tests import api 27 | from refstack import db 28 | 29 | 30 | class TestProfileEndpoint(api.FunctionalTest): 31 | """Test case for the 'profile' API endpoint.""" 32 | 33 | URL = '/v1/profile/' 34 | 35 | def setUp(self): 36 | super(TestProfileEndpoint, self).setUp() 37 | self.user_info = { 38 | 'openid': 'test-open-id', 39 | 'email': 'foo@bar.com', 40 | 'fullname': 'Foo Bar' 41 | } 42 | db.user_save(self.user_info) 43 | 44 | @mock.patch('refstack.api.utils.get_user_id', return_value='test-open-id') 45 | def test_get(self, mock_get_user): 46 | response = self.get_json(self.URL) 47 | self.user_info['is_admin'] = False 48 | self.assertEqual(self.user_info, response) 49 | 50 | @mock.patch('refstack.api.utils.get_user_id', return_value='test-open-id') 51 | def test_pubkeys(self, mock_get_user): 52 | """Test '/v1/profile/pubkeys' API endpoint.""" 53 | url = self.URL + 'pubkeys' 54 | key = rsa.generate_private_key( 55 | public_exponent=65537, 56 | key_size=1024, 57 | backend=default_backend() 58 | ) 59 | signer = key.signer(padding.PKCS1v15(), hashes.SHA256()) 60 | signer.update('signature'.encode('utf-8')) 61 | sign = signer.finalize() 62 | pubkey = key.public_key().public_bytes( 63 | serialization.Encoding.OpenSSH, 64 | serialization.PublicFormat.OpenSSH 65 | ).decode('utf-8') 66 | body = {'raw_key': pubkey, 67 | 'self_signature': binascii.b2a_hex(sign).decode('utf-8')} 68 | json_params = json.dumps(body) 69 | 70 | # POST endpoint 71 | pubkey_id = self.post_json(url, params=json_params) 72 | 73 | # GET endpoint 74 | user_pubkeys = self.get_json(url) 75 | self.assertEqual(1, len(user_pubkeys)) 76 | self.assertEqual(pubkey.split()[1], user_pubkeys[0]['pubkey']) 77 | self.assertEqual('ssh-rsa', user_pubkeys[0]['format']) 78 | self.assertEqual(pubkey_id, user_pubkeys[0]['id']) 79 | 80 | delete_url = '{}/{}'.format(url, pubkey_id) 81 | # DELETE endpoint 82 | response = self.delete(delete_url) 83 | self.assertEqual(204, response.status_code) 84 | 85 | user_pubkeys = self.get_json(url) 86 | self.assertEqual(0, len(user_pubkeys)) 87 | 88 | # DELETE endpoint - nonexistent pubkey 89 | self.assertRaises(webtest.app.AppError, self.delete, delete_url) 90 | -------------------------------------------------------------------------------- /refstack/tests/unit/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | """Refstack unittests.""" 16 | 17 | import mock 18 | from oslotest import base 19 | 20 | 21 | class RefstackBaseTestCase(base.BaseTestCase): 22 | """Refstack test base class.""" 23 | 24 | def setup_mock(self, *args, **kwargs): 25 | """Mock in test setup.""" 26 | patcher = mock.patch(*args, **kwargs) 27 | self.addCleanup(patcher.stop) 28 | return patcher.start() 29 | -------------------------------------------------------------------------------- /refstack/tests/unit/test_migration.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Mirantis, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | """Tests for refstack's migrations.""" 17 | 18 | import alembic 19 | import mock 20 | from oslotest import base 21 | 22 | from refstack.db import migration 23 | from refstack.db.migrations.alembic import utils 24 | 25 | 26 | class AlembicConfigTestCase(base.BaseTestCase): 27 | 28 | @mock.patch('alembic.config.Config') 29 | @mock.patch('os.path.join') 30 | def test_alembic_config(self, os_join, alembic_config): 31 | os_join.return_value = 'fake_path' 32 | alembic_config.return_value = 'fake_config' 33 | result = utils.alembic_config() 34 | self.assertEqual(result, 'fake_config') 35 | alembic_config.assert_called_once_with('fake_path') 36 | 37 | 38 | class MigrationTestCase(base.BaseTestCase): 39 | """Test case for alembic's migrations API.""" 40 | 41 | def setUp(self): 42 | super(MigrationTestCase, self).setUp() 43 | self.config_patcher = mock.patch( 44 | 'refstack.db.migrations.alembic.utils.alembic_config') 45 | self.config = self.config_patcher.start() 46 | self.config.return_value = 'fake_config' 47 | self.addCleanup(self.config_patcher.stop) 48 | 49 | @mock.patch.object(alembic.migration.MigrationContext, 'configure', 50 | mock.Mock()) 51 | def test_version(self): 52 | context = mock.Mock() 53 | context.get_current_revision = mock.Mock() 54 | alembic.migration.MigrationContext.configure.return_value = context 55 | with mock.patch('refstack.db.sqlalchemy.api.get_engine') as get_engine: 56 | engine = mock.Mock() 57 | engine.connect = mock.MagicMock() 58 | get_engine.return_value = engine 59 | migration.version() 60 | context.get_current_revision.assert_called_with() 61 | engine.connect.assert_called_once_with() 62 | 63 | @mock.patch('alembic.command.upgrade') 64 | def test_upgrade(self, upgrade): 65 | migration.upgrade('some_revision') 66 | upgrade.assert_called_once_with('fake_config', 'some_revision') 67 | 68 | @mock.patch('alembic.command.upgrade') 69 | def test_upgrade_without_revision(self, upgrade): 70 | migration.upgrade(None) 71 | upgrade.assert_called_once_with('fake_config', 'head') 72 | 73 | @mock.patch('alembic.command.downgrade') 74 | def test_downgrade(self, downgrade): 75 | migration.downgrade('some_revision') 76 | downgrade.assert_called_once_with('fake_config', 'some_revision') 77 | 78 | @mock.patch('alembic.command.downgrade') 79 | def test_downgrade_without_revision(self, downgrade): 80 | migration.downgrade(None) 81 | downgrade.assert_called_once_with('fake_config', 'base') 82 | 83 | @mock.patch('alembic.command.stamp') 84 | def test_stamp(self, stamp): 85 | migration.stamp('some_revision') 86 | stamp.assert_called_once_with('fake_config', 'some_revision') 87 | 88 | @mock.patch('alembic.command.stamp') 89 | def test_stamp_without_revision(self, stamp): 90 | migration.stamp(None) 91 | stamp.assert_called_once_with('fake_config', 'head') 92 | 93 | @mock.patch('alembic.command.revision') 94 | def test_revision(self, revision): 95 | migration.revision('some_message', True) 96 | revision.assert_called_once_with('fake_config', 'some_message', True) 97 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | SQLAlchemy>=0.8.3 2 | alembic 3 | beaker 4 | beautifulsoup4 5 | cryptography>=1.0,!=1.3.0 # BSD/Apache-2.0 6 | docutils>=0.11 7 | oslo.config>=1.6.0 # Apache-2.0 8 | oslo.db>=1.4.1 # Apache-2.0 9 | oslo.log>=3.11.0 10 | oslo.utils>=3.16.0 # Apache-2.0 11 | six>=1.9.0 # MIT 12 | pecan>=0.8.2 13 | requests>=2.2.0,!=2.4.0 14 | requests-cache>=0.4.9 15 | jsonschema>=2.0.0,<3.0.0 16 | PyJWT>=1.0.1 # MIT 17 | WebOb>=1.7.1 # MIT 18 | PyMySQL>=0.6.2,!=0.6.4 19 | -------------------------------------------------------------------------------- /setup-mysql-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | 3 | wait_for_line () { 4 | while read line 5 | do 6 | echo "$line" | grep -q "$1" && break 7 | done < "$2" 8 | # Read the fifo for ever otherwise process would block 9 | cat "$2" >/dev/null & 10 | } 11 | 12 | # insert sbin into path if it exists and isnt already there 13 | echo $PATH | grep -q "/usr/sbin" 14 | 15 | if [ $? -ne 0 ] && [ -d "/usr/sbin" ]; then 16 | echo "SBIN NOT IN PATH" 17 | export PATH="$PATH:/usr/sbin" 18 | echo "$PATH" 19 | fi 20 | 21 | # If test DB url is provided, run tests with it 22 | if [[ "$REFSTACK_TEST_MYSQL_URL" ]] 23 | then 24 | $* 25 | exit $? 26 | fi 27 | 28 | # Else setup mysql base for tests. 29 | # Start MySQL process for tests 30 | MYSQL_DATA=`mktemp -d /tmp/refstack-mysql-XXXXX` 31 | mkfifo ${MYSQL_DATA}/out 32 | # On systems like Fedora here's where mysqld can be found 33 | PATH=$PATH:/usr/libexec 34 | mysqld --no-defaults --datadir=${MYSQL_DATA} --pid-file=${MYSQL_DATA}/mysql.pid --socket=${MYSQL_DATA}/mysql.socket --skip-networking --skip-grant-tables &> ${MYSQL_DATA}/out & 35 | # Wait for MySQL to start listening to connections 36 | wait_for_line "mysqld: ready for connections." ${MYSQL_DATA}/out 37 | export REFSTACK_TEST_MYSQL_URL="mysql+pymysql://root@localhost/test?unix_socket=${MYSQL_DATA}/mysql.socket&charset=utf8" 38 | mysql --no-defaults -S ${MYSQL_DATA}/mysql.socket -e 'set @@global.show_compatibility_56=ON;' > /dev/null 2>&1 39 | mysql --no-defaults -S ${MYSQL_DATA}/mysql.socket -e 'CREATE DATABASE test;' 40 | 41 | # Yield execution to venv command 42 | $* 43 | 44 | # Cleanup after tests 45 | ret=$? 46 | kill $(jobs -p) 47 | rm -rf "${MYSQL_DATA}" 48 | exit $ret 49 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = refstack 3 | summary = OpenStack interop testing 4 | description-file = 5 | README.rst 6 | author = OpenStack 7 | author-email = openstack-discuss@lists.openstack.org 8 | home-page = https://refstack.openstack.org 9 | classifier = 10 | Environment :: OpenStack 11 | Intended Audience :: Developers 12 | Intended Audience :: Information Technology 13 | License :: OSI Approved :: Apache Software License 14 | Operating System :: POSIX :: Linux 15 | Programming Language :: Python 16 | Programming Language :: Python :: 2 17 | Programming Language :: Python :: 2.7 18 | Programming Language :: Python :: 3 19 | Programming Language :: Python :: 3.5 20 | 21 | [files] 22 | packages = 23 | refstack 24 | 25 | scripts = 26 | bin/refstack-manage 27 | bin/refstack-api 28 | 29 | [global] 30 | setup-hooks = 31 | pbr.hooks.setup_hook 32 | 33 | [entry_points] 34 | oslo.config.opts = 35 | refstack = refstack.opts:list_opts 36 | 37 | [build_sphinx] 38 | all_files = 1 39 | build-dir = doc/build 40 | source-dir = doc/source 41 | 42 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2014 Piston Cloud Computing, inc. all rights reserved 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | # implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import setuptools 18 | 19 | setuptools.setup( 20 | setup_requires=['pbr'], 21 | pbr=True) 22 | -------------------------------------------------------------------------------- /specs/README.rst: -------------------------------------------------------------------------------- 1 | ======================= 2 | Refstack Specifications 3 | ======================= 4 | 5 | This folder is used to hold design specifications for additions 6 | to the RefStack project. Reviews of the specs are done in gerrit, using a 7 | similar workflow to how we review and merge changes to the code itself. 8 | 9 | The layout of this folder is as follows:: 10 | 11 | specs// 12 | specs//approved 13 | specs//implemented 14 | 15 | The lifecycle of a specification 16 | -------------------------------- 17 | 18 | Specifications are proposed by adding an .rst file to the 19 | ``specs//approved`` directory and posting it for review. You can 20 | find an example specification in ``/specs/template.rst``. 21 | 22 | Once a specification has been fully implemented, meaning a patch has landed, 23 | it will be moved to the ``implemented`` directory and the corresponding 24 | blueprint will be marked as complete. 25 | 26 | `Specifications are only approved for a single release`. If a specification 27 | was previously approved but not implemented (or not completely implemented), 28 | then the specification needs to be re-proposed by copying (not move) it to 29 | the right directory for the current release. 30 | 31 | Previously approved specifications 32 | ---------------------------------- 33 | 34 | The RefStack specs directory was re-structured during the Mitaka cycle. 35 | Therefore, the specs approved and implemented prior to the Mitaka cycle will be 36 | saved in the ``specs/prior/`` directories. 37 | 38 | Others 39 | ------ 40 | 41 | Please note, Launchpad blueprints are still used for tracking the status of the 42 | blueprints. For more information, see:: 43 | 44 | https://wiki.openstack.org/wiki/Blueprints 45 | https://blueprints.launchpad.net/refstack 46 | 47 | For more information about working with gerrit, see:: 48 | 49 | http://docs.openstack.org/infra/manual/developers.html#development-workflow 50 | 51 | To validate that the specification is syntactically correct (i.e. get more 52 | confidence in the Jenkins result), please execute the following command:: 53 | 54 | $ tox 55 | -------------------------------------------------------------------------------- /specs/mitaka/implemented/rsa-key-existence-check.rst: -------------------------------------------------------------------------------- 1 | ============================================== 2 | RSA Key Existence Check for Signed Data Upload 3 | ============================================== 4 | 5 | Launchpad blueprint: 6 | 7 | 8 | RefStack recently added features to enable the uploading of data with key. 9 | Currently, RefStack accepts the uploaded data regardless of whether 10 | the public keys exist in RefStack or not. This document describes the 11 | validation process update needed to ensure that RefStack only accepts 12 | data with those keys that are previously imported into RefStack. 13 | 14 | 15 | Problem description 16 | =================== 17 | 18 | Currently, the RefStack API server would accept the uploaded data regardless 19 | of whether the keys exist in RefStack or not. More importantly, those keys are 20 | used to associated the test data to the users. And, there is no enforcement 21 | that the keys used for data uploading must exist in RefStack. In addition, 22 | for security reasons, keys are expected to be updated from time to time. 23 | As a consequence of the non-existing or updated keys, some data will be 24 | inaccessible. 25 | 26 | 27 | Proposed change 28 | =============== 29 | 30 | * RefStack API servers will check whether the key used to upload data exists in 31 | the 'pubkeys' table and reject the data if it does not. Note that this method 32 | of checking is possible because RefStack currently enforces a policy such 33 | that there are no duplicate public keys in the database. This implies that 34 | no two users can have the same public key uploaded, key-pairs can not be 35 | shared, and if a user creates a new openstackid account, he/she would have to 36 | use a different key or delete the public key from his/her old account. 37 | 38 | * RefStack then associate the data to the user ID of the key owner by adding, 39 | in the "meta" table, a "meta_key" named "user" with value being the "openid" 40 | from the "user" table. 41 | 42 | 43 | Alternatives 44 | ------------ 45 | 46 | Alternatively, if RefStack wants to allow for key sharing among users in the 47 | future, an additional user identifier parameter such as user email is needed, 48 | besides the key, for data uploading. In this case, RefStack will check for 49 | for the existence of the key in the user's profile. 50 | 51 | As for orphan data management, RefStack may want to implement a limited life 52 | time policy for data without owner associated to them. 53 | 54 | Open to other suggestions. 55 | 56 | 57 | Data model impact 58 | ----------------- 59 | 60 | None. 61 | 62 | There is no data modal change needed. 63 | 64 | 65 | REST API impact 66 | --------------- 67 | 68 | None 69 | 70 | 71 | Security impact 72 | --------------- 73 | 74 | None 75 | 76 | Notifications impact 77 | -------------------- 78 | 79 | None 80 | 81 | Other end user impact 82 | --------------------- 83 | 84 | None 85 | 86 | Performance Impact 87 | ------------------ 88 | 89 | None 90 | 91 | Other deployer impact 92 | --------------------- 93 | 94 | None 95 | 96 | 97 | Developer impact 98 | ---------------- 99 | 100 | None 101 | 102 | 103 | Implementation 104 | ============== 105 | 106 | Assignee(s) 107 | ----------- 108 | 109 | Primary assignee: 110 | TBD 111 | 112 | Other contributors: 113 | TBD 114 | 115 | Work Items 116 | ---------- 117 | 118 | * RefStack API server will need to validate the existing of the key in RefStack 119 | 120 | 121 | Dependencies 122 | ============ 123 | 124 | None 125 | 126 | 127 | Testing 128 | ======= 129 | 130 | None 131 | 132 | 133 | Documentation Impact 134 | ==================== 135 | 136 | None 137 | 138 | 139 | References 140 | ========== 141 | 142 | None 143 | -------------------------------------------------------------------------------- /specs/mitaka/implemented/use-url-as-cpid.rst: -------------------------------------------------------------------------------- 1 | ============================================== 2 | Use Cloud URL as the Cloud Provider ID (CPID) 3 | ============================================== 4 | 5 | Launchpad blueprint: 6 | 7 | This spec proposes RefStack to add a method to use the cloud 8 | access URL as the base to generate the CPID. 9 | 10 | 11 | Problem description 12 | =================== 13 | 14 | As defined in the "Test Submission API should use Target Identity 15 | Endpoint UUID" specification (refstack-org-api-cloud-uuid.rst). Currently, 16 | RefStack uses the cloud's Identity (Keystone) UUID as the CPID. 17 | 18 | For Keystone V2 API, this ID can be the ID of any one of the 19 | three access endpoints, namely admin, public or private endpoints. However, 20 | for Keystone V3 API, this ID is the ID of the Keystone service. Furthermore, 21 | when testing a distro product, the Identity ID will be different every time 22 | a cloud is stood up, regardless that whether this cloud is built by the 23 | same person, with exactly the same OpenStack code and configuration. In such 24 | circumstances, multiple CPIDs could represent a single cloud. 25 | 26 | We have also encountered some cases that the cloud's Keystone does not even 27 | returns the identity service ID in the tokens it returns. In addition, there 28 | is recent request for RefStack to support uploading test results that were 29 | not collected using refstack-client. These type of data in subunit format 30 | won't have CPID created at testing time. RefStack should provide a method 31 | to generate CPID without the need of re-connecting to the cloud again. 32 | 33 | 34 | Proposed change 35 | =============== 36 | 37 | In addition to the current practice of using the different types of Identity 38 | ID for CPID, RefStack should add additional support to generate the 39 | CPID based on the cloud URL. This will also be used as the failover method 40 | for CPID. 41 | 42 | 43 | Alternatives 44 | ------------ 45 | 46 | For consistency, RefStack should consider to only use the cloud access URL 47 | to generate the UUID for CPID. In consequence, RefStack no longer has to keep 48 | track and adjust to changes in Keystone client and API for retrieving the 49 | the CPID. 50 | 51 | Open to other suggestions. 52 | 53 | 54 | Data model impact 55 | ----------------- 56 | 57 | None. 58 | 59 | There is no data modal change needed. 60 | 61 | 62 | REST API impact 63 | --------------- 64 | 65 | None 66 | 67 | 68 | Security impact 69 | --------------- 70 | 71 | None 72 | 73 | Notifications impact 74 | -------------------- 75 | 76 | None 77 | 78 | Other end user impact 79 | --------------------- 80 | 81 | With this failover addition, refstack-client should never again fail due to 82 | CPID retrieval error. This also allows RefStack to provide users with an 83 | option to upload data in subunit format. 84 | 85 | 86 | Performance Impact 87 | ------------------ 88 | 89 | None 90 | 91 | Other deployer impact 92 | --------------------- 93 | 94 | There is possibility of CPIDs being the same for two different clouds. 95 | This can happen primarily in the private address space, where people may 96 | have use the same IP address such as 192.168.*.* (or whatever commonly used 97 | default addresses) for keystone address. Since this likely won't be the case 98 | with actual production clouds and it is a last resort, we are okay with this 99 | possibility. 100 | 101 | Furthermore, RefStack is no longer completely dependent on whether or not 102 | the cloud's Keystone even returns the Identity service ID in the tokens it 103 | returns. 104 | 105 | Developer impact 106 | ---------------- 107 | 108 | None 109 | 110 | Implementation 111 | ============== 112 | 113 | Assignee(s) 114 | ----------- 115 | 116 | Primary assignee: 117 | TBD 118 | 119 | Other contributors: 120 | TBD 121 | 122 | Work Items 123 | ---------- 124 | 125 | * Develop code to generate CPID based on access URL 126 | 127 | 128 | Dependencies 129 | ============ 130 | 131 | None 132 | 133 | 134 | Testing 135 | ======= 136 | 137 | None 138 | 139 | 140 | Documentation Impact 141 | ==================== 142 | 143 | None 144 | 145 | 146 | References 147 | ========== 148 | 149 | None 150 | -------------------------------------------------------------------------------- /specs/pike/approved/add-refstack-docs.rst: -------------------------------------------------------------------------------- 1 | ============================================= 2 | Displaying RefStack Documentation Directly on Website 3 | ============================================= 4 | 5 | Launchpad blueprint: 6 | 7 | * https://blueprints.launchpad.net/refstack/+spec/user-documentation 8 | 9 | This specification defines the changes to the "About" page of the RefStack 10 | website in that are necessary in order to allow RefStack documentation to be 11 | displayed natively on the RefStack site. 12 | 13 | Problem description 14 | =================== 15 | 16 | To make RefStack information more accessible to users, RefStack documentation 17 | should be displayed in a format more closely matching that of the rest of 18 | the RefStack site. Currently, documentation is maintained as RST files in the 19 | ‘doc’ folder of the RefStack repository, but with this change, users will also 20 | be able to view them as HTML files via the RefStack site. 21 | 22 | 23 | Proposed change 24 | =============== 25 | 26 | As mentioned above, it would be ideal to be able to access RefStack 27 | documentation in HTML format. The current plan is to use docutills in 28 | combination with sphinx in order to create HTML templates which will then 29 | be able to be integrated into the existing RefStack website. 30 | 31 | Another goal of this documentation update will be to a duplicate set of docs 32 | intended for users from the rest of the docs, in order to ensure that they 33 | will be more easily accessed by end users. These docs will be displayed on the 34 | the RefStack website. A second set of docs, the RefStack Project docs, 35 | will be hosted at the OpenStack docs website. These will be the same docs 36 | that are published in the RefStack repo in RST format. 37 | 38 | 39 | Possible libraries to use: 40 | 41 | sphinx 42 | 43 | docutils 44 | 45 | Alternatives 46 | ------------ 47 | 48 | Data model impact 49 | ----------------- 50 | 51 | None 52 | 53 | REST API impact 54 | --------------- 55 | 56 | None 57 | 58 | Security impact 59 | --------------- 60 | 61 | None 62 | 63 | Notifications impact 64 | -------------------- 65 | 66 | None 67 | 68 | Other end user impact 69 | --------------------- 70 | 71 | None 72 | 73 | Performance Impact 74 | ------------------ 75 | 76 | None 77 | 78 | Other deployer impact 79 | --------------------- 80 | 81 | None 82 | 83 | Developer impact 84 | ---------------- 85 | 86 | None 87 | 88 | Implementation 89 | ============== 90 | 91 | Assignee(s) 92 | ----------- 93 | 94 | Primary assignee: 95 | Paul Van Eck 96 | 97 | Other contributors: 98 | Luz Cazares 99 | 100 | Work Items 101 | ---------- 102 | 103 | None 104 | 105 | Dependencies 106 | ============ 107 | 108 | None 109 | 110 | Testing 111 | ======= 112 | 113 | None 114 | 115 | Documentation Impact 116 | ==================== 117 | 118 | User specific documents will now be available on the RefStack website in 119 | simple HTML format. It will be listed under the "About" section on the main 120 | menu bar. This will be a change from the current state in that users will now 121 | be able to view documentation concerning running tests and uploading results 122 | in a format which is similar to the rest of the RefStack website. 123 | 124 | RefStack documentation will now also be available on the main OpenStack docs 125 | site. These docs will use the same source as those hosted on the RefStack site. 126 | 127 | References 128 | ========== 129 | 130 | -------------------------------------------------------------------------------- /specs/prior/approved/ability_to_upload_a_complete_tempest_config.rst: -------------------------------------------------------------------------------- 1 | =========================================== 2 | Ability to upload a complete tempest config 3 | =========================================== 4 | 5 | storyboard: https://storyboard.openstack.org/#!/story/105 6 | 7 | **Problem description** 8 | 9 | It makes sense that if an admin already has a working and tested tempest config, 10 | they should be able to use it with the refstack tester. 11 | 12 | **Proposed change** 13 | 14 | Allowing the user to use a custom tempest config would require changes to the 15 | tester cli as well as the web interface. We can safely break the code commits 16 | for this into two tasks. 17 | 18 | * The CLI would require an extra argument for a path to a config file. As well 19 | as some logic that bypassed the internal config generation. 20 | 21 | **Alternatives** 22 | 23 | None off hand. 24 | 25 | **Data model impact** 26 | 27 | None. 28 | 29 | **REST API impact** 30 | 31 | None 32 | 33 | **Performance Impact** 34 | 35 | This should speed up the tester because now it will not have to 36 | generate/discover config or prepare the cloud to match config options. 37 | 38 | **Developer impact** 39 | 40 | n/a 41 | 42 | **Implementation:** 43 | 44 | **Assignee(s)** 45 | 46 | Primary assignee: 47 | dlenwell 48 | 49 | **Work Items** 50 | 51 | * Implement CLI code 52 | 53 | **Dependencies** 54 | 55 | N/A 56 | 57 | **Testing** 58 | 59 | N/A 60 | 61 | **Documentation Impact** 62 | 63 | Cli changes should be noted in the --help output as well as written into any documentation for the tester. 64 | 65 | **References** 66 | 67 | N/A 68 | 69 | -------------------------------------------------------------------------------- /specs/prior/implemented/coretest-testid.rst: -------------------------------------------------------------------------------- 1 | 2 | ================================================= 3 | Use fully qualified test id in the coretests file 4 | ================================================= 5 | 6 | Launchpad blueprint: 7 | 8 | 9 | This document describes the format for the test ids that will be used in the 10 | DefCore coretests.json file. 11 | 12 | 13 | Problem description 14 | =================== 15 | 16 | The coretests.json file includes a list of tests that are defined as "core" by 17 | the DefCore committee. Currently, the coretests.json file (in the 18 | defcore/havana directory) uses the method names defined in the Tempest Python 19 | test classes as the test names. While these method names are unique in Havana, 20 | it is not the case in Icehouse where some of the method names are being used by 21 | multiple test classes of different OpenStack components. 22 | 23 | 24 | Proposed change 25 | =============== 26 | 27 | The proposal is to adopt the test id as used by the subunit package to identify 28 | each individual test. The test id is a fully qualified name which includes the 29 | fully qualified class name of the Python test class and the method name. Using 30 | this test id format will also help the performance of processing subunit test 31 | results against the core tests list for compliance checking. 32 | 33 | The following is an example which shows how the test_get_default_quotas test is 34 | currently defined in the coretests.json file versus the proposed test id format. 35 | 36 | * Current definition 37 | 38 | .. parsed-literal:: 39 | "test_access_public_container_object_without_using_creds":\ 40 | { "file": "test_object_services.py" } 41 | 42 | * Proposed test id format 43 | 44 | .. parsed-literal:: 45 | "tempest.api.object_storage.test_object_services.PublicObjectTest.\ 46 | test_access_public_container_object_without_using_creds" 47 | 48 | 49 | Alternatives 50 | ------------ 51 | 52 | Open to suggestions on better ways to uniquely identify each test case with run 53 | time processing performance in mind. 54 | 55 | 56 | Data model impact 57 | ----------------- 58 | 59 | None 60 | 61 | 62 | REST API impact 63 | --------------- 64 | 65 | None 66 | 67 | 68 | Security impact 69 | --------------- 70 | 71 | None 72 | 73 | Notifications impact 74 | -------------------- 75 | 76 | None 77 | 78 | Other end user impact 79 | --------------------- 80 | 81 | None 82 | 83 | Performance Impact 84 | ------------------ 85 | 86 | Using the test id will help the performance of run time result processing. 87 | 88 | 89 | Other deployer impact 90 | --------------------- 91 | 92 | None 93 | 94 | 95 | Developer impact 96 | ---------------- 97 | 98 | None 99 | 100 | 101 | Implementation 102 | ============== 103 | 104 | Assignee(s) 105 | ----------- 106 | 107 | Primary assignee: 108 | Catherine Diep 109 | 110 | Other contributors: 111 | Rob Hirschfeld 112 | 113 | Work Items 114 | ---------- 115 | 116 | * Catherine to create the corresponding test id from the tests listed in the 117 | coretests.json file. 118 | * Rob Hirschfeld to review and validate the result test id list 119 | 120 | 121 | Dependencies 122 | ============ 123 | 124 | None 125 | 126 | 127 | Testing 128 | ======= 129 | 130 | None 131 | 132 | 133 | Documentation Impact 134 | ==================== 135 | 136 | None 137 | 138 | 139 | References 140 | ========== 141 | 142 | None 143 | -------------------------------------------------------------------------------- /specs/prior/implemented/seperate_refstack_tester_from_refstack.rst: -------------------------------------------------------------------------------- 1 | ====================================== 2 | separate refstack tester from refstack 3 | ====================================== 4 | 5 | **Problem description** 6 | 7 | The refstack tester needs to be easily installable on its own without checking 8 | out the refstack code. 9 | 10 | **Proposed change** 11 | 12 | This would require taking the code that lives in refstack/tools/tester and moving 13 | it into its own repository. probably in openstack-infra. 14 | 15 | **Alternatives** 16 | 17 | we could leave the code were it is and force anything that wants to install it to 18 | checkout the whole of refstack to do so. 19 | 20 | **Data model impact** 21 | 22 | none. 23 | 24 | **REST API impact** 25 | 26 | none. 27 | 28 | **Security impact** 29 | 30 | * Does this change touch sensitive data such as tokens, keys, or user data? **no** 31 | 32 | * Does this change alter the API in a way that may impact security, such as 33 | a new way to access sensitive information or a new way to login? **no** 34 | 35 | * Does this change involve cryptography or hashing? **no** 36 | 37 | * Does this change require the use of sudo or any elevated privileges? **no** 38 | 39 | * Does this change involve using or parsing user-provided data? This could 40 | be directly at the API level or indirectly such as changes to a cache layer. **no** 41 | 42 | * Can this change enable a resource exhaustion attack, such as allowing a 43 | single API interaction to consume significant server resources? Some examples 44 | of this include launching subprocesses for each connection, or entity 45 | expansion attacks in XML. **no** 46 | 47 | **Notifications impact** 48 | 49 | none. 50 | 51 | **Other end user impact** 52 | 53 | The tester would need to remain compatible with the v1 api spec. 54 | 55 | **Performance Impact** 56 | 57 | none/ 58 | 59 | **Developer impact** 60 | 61 | When finished tcup would need to have the tester as a dependency. 62 | 63 | **Implementation:** 64 | 65 | **Assignee(s)** 66 | 67 | Primary assignee: 68 | dlenwell 69 | 70 | **Work Items** 71 | 72 | * put code from refstack/tools/tester in external github repo (i.e. github.com/dlenwell/refstack-tester) 73 | * add basic unit and node test to new project to insure it works in ci 74 | * create project in openstack-infra/config for project with the above repo set as its upstream. 75 | * insure that project has the enable storyboard flag. 76 | * add refstack-tester to requirements.txt in refstack (will still be needed by tcup) 77 | * deprecate code in refstack/tools/tester 78 | 79 | **Dependencies** 80 | 81 | none. 82 | 83 | **Testing** 84 | 85 | The new project will require a base set of tests so that ci works properly. 86 | 87 | **Documentation Impact** 88 | 89 | Since we have not written docs for the api.. let this document serve as the 90 | starting place for that. 91 | 92 | **References** 93 | 94 | N/A 95 | -------------------------------------------------------------------------------- /specs/prior/implemented/simplify-uploads-by-only-sending-pass-results.rst: -------------------------------------------------------------------------------- 1 | ============================================= 2 | Simplify Uploads by only sending Pass results 3 | ============================================= 4 | 5 | Blueprint: https://blueprints.launchpad.net/refstack/+spec/pass-only-uploads 6 | Storyboard: https://storyboard.openstack.org/#!/story/108 7 | 8 | As part of helping the community accept the publication of results, refstack 9 | test uploads should default to only PASS results. We are NOT uploading skips 10 | or errors. This aligns with the interop objective because we want have a 11 | positive assertion. 12 | 13 | Problem description 14 | =================== 15 | 16 | Because fail results often include sensitive information it has been a pain 17 | point for some of our early adopters to upload results to a foundation 18 | controlled database. Since refstack really only cares about the things that 19 | pass, we'll just parse the results and leave the fails out. 20 | 21 | Proposed change 22 | =============== 23 | 24 | This would involve using the subunit parsing code inside the tester. We would 25 | run the parser on the subunit before uploading the results to the api. 26 | 27 | We'll want to have a non-default option to send all data. Because I am sure 28 | that some folks will want to use refstack internally for real debugging and 29 | test regression. 30 | 31 | If a user tries to submit results to the public api server (Regardless of what 32 | the non-defaulting option is set to.). It will either produce an error (which I 33 | don't think is as useful) or it will just scrub the results anyways. 34 | 35 | Alternatives 36 | ------------ 37 | 38 | We could take the full upload and do the processing server side to accomplish 39 | the DefCore objective; however, that does not avoid the data leak issue. 40 | 41 | Data model impact 42 | ----------------- 43 | 44 | None 45 | 46 | REST API impact 47 | --------------- 48 | 49 | None 50 | 51 | Security impact 52 | --------------- 53 | 54 | This change addresses security concerns. as far as I can tell it will not 55 | create any new ones. 56 | 57 | Notifications impact 58 | -------------------- 59 | 60 | It should mention that results are being scrubbed in the on screen log messages. 61 | so that it's clear on every action. 62 | 63 | Other end user impact 64 | --------------------- 65 | 66 | None 67 | 68 | Performance Impact 69 | ------------------ 70 | 71 | This will slow down the tester but not by a lot. However it will also speed up 72 | the upload of the results since they will be trimmed. 73 | 74 | Other deployer impact 75 | --------------------- 76 | 77 | None 78 | 79 | Developer impact 80 | ---------------- 81 | 82 | This could potentially be a maintenance problem moving forward as we move the 83 | subunit parsing utils into tempest then move the tester into its own repo. 84 | 85 | It will require that someone stays on top of things during those changes to avoid 86 | duplication of code. 87 | 88 | 89 | Implementation 90 | ============== 91 | 92 | Assignee(s) 93 | ----------- 94 | 95 | Primary assignee: 96 | dlenwell 97 | 98 | Other contributors: 99 | catherine wrote the parsing utils. So some support might be needed. 100 | 101 | Work Items 102 | ---------- 103 | 104 | The tester already has a stubbed out function for this code. Just needs 105 | to be filled in. 106 | 107 | Dependencies 108 | ============ 109 | 110 | None 111 | 112 | Testing 113 | ======= 114 | 115 | None 116 | 117 | Documentation Impact 118 | ==================== 119 | 120 | It should be written down that we aren't uploading fails. So its known to 121 | operators and they don't have to be concerned about security leaks. 122 | 123 | But outside of that I don't see the need for documentation. 124 | 125 | 126 | References 127 | ========== 128 | 129 | N/A 130 | -------------------------------------------------------------------------------- /test-requirements.txt: -------------------------------------------------------------------------------- 1 | coverage>=3.6 2 | pep8==1.5.7 3 | pyflakes==0.8.1 4 | flake8==2.2.4 5 | docutils>=0.11 # OSI-Approved Open Source, Public Domain 6 | httmock>=1.2.4 7 | mock 8 | oslotest>=1.2.0 # Apache-2.0 9 | python-subunit>=0.0.18 10 | testrepository>=0.0.18 11 | testtools>=0.9.34 12 | pep257>=0.5.0 13 | PyMySQL>=0.6.2,!=0.6.4 14 | -------------------------------------------------------------------------------- /tools/convert-docs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2017 IBM, Inc. 3 | # All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | """ 18 | Convert RST files to basic HTML. The primary use case is to provide a way 19 | to display RefStack documentation on the RefStack website. 20 | """ 21 | 22 | import argparse 23 | import glob 24 | import os 25 | 26 | from bs4 import BeautifulSoup 27 | from docutils.core import publish_file 28 | 29 | 30 | def extract_body(html): 31 | """Extract the content of the body tags of an HTML string.""" 32 | soup = BeautifulSoup(html, "html.parser") 33 | return ''.join(['%s' % str(a) for a in soup.body.contents]) 34 | 35 | 36 | if __name__ == '__main__': 37 | parser = argparse.ArgumentParser( 38 | description='Convert RST files to basic HTML template files.' 39 | ) 40 | parser.add_argument('files', 41 | metavar='file', 42 | nargs='+', 43 | help='RST file(s) to be converted to HTML templates.') 44 | parser.add_argument('-o', '--output_dir', 45 | required=False, 46 | help='The directory where template files should be ' 47 | 'output to. Defaults to the current directory.') 48 | args = parser.parse_args() 49 | 50 | if args.output_dir: 51 | output_dir = args.output_dir 52 | # If the output directory doesn't exist, create it. 53 | if not os.path.exists(output_dir): 54 | try: 55 | os.makedirs(output_dir) 56 | except OSError: 57 | if not os.path.isdir(output_dir): 58 | raise 59 | else: 60 | output_dir = os.getcwd() 61 | 62 | for path in args.files: 63 | for file in glob.glob(path): 64 | base_file = os.path.splitext(os.path.basename(file))[0] 65 | 66 | # Calling publish_file will also print to stdout. Destination path 67 | # is set to /dev/null to suppress this. 68 | html = publish_file(source_path=file, 69 | destination_path='/dev/null', 70 | writer_name='html',) 71 | body = extract_body(html) 72 | 73 | output_file = os.path.join(output_dir, base_file + ".html") 74 | with open(output_file, "w") as template_file: 75 | template_file.write(body) 76 | -------------------------------------------------------------------------------- /tools/cover.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2015: Mirantis Inc. 4 | # All Rights Reserved. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | # not use this file except in compliance with the License. You may obtain 8 | # a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | # License for the specific language governing permissions and limitations 16 | # under the License. 17 | 18 | ALLOWED_EXTRA_MISSING=30 19 | 20 | show_diff () { 21 | head -1 $1 22 | diff -U 0 $1 $2 | sed 1,2d 23 | } 24 | 25 | # Stash uncommited changes, checkout master and save coverage report 26 | uncommited=$(git status --porcelain | grep -v "^??") 27 | [[ -n $uncommited ]] && git stash > /dev/null 28 | git checkout HEAD^ 29 | 30 | baseline_report=$(mktemp -t refstack_coverageXXXXXXX) 31 | find . -type f -name "*.pyc" -delete && python setup.py testr --coverage --testr-args="$*" 32 | coverage report > $baseline_report 33 | baseline_missing=$(awk 'END { print $3 }' $baseline_report) 34 | 35 | # Checkout back and unstash uncommited changes (if any) 36 | git checkout - 37 | [[ -n $uncommited ]] && git stash pop > /dev/null 38 | 39 | # Generate and save coverage report 40 | current_report=$(mktemp -t refstack_coverageXXXXXXX) 41 | find . -type f -name "*.pyc" -delete && python setup.py testr --coverage --testr-args="$*" 42 | coverage report > $current_report 43 | current_missing=$(awk 'END { print $3 }' $current_report) 44 | 45 | baseline_percentage=$(awk 'END { print $4 }' $baseline_report) 46 | current_percentage=$(awk 'END { print $4 }' $current_report) 47 | # Show coverage details 48 | allowed_missing=$((baseline_missing+ALLOWED_EXTRA_MISSING)) 49 | 50 | echo "Baseline report: $(cat ${baseline_report})" 51 | echo "Proposed change report: $(cat ${current_report})" 52 | echo "" 53 | echo "" 54 | echo "Allowed to introduce missing lines : ${ALLOWED_EXTRA_MISSING}" 55 | echo "Missing lines in master : ${baseline_missing}" 56 | echo "Missing lines in proposed change : ${current_missing}" 57 | echo "Current percentage : ${baseline_percentage}" 58 | echo "Proposed change percentage : ${current_percentage}" 59 | 60 | if [[ $allowed_missing -gt $current_missing ]]; 61 | then 62 | if [[ $baseline_missing -lt $current_missing ]]; 63 | then 64 | show_diff $baseline_report $current_report 65 | echo "I believe you can cover all your code with 100% coverage!" 66 | else 67 | echo "Thank you! You are awesome! Keep writing unit tests! :)" 68 | fi 69 | exit_code=0 70 | else 71 | show_diff $baseline_report $current_report 72 | echo "Please write more unit tests, we should keep our test coverage :( " 73 | exit_code=1 74 | fi 75 | 76 | rm $baseline_report $current_report 77 | exit $exit_code 78 | -------------------------------------------------------------------------------- /tools/install-js-tools.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2017 Red Hat, Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | # implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | if [ $EUID -ne 0 ] ; then 17 | SUDO='sudo -E' 18 | fi 19 | 20 | if type apt-get; then 21 | # Install https transport - otherwise apt-get HANGS on https urls 22 | # Install curl so the curl commands work 23 | # Install gnupg2 so that the apt-key add works 24 | $SUDO apt-get update 25 | $SUDO apt-get install -y apt-transport-https curl gnupg2 26 | # Install recent NodeJS repo 27 | curl -sS https://deb.nodesource.com/gpgkey/nodesource.gpg.key | $SUDO apt-key add - 28 | echo "deb https://deb.nodesource.com/node_10.x bionic main" | $SUDO tee /etc/apt/sources.list.d/nodesource.list 29 | # Install yarn repo 30 | curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | $SUDO apt-key add - 31 | echo "deb https://dl.yarnpkg.com/debian/ stable main" | $SUDO tee /etc/apt/sources.list.d/yarn.list 32 | $SUDO apt-get update 33 | DEBIAN_FRONTEND=noninteractive \ 34 | $SUDO apt-get -q --option "Dpkg::Options::=--force-confold" --assume-yes \ 35 | install nodejs yarn 36 | elif type yum; then 37 | $SUDO curl https://dl.yarnpkg.com/rpm/yarn.repo -o /etc/yum.repos.d/yarn.repo 38 | $SUDO $(dirname $0)/install-js-repos-rpm.sh 39 | $SUDO yum -y install nodejs yarn 40 | elif type zypper; then 41 | $SUDO zypper install -y nodejs10 npm10 42 | $SUDO npm install yarn 43 | elif type brew; then 44 | brew install nodejs yarn 45 | else 46 | echo "Unsupported platform" 47 | fi 48 | -------------------------------------------------------------------------------- /tools/test-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -xe 2 | 3 | # This script will be run by OpenStack CI before unit tests are run, 4 | # it sets up the test system as needed. 5 | # Developers should setup their test systems in a similar way. 6 | 7 | # This setup needs to be run as a user that can run sudo. 8 | 9 | # The root password for the MySQL database; pass it in via 10 | # MYSQL_ROOT_PW. 11 | DB_ROOT_PW=${MYSQL_ROOT_PW:-insecure_slave} 12 | 13 | # This user and its password are used by the tests, if you change it, 14 | # your tests might fail. 15 | DB_USER=openstack_citest 16 | DB_PW=openstack_citest 17 | 18 | sudo -H mysqladmin -u root password $DB_ROOT_PW 19 | 20 | # It's best practice to remove anonymous users from the database. If 21 | # a anonymous user exists, then it matches first for connections and 22 | # other connections from that host will not work. 23 | sudo -H mysql -u root -p$DB_ROOT_PW -h localhost -e " 24 | DELETE FROM mysql.user WHERE User=''; 25 | FLUSH PRIVILEGES; 26 | GRANT ALL PRIVILEGES ON *.* 27 | TO '$DB_USER'@'%' identified by '$DB_PW' WITH GRANT OPTION;" 28 | 29 | # Now create our database. 30 | mysql -u $DB_USER -p$DB_PW -h 127.0.0.1 -e " 31 | SET default_storage_engine=MYISAM; 32 | DROP DATABASE IF EXISTS openstack_citest; 33 | CREATE DATABASE openstack_citest CHARACTER SET utf8;" 34 | 35 | # Same for PostgreSQL 36 | # The root password for the PostgreSQL database; pass it in via 37 | # POSTGRES_ROOT_PW. 38 | DB_ROOT_PW=${POSTGRES_ROOT_PW:-insecure_slave} 39 | 40 | # Setup user 41 | root_roles=$(sudo -H -u postgres psql -t -c " 42 | SELECT 'HERE' from pg_roles where rolname='$DB_USER'") 43 | if [[ ${root_roles} == *HERE ]];then 44 | sudo -H -u postgres psql -c "ALTER ROLE $DB_USER WITH SUPERUSER LOGIN PASSWORD '$DB_PW'" 45 | else 46 | sudo -H -u postgres psql -c "CREATE ROLE $DB_USER WITH SUPERUSER LOGIN PASSWORD '$DB_PW'" 47 | fi 48 | 49 | # Store password for tests 50 | cat << EOF > $HOME/.pgpass 51 | *:*:*:$DB_USER:$DB_PW 52 | EOF 53 | chmod 0600 $HOME/.pgpass 54 | 55 | # Now create our database 56 | psql -h 127.0.0.1 -U $DB_USER -d template1 -c "DROP DATABASE IF EXISTS openstack_citest" 57 | createdb -h 127.0.0.1 -U $DB_USER -l C -T template0 -E utf8 openstack_citest 58 | -------------------------------------------------------------------------------- /tools/update-rs-db.rst: -------------------------------------------------------------------------------- 1 | ####################################################################### 2 | # update-rs-db.py # 3 | ####################################################################### 4 | 5 | This document contains some details that are necessary to know to be 6 | successful in the usage of the script update-rs-db.py. 7 | 8 | The script can be run using the following formatting: 9 | "./update-rs-db.py --file /tmp/datasource.csv --endpoint 10 | http://example.com:8000/v1 --tokenfile ". In order to 11 | successfully update and verify results, you will need admin rights 12 | for the refstack server in question. Instructions on how to get 13 | these for your local install can be found at https://github.com/openstack/refstack/blob/master/doc/source/refstack.rst#optional-configure-foundation-organization-and-group 14 | 15 | This script updates RefStack tests as verified given a specific 16 | spreadsheet. The columns in this spreadsheet are, in this order: 17 | 18 | * Company Name 19 | 20 | * Product Name 21 | 22 | * Type (Distribution, Public, or Private) 23 | 24 | * Region 25 | 26 | * Guideline 27 | 28 | * Component (Compute, Platform, or Object) 29 | 30 | * Reported Release 31 | 32 | * Passed Release 33 | 34 | * Federated identity (yes/no) 35 | 36 | * Refstack Link 37 | 38 | * Zendesk Link 39 | 40 | * Marketplace Link 41 | 42 | * License Date 43 | 44 | * Update Product (yes/no) 45 | 46 | * Contacts 47 | 48 | * Notes 49 | 50 | * License Link 51 | 52 | * Active (1 or 0) 53 | 54 | * Public (1 or 0) 55 | 56 | The data is pulled from a csv file. The default csv name is toadd.csv, 57 | but using the -f flag, we can use csv of a different filename. 58 | 59 | The refstack database that we are pushing updates to is set via the "-e", 60 | or "--endpoint flag. This flag specifies the refstack api endpoint to be 61 | used to update the database. This is a required flag. 62 | 63 | Because editing arbitrary test results requires administrative privileges, 64 | an auth token must be used with the RefStack API. This token can be 65 | generated by entering the command "jwt --key="$( cat 66 | )" --alg=RS256 user_openid= exp=+100500". This generates a 67 | json web token, which we must link using the "--tokenfile" flag. Because 68 | we cannot auth without this token, the token is a required flag. 69 | 70 | The script will go through each line of the CSV, grabbing the refstack link, 71 | the guideline, and the component. It also uses the refstack result to get a 72 | test result Id. 73 | 74 | It then uses that test ID to update the internal db using refstack's built 75 | in RESTful api. 76 | 77 | Lastly, if at least one of the links has proven to be valid, we will 78 | then use the same RESTful api and test ID to update the verification_status 79 | field associated with that test result. 80 | 81 | The status of each of these steps will be output to "verification_status.csv" 82 | by default. A '1' will denote that the resource was successfully updated while 83 | a '0' will denote that the resource was not successfully updated. The order of 84 | fields of this file are as follows: 85 | 86 | * Date modified 87 | 88 | * API link 89 | 90 | * Shared update status 91 | 92 | * Guideline 93 | 94 | * Guideline update success status 95 | 96 | * Target 97 | 98 | * Target update success statu 99 | 100 | * Verification update success status 101 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | # py3* tests should be run before py27 3 | # it is a workaround for testr bug 4 | # https://bugs.launchpad.net/testrepository/+bug/1229445 5 | envlist = py35,py36,py27,pep8,pip-check-reqs 6 | minversion = 1.6 7 | skipsdist = True 8 | 9 | [testenv] 10 | usedevelop = True 11 | install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} -U {opts} {packages} 12 | setenv = VIRTUAL_ENV={envdir} 13 | LANG=en_US.UTF-8 14 | LANGUAGE=en_US:en 15 | LC_ALL=C 16 | deps = -r{toxinidir}/requirements.txt 17 | -r{toxinidir}/test-requirements.txt 18 | commands = /bin/rm -f .testrepository/times.dbm 19 | python setup.py testr --slowest --testr-args='{posargs}' 20 | distribute = false 21 | 22 | [testenv:py27-func-mysql] 23 | basepython = python2.7 24 | setenv = SUBUNIT_TEST_PATH=./refstack/tests/api 25 | # Integration/functional tests 26 | # must not be run in parallel (--concurrency=1), 27 | # because each of these tests 28 | # require cleanup of database 29 | commands = {toxinidir}/setup-mysql-tests.sh python setup.py testr --slowest --testr-args='{posargs:--concurrency=1}' 30 | 31 | [testenv:py36-func-mysql] 32 | basepython = python3.6 33 | setenv = SUBUNIT_TEST_PATH=./refstack/tests/api 34 | # Integration/functional tests 35 | # must not be run in parallel (--concurrency=1), 36 | # because each of these tests 37 | # require cleanup of database 38 | commands = {toxinidir}/setup-mysql-tests.sh python setup.py testr --slowest --testr-args='{posargs:--concurrency=1}' 39 | 40 | 41 | [testenv:pep8] 42 | commands = 43 | flake8 {posargs} 44 | flake8 --filename=refstack* bin 45 | pep257 refstack 46 | distribute = false 47 | 48 | [testenv:genconfig] 49 | commands = 50 | oslo-config-generator --output-file etc/refstack.conf.sample \ 51 | --namespace refstack \ 52 | --namespace oslo.db \ 53 | --namespace oslo.log 54 | 55 | [testenv:venv] 56 | commands = {posargs} 57 | 58 | [testenv:gen-cover] 59 | commands = python setup.py testr --coverage \ 60 | --omit='{toxinidir}/refstack/tests/unit/*,{toxinidir}/refstack/tests/api/*,{toxinidir}/refstack/api/config.py,{toxinidir}/refstack/db/migrations/alembic/*,{toxinidir}/refstack/opts.py' \ 61 | --testr-args='{posargs}' 62 | 63 | [testenv:cover] 64 | commands = {toxinidir}/tools/cover.sh {posargs} 65 | 66 | [testenv:docs] 67 | deps = -r{toxinidir}/doc/requirements.txt 68 | commands = sphinx-build -b html doc/source doc/build/html 69 | 70 | [flake8] 71 | # E125 continuation line does not distinguish itself from next logical line 72 | # H404 multi line docstring should start with a summary 73 | ignore = E125,H404 74 | enable-extensions = H203 75 | show-source = true 76 | builtins = _ 77 | exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build 78 | 79 | [testenv:pip-check-reqs] 80 | # Do not install test-requirements as that will pollute the virtualenv for 81 | # determining missing packages. 82 | # This also means that pip-check-reqs must be installed separately, outside 83 | # of the requirements.txt files 84 | deps = pip_check_reqs 85 | -r{toxinidir}/requirements.txt 86 | commands= 87 | pip-extra-reqs -d --ignore-file=refstack/tests/* refstack 88 | pip-missing-reqs -d --ignore-file=refstack/tests/* refstack 89 | 90 | [testenv:debug] 91 | commands = oslo_debug_helper -t refstack/tests/unit {posargs} 92 | 93 | [testenv:debug35] 94 | basepython = python3.5 95 | commands = oslo_debug_helper -t refstack/tests/unit {posargs} 96 | 97 | [testenv:bindep] 98 | basepython = python3 99 | # Do not install any requirements. We want this to be fast and work even if 100 | # system dependencies are missing, since it's used to tell you what system 101 | # dependencies are missing! This also means that bindep must be installed 102 | # separately, outside of the requirements files, and develop mode disabled 103 | # explicitly to avoid unnecessarily installing the checked-out repo too (this 104 | # further relies on "tox.skipsdist = True" above). 105 | deps = bindep 106 | commands = bindep test 107 | usedevelop = False 108 | --------------------------------------------------------------------------------