-
Notifications
You must be signed in to change notification settings - Fork 47
ARIA-92 Automatic operation task configuration #95
Conversation
Can one of the admins verify this patch? |
""" | ||
|
||
__tablename__ = 'task' | ||
|
||
__private_fields__ = ['node_fk', | ||
'relationship_fk', | ||
'plugin_fk', | ||
'execution_fk', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The diff algorithm borked here, if you look at the file it makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
aria/modeling/orchestration.py
Outdated
retry_interval = Column(Float, default=0) | ||
ignore_failure = Column(Boolean, default=False) | ||
due_at = Column(DateTime, nullable=False, index=True, default=datetime.utcnow()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you already do the separation to "state" fields and other, this should probably be under state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does due_at
ever change after creation? I guess you're implying that it does...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup :)
super(OperationTask, self).__init__() | ||
|
||
self.actor = actor | ||
self.actor_type = actor_type |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why are any changes needed here (besides runs_on)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because we decided together that the logic that was here should not be in operation creation, but in service instance creation.
@@ -63,24 +63,48 @@ interface_types: | |||
pre_configure_source: | |||
description: >- | |||
Operation to pre-configure the source endpoint. | |||
_extensions: | |||
default_dependencies: | |||
- edge > source |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as we mentioned, these should not be a part of the dependencies. it should sit elsewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
furthermore, for the time being, it's not "default", but rather the only place where one can determine where the operation shall run.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought you said that if there is no cost to allowing the user to override it, we could.
@@ -583,48 +583,49 @@ def test_node_model_creation(self, node_template_storage, is_valid, name, runtim | |||
node_template_storage.service.list()[0] | |||
|
|||
|
|||
class TestNodeIP(object): | |||
class TestNodeHostAddress(object): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why change this everywhere?
should really talk about massive renames first, seems like a waste to have to revert everything if that'll be needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That whole part of the code (finding the "ip") had to be refactored because it was essentially broken. As I was changing it anyway it made sense to change the name. It's really not hard to change back.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
implementation: | ||
primary: changed.sh | ||
dependencies: | ||
- edge > target |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove
@@ -145,6 +145,24 @@ def consume(self): | |||
self.context.modeling.instance.validate_capabilities() | |||
|
|||
|
|||
class FindHosts(Consumer): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this implemented as another consumer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just how phases work right now in "the parser". We keep talking about all of this moving into a separate "instantiation" module, but this is what we have for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
from ...parser.consumption import ConsumptionContext | ||
|
||
|
||
def configure_operation(operation): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
almost everything in this module needs to go. this method is okay, the rest should be transparent to the execution plugin
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, where do you suggest moving them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
aria/modeling/service_instance.py
Outdated
@@ -1407,6 +1441,19 @@ def operation_template_fk(cls): | |||
|
|||
# endregion | |||
|
|||
def configure(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this definitely cant be here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, so where? We don't have these separate module yet and I did not think I should be creating it as part of this PR.
@@ -180,6 +181,16 @@ def validate_capabilities(self): | |||
satisfied = False | |||
return satisfied | |||
|
|||
def find_hosts(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
more methods on the models which we'll want to remove in a short while :(
i mean, this method isn't relevant for an instantiated service - it's only used for its (and its dependencies) creation. why would it sit as part of the instance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not true: this phase will need be run every time there is a change in the topology. This is part of the reason I don't think instantiation
is the right name for this module we talk about, since it would actually be used in many cases after instantiation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
return host | ||
return None | ||
|
||
self.host = _find_host(self) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this dynamic? are we expecting this to change for an existing node?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know what you mean by "dynamic" here. These functions must be called during the initial instantiation phase, and then will need to be called again whenever there is a change in the topology.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
if host is not None: | ||
return host | ||
for the_relationship in node.inbound_relationships: | ||
if (the_relationship.target_capability is not None) and \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems odd, target_capability is basicaly self.capabilities, isn't it? why would i need to traverse all the inbound relationships an test each ones target_capability?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit hard to explain. We tend to think of the host relationship as "contained in", but TOSCA also allows for "hosts a" kind of relationship, for example for features. See here: to find out the loadbalancer node's host, we need to look inbound that it is a feature of Nginx.
So, bottom line is that this function has to be bi-recursive, both inbound and outbound. Yay. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
@@ -529,6 +524,32 @@ def validate_capabilities(self): | |||
satisfied = False | |||
return satisfied | |||
|
|||
def find_host(self): | |||
def _find_host(node): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why would i need to define a helper method, and not just call the_relationship.target_node.find_host()
for example?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we do that then we'll be calling find_host
again on again on the same nodes that are targets of different relationships. So, to do that efficiently we would need some extra flag (found_host
?) to signify that we don't have to do that again for that node. It made more sense to me to have it set once and only once per node.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
im still a bit confused by this myself - caching the host is definitely a good idea, but it looks here like the internal method _find_host is called recursively repeatedly.
I'm not sure this is exactly the same as what Maxim meant, but I think I'd expect each node's host finding method to run once at most, so that would mean either "host" is a self-initializing property, or a field initialized at its constructor, or you could use the "find_host" method, but basically what i'm missing here is the check for whether the value has been cached already or not.
i'd expect it to be more like:
def find_host(self):
def _find_host(node):
....
host = the_relationship.target_node.find_host()
....
if not self.host: # or indeed use some extra flag
self.host = _find_host(node)
return self.host
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
aria/modeling/service_template.py
Outdated
plugin = self.plugin_specification.find_plugin(workflow_context.model.plugin.list()) | ||
except context.exceptions.ContextException: | ||
pass | ||
# TODO |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we need to talk about this. :) I don't know how else to find other models. Is there a current context for SQLAlchemy?
@@ -583,48 +583,49 @@ def test_node_model_creation(self, node_template_storage, is_valid, name, runtim | |||
node_template_storage.service.list()[0] | |||
|
|||
|
|||
class TestNodeIP(object): | |||
class TestNodeHostAddress(object): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no new tests were written for the new feature.
fb80405
to
3bb7e4c
Compare
aria/modeling/service_instance.py
Outdated
if host_ip_property: | ||
return host_ip_property.value | ||
def host_address(self): | ||
if self.host: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
&&? :P
aria/modeling/service_instance.py
Outdated
def host_address(self): | ||
if self.host: | ||
if self.host.runtime_properties: | ||
return self.host.runtime_properties.get('host_address') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
while I understand your reasoning to using "host_address" instead of "ip" everywhere, the actual runtime property that gets set by plugins remains to be "ip". this can't change at this stage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
def plugin_specification_fk(cls): | ||
"""For Operation one-to-one to PluginSpecification""" | ||
return relationship.foreign_key('plugin_specification', nullable=True) | ||
def plugin_fk(cls): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does it make sense for operation no longer to have an fk for plugin specification at all?
is that going to remain for operation template?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
aria/modeling/service_instance.py
Outdated
dependencies = Column(modeling_types.StrictList(item_cls=basestring)) | ||
executor = Column(Text) | ||
max_retries = Column(Integer) | ||
retry_interval = Column(Integer) | ||
|
||
def configure(self): | ||
from . import models | ||
if (self.implementation is None) or (self.interface is None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how can interface be None here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will be None if the operation is attached to Service
as a workflow :) I know you suggested a different model entirely for workflows ... that would be a separate JIRA perhaps.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, right. Please add a tiny comment next to it explaining that this case is only relevant for "Workflow operations" because I'm afraid I'll wonder about it the next time I see it as well 😅 thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
aria/modeling/service_instance.py
Outdated
return | ||
|
||
if self.plugin is None: | ||
arguments = configure_operation(self) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please use execution_plugin.instantiation.configure_operation
or so, there are enough configure_operation
calls in this module as it is and it's easily confusing :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
@@ -98,8 +98,8 @@ topology_template: | |||
#token: { get_property: [ HOST, flavor_name ] } | |||
interfaces: | |||
Maintenance: | |||
enable: juju > charm.maintenance_on | |||
disable: juju > charm.maintenance_off | |||
enable: juju \> charm.maintenance_on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wait so what's this symbol about again? :)
Why are we not using the same symbol as we do for dependencies
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've put the backslash to escape it and disable it for now. :/ Since the Juju plugin is not installed, the tests that use this would fail otherwise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm changing this back. :) Will explain the new behavior on Slack.
@@ -78,6 +78,11 @@ def get_inherited_capability_definitions(context, presentation, for_presentation | |||
#capability_definitions[capability_name] = capability_definition | |||
else: | |||
capability_definition = our_capability_definition._clone(for_presentation) | |||
if isinstance(capability_definition._raw, basestring): | |||
# # Make sure we have a dict |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
double comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
) | ||
if target_operation_name: | ||
operations.append( | ||
OperationTask.for_relationship(relationship=relationship, | ||
interface_name=interface_name, | ||
operation_name=target_operation_name, | ||
runs_on='target') | ||
operation_name=target_operation_name,) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tuple?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 (merge leftover -- though it's totally legal in Python and in fact is a tuple!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes i know, I just figured this is not what you wanted to have there 😅 anyway i see its fixed, thanks
level=Issue.BETWEEN_TYPES) | ||
|
||
relationship_edge = operation._get_extensions(context).get('relationship_edge') | ||
if relationship_edge is not None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what if it is None? where do we set the default? I think either we don't or I've missed it :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
None is the default -- it means that nowhere did the user set the edge explicitly set one way or the other (the column is nullable).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't you think it'd be better to have it be set to source
by default?
I assume when it's None
we should treat it as source
, so I think it'd be better if the data model reflects this design decision, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We agreed that it's better to keep this "edge was not set" information.
model.plugin_specification = service_template.plugin_specifications.get(plugin_name) | ||
if model.plugin_specification is None: | ||
context.validation.report( | ||
'unknown plugin "%s" specified in operation implementation: %s' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this message clear enough about the fact that the plugin is not properly declared as a policy in the service template (rather than the question of whether it exists or not in the plugin repository)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
model.plugin_specification, model.implementation = \ | ||
parse_implementation_string(context, service_template, prop.value) | ||
plugin_name, model.implementation = split_prefix(prop.value) | ||
if plugin_name is not None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
duplicate code, why not refactor this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
|
||
implementation = implementation[index+1:].strip() | ||
return plugin_specification, implementation | ||
def set_nested(the_dict, keys, value): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you add a few comments here, possibly an example of input --> output, just so it's clearer? thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
aria/modeling/service_template.py
Outdated
# moved to. There, we will probably have a context with a storage manager. Until then, | ||
# this is the only potentially available context, which of course will only be available | ||
# if we're in a workflow. | ||
plugin = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
redundant
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
aria/modeling/service_template.py
Outdated
@@ -1848,11 +1856,30 @@ def as_raw(self): | |||
|
|||
def instantiate(self, container): | |||
from . import models | |||
from ..orchestrator import context | |||
plugin = None | |||
if self.plugin_specification is not None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand this section.
Operations are instantiated at service creation time. No workflow context should be available at this time.
If a storage model object is required, it should be passed into the instantiate module directly (the current interface is problematic as is, and I've already broken it on the CLI branch in order to pass inputs to service template's instantiate), not taken off some thread local context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are correct, I had a long conversation with Maxim about it. Eventually when we have an instantiation module, it is very likely that the module will have a context. See comment at line 1861.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand and I have seen the comment, but I'm saying the changes I've mentioned need to happen now, not after we have the instantiation module.
More so, I don't understand how this is expected to work anyway, as no execution context is expect to be present at the time this method is called.
if interface is not None: | ||
operation = interface.operations.get(operation_name) | ||
|
||
if operation is None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(FYI this section is refactored elsewhere in the CLI branch, so it will get overridden here too)
'Could not find operation "{0}" on interface "{1}" for {2} "{3}"' | ||
.format(operation_name, interface_name, actor_type, actor.name)) | ||
|
||
if operation.implementation is None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why should an empty operation raise an error (rather than do nothing)?
if a node template has its type as root, and implements create
with a proper implementation but does not use configure
, would it not appear as an empty implementation operation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the meaning of an empty implementation operation? The operation would fail in the orchestrator, seems much better to catch that here and not create that operation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We agreed to keep this behavior for now.
""" | ||
Remote SSH operation via Fabric. | ||
""" | ||
default_user = 'admin' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why these defaults? wouldnt it be better to fail?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, leftover from devs. 👍
arguments['fabric_env']['password'] = ssh.get('password') | ||
arguments['fabric_env']['key'] = ssh.get('key') | ||
arguments['fabric_env']['key_filename'] = ssh.get('key_filename') | ||
if 'address' in ssh: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i assume you came up with this one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I read our code and figured these out. :) They were not well documented anywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
ssh['user'] = default_user | ||
if ('password' not in ssh) and ('key' not in ssh) and ('key_filename' not in ssh): | ||
ssh['password'] = default_password | ||
arguments['use_sudo'] = ssh.get('use_sudo') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not use copy of some sort?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Due to the various validations. The structure is also a little bit different. In the service template, the user doesn't have to write "fabric_env" (or really shouldn't care about our implementation detail at all), the just write "ssh.password".
.format(name, full_type_name(the_type)), | ||
level=validation.Issue.BETWEEN_TYPES) | ||
|
||
def _str_to_bool(value, name): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this and the next method need to sit elsewhere. on CLI branch there's a type
module under utils
, and the dict_to_list
should go to where there's already similar methods.
the validation thats called within these methods can be called before or after them
return {} | ||
_validate_type(value, dict, 'ssh') | ||
for k, v in value.iteritems(): | ||
if k == 'use_sudo': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think it'd probably have been neater to have a dict mapping field-name to type and then validating it rather than having to add here manually every new field that might get introduced
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought it would be more straightforward to do this than have a generic mechanism. Since only this plugin does this (for all the rest the configuration params get merged into inputs) it seemed unnecessary for me to create a generic mechanism.
3bb7e4c
to
a062d12
Compare
model.plugin_specification = service_template.plugin_specifications.get(plugin_name) | ||
if model.plugin_specification is None: | ||
context.validation.report( | ||
'no policy for plugin "%s" specified in operation implementation: %s' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use string format please 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
6e807d9
to
fe5e964
Compare
aria/modeling/service_template.py
Outdated
if self.plugin_specification and self.plugin_specification.enabled: | ||
plugin = self.plugin_specification.plugin | ||
implementation = self.implementation if plugin is not None else None | ||
# "plugin" would be none if a match was not found. In that case, a validation error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
im not sure i understood this comment.
i think you're trying to say that plugin
can't be None here since the test for enabled has already been performed earlier?
the first line of the comment makes it sound like plugin
can be None when it cant
aria/modeling/service_template.py
Outdated
|
||
def coerce_values(self, container, report_issues): | ||
pass | ||
|
||
def instantiate(self, container): | ||
from . import models |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wait, so instantiate is now empty? 😕
fe5e964
to
b5a373a
Compare
b5a373a
to
a7e7826
Compare
Main changes:
runs_on
field from task model and APIconfiguration
field inOperationTemplate
usingdependencies
in TOSCA syntax