Merge "Respect format when printing Fuel's VERSION"

This commit is contained in:
Jenkins
2015-04-08 11:15:25 +00:00
committed by Gerrit Code Review
12 changed files with 212 additions and 34 deletions

View File

@@ -14,6 +14,7 @@
import argparse
from fuelclient.cli import serializers
from fuelclient import client
@@ -23,4 +24,6 @@ class FuelVersionAction(argparse._VersionAction):
:returns: prints fuel server version
"""
def __call__(self, parser, namespace, values, option_string=None):
parser.exit(message=client.APIClient.get_fuel_version())
serializer = serializers.Serializer.from_params(namespace)
version = client.APIClient.get_fuel_version()
parser.exit(message=serializer.serialize(version))

View File

@@ -17,6 +17,7 @@ from itertools import chain
import os
from fuelclient import __version__
from fuelclient.actions import fuel_version
from fuelclient.cli.error import ArgumentException
from fuelclient.client import APIClient
@@ -92,15 +93,6 @@ class NodeAction(argparse.Action):
setattr(namespace, self.dest, map(int, only_ids))
class FuelVersionAction(argparse._VersionAction):
"""Custom argparse._VersionAction subclass to compute fuel server version
:returns: prints fuel server version
"""
def __call__(self, parser, namespace, values, option_string=None):
parser.exit(message=APIClient.get_fuel_version())
class SetAction(argparse.Action):
"""Custom argparse.Action subclass to store distinct values
@@ -139,7 +131,7 @@ def get_fuel_version_arg():
return {
"args": ["--fuel-version"],
"params": {
"action": FuelVersionAction,
"action": fuel_version.FuelVersionAction,
"help": "show Fuel server's version number and exit"
}
}

View File

