Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files
ARIA-99 Straightforward end-to-end tests for parser and built-in work…
…flow
  • Loading branch information
tliron committed Feb 8, 2017
1 parent e282f23 commit d35d09a35422add7c3ee34053add05f41b8de1ba
Show file tree
Hide file tree
Showing 20 changed files with 1,114 additions and 9 deletions.
@@ -17,7 +17,9 @@
CLI Entry point
"""

import os
import logging
import tempfile

from .. import install_aria_extensions
from ..logger import (
@@ -100,7 +102,7 @@ def main():
create_logger(
handlers=[
create_console_log_handler(),
create_file_log_handler(file_path='/tmp/aria_cli.log'),
create_file_log_handler(file_path=os.path.join(tempfile.gettempdir(), 'aria_cli.log')),
],
level=logging.INFO)
with AriaCli() as aria:
@@ -13,16 +13,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import tempfile

from requests import Session
from requests.exceptions import ConnectionError
from requests.exceptions import (ConnectionError, InvalidSchema)
from cachecontrol import CacheControl
from cachecontrol.caches import FileCache

from .exceptions import LoaderException, DocumentNotFoundException
from .loader import Loader

SESSION = None
SESSION_CACHE_PATH = '/tmp'
SESSION_CACHE_PATH = os.path.join(tempfile.gettempdir(), 'aria_requests')


class RequestLoader(Loader):
@@ -53,6 +56,8 @@ def open(self):

try:
self._response = SESSION.get(self.uri, headers=self.headers)
except InvalidSchema as e:
raise DocumentNotFoundException('document not found: "%s"' % self.uri, cause=e)
except ConnectionError as e:
raise LoaderException('request connection error: "%s"' % self.uri, cause=e)
except Exception as e:
@@ -66,8 +66,9 @@ def open(self):
except DocumentNotFoundException:
# Try prefixes in order
for prefix in self._prefixes:
if as_file(prefix) is not None:
uri = os.path.join(prefix, self.location.uri)
prefix_as_file = as_file(prefix)
if prefix_as_file is not None:
uri = os.path.join(prefix_as_file, self.location.uri)
else:
uri = urljoin(prefix, self.location.uri)
try:
@@ -17,10 +17,10 @@
"""
import os
import shutil
from multiprocessing import RLock
from contextlib import contextmanager
from functools import partial
from distutils import dir_util # https://github.com/PyCQA/pylint/issues/73; pylint: disable=no-name-in-module
from multiprocessing import RLock

