Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Orlo 0.4.3 #53

Merged
merged 22 commits into from
May 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions Vagrantfile
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,21 @@ Vagrant.configure(2) do |config|
apt-get -f install -y

# Setup a virtualenv; avoids conflicts, particularly with python-six
virtualenv /home/vagrant/virtualenv/orlo
source /home/vagrant/virtualenv/orlo/bin/activate
echo "source ~/virtualenv/orlo/bin/activate" >> /home/vagrant/.profile
virtualenv /home/vagrant/virtualenv/orlo_py27 --python=python2.7
virtualenv /home/vagrant/virtualenv/orlo_py34 --python=python3.4

source /home/vagrant/virtualenv/orlo_py34/bin/activate
echo "source ~/virtualenv/orlo_py34/bin/activate" >> /home/vagrant/.profile

pip install --upgrade pip setuptools

cd /vagrant/orlo
pip install .[test]
pip install -r /vagrant/orlo/docs/requirements.txt
pip install .[doc]
python setup.py develop

mkdir -p /etc/orlo /var/log/orlo
chown -R vagrant:vagrant /var/log/orlo
# echo -e "[db]\nuri=postgres://orlo:[email protected]" > /etc/orlo/orlo.ini


Expand All @@ -83,7 +86,7 @@ Vagrant.configure(2) do |config|

config.vm.define "jessie" do |jessie|
jessie.vm.box = "bento/debian-8.7"
jessie.vm.network "forwarded_port", guest: 5000, host: 5000
jessie.vm.network "forwarded_port", guest: 5000, host: 5100
jessie.vm.network "private_network", ip: "192.168.57.20"
jessie.vm.provision "shell", inline: <<-SHELL
SHELL
Expand Down
8 changes: 8 additions & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
orlo (0.4.2) jessie; urgency=medium

Build changes only

* Fix preinst script on install

-- Alex Forbes <[email protected]> Tue, 11 Apr 2017 13:52:57 +0000

orlo (0.4.1) jessie; urgency=medium

Build changes only
Expand Down
8 changes: 5 additions & 3 deletions debian/preinst
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ set -e
case "$1" in
# If version 0.3.x we need to purge the user and group, as they were
# non-system beforehand, and the new postinst creates them with --system
upgrade|install)
old_uid=$(id -u orlo)
old_gid=$(getent group orlo | cut -d: -f3)
upgrade)
old_uid=$(id -u orlo || true)
old_gid=$(getent group orlo || true | cut -d: -f3)

if [ 0$old_uid -gt 1000 ]; then
deluser orlo || true
Expand All @@ -18,4 +18,6 @@ case "$1" in
delgroup orlo || true
fi
;;
install)
;;
esac
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
# The short X.Y version.
version = '0.4'
# The full version, including alpha/beta/rc tags.
release = '0.4.0'
release = '0.4.3'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
6 changes: 5 additions & 1 deletion etc/orlo.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[db]
uri = sqlite://
uri = sqlite:////var/lib/orlo/orlo.db
; Mapped to SQLALCHEMY_ECHO
echo_queries = false

Expand Down Expand Up @@ -31,3 +31,7 @@ user_base_dn = ou=people,ou=example,o=test
[logging]
directory = /var/log/orlo
level = info

; orloclient will read /etc/orlo/orlo.ini to set the default uri
[client]
uri = http://localhost:8080
2 changes: 1 addition & 1 deletion orlo/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def load(self):


if config.getboolean('security', 'enabled') and \
config.get('security', 'secret_key') == 'change_me':
config.get('security', 'secret_key') == 'change_me':
raise OrloStartupError(
"Security is enabled, please configure security:secret_key in orlo.ini")

Expand Down
3 changes: 3 additions & 0 deletions orlo/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,7 @@
config.set('logging', 'directory', defaults['ORLO_LOGDIR']) # "disabled" for no
# log files

config.add_section('behaviour')
config.set('behaviour', 'versions_by_release', 'false')

config.read(defaults['ORLO_CONFIG'])
33 changes: 33 additions & 0 deletions orlo/migrations/0868747e62ff_add_unique_constraints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Add unique constraints

Revision ID: 0868747e62ff
Revises: e60a77e44da8
Create Date: 2017-04-11 16:10:42.109777

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '0868747e62ff'
down_revision = 'e60a77e44da8'
branch_labels = ()
depends_on = None

