add usage API
Change-Id: Id6a688192277f9a58b13ca45d2596d375c5c187d
This commit is contained in:
parent
d5f060ad09
commit
84bc653862
@ -7,6 +7,9 @@
|
|||||||
- context: .
|
- context: .
|
||||||
repository: vexxhost/atmosphere-ingress
|
repository: vexxhost/atmosphere-ingress
|
||||||
target: atmosphere-ingress
|
target: atmosphere-ingress
|
||||||
|
- context: .
|
||||||
|
repository: vexxhost/atmosphere-usage
|
||||||
|
target: atmosphere-usage
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
name: atmosphere:image:upload
|
name: atmosphere:image:upload
|
||||||
|
@ -27,3 +27,6 @@ ENV FLASK_APP=atmosphere.app \
|
|||||||
|
|
||||||
FROM atmosphere AS atmosphere-ingress
|
FROM atmosphere AS atmosphere-ingress
|
||||||
ENV UWSGI_WSGI_FILE=/usr/local/bin/atmosphere-ingress-wsgi
|
ENV UWSGI_WSGI_FILE=/usr/local/bin/atmosphere-ingress-wsgi
|
||||||
|
|
||||||
|
FROM atmosphere AS atmosphere-usage
|
||||||
|
ENV UWSGI_WSGI_FILE=/usr/local/bin/atmosphere-usage-wsgi
|
62
atmosphere/api/usage.py
Normal file
62
atmosphere/api/usage.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# Copyright 2020 VEXXHOST, Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
"""Usage API."""
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from flask import abort
|
||||||
|
from flask import Blueprint
|
||||||
|
from flask import request
|
||||||
|
from flask import jsonify
|
||||||
|
from keystonemiddleware import auth_token
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from atmosphere.app import create_app
|
||||||
|
from atmosphere import models
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
blueprint = Blueprint('usage', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
def init_application(config=None):
|
||||||
|
"""Create usage API application."""
|
||||||
|
app = create_app(config)
|
||||||
|
app.register_blueprint(blueprint)
|
||||||
|
|
||||||
|
cfg.CONF([])
|
||||||
|
authtoken_config = dict(CONF.keystone_authtoken)
|
||||||
|
authtoken_config['log_name'] = app.name
|
||||||
|
|
||||||
|
app.wsgi_app = auth_token.AuthProtocol(app.wsgi_app, authtoken_config)
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/v1/resources')
|
||||||
|
def list_resources():
|
||||||
|
"""List all resources for a specific project."""
|
||||||
|
# Project ID from request (or allow override if admin)
|
||||||
|
project_id = request.headers['X-Project-Id']
|
||||||
|
if 'admin' in request.headers['X-Roles'] and 'project_id' in request.args:
|
||||||
|
project_id = request.args['project_id']
|
||||||
|
|
||||||
|
try:
|
||||||
|
start = datetime.fromisoformat(request.args['start'])
|
||||||
|
end = datetime.fromisoformat(request.args['end'])
|
||||||
|
except (KeyError, ValueError):
|
||||||
|
abort(400)
|
||||||
|
|
||||||
|
resources = models.Resource.get_all_by_time_range(start, end, project_id)
|
||||||
|
return jsonify(resources)
|
@ -15,8 +15,25 @@
|
|||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from atmosphere.api import ingress
|
||||||
from atmosphere.tests.unit import fake
|
from atmosphere.tests.unit import fake
|
||||||
from atmosphere import models
|
from atmosphere import models
|
||||||
|
from atmosphere.models import db
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def app():
|
||||||
|
app = ingress.init_application()
|
||||||
|
app.config['TESTING'] = True
|
||||||
|
app.config['SQLALCHEMY_ECHO'] = True
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def _db(app):
|
||||||
|
db.init_app(app)
|
||||||
|
db.create_all()
|
||||||
|
return db
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("client", "db_session")
|
@pytest.mark.usefixtures("client", "db_session")
|
||||||
|
37
atmosphere/tests/unit/api/test_usage.py
Normal file
37
atmosphere/tests/unit/api/test_usage.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# Copyright 2020 VEXXHOST, Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from atmosphere.api import usage
|
||||||
|
from atmosphere.tests.unit import fake
|
||||||
|
from atmosphere import models
|
||||||
|
from atmosphere.models import db
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def app():
|
||||||
|
app = usage.init_application()
|
||||||
|
app.config['TESTING'] = True
|
||||||
|
app.config['SQLALCHEMY_ECHO'] = True
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("client")
|
||||||
|
class TestResourceNoAuth:
|
||||||
|
def test_get_resources(self, client):
|
||||||
|
response = client.get('/v1/resources')
|
||||||
|
|
||||||
|
assert response.status_code == 401
|
@ -18,7 +18,6 @@ from flask_sqlalchemy import SQLAlchemy
|
|||||||
|
|
||||||
from atmosphere.app import create_app
|
from atmosphere.app import create_app
|
||||||
from atmosphere.api import ingress
|
from atmosphere.api import ingress
|
||||||
from atmosphere.models import db
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=[
|
@pytest.fixture(params=[
|
||||||
@ -36,19 +35,3 @@ from atmosphere.models import db
|
|||||||
])
|
])
|
||||||
def ignored_event(request):
|
def ignored_event(request):
|
||||||
yield request.param
|
yield request.param
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def app():
|
|
||||||
app = create_app()
|
|
||||||
app.config['TESTING'] = True
|
|
||||||
app.config['SQLALCHEMY_ECHO'] = True
|
|
||||||
app.register_blueprint(ingress.blueprint)
|
|
||||||
return app
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def _db(app):
|
|
||||||
db.init_app(app)
|
|
||||||
db.create_all()
|
|
||||||
return db
|
|
||||||
|
@ -22,11 +22,28 @@ from dateutil.relativedelta import relativedelta
|
|||||||
from freezegun import freeze_time
|
from freezegun import freeze_time
|
||||||
import before_after
|
import before_after
|
||||||
|
|
||||||
|
from atmosphere.api import ingress
|
||||||
from atmosphere import models
|
from atmosphere import models
|
||||||
|
from atmosphere.models import db
|
||||||
from atmosphere import exceptions
|
from atmosphere import exceptions
|
||||||
from atmosphere.tests.unit import fake
|
from atmosphere.tests.unit import fake
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def app():
|
||||||
|
app = ingress.init_application()
|
||||||
|
app.config['TESTING'] = True
|
||||||
|
app.config['SQLALCHEMY_ECHO'] = True
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def _db(app):
|
||||||
|
db.init_app(app)
|
||||||
|
db.create_all()
|
||||||
|
return db
|
||||||
|
|
||||||
|
|
||||||
class GetOrCreateTestMixin:
|
class GetOrCreateTestMixin:
|
||||||
def test_with_existing_object(self):
|
def test_with_existing_object(self):
|
||||||
event = fake.get_normalized_event()
|
event = fake.get_normalized_event()
|
||||||
|
@ -2,5 +2,6 @@ ceilometer
|
|||||||
Flask
|
Flask
|
||||||
Flask-Migrate
|
Flask-Migrate
|
||||||
Flask-SQLAlchemy
|
Flask-SQLAlchemy
|
||||||
|
keystonemiddleware
|
||||||
python-dateutil
|
python-dateutil
|
||||||
PyMySQL
|
PyMySQL
|
||||||
|
@ -8,6 +8,7 @@ packages =
|
|||||||
[entry_points]
|
[entry_points]
|
||||||
wsgi_scripts =
|
wsgi_scripts =
|
||||||
atmosphere-ingress-wsgi = atmosphere.api.ingress:init_application
|
atmosphere-ingress-wsgi = atmosphere.api.ingress:init_application
|
||||||
|
atmosphere-usage-wsgi = atmosphere.api.usage:init_application
|
||||||
|
|
||||||
[tool:pytest]
|
[tool:pytest]
|
||||||
mocked-sessions=atmosphere.models.db.session
|
mocked-sessions=atmosphere.models.db.session
|
||||||
|
6
tox.ini
6
tox.ini
@ -4,10 +4,12 @@ skipsdist = True
|
|||||||
[testenv]
|
[testenv]
|
||||||
envdir = {toxworkdir}/shared
|
envdir = {toxworkdir}/shared
|
||||||
usedevelop = True
|
usedevelop = True
|
||||||
setenv =
|
|
||||||
FLASK_APP=atmosphere.app
|
|
||||||
passenv =
|
passenv =
|
||||||
|
OS_*
|
||||||
|
FLASK_APP
|
||||||
DATABASE_URI
|
DATABASE_URI
|
||||||
|
setenv =
|
||||||
|
FLASK_ENV=development
|
||||||
deps =
|
deps =
|
||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
-r{toxinidir}/requirements.txt
|
-r{toxinidir}/requirements.txt
|
||||||
|
Loading…
Reference in New Issue
Block a user