from aria.storage import (
api,
@@ -16,13 +16,29 @@
import os
import urlparse


_IS_WINDOWS = (os.name == 'nt')


def as_file(uri):
"""
If the URI is a file (either the :code:`file` scheme or no scheme), then returns the absolute
If the URI is a file (either the :code:`file` scheme or no scheme), then returns the normalized
path. Otherwise, returns None.
"""

if _IS_WINDOWS:
# We need this extra check in Windows before urlparse because paths might have a drive
# prefix, e.g. "C:" which will be considered a scheme for urlparse below
path = uri.replace('/', '\\')
if os.path.exists(path):
return os.path.normpath(path)

url = urlparse.urlparse(uri)
if (not url.scheme) or (url.scheme == 'file'):
return os.path.abspath(url.path)
scheme = url.scheme
if (not scheme) or (scheme == 'file'):
path = url.path
if _IS_WINDOWS:
path = path.replace('/', '\\')
return os.path.normpath(path)

return None
File renamed without changes.
@@ -0,0 +1,60 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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 sys

from aria.orchestrator.runner import Runner
from aria.orchestrator.workflows.builtin import BUILTIN_WORKFLOWS
from aria.parser.modeling.storage import initialize_storage
from aria.utils.imports import import_fullname
from aria.utils.collections import OrderedDict

from tests.parser.service_templates import consume_node_cellar


WORKFLOW_POLICY_INTERNAL_PROPERTIES = ('function', 'implementation', 'dependencies')


def test_install():
_workflow('install')


def test_custom():
_workflow('maintenance_on')


def _workflow(workflow_name):
context, _ = consume_node_cellar()

# TODO: this logic will eventually stabilize and be part of the ARIA API,
# likely somewhere in aria.orchestrator.workflows
if workflow_name in BUILTIN_WORKFLOWS:
workflow_fn = import_fullname('aria.orchestrator.workflows.builtin.' + workflow_name)
inputs = {}
else:
policy = context.modeling.instance.policies[workflow_name]
sys.path.append(policy.properties['implementation'].value)

workflow_fn = import_fullname(policy.properties['function'].value)
inputs = OrderedDict([
(k, v.value) for k, v in policy.properties.iteritems()
if k not in WORKFLOW_POLICY_INTERNAL_PROPERTIES
])

def _initialize_storage(model_storage):
initialize_storage(context, model_storage, 1)

runner = Runner(workflow_name, workflow_fn, inputs, _initialize_storage, 1)
runner.run()
@@ -0,0 +1,40 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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 tests.parser.service_templates import consume_node_cellar


def test_validation():
consume_node_cellar('validate')


def test_validation_no_cache():
consume_node_cellar('validate', False)


def test_presentation():
consume_node_cellar('presentation')


def test_model():
consume_node_cellar('model')


def test_types():
consume_node_cellar('types')


def test_instance():
consume_node_cellar('instance')
@@ -0,0 +1,14 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
@@ -0,0 +1,30 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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 aria.utils.caching import cachedmethod

from .utils import (get_uri, create_context, create_consumer)


def consume_node_cellar(consumer_class_name='instance', cache=True):
cachedmethod.ENABLED = cache
uri = get_uri('node-cellar', 'node-cellar.yaml')
context = create_context(uri)
context.args.append('--inputs=' + get_uri('node-cellar', 'inputs.yaml'))
consumer, dumper = create_consumer(context, consumer_class_name)
consumer.consume()
context.validation.dump_issues()
assert not context.validation.has_issues
return context, dumper
@@ -0,0 +1,78 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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 os

from aria.parser.loading import UriLocation
from aria.parser.consumption import (
ConsumptionContext,
ConsumerChain,
Read,
Validate,
Model,
Types,
Inputs,
Instance
)
from aria.utils.imports import import_fullname

from tests.resources import DIR


SERVICE_TEMPLATES_DIR = os.path.join(DIR, 'service_templates')


def get_uri(*args):
return os.path.join(SERVICE_TEMPLATES_DIR, *args)


def create_context(uri,
loader_source='aria.parser.loading.DefaultLoaderSource',
reader_source='aria.parser.reading.DefaultReaderSource',
presenter_source='aria.parser.presentation.DefaultPresenterSource',
presenter=None,
debug=False):
context = ConsumptionContext()
context.loading.loader_source = import_fullname(loader_source)()
context.reading.reader_source = import_fullname(reader_source)()
context.presentation.location = UriLocation(uri) if isinstance(uri, basestring) else uri
context.presentation.presenter_source = import_fullname(presenter_source)()
context.presentation.presenter_class = import_fullname(presenter)
context.presentation.print_exceptions = debug
return context


def create_consumer(context, consumer_class_name):
consumer = ConsumerChain(context, (Read, Validate))
dumper = None
if consumer_class_name == 'validate':
dumper = None
elif consumer_class_name == 'presentation':
dumper = consumer.consumers[0]
elif consumer_class_name == 'model':
consumer.append(Model)
elif consumer_class_name == 'types':
consumer.append(Model, Types)
elif consumer_class_name == 'instance':
consumer.append(Model, Inputs, Instance)
else:
consumer.append(Model, Inputs, Instance)
consumer.append(import_fullname(consumer_class_name))

if dumper is None:
# Default to last consumer
dumper = consumer.consumers[-1]

return consumer, dumper
@@ -0,0 +1,3 @@
openstack_credential:
user: username
token: password

0 comments on commit d35d09a

Please sign in to comment.