def upgrade():
op.create_unique_constraint(None, 'package', ['id'])
op.create_unique_constraint(None, 'package_result', ['id'])
op.create_unique_constraint(None, 'platform', ['id'])
op.create_unique_constraint(None, 'release', ['id'])
op.create_unique_constraint(None, 'release_metadata', ['id'])
op.create_unique_constraint(None, 'release_note', ['id'])


def downgrade():
op.drop_constraint(None, 'release_note', type_='unique')
op.drop_constraint(None, 'release_metadata', type_='unique')
op.drop_constraint(None, 'release', type_='unique')
op.drop_constraint(None, 'platform', type_='unique')
op.drop_constraint(None, 'package_result', type_='unique')
op.drop_constraint(None, 'package', type_='unique')
4 changes: 2 additions & 2 deletions orlo/orm.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class Release(db.Model):
user = db.Column(db.String, nullable=False)
team = db.Column(db.String)
packages = db.relationship("Package", backref=db.backref("release"))
notes = db.relationship("ReleaseNote", backref=db.backref("release"))

def __init__(self, platforms, user, team=None, references=None):
# platforms and references are stored as strings in DB but really are lists
Expand Down Expand Up @@ -101,6 +102,7 @@ def to_dict(self):
'metadata': metadata,
'user': self.user,
'team': self.team,
'notes': [n.content for n in self.notes],
}

def start(self):
Expand Down Expand Up @@ -215,9 +217,7 @@ class ReleaseNote(db.Model):

id = db.Column(UUIDType, primary_key=True, unique=True)
content = db.Column(db.Text, nullable=False)

release_id = db.Column(UUIDType, db.ForeignKey("release.id"))
release = db.relationship("Release", backref=db.backref('notes', order_by=id))

def __init__(self, release_id, content):
self.id = uuid.uuid4()
Expand Down
87 changes: 47 additions & 40 deletions orlo/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def filter_release_status(query, status):
:param status: The status to filter on
:return:
"""
app.logger.info("Filtering release status on {}".format(status))
enums = Package.status.property.columns[0].type.enums
if status not in enums:
raise InvalidUsage("Invalid package status, {} is not in {}".format(
Expand All @@ -60,6 +61,8 @@ def filter_release_status(query, status):
elif status in ["FAILED", "IN_PROGRESS"]:
# ANY package can match for this status to apply to the release
query = query.filter(Release.packages.any(Package.status == status))
else:
app.logger.debug("Not filtering query on release status")
return query


Expand Down Expand Up @@ -246,41 +249,57 @@ def apply_package_filters(query, args):
return query


def releases(limit=None, offset=None, asc=None, **kwargs):
def build_query(object_type, limit=None, offset=None, asc=None, **kwargs):
"""
Return whole releases, based on filters

:param object_type: Object type to query, Release or Package
:param limit: Max number of results to return
:param offset: Offset results. Provides pagination when combined with limit.
:param asc: Sort ascending instead of the default descending order
:param kwargs: Request arguments
:return:
"""
if object_type is Release:
filter_function = apply_filters
elif object_type is Package:
filter_function = apply_package_filters
else:
raise OrloError("build_query does not support object {}".format(
object_type
))

if any(field.startswith('package_') for field in kwargs.keys()) \
or "status" in kwargs.keys():
# Package attributes need the join, as does status as it's really a
# package attribute
query = db.session.query(Release).join(Package)
or ("status" in kwargs.keys() and object_type is Release):
if object_type is not Release:
raise InvalidUsage(
"'package_' parameters are only valid for Release queries. "
"(hint: retry without the package_ prefix)")
else:
# Package attributes need the join, as does status as it's really a
# package attribute
query = db.session.query(object_type).join(Package)
else:
# No need to join on package if none of our params need it
query = db.session.query(Release)
query = db.session.query(object_type)

for key in kwargs.keys():
if isinstance(kwargs[key], bool):
continue
if kwargs[key].lower() in ['null', 'none']:
kwargs[key] = None

try:
query = apply_filters(query, kwargs)
query = filter_function(query, kwargs)
except AttributeError as e:
raise InvalidUsage(
"An invalid field was specified: {}".format(e.args[0]))
"An invalid field for table {} was specified: {}".format(
object_type.__tablename__, e.args[0]))

if asc:
stime_field = Release.stime.asc
stime_field = object_type.stime.asc
else:
stime_field = Release.stime.desc
stime_field = object_type.stime.desc

query = query.order_by(stime_field())

Expand All @@ -299,32 +318,8 @@ def releases(limit=None, offset=None, asc=None, **kwargs):

return query


def packages(**kwargs):
"""
Return a list of packages

