Merge "Change the mistralclient for Mistral action pack"

This commit is contained in:
Jenkins
2016-02-29 21:38:07 +00:00
committed by Gerrit Code Review
15 changed files with 355 additions and 3 deletions

View File

@@ -15,7 +15,7 @@
import six
from mistralclient.api import base
from mistralclient import utils
urlparse = six.moves.urllib.parse
@@ -30,6 +30,10 @@ class ActionManager(base.ResourceManager):
def create(self, definition, scope='private'):
self._ensure_not_empty(definition=definition)
# If the specified definition is actually a file, read in the
# definition file
definition = utils.get_contents_if_file(definition)
resp = self.client.http_client.post(
'/actions?scope=%s' % scope,
definition,
@@ -45,6 +49,10 @@ class ActionManager(base.ResourceManager):
def update(self, definition, scope='private'):
self._ensure_not_empty(definition=definition)
# If the specified definition is actually a file, read in the
# definition file
definition = utils.get_contents_if_file(definition)
resp = self.client.http_client.put(
'/actions?scope=%s' % scope,
definition,

View File

@@ -17,6 +17,7 @@ import json
import six
from mistralclient.api import base
from mistralclient import utils
class Environment(base.Resource):
@@ -39,6 +40,12 @@ class EnvironmentManager(base.ResourceManager):
resource_class = Environment
def create(self, **kwargs):
# Check to see if the file name or URI is being passed in. If so,
# read it's contents first.
if 'file' in kwargs:
file = kwargs['file']
kwargs = utils.load_content(utils.get_contents_if_file(file))
self._ensure_not_empty(name=kwargs.get('name', None),
variables=kwargs.get('variables', None))
@@ -49,6 +56,12 @@ class EnvironmentManager(base.ResourceManager):
return self._create('/environments', kwargs)
def update(self, **kwargs):
# Check to see if the file name or URI is being passed in. If so,
# read it's contents first.
if 'file' in kwargs:
file = kwargs['file']
kwargs = utils.load_content(utils.get_contents_if_file(file))
name = kwargs.get('name', None)
self._ensure_not_empty(name=name)

View File

@@ -14,6 +14,7 @@
# limitations under the License.
from mistralclient.api import base
from mistralclient import utils
class Workbook(base.Resource):
@@ -26,6 +27,10 @@ class WorkbookManager(base.ResourceManager):
def create(self, definition):
self._ensure_not_empty(definition=definition)
# If the specified definition is actually a file, read in the
# definition file
definition = utils.get_contents_if_file(definition)
resp = self.client.http_client.post(
'/workbooks',
definition,
@@ -40,6 +45,10 @@ class WorkbookManager(base.ResourceManager):
def update(self, definition):
self._ensure_not_empty(definition=definition)
# If the specified definition is actually a file, read in the
# definition file
definition = utils.get_contents_if_file(definition)
resp = self.client.http_client.put(
'/workbooks',
definition,
@@ -67,6 +76,10 @@ class WorkbookManager(base.ResourceManager):
def validate(self, definition):
self._ensure_not_empty(definition=definition)
# If the specified definition is actually a file, read in the
# definition file
definition = utils.get_contents_if_file(definition)
resp = self.client.http_client.post(
'/workbooks/validate',
definition,

View File

@@ -16,6 +16,7 @@
import six
from mistralclient.api import base
from mistralclient import utils
urlparse = six.moves.urllib.parse
@@ -31,6 +32,10 @@ class WorkflowManager(base.ResourceManager):
def create(self, definition, scope='private'):
self._ensure_not_empty(definition=definition)
# If the specified definition is actually a file, read in the
# definition file
definition = utils.get_contents_if_file(definition)
resp = self.client.http_client.post(
'/workflows?scope=%s' % scope,
definition,
@@ -48,6 +53,10 @@ class WorkflowManager(base.ResourceManager):
url_pre = ('/workflows/%s' % id) if id else '/workflows'
# If the specified definition is actually a file, read in the
# definition file
definition = utils.get_contents_if_file(definition)
resp = self.client.http_client.put(
'%s?scope=%s' % (url_pre, scope),
definition,
@@ -99,6 +108,10 @@ class WorkflowManager(base.ResourceManager):
def validate(self, definition):
self._ensure_not_empty(definition=definition)
# If the specified definition is actually a file, read in the
# definition file
definition = utils.get_contents_if_file(definition)
resp = self.client.http_client.post(
'/workflows/validate',
definition,

View File

@@ -0,0 +1,10 @@
---
version: 2.0
my_action:
base: std.echo
base-input:
output: 'Bye!'
output:
info: <% $.output %>

View File

@@ -0,0 +1,8 @@
{
"name": "env1",
"description": "Test Environment #1",
"scope": "private",
"variables": {
"server": "localhost"
}
}

View File

@@ -0,0 +1,7 @@
---
"name": "env1"
"description": "Test Environment #1"
"scope": "private"
"variables":
"server": "localhost"

View File

@@ -0,0 +1,21 @@
---
version: 2.0
name: wb
workflows:
wf1:
type: direct
input:
- param1
- param2
tasks:
task1:
action: std.http url="localhost:8989"
on-success:
- test_subsequent
test_subsequent:
action: std.http url="http://some_url" server_id=1

View File

@@ -0,0 +1,10 @@
---
version: 2.0
my_wf:
type: direct
tasks:
task1:
action: std.echo output="hello, world"

View File

@@ -11,6 +11,11 @@
# 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 pkg_resources as pkg
from six.moves.urllib import parse
from six.moves.urllib import request
from mistralclient.api.v2 import actions
from mistralclient.tests.unit.v2 import base
@@ -54,6 +59,26 @@ class TestActionsV2(base.BaseClientV2Test):
headers={'content-type': 'text/plain'}
)
def test_create_with_file(self):
mock = self.mock_http_post(content={'actions': [ACTION]})
# The contents of action_v2.yaml must be identical to ACTION_DEF
path = pkg.resource_filename(
'mistralclient',
'tests/unit/resources/action_v2.yaml'
)
actions = self.actions.create(path)
self.assertIsNotNone(actions)
self.assertEqual(ACTION_DEF, actions[0].definition)
mock.assert_called_once_with(
URL_TEMPLATE_SCOPE,
ACTION_DEF,
headers={'content-type': 'text/plain'}
)
def test_update(self):
mock = self.mock_http_put(content={'actions': [ACTION]})
@@ -68,6 +93,29 @@ class TestActionsV2(base.BaseClientV2Test):
headers={'content-type': 'text/plain'}
)
def test_update_with_file_uri(self):
mock = self.mock_http_put(content={'actions': [ACTION]})
# The contents of action_v2.yaml must be identical to ACTION_DEF
path = pkg.resource_filename(
'mistralclient',
'tests/unit/resources/action_v2.yaml'
)
# Convert the file path to file URI
uri = parse.urljoin('file:', request.pathname2url(path))
actions = self.actions.update(uri)
self.assertIsNotNone(actions)
self.assertEqual(ACTION_DEF, actions[0].definition)
mock.assert_called_once_with(
URL_TEMPLATE_SCOPE,
ACTION_DEF,
headers={'content-type': 'text/plain'}
)
def test_list(self):
mock = self.mock_http_get(content={'actions': [ACTION]})

View File

@@ -11,12 +11,17 @@
# 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 collections import OrderedDict
import copy
import json
import pkg_resources as pkg
from six.moves.urllib import parse
from six.moves.urllib import request
from mistralclient.api.v2 import environments
from mistralclient.tests.unit.v2 import base
from mistralclient import utils
ENVIRONMENT = {
@@ -33,6 +38,7 @@ URL_TEMPLATE_NAME = '/environments/%s'
class TestEnvironmentsV2(base.BaseClientV2Test):
def test_create(self):
data = copy.deepcopy(ENVIRONMENT)
@@ -46,6 +52,32 @@ class TestEnvironmentsV2(base.BaseClientV2Test):
mock.assert_called_once_with(URL_TEMPLATE, json.dumps(expected_data))
def test_create_with_json_file_uri(self):
# The contents of env_v2.json must be equivalent to ENVIRONMENT
path = pkg.resource_filename(
'mistralclient',
'tests/unit/resources/env_v2.json'
)
# Convert the file path to file URI
uri = parse.urljoin('file:', request.pathname2url(path))
data = OrderedDict(
utils.load_content(
utils.get_contents_if_file(uri)
)
)
mock = self.mock_http_post(content=data)
file_input = {'file': uri}
env = self.environments.create(**file_input)
self.assertIsNotNone(env)
expected_data = copy.deepcopy(data)
expected_data['variables'] = json.dumps(expected_data['variables'])
mock.assert_called_once_with(URL_TEMPLATE, json.dumps(expected_data))
def test_update(self):
data = copy.deepcopy(ENVIRONMENT)
@@ -59,6 +91,29 @@ class TestEnvironmentsV2(base.BaseClientV2Test):
mock.assert_called_once_with(URL_TEMPLATE, json.dumps(expected_data))
def test_update_with_yaml_file(self):
# The contents of env_v2.json must be equivalent to ENVIRONMENT
path = pkg.resource_filename(
'mistralclient',
'tests/unit/resources/env_v2.json'
)
data = OrderedDict(
utils.load_content(
utils.get_contents_if_file(path)
)
)
mock = self.mock_http_put(content=data)
file_input = {'file': path}
env = self.environments.update(**file_input)
self.assertIsNotNone(env)
expected_data = copy.deepcopy(data)
expected_data['variables'] = json.dumps(expected_data['variables'])
mock.assert_called_once_with(URL_TEMPLATE, json.dumps(expected_data))
def test_list(self):
mock = self.mock_http_get(content={'environments': [ENVIRONMENT]})

View File

@@ -13,6 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import pkg_resources as pkg
from six.moves.urllib import parse
from six.moves.urllib import request
from mistralclient.api import base as api_base
from mistralclient.api.v2 import workbooks
from mistralclient.tests.unit.v2 import base
@@ -79,6 +83,29 @@ class TestWorkbooksV2(base.BaseClientV2Test):
headers={'content-type': 'text/plain'}
)
def test_create_with_file_uri(self):
mock = self.mock_http_post(content=WORKBOOK)
# The contents of wb_v2.yaml must be identical to WB_DEF
path = pkg.resource_filename(
'mistralclient',
'tests/unit/resources/wb_v2.yaml'
)
# Convert the file path to file URI
uri = parse.urljoin('file:', request.pathname2url(path))
wb = self.workbooks.create(uri)
self.assertIsNotNone(wb)
self.assertEqual(WB_DEF, wb.definition)
mock.assert_called_once_with(
URL_TEMPLATE,
WB_DEF,
headers={'content-type': 'text/plain'}
)
def test_update(self):
mock = self.mock_http_put(content=WORKBOOK)
@@ -93,6 +120,26 @@ class TestWorkbooksV2(base.BaseClientV2Test):
headers={'content-type': 'text/plain'}
)
def test_update_with_file(self):
mock = self.mock_http_put(content=WORKBOOK)
# The contents of wb_v2.yaml must be identical to WB_DEF
path = pkg.resource_filename(
'mistralclient',
'tests/unit/resources/wb_v2.yaml'
)
wb = self.workbooks.update(path)
self.assertIsNotNone(wb)
self.assertEqual(WB_DEF, wb.definition)
mock.assert_called_once_with(
URL_TEMPLATE,
WB_DEF,
headers={'content-type': 'text/plain'}
)
def test_list(self):
mock = self.mock_http_get(content={'workbooks': [WORKBOOK]})
@@ -145,6 +192,28 @@ class TestWorkbooksV2(base.BaseClientV2Test):
headers={'content-type': 'text/plain'}
)
def test_validate_with_file(self):
mock = self.mock_http_post(status_code=200,
content={'valid': True})
# The contents of wb_v2.yaml must be identical to WB_DEF
path = pkg.resource_filename(
'mistralclient',
'tests/unit/resources/wb_v2.yaml'
)
result = self.workbooks.validate(path)
self.assertIsNotNone(result)
self.assertIn('valid', result)
self.assertTrue(result['valid'])
mock.assert_called_once_with(
URL_TEMPLATE_VALIDATE,
WB_DEF,
headers={'content-type': 'text/plain'}
)
def test_validate_failed(self):
mock_result = {
"valid": False,

View File

@@ -11,6 +11,11 @@
# 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 pkg_resources as pkg
from six.moves.urllib import parse
from six.moves.urllib import request
from mistralclient.api.v2 import workflows
from mistralclient.tests.unit.v2 import base
@@ -54,6 +59,26 @@ class TestWorkflowsV2(base.BaseClientV2Test):
headers={'content-type': 'text/plain'}
)
def test_create_with_file(self):
mock = self.mock_http_post(content={'workflows': [WORKFLOW]})
# The contents of wf_v2.yaml must be identical to WF_DEF
path = pkg.resource_filename(
'mistralclient',
'tests/unit/resources/wf_v2.yaml'
)
wfs = self.workflows.create(path)
self.assertIsNotNone(wfs)
self.assertEqual(WF_DEF, wfs[0].definition)
mock.assert_called_once_with(
URL_TEMPLATE_SCOPE,
WF_DEF,
headers={'content-type': 'text/plain'}
)
def test_update(self):
mock = self.mock_http_put(content={'workflows': [WORKFLOW]})
@@ -82,6 +107,29 @@ class TestWorkflowsV2(base.BaseClientV2Test):
headers={'content-type': 'text/plain'}
)
def test_update_with_file_uri(self):
mock = self.mock_http_put(content={'workflows': [WORKFLOW]})
# The contents of wf_v2.yaml must be identical to WF_DEF
path = pkg.resource_filename(
'mistralclient',
'tests/unit/resources/wf_v2.yaml'
)
# Convert the file path to file URI
uri = parse.urljoin('file:', request.pathname2url(path))
wfs = self.workflows.update(uri)
self.assertIsNotNone(wfs)
self.assertEqual(WF_DEF, wfs[0].definition)
mock.assert_called_once_with(
URL_TEMPLATE_SCOPE,
WF_DEF,
headers={'content-type': 'text/plain'}
)
def test_list(self):
mock = self.mock_http_get(content={'workflows': [WORKFLOW]})

View File

@@ -14,9 +14,12 @@
# limitations under the License.
import json
import os
import yaml
from six.moves.urllib import parse
from six.moves.urllib import request
from mistralclient import exceptions
@@ -51,3 +54,28 @@ def load_content(content):
def load_file(path):
with open(path, 'r') as f:
return load_content(f.read())
def get_contents_if_file(contents_or_file_name):
"""Get the contents of a file.
If the value passed in is a file name or file URI, return the
contents. If not, or there is an error reading the file contents,
return the value passed in as the contents.
For example, a workflow definition will be returned if either the
workflow definition file name, or file URI are passed in, or the
actual workflow definition itself is passed in.
"""
try:
if parse.urlparse(contents_or_file_name).scheme:
definition_url = contents_or_file_name
else:
path = os.path.abspath(contents_or_file_name)
definition_url = parse.urljoin(
'file:',
request.pathname2url(path)
)
return request.urlopen(definition_url).read().decode('utf8')
except Exception:
return contents_or_file_name

View File

@@ -7,3 +7,4 @@ python-keystoneclient!=1.8.0,!=2.1.0,>=1.6.0 # Apache-2.0
python-openstackclient>=2.1.0 # Apache-2.0
PyYAML>=3.1.0 # MIT
requests!=2.9.0,>=2.8.1 # Apache-2.0
six>=1.9.0 # MIT