commit 957f89f3665b5b832bdeee37828dd53a4c722b58 Author: Douglas Mendizábal Date: Tue Sep 24 07:38:01 2019 -0500 Initial UI-Cookiecutter Commit. diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..9fb8f2c --- /dev/null +++ b/.eslintrc @@ -0,0 +1,59 @@ +# Set up globals +globals: + angular: false + +extends: openstack + +# Most environment options are not explicitly enabled or disabled, only +# included here for completeness' sake. They are commented out, because the +# global updates.py script would otherwise override them during a global +# requirements synchronization. +# +# Individual projects should choose which platforms they deploy to. + +env: + # browser global variables. + browser: true + + # Adds all of the Jasmine testing global variables for version 1.3 and 2.0. + jasmine: true + +# Enable eslint-plugin-angular +plugins: + - angular + +# Below we adjust rules specific to horizon's usage of openstack's linting +# rules, and its own plugin inclusions. +rules: + ############################################################################# + # Disabled Rules from eslint-config-openstack + ############################################################################# + valid-jsdoc: [1, { + requireParamDescription: false + }] + brace-style: 1 + block-scoped-var: 1 + callback-return: 1 + consistent-return: 1 + guard-for-in: 1 + no-extra-parens: 1 + no-new: 1 + no-redeclare: 1 + no-undefined: 1 + no-unneeded-ternary: 1 + no-use-before-define: 1 + quote-props: 0 + semi-spacing: 1 + space-in-parens: 1 + + ############################################################################# + # Angular Plugin Customization + ############################################################################# + + angular/controller-as-vm: + - 1 + - "ctrl" + + # Remove after migrating to angular 1.4 or later. + angular/no-cookiestore: + - 1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6dccfad --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# Add patterns in here to exclude files created by tools integrated with this +# repository, such as test frameworks from the project's recommended workflow, +# rendered documentation and package builds. +# +# Don't add patterns to exclude files created by preferred personal tools +# (editors, IDEs, your operating system itself even). These should instead be +# maintained outside the repository, for example in a ~/.gitignore file added +# with: +# +# git config --global core.excludesfile '~/.gitignore' + +AUTHORS +ChangeLog +build +cover +doc/source/contributor/api +node_modules +npm-debug.log +releasenotes/build +barbican_ui/test/.secret_key_store +.coverage* +.jshintrc +.settings +.tox +.venv +*.egg* +*.lock +*.mo +*.py[cod] +*nose_results.html diff --git a/.gitreview b/.gitreview new file mode 100644 index 0000000..41e135b --- /dev/null +++ b/.gitreview @@ -0,0 +1,4 @@ +[gerrit] +host=review.opendev.org +port=29418 +project=openstack/barbican-ui.git diff --git a/.zuul.yaml b/.zuul.yaml new file mode 100644 index 0000000..efe6da3 --- /dev/null +++ b/.zuul.yaml @@ -0,0 +1,11 @@ +- project: + templates: + - horizon-nodejs10-jobs + check: + jobs: + - openstack-tox-py36: + voting: false + - horizon-openstack-tox-python3-django111 + gate: + jobs: + - horizon-openstack-tox-python3-django111 diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 0000000..a6a2271 --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,14 @@ +If you would like to contribute to the development of OpenStack, you must +follow the steps in this page: +https://docs.openstack.org/infra/manual/developers.html + +If you already have a good understanding of how the system works and your +OpenStack accounts are set up, you can skip to the development workflow +section of this documentation to learn how changes to OpenStack should be +submitted for review via the Gerrit tool: +https://docs.openstack.org/infra/manual/developers.html#development-workflow + +Pull requests submitted through GitHub will be ignored. + +Bugs should be filed on Launchpad, not GitHub: +https://bugs.launchpad.net/barbican-ui diff --git a/HACKING.rst b/HACKING.rst new file mode 100644 index 0000000..0dd4a05 --- /dev/null +++ b/HACKING.rst @@ -0,0 +1,4 @@ +barbican-ui Style Commandments +=============================================== + +Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..68c771a --- /dev/null +++ b/LICENSE @@ -0,0 +1,176 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..e925ad4 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,9 @@ +include AUTHORS +include ChangeLog +exclude .gitignore +exclude .gitreview +include setup.py + +recursive-include barbican_ui *.js *.html *.scss + +global-exclude *.pyc diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..fcad69f --- /dev/null +++ b/README.rst @@ -0,0 +1,64 @@ +=============================== +Barbican UI +=============================== + +Barbican User Interface + +* Free software: Apache license +* Source: http://opendev.org/openstack/barbican-ui +* Bugs: http://bugs.launchpad.net/barbican-ui + +Features +-------- + +* TODO + +Enabling in DevStack +-------------------- + +Add this repo as an external repository into your ``local.conf`` file:: + + [[local|localrc]] + enable_plugin barbican_ui https://github.com/openstack/barbican-ui + +Manual Installation +------------------- + +Begin by cloning the Horizon and Barbican UI repositories:: + + git clone https://github.com/openstack/horizon + git clone https://github.com/openstack/barbican-ui + +Create a virtual environment and install Horizon dependencies:: + + cd horizon + python tools/install_venv.py + +Set up your ``local_settings.py`` file:: + + cp openstack_dashboard/local/local_settings.py.example openstack_dashboard/local/local_settings.py + +Open up the copied ``local_settings.py`` file in your preferred text +editor. You will want to customize several settings: + +- ``OPENSTACK_HOST`` should be configured with the hostname of your + OpenStack server. Verify that the ``OPENSTACK_KEYSTONE_URL`` and + ``OPENSTACK_KEYSTONE_DEFAULT_ROLE`` settings are correct for your + environment. (They should be correct unless you modified your + OpenStack server to change them.) + +Install Barbican UI with all dependencies in your virtual environment:: + + tools/with_venv.sh pip install -e ../barbican-ui/ + +And enable it in Horizon:: + + ln -s ../barbican-ui/barbican_ui/enabled/_90_project_barbican_panelgroup.py openstack_dashboard/local/enabled + ln -s ../barbican-ui/barbican_ui/enabled/_91_project_barbican_secrets_panel.py openstack_dashboard/local/enabled + +To run horizon with the newly enabled Barbican UI plugin run:: + + ./run_tests.sh --runserver 0.0.0.0:8080 + +to have the application start on port 8080 and the horizon dashboard will be +available in your browser at http://localhost:8080/ diff --git a/babel-django.cfg b/babel-django.cfg new file mode 100644 index 0000000..e78d6c0 --- /dev/null +++ b/babel-django.cfg @@ -0,0 +1,5 @@ +[extractors] +django = django_babel.extract:extract_django + +[python: **.py] +[django: **/templates/**.html] diff --git a/babel-djangojs.cfg b/babel-djangojs.cfg new file mode 100644 index 0000000..a8273b6 --- /dev/null +++ b/babel-djangojs.cfg @@ -0,0 +1,14 @@ +[extractors] +# We use a custom extractor to find translatable strings in AngularJS +# templates. The extractor is included in horizon.utils for now. +# See http://babel.pocoo.org/docs/messages/#referencing-extraction-methods for +# details on how this works. +angular = horizon.utils.babel_extract_angular:extract_angular + +[javascript: **.js] + +# We need to look into all static folders for HTML files. +# The **/static ensures that we also search within +# /openstack_dashboard/dashboards/XYZ/static which will ensure +# that plugins are also translated. +[angular: **/static/**.html] diff --git a/barbican_ui/__init__.py b/barbican_ui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/barbican_ui/api/__init__.py b/barbican_ui/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/barbican_ui/api/client.py b/barbican_ui/api/client.py new file mode 100644 index 0000000..26149d0 --- /dev/null +++ b/barbican_ui/api/client.py @@ -0,0 +1,138 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +from __future__ import absolute_import + +import logging + +# enable following after client product implemented. +# from barbicanclient.v1 import client as barbican_client + +from horizon import exceptions +from horizon.utils.memoized import memoized +from openstack_dashboard.api import base + +# for stab, should remove when use CLI API +import copy +from datetime import datetime +import uuid + + +LOG = logging.getLogger(__name__) + +ATTRIBUTES = ['name', 'description', 'enabled', 'size', 'temperature', + 'base', 'flavor', 'topping'] + +STUB_DATA = {} + + +# for stab, should be removed when use CLI API +class StubResponse(object): + + def __init__(self, info): + self._info = info + + def __repr__(self): + reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_') + info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys) + return "<%s %s>" % (self.__class__.__name__, info) + + def to_dict(self): + return copy.deepcopy(self._info) + + +@memoized +def apiclient(request): + api_url = "" + c = None + + try: + api_url = base.url_for(request, 'secret') + except exceptions.ServiceCatalogException: + LOG.debug('No Secret Management service is configured.') + return None + + LOG.debug('barbicanclient connection created using the token "%s" and url' + '"%s"' % (request.user.token.id, api_url)) + # enable following after client product implemented. + # c = barbican_client.Client( + # username=request.user.username, + # project_id=request.user.tenant_id, + # input_auth_token=request.user.token.id, + # api_url=api_url) + return c + + +def secret_create(request, **kwargs): + args = {} + for (key, value) in kwargs.items(): + if key in ATTRIBUTES: + args[str(key)] = value + else: + raise exceptions.BadRequest( + "Key must be in %s" % ",".join(ATTRIBUTES)) + # created = apiclient(request).secrets.create(**args) + + # create dummy response + args["uuid"] = uuid.uuid1().hex + args["created_at"] = datetime.now().isoformat() + created = StubResponse(args) + for k in args: + setattr(created, k, args[k]) + STUB_DATA[created.uuid] = created + + return created + + +def secret_update(request, id, **kwargs): + args = {} + for (key, value) in kwargs.items(): + if key in ATTRIBUTES: + args[str(key)] = value + else: + raise exceptions.BadRequest( + "Key must be in %s" % ",".join(ATTRIBUTES)) + # updated = apiclient(request).secret.update(id, **args) + + # update dummy response + args["uuid"] = id + args["updated_at"] = datetime.now().isoformat() + updated = StubResponse(args) + for k in args: + setattr(updated, k, args[k]) + STUB_DATA[updated.uuid] = updated + + return updated + + +def secret_delete(request, id): + # deleted = apiclient(request).secrets.delete(id) + deleted = STUB_DATA.pop(id) + + return deleted + + +def secret_list( + request, limit=None, marker=None, sort_key=None, + sort_dir=None, detail=True): + + # list = apiclient(request).Secrets.list(limit, marker, sort_key, + # sort_dir, detail) + list = [STUB_DATA[data] for data in STUB_DATA] + return list + + +def secret_show(request, id): + # show = apiclient(request).secrets.get(id) + show = STUB_DATA.get(id) + return show diff --git a/barbican_ui/api/rest_api.py b/barbican_ui/api/rest_api.py new file mode 100644 index 0000000..8a404f1 --- /dev/null +++ b/barbican_ui/api/rest_api.py @@ -0,0 +1,86 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from django.views import generic + +from barbican_ui.api import client + +from openstack_dashboard.api.rest import urls +from openstack_dashboard.api.rest import utils as rest_utils + + +def change_to_id(obj): + """Change key named 'uuid' to 'id' + + API returns objects with a field called 'uuid' many of Horizons + directives however expect objects to have a field called 'id'. + """ + obj['id'] = obj.pop('uuid') + return obj + + +@urls.register +class Secret(generic.View): + """API for retrieving a single Secret""" + url_regex = r'barbican/secrets/(?P[^/]+)$' + + @rest_utils.ajax() + def get(self, request, id): + """Get a specific secret""" + return change_to_id(client.secret_show(request, id).to_dict()) + + @rest_utils.ajax(data_required=True) + def post(self, request, id): + """Update a Secret. + + Returns the updated Secret object on success. + """ + secret = client.secret_update(request, id, **request.DATA) + return rest_utils.CreatedResponse( + '/api/barbican/secret/%s' % secret.uuid, + secret.to_dict()) + + +@urls.register +class Secrets(generic.View): + """API for Secrets""" + url_regex = r'barbican/secrets/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of the Secrets for a project. + + The returned result is an object with property 'items' and each + item under this is a Secret. + """ + result = client.secret_list(request) + return {'items': [change_to_id(n.to_dict()) for n in result]} + + @rest_utils.ajax(data_required=True) + def delete(self, request): + """Delete one or more Secrets by id. + + Returns HTTP 204 (no content) on successful deletion. + """ + for id in request.DATA: + client.secret_delete(request, id) + + @rest_utils.ajax(data_required=True) + def put(self, request): + """Create a new Secret. + + Returns the new Secret object on success. + """ + secret = client.secret_create(request, **request.DATA) + return rest_utils.CreatedResponse( + '/api/barbican/secret/%s' % secret.uuid, + secret.to_dict()) diff --git a/barbican_ui/content/__init__.py b/barbican_ui/content/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/barbican_ui/content/secrets/__init__.py b/barbican_ui/content/secrets/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/barbican_ui/content/secrets/panel.py b/barbican_ui/content/secrets/panel.py new file mode 100644 index 0000000..2eb35d8 --- /dev/null +++ b/barbican_ui/content/secrets/panel.py @@ -0,0 +1,23 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from django.utils.translation import ugettext_lazy as _ +import horizon + +# This panel will be loaded from horizon, because specified in enabled file. +# To register REST api, import below here. +from barbican_ui.api import rest_api # noqa: F401 + + +class Secrets(horizon.Panel): + name = _("Secrets") + slug = "secrets" diff --git a/barbican_ui/content/secrets/urls.py b/barbican_ui/content/secrets/urls.py new file mode 100644 index 0000000..51c4607 --- /dev/null +++ b/barbican_ui/content/secrets/urls.py @@ -0,0 +1,20 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from django.conf.urls import url +from django.utils.translation import ugettext_lazy as _ +from horizon.browsers import views + +title = _("Secrets") +urlpatterns = [ + url('', views.AngularIndexView.as_view(title=title), name='index'), +] diff --git a/barbican_ui/enabled/_90_barbican_barbican_panelgroup.py b/barbican_ui/enabled/_90_barbican_barbican_panelgroup.py new file mode 100644 index 0000000..2e0710a --- /dev/null +++ b/barbican_ui/enabled/_90_barbican_barbican_panelgroup.py @@ -0,0 +1,36 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from django.utils.translation import ugettext_lazy as _ + +# The slug of the panel group to be added to HORIZON_CONFIG. Required. +PANEL_GROUP = 'barbican' +# The display name of the PANEL_GROUP. Required. +PANEL_GROUP_NAME = _('Barbican') +# The slug of the dashboard the PANEL_GROUP associated with. Required. +PANEL_GROUP_DASHBOARD = 'barbican' + +ADD_INSTALLED_APPS = ['barbican_ui'] + +ADD_ANGULAR_MODULES = [ + 'horizon.dashboard.barbican' +] + +ADD_JS_FILES = [ + 'horizon/lib/angular/angular-route.js' +] + +ADD_SCSS_FILES = [ + 'dashboard/barbican/barbican.scss' +] + +AUTO_DISCOVER_STATIC_FILES = True diff --git a/barbican_ui/enabled/_91_barbican_barbican_secrets_panel.py b/barbican_ui/enabled/_91_barbican_barbican_secrets_panel.py new file mode 100644 index 0000000..75ef15e --- /dev/null +++ b/barbican_ui/enabled/_91_barbican_barbican_secrets_panel.py @@ -0,0 +1,21 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# The slug of the panel to be added to HORIZON_CONFIG. Required. +PANEL = 'secrets' +# The slug of the panel group the PANEL is associated with. +PANEL_GROUP = 'barbican' +# The slug of the dashboard the PANEL associated with. Required. +PANEL_DASHBOARD = 'barbican' + +# Python panel class of the PANEL to be added. +ADD_PANEL = 'barbican_ui.content.secrets.panel.Secrets' diff --git a/barbican_ui/enabled/__init__.py b/barbican_ui/enabled/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/barbican_ui/karma.conf.js b/barbican_ui/karma.conf.js new file mode 100644 index 0000000..6e713d7 --- /dev/null +++ b/barbican_ui/karma.conf.js @@ -0,0 +1,157 @@ +/* + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var child_process = require("child_process"); + +module.exports = function (config) { + + var pythonVersion = "python3."; + var stdout = child_process.execFileSync("python3", ["--version"]); + pythonVersion += stdout.toString().split(".")[1]; + var toxPath = '../.tox/karma/lib/' + pythonVersion + '/site-packages/'; + console.log("Karma will check on directory: ", toxPath); + + process.env.PHANTOMJS_BIN = 'node_modules/phantomjs-prebuilt/bin/phantomjs'; + + config.set({ + preprocessors: { + // Used to collect templates for preprocessing. + // NOTE: the templates must also be listed in the files section below. + './static/**/*.html': ['ng-html2js'], + // Used to indicate files requiring coverage reports. + './static/**/!(*.spec).js': ['coverage'], + }, + + // Sets up module to process templates. + ngHtml2JsPreprocessor: { + prependPrefix: '/', + moduleName: 'templates' + }, + + basePath: './', + + // Contains both source and test files. + files: [ + /* + * shim, partly stolen from /i18n/js/horizon/ + * Contains expected items not provided elsewhere (dynamically by + * Django or via jasmine template. + */ + '../test-shim.js', + + // from jasmine.html + toxPath + 'xstatic/pkg/jquery/data/jquery.js', + toxPath + 'xstatic/pkg/angular/data/angular.js', + toxPath + 'xstatic/pkg/angular/data/angular-route.js', + toxPath + 'xstatic/pkg/angular/data/angular-mocks.js', + toxPath + 'xstatic/pkg/angular/data/angular-cookies.js', + toxPath + 'xstatic/pkg/angular_bootstrap/data/angular-bootstrap.js', + toxPath + 'xstatic/pkg/angular_gettext/data/angular-gettext.js', + toxPath + 'xstatic/pkg/angular/data/angular-sanitize.js', + toxPath + 'xstatic/pkg/d3/data/d3.js', + toxPath + 'xstatic/pkg/rickshaw/data/rickshaw.js', + toxPath + 'xstatic/pkg/angular_smart_table/data/smart-table.js', + toxPath + 'xstatic/pkg/angular_lrdragndrop/data/lrdragndrop.js', + toxPath + 'xstatic/pkg/spin/data/spin.js', + toxPath + 'xstatic/pkg/spin/data/spin.jquery.js', + toxPath + 'xstatic/pkg/tv4/data/tv4.js', + toxPath + 'xstatic/pkg/objectpath/data/ObjectPath.js', + toxPath + 'xstatic/pkg/angular_schema_form/data/schema-form.js', + toxPath + 'xstatic/pkg/angular_fileupload/data/ng-file-upload.js', + + // TODO: These should be mocked. + toxPath + 'horizon/static/horizon/js/horizon.js', + + /** + * Include framework source code from horizon that we need. + * Otherwise, karma will not be able to find them when testing. + * These files should be mocked in the foreseeable future. + */ + toxPath + 'horizon/static/framework/**/*.module.js', + toxPath + 'horizon/static/framework/**/!(*.spec|*.mock).js', + toxPath + 'openstack_dashboard/static/**/*.module.js', + toxPath + 'openstack_dashboard/static/**/!(*.spec|*.mock).js', + toxPath + 'openstack_dashboard/dashboards/**/static/**/*.module.js', + toxPath + 'openstack_dashboard/dashboards/**/static/**/!(*.spec|*.mock).js', + + /** + * First, list all the files that defines application's angular modules. + * Those files have extension of `.module.js`. The order among them is + * not significant. + */ + './static/**/*.module.js', + + /** + * Followed by other JavaScript files that defines angular providers + * on the modules defined in files listed above. And they are not mock + * files or spec files defined below. The order among them is not + * significant. + */ + './static/**/!(*.spec|*.mock).js', + + /** + * Then, list files for mocks with `mock.js` extension. The order + * among them should not be significant. + */ + toxPath + 'openstack_dashboard/static/**/*.mock.js', + + /** + * Finally, list files for spec with `spec.js` extension. The order + * among them should not be significant. + */ + './static/**/*.spec.js', + + /** + * Angular external templates + */ + './static/**/*.html' + ], + + autoWatch: true, + + frameworks: ['jasmine'], + + browsers: ['PhantomJS'], + + browserNoActivityTimeout: 60000, + + reporters: ['progress', 'coverage', 'threshold'], + + plugins: [ + 'karma-phantomjs-launcher', + 'karma-jasmine', + 'karma-ng-html2js-preprocessor', + 'karma-coverage', + 'karma-threshold-reporter' + ], + + // Places coverage report in HTML format in the subdirectory below. + coverageReporter: { + type: 'html', + dir: '../cover/karma/' + }, + + // Coverage threshold values. + thresholdReporter: { + statements: 10, // target 100 + branches: 0, // target 100 + functions: 10, // target 100 + lines: 10 // target 100 + } + }); +}; diff --git a/barbican_ui/static/dashboard/barbican/barbican.module.js b/barbican_ui/static/dashboard/barbican/barbican.module.js new file mode 100644 index 0000000..a02361e --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/barbican.module.js @@ -0,0 +1,40 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name horizon.dashboard.barbican + * @description + * Dashboard module to host various barbican panels. + */ + // fixme: if ngRoute and $routeProvider are unnecessary, remove them + /* eslint-disable no-unused-vars */ + angular + .module('horizon.dashboard.barbican', [ + 'horizon.dashboard.barbican.secrets', + 'ngRoute' + ]) + .config(config); + + config.$inject = ['$provide', '$windowProvider', '$routeProvider']; + + function config($provide, $windowProvider, $routeProvider) { + var path = $windowProvider.$get().STATIC_URL + 'dashboard/barbican/'; + $provide.constant('horizon.dashboard.barbican.basePath', path); + } + /* eslint-disable no-unused-vars */ +})(); diff --git a/barbican_ui/static/dashboard/barbican/barbican.module.spec.js b/barbican_ui/static/dashboard/barbican/barbican.module.spec.js new file mode 100644 index 0000000..3430e3e --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/barbican.module.spec.js @@ -0,0 +1,23 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +(function() { + 'use strict'; + + describe('horizon.dashboard.barbican', function() { + it('should exist', function() { + expect(angular.module('horizon.dashboard.barbican')).toBeDefined(); + }); + }); + +})(); diff --git a/barbican_ui/static/dashboard/barbican/barbican.scss b/barbican_ui/static/dashboard/barbican/barbican.scss new file mode 100644 index 0000000..48598a2 --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/barbican.scss @@ -0,0 +1,8 @@ +@import "secrets/secrets"; + +.batch-action { + float: right; + action-list { + padding-left: 0.3em; + } +} diff --git a/barbican_ui/static/dashboard/barbican/barbican.service.js b/barbican_ui/static/dashboard/barbican/barbican.service.js new file mode 100644 index 0000000..b497c23 --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/barbican.service.js @@ -0,0 +1,80 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +(function () { + 'use strict'; + + angular + .module('horizon.app.core.openstack-service-api') + .factory('horizon.app.core.openstack-service-api.barbican', API); + + API.$inject = [ + 'horizon.framework.util.http.service', + 'horizon.framework.widgets.toast.service', + 'horizon.framework.util.i18n.gettext' + ]; + + function API(apiService, toastService, gettext) { + var service = { + getSecret: getSecret, + getSecrets: getSecrets, + createSecret: createSecret, + updateSecret: updateSecret, + deleteSecret: deleteSecret + }; + + return service; + + /////////////////////////////// + // Secrets + + function getSecret(id) { + return apiService.get('/api/barbican/secrets/' + id) + .error(function() { + var msg = gettext('Unable to retrieve the Secret with id: %(id)s.'); + toastService.add('error', interpolate(msg, {id: id}, true)); + }); + } + + function getSecrets() { + return apiService.get('/api/barbican/secrets/') + .error(function() { + toastService.add('error', gettext('Unable to retrieve the Secrets.')); + }); + } + + function createSecret(params) { + return apiService.put('/api/barbican/secrets/', params) + .error(function() { + var msg = gettext('Unable to create the Secret with name: %(name)s'); + toastService.add('error', interpolate(msg, { name: params.name }, true)); + }); + } + + function updateSecret(id, params) { + return apiService.post('/api/barbican/secrets/' + id, params) + .error(function() { + var msg = gettext('Unable to update the Secret with id: %(id)s'); + toastService.add('error', interpolate(msg, { id: params.id }, true)); + }); + } + + function deleteSecret(id, suppressError) { + var promise = apiService.delete('/api/barbican/secrets/', [id]); + return suppressError ? promise : promise.error(function() { + var msg = gettext('Unable to delete the Secret with id: %(id)s'); + toastService.add('error', interpolate(msg, { id: id }, true)); + }); + } + } +}()); diff --git a/barbican_ui/static/dashboard/barbican/secrets/actions/actions.module.js b/barbican_ui/static/dashboard/barbican/secrets/actions/actions.module.js new file mode 100644 index 0000000..e5e427f --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/secrets/actions/actions.module.js @@ -0,0 +1,87 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @ngname horizon.dashboard.barbican.secrets.actions + * + * @description + * Provides all of the actions for Secrets. + */ + angular + .module('horizon.dashboard.barbican.secrets.actions', [ + 'horizon.framework', + 'horizon.dashboard.barbican' + ]) + .run(registerSecretActions); + + registerSecretActions.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.dashboard.barbican.secrets.create.service', + 'horizon.dashboard.barbican.secrets.update.service', + 'horizon.dashboard.barbican.secrets.delete.service', + 'horizon.dashboard.barbican.secrets.resourceType' + ]; + + function registerSecretActions ( + registry, + gettext, + createSecretService, + updateSecretService, + deleteSecretService, + resourceType + ) { + var secretsResourceType = registry.getResourceType(resourceType); + secretsResourceType.globalActions + .append({ + id: 'createSecretAction', + service: createSecretService, + template: { + type: 'create', + text: gettext('Create Secret') + } + }); + + secretsResourceType.batchActions + .append({ + id: 'batchDeleteSecretAction', + service: deleteSecretService, + template: { + type: 'delete-selected', + text: gettext('Delete Secrets') + } + }); + + secretsResourceType.itemActions + .append({ + id: 'updateSecretAction', + service: updateSecretService, + template: { + text: gettext('Update Secret') + } + }) + .append({ + id: 'deleteSecretAction', + service: deleteSecretService, + template: { + type: 'delete', + text: gettext('Delete Secret') + } + }); + } +})(); diff --git a/barbican_ui/static/dashboard/barbican/secrets/actions/create.service.js b/barbican_ui/static/dashboard/barbican/secrets/actions/create.service.js new file mode 100644 index 0000000..fd6aeaf --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/secrets/actions/create.service.js @@ -0,0 +1,103 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name horizon.dashboard.barbican.secrets.create.service + * @description Service for the secret create modal + */ + angular + .module('horizon.dashboard.barbican.secrets') + .factory('horizon.dashboard.barbican.secrets.create.service', createService); + + createService.$inject = [ + '$location', + 'horizon.app.core.openstack-service-api.barbican', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.actions.action-result.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.util.q.extensions', + 'horizon.framework.widgets.toast.service', + 'horizon.dashboard.barbican.secrets.events', + 'horizon.dashboard.barbican.secrets.model', + 'horizon.dashboard.barbican.secrets.resourceType', + 'horizon.dashboard.barbican.secrets.workflow' + ]; + + function createService( + $location, api, policy, actionResult, gettext, $qExtensions, + toast, events, model, resourceType, workflow + ) { + + var message = { + success: gettext('Secret %s was successfully created.') + }; + + var service = { + initAction: initAction, + perform: perform, + allowed: allowed + }; + + return service; + + ////////////// + + // fixme: include this function in your service + // if you plan to emit events to the parent controller, + // otherwise remove it + function initAction() { + } + + // fixme: if newScope is unnecessary, remove it + /* eslint-disable no-unused-vars */ + function perform(selected, newScope) { + // modal title, buttons + var title, submitText, submitIcon; + title = gettext("Create Secret"); + submitText = gettext("Create"); + submitIcon = "fa fa-check"; + model.init(); + + var result = workflow.init(title, submitText, submitIcon, model.spec); + return result.then(submit); + } + + function allowed() { + return $qExtensions.booleanAsPromise(true); + // fixme: if you need to set policy, change as follow + //return policy.ifAllowed({ rules: [['secret', 'create_secret']] }); + } + + function submit() { + model.cleanProperties(); + return api.createSecret(model.spec).then(success); + } + + function success(response) { + response.data.id = response.data.uuid; + toast.add('success', interpolate(message.success, [response.data.id])); + var result = actionResult.getActionResult() + .created(resourceType, response.data.id); + if (result.result.failed.length === 0 && result.result.created.length > 0) { + $location.path('/barbican/secrets'); + } else { + return result.result; + } + } + } +})(); diff --git a/barbican_ui/static/dashboard/barbican/secrets/actions/delete.service.js b/barbican_ui/static/dashboard/barbican/secrets/actions/delete.service.js new file mode 100644 index 0000000..8b778fd --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/secrets/actions/delete.service.js @@ -0,0 +1,159 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use self file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +(function() { + 'use strict'; + + /** + * @ngDoc factory + * @name horizon.dashboard.barbican.secrets.delete.service + * @Description + * Brings up the delete secrets confirmation modal dialog. + * On submit, delete selected resources. + * On cancel, do nothing. + */ + angular + .module('horizon.dashboard.barbican.secrets') + .factory('horizon.dashboard.barbican.secrets.delete.service', deleteService); + + deleteService.$inject = [ + '$location', + '$q', + '$rootScope', + 'horizon.app.core.openstack-service-api.barbican', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.actions.action-result.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.util.q.extensions', + 'horizon.framework.widgets.modal.deleteModalService', + 'horizon.framework.widgets.table.events', + 'horizon.framework.widgets.toast.service', + 'horizon.dashboard.barbican.secrets.resourceType', + 'horizon.dashboard.barbican.secrets.events' + ]; + + function deleteService( + $location, $q, $rootScope, api, policy, actionResult, gettext, $qExtensions, + deleteModal, tableEvents, toast, resourceType, events + ) { + var scope; + var context = { + labels: null, + deleteEntity: deleteEntity, + successEvent: events.DELETE_SUCCESS + }; + var service = { + initAction: initAction, + allowed: allowed, + perform: perform + }; + var notAllowedMessage = + gettext("You are not allowed to delete secrets: %s"); + + return service; + + ////////////// + + // fixme: include this function in your service + // if you plan to emit events to the parent controller, + // otherwise remove it + function initAction() { + } + + function allowed() { + return $qExtensions.booleanAsPromise(true); + // fixme: if you need to set policy, change as follow + //return policy.ifAllowed({ rules: [['secret', 'delete_secret']] }); + } + + // delete selected resource objects + function perform(selected, newScope) { + scope = newScope; + selected = angular.isArray(selected) ? selected : [selected]; + context.labels = labelize(selected.length); + return $qExtensions.allSettled(selected.map(checkPermission)).then(afterCheck); + } + + function labelize(count) { + return { + title: ngettext('Confirm Delete Secret', + 'Confirm Delete Secrets', count), + /* eslint-disable max-len */ + message: ngettext('You have selected "%s". Please confirm your selection. Deleted secret is not recoverable.', + 'You have selected "%s". Please confirm your selection. Deleted secrets are not recoverable.', count), + /* eslint-enable max-len */ + submit: ngettext('Delete Secret', + 'Delete Secrets', count), + success: ngettext('Deleted Secret: %s.', + 'Deleted Secrets: %s.', count), + error: ngettext('Unable to delete Secret: %s.', + 'Unable to delete Secrets: %s.', count) + }; + } + + // for batch delete + function checkPermission(selected) { + return {promise: allowed(selected), context: selected}; + } + + // for batch delete + function afterCheck(result) { + var outcome = $q.reject(); // Reject the promise by default + if (result.fail.length > 0) { + toast.add('error', getMessage(notAllowedMessage, result.fail)); + outcome = $q.reject(result.fail); + } + if (result.pass.length > 0) { + outcome = deleteModal.open(scope, result.pass.map(getEntity), context).then(createResult); + } + return outcome; + } + + function createResult(deleteModalResult) { + // To make the result of this action generically useful, reformat the return + // from the deleteModal into a standard form + var result = actionResult.getActionResult(); + deleteModalResult.pass.forEach(function markDeleted(item) { + result.deleted(resourceType, getEntity(item).id); + }); + deleteModalResult.fail.forEach(function markFailed(item) { + result.failed(resourceType, getEntity(item).id); + }); + if (result.result.failed.length === 0 && result.result.deleted.length > 0) { + $location.path('/barbican/secrets'); + } else { + $rootScope.$broadcast(tableEvents.CLEAR_SELECTIONS); + return result.result; + } + } + + function getMessage(message, entities) { + return interpolate(message, [entities.map(getName).join(", ")]); + } + + function getName(result) { + return getEntity(result).name; + } + + // for batch delete + function getEntity(result) { + return result.context; + } + + // call delete REST API + function deleteEntity(id) { + return api.deleteSecret(id, true); + } + } +})(); diff --git a/barbican_ui/static/dashboard/barbican/secrets/actions/update.service.js b/barbican_ui/static/dashboard/barbican/secrets/actions/update.service.js new file mode 100644 index 0000000..c637bdc --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/secrets/actions/update.service.js @@ -0,0 +1,122 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name horizon.dashboard.barbican.secrets.update.service + * @description Service for the secret update modal + */ + angular + .module('horizon.dashboard.barbican.secrets') + .factory('horizon.dashboard.barbican.secrets.update.service', updateService); + + updateService.$inject = [ + '$location', + 'horizon.app.core.openstack-service-api.barbican', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.actions.action-result.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.util.q.extensions', + 'horizon.framework.widgets.toast.service', + 'horizon.dashboard.barbican.secrets.events', + 'horizon.dashboard.barbican.secrets.model', + 'horizon.dashboard.barbican.secrets.resourceType', + 'horizon.dashboard.barbican.secrets.workflow' + ]; + + function updateService( + $location, api, policy, actionResult, gettext, $qExtensions, + toast, events, model, resourceType, workflow + ) { + + var message = { + success: gettext('Secret %s was successfully updated.') + }; + + var service = { + initAction: initAction, + perform: perform, + allowed: allowed + }; + + var id; + + return service; + + ////////////// + + // fixme: include this function in your service + // if you plan to emit events to the parent controller, + // otherwise remove it + function initAction() { + } + + // fixme: if newScope is unnecessary, remove it + /* eslint-disable no-unused-vars */ + function perform(selected, newScope) { + // modal title, buttons + var title, submitText, submitIcon; + title = gettext("Update Secret"); + submitText = gettext("Update"); + submitIcon = "fa fa-check"; + model.init(); + + // load current data + id = selected.id; + var deferred = api.getSecret(id); + deferred.then(onLoad); + + function onLoad(response) { + model.spec.id = response.data.id; + model.spec.name = response.data.name; + model.spec.description = response.data.description; + model.spec.enabled = response.data.enabled; + model.spec.size = response.data.size; + model.spec.temperature = response.data.temperature; + model.spec.base = response.data.base; + model.spec.flavor = response.data.flavor; + model.spec.topping = response.data.topping; + } + + var result = workflow.init(title, submitText, submitIcon, model.spec); + return result.then(submit); + } + + function allowed() { + return $qExtensions.booleanAsPromise(true); + // fixme: if you need to set policy, change as follow + //return policy.ifAllowed({ rules: [['secret', 'update_secret']] }); + } + + function submit() { + model.cleanProperties(); + return api.updateSecret(id, model.spec).then(success); + } + + function success(response) { + response.data.id = response.data.uuid; + toast.add('success', interpolate(message.success, [response.data.id])); + var result = actionResult.getActionResult() + .updated(resourceType, response.data.id); + if (result.result.failed.length === 0 && result.result.updated.length > 0) { + $location.path('/barbican/secrets'); + } else { + return result.result; + } + } + } +})(); diff --git a/barbican_ui/static/dashboard/barbican/secrets/details/details.module.js b/barbican_ui/static/dashboard/barbican/secrets/details/details.module.js new file mode 100644 index 0000000..302b1f5 --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/secrets/details/details.module.js @@ -0,0 +1,57 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @ngname horizon.dashboard.barbican.secrets.details + * + * @description + * Provides details features for Secret. + */ + angular + .module('horizon.dashboard.barbican.secrets.details', [ + 'horizon.app.core', + 'horizon.framework.conf' + ]) + .run(registerDetails); + + registerDetails.$inject = [ + 'horizon.app.core.openstack-service-api.barbican', + 'horizon.dashboard.barbican.secrets.basePath', + 'horizon.dashboard.barbican.secrets.resourceType', + 'horizon.framework.conf.resource-type-registry.service' + ]; + + function registerDetails( + api, + basePath, + resourceType, + registry + ) { + registry.getResourceType(resourceType) + .setLoadFunction(loadFunction) + .detailsViews.append({ + id: 'secretDetailsOverview', + name: gettext('Overview'), + template: basePath + 'details/overview.html' + }); + + function loadFunction(identifier) { + return api.getSecret(identifier); + } + } +})(); diff --git a/barbican_ui/static/dashboard/barbican/secrets/details/drawer.html b/barbican_ui/static/dashboard/barbican/secrets/details/drawer.html new file mode 100644 index 0000000..5e2de19 --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/secrets/details/drawer.html @@ -0,0 +1,6 @@ + + diff --git a/barbican_ui/static/dashboard/barbican/secrets/details/overview.controller.js b/barbican_ui/static/dashboard/barbican/secrets/details/overview.controller.js new file mode 100644 index 0000000..f8684ff --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/secrets/details/overview.controller.js @@ -0,0 +1,37 @@ +/* + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +(function() { + "use strict"; + + angular + .module('horizon.dashboard.barbican.secrets') + .controller('horizon.dashboard.barbican.secrets.OverviewController', controller); + + controller.$inject = [ + '$scope' + ]; + + function controller( + $scope + ) { + var ctrl = this; + ctrl.secret = {}; + + $scope.context.loadPromise.then(onGetSecret); + + function onGetSecret(secret) { + ctrl.secret = secret.data; + } + } +})(); diff --git a/barbican_ui/static/dashboard/barbican/secrets/details/overview.html b/barbican_ui/static/dashboard/barbican/secrets/details/overview.html new file mode 100644 index 0000000..f971362 --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/secrets/details/overview.html @@ -0,0 +1,16 @@ +
+
+
+