Fairly simple at present, as this is not envisioned to be frequently used.

:param kwargs: Filters
:return:
"""
query = db.session.query(Package)

for key in kwargs.keys():
if isinstance(kwargs[key], bool):
continue
if kwargs[key].lower() in ['null', 'none']:
kwargs[key] = None
try:
query = apply_package_filters(query, kwargs)
except AttributeError as e:
raise InvalidUsage("An invalid field was specified")

query = query.order_by(Package.stime.asc())

return query

def packages():
pass

def user_summary(platform=None):
"""
Expand Down Expand Up @@ -465,7 +460,7 @@ def package_list(platform=None):
return query


def package_versions(platform=None):
def package_versions(platform=None, by_release=False):
"""
List the current version of all packages

Expand All @@ -474,6 +469,11 @@ def package_versions(platform=None):
release time

:param platform: Platform to filter on
:param bool by_release: If true, a package, that is part of a release which
is not SUCCESSFUL, will not be considered the current version, even
if its own status is SUCCESSFUL. If false, a package will be the
current version as long as its own status is SUCCESSFUL.
Default: False.
"""

# Sub query gets a list of successful packages by last successful release
Expand All @@ -482,10 +482,17 @@ def package_versions(platform=None):
Package.name.label('name'),
db.func.max(Package.stime).label('max_stime')) \
.filter(Package.status == 'SUCCESSFUL')

if platform or by_release:
# Need to join on Release
sub_q = sub_q.join(Release)
if platform: # filter by platform
sub_q = sub_q \
.join(Release) \
.filter(Release.platforms.any(Platform.name == platform))
sub_q = sub_q.filter(Release.platforms.any(Platform.name == platform))
if by_release:
# Filter out packages which are part of a release in progress,
# see issue#52
sub_q = filter_release_status(sub_q, status="SUCCESSFUL")

sub_q = sub_q \
.group_by(Package.name) \
.subquery()
Expand Down
17 changes: 15 additions & 2 deletions orlo/routes/info.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from __future__ import print_function
from flask import request, jsonify, url_for
from orlo.app import app
from orlo.util import str_to_bool
from orlo.config import config
import orlo.queries as queries

__author__ = 'alforbes'
Expand Down Expand Up @@ -116,10 +118,21 @@ def info_package_versions():
"""
Return current version of all packages

:query platform:
:query bool platform: Filter versions by platform
:query bool by_release: If true, a package that is part of a release, which
is not SUCCESSFUL, will not be considered the current version, even if
its own status is SUCCESSFUL. If false, a package will be the current
version as long as its own status is SUCCESSFUL. Default: False.
"""
platform = request.args.get('platform')
q = queries.package_versions(platform=platform)
exclude_partial_releases = str_to_bool(
request.args.get('by_release') or \
config.get('behaviour', 'versions_by_release')
)

q = queries.package_versions(
platform=platform,
by_release=exclude_partial_releases)
result = q.all()

packages = {}
Expand Down
14 changes: 8 additions & 6 deletions orlo/routes/packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,24 +37,26 @@ def get_packages(package_id=None):
if not is_uuid(package_id):
raise InvalidUsage("Package ID given is not a valid UUID")
query = queries.get_package(package_id)
elif len([x for x in request.args.keys()]) == 0:
raise InvalidUsage("Please specify a filter. See "
"http://orlo.readthedocs.org/en/latest/rest.html"
"#get--packages for more info")
else: # Bit more complex
# Flatten args, as the ImmutableDict puts some values in a list when
# expanded
args = {}
args = {
'limit': 100
}
for k in request.args.keys():
if k in booleans:
args[k] = str_to_bool(request.args.get(k))
else:
args[k] = request.args.get(k)
query = queries.packages(**args)
query = queries.build_query(Package, **args)

# Execute eagerly to avoid confusing stack traces within the Response on
# error
db.session.execute(query)

if query.count() == 0:
response = jsonify(message="No packages found", packages=[])
return response, 404

return Response(stream_json_list('packages', query),
content_type='application/json')
Loading