Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add updates section #198

Merged
merged 4 commits into from
Sep 25, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 69 additions & 38 deletions solar/solar/core/resource/virtual_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
from jinja2 import Template, Environment, meta

from solar.core import provider
from solar.core import resource
from solar.core import signals
from solar.core.resource import load as load_resource
from solar.core.resource import Resource
from solar.events.api import add_event
from solar.events.controls import React, Dep

Expand Down Expand Up @@ -57,20 +58,22 @@ def create_resource(name, base_path, args=None, virtual_resource=None):

# List args init with empty list. Elements will be added later
args = {key: (value if not isinstance(value, list) else []) for key, value in args.items()}
r = resource.Resource(
r = Resource(
name, base_path, args=args, tags=[], virtual_resource=virtual_resource
)
return r


def create_virtual_resource(vr_name, template):
template_resources = template['resources']
template_events = template.get('events', {})
template_resources = template.get('resources', [])
template_events = template.get('events', [])
resources_to_update = template.get('updates', [])

created_resources = create_resources(template_resources)
events = parse_events(template_events)
for event in events:
add_event(event)
update_resources(resources_to_update)
return created_resources


Expand Down Expand Up @@ -113,16 +116,36 @@ def create_resources(resources):
cwd = os.getcwd()
for r in resources:
resource_name = r['id']
base_path = os.path.join(cwd, r['from'])
args = r['values']
new_resources = create(resource_name, base_path, args)
from_path = r.get('from', None)
base_path = os.path.join(cwd, from_path)
new_resources = create(resource_name, base_path)
created_resources += new_resources

if not is_virtual(base_path):
add_connections(resource_name, args)
update_inputs(resource_name, args)
return created_resources


def update_resources(resources):
for r in resources:
resource_name = r['id']
args = r['values']
update_inputs(resource_name, args)


def update_inputs(child, args):
child = load_resource(child)
connections, assignments = parse_inputs(args)
for c in connections:
mapping = {}
parent = load_resource(c['parent'])
events = c['events']
mapping[c['parent_input']] = c['child_input']
signals.connect(parent, child, mapping, events)

child.update(assignments)


def parse_events(events):
parsed_events = []
for event in events:
Expand All @@ -140,38 +163,46 @@ def parse_events(events):
return parsed_events


def add_connections(resource_name, args):
def parse_inputs(args):
connections = []
for receiver_input, arg in args.items():
assignments = {}
for r_input, arg in args.items():
if isinstance(arg, list):
for item in arg:
c = parse_connection(resource_name, receiver_input, item)
connections.append(c)
c, a = parse_list_input(r_input, arg)
connections.extend(c)
assignments.update(a)
else:
c = parse_connection(resource_name, receiver_input, arg)
connections.append(c)

connections = [c for c in connections if c is not None]
for c in connections:
parent = resource.load(c['parent'])
child = resource.load(c['child'])
events = c['events']
mapping = {c['parent_input'] : c['child_input']}
signals.connect(parent, child, mapping, events)
if isinstance(arg, basestring) and '::' in arg:
c = parse_connection(r_input, arg)
connections.append(c)
else:
assignments[r_input] = arg
return connections, assignments


def parse_connection(receiver, receiver_input, element):
if isinstance(element, basestring) and '::' in element:
emitter, src = element.split('::', 1)
try:
src, events = src.split('::')
if events == 'NO_EVENTS':
events = False
except ValueError:
events = None
return {'child': receiver,
'child_input': receiver_input,
'parent' : emitter,
'parent_input': src,
'events' : events
}
def parse_list_input(r_input, args):
connections = []
assignments = {}
for arg in args:
if isinstance(arg, basestring) and '::' in arg:
c = parse_connection(r_input, arg)
connections.append(c)
else:
# Not supported yet
raise Exception('Only connections are supported in lists')
return connections, assignments


def parse_connection(child_input, element):
parent, parent_input = element.split('::', 1)
try:
parent_input, events = parent_input.split('::')
if events == 'NO_EVENTS':
events = False
except ValueError:
events = None
return {'child_input': child_input,
'parent' : parent,
'parent_input': parent_input,
'events' : events
}
5 changes: 5 additions & 0 deletions solar/solar/test/resource_fixtures/update.yaml.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
id: simple_multinode
updates:
- id: node1
values:
ip: '10.0.0.4'
42 changes: 30 additions & 12 deletions solar/solar/test/test_virtual_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ def bad_event_type():
'''
return yaml.load(StringIO(events))

def test_create_path_does_not_exists():
with pytest.raises(Exception) as excinfo:
vr.create('node1', '/path/does/not/exists')
err = 'Base resource does not exist: /path/does/not/exists'
assert str(excinfo.value) == err

def test_create_resource():
node_path = os.path.join(
Expand All @@ -68,6 +73,26 @@ def test_create_virtual_resource(tmpdir):
resources = vr.create('nodes', str(vr_file))
assert len(resources) == 2

def test_update(tmpdir):
# XXX: make helper for it
base_path = os.path.join(
os.path.dirname(os.path.realpath(__file__)),
'resource_fixtures')
vr_node_tmpl_path = os.path.join(base_path, 'nodes.yaml.tmpl')
vr_update_tmpl_path = os.path.join(base_path, 'update.yaml.tmpl')
update_path = os.path.join(base_path, 'update')
node_resource_path = os.path.join(base_path, 'node')
with open(vr_node_tmpl_path) as f:
vr_data = f.read().format(resource_path=node_resource_path)
with open(vr_update_tmpl_path) as f:
update_data = f.read().format(resource_path=update_path)
vr_file = tmpdir.join('nodes.yaml')
vr_file.write(vr_data)
update_file = tmpdir.join('update.yaml')
update_file.write(update_data)
resources = vr.create('nodes', str(vr_file))
vr.create('updates', str(update_file))
assert resources[0].args['ip'] == '10.0.0.4'

def test_parse_events(good_events):
events =[Dep(parent='service1', parent_action='run',
Expand All @@ -92,30 +117,23 @@ def test_add_connections(mocker, resources):
'servers': ['node1::ip', 'node2::ip'],
'alias': 'ser1'
}
vr.add_connections('service1', args)
vr.update_inputs('service1', args)
assert mocked_signals.connect.call_count == 3


def test_parse_connection():
correct_connection = {'child': 'host_file',
'child_input': 'ip',
correct_connection = {'child_input': 'ip',
'parent' : 'node1',
'parent_input': 'ip',
'events' : None
}
connection = vr.parse_connection('host_file', 'ip', 'node1::ip')
connection = vr.parse_connection('ip', 'node1::ip')
assert correct_connection == connection

def test_parse_connection_disable_events():
correct_connection = {'child': 'host_file',
'child_input': 'ip',
correct_connection = {'child_input': 'ip',
'parent' : 'node1',
'parent_input': 'ip',
'events' : False
}
connection = vr.parse_connection('host_file', 'ip', 'node1::ip::NO_EVENTS')
connection = vr.parse_connection('ip', 'node1::ip::NO_EVENTS')
assert correct_connection == connection

def test_parse_connection_no_connection():
connection = vr.parse_connection('host_file', 'ip', '10.0.0.2')
assert None == connection