Secret

+
+ + +
+
+
diff --git a/barbican_ui/static/dashboard/barbican/secrets/panel.html b/barbican_ui/static/dashboard/barbican/secrets/panel.html new file mode 100644 index 0000000..c4866c4 --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/secrets/panel.html @@ -0,0 +1,6 @@ + + + + + diff --git a/barbican_ui/static/dashboard/barbican/secrets/secrets.module.js b/barbican_ui/static/dashboard/barbican/secrets/secrets.module.js new file mode 100644 index 0000000..b0efc9e --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/secrets/secrets.module.js @@ -0,0 +1,173 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name horizon.dashboard.barbican.secrets + * @ngModule + * @description + * Provides all the services and widgets require to display the Secret + * panel + */ + angular + .module('horizon.dashboard.barbican.secrets', [ + 'ngRoute', + 'horizon.dashboard.barbican.secrets.actions', + 'horizon.dashboard.barbican.secrets.details' + ]) + .constant('horizon.dashboard.barbican.secrets.events', events()) + .constant('horizon.dashboard.barbican.secrets.resourceType', 'OS::Barbican::Secret') + .run(run) + .config(config); + + /** + * @ngdoc constant + * @name horizon.dashboard.barbican.secrets.events + * @description A list of events used by Secret + * @returns {Object} events + */ + function events() { + return { + CREATE_SUCCESS: 'horizon.dashboard.barbican.secrets.CREATE_SUCCESS', + DELETE_SUCCESS: 'horizon.dashboard.barbican.secrets.DELETE_SUCCESS' + }; + } + + run.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.dashboard.barbican.secrets.service', + 'horizon.dashboard.barbican.secrets.basePath', + 'horizon.dashboard.barbican.secrets.resourceType' + ]; + + function run(registry, service, basePath, resourceType) { + registry.getResourceType(resourceType) + .setNames(gettext('Secret'), gettext('Secrets')) + // for detail summary view on table row + .setSummaryTemplateUrl(basePath + 'details/drawer.html') + // set default url for index view. this will be used for reproducing + // sidebar and breadcrumb when refreshing or accessing directly + // details view. + .setDefaultIndexUrl('/barbican/secrets/') + // specify items for table row items, summary view and details view + .setProperties(properties()) + // get items for table + .setListFunction(service.getPromise) + // specify table columns + .tableColumns + .append({ + id: 'name', + priority: 1, + sortDefault: true, + filters: ['noName'], + urlFunction: service.urlFunction + }) + .append({ + id: 'size', + priority: 1, + filters: ['noValue'] + }) + .append({ + id: 'temperature', + priority: 1, + filters: ['noValue'] + }) + .append({ + id: 'base', + priority: 1, + filters: ['noValue'] + }) + .append({ + id: 'flavor', + priority: 1, + filters: ['noValue'] + }) + .append({ + id: 'topping', + priority: 2, + filters: ['noValue'] + }) + .append({ + id: 'created_at', + priority: 2 + }) + .append({ + id: 'updated_at', + priority: 2 + }); + // for magic-search + registry.getResourceType(resourceType).filterFacets + .append({ + 'label': gettext('Name'), + 'name': 'name', + 'singleton': true + }) + .append({ + 'label': gettext('Base'), + 'name': 'base', + 'singleton': true + }) + .append({ + 'label': gettext('Flavor'), + 'name': 'flavor', + 'singleton': true + }) + .append({ + 'label': gettext('ID'), + 'name': 'id', + 'singleton': true + }); + } + + function properties() { + return { + id: { label: gettext('ID'), filters: ['noValue'] }, + name: { label: gettext('Name'), filters: ['noName'] }, + description: { label: gettext('Description'), filters: ['noValue'] }, + enabled: { label: gettext('Enabled'), filters: ['yesno'] }, + size: { label: gettext('Size'), filters: ['noValue'] }, + temperature: { label: gettext('Temperature'), filters: ['noValue'] }, + base: { label: gettext('Base'), filters: ['noValue'] }, + flavor: { label: gettext('Flavor'), filters: ['noValue'] }, + topping: { label: gettext('Topping'), filters: ['noValue'] }, + created_at: { label: gettext('Created'), filters: ['simpleDate', 'noValue'] }, + updated_at: { label: gettext('Updated'), filters: ['simpleDate', 'noValue'] } + }; + } + + config.$inject = [ + '$provide', + '$windowProvider', + '$routeProvider' + ]; + + /** + * @name config + * @param {Object} $provide + * @param {Object} $windowProvider + * @param {Object} $routeProvider + * @description Routes used by this module. + * @returns {undefined} Returns nothing + */ + function config($provide, $windowProvider, $routeProvider) { + var path = $windowProvider.$get().STATIC_URL + 'dashboard/barbican/secrets/'; + $provide.constant('horizon.dashboard.barbican.secrets.basePath', path); + $routeProvider.when('/barbican/secrets', { + templateUrl: path + 'panel.html' + }); + } +})(); diff --git a/barbican_ui/static/dashboard/barbican/secrets/secrets.module.spec.js b/barbican_ui/static/dashboard/barbican/secrets/secrets.module.spec.js new file mode 100644 index 0000000..674668f --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/secrets/secrets.module.spec.js @@ -0,0 +1,23 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +(function() { + 'use strict'; + + describe('horizon.dashboard.barbican.secrets', function() { + it('should exist', function() { + expect(angular.module('horizon.dashboard.barbican.secrets')).toBeDefined(); + }); + }); + +})(); diff --git a/barbican_ui/static/dashboard/barbican/secrets/secrets.scss b/barbican_ui/static/dashboard/barbican/secrets/secrets.scss new file mode 100644 index 0000000..e69de29 diff --git a/barbican_ui/static/dashboard/barbican/secrets/secrets.service.js b/barbican_ui/static/dashboard/barbican/secrets/secrets.service.js new file mode 100644 index 0000000..407d30a --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/secrets/secrets.service.js @@ -0,0 +1,63 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +(function() { + "use strict"; + + angular.module('horizon.dashboard.barbican.secrets') + .factory('horizon.dashboard.barbican.secrets.service', + service); + + service.$inject = [ + '$filter', + 'horizon.app.core.detailRoute', + 'horizon.app.core.openstack-service-api.barbican' + ]; + + /* + * @ngdoc factory + * @name horizon.dashboard.barbican.secrets.service + * + * @description + * This service provides functions that are used through the Secrets + * features. These are primarily used in the module registrations + * but do not need to be restricted to such use. Each exposed function + * is documented below. + */ + function service($filter, detailRoute, api) { + return { + getPromise: getPromise, + urlFunction: urlFunction + }; + + function getPromise(params) { + return api.getSecrets(params).then(modifyResponse); + } + + function modifyResponse(response) { + return {data: {items: response.data.items.map(modifyItem)}}; + + function modifyItem(item) { + var timestamp = item.updated_at ? item.updated_at : item.created_at; + item.trackBy = item.id.concat(timestamp); + return item; + } + } + + function urlFunction(item) { + return detailRoute + 'OS::Barbican::Secret/' + item.id; + } + } +})(); + diff --git a/barbican_ui/static/dashboard/barbican/secrets/secrets.service.spec.js b/barbican_ui/static/dashboard/barbican/secrets/secrets.service.spec.js new file mode 100644 index 0000000..711d1c0 --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/secrets/secrets.service.spec.js @@ -0,0 +1,53 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +(function() { + "use strict"; + + describe('Secrets service', function() { + var service; + beforeEach(module('horizon.app.core.openstack-service-api')); + beforeEach(module('horizon.dashboard.barbican.secrets')); + beforeEach(inject(function($injector) { + service = $injector.get('horizon.dashboard.barbican.secrets.service'); + })); + + describe('getPromise', function() { + it("provides a promise", inject(function($q, $injector, $timeout) { + var api = $injector.get('horizon.app.core.openstack-service-api.barbican'); + var deferred = $q.defer(); + spyOn(api, 'getSecrets').and.returnValue(deferred.promise); + var result = service.getPromise({}); + deferred.resolve({ + data:{ + items: [{id: '123abc', name: 'resource1'}] + } + }); + $timeout.flush(); + expect(api.getSecrets).toHaveBeenCalled(); + expect(result.$$state.value.data.items[0].name).toBe('resource1'); + })); + }); + + describe('urlFunction', function() { + it("get url", inject(function($injector) { + var detailRoute = $injector.get('horizon.app.core.detailRoute'); + var result = service.urlFunction({id:"123abc"}); + expect(result).toBe(detailRoute + "OS::Barbican::Secret/123abc"); + })); + }); + + }); + +})(); + diff --git a/barbican_ui/static/dashboard/barbican/secrets/workflow/info.help.html b/barbican_ui/static/dashboard/barbican/secrets/workflow/info.help.html new file mode 100644 index 0000000..1cd2914 --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/secrets/workflow/info.help.html @@ -0,0 +1,4 @@ +
+
Secret Name
+
An arbitrary human-readable name
+
diff --git a/barbican_ui/static/dashboard/barbican/secrets/workflow/recipe.help.html b/barbican_ui/static/dashboard/barbican/secrets/workflow/recipe.help.html new file mode 100644 index 0000000..7f7cd85 --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/secrets/workflow/recipe.help.html @@ -0,0 +1,7 @@ +
+
Base
+
Choose base drink.
+
Other options
+
Choose favorite options.
+
+ diff --git a/barbican_ui/static/dashboard/barbican/secrets/workflow/secret-model.js b/barbican_ui/static/dashboard/barbican/secrets/workflow/secret-model.js new file mode 100644 index 0000000..1da0687 --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/secrets/workflow/secret-model.js @@ -0,0 +1,63 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc model + * @name horizon.dashboard.barbican.secrets.model + * @description Service for the secret model + */ + angular + .module('horizon.dashboard.barbican.secrets') + .factory('horizon.dashboard.barbican.secrets.model', model); + + model.$inject = [ + ]; + + function model() { + var model = { + // params + "spec": {}, + + // methods + "init": init, + "cleanProperties": cleanProperties + }; + + function init() { + // initialize model + model.spec = { + "id": "", + "name": "", // text required + "description": "", // textarea + "enabled": true, // checkbox + "size": "M", // radio + "temperature": "H", // radio + "base": "", // select + "flavor": "", // select + "topping": "" // checkboxes + }; + } + + function cleanProperties() { + delete model.spec.id; + delete model.spec.tabs; + } + + return model; + } +})(); + diff --git a/barbican_ui/static/dashboard/barbican/secrets/workflow/workflow.service.js b/barbican_ui/static/dashboard/barbican/secrets/workflow/workflow.service.js new file mode 100644 index 0000000..322d592 --- /dev/null +++ b/barbican_ui/static/dashboard/barbican/secrets/workflow/workflow.service.js @@ -0,0 +1,211 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc workflow + * @name horizon.dashboard.barbican.secrets.workflow + * @description Service for the create/update workflow + */ + angular + .module('horizon.dashboard.barbican.secrets') + .factory('horizon.dashboard.barbican.secrets.workflow', workflow); + + workflow.$inject = [ + 'horizon.dashboard.barbican.basePath', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.widgets.form.ModalFormService' + ]; + + function workflow(basePath, gettext, modal) { + var workflow = { + init: init + }; + + function init(title, submitText, submitIcon, model) { + var schema, form; + + // schema + schema = { + "type": "object", + "properties": { + "name": { + "title": gettext("Name"), + "type": "string" + }, + "description": { + "title": gettext("Description"), + "type": "string" + }, + "enabled": { + "title": gettext("Enabled"), + "type": "boolean", + "default": true + }, + "size": { + "title": gettext("Size"), + "type": "string", + "default": "M" + }, + "temperature": { + "title": gettext("Temperature"), + "type": "string", + "default": "H" + }, + "base": { + "title": gettext("Base"), + "type": "string", + "default": "" + }, + "flavor": { + "title": gettext("Flavor"), + "type": "string", + "default": "" + }, + "topping": { + "title": gettext("Topping") + } + } + }; + + // form + form = [ + { + "type": "tabs", + "tabs": [ + { + "title": gettext("Info"), + "help": basePath + "secrets/workflow/info.help.html", + "items": [ + { + "key": "name", + "placeholder": gettext("Name of the secret."), + "required": true + }, + { + "key": "description", + "type": "textarea" + }, + { + "key": "enabled", + "type": "checkbox" + } + ] + }, + { + "title": gettext("Recipe"), + "help": basePath + "secrets/workflow/recipe.help.html", + "items": [ + { + "key": "size", + "type": "radiobuttons", + "titleMap": [ + {"value": "S", "name": gettext("Small")}, + {"value": "M", "name": gettext("Medium")}, + {"value": "L", "name": gettext("Large")}, + {"value": "XL", "name": gettext("Extra Large")} + ] + }, + { + "key": "temperature", + "type": "radiobuttons", + "titleMap": [ + {"value": "H", "name": gettext("Hot")}, + {"value": "I", "name": gettext("Ice")} + ] + }, + { + "key": "base", + "type": "select", + "titleMap": [ + {"value": "", "name": gettext("Choose base.")}, + { + "value": "blend", + "name": gettext("House Blend"), + "group": gettext("Coffee") + }, + { + "value": "mandheling", + "name": gettext("Mandheling"), + "group": gettext("Coffee")}, + { + "value": "colombia", + "name": gettext("Colombia"), + "group": gettext("Coffee") + }, + { + "value": "espresso", + "name": gettext("Espresso"), + "group": gettext("Coffee") + }, + { + "value": "earl_gray", + "name": gettext("Earl Gray"), + "group": gettext("Tea") + }, + { + "value": "darjeeling", + "name": gettext("Darjeeling"), + "group": gettext("Tea")}, + { + "value": "orange_pekoe", + "name": gettext("Orange Pekoe"), + "group": gettext("Tea") + } + ] + }, + { + "key": "flavor", + "type": "select", + "titleMap": [ + {"value": "", "name": gettext("Choose flavor.")}, + {"value": "chocolate", "name": gettext("Chocolate")}, + {"value": "mocha", "name": gettext("Mocha")}, + {"value": "strawberry", "name": gettext("Strawberry")}, + {"value": "blueberry", "name": gettext("Blueberry")}, + {"value": "raspberry", "name": gettext("Raspberry")} + ] + }, + { + "key": "topping", + "type": "checkboxes", + "titleMap": [ + {"value": "clushed_nuts", "name": gettext("Clushed Nuts")}, + {"value": "whip_cream", "name": gettext("Whip Cream")}, + {"value": "mixed_serial", "name": gettext("Mixed Serial")} + ] + } + ] // items + } // tab + ] // tabs + } + ]; // form + + var config = { + "title": title, + "submitText": submitText, + "schema": schema, + "form": form, + "model": model + }; + + return modal.open(config); + } + + return workflow; + } +})(); + diff --git a/barbican_ui/test/__init__.py b/barbican_ui/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/barbican_ui/test/api_tests/__init__.py b/barbican_ui/test/api_tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/barbican_ui/test/api_tests/test_client.py b/barbican_ui/test/api_tests/test_client.py new file mode 100644 index 0000000..c6dc642 --- /dev/null +++ b/barbican_ui/test/api_tests/test_client.py @@ -0,0 +1,20 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from openstack_dashboard.test import helpers as test + + +class SecretsClientTestCase(test.TestCase): + + # Unit tests for Client API. + def test_me(self): + self.assertTrue(1 + 1 == 2) diff --git a/barbican_ui/test/api_tests/test_rest_api.py b/barbican_ui/test/api_tests/test_rest_api.py new file mode 100644 index 0000000..4afe9a9 --- /dev/null +++ b/barbican_ui/test/api_tests/test_rest_api.py @@ -0,0 +1,20 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from openstack_dashboard.test import helpers as test + + +class SecretsRestTestCase(test.TestCase): + + # Unit tests for REST API. + def test_me(self): + self.assertTrue(1 + 1 == 2) diff --git a/barbican_ui/test/integration_tests/__init__.py b/barbican_ui/test/integration_tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/barbican_ui/test/settings.py b/barbican_ui/test/settings.py new file mode 100644 index 0000000..dcb06fd --- /dev/null +++ b/barbican_ui/test/settings.py @@ -0,0 +1,37 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# Default to Horizons test settings to avoid any missing keys +from horizon.test.settings import * # noqa +from openstack_dashboard.test.settings import * # noqa + +# Update the dashboards with barbican_ui +import barbican_ui.enabled +import openstack_dashboard.enabled +from openstack_dashboard.utils import settings + +# pop these keys to avoid log warnings about deprecation +# update_dashboards will populate them anyway +HORIZON_CONFIG.pop('dashboards', None) +HORIZON_CONFIG.pop('default_dashboard', None) + +settings.update_dashboards( + [ + barbican_ui.enabled, + openstack_dashboard.enabled, + ], + HORIZON_CONFIG, + INSTALLED_APPS +) + +# Ensure any duplicate apps are removed after the update_dashboards call +INSTALLED_APPS = list(set(INSTALLED_APPS)) diff --git a/barbican_ui/version.py b/barbican_ui/version.py new file mode 100644 index 0000000..8f90a1b --- /dev/null +++ b/barbican_ui/version.py @@ -0,0 +1,14 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +import pbr.version + +version_info = pbr.version.VersionInfo('barbican_ui') diff --git a/doc/requirements.txt b/doc/requirements.txt new file mode 100644 index 0000000..f696fb1 --- /dev/null +++ b/doc/requirements.txt @@ -0,0 +1,14 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +# Order matters to the pip dependency resolver, so sorting this file +# changes how packages are installed. New dependencies should be +# added in alphabetical order, however, some dependencies may need to +# be installed in a specific order. +# +# Requirements for docs +mock>=2.0.0 # BSD +openstackdocstheme>=1.18.1 # Apache-2.0 +reno>=2.5.0 # Apache-2.0 +sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD +sphinxcontrib-apidoc>=0.2.0 # BSD diff --git a/doc/source/conf.py b/doc/source/conf.py new file mode 100644 index 0000000..5726f33 --- /dev/null +++ b/doc/source/conf.py @@ -0,0 +1,337 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# Horizon documentation build configuration file, created by +# sphinx-quickstart on Thu Oct 27 11:38:59 2011. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +from __future__ import print_function + +import os +import sys + +import django + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", "..")) + +sys.path.insert(0, ROOT) + +# This is required for ReadTheDocs.org, but isn't a bad idea anyway. +os.environ.setdefault('DJANGO_SETTINGS_MODULE', + 'barbican_ui.test.settings') + +# Starting in Django 1.7, standalone scripts, such as a sphinx build +# require that django.setup() be called first. +# https://docs.djangoproject.com/en/1.8/releases/1.7/#standalone-scripts +django.setup() + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ---------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. +# They can be extensions coming with Sphinx (named 'sphinx.ext.*') +# or your custom ones. +extensions = ['sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.viewcode', + 'sphinxcontrib.apidoc', + 'openstackdocstheme', + ] + +# Add any paths that contain templates here, relative to this directory. +# templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Barbican UI' +copyright = u'2017, OpenStack Foundation' + +# Release notes are version independent. +# The full version, including alpha/beta/rc tags. +release = '' +# The short X.Y version. +version = '' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# today = '' +# Else, today_fmt is used as the format for a strftime call. +# today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['**/#*', '**~', '**/#*#'] + +# The reST default role (used for this markup: `text`) +# to use for all documents. +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + +primary_domain = 'py' +nitpicky = False + +# sphinxcontrib-apidoc +apidoc_module_dir = '../../barbican_ui' +apidoc_output_dir = 'contributor/api' +apidoc_excluded_paths = [ + 'test', +] + +# -- Options for HTML output -------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'openstackdocs' + +# openstackdocstheme options +repository_name = 'openstack/barbican-ui' +bug_project = 'barbican-ui' +bug_tag = '' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +# html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +# html_static_path = ['_static'] + +# Must set this variable to include year, month, day, hours, and minutes. +html_last_updated_fmt = '%Y-%m-%d %H:%M' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# html_additional_pages = {} + +# If false, no module index is generated. +# html_domain_indices = True + +# If false, no index is generated. +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Barbican-UIdoc' + + +# -- Options for LaTeX output ------------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # 'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass +# [howto/manual]). +latex_documents = [ + ('index', 'Barbican-UI.tex', + u'Barbican UI Documentation', + u'OpenStack Foundation', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +# latex_use_parts = False + +# If true, show page references after internal links. +# latex_show_pagerefs = False + +# If true, show URL addresses after external links. +# latex_show_urls = False + +# Documents to append as an appendix to all manuals. +# latex_appendices = [] + +# If false, no module index is generated. +# latex_domain_indices = True + + +# -- Options for manual page output ------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', u'Barbican UI Documentation', + 'Documentation for the Barbican UI plugin to the Openstack\ + Dashboard (Horizon)', + [u'OpenStack'], 1) +] + +# If true, show URL addresses after external links. +# man_show_urls = False + + +# -- Options for Texinfo output ----------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'Barbican UI', + u'Barbican UI Documentation', u'OpenStack', + 'Barbican UI', + 'Barbican User Interface', 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +# texinfo_appendices = [] + +# If false, no module index is generated. +# texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# texinfo_show_urls = 'footnote' + + +# -- Options for Epub output -------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = u'Barbican UI' +epub_author = u'OpenStack' +epub_publisher = u'OpenStack' +epub_copyright = u'2017, OpenStack' + +# The language of the text. It defaults to the language option +# or en if the language is not set. +# epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +# epub_scheme = '' + +# The unique identifier of the text. This can be an ISBN number +# or the project homepage. +# epub_identifier = '' + +# A unique identification for the text. +# epub_uid = '' + +# A tuple containing the cover image and cover page html template filenames. +# epub_cover = () + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +# epub_pre_files = [] + +# HTML files shat should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +# epub_post_files = [] + +# A list of files that should not be packed into the epub file. +# epub_exclude_files = [] + +# The depth of the table of contents in toc.ncx. +# epub_tocdepth = 3 + +# Allow duplicate toc entries. +# epub_tocdup = True diff --git a/doc/source/configuration/index.rst b/doc/source/configuration/index.rst new file mode 100644 index 0000000..5f23f7f --- /dev/null +++ b/doc/source/configuration/index.rst @@ -0,0 +1,10 @@ +============= +Configuration +============= + +Barbican UI has no configuration option. + +For more configurations, see +`Deployment & Configuration +`__ +in the Horizon documentation. diff --git a/doc/source/contributor/api.rst b/doc/source/contributor/api.rst new file mode 100644 index 0000000..dd3be26 --- /dev/null +++ b/doc/source/contributor/api.rst @@ -0,0 +1,9 @@ +===================== +Source Code Reference +===================== + +.. toctree:: + :maxdepth: 1 + :glob: + + api/* diff --git a/doc/source/contributor/index.rst b/doc/source/contributor/index.rst new file mode 100644 index 0000000..87ff3ed --- /dev/null +++ b/doc/source/contributor/index.rst @@ -0,0 +1,16 @@ +================= +Contributor Guide +================= + +There is no topic specific to Barbican UI now. + +See `Horizon Contributor Documentation +`__ +for general topic on developing a dashboard on horizon. + +---- + +.. toctree:: + :maxdepth: 1 + + api diff --git a/doc/source/index.rst b/doc/source/index.rst new file mode 100644 index 0000000..3199ab8 --- /dev/null +++ b/doc/source/index.rst @@ -0,0 +1,33 @@ +=============================== +Barbican UI +=============================== + +Barbican User Interface + +* Free software: Apache license +* Source: http://opendev.org/openstack/barbican-ui +* Bugs: http://bugs.launchpad.net/barbican-ui + +Features +-------- + +* TODO + +User Documentation +------------------ + +.. toctree:: + :maxdepth: 2 + + install/index + configuration/index + Release Notes + +Contributor Guide +----------------- + +.. toctree:: + :glob: + :maxdepth: 2 + + contributor/index diff --git a/doc/source/install/index.rst b/doc/source/install/index.rst new file mode 100644 index 0000000..0c4b9a4 --- /dev/null +++ b/doc/source/install/index.rst @@ -0,0 +1,53 @@ +============ +Installation +============ + +Enabling in DevStack +-------------------- + +Add this repo as an external repository into your ``local.conf`` file:: + + [[local|localrc]] + enable_plugin barbican_ui https://github.com/openstack/barbican-ui + +Manual Installation +------------------- + +Begin by cloning the Horizon and Barbican UI repositories:: + + git clone https://github.com/openstack/horizon + git clone https://github.com/openstack/barbican-ui + +Create a virtual environment and install Horizon dependencies:: + + cd horizon + python tools/install_venv.py + +Set up your ``local_settings.py`` file:: + + cp openstack_dashboard/local/local_settings.py.example openstack_dashboard/local/local_settings.py + +Open up the copied ``local_settings.py`` file in your preferred text +editor. You will want to customize several settings: + +- ``OPENSTACK_HOST`` should be configured with the hostname of your + OpenStack server. Verify that the ``OPENSTACK_KEYSTONE_URL`` and + ``OPENSTACK_KEYSTONE_DEFAULT_ROLE`` settings are correct for your + environment. (They should be correct unless you modified your + OpenStack server to change them.) + +Install Barbican UI with all dependencies in your virtual environment:: + + tools/with_venv.sh pip install -e ../barbican-ui/ + +And enable it in Horizon:: + + ln -s ../barbican-ui/barbican_ui/enabled/_90_project_barbican_panelgroup.py openstack_dashboard/local/enabled + ln -s ../barbican-ui/barbican_ui/enabled/_91_project_barbican_secrets_panel.py openstack_dashboard/local/enabled + +To run horizon with the newly enabled Barbican UI plugin run:: + + ./run_tests.sh --runserver 0.0.0.0:8080 + +to have the application start on port 8080 and the horizon dashboard will be +available in your browser at http://localhost:8080/ diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..2eb38a6 --- /dev/null +++ b/manage.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os +import sys + +from django.core.management import execute_from_command_line + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", + "barbican_ui.test.settings") + execute_from_command_line(sys.argv) diff --git a/package.json b/package.json new file mode 100644 index 0000000..4a76565 --- /dev/null +++ b/package.json @@ -0,0 +1,34 @@ +{ + "name": "barbican-ui", + "description": "Barbican UI JavaScript tests", + "repository": { + "type": "git", + "url": "https://opendev.org/openstack/barbican-ui" + }, + "version": "0.0.0", + "private": true, + "license": "Apache 2.0", + "author": "Openstack ", + "devDependencies": { + "eslint": "3.19.x", + "eslint-config-openstack": "^4.0.1", + "eslint-plugin-angular": "3.1.x", + "jasmine-core": "2.8.x", + "karma": "1.7.x", + "karma-chrome-launcher": "^2.2.0", + "karma-cli": "1.0.x", + "karma-coverage": "1.1.x", + "karma-jasmine": "1.1.x", + "karma-ng-html2js-preprocessor": "1.0.x", + "karma-phantomjs-launcher": "1.0.x", + "karma-threshold-reporter": "0.1.x", + "phantomjs-prebuilt": "2.1.x" + }, + "dependencies": {}, + "scripts": { + "postinstall": "if [ ! -d .tox ] || [ ! -d .tox/karma ]; then tox -ekarma --notest; python3 -m pip install -U -t ./.tox/karma/lib/`python3 -V|tr -d ' '|tr 'P' 'p'|cut -c -9`/site-packages/ -chttps://opendev.org/openstack/requirements/raw/branch/master/upper-constraints.txt ../horizon; fi", + "lint": "eslint --no-color barbican_ui/static", + "lintq": "eslint --quiet barbican_ui/static", + "test": "karma start barbican_ui/karma.conf.js --single-run" + } +} diff --git a/releasenotes/notes/.placeholder b/releasenotes/notes/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py new file mode 100644 index 0000000..7e7cba2 --- /dev/null +++ b/releasenotes/source/conf.py @@ -0,0 +1,280 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from barbican_ui import version as ui_ver + +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'openstackdocstheme', + 'reno.sphinxext', +] + +# Add any paths that contain templates here, relative to this directory. +# templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Barbican UI Release Notes' +copyright = u'2017, OpenStack Foundation' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +# The full version, including alpha/beta/rc tags. +release = ui_ver.version_info.release_string() +# The short X.Y version. +version = ui_ver.version_info.version_string() + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# today = '' +# Else, today_fmt is used as the format for a strftime call. +# today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +# keep_warnings = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'openstackdocs' + +# openstackdocstheme options +repository_name = 'openstack/barbican-ui' +bug_project = 'barbican-ui' +bug_tag = '' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +# html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +# html_static_path = [] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +# html_extra_path = [] + +# Must set this variable to include year, month, day, hours, and minutes. +html_last_updated_fmt = '%Y-%m-%d %H:%M' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# html_additional_pages = {} + +# If false, no module index is generated. +# html_domain_indices = True + +# If false, no index is generated. +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Barbican-UIReleaseNotesdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # 'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ('index', + 'Barbican-UIReleaseNotes.tex', + u'Barbican UI Release Notes Documentation', + u'Barbican UI Developers', + 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +# latex_use_parts = False + +# If true, show page references after internal links. +# latex_show_pagerefs = False + +# If true, show URL addresses after external links. +# latex_show_urls = False + +# Documents to append as an appendix to all manuals. +# latex_appendices = [] + +# If false, no module index is generated. +# latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'barbican_uireleasenotes', u'Barbican UI Release Notes Documentation', + [u'Barbican UI Developers'], 1) +] + +# If true, show URL addresses after external links. +# man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'Barbican-UIReleaseNotes', u'Barbican UI Release Notes Documentation', + u'Barbican UI Developers', 'Barbican-UIReleaseNotes', + 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +# texinfo_appendices = [] + +# If false, no module index is generated. +# texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +# texinfo_no_detailmenu = False + +# -- Options for Internationalization output ------------------------------ +locale_dirs = ['locale/'] diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst new file mode 100644 index 0000000..4939b15 --- /dev/null +++ b/releasenotes/source/index.rst @@ -0,0 +1,8 @@ +========================================== +barbican_ui Release Notes +========================================== + +.. toctree:: + :maxdepth: 1 + + unreleased diff --git a/releasenotes/source/unreleased.rst b/releasenotes/source/unreleased.rst new file mode 100644 index 0000000..cd22aab --- /dev/null +++ b/releasenotes/source/unreleased.rst @@ -0,0 +1,5 @@ +============================== + Current Series Release Notes +============================== + +.. release-notes:: diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7a7b65e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,12 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +# Order matters to the pip dependency resolver, so sorting this file +# changes how packages are installed. New dependencies should be +# added in alphabetical order, however, some dependencies may need to +# be installed in a specific order. +# +# PBR should always appear first +pbr!=2.1.0,>=2.0.0 # Apache-2.0 + +horizon>=14.0.0.0b3 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..7fd2cb6 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,24 @@ +[metadata] +name = barbican-ui +summary = Barbican User Interface +description-file = + README.rst +author = OpenStack +author-email = openstack-discuss@lists.openstack.org +home-page = http://www.openstack.org/ +classifier = + Environment :: OpenStack + Intended Audience :: Information Technology + Intended Audience :: System Administrators + License :: OSI Approved :: Apache Software License + Operating System :: POSIX :: Linux + Programming Language :: Python + Programming Language :: Python :: 2 + Programming Language :: Python :: 2.7 + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.6 + +[files] +packages = + barbican_ui + diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..d74ff58 --- /dev/null +++ b/setup.py @@ -0,0 +1,27 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT +import setuptools + +# In python < 2.7.4, a lazy loading of package `pbr` will break +# setuptools if some other modules registered functions in `atexit`. +# solution from: http://bugs.python.org/issue15881#msg170215 +try: + import multiprocessing # noqa +except ImportError: + pass + +setuptools.setup( + setup_requires=['pbr>=2.0.0'], + pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000..d3163df --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,14 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +# Order matters to the pip dependency resolver, so sorting this file +# changes how packages are installed. New dependencies should be +# added in alphabetical order, however, some dependencies may need to +# be installed in a specific order. +# +# Hacking should appear first in case something else depends on pep8 +hacking>=1.1.0,<1.2.0 # Apache-2.0 + +coverage!=4.4,>=4.0 # Apache-2.0 +mock>=2.0.0 # BSD +testtools>=2.2.0 # MIT diff --git a/test-shim.js b/test-shim.js new file mode 100644 index 0000000..5b364ba --- /dev/null +++ b/test-shim.js @@ -0,0 +1,96 @@ +/* + * Shim for Javascript unit tests; supplying expected global features. + * This should be removed from the codebase once i18n services are provided. + * Taken from default i18n file provided by Django. + */ + +var horizonPlugInModules = []; + + +(function (globals) { + + var django = globals.django || (globals.django = {}); + + + django.pluralidx = function (count) { return (count == 1) ? 0 : 1; }; + + /* gettext identity library */ + + django.gettext = function (msgid) { return msgid; }; + django.ngettext = function (singular, plural, count) { return (count == 1) ? singular : plural; }; + django.gettext_noop = function (msgid) { return msgid; }; + django.pgettext = function (context, msgid) { return msgid; }; + django.npgettext = function (context, singular, plural, count) { return (count == 1) ? singular : plural; }; + + + django.interpolate = function (fmt, obj, named) { + if (named) { + return fmt.replace(/%\(\w+\)s/g, function(match){return String(obj[match.slice(2,-2)])}); + } else { + return fmt.replace(/%s/g, function(match){return String(obj.shift())}); + } + }; + + + /* formatting library */ + + django.formats = { + "DATETIME_FORMAT": "N j, Y, P", + "DATETIME_INPUT_FORMATS": [ + "%Y-%m-%d %H:%M:%S", + "%Y-%m-%d %H:%M:%S.%f", + "%Y-%m-%d %H:%M", + "%Y-%m-%d", + "%m/%d/%Y %H:%M:%S", + "%m/%d/%Y %H:%M:%S.%f", + "%m/%d/%Y %H:%M", + "%m/%d/%Y", + "%m/%d/%y %H:%M:%S", + "%m/%d/%y %H:%M:%S.%f", + "%m/%d/%y %H:%M", + "%m/%d/%y" + ], + "DATE_FORMAT": "N j, Y", + "DATE_INPUT_FORMATS": [ + "%Y-%m-%d", + "%m/%d/%Y", + "%m/%d/%y" + ], + "DECIMAL_SEPARATOR": ".", + "FIRST_DAY_OF_WEEK": "0", + "MONTH_DAY_FORMAT": "F j", + "NUMBER_GROUPING": "3", + "SHORT_DATETIME_FORMAT": "m/d/Y P", + "SHORT_DATE_FORMAT": "m/d/Y", + "THOUSAND_SEPARATOR": ",", + "TIME_FORMAT": "P", + "TIME_INPUT_FORMATS": [ + "%H:%M:%S", + "%H:%M:%S.%f", + "%H:%M" + ], + "YEAR_MONTH_FORMAT": "F Y" + }; + + django.get_format = function (format_type) { + var value = django.formats[format_type]; + if (typeof(value) == 'undefined') { + return format_type; + } else { + return value; + } + }; + + /* add to global namespace */ + globals.pluralidx = django.pluralidx; + globals.gettext = django.gettext; + globals.ngettext = django.ngettext; + globals.gettext_noop = django.gettext_noop; + globals.pgettext = django.pgettext; + globals.npgettext = django.npgettext; + globals.interpolate = django.interpolate; + globals.get_format = django.get_format; + globals.STATIC_URL = '/static/'; + globals.WEBROOT = '/'; + +}(this)); diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..cecff70 --- /dev/null +++ b/tox.ini @@ -0,0 +1,124 @@ +[tox] +envlist = pep8,py27-local,py36-local,py3-dj111-local,eslint,karma-local,docs-local,releasenotes +minversion = 2.3.2 +skipsdist = True + +[testenv] +usedevelop = True +setenv = VIRTUAL_ENV={envdir} + BRANCH_NAME=master + CLIENT_NAME=barbican-ui + NOSE_WITH_OPENSTACK=1 + NOSE_OPENSTACK_COLOR=1 + NOSE_OPENSTACK_RED=0.05 + NOSE_OPENSTACK_YELLOW=0.025 + NOSE_OPENSTACK_SHOW_ELAPSED=1 +deps = + -c{env:UPPER_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/branch/master/upper-constraints.txt} + -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt +commands = python manage.py test {posargs} --settings=barbican_ui.test.settings + +# For installation of horizon on local +# NOTICE: this tox.ini requires horizon repository cloned in sibling directory. +[testenv:hz-local] +commands = + pip install -e ../horizon + +[testenv:venv] +commands = {posargs} + +[testenv:pep8] +basepython = python3 +commands = flake8 {posargs} + +[flake8] +ignore = F405 +exclude = .venv,.git,.tox,dist,*lib/python*,*egg,build,node_modules +max-complexity = 20 + +[testenv:cover] +basepython = python3 +commands = + coverage erase + coverage run {toxinidir}/manage.py test barbican_ui --settings=barbican_ui.test.settings {posargs} --exclude-dir=barbican_ui/test/integration_tests {posargs} + coverage xml --omit '.tox/cover/*' -o 'cover/coverage.xml' + coverage html --omit '.tox/cover/*' -d 'cover/htmlcov' + +# NOTE(shu-mutow): On CI infra, horizon will be installed +# according to job setting. but on local, we need to install +# horizon from master branch. +[testenv:py27-local] +basepython = python2.7 +commands = + {[testenv:hz-local]commands} + {[testenv]commands} + +[testenv:py36-local] +basepython = python3.6 +commands = + {[testenv:hz-local]commands} + {[testenv]commands} + +[testenv:py3-dj111] +basepython = python3 +commands = + pip install django>=1.11,<2 + {[testenv]commands} + +[testenv:py3-dj111-local] +basepython = python3 +commands = + {[testenv:hz-local]commands} + pip install django>=1.11,<2 + {[testenv]commands} + +[testenv:eslint] +basepython = python3 +whitelist_externals = + npm +commands = + npm install + npm run lint + +# NOTE(shu-mutow): The "postinstall" script on package.json will install horizon +# from master branch into python3.x environment for testing javascripts. +# Horizon from master is needed to be cloned into ../horizon on both local and CI. +[testenv:karma] +basepython = python3 +whitelist_externals = + {[testenv:eslint]whitelist_externals} +commands = + npm install + npm run test + +[testenv:karma-local] +basepython = python3 +whitelist_externals = + {[testenv:eslint]whitelist_externals} +commands = + {[testenv:karma]commands} + +[testenv:docs] +basepython = python3 +deps = + -c{env:UPPER_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/branch/master/upper-constraints.txt} + -r{toxinidir}/doc/requirements.txt +commands= + sphinx-build -W -b html doc/source doc/build/html + +[testenv:docs-local] +basepython = python3 +deps = + {[testenv:docs]deps} +commands= + {[testenv:hz-local]commands} + {[testenv:docs]commands} + +[testenv:releasenotes] +basepython = python3 +deps = + {[testenv:docs]deps} +commands = + sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html +