Retiring App Catalog

This commit removes content associated with the app catalog.
This is part of the first step, following the guide at:
https://docs.openstack.org/infra/manual/drivers.html#retiring-a-project

Earlier this year the TC agreed to retire this project:
https://review.openstack.org/#/c/452086/

In July, Fungi sent
a note to the operators list as well:
http://lists.openstack.org/pipermail/openstack-operators/2017-July/013965.html

Depends-On: I52ce13057643d69a0fd87bce20ee460c6b7c2f17
Change-Id: Idd69df495663e5ab6466c8d06945c9257a93842a
This commit is contained in:
Christopher Aedo 2017-12-10 10:12:12 -08:00 committed by Andreas Jaeger
parent 56fc380d8e
commit f0738be0c4
86 changed files with 8 additions and 8259 deletions

View File

@ -1,7 +0,0 @@
[run]
branch = True
source = openstack_catalog
omit = openstack_catalog/tests/*,openstack_catalog/openstack/*
[report]
ignore_errors = True

View File

@ -1,13 +0,0 @@
# use standard openstack linting profile
extends: openstack
env:
# browser global variables.
browser: true
rules:
# Require camel case names
# http://eslint.org/docs/rules/camelcase
camelcase:
- 1 # report camelcase properties as warnings
- properties: "never"

53
.gitignore vendored
View File

@ -1,53 +0,0 @@
*.py[cod]
# C extensions
*.so
# Packages
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
lib
lib64
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
nosetests.xml
.testrepository
.venv
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# Complexity
output/*.html
output/*/index.html
# Sphinx
doc/build
# pbr generates these
AUTHORS
ChangeLog
# Editors
*~
.*.swp
.*sw?

View File

@ -1,4 +0,0 @@
[gerrit]
host=review.openstack.org
port=29418
project=openstack/app-catalog.git

View File

@ -1,3 +0,0 @@
# Format is:
# <preferred e-mail> <other e-mail 1>
# <preferred e-mail> <other e-mail 2>

View File

@ -1,7 +0,0 @@
[DEFAULT]
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list

View File

@ -1,16 +0,0 @@
If you would like to contribute to the development of OpenStack,
you must follow the steps in this page:
http://docs.openstack.org/infra/manual/developers.html
Once those steps have been completed, changes to OpenStack
should be submitted for review via the Gerrit tool, following
the workflow documented at:
http://docs.openstack.org/infra/manual/developers.html#development-workflow
Pull requests submitted through GitHub will be ignored.
Bugs should be filed on Launchpad, not GitHub:
https://bugs.launchpad.net/catalog

View File

@ -1,4 +0,0 @@
catalog Style Commandments
===============================================
Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/

180
LICENSE
View File

@ -1,180 +0,0 @@
Some files from:
http://www.iconarchive.com/show/beautiful-flat-icons-by-elegantthemes/plugin-icon.html
Everything else:
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.

View File

@ -1,6 +0,0 @@
include AUTHORS
include ChangeLog
exclude .gitignore
exclude .gitreview
global-exclude *.pyc

View File

@ -1,48 +1,10 @@
===============================
OpenStack Community App Catalog
===============================
Application Catalog for OpenStack
This project is no longer maintained.
The OpenStack Community App Catalog will help you make
applications available on your OpenStack cloud by providing
a community driven catalog containing Glance images, Heat
templates and Murano applications.
The contents of this repository are still available in the Git
source code management system. To see the contents of this
repository before it reached its end of life, please check out the
previous commit with "git checkout HEAD^1".
Overview
--------
By providing a public location where OpenStack users can
publish and consume artifacts to share and add additional
capabilities, all OpenStack clouds become more valuable. For
example vendors can publish Glance images for launching new
VMs, Heat templates for creating new stacks, or Murano app
packages for installing complete applications. When many
OpenStack community members are publishing artifacts that
users can easily download and install into their clouds this
will multiply the value of their OpenStack cloud. Easy
access to images, templates and apps will solve the "what
now" question some user consider after successfully
deploying an OpenStack environment.
* Free software: Apache 2.0 License. See LICENSE file.
* Documentation: http://wiki.openstack.org/wiki/App-Catalog
Getting started with the Community App Catalog
----------------------------------------------
See the wiki page to see how to get started using or
contributing to the App Catalog. Visit
https://wiki.openstack.org/wiki/App-Catalog for more
details.
Project Info
------------
* Web-site: http://apps.openstack.org
* Source: http://git.openstack.org/cgit/openstack/app-catalog
* Wiki: https://wiki.openstack.org/wiki/App-Catalog
* Launchpad: https://launchpad.net/app-catalog
* Blueprints: https://blueprints.launchpad.net/app-catalog
* Bugs: http://bugs.launchpad.net/app-catalog
* Code Reviews: https://review.openstack.org/#q,status:open+app-catalog,n,z
* IRC: #openstack-app-catalog at freenode
For any further questions, please email
openstack-dev@lists.openstack.org or join #openstack-dev on
Freenode.

View File

@ -1,2 +0,0 @@
[python: **.py]

View File

@ -1,40 +0,0 @@
# This is a cross-platform list tracking distribution packages needed by tests;
# see http://docs.openstack.org/infra/bindep/ for additional information.
apt-transport-https
lsb-release
ant
build-essential [platform:dpkg]
curl
cyrus-sasl-devel [platform:rpm]
dbus
firefox [!platform:debian]
gawk
iceweasel [platform:debian]
language-pack-en [platform:ubuntu]
libcurl-devel [platform:rpm]
libcurl4-gnutls-dev [platform:dpkg]
libffi-dev [platform:dpkg]
libffi-devel [platform:rpm]
libjpeg-dev [platform:dpkg]
libjpeg-turbo-devel [platform:rpm]
libldap2-dev [platform:dpkg]
libsasl2-dev [platform:dpkg]
libselinux-python [platform:rpm]
libuuid-devel [platform:rpm]
locales [platform:debian]
pkg-config [platform:dpkg]
pkgconfig [platform:rpm]
python-dev [platform:dpkg]
python-devel [platform:rpm]
python-libvirt [platform:dpkg]
python-lxml
python3-all-dev [platform:ubuntu-trusty]
python3-dev [platform:dpkg]
python3-devel [platform:fedora]
python3.4 [platform:ubuntu-trusty]
python34-devel [platform:centos]
unzip
uuid-dev [platform:dpkg]
xorg-x11-server-Xvfb [platform:rpm]
xvfb [platform:dpkg]

View File