@@ -22,6 +22,7 @@ from fuelclient.cli.arguments import substitutions
from fuelclient.cli.error import exceptions_decorator
from fuelclient.cli.error import ParserException
from fuelclient.cli.serializers import Serializer
from fuelclient import consts
from fuelclient import profiler
@@ -157,13 +158,15 @@ class Parser:
prof.save_data()
def add_serializers_args(self):
serializers = self.parser.add_mutually_exclusive_group()
for format_name in Serializer.serializers.keys():
serialization_flag = "--{0}".format(format_name)
self.universal_flags.append(serialization_flag)
self.parser.add_argument(
serializers.add_argument(
serialization_flag,
dest=format_name,
action="store_true",
dest=consts.SERIALIZATION_FORMAT_FLAG,
action="store_const",
const=format_name,
help="prints only {0} to stdout".format(format_name),
default=False
)

View File

@@ -21,6 +21,7 @@ import os
import yaml
from fuelclient.cli import error
from fuelclient import consts
class Serializer(object):
@@ -42,21 +43,33 @@ class Serializer(object):
default_format = "yaml"
format = default_format
def __init__(self, **kwargs):
for f in self.serializers:
if kwargs.get(f, False):
self.format = f
self.format_flags = True
break
def __init__(self, format=None):
if format and format in self.serializers:
self.format = format
self.format_flags = True
@property
def serializer(self):
"""Returns dicts with methods for loadin/dumping current fromat.
Returned dict's keys:
* 'w' - from 'writing', method for serializing/dumping data
* 'r' - from 'reading', method for deserializing/loading data
"""
return self.serializers[self.format]
def serialize(self, data):
"""Shortcut for serializing data with current format."""
return self.serializer['w'](data)
def deserialize(self, data):
"""Shortcut for deserializing data with current format."""
return self.serializer['r'](data)
@classmethod
def from_params(cls, params):
kwargs = dict((key, getattr(params, key)) for key in cls.serializers)
return cls(**kwargs)
return cls(format=getattr(params,
consts.SERIALIZATION_FORMAT_FLAG, None))
def print_formatted(self, data):
print(self.serializer["w"](data))

View File

@@ -18,7 +18,6 @@ import requests
from keystoneclient.v2_0 import client as auth_client
from six.moves.urllib import parse as urlparse
import yaml
from fuelclient.cli.error import exceptions_decorator
from fuelclient import fuelclient_settings
@@ -188,10 +187,7 @@ class Client(object):
@exceptions_decorator
def get_fuel_version(self):
return yaml.safe_dump(
self.get_request("version"),
default_flow_style=False
)
return self.get_request("version")
# This line is single point of instantiation for 'Client' class,
# which intended to implement Singleton design pattern.

17
fuelclient/consts.py Normal file
View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# Copyright 2015 Mirantis, 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.
SERIALIZATION_FORMAT_FLAG = 'serialization_format'

View File

@@ -30,7 +30,7 @@ class BaseObject(object):
def __init__(self, obj_id, **kwargs):
self.connection = APIClient
self.serializer = Serializer(**kwargs)
self.serializer = Serializer.from_params(kwargs.get('params'))
self.id = obj_id
self._data = None

View File

@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
# Copyright 2015 Mirantis, 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.
import json
import mock
import six
import yaml
from fuelclient.cli.serializers import Serializer
from fuelclient.tests import base
class TestSerializers(base.UnitTestCase):
DATA = {
'a': 1,
'b': {
'c': [2, 3, 4],
'd': 'string',
}
}
def test_get_from_params(self):
params_to_formats = (
('yaml', 'yaml'),
('json', 'json'),
('xyz', Serializer.default_format),
)
for param, format in params_to_formats:
params = mock.Mock(serialization_format=format)
serializer = Serializer.from_params(params)
self.assertEqual(serializer.format, format)
def test_serialize(self):
deserializers = {'json': json.loads, 'yaml': yaml.load}
for format, deserialize in six.iteritems(deserializers):
serialized = Serializer(format).serialize(self.DATA)
self.assertEqual(self.DATA, deserialize(serialized))
def test_deserialize(self):
serializers = {'json': json.dumps, 'yaml': yaml.safe_dump}
for format, serialize in six.iteritems(serializers):
serialized = serialize(self.DATA)
deserialized = Serializer(format).deserialize(serialized)
self.assertEqual(self.DATA, deserialized)

View File

@@ -22,7 +22,7 @@ from fuelclient.tests import base
class TestHandlers(base.BaseTestCase):
def test_env_action(self):
#check env help
# check env help
help_msgs = ["usage: fuel environment [-h]",
"[--list | --set | --delete | --create | --update]",
"optional arguments:", "--help", "--list", "--set",
@@ -31,13 +31,13 @@ class TestHandlers(base.BaseTestCase):
"--network-mode", "--nst", "--net-segment-type",
"--deployment-mode", "--update", "--env-update"]
self.check_all_in_msg("env --help", help_msgs)
#no clusters
# no clusters
self.check_for_rows_in_table("env")
for action in ("set", "create", "delete"):
self.check_if_required("env {0}".format(action))
#list of tuples (<fuel CLI command>, <expected output of a command>)
# list of tuples (<fuel CLI command>, <expected output of a command>)
expected_stdout = \
[(
"env --create --name=TestEnv --release=1",

View File

@@ -14,7 +14,11 @@
# License for the specific language governing permissions and limitations
# under the License.
import json
import mock
import requests_mock
import yaml
from fuelclient.cli.actions import base
from fuelclient.cli import error
@@ -62,3 +66,63 @@ class TestBaseAction(base_tests.UnitTestCase):
m_os.path.exists.return_value = True
self.action.full_path_directory('/base/path', 'subdir')
self.assertEqual(m_os.mkdir.call_count, 0)
@requests_mock.mock()
class TestFuelVersion(base_tests.UnitTestCase):
VERSION = {
"astute_sha": "16b252d93be6aaa73030b8100cf8c5ca6a970a91",
"release": "6.0",
"build_id": "2014-12-26_14-25-46",
"build_number": "58",
"auth_required": True,
"fuellib_sha": "fde8ba5e11a1acaf819d402c645c731af450aff0",
"production": "docker",
"nailgun_sha": "5f91157daa6798ff522ca9f6d34e7e135f150a90",
"release_versions": {
"2014.2-6.0": {
"VERSION": {
"ostf_sha": "a9afb68710d809570460c29d6c3293219d3624d4",
"astute_sha": "16b252d93be6aaa73030b8100cf8c5ca6a970a91",
"release": "6.0",
"build_id": "2014-12-26_14-25-46",
"build_number": "58",
"fuellib_sha": "fde8ba5e11a1acaf819d402c645c731af450aff0",
"production": "docker",
"nailgun_sha": "5f91157daa6798ff522ca9f6d34e7e135f150a90",
"api": "1.0",
"fuelmain_sha": "81d38d6f2903b5a8b4bee79ca45a54b76c1361b8",
"feature_groups": [
"mirantis"
]
}
}
},
"api": "1.0",
"fuelmain_sha": "81d38d6f2903b5a8b4bee79ca45a54b76c1361b8",
"feature_groups": [
"mirantis"
]
}
def test_return_yaml(self, mrequests):
mrequests.get('/api/v1/version', json=self.VERSION)
with mock.patch('sys.stderr') as mstderr:
with self.assertRaises(SystemExit):
self.execute_wo_auth(['fuel', '--fuel-version', '--yaml'])
args, _ = mstderr.write.call_args
with self.assertRaisesRegexp(
ValueError, 'No JSON object could be decoded'):
json.loads(args[0])
yaml.load(args[0])
def test_return_json(self, mrequests):
mrequests.get('/api/v1/version', json=self.VERSION)
with mock.patch('sys.stderr') as mstderr:
with self.assertRaises(SystemExit):
self.execute_wo_auth(['fuel', '--fuel-version', '--json'])
args, _ = mstderr.write.call_args
json.loads(args[0])

View File

@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
# Copyright 2015 Mirantis, 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.
import mock
from fuelclient.tests import base
class TestParser(base.UnitTestCase):
def test_choose_only_one_format(self):
with mock.patch('sys.stderr') as mstderr:
with self.assertRaises(SystemExit):
self.execute_wo_auth(['fuel', '--json', '--yaml'])
args, _ = mstderr.write.call_args
self.assertRegexpMatches(
args[0],
r"argument (--json|--yaml): not allowed with"
r" argument (--yaml|--json)")

View File

@@ -22,13 +22,13 @@ from fuelclient.tests import base
class TestSnapshot(base.UnitTestCase):
@patch('fuelclient.cli.actions.snapshot.SnapshotTask.get_default_config')
@patch('sys.stdout.write')
def test_get_default_config(self, mwrite, mconf):
@patch('sys.stdout')
def test_get_default_config(self, mstdout, mconf):
mconf.return_value = {'key': 'value'}
self.execute(['fuel', 'snapshot', '--conf'])
self.assertEqual(mwrite.call_args_list, [call('key: value\n')])
self.assertEqual(mstdout.write.call_args_list, [call('key: value\n')])
@patch('fuelclient.cli.actions.snapshot.SnapshotTask.start_snapshot_task')
@patch('fuelclient.cli.actions.snapshot.'