Add basic CLI app and client

Change-Id: Ib4a55bf91e16aad2e56fb358a1b23f0b66237953
This commit is contained in:
Yuriy Taraday
2016-04-07 12:29:00 +03:00
parent cd04152f8f
commit f163cb20e0
5 changed files with 142 additions and 0 deletions

View File

@@ -7,3 +7,5 @@ flask
flask-sqlalchemy
flask-restful
alembic
cliff
requests

View File

@@ -14,3 +14,4 @@ oslotest>=1.10.0 # Apache-2.0
testrepository>=0.0.18
testscenarios>=0.4
testtools>=1.4.0
requests-mock

View File

@@ -0,0 +1,27 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from cliff import app
from cliff import commandmanager
import tuning_box
class TuningBoxApp(app.App):
def __init__(self, client, **kwargs):
super(TuningBoxApp, self).__init__(
description='Tuning Box - configuration storage for your cloud',
version=tuning_box.__version__,
command_manager=commandmanager.CommandManager('tuning_box.cli'),
**kwargs
)
self.client = client

43
tuning_box/client.py Normal file
View File

@@ -0,0 +1,43 @@
# 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
class HTTPClient(object):
def __init__(self, base_url):
self.base_url = base_url
self.session = self.get_session()
def get_session(self):
session = requests.Session()
session.headers.update(self.default_headers())
return session
def default_headers(self):
return {
"Content-Type": "application/json",
"Accept": "application/json",
}
def request(self, method, url, **kwargs):
full_url = self.base_url + url
resp = self.session.request(method, full_url, **kwargs)
resp.raise_for_status()
if resp.headers.get('Content-Type') == 'application/json' and \
resp.content:
return resp.json()
else:
return None
def get(self, url, params=None):
return self.request('GET', url, params=params)

View File

@@ -0,0 +1,69 @@
# 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 io
from requests_mock.contrib import fixture as req_fixture
from tuning_box import cli
from tuning_box import client as tb_client
from tuning_box.tests import base
class SafeTuningBoxApp(cli.TuningBoxApp):
def __init__(self, client):
super(SafeTuningBoxApp, self).__init__(
client=client,
**self.get_std_streams()
)
@staticmethod
def get_std_streams():
if bytes is str:
io_cls = io.BytesIO
else:
io_cls = io.StringIO
return {k: io_cls() for k in ('stdin', 'stdout', 'stderr')}
def build_option_parser(self, description, version, argparse_kwargs=None):
parser = super(SafeTuningBoxApp, self).build_option_parser(
description, version, argparse_kwargs)
parser.set_defaults(debug=True)
return parser
def get_fuzzy_matches(self, cmd):
# Turn off guessing, we need exact failures in tests
return []
def run(self, argv):
try:
exit_code = super(SafeTuningBoxApp, self).run(argv)
except SystemExit as e:
exit_code = e.code
assert exit_code == 0
class _BaseCLITest(base.TestCase):
BASE_URL = 'http://somehost/prefix'
def setUp(self):
super(_BaseCLITest, self).setUp()
client = tb_client.HTTPClient(self.BASE_URL)
self.req_mock = self.useFixture(req_fixture.Fixture())
self.cli = SafeTuningBoxApp(client=client)
class TestApp(_BaseCLITest):
def test_help(self):
self.cli.run(["--help"])
self.assertEqual('', self.cli.stderr.getvalue())
self.assertNotIn('Could not', self.cli.stdout.getvalue())