Merge "Add Sync template feature to KB-client."

This commit is contained in:
Zuul 2017-12-28 10:01:01 +00:00 committed by Gerrit Code Review
commit 491d800a15
5 changed files with 299 additions and 2 deletions

View File

@ -80,13 +80,51 @@ You can see the list of available commands typing::
$ kingbird --help
About sync template command
============================
Provide the input file in .yaml/.yml/.json to sync multiple resource-types to multiple regions
Sample input file for .yaml/.yml
--------------------------------
Eg::
Sync:
- resource_type: fake_resource_type
resources:
- fake_resource_1
- fake_resource_2
source_region:
- fake_source_region
target_region:
- fake_target_region_1
- fake_target_region_2
Sample input file for .json
--------------------------
Eg::
{
"Sync": [
{
"resource_type": "fake_resource_type",
"resources": [
"fake_resource_1",
"fake_resource_2"
],
"source_region":["fake_source_region"],
"target_region":["fake_target_region_1","fake_target_region_2"]
}
]
}
Useful Links
============
* Free software: Apache license
* `PyPi`_ - package installation
* `Launchpad project`_ - release management
* `Blueprints`_ - feature specifications
* `Bugs`_ - issue tracking
* `Bugs` - issue tracking
* `Source`_
* `How to Contribute`_
* `Documentation`_

View File

@ -12,6 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import yaml
from osc_lib.command import command
from kingbirdclient.commands.v1 import base
@ -142,6 +145,72 @@ class ResourceSync(base.KingbirdLister):
return kingbird_client.sync_manager.sync_resources(**kwargs)
class TemplateResourceSync(base.KingbirdLister):
"""Sync multiple resource-types to multiple regions."""
def _get_format_function(self):
return sync_format
def get_parser(self, parsed_args):
parser = super(TemplateResourceSync, self).get_parser(parsed_args)
parser.add_argument(
'--template',
required=True,
help='Specify the name of an input file in .yaml/.yml/.json.'
)
return parser
def _get_resources(self, parsed_args):
kingbird_client = self.app.client_manager.sync_engine
kwargs = dict()
sync_template = parsed_args.template
if sync_template.endswith('.yaml') or sync_template.endswith('.yml') \
or sync_template.endswith('.json'):
try:
if sync_template.endswith('.json'):
with open(sync_template) as json_data:
data = json.load(json_data)
else:
data = yaml.load(open(sync_template))
except Exception:
raise exceptions.TemplateError(
'Syntactical errors in the template')
else:
raise exceptions.TemplateError(
'Provide a template with a valid extension(.yaml/.yml/.json)')
for iteration in data['Sync']:
if 'source_region' not in iteration:
raise exceptions.TemplateError(
'source_region parameter is missing in template')
if not iteration['source_region']:
raise exceptions.TemplateError(
'source_region parameter value is missing')
if 'target_region' not in iteration:
raise exceptions.TemplateError(
'target_region parameter is missing in template')
if not iteration['target_region']:
raise exceptions.TemplateError(
'target_region parameter value is missing')
if 'resource_type' not in iteration:
raise exceptions.TemplateError(
'resource_type parameter is missing in template')
if not iteration['resource_type']:
raise exceptions.TemplateError(
'resource_type parameter value is missing')
if 'resources' not in iteration:
raise exceptions.TemplateError(
'resources parameter is missing in template')
if not iteration['resources']:
raise exceptions.TemplateError(
'resources parameter value is missing')
kwargs.update(data)
return kingbird_client.sync_manager.sync_resources(**kwargs)
class SyncList(base.KingbirdLister):
"""List Sync Jobs."""

View File

@ -54,3 +54,12 @@ class APIException(Exception):
super(APIException, self).__init__(error_message)
self.error_code = error_code
self.error_message = error_message
class TemplateError(KingbirdClientException):
message = "Insufficient parameters in the template"
code = "TEMPLATE_ERROR_EXCEPTION"
def __init__(self, message=None):
if message:
self.message = message

View File

@ -471,6 +471,7 @@ class KingbirdShell(app.App):
'sync create': sm.ResourceSync,
'sync list': sm.SyncList,
'sync show': sm.SyncShow,
'sync template': sm.TemplateResourceSync,
'sync delete': sm.SyncDelete,
}