@ -1,48 +0,0 @@
app-catalog-ci
===============
Description
-----------
This is a collection of deployment scripts for app-catalog CI project.
It consists of Puppet module catalog_ci and an additional shell script.
The scripts allow to setup Jenkins with access to Gerrit to trigger
appropriate jobs on a commit to the app-catalog project.
Deployment
----------
Execute deploy.sh script to begin deployment:
(some operations require superuser access rights)
./deploy.sh
The script will install necessary packages and deploy/configure Jenkins.
You will be able to access it at http://server_ip:8080
The following steps are required after deployment:
- Setup access rights. By default Jenkins uses Launchpad OpenID and all users
have full access to Jenkins.
Proceed to Manage Jenkins -> Configure Global Security and setup security.
- Finish Gerrit auth setup:
Add a private key file (id_rsa) to Jenkins ssh directory:
* sudo mkdir -p /var/lib/jenkins/.ssh
* sudo cp id_rsa /var/lib/jenkins/.ssh
* sudo chown -R jenkins:jenkins /var/lib/jenkins/.ssh
* sudo chmod 600 /var/lib/jenkins/.ssh/id_rsa
Then proceed to Manage Jenkins -> Gerrit Trigger and press the button
in 'Status' column. If button will change its color to green, your connection
to Gerrit works OK and Jenkins is receiving Gerrit events. Otherwise please
check Gerrit server parameters.
- rclone (http://rclone.org/) is used to upload images to CDN.
Please install and configure it manually, if it's required.
'jenkins' user should be able to access default rclone configuration file
in order to use it.
Jenkins Jobs
------------
Jenkins Job Builder is used to configure Jenkins jobs. It will be automatically
installed by deployment scripts. Jobs configuration files will be placed to
/etc/jenkins_jobs/jobs. You can use the following command to apply your changes
jenkins-jobs update /etc/jenkins_jobs/jobs

View File

@ -1 +0,0 @@
include catalog_ci

View File

@ -1,45 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<hudson>
<disabledAdministrativeMonitors/>
<version>1.596.2</version>
<numExecutors>3</numExecutors>
<mode>NORMAL</mode>
<useSecurity>true</useSecurity>
<authorizationStrategy class="hudson.security.GlobalMatrixAuthorizationStrategy">
<permission>hudson.model.Hudson.Administer:anonymous</permission>
<permission>hudson.model.Hudson.Read:anonymous</permission>
<permission>hudson.model.Item.Read:anonymous</permission>
</authorizationStrategy>
<securityRealm class="hudson.plugins.openid.OpenIdSsoSecurityRealm" plugin="openid@2.1.1">
<endpoint>https://login.launchpad.net/+openid</endpoint>
</securityRealm>
<disableRememberMe>false</disableRememberMe>
<projectNamingStrategy class="jenkins.model.ProjectNamingStrategy$DefaultProjectNamingStrategy"/>
<workspaceDir>${JENKINS_HOME}/workspace/${ITEM_FULLNAME}</workspaceDir>
<buildsDir>${ITEM_ROOTDIR}/builds</buildsDir>
<markupFormatter class="hudson.markup.RawHtmlMarkupFormatter" plugin="antisamy-markup-formatter@1.1">
<disableSyntaxHighlighting>false</disableSyntaxHighlighting>
</markupFormatter>
<jdks/>
<viewsTabBar class="hudson.views.DefaultViewsTabBar"/>
<myViewsTabBar class="hudson.views.DefaultMyViewsTabBar"/>
<clouds/>
<slaves/>
<quietPeriod>5</quietPeriod>
<scmCheckoutRetryCount>0</scmCheckoutRetryCount>
<views>
<hudson.model.AllView>
<owner class="hudson" reference="../../.."/>
<name>All</name>
<filterExecutors>false</filterExecutors>
<filterQueue>false</filterQueue>
<properties class="hudson.model.View$PropertyList"/>
</hudson.model.AllView>
</views>
<primaryView>All</primaryView>
<slaveAgentPort>0</slaveAgentPort>
<label></label>
<nodeProperties/>
<globalNodeProperties/>
<noUsageStatistics>true</noUsageStatistics>
</hudson>

View File

@ -1,67 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<com.sonyericsson.hudson.plugins.gerrit.trigger.PluginImpl plugin="gerrit-trigger@2.13.0">
<servers class="java.util.concurrent.CopyOnWriteArrayList">
<com.sonyericsson.hudson.plugins.gerrit.trigger.GerritServer>
<name>review.openstack.org</name>
<noConnectionOnStartup>false</noConnectionOnStartup>
<config class="com.sonyericsson.hudson.plugins.gerrit.trigger.config.Config">
<gerritHostName>review.openstack.org</gerritHostName>
<gerritSshPort>29418</gerritSshPort>
<gerritProxy></gerritProxy>
<gerritUserName>catalog-ci</gerritUserName>
<gerritEMail>catalog-ci@mirantis.com</gerritEMail>
<gerritAuthKeyFile>/var/lib/jenkins/.ssh/id_rsa</gerritAuthKeyFile>
<useRestApi>false</useRestApi>
<restCodeReview>false</restCodeReview>
<restVerified>false</restVerified>
<gerritBuildCurrentPatchesOnly>false</gerritBuildCurrentPatchesOnly>
<gerritVerifiedCmdBuildSuccessful>gerrit review &lt;CHANGE&gt;,&lt;PATCHSET&gt; --message &apos;Build Successful &lt;BUILDS_STATS&gt;&apos; --verified &lt;VERIFIED&gt; --code-review &lt;CODE_REVIEW&gt;</gerritVerifiedCmdBuildSuccessful>
<gerritVerifiedCmdBuildUnstable>gerrit review &lt;CHANGE&gt;,&lt;PATCHSET&gt; --message &apos;Build Unstable &lt;BUILDS_STATS&gt;&apos; --verified &lt;VERIFIED&gt; --code-review &lt;CODE_REVIEW&gt;</gerritVerifiedCmdBuildUnstable>
<gerritVerifiedCmdBuildFailed>gerrit review &lt;CHANGE&gt;,&lt;PATCHSET&gt; --message &apos;Build Failed &lt;BUILDS_STATS&gt;&apos; --verified &lt;VERIFIED&gt; --code-review &lt;CODE_REVIEW&gt;</gerritVerifiedCmdBuildFailed>
<gerritVerifiedCmdBuildStarted></gerritVerifiedCmdBuildStarted>
<gerritVerifiedCmdBuildNotBuilt>gerrit review &lt;CHANGE&gt;,&lt;PATCHSET&gt; --message &apos;No Builds Executed &lt;BUILDS_STATS&gt;&apos; --verified &lt;VERIFIED&gt; --code-review &lt;CODE_REVIEW&gt;</gerritVerifiedCmdBuildNotBuilt>
<gerritFrontEndUrl>https://review.openstack.org/</gerritFrontEndUrl>
<gerritBuildStartedVerifiedValue>0</gerritBuildStartedVerifiedValue>
<gerritBuildStartedCodeReviewValue>0</gerritBuildStartedCodeReviewValue>
<gerritBuildSuccessfulVerifiedValue>1</gerritBuildSuccessfulVerifiedValue>
<gerritBuildSuccessfulCodeReviewValue>0</gerritBuildSuccessfulCodeReviewValue>
<gerritBuildFailedVerifiedValue>-1</gerritBuildFailedVerifiedValue>
<gerritBuildFailedCodeReviewValue>0</gerritBuildFailedCodeReviewValue>
<gerritBuildUnstableVerifiedValue>0</gerritBuildUnstableVerifiedValue>
<gerritBuildUnstableCodeReviewValue>0</gerritBuildUnstableCodeReviewValue>
<gerritBuildNotBuiltVerifiedValue>0</gerritBuildNotBuiltVerifiedValue>
<gerritBuildNotBuiltCodeReviewValue>0</gerritBuildNotBuiltCodeReviewValue>
<enableManualTrigger>true</enableManualTrigger>
<enablePluginMessages>true</enablePluginMessages>
<buildScheduleDelay>3</buildScheduleDelay>
<dynamicConfigRefreshInterval>30</dynamicConfigRefreshInterval>
<categories class="linked-list">
<com.sonyericsson.hudson.plugins.gerrit.trigger.VerdictCategory>
<verdictValue>CRVW</verdictValue>
<verdictDescription>Code Review</verdictDescription>
</com.sonyericsson.hudson.plugins.gerrit.trigger.VerdictCategory>
<com.sonyericsson.hudson.plugins.gerrit.trigger.VerdictCategory>
<verdictValue>VRIF</verdictValue>
<verdictDescription>Verified</verdictDescription>
</com.sonyericsson.hudson.plugins.gerrit.trigger.VerdictCategory>
</categories>
<replicationConfig>
<enableReplication>false</enableReplication>
<slaves class="linked-list"/>
<enableSlaveSelectionInJobs>false</enableSlaveSelectionInJobs>
</replicationConfig>
<watchdogTimeoutMinutes>0</watchdogTimeoutMinutes>
<watchTimeExceptionData>
<daysOfWeek/>
<timesOfDay class="linked-list"/>
</watchTimeExceptionData>
<notificationLevel>ALL</notificationLevel>
</config>
</com.sonyericsson.hudson.plugins.gerrit.trigger.GerritServer>
</servers>
<pluginConfig>
<numberOfReceivingWorkerThreads>3</numberOfReceivingWorkerThreads>
<numberOfSendingWorkerThreads>1</numberOfSendingWorkerThreads>
<replicationCacheExpirationInMinutes>360</replicationCacheExpirationInMinutes>
</pluginConfig>
</com.sonyericsson.hudson.plugins.gerrit.trigger.PluginImpl>

View File

@ -1,67 +0,0 @@
- job:
name: check-image
node: master
project-type: freestyle
description: "This job checks glance images"
defaults: global
disabled: false
concurrent: true
scm:
- git:
url: https://git.openstack.org/openstack/app-catalog
refspec: $GERRIT_REFSPEC
name:
choosing-strategy: gerrit
skip-tag: false
wipe-workspace: true
branches:
- "$GERRIT_BRANCH"
triggers:
- gerrit:
server-name: "review.openstack.org"
trigger-on-patchset-uploaded-event: true
projects:
- project-compare-type: 'PLAIN'
project-pattern: 'openstack/app-catalog'
branch-compare-type: 'PLAIN'
branch-pattern: master
builders:
- shell: "git checkout $FETCH_HEAD\
\n$JENKINS_HOME/scripts/app-catalog.sh"
- job:
name: merge-image
node: master
project-type: freestyle
description: "This job checks and uploads glance images to CDN"
defaults: global
disabled: false
concurrent: true
scm:
- git:
url: https://git.openstack.org/openstack/app-catalog
refspec: $GERRIT_REFSPEC
name:
choosing-strategy: gerrit
skip-tag: false
wipe-workspace: true
branches:
- "$GERRIT_BRANCH"
triggers:
- gerrit:
server-name: "review.openstack.org"
trigger-on-change-merged-event: true
projects:
- project-compare-type: 'PLAIN'
project-pattern: 'openstack/app-catalog'
branch-compare-type: 'PLAIN'
branch-pattern: master
builders:
- shell: "git checkout $FETCH_HEAD\
\n$JENKINS_HOME/scripts/app-catalog.sh"

View File

@ -1,96 +0,0 @@
#!/bin/bash -x
WDIR=$JENKINS_HOME/scripts
PATCH=$GERRIT_CHANGE_NUMBER
CODES=(100 200 302)
TMP_DIR=$(mktemp -d)
TMP_FILE=$(mktemp)
EVENT=$GERRIT_EVENT_TYPE
FILE_LIST=$(git diff HEAD~1 --name-only)
IMAGE_CONFIG="openstack_catalog/web/static/glance_images.yaml"
IMAGE_CDN_PATH="catalog_ci:catalog/images"
clean() {
rm -rf $TMP_DIR
rm -f $TMP_FILE
}
upload_image () {
local CONFIG
local OLD_CONFIG
local IMAGE_NAME
local IMAGE_PATH
IMAGE_PATH=$1
CONFIG=$TMP_DIR/$(basename $IMAGE_CONFIG)
OLD_CONFIG=$TMP_DIR/$(basename $IMAGE_CONFIG).old
IMAGE_NAME=$(python $WDIR/generate_names.py glance $OLD_CONFIG $CONFIG)
if [ -z "$IMAGE_NAME" ]; then
echo "Image file can't be generated"
exit 1
fi
echo "Uploading image $IMAGE_NAME from $IMAGE_PATH"
mv $IMAGE_PATH $(dirname $IMAGE_PATH)/$IMAGE_NAME
rclone copy $(dirname $IMAGE_PATH)/$IMAGE_NAME $IMAGE_CDN_PATH
clean
}
main() {
local URL
local HASH
local REAL_HASH
local HTTP_CODE
local IMAGE
ssh -p 29418 catalog-ci@review.openstack.org gerrit query $PATCH > $TMP_FILE
URL=$(cat $TMP_FILE | egrep "^\s*Image-URL:\s(https?|ftp)://.*" | egrep -o "(https?|ftp)://.*$")
HASH=$(cat $TMP_FILE | egrep "^\s*Image-hash:\s[A-Za-z0-9]*$" | egrep -o "[A-Za-z0-9]*$")
cat $TMP_FILE | grep Image-URL | grep -q Unknown && exit 0
# if [ $(cat $TMP_FILE | grep Image-URL | grep Unknown) ]; then
# echo "Image URL is unknown, no checks and changes will be performed"
# exit 0
# fi
if [ -z "$URL" -o -z "$HASH" ]; then
echo "Image URL or hash wasn't found"
clean
exit 0
else
HTTP_CODE=$(curl -o /dev/null --silent --head --write-out '%{http_code}\n' $URL)
if ! [[ " ${CODES[*]} " == *" $HTTP_CODE "* ]]; then
echo "File wasn't found"
clean
exit 1
fi
fi
wget $URL -P $TMP_DIR
if [ "$HASH" == "Unknown" ]; then
echo "Image hash is unknown, skipping checks..."
else
REAL_HASH=$(md5sum $TMP_DIR/* | awk '{print $1}')
if [ "$REAL_HASH" != "$HASH" ]; then
echo "Hash mismatch"
clean
exit 1
else
echo "Image hash is correct"
fi
fi
if [ "$EVENT" == "change-merged" ]; then
IMAGE=$(ls $TMP_DIR/*)
cp $IMAGE_CONFIG $TMP_DIR
git checkout HEAD~1
cp $IMAGE_CONFIG $TMP_DIR/$(basename $IMAGE_CONFIG).old
upload_image $IMAGE
else
clean
fi
}
if [[ ${FILE_LIST[*]} =~ "$IMAGE_CONFIG" ]]; then
main "$@"
fi

View File

@ -1,50 +0,0 @@
#!/usr/bin/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 itertools
import re
from sys import argv
import yaml
def yaml_to_dict(infile, k):
stream = open(infile, 'r')
rdict = yaml.safe_load(stream)[k]
return rdict
def diff_images_config(images1, images2):
if images1 == images2:
return ''
intersec = [item for item in images1 if item in images2]
sym_diff = [item for item in itertools.chain(
images1, images2) if item not in intersec]
name = ''
d_size = len(sym_diff)
if d_size <= 2:
i = d_size - 1
else:
return ''
if 'name' in sym_diff[i].keys() and 'format' in sym_diff[i].keys():
i_name = re.sub('[(){}<>]', '', sym_diff[i]['name'])
i_type = sym_diff[i]['format']
name = i_name + '.' + i_type
name = name.lower().replace(" ", "_")
return name
if __name__ == '__main__':
if argv[1] == 'glance':
images1 = yaml_to_dict(argv[2], 'images')
images2 = yaml_to_dict(argv[3], 'images')
print(diff_images_config(images1, images2))

View File

@ -1,123 +0,0 @@
class catalog_ci {
$user = 'admin'
$password = 'adminpassword'
$jenkins_config = '/var/lib/jenkins/config.xml'
$gerrit_config = '/var/lib/jenkins/gerrit-trigger.xml'
class{ 'jenkins':
lts => true,
install_java => true,
plugin_hash => {
'git' => {},
'parameterized-trigger' => {},
'token-macro' => {},
'mailer' => {},
'scm-api' => {},
'promoted-builds' => {},
'matrix-project' => {},
'git-client' => {},
'ssh-credentials' => {},
'credentials' => {},
'gerrit-trigger' => {},
'rebuild' => {},
'git-client' => {},
'rabbitmq-consumer' => {},
'openid' => {},
'openid4java' => {},
}
}
jenkins::user { "$user":
email => 'admin@example.com',
password => "$password",
}
class{ 'jenkins::security':
security_model => 'full_control',
}
Class['jenkins'] -> Jenkins::User["$user"] -> Class['jenkins::security']
-> Exec['create_jobs'] -> File["$jenkins_config"]
package {'python-pip':
ensure => present,
}
package {'deepdiff':
ensure => present,
provider => 'pip',
}
package {'pyyaml':
ensure => present,
provider => 'pip',
}
package {'jenkins-job-builder':
ensure => present,
provider => 'pip',
}
file {'/var/lib/jenkins/scripts':
owner => 'root',
group => 'root',
ensure => directory,
source => 'puppet:///modules/catalog_ci/scripts',
recurse => true,
require => File['/etc/jenkins_jobs'],
}
file {'/etc/jenkins_jobs':
owner => 'root',
group => 'root',
ensure => directory,
require => Package['jenkins-job-builder'],
}
file {'/etc/jenkins_jobs/jenkins_jobs.ini':
owner => 'root',
group => 'root',
content => template('catalog_ci/jenkins_jobs.ini.erb'),
require => File['/etc/jenkins_jobs'],
}
file {'/etc/jenkins_jobs/jobs':
owner => 'root',
group => 'root',
ensure => directory,
source => 'puppet:///modules/catalog_ci/jobs',
recurse => true,
require => File['/etc/jenkins_jobs'],
}
file {"$jenkins_config":
owner => 'jenkins',
group => 'jenkins',
ensure => present,
source => 'puppet:///modules/catalog_ci/config.xml',
}
file {"$gerrit_config":
owner => 'jenkins',
group => 'jenkins',
ensure => present,
source => 'puppet:///modules/catalog_ci/gerrit-trigger.xml',
require => File["$jenkins_config"],
}
exec {'create_jobs':
command => 'jenkins-jobs update /etc/jenkins_jobs/jobs',
path => '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin',
require => File['/etc/jenkins_jobs/jobs'],
}
exec{'restart_jenkins':
command => 'service jenkins restart',
path => '/bin:/sbin:/usr/bin:/usr/sbin',
subscribe => [ File["$jenkins_config"], File["$gerrit_config"] ],
}
Package['python-pip'] -> Package['deepdiff'] ->
Package['pyyaml'] -> Package['jenkins-job-builder']
}

View File

@ -1,8 +0,0 @@
[job_builder]
ignore_cache=True
[jenkins]
user=<%= @user %>
password=<%= @password %>
url=http://127.0.0.1:8080
query_plugins_info=False

View File

@ -1,19 +0,0 @@
#!/bin/bash -e
# Generate a password for service account
ADMIN_PASS=$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c10)
sed -i "s/adminpassword/$ADMIN_PASS/g" catalog-ci-jenkins/modules/catalog_ci/manifests/init.pp
sudo apt-get update
sudo apt-get install git puppet -y
# Using custom (forked) puppet-jenkins module
# due to critical problem in the upstream one
git clone https://github.com/skolekonov/puppet-jenkins.git
tar czf rtyler-jenkins-1.3.0.tar.gz puppet-jenkins/*
sudo puppet module install rtyler-jenkins-1.3.0.tar.gz
sudo puppet apply -vd --modulepath catalog-ci-jenkins/modules:/etc/puppet/modules catalog-ci-jenkins/manifests/site.pp
echo "Deployment completed"
echo "WARNING. Please open Jenkins WebUI and setup user access matrix"

View File

@ -1,75 +0,0 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import sys
sys.path.insert(0, os.path.abspath('../..'))
# -- General configuration ----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
'sphinx.ext.autodoc',
#'sphinx.ext.intersphinx',
'oslosphinx'
]
# autodoc generation is a bit aggressive and a nuisance when doing heavy
# text edit cycles.
# execute "export SPHINX_DEBUG=1" in your terminal to disable
# The suffix of source filenames.
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'catalog'
copyright = u'2013, OpenStack Foundation'
# If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
add_module_names = True
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# -- Options for HTML output --------------------------------------------------
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
# html_theme_path = ["."]
# html_theme = '_theme'
# html_static_path = ['static']
# Output file base name for HTML help builder.
htmlhelp_basename = '%sdoc' % project
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass
# [howto/manual]).
latex_documents = [
('index',
'%s.tex' % project,
u'%s Documentation' % project,
u'OpenStack Foundation', 'manual'),
]
# Example configuration for intersphinx: refer to the Python standard library.
#intersphinx_mapping = {'http://docs.python.org/': None}

View File

@ -1,4 +0,0 @@
============
Contributing
============
.. include:: ../../CONTRIBUTING.rst

View File

@ -1,24 +0,0 @@
.. catalog documentation master file, created by
sphinx-quickstart on Tue Jul 9 22:26:36 2013.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to catalog's documentation!
========================================================
Contents:
.. toctree::
:maxdepth: 2
readme
installation
usage
contributing
Indices and tables
==================
* :ref:`genindex`
* :ref:`search`

View File

@ -1,12 +0,0 @@
============
Installation
============
At the command line::
$ pip install catalog
Or, if you have virtualenvwrapper installed::
$ mkvirtualenv catalog
$ pip install catalog

View File

@ -1 +0,0 @@
.. include:: ../../README.rst

View File

@ -1,7 +0,0 @@
========
Usage
========
To use catalog in a project::
import openstack_catalog

View File

@ -1,25 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE",
"openstack_catalog.settings")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)

View File

@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import pbr.version
__version__ = pbr.version.VersionInfo(
'openstack_catalog').version_string()

View File

@ -1,39 +0,0 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from flask import Flask
from flask import Response
api = Flask('api')
api.debug = True
def cors_allow(resp):
h = ['Origin',
'Accept-Encoding',
'Content-Type',
'X-App-Catalog-Versions']
resp.headers['Access-Control-Allow-Origin'] = '*'
resp.headers['Access-Control-Allow-Headers'] = ', '.join(h)
@api.route('/')
def index():
data = "v1\n"
resp = Response(data, status=200, mimetype='plain/text')
cors_allow(resp)
return resp
# Pull in v1 api into the server.
from openstack_catalog.api.v1 import * # noqa

View File

@ -1,52 +0,0 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from flask import request
from flask import Response
from openstack_catalog.api import api
from openstack_catalog.api import cors_allow
from openstack_catalog import settings
@api.route('/v1')
def v1_index():
data = "assets\nmurano_repo\n"
resp = Response(data, status=200, mimetype='plain/text')
cors_allow(resp)
return resp
@api.route('/v1/assets', methods=['GET', 'OPTIONS'])
def assets_index():
if request.method == 'OPTIONS':
data = ''
else:
f = open(settings.ASSETS_FILE, 'r')
data = f.read()
f.close()
resp = Response(data, status=200, mimetype='application/json')
resp.headers['Access-Control-Max-Age'] = '3600'
resp.headers['Cache-Control'] = 'max-age=3600'
cors_allow(resp)
return resp
@api.route('/v1/murano_repo/<release>/<path:path>')
def murano_repo_index(release, path):
resp = Response('', status=302)
resp.headers['Location'] = \
"http://storage.apps.openstack.org/{}".format(path)
return resp

View File

@ -1,107 +0,0 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Django settings for openstack_catalog project.
For more information on this file, see
https://docs.djangoproject.com/en/1.6/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.6/ref/settings/
"""
import os.path
from openstack_catalog.static_settings import get_staticfiles_dirs
ASSETS_FILE = 'openstack_catalog/web/api/v1/assets'
STATIC_URL = '/static/'
STATIC_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__),
'web',
'static'))
STATICFILES_DIRS = get_staticfiles_dirs(STATIC_URL)
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
SECRET_KEY = 'notused'
DEBUG = True
ALLOWED_HOSTS = []
PROJECT_PATH = os.path.realpath(os.path.dirname(__file__))
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
PROJECT_PATH + '/templates/'
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.contrib.auth.context_processors.auth',
'django.template.context_processors.debug',
'django.template.context_processors.i18n',
'django.template.context_processors.media',
'django.template.context_processors.static',
'django.template.context_processors.tz',
'django.contrib.messages.context_processors.messages',
],
'debug': True
},
},
]
COMPRESS_ENABLED = False
COMPRESS_CSS_HASHING_METHOD = 'hash'
COMPRESS_PARSER = 'compressor.parser.HtmlParser'
COMPRESS_OFFLINE_CONTEXT = {
'STATIC_URL': STATIC_URL,
}
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
'compressor.finders.CompressorFinder',
)
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.staticfiles',
'compressor',
)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
)
ROOT_URLCONF = 'openstack_catalog.urls'
WSGI_APPLICATION = 'openstack_catalog.wsgi.application'
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Override some values from local_settings.py if found
try:
from openstack_catalog.local_settings import * # noqa
except ImportError:
pass
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.6/howto/static-files/

View File

@ -1,28 +0,0 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import xstatic.main
import xstatic.pkg.angular
import xstatic.pkg.magic_search
def get_staticfiles_dirs(webroot='/'):
return [
('lib/angular',
xstatic.main.XStatic(xstatic.pkg.angular,
root_url=webroot).base_dir),
('lib/magic_search',
xstatic.main.XStatic(xstatic.pkg.magic_search,
root_url=webroot).base_dir),
]

View File

@ -1,6 +0,0 @@
{% load compress %}
{% compress js %}
<script src="{{ STATIC_URL }}lib/angular/angular.js" type='text/javascript' charset="utf-8"></script>
<script src="{{ STATIC_URL }}lib/magic_search/magic_search.js" type='text/javascript' charset="utf-8"></script>
{% endcompress %}

View File

@ -1,523 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<meta name="description" content="OpenStack market place" />
<meta name="keywords" content="openstack, glance images, heat templates, murano applications, tosca templates, tosca csar" />
<meta name="author" content="Mirantis on behalf of OpenStack Foundation" />
<title>Community App Catalog</title>
<link href="/static/images/favicon.ico" rel="shortcut icon" type="image/x-icon" />
{% include "_scripts.html" %}
{% verbatim %}
<!-- Bootstrap core CSS -->
<link href="//cdn.jsdelivr.net/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<!-- Custom styles for this template -->
<link href="static/css/theme.css" rel="stylesheet" type="text/css" />
<link href="//cdn.jsdelivr.net/fontawesome/4.3.0/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
<link href="//fonts.googleapis.com/css?family=Open+Sans:400italic,400,700" rel="stylesheet" type="text/css" />
<link rel="stylesheet" type="text/css" href="//code.jquery.com/ui/1.11.4/themes/cupertino/jquery-ui.css" />
<link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/jquery.datatables/1.10.4/css/jquery.dataTables.min.css" />
<link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/select2/3.5.2/select2.css" />
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="//oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="//oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body role="document">
<!-- Google Tag Manager -->
<noscript><iframe src="//www.googletagmanager.com/ns.html?id=GTM-K2J65K"
height="0" width="0"
style="display:none;visibility:hidden"></iframe></noscript>
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var
f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-K2J65K');</script>
<!-- End Google Tag Manager -->
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-17511903-16', {'allowAnchor': true});
ga('send', 'pageview', { 'page': location.pathname + location.search + location.hash});
</script>
<!-- Fixed navbar -->
<div class="masthead navbar-fixed-top">
<div class="container">
<a class="navbar-brand" href="https://www.openstack.org" target="_blank"></a>
<h1><a href="#">Community App Catalog <sup><small>[beta]</small></sup></a></h1>
<a href="https://wiki.openstack.org/wiki/App-Catalog#FAQ" target="_blank" class="btn btn-default pull-right">FAQ</a>
</div>
<nav class="navbar navbar-inverse">
<div class="container">
<div class="navbar-collapse collapse">
<ul id="navbar" class="nav navbar-nav">
<li><a href="#murano-apps">Murano packages</a></li>
<li><a href="#heat-templates">Heat Templates</a></li>
<li><a href="#glance-images">Glance Images</a></li>
<li><a href="#tosca-templates">TOSCA Templates</a></li>
<li><a href="#addContent">Add New Content</a></li>
</ul>
</div>
</div>
</nav>
</div>
<div class="container" role="main">
<!-- Main jumbotron for a primary marketing message or call to
action -->
<div id="landing-page" class="content" style="display:none">
<h1>The OpenStack Application Catalog will help you make applications available on your cloud. </h1>
<div class="row featured">
<div class="col-md-2 col-sm-6">
<div class="inner title">
<div>Recently Added Apps</div>
<p>(Requires OpenStack)</p>
</div>
</div>
</div>
<div class="row pluginbox">
<div class="col-sm-2">
<a href="https://wiki.openstack.org/wiki/App-Catalog#Horizon_Plugin_for_Native_Access" target="_blank">
<img src="static/images/plugin-icon.png">
<div class='lbl'>Horizon Plugin</div>
</a>
</div><div class="col-sm-10">
<p><strong>Want this catalog included in your OpenStack environment?</strong></p>
<p>There is a Horizon plugin available at <a href="https://git.openstack.org/cgit/openstack/app-catalog-ui" target="_blank">https://git.openstack.org/cgit/openstack/app-catalog-ui</a> which gives users the ability to search and retrieve App Catalog contents directly from Horizon &mdash; see <a href="https://wiki.openstack.org/wiki/App-Catalog#Horizon_Plugin_for_Native_Access" target="_blank">https://wiki.openstack.org/wiki/App-Catalog#Horizon_Plugin_for_Native_Access</a> for more details. Add it to your environment today!</p>
</div>
</div>
<p><strong>Don't have OpenStack yet?</strong></p>
<p>Visit the <a href="https://www.openstack.org/marketplace/" target="_blank">OpenStack Marketplace</a> and select tools and methods for building your cloud. Then come back to the OpenStack Application Catalog to make your cloud sing with applications.</p>
<div class="row">
<div class="col-md-4 bluebox">
<div class="inner">
<a href="#tab=murano-apps">
<h2>Murano Packages</h2>
<img src="static/images/murano-diagram.png">
</a>
<p>
In Murano Packages you will find complete
applications, in both simple and clustered configurations, ready to deploy
on your cloud. To use them:
</p>
<ol>
<li>Verify that you have <A href="http://docs.openstack.org/developer/murano/" target="_blank">Murano</a> installed</li>
<li>Browse the list and find the app package you want</li>
<li>Highlight and copy the name of the app package</li>
<li>In Horizon, navigate to Murano &gt; Manage &gt; Package Definitions and click Import Package</li>
<li>Paste the app package name into the labeled text field, and click Next. This will download and install the package components.</li>
<li>Now deploy the app on your cloud by navigating to Application Catalog &gt; Environments, then creating and deploying that environment.</li>
</ol>
</div>
</div>
<div class="col-md-4 bluebox">
<div class="inner">
<a href="#tab=heat-templates">
<h2>Heat Templates</h2>
<img src="static/images/heat-diagram.png"
class="text-center">
</a>
<p>
In Heat Templates you will find templates for creating
complete stacks in your cloud. To use them:
</p>
<ol>
<li>Browse the list and find the one you want</li>
<li>Download the template yaml (.yml) file to your local machine</li>
<li>Use Horizon to add the template to Heat (by uploading it via file selector, or copy/pasting it into the editable field)</li>
<li>Use the <a href="http://docs.openstack.org/cli-reference/heat.html" target="_blank">Heat client</a> to create a stack</li>
</ol>
</div>
</div>
<div class="col-md-4 bluebox">
<div class="inner">
<a href="#tab=glance-images">
<h2>Glance Images</h2>
<img src="static/images/glance-diagram.png">
</a>
<br/><br/>
<p>
In Glance Images you will find a library of
preconfigured images ready to launch virtual machines
on your cloud. To use them:
</p>
<ol>
<li>Browse the list and find the one you want</li>
<li>Copy the URL for the image file</li>
<li>Use the <a href="http://docs.openstack.org/cli-reference/glance.html" target="_blank">Glance client</a> to add it to your cloud with the --copy-from option, or add the image from Horizon by creating a new image, and specifying the URL as the image location.</li>
<li>You can use Nova to launch a VM from the new image; or, you can choose an image to launch from the Glance images list shown in Horizon. Of course, if a service is preconfigured to launch VMs by itself directly from specific guest images, you're good to go.
</li>
</ol>
</div>
</div>
<div class="col-md-4 bluebox">
<div class="inner">
<a href="#tab=tosca-templates">
<h2>TOSCA Templates</h2>
<img src="static/images/tosca-diagram.png"
class="text-center">
</a>
<p>
In TOSCA Templates you will find templates and Cloud Service Archives (CSAR). To use them:
</p>
<ol>
<li>Browse the list and find the one you want</li>
<li>Download the raw template file or CSAR to your local machine</li>
<li>Use the template or CSAR per your need</li>
<li>To deploy template with OpenStack Heat, provide the template or CSAR to the OpenStack <a href="http://docs.openstack.org/developer/heat-translator/usage.html" target="_blank">Heat-Translator</a> which will produce a Heat Orchestration Template (HOT) as an output.</li>
<li>To simply parse the template, use it with OpenStack <a href="http://docs.openstack.org/developer/tosca-parser/usage.html" target="_blank">TOSCA-Parser</a> project.</li>
</ol>
</div>
</div>
</div>
<div class="row">
<p><strong>Contributing to the OpenStack Application Catalog</strong></p>
<p>
The application catalog is built from the YAML files found in the
<a href="https://git.openstack.org/cgit/openstack/app-catalog" target="_blank">repository</a>.
Consistent with other OpenStack projects, additions to the
catalog take the form of git commits which are reviewed with Gerrit
prior to being merged. Detailed instructions for adding to
the YAML files can be found on the
<a href="https://wiki.openstack.org/wiki/App-Catalog" target="_blank">
OpenStack wiki</a>.
</p>
<p><strong>Catalogue Disclaimer</strong></p>
<p>Please keep in mind that this catalog contains content from many sources,
contributed by the community, for the community. The OpenStack Foundation is not endorsing any particular item listed. Read the <a href="https://wiki.openstack.org/wiki/App-Catalog/Catalogue_Disclaimer" target="_blank">Catalog Disclaimer</a> for additional information.</p>
</div>
</div>
<div id="glance-images" class="content" style="display:none">
<div class="row">
<div class="col-md-6 filters">
&nbsp;
</div>
</div>
<div class="row">
<div class="col-md-12">
<table id="glance-images-table" class="table table-striped">
<thead>
<tr>
<th>Image&nbsp;Name</th>
<th>Description</th>
<th>Format</th>
<th>License</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
<div id="heat-templates" class="content" style="display:none">
<div class="row">
<div class="col-md-6 filters">
<form class="form-inline">
<div class="form-group">
<label>Release</label>
<input type="hidden" id="heat-release"
style="width:10em" data-placeholder="Any release"/>
</div>
</form>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table id="heat-templates-table" class="table table-striped">
<thead>
<tr>
<th>Template&nbsp;Name</th>
<th>Description</th>
<th>Release</th>
<th>Format</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
<div id="murano-apps" class="content" style="display:none">
<div class="row">
<div class="col-md-6 filters">
<form class="form-inline">
<div class="form-group">
<label>Release</label>
<input type="hidden" id="murano-release"
style="width:10em" data-placeholder="Any release"/>
</div>
</form>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table id="murano-apps-table" class="table table-striped">
<thead>
<tr>
<th>Package&nbsp;Name</th>
<th>Description</th>
<th>Release</th>
<th>Format</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
<div id="tosca-templates" class="content" style="display:none">
<div class="row">
<div class="col-md-6 filters">
<form class="form-inline">
<div class="form-group">
<label>Release</label>
<input type="hidden" id="tosca-release"
style="width:10em" data-placeholder="Any release"/>
</div>
</form>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table id="tosca-templates-table" class="table table-striped">
<thead>
<tr>
<th>Template&nbsp;Name</th>
<th>Description</th>
<th>Release</th>
<th>Format</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
<div id="info-page" class="content">
<div class="row">
<div class="col-md-12">
<div id="info-content"></div>
</div>
</div>
</div>
<div id="info-dialog" title="Details">
<div id="info-container"></div>
</div>
</div>
<!-- core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script type="text/javascript" src="//cdn.jsdelivr.net/jquery/2.1.3/jquery.min.js"></script>
<script type="text/javascript" src="//code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script>
<script type="text/javascript" src="//cdn.jsdelivr.net/jquery.datatables/1.10.4/js/jquery.dataTables.min.js"></script>
<script type="text/javascript" src="//cdn.jsdelivr.net/select2/3.5.2/select2.min.js"></script>
<script type="text/javascript" src="//ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
<script type="text/javascript" src="//cdn.jsdelivr.net/jquery.timeago/1.4.1/jquery.timeago.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery.matchHeight/0.6.0/jquery.matchHeight-min.js"></script>
<script type="text/javascript" src="static/js/apps-catalog.js"></script>
<script type="text/javascript">
$(function() { initMarketPlace (); });
$(document).ready(function () { ; } );
</script>
<script id="glance-images-info" type="text/x-jquery-tmpl">
<h2>${name} (${service['disk_format']})</h2>
<div>
<span class="label">Last modified: </span><span class="value">${last_modified}</span>
</div>
<div>
{{if attributes['url']}}
<span class="label">glance image-create:</span>
<input type="text" value="glance --os-image-api-version 1 image-create --copy-from ${attributes['url']} --disk-format ${service['disk_format']} --container-format ${service['container_format']} --name '${name}'" onfocus="this.select();" onmouseup="return false;">
{{else}}
<span class="label">Website where the image can be downloaded:</span>
<a href="${attributes['indirect_url']}">Link...</a>
{{/if}}
</div>
{{if supported_by}}
<div><span class="label">Supported by</span> <span class="value">${supported_by.name}</span></div>
{{/if}}
{{if license}}
{{if license_url}}
<div><span class="label">License</span> <a href="${license_url}"> <span class="value">${license}</span></a></div>
{{else}}
<div><span class="label">License</span> <span class="value">${license}</span></div>
{{/if}}
{{/if}}
{{if cloud_user}}
<div><span class="label">Cloud User</span> <span class="value">${cloud_user}</span></div>
{{/if}}
<div><span class="label">Description:</span> <span class="value">${description}</span></div>
{{if hash }}
<div><span class="label">Hash:</span> <span class="value">${hash}</span></div>
{{/if}}
<h3>Author</h3>
<div><span class="label">Contact:
</span><a href="${provided_by.href}"> <span class="value">${provided_by.name}</span> </a></div>
<div><span class="label">Company:</span> <span class="value">${provided_by.company}</span></div>
{{if attributes }}
<h3>Attributes</h3>
{{each( key, value ) attributes }}
<div><span class="label">${key}:</span> <span class="value">${value}</span></div>
{{/each}}
{{/if}}
</script>
<script id="heat-templates-info" type="text/x-jquery-tmpl">
<h2>${name} (${service['format']})</h2>
<div>
<span class="label">Last modified: </span><span class="value">${last_modified}</span>
</div>
{{if supported_by}}
<div><span class="label">Supported by</span> <span class="value">${supported_by.name}</span></div>
{{/if}}
<div><span class="label">Description:</span> <span class="value">${description}</span></div>
<div><span class="label">Available in Releases:</span> <span class="value">${release_html}</span></div>
<h3>Author</h3>
<div><span class="label">Contact:
</span><a href="${provided_by.href}"> <span class="value">${provided_by.name}</span> </a></div>
<div><span class="label">Company:</span> <span class="value">${provided_by.company}</span></div>
{{if attributes }}
<h3>Attributes</h3>
{{each( key, value ) attributes }}
<div><span class="label">${key}:</span> <span class="value">${value}</span></div>
{{/each}}
{{/if}}
</script>
<script id="murano-apps-info" type="text/x-jquery-tmpl">
<h2>${name} ({{if service.type == "bundle"}}bundle{{else}}${service.format}{{/if}})</h2>
<div>
<span class="label">Last modified: </span><span class="value">${last_modified}</span>
</div>
<div>
<span class="label">{{if service.format == 'package'}}Package{{else}}Bundle{{/if}} name:</span>
{{if service.type == 'bundle'}}
<input type="text" value="${service.murano_package_name}" onfocus="this.select();" onmouseup="return false;">
{{else}}
<input type="text" value="${service.package_name}" onfocus="this.select();" onmouseup="return false;">
{{/if}}
</div>
<h3>Description</h3>
<div><span class="value">${description}</span></div>
<h3>Supported OpenStack Releases</h3>
<div><span class="value">${release_html}</span></div>
<h3>Author</h3>
<div><span class="label">Contact: </span><a href="${provided_by.href}"> <span class="value">${provided_by.name}</span> </a></div>
<div><span class="label">Company:</span> <span class="value">${provided_by.company}</span></div>
{{if depends }}
<h3>Depends On</h3>
<p>Package may depend on image and on other packages, during import of
this package to Murano following dependencies will be also downloaded
and imported, but existing entries will not be overwritten.</p>
{{if depends }}
Dependencies:
<ul>
{{each depends }}
<li>${depends[$index]['name']}</li>
{{/each}}
</ul>
{{/if}}
{{/if}}
{{if attributes }}
<h3>Attributes</h3>
{{each( key, value ) attributes }}
<div><span class="label">${key}:</span> <span class="value">${value}</span></div>
{{/each}}
{{/if}}
</script>
<script id="tosca-templates-info" type="text/x-jquery-tmpl">
<h2>${name} (${service['template_format']})</h2>
<div>
<span class="label">Last modified: </span><span class="value">${last_modified}</span>
</div>
{{if supported_by}}
<div><span class="label">Supported by</span> <span class="value">${supported_by.name}</span></div>
{{/if}}
<div><span class="label">Description:</span> <span class="value">${description}</span></div>
<div><span class="label">Available in Releases:</span> <span class="value">${release_html}</span></div>
<h3>Author</h3>
<div><span class="label">Contact:
</span><a href="${provided_by.href}"> <span class="value">${provided_by.name}</span> </a></div>
<div><span class="label">Company:</span> <span class="value">${provided_by.company}</span></div>
{{if attributes }}
<h3>Attributes</h3>
{{each( key, value ) attributes }}
<div><span class="label">${key}:</span> <span class="value">${value}</span></div>
{{/each}}
{{/if}}
</script>
</body>
</html>
{% endverbatim %}

View File

@ -1,61 +0,0 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
test_openstack_catalog
----------------------------------
Tests for `openstack_catalog` module.
"""
import functools
import jsonschema
import six
import testtools
import yaml
class TestOpenstack_catalog(testtools.TestCase):
def setUp(self):
super(TestOpenstack_catalog, self).setUp()
def _read_raw_file(self, file_name):
if six.PY3:
opener = functools.partial(open, encoding='utf8')
else:
opener = open
with opener(file_name, 'r') as content_file:
return content_file.read()
def _read_file(self, file_name):
return yaml.safe_load(self._read_raw_file(file_name))
def _verify_by_schema(self, file_name, schema):
data = self._read_file(file_name)
schema = self._read_file(schema)
names = {}
for asset in data['assets']:
name = asset['name']
if name in names:
self.fail("Duplicate asset name: %s" % name)
names[name] = True
try:
jsonschema.validate(data, schema)
except jsonschema.ValidationError as e:
self.fail(e)
def test_asset_schema_conformance(self):
self._verify_by_schema(
'openstack_catalog/web/static/assets.yaml',
'openstack_catalog/web/static/assets.schema.yaml')

View File

@ -1,8 +0,0 @@
from django.conf.urls import url
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from openstack_catalog import views
urlpatterns = [url(r'^$', views.index)]
urlpatterns += staticfiles_urlpatterns()

View File

@ -1,5 +0,0 @@
from django.shortcuts import render
def index(request):
return render(request, 'index.html', {})

View File

@ -1,274 +0,0 @@
---
$schema: http://json-schema.org/draft-04/schema#
type: object
required:
- assets
properties:
assets:
type: array
items:
type: object
required:
- service
- name
- provided_by
- description
- license
additionalProperties: false
properties:
active:
type: boolean
hash_url:
type: string
hash:
type: string
service:
oneOf:
- $ref: "#/definitions/glance"
- $ref: "#/definitions/heat"
- $ref: "#/definitions/murano"
- $ref: "#/definitions/bundle"
- $ref: "#/definitions/tosca"
name:
type: string
tags:
type: array
items:
enum:
- app
depends:
type: array
items:
type: object
required:
- name
additionalProperties: false
properties:
name:
type: string
release:
type: array
items:
enum:
- Austin
- Bexar
- Cactus
- Diablo
- Essex
- Folsom
- Grizzly
- Havana
- Icehouse
- Juno
- Kilo
- Liberty
- Mitaka
- Newton
- Ocata
- Pike
minItems: 1
provided_by:
type: object
required:
- name
- href
- company
additionalProperties: false
properties:
name:
type: string
href:
type: string
pattern: "^((https?://)[a-z][a-z0-9_-]+(\\.[a-z][a-z0-9_-]+)+(/[a-z0-9\\._/~%\\-\\+&\\#\\?!=\\(\\)@]*)?)|(mailto:[a-z][a-z0-9._-]+@[a-z][a-z0-9_-]+(\\.[a-z][a-z0-9_-]+)+)$"
company:
type: string
supported_by:
type: object
required:
- name
additionalProperties: false
properties:
name:
type: string
href:
type: string
pattern: "^((https?://)[a-z][a-z0-9_-]+(\\.[a-z][a-z0-9_-]+)+(/[a-z0-9\\._/~%\\-\\+&\\#\\?!=\\(\\)@]*)?)|(mailto:[a-z][a-z0-9._-]+@[a-z][a-z0-9_-]+(\\.[a-z][a-z0-9_-]+)+)$"
company:
type: string
icon:
type: object
required:
- top
- left
- height
- url
additionalProperties: false
properties:
top:
type: number
left:
type: number
height:
type: number
url:
type: string
pattern: "^((https?://)[a-z][a-z0-9_-]*(\\.[a-z][a-z0-9_-]*)+(/[a-z0-9\\._/~%\\-\\+&\\#\\?!=\\(\\)@]*)?)"
description:
type: string
license:
type: string
pattern: "^(GPL .*)|(Apache .*)|(BSD .*)|(MIT)|(Free <= [0-9]+ (Users|Nodes))|(Multi-licensed OpenSource)|(Other)|(Unknown)$"
license_url:
type: string
pattern: "^((https?://)[a-z][a-z0-9_-]+(\\.[a-z][a-z0-9_-]+)+(/[a-z0-9\\._/~%\\-\\+&\\#\\?!=\\(\\)@]*)?)$"
cloud_user:
type: string
attributes:
type: object
additionalProperties: true
patternProperties:
.*:
type:
- string
- number
definitions:
glance:
required:
- type
- container_format
- disk_format
properties:
type:
type: string
enum:
- glance
container_format:
enum:
- ami
- ari
- aki
- bare
- ovf
disk_format:
enum:
- ami
- ari
- aki
- vhd
- vmdk
- raw
- qcow2
- vdi
- iso
min_ram:
type: number
min_disk:
type: number
additionalProperties: false
heat:
required:
- type
properties:
type:
type: string
enum:
- heat
environment:
type: object
additionalProperties: true
ever:
type: array
items:
type: object
additionalProperties: false
properties:
min:
type: array
items:
type: number
minItems: 3
maxItems: 3
max:
type: array
items:
type: number
minItems: 3
maxItems: 3
murano:
required:
- type
additionalProperties: false
properties:
type:
type: string
enum:
- murano
format:
type: string
enum:
- package
package_name:
type: string
ever:
type: array
items:
type: object
additionalProperties: false
properties:
min:
type: array
items:
type: number
minItems: 3
maxItems: 3
max:
type: array
items:
type: number
minItems: 3
maxItems: 3
bundle:
required:
- type
additionalProperties: false
properties:
type:
type: string
enum:
- bundle
murano_package_name:
type: string
tosca:
required:
- type
- template_format
additionalProperties: false
properties:
type:
type: string
enum:
- tosca
template_format:
type: string
enum:
- yaml
- csar
ever:
type: array
items:
type: object
additionalProperties: false
properties:
min:
type: array
items:
type: number
minItems: 3
maxItems: 3
max:
type: array
items:
type: number
minItems: 3
maxItems: 3

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +0,0 @@
assets:
stackato-3.4.2-murano-agent: {active: false}

View File

@ -1,225 +0,0 @@
h1, h2, h3, h4, h5, h6 {
font-family: "Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
}
body {
padding-top: 133px;
padding-bottom: 30px;
}
a {
color: #30739c;
}
.masthead {
background: #fff;
}
.masthead h1 {
display: inline-block;
border-left: 1px solid #ccc;
padding-left: 20px;
margin-left: 20px;
height: 33px;
line-height: 33px;
font-size: 21px;
color: #264d69;
}
.navbar {
margin: 0;
border-radius: 0;
border: none;
}
.navbar-brand {
width: 136px;
height: 33px;
background: url(../images/openstack-logo.png) no-repeat;
display: block;
padding: 0;
margin: 20px 0;
}
.navbar-inverse {
background: #edf2f7;
border: none;
padding: 10px 0;
}
.navbar-inverse .navbar-nav>li>a {
color: #486278;
padding-top: 10px;
padding-bottom: 10px;
border-radius: 5px;
margin-right: 10px;
font-family: "Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
text-transform: uppercase;
}
.navbar-inverse .navbar-nav>.active>a, .navbar-inverse .navbar-nav>.open>a {
background: #fff;
box-shadow: none;
color: #486278;
}
.navbar-inverse .navbar-nav>li>a:focus {
color: #486278;
}
.navbar-inverse .navbar-nav>li>a:hover, .navbar-inverse .navbar-nav>.active>a:focus, .navbar-inverse .navbar-nav>.active>a:hover {
background: #fff;
color: #486278;
}
.filters #supported-by {
margin-right: 20px;
}
.form-inline label {
margin-right: 3px;
}
.table th {
font-weight: 400;
color: #30739c;
}
.table tr td:first-child {
font-weight: 700;
}
.table tr td:last-child {
white-space: nowrap;
}
.content {
font-family: "Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
padding: 25px 0;
}
.btn {
border-radius: 0;
}
a.details {
text-transform: uppercase;
font-weight: 700;
font-size: .9em;
}
a.details i {
background: #30739c;
color: #fff;
display: inline-block;
padding: 2px 3px;
border-radius: 4px;
}
.bluebox .inner {
background-color: #edf2f7;
padding: 20px;
margin: 20px 0;
}
.bluebox h2 {
margin-top: 0;
color: #3597da;
font-size: 24px;
}
.bluebox img {
margin: 20px auto;
text-align: center;
}
.featured {
}
.featured .inner {
padding: 15px;
border: 1px #ccc solid;
border-radius: 4px;
text-align: center;
margin: 20px 0;
}
.featured img {
max-width: 100%;
margin-bottom: 10px;
}
.featured .inner p {
margin-bottom: 0;
font-size: .9em;
}
.pluginbox {
background-color: #edf2f7;
padding: 20px;
margin: 20px 0;
}
.pluginbox .lbl {
text-align: center;
}
.title div {
font-size: 18px;
text-transform: uppercase;
color: #fff;
text-align: left;
font-weight: 700;
}
.title {
text-align: left;
color: #fff;
background: #3597da;
border: none;
}
h1 {
color: #2A4E68;
font-weight: 300;
font-size: 28px;
}
.featured .glance {
background: url(../images/featured-corner-glance.png) no-repeat;
background-position: top right;
}
.featured .heat {
background: url(../images/featured-corner-heat.png) no-repeat;
background-position: top right;
}
.featured .murano {
background: url(../images/featured-corner-murano.png) no-repeat;
background-position: top right;
}
.filters {
z-index: 1;
position: relative;
}
.navbar-fixed-top {
z-index: 2;
}
.dataTables_wrapper {
z-index: 0;
top: -27px;
}
.dataTables_length {
float: right !important;
}
.dataTables_filter {
margin: 0 20px 20px 0;
}
.dataTables_filter input {
width: 70%;
border: 1px solid #ccc;
border-radius: 4px;
}
.ui-widget-overlay {
background-image: none !important;
background: #000 !important;
opacity: 0.6 !important;
}
.ui-dialog-titlebar {
display: none;
}
.ui-dialog h2 {
margin-top: 0;
color: #30739c;
}
.ui-dialog h3 {
color: #2a4e68;
}
.ui-widget-content {
background-image: none !important;
}
.ui-state-default {
background: none !important;
border: 1px solid #999 !important;
color: #666 !important;
}
.label {
font-size: 100% !important;
color: #000 !important;
}
.masthead h1 a:active, .masthead h1 a:focus {
text-decoration: none;
}
.form-group {
margin-left: 15px;
}
input[type=text] {
width: 250px;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

View File

@ -1,418 +0,0 @@
function getUrlVars() {
"use strict";
var vars = {};
window.location.href.replace(/[?&#]+([^=&]+)=([^&#]*)/gi, function (m, key, value) {
vars[key] = decodeURIComponent(value);
});
return vars;
}
function make_uri(uri, options) {
"use strict";
var ops = {};
$.extend(ops, getUrlVars());
if (options !== null) {
$.extend(ops, options);
}
var str = $.map(ops, function (val, index) {
return index + "=" + encodeURIComponent(val).toLowerCase();
}).join("&");
return (str === "") ? uri : uri + "?" + str;
}
function reload(extra) {
"use strict";
window.location.search = make_uri ("", extra);
}
function update_url(extra) {
var ops = {};
$.extend(ops, getUrlVars ());
if (extra !== null) {
$.extend(ops, extra);
}
window.location.hash = $.map(ops, function (val, index) {
return val ? (index + "=" + encodeURIComponent(val)) : null;
}).join("&");
}
function initSingleSelector(selector_id, property, dataSet, update_handler) {
var values = {};
var result = [];
var value, key;
for (var i = 0; i < dataSet.length; i++) {
var element = dataSet[i][property];
if (element instanceof Array) {
for (key in element)
if (key) {
values[element[key]] = element[key];
}
} else {
values[element] = element;
}
}
for (value in values)
if (value) {
result.push({"id": value, "text": value});
}
$("#" + selector_id).
val (getUrlVars()[property]).
on("select2-selecting", function (e) {
var options = {};
options[property] = e.val;
update_url (options);
update_handler ();
}).
on("select2-removed", function (e) {
var options = {};
options[property] = '';
update_url (options);
update_handler ();
}).
select2({data: result, allowClear: true});
}
function filterData (tableData, filters) {
var filteredData = [];
var key, column;
for (var i = 0; i < tableData.length; i++) {
var row = tableData[i];
var filtered = true;
for (column in filters) {
if (column in row) {
if (row[column] instanceof Array) {
filtered = false;
for (key in row[column])
if (filters[column] == row[column][key]) {
filtered = true;
}
} else {
if (filters[column] != row[column]) {
filtered = false;
}
}
}
if (filtered == false) {
break;
}
}
if (filtered) {
filteredData.push(row);
}
}
return filteredData;
}
function populate_table (table_id, table_column_names, tableData)
{
var tableColumns = [];
for (var i = 0; i < table_column_names.length; i++) {
tableColumns.push({"mData": table_column_names[i]});
for (var j = 0; j < tableData.length; j++) {
if (!(table_column_names[i] in tableData[j])) {
tableData[j][table_column_names[i]] = "";
}
}
}
if (table_id) {
$("#" + table_id).dataTable({
"aLengthMenu": [
[5, 10, 25, 50, -1],
[5, 10, 25, 50, "All"]
],
"bDestroy": true,
"iDisplayLength": -1,
"bAutoWidth": false,
"bPaginate": true,
"pagingType": "full_numbers",
"aaData": tableData,
"aoColumns": tableColumns
});
}
}
function showInfoDialog (tab, info) {
$("#info-container").empty();
$("#" + tab + "-info").tmpl(info).appendTo("#info-container");
$("#info-dialog").dialog("open");
$("button").focus ();
}
function showInfoPage (tab, info)
{
$("#info-content").empty();
$("#" + tab + "-info").tmpl(info).appendTo("#info-content");
$( ".content" ).hide ();
$( "#info-page" ).show ();
update_url ({ tab : tab, asset : info.name});
$(".value").replaceWith (function(idx, element) {
return element.replace (/https?:\/\/[^ \t\n\r]+/gi, '<a target="_blank" href="$&">$&</a>');
});
}
function setupInfoHandler (tab, element_id, info) {
info.name_html = "<a id=\"" + tab + "-" + element_id +
"\" href=\"#\" title=\"Show details\">" + info.name + "</a>";
$("#" + tab + "-table").on("click", "#" + tab + "-" + element_id, function (event) {
event.preventDefault();
event.stopPropagation();
showInfoPage (tab, info);
});
}
var assets = { assets: [] };
var glance_images = { assets: [] };
function show_glance_images ()
{
populate_table ("glance-images-table",
["name_html", "description", "service.disk_format", "license"],
filterData (glance_images.assets, getUrlVars ()));
}
var heat_templates = { assets: [] };
function show_heat_templates ()
{
populate_table ("heat-templates-table",
["name_html", "description", "release_html", "service.format"],
filterData (heat_templates.assets, getUrlVars ()));
}
var murano_apps = { assets: [] };
function show_murano_apps ()
{
populate_table ("murano-apps-table",
["name_html", "description", "release_html", "service.format"],
filterData (murano_apps.assets, getUrlVars ()));
}
var tosca_templates = { assets: [] };
function show_tosca_templates ()
{
populate_table ("tosca-templates-table",
["name_html", "description", "release_html", "service.template_format"],
filterData (tosca_templates.assets, getUrlVars ()));
}
function initTabs ()
{
$( "ul.nav > li > a" ).on("click", function (event) {
event.preventDefault ();
});
$( "ul.nav > li" ).not(":last-child").on("click", function (event) {
update_url ({ tab : this.children[0].hash.substring (1), asset: "" });
});
$( "ul.nav > li:last-child").on("click", function (event) {
window.open('https://wiki.openstack.org/wiki/App-Catalog#How_to_contribute', '_blank');
});
}
function show_asset (tab, tableData)
{
var options = getUrlVars ();
if ((tab == options.tab) && ("asset" in options)) {
for (var i = 0; i < tableData.length; ++i) {
if (tableData[i].name == options.asset) {
showInfoPage (tab, tableData[i]);
}
}
}
}
var recent_apps = [];
function build_recently_added ()
{
assets.assets.sort(function(a,b) {
return new Date(b.last_modified) - new Date(a.last_modified);
});
sorted_assets = assets.assets.slice(0,15);
sorted_assets.sort(
function() {
return 0.5 - Math.random();
});
for (var i = 0; i < 5; i++) {
var iconurl,
fittedname,
divclass,
hreftab;
if (typeof (sorted_assets[i].icon) === 'undefined') {
iconurl = "static/images/openstack-icon.png";
} else {
iconurl = sorted_assets[i].icon.url;
}
if (sorted_assets[i].name.length > 15) {
fittedname = sorted_assets[i].name.slice(0,13) + "...";
} else
{ fittedname = sorted_assets[i].name; }
if (sorted_assets[i].service.type == 'glance') {
divclass = "glance";
hreftab = "#tab=glance-images&asset=";
} else if (sorted_assets[i].service.type == 'heat') {
divclass = "heat";
hreftab = "#tab=heat-templates&asset=";
} else if ((sorted_assets[i].service.type == 'murano') ||
(sorted_assets[i].service.type == 'bundle')) {
divclass = "murano";
hreftab = "#tab=murano-apps&asset=";
}else if (sorted_assets[i].service.type == 'tosca') {
divclass = "tosca";
hreftab = "#tab=tosca-templates&asset=";
}
$('.featured').append(
$('<div>', {class: "col-md-2 col-sm-6"})
.append($('<div>', {class: "inner " + divclass})
.append($("<a>", {href: hreftab + sorted_assets[i].name})
.append($('<img>', {src: iconurl, height: 90}))
.append($('<p>', {text: fittedname})))
)
);
}
}
function initMarketPlace ()
{
navigate ();
initTabs ();
$( ".inner" ).matchHeight ();
$("#info-dialog").dialog({
autoOpen: false,
width: "70%",
modal: true,
buttons: {
Close: function () {
$(this).dialog("close");
}
},
close: function () { }
});
$.ajax({ url: "api/v1/assets" }).
done (function (data) {
assets = data;
build_recently_added ();
for (var i = 0; i < assets.assets.length; i++) {
var asset = assets.assets[i];
if (asset.service.type == 'glance') {
glance_images.assets.push(asset);
} else if (asset.service.type == 'heat') {
heat_templates.assets.push(asset);
} else if (asset.service.type == 'murano') {
murano_apps.assets.push(asset);
if (asset.service.type === 'bundle') {
asset.service.format = 'bundle';
}
} else if (asset.service.type == 'bundle') {
if ('murano_package_name' in asset.service) {
murano_apps.assets.push(asset);
asset.service.format = 'bundle';
}
}else if (asset.service.type == 'tosca') {
tosca_templates.assets.push(asset);
}
}
var tableData;
tableData = glance_images.assets;
for (var i = 0; i < tableData.length; i++) {
setupInfoHandler ("glance-images", i, tableData[i]);
}
show_asset ("glance-images", tableData);
show_glance_images ();
tableData = heat_templates.assets;
for (var i = 0; i < tableData.length; i++) {
tableData[i].release_html = "";
if (tableData[i].release) {
tableData[i].release_html = tableData[i].release.join(", ");
}
setupInfoHandler ("heat-templates", i, tableData[i]);
}
show_asset ("heat-templates", tableData);
show_heat_templates ();
initSingleSelector ("heat-release", "release", tableData, show_heat_templates);
tableData = murano_apps.assets;
for (var i = 0; i < tableData.length; i++) {
tableData[i].release_html = "";
if (tableData[i].release) {
tableData[i].release_html = tableData[i].release.join(", ");
}
setupInfoHandler ("murano-apps", i, tableData[i]);
}
show_asset ("murano-apps", tableData);
show_murano_apps ();
initSingleSelector ("murano-release", "release", tableData, show_murano_apps);
tableData = tosca_templates.assets;
for (var i = 0; i < tableData.length; i++) {
tableData[i].release_html = "";
if (tableData[i].release) {
tableData[i].release_html = tableData[i].release.join(", ");
}
setupInfoHandler ("tosca-templates", i, tableData[i]);
}
show_asset ("tosca-templates", tableData);
show_tosca_templates ();
initSingleSelector ("tosca-release", "release", tableData, show_tosca_templates);
});
}
function navigate ()
{
var tabs_list = $("#navbar")[0].children;
var selected_tab_name = null;
var options = getUrlVars ();
$( "ul.nav > li" ).removeClass ("active");
if ("tab" in options) {
for (var i = 0; i < tabs_list.length; ++i) {
var tab_name = tabs_list[i].children[0].hash.substring (1);
if (tab_name == options.tab) {
selected_tab_name = tab_name;
if (!("asset" in options)) {
tabs_list[i].className = "active";
}
break;
}
}
}
$( ".content" ).hide ();
if (selected_tab_name === null) {
$( "#landing-page" ).show ();
} else if ("asset" in options) {
show_asset ("murano-apps", murano_apps.assets);
show_asset ("heat-templates", heat_templates.assets);
show_asset ("glance-images", glance_images.assets);
show_asset ("tosca-templates", tosca_templates.assets);
} else {
$( "#" + selected_tab_name ).show ();
}
}
window.onhashchange = navigate;

View File

@ -1,116 +0,0 @@
<!DOCTYPE html>
<html>
<script src= "http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $http) {
$http({url:'/api/v1/assets'}).success(function(data) {
$scope.assets = [];
for(i in data.assets) {
var asset = data.assets[i];
if('tags' in asset) {
var found;
for(j in asset['tags']) {
if(asset['tags'][j] == 'app') {
$scope.assets.push(asset);
}
}
}
}
console.log($scope.assets);
});
});
</script>
<body>
<div style="margin-bottom: 4px;" ng-app="myApp" ng-controller="myCtrl">
<div style="
background-color:#f9f9f9;
overflow: auto;
border-color:#cccccc; border-width:1px; border-style: solid;
">
<div ng-repeat="asset in assets | orderBy:'name':false" style="border:1px;
margin: 10px;
width: 200px; height: 180px; float: left;
">
<div style="
margin:10px;
border-color:#cccccc; border-width:1px;
border-radius: 2px;
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.1);
padding:8px;
height: 170px;
overflow: hidden;
background-color:#ffffff;
position: relative;
">
<div style="">
<div style="
width:128px;
height:128px;
overflow: hidden;
margin-left: auto; margin-right:auto;
">
<img style="
margin: {{ asset.icon.top }}px 0px 0px {{ asset.icon.left }}px;
height: {{ asset.icon.height }}px;
" src="{{ asset.icon.url }}">
</div>
<span style="
max-height: 100%;
width: 23px;
height: 23px;
top: 0;
right: 0;
position: absolute;
background: rgba(0, 0, 0, 0) url('http://apps.openstack.org/static/images/featured-corner-{{ asset.service.type }}.png') no-repeat scroll right top;
">
</span>
</div>
<div title="{{ asset.name }}" style="
overflow: hidden;
white-space: nowrap;
position: relative;
line-height: 18px;
font-size: 16px;
"> {{ asset.name }}
<span style="
max-height: 100%;
width: 40px;
height: 20px;
bottom: 0;
right: 0;
position: absolute;
background: rgba(0, 0, 0, 0) linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1)) repeat scroll 0 0;
">
</span>
</div>
<div title="{{ asset.provided_by.name }}" style="
overflow: hidden;
white-space: nowrap;
position: relative;
line-height: 14px;
font-size: 12px;
"> {{ asset.provided_by.name }}
<span style="
max-height: 100%;
width: 40px;
height: 20px;
bottom: 0;
right: 0;
position: absolute;
background: rgba(0, 0, 0, 0) linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1)) repeat scroll 0 0;
">
</span>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,7 +0,0 @@
from openstack_catalog import wsgi_django
from openstack_catalog.api import api
from werkzeug.wsgi import DispatcherMiddleware
application = DispatcherMiddleware(wsgi_django.application, {'/api': api})

View File

@ -1,28 +0,0 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
WSGI config for openstack_catalog project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/
"""
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "openstack_catalog.settings")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

View File

@ -1,16 +0,0 @@
{
"version": "0.0.0",
"private": true,
"name": "app-catalog",
"description": "OpenStack App Catalog",
"repository": "none",
"license": "Apache 2.0",
"devDependencies": {
"eslint": "^0.23.0",
"eslint-config-openstack": "1.2.0"
},
"scripts": {
"lint": "eslint --no-color openstack_catalog/web/static/js/"
},
"dependencies": {}
}

View File

@ -1,13 +0,0 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
pbr>=1.3
Babel>=1.3
Django<1.9,>=1.8
django-compressor>=1.4
XStatic>=1.0.0 # MIT License
XStatic-Angular>=1.3.7 # MIT License
XStatic-Magic-Search>=0.2.5.1 # Apache 2.0 License
werkzeug
flask

View File

@ -1,70 +0,0 @@
#!/bin/bash
set -o errexit
function usage {
echo "Usage: $0 [OPTION]..."
echo "Run App Catalog's test suite(s)"
echo ""
echo " --runserver Run the development server for"
echo " openstack_catalog in the virtual"
echo " environment."
echo " -h, --help Print this usage message"
echo ""
exit
}
# DEFAULTS FOR RUN_TESTS.SH
#
root=`pushd $(dirname $0) > /dev/null; pwd; popd > /dev/null`
venv=$root/.venv
runserver=0
testopts=""
testargs=""
# Jenkins sets a "JOB_NAME" variable, if it's not set, we'll make it "default"
[ "$JOB_NAME" ] || JOB_NAME="default"
function process_option {
case "$1" in
-h|--help) usage;;
--runserver) runserver=1;;
-*) testopts="$testopts $1";;
*) testargs="$testargs $1"
esac
}
# PROCESS ARGUMENTS, OVERRIDE DEFAULTS
for arg in "$@"; do
process_option $arg
done
function run_server {
echo "Starting development server..."
$root/tools/update_assets.sh
if [ ! -d $venv ]; then
virtualenv $venv
. $venv/bin/activate
fi
. $venv/bin/activate
pip install -r $root/requirements.txt
#FIXME make venv cleaner.
# FIXME remove when CORS works
# pushd $root/openstack_catalog/web > /dev/null
# ${command_wrapper} python $root/tools/testserver.py runserver $testopts $testargs
${command_wrapper} python manage.py runserver $testopts $testargs
# popd > /dev/null
echo "Server stopped."
}
# Development server
if [ $runserver -eq 1 ]; then
if [ "x$testargs" = "x" -o "$testargs x" = " x" ]; then
testargs="127.0.0.1:18001"
fi
run_server
exit $?
fi

View File

@ -1,45 +0,0 @@
[metadata]
name = openstack_catalog
summary = Application catalog for OpenStack
description-file =
README.rst
author = OpenStack
author-email = openstack-dev@lists.openstack.org
home-page = http://www.openstack.org/
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology
Intended Audience :: System Administrators
License :: OSI Approved :: Apache Software License
Operating System :: POSIX :: Linux
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.4
[files]
packages =
openstack_catalog
[build_sphinx]
source-dir = doc/source
build-dir = doc/build
all_files = 1
[upload_sphinx]
upload-dir = doc/build/html
[compile_catalog]
directory = openstack_catalog/locale
domain = catalog
[update_catalog]
domain = catalog
output_dir = openstack_catalog/locale
input_file = openstack_catalog/locale/catalog.pot
[extract_messages]
keywords = _ gettext ngettext l_ lazy_gettext
mapping_file = babel.cfg
output_file = openstack_catalog/locale/catalog.pot

View File

@ -1,30 +0,0 @@
#!/usr/bin/env python
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
import setuptools
# In python < 2.7.4, a lazy loading of package `pbr` will break
# setuptools if some other modules registered functions in `atexit`.
# solution from: http://bugs.python.org/issue15881#msg170215
try:
import multiprocessing # noqa
except ImportError:
pass
setuptools.setup(
setup_requires=['pbr'],
pbr=True)

View File

@ -1,19 +0,0 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
hacking<0.11,>=0.10.0
coverage>=3.6
python-subunit>=0.0.18
sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3
jsonschema>=2.0.0,<3.0.0
oslosphinx>=2.2.0 # Apache-2.0
oslotest>=1.2.0 # Apache-2.0
testrepository>=0.0.18
testscenarios>=0.4
testtools>=0.9.36,!=1.2.0
PyYAML>=3.1.0
# js lint tests
nodeenv>=0.9.4 # BSD License

View File

@ -1,10 +0,0 @@
asset_file="$1"
#Chunk the yaml assets into begin_line_number,end_line_number sections
awk '{line+=1}/^ -/{end=line-1; if(start > 0){print start "," end}; count+=1;start=line;}END{print start "," line}' "$asset_file" | while read line; do
size=`echo $line | awk -F, '{print $2-$1+1}'`
end=`echo $line | awk -F, '{print $2}'`
name=`head -n $end "$asset_file" | tail -n $size | python -c 'import yaml,sys; print yaml.safe_load(sys.stdin)[0]["name"]'`
date=`git blame -w -L $line "$asset_file" | sed 's/^[^(]*(\([^)]*\)).*/\1/' | python -c 'import sys,dateutil.parser; print max([dateutil.parser.parse("%s %s%s"%(j[0], j[1], j[2])) for j in [i.split()[-4:] for i in sys.stdin.readlines()]])'`
#Dump out the name of the asset, and the last modified date as a json doc to stdout to be reassembled outside the loop into one document
(echo $name; echo $date) | python -c 'import sys,json; print json.dumps([i.strip() for i in sys.stdin.readlines()]),'
done | python -c 'import sys,json,yaml; print yaml.safe_dump({"assets":dict([[j[0], {"last_modified":j[1]}] for j in [json.loads(i) for i in sys.stdin.readlines()]])}),' #Assemble the individual json documents from the loop into one big one.

View File

@ -1,137 +0,0 @@
#! /usr/bin/env python
#
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import requests
import requestsexceptions
import yaml
from collections import OrderedDict
def project_representer(dumper, data):
return dumper.represent_mapping('tag:yaml.org,2002:map',
data.items())
def construct_yaml_map(self, node):
data = OrderedDict()
yield data
value = self.construct_mapping(node)
if isinstance(node, yaml.MappingNode):
self.flatten_mapping(node)
else:
raise yaml.constructor.ConstructorError(
None, None,
'expected a mapping node, but found %s' % node.id,
node.start_mark)
mapping = OrderedDict()
for key_node, value_node in node.value:
key = self.construct_object(key_node, deep=False)
try:
hash(key)
except TypeError as exc:
raise yaml.constructor.ConstructorError(
'while constructing a mapping', node.start_mark,
'found unacceptable key (%s)' % exc, key_node.start_mark)
value = self.construct_object(value_node, deep=False)
mapping[key] = value
data.update(mapping)
class IndentedEmitter(yaml.emitter.Emitter):
def expect_block_sequence(self):
self.increase_indent(flow=False, indentless=False)
self.state = self.expect_first_block_sequence_item
class IndentedDumper(IndentedEmitter, yaml.serializer.Serializer,
yaml.representer.Representer, yaml.resolver.Resolver):
def __init__(self, stream,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding=None, explicit_start=None, explicit_end=None,
version=None, tags=None):
IndentedEmitter.__init__(
self, stream, canonical=canonical,
indent=indent, width=width,
allow_unicode=allow_unicode,
line_break=line_break)
yaml.serializer.Serializer.__init__(
self, encoding=encoding,
explicit_start=explicit_start,
explicit_end=explicit_end,
version=version, tags=tags)
yaml.representer.Representer.__init__(
self, default_style=default_style,
default_flow_style=default_flow_style)
yaml.resolver.Resolver.__init__(self)
def get_hashes(hash_url):
hashes = {}
r = requests.get(hash_url, allow_redirects=True)
if r.status_code == 200:
for line in r.iter_lines():
try:
hash, file = line.split(" ")
except ValueError:
continue
hashes[file] = hash
return hashes
def main():
requestsexceptions.squelch_warnings()
yaml.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
construct_yaml_map)
yaml.add_representer(OrderedDict, project_representer,
Dumper=IndentedDumper)
data = yaml.safe_load(open('openstack_catalog/web/static/assets.yaml'))
assets = []
for a in data['assets']:
url = a.get('attributes', {}).get('url')
if not a.get('active', True) or not url:
assets.append(a)
continue
r = requests.head(url, allow_redirects=True)
if r.status_code != 200:
a['active'] = False
else:
hash_url = a.get('hash_url')
if hash_url:
hashes = get_hashes(hash_url)
filename = url.split("/")[-1]
a['attributes']['hash'] = hashes.get(filename, 'unknown')
assets.append(a)
output = {'assets': assets}
with open('openstack_catalog/web/static/assets.yaml', 'w') as out:
out.write(yaml.dump(output, default_flow_style=False,
Dumper=IndentedDumper, width=80,
indent=2))
if __name__ == '__main__':
main()

View File

@ -1,41 +0,0 @@
#!/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 SimpleHTTPServer
import SocketServer
class AllowOriginRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):
print("App Catalog Versions:",
self.headers.get('X-App-Catalog-Versions', ''))
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
def end_headers(self):
self.send_header("Access-Control-Allow-Origin", "*")
SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(self)
def do_OPTIONS(self):
self.send_response(200)
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Access-Control-Allow-Headers",
"X-App-Catalog-Versions")
self.send_header("Allow", "GET")
if __name__ == '__main__':
PORT = 18001
httpd = SocketServer.TCPServer(("", PORT), AllowOriginRequestHandler)
print("serving at port", PORT)
httpd.serve_forever()

View File

@ -1,19 +0,0 @@
#!/bin/bash
ROOTDIR=`pushd $(dirname $0)/.. > /dev/null /dev/null; pwd; popd > /dev/null`
echo $ROOTDIR
pushd $ROOTDIR/openstack_catalog/web > /dev/null
mkdir -p api/v1/
if [ ! -f static/assets_dead.yaml ];
then
echo 'assets: {}' > static/assets_dead.yaml
fi
if [ ! -f api/v1/assets ] || [ static/assets.yaml -nt api/v1/assets ] || [ static/assets_dead.yaml -nt api/v1/assets ];
then
$ROOTDIR/tools/asset_history.sh static/assets.yaml > static/assets_merge.yaml
python $ROOTDIR/tools/yaml2json.py static/assets_merge.yaml static/assets_dead.yaml < static/assets.yaml > /tmp/assets.$$
zopfli --i150 -c /tmp/assets.$$ > /tmp/assets.$$.gz || gzip -c /tmp/assets.$$ > /tmp/assets.$$.gz
mv /tmp/assets.$$.gz api/v1/assets.gz
mv /tmp/assets.$$ api/v1/assets
rm -f static/assets_merge.yaml
fi
popd > /dev/null

View File

@ -1,51 +0,0 @@
#!/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 argparse
import json
import sys
import yaml
parser = argparse.ArgumentParser(description='Merge and convert yaml to json.')
parser.add_argument('files', metavar='F', type=str, nargs='+',
help='file to process')
args = parser.parse_args()
def dict_merge(a, b):
for k, v in b.items():
if isinstance(v, dict):
n = a.setdefault(k, {})
dict_merge(n, v)
else:
a[k] = v
return a
merge = {}
for f in args.files:
merge = dict_merge(merge, yaml.safe_load(open(f))['assets'])
y = yaml.safe_load(sys.stdin)
for a in y['assets']:
s = a['service']
if s['type'] == 'heat':
if 'environment' in s:
s['environment'] = yaml.dump(s['environment'])
if a['name'] in merge:
dict_merge(a, merge[a['name']])
y = [a for a in y['assets'] if a.get('active', True)]
y = {'assets': y}
json.dump(y, sys.stdout)

50
tox.ini
View File

@ -1,50 +0,0 @@
[tox]
minversion = 1.6
envlist = py34,py27,pep8
skipsdist = True
[testenv]
usedevelop = True
install_command = pip install -U {opts} {packages}
setenv =
VIRTUAL_ENV={envdir}
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands = python setup.py testr --slowest --testr-args='{posargs}'
[testenv:pep8]
commands = flake8
[testenv:venv]
commands = {posargs}
[testenv:eslint]
deps = -r{toxinidir}/test-requirements.txt
passenv = *
commands = nodeenv -p
npm install
npm run lint
[testenv:cover]
commands = python setup.py testr --coverage --testr-args='{posargs}'
[testenv:docs]
commands = python setup.py build_sphinx
[testenv:debug]
commands = oslo_debug_helper {posargs}
[flake8]
# E123, E125 skipped as they are invalid PEP-8.
show-source = True
builtins = _
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build
[testenv:bindep]
# Do not install any requirements. We want this to be fast and work even if
# system dependencies are missing, since it's used to tell you what system
# dependencies are missing! This also means that bindep must be installed
# separately, outside of the requirements files.
deps = bindep
commands = bindep test