View File

@ -13,12 +13,13 @@
# limitations under the License.
import mock
import os
from oslo_utils import timeutils
from oslo_utils import uuidutils
from kingbirdclient.api.v1 import sync_manager as sm
from kingbirdclient.commands.v1 import sync_manager as sync_cmd
from kingbirdclient import exceptions
from kingbirdclient.tests import base
TIME_NOW = timeutils.utcnow().isoformat()
@ -31,6 +32,41 @@ FAKE_SOURCE_REGION = 'fake_region_1'
FAKE_TARGET_REGION = 'fake_region_2'
FAKE_RESOURCE_TYPE = 'fake_resource'
tempdef = """Sync:
- resource_type: fake_resource_type
resources:
- fake_resource_1
- fake_resource_2
source_region:
- fake_source_region
target_region:
- fake_target_region_1
- fake_target_region_2
"""
RESOURCE_TYPE_INDEX = tempdef.index('resource_type:')
RESOURCE_INDEX = tempdef.index('resources:')
SOURCE_INDEX = tempdef.index('source_region:')
TARGET_INDEX = tempdef.index('target_region:')
tempdefjson = """{
"Sync": [
{
"resource_type": "fake_resource_type",
"resources": [
"fake_resource_1",
"fake_resource_2"
],
"source_region":["fake_source_region"],
"target_region":["fake_target_region_1","fake_target_region_2"]
}
]
}
"""
RESOURCE_TYPE_INDEX_JSON = tempdefjson.index('"resource_type"')
RESOURCE_INDEX_JSON = tempdefjson.index('"resources"')
SOURCE_INDEX_JSON = tempdefjson.index('"source_region"')
TARGET_INDEX_JSON = tempdefjson.index(",", SOURCE_INDEX_JSON)
RESOURCE_DICT = {
'ID': ID,
'STATUS': FAKE_STATUS,
@ -80,6 +116,7 @@ SYNC_RESOURCEMANAGER = sm.Resource(mock, id=RESOURCE_DICT['ID'],
class TestCLISyncManagerV1(base.BaseCommandTest):
"""Testcases for sync command."""
def test_sync_jobs_list(self):
self.client.sync_manager.list_sync_jobs.return_value = [SYNCMANAGER]
@ -196,3 +233,146 @@ class TestCLISyncManagerV1(base.BaseCommandTest):
'--target', FAKE_TARGET_REGION,
'--force'])
self.assertEqual([(ID, FAKE_STATUS, TIME_NOW)], actual_call[1])
def test_template_resource_sync_with_template_yaml(self):
with open('test_template.yaml', 'w') as f:
f.write(tempdef)
f.close()
self.client.sync_manager.sync_resources.\
return_value = [SYNC_RESOURCEMANAGER]
actual_call = self.call(
sync_cmd.TemplateResourceSync, app_args=[
'--template', 'test_template.yaml'])
self.assertEqual([(ID, FAKE_STATUS, TIME_NOW)], actual_call[1])
os.remove("test_template.yaml")
def test_template_resource_sync_with_template_yml(self):
with open('test_template.yml', 'w') as f:
f.write(tempdef)
f.close()
self.client.sync_manager.sync_resources.\
return_value = [SYNC_RESOURCEMANAGER]
actual_call = self.call(
sync_cmd.TemplateResourceSync, app_args=[
'--template', 'test_template.yml'])
self.assertEqual([(ID, FAKE_STATUS, TIME_NOW)], actual_call[1])
os.remove("test_template.yml")
def test_template_resource_sync_with_template_json(self):
with open('test_template.json', 'w') as f:
f.write(tempdefjson)
f.close()
self.client.sync_manager.sync_resources.\
return_value = [SYNC_RESOURCEMANAGER]
actual_call = self.call(
sync_cmd.TemplateResourceSync, app_args=[
'--template', 'test_template.json'])
self.assertEqual([(ID, FAKE_STATUS, TIME_NOW)], actual_call[1])
os.remove("test_template.json")
def test_template_resource_sync_without_template(self):
self.client.sync_manager.sync_resources.\
return_value = [SYNC_RESOURCEMANAGER]
self.assertRaises(
SystemExit, self.call, sync_cmd.TemplateResourceSync, app_args=[])
def test_template_resource_sync_invalid_extension(self):
self.assertRaises(
exceptions.TemplateError, self.call,
sync_cmd.TemplateResourceSync,
app_args=['--template', 'test_template.yzx'])
def test_template_resource_sync_source_missing_yaml(self):
temp = tempdef.replace(tempdef[SOURCE_INDEX:TARGET_INDEX], "")
with open('test_source_missing_template.yaml', 'w') as f:
f.write(temp)
f.close()
self.assertRaises(
exceptions.TemplateError, self.call,
sync_cmd.TemplateResourceSync,
app_args=['--template', 'test_source_missing_template.yaml'])
os.remove("test_source_missing_template.yaml")
def test_template_resource_sync_target_missing_yaml(self):
temp = tempdef.replace(tempdef[TARGET_INDEX:], "")
with open('test_target_missing_template.yaml', 'w') as f:
f.write(temp)
f.close()
self.assertRaises(
exceptions.TemplateError, self.call,
sync_cmd.TemplateResourceSync,
app_args=['--template', 'test_target_missing_template.yaml'])
os.remove("test_target_missing_template.yaml")
def test_template_resource_sync_resource_type_missing_yaml(self):
temp = tempdef.replace(tempdef[RESOURCE_TYPE_INDEX:RESOURCE_INDEX], "")
with open('test_resource_type_missing_template.yaml', 'w') as f:
f.write(temp)
f.close()
self.assertRaises(
exceptions.TemplateError, self.call,
sync_cmd.TemplateResourceSync,
app_args=['--template',
'test_resource_type_missing_template.yaml'])
os.remove("test_resource_type_missing_template.yaml")
def test_template_resource_sync_resources_missing_yaml(self):
temp = tempdef.replace(tempdef[RESOURCE_INDEX:SOURCE_INDEX], "")
with open('test_resource_missing_template.yaml', 'w') as f:
f.write(temp)
f.close()
self.assertRaises(
exceptions.TemplateError, self.call,
sync_cmd.TemplateResourceSync,
app_args=['--template', 'test_resource_missing_template.yaml'])
os.remove("test_resource_missing_template.yaml")
def test_template_resource_sync_source_missing_json(self):
temp = tempdefjson.replace(
tempdefjson[SOURCE_INDEX_JSON:TARGET_INDEX_JSON + 1], "")
with open('test_source_missing_template.json', 'w') as f:
f.write(temp)
f.close()
self.assertRaises(
exceptions.TemplateError, self.call,
sync_cmd.TemplateResourceSync,
app_args=['--template',
'test_source_missing_template.json'])
os.remove("test_source_missing_template.json")
def test_template_resource_sync_target_missing_json(self):
temp = tempdefjson.replace(
tempdefjson[TARGET_INDEX_JSON:tempdefjson.index("}")], "")
with open('test_target_missing_template.json', 'w') as f:
f.write(temp)
f.close()
self.assertRaises(
exceptions.TemplateError, self.call,
sync_cmd.TemplateResourceSync,
app_args=['--template', 'test_target_missing_template.json'])
os.remove("test_target_missing_template.json")
def test_template_resource_sync_resource_type_missing_json(self):
temp = tempdefjson.replace(
tempdefjson[RESOURCE_TYPE_INDEX_JSON:RESOURCE_INDEX_JSON], "")
with open('test_resource_type_missing_template.json', 'w') as f:
f.write(temp)
f.close()
self.assertRaises(
exceptions.TemplateError, self.call,
sync_cmd.TemplateResourceSync,
app_args=['--template',
'test_resource_type_missing_template.json'])
os.remove("test_resource_type_missing_template.json")
def test_template_resource_sync_resources_missing_json(self):
temp = tempdefjson.replace(
tempdefjson[RESOURCE_INDEX_JSON:SOURCE_INDEX_JSON], "")
with open('test_resource_missing_template.json', 'w') as f:
f.write(temp)
f.close()
self.assertRaises(
exceptions.TemplateError, self.call,
sync_cmd.TemplateResourceSync,
app_args=['--template', 'test_resource_missing_template.json'])
os.remove("test_resource_missing_template.json")