-
Notifications
You must be signed in to change notification settings - Fork 897
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
create service template REST api #12594
Conversation
config/api.yml
Outdated
@@ -1386,6 +1388,8 @@ | |||
:post: | |||
- :name: edit | |||
:identifier: catalogitem_edit | |||
- :name: create | |||
:identifier: catalogitem_new |
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 create action at the resource level ? probably only needed on collection.
@bzwei @gmcculloug I worked on this based off of the Still need to add in the case for generic / composite. |
@@ -4,5 +4,56 @@ class ServiceTemplatesController < BaseController | |||
include Subcollections::Tags | |||
include Subcollections::ResourceActions | |||
include Subcollections::ServiceRequests | |||
|
|||
def create_resource(_type, _id, data) |
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.
Shouldn't the method be named create_service_template
unless resource
is a term used by the API
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's a term used by the API
|
||
private | ||
|
||
def create_atomic(data) |
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 name is also misleading. Looks like here you are creating type with a workflow. Generic types are also atomic.
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.
generic can also be created through this method. will add in a test for that.
|
||
def validate_atomic_data(data) | ||
raise 'Must provide request info' unless data.key?('request_info') | ||
raise 'Provisioning Entry Point is required' unless data['request_info']['fqname'] |
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.
Do we really want to enforce an provisioning entry point? I think now the backend have default value for all provisioning types.
In general I don't know whether it is necessary to add validation for too many fields, because even if some fields are missing at creation time, they can be added through update API.
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.
Sounds good to me! Was just adding in the validations I saw in the other controller.
@miq-bot remove_label wip |
ra.update_attributes(:dialog => dialog, :fqname => request_data[action[:param_key]]) | ||
end | ||
end | ||
service_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.
although no harm, what's the purpose of returning service_template
?
def create_atomic(data) | ||
service_template = ServiceTemplate.new(data.except('request_info')) | ||
if data.key?('request_info') | ||
service_template.add_resource(create_service_template_request(data['request_info'])) |
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 you need to make data['request_info']
with more structures instead of a plain hash. You code does not create service_template properly for generic_orchestration
, generic_ansible_tower
, and generic_container
(to come). For these types different entries are added to service_resources table.
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.
@bzwei exactly why I wanted this feedback 😃 is there a place in the code already doing this where I can follow along make it work for the REST api? Didn't quite see it in the existing controller...
{:name => 'Reconfigure', :param_key => 'reconfigure_fqname'}, | ||
{:name => 'Retirement', :param_key => 'retire_fqname'} | ||
].each do |action| | ||
unless request_data[action[:param_key]].nil? |
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.
Even if the request_data does not have an action you'll still need to create an resource_action with default entry point. Look at ServiceTemplate
and its subclasses for default entry points.
service_template = ServiceTemplate.new(data.except('request_info')) | ||
dialog = nil | ||
if data.key?('request_info') | ||
service_template.add_resource(create_service_template_request(data['request_info'])) |
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.
@jntullo Adding resources to a generic service_template, you can follow the examples in catalog_controller. Basically there is no common code. You need to call generic type specific methods to add them: https://github.com/ManageIQ/manageiq/blob/master/app/controllers/catalog_controller.rb#L1507 https://github.com/ManageIQ/manageiq/blob/master/app/controllers/catalog_controller.rb#L1509 https://github.com/ManageIQ/manageiq/blob/master/app/controllers/catalog_controller.rb#L1514
service_template.job_template = unless request_info['template_id'].nil? | ||
ConfigurationScript.find(request_info['template_id']) | ||
end | ||
end |
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.
Quite a bit of code here. Any of these generic enough (base/foundation/verification) so they can live in the model at app/models/service_template.rb ?
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.
Agreed @abellotti - once I had the logic down, was going to see about moving some of it into the model. 👍
end | ||
|
||
def add_orchestration_template_vars(service_template, request_info) | ||
return unless service_template.kind_of?(ServiceTemplateOrchestration) |
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 never work because the type is always ServiceTemplate
according to https://github.com/jntullo/manageiq/blob/4bf6307a7f67bb9a5223048fa122607ecc653e6a/app/controllers/api/service_templates_controller.rb#L19
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 it does, it takes in a type parameter. It's also tested in the tests (checks that the count of ServiceTemplateOrchestration increases)
end | ||
|
||
def add_ansible_tower_job_template_vars(service_template, request_info) | ||
return unless service_template.kind_of?(ServiceTemplateAnsibleTower) |
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.
same as above
request | ||
end | ||
|
||
def set_provision_action(service_template, dialog, request_info) |
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 do you need to separate provision from retirement and reconfigure?
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.
Mostly separated because the provisioning entrypoint takes in the service type, the others don't, and when I put them together rubocop yelled about complexity. Will try and refactor though.
app/models/service_template.rb
Outdated
|
||
def set_retire_reconfigure_actions(config_info, dialog) | ||
[ | ||
{:name => 'Reconfigure', :param_key => 'reconfigure_fqname', :method => 'default_reconfiguration_entry_point'}, |
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 can combine two methods by adding :args => []
to the hash, so it looks like
{:name => 'Provision', :param_key..., :method ..., :args => [service_type]},
{:name => 'Reconfigure', :param_key..., :method..., :args =>[]},
...
Then self.class.send(action[:method], *action[:args])
app/models/service_template.rb
Outdated
@@ -258,6 +258,11 @@ def self.default_reconfiguration_entry_point | |||
nil | |||
end | |||
|
|||
def set_resource_actions(config_info, dialog) |
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 am not very convinced that we want this method in ServiceTemplate
, especially here the keys in config_info
are hard codes, obviously inherited from API controller. If we really want it here, can you document the use of this method?
@gmcculloug any thoughts?
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.
Thoughts @gmcculloug?
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.
Thanks for the reminder. Discussed with @bzwei before the break and I am not sure the best place for this code at the moment.
@abellotti Any suggestions on where this method should live.
ping @gmcculloug |
app/models/service_template.rb
Outdated
end | ||
save! | ||
end | ||
|
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.
Hi @jntullo, I've looked at this with @gmcculloug a bit,
this method can stay here in app/models/service_template.rb, seems generic enough, the only thing that took a while to understand was the config_info.
Can you change the signature in this method (and update the calling sequence in API accordingly) as follows:
def set_resource_actions(ae_endpoints, dialog)
where ae_endpoints would be the following hash:
{
:provisioning => "/.....",
:retirement => "/.....",
:reconfigure => "/....."
}
non-existent/nil entries honored as you have it today.
Thanks,
Thanks @jntullo for the update. LGTM!! @gmcculloug @bzwei ok with you ? |
ping @gmcculloug @bzwei |
service_template = ServiceTemplate.new(data.except('config_info')) | ||
config_info = data['config_info'].except('ae_endpoints') || {} | ||
case service_template.type | ||
when 'ServiceTemplateOrchestration' |
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 are going to add new type ServiceTemplateContainer
, and possibly other types in the future. How do we remember to come back to update this case...when
?
The API controller attempts to create a service template, but the actual ServiceTemplate has many subclasses. Need a generic way to handle subtypes.
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.
@bzwei good question. Almost sounds like it should be a part of the initialization process
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.
@jntullo I am currently developing logic to create service template for the new AnsiblePlaybook service type. I think for each type we should have a class method to create a full stack of catalog item definition, meaning ServiceTemplate, ServiceResource, and ResourceActions. We can work together to move us to the same direction.
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 sounds good to me @bzwei
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.
@bzwei are you okay with this PR as is, and then enhancing it as the other work gets completed?
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.
@jntullo We can merge this one if it becomes a blocker for other development work; otherwise I would think this is the right place to refactor.
I started to add the following to service_template.rb
# create ServiceTemplate and supporting ServiceResources and ResourceActions
def self.create_catalog_item(options)
end
The implementation is empty right now. You can move logic to this block and corresponding subclasses.
The method should return an instance of ServiceTemplate
or subclass.
The method name can be debatable; I could not think of a better 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.
any update on this PR ?
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.
@abellotti I will be updating this soon (this week) based off of the class methods @bzwei and I have been working on
|
||
def create_resource(_type, _id, data) | ||
raise 'Must provide a type' unless data['type'] | ||
type = data['type'].constantize |
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.
Here we have both _type
and type
. Consider to rename either one to something more meaningful.
:instance_type => [flavor.id, flavor.name], | ||
:src_ems_id => [ems.id, ems.name], | ||
:provision => { | ||
:fqname => ra1.fqname, |
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.
In reality you still need a service dialog for provisioning.
LGTM except for the two minor comments. |
Please rebase this PR to incorporate #13846 in order to ensure api.yml integrity ❤️ ❤️ ❤️ |
ping @abellotti |
@@ -4,5 +4,12 @@ class ServiceTemplatesController < BaseController | |||
include Subcollections::Tags | |||
include Subcollections::ResourceActions | |||
include Subcollections::ServiceRequests | |||
|
|||
def create_resource(type, _id, data) | |||
catalog_item_type = data['type'].try(:constantize) || collection_class(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.
we really shouldn't be running constantize on user specified data. Prefer if model returned the class for us. Are these known classes, or subclasses of known base classes ?
see app/models/miq_request's class_from_request_data() that we call from API. Might need something similar here.
app/models/service_template.rb
Outdated
"openstack" => :ServiceTemplate, | ||
"redhat" => :ServiceTemplate, | ||
"vmware" => :ServiceTemplate | ||
}.freeze |
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.
@bzwei Can you verify that these are accurate?
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.
wouldn't logic like the following work here ?
return klass if request_type == ServiceTemplate.name
request_klass = ServiceTemplate.descendants.detect { |sub_klass| request_type == sub_klass.name }
return request_klass if request_klass
since:
ServiceTemplate.descendants.collect(&:name)
=> ["ServiceTemplateAnsibleTower", "ServiceTemplateGeneric", "ServiceTemplateOrchestration", "ServiceTemplateAnsiblePlaybook"]
This pull request is not mergeable. Please rebase and repush. |
app/models/service_template.rb
Outdated
@@ -75,6 +88,12 @@ def self.create_catalog_item(options, auth_user) | |||
end | |||
end | |||
|
|||
def self.class_from_request_data(data) | |||
request_type = data['prov_type'] | |||
model_symbol = PROV_TYPE_TO_MODEL[request_type] || raise(ArgumentError, 'Invalid prov_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.
We can/should avoid lookup from a hash and prefer naming convention. If the type starts with generic_
, we can do an auto conversion from generic_abc
to ServiceTemplateAbc
; for all the rest, directly ServiceTemplate
.
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.
sounds good, thanks @bzwei
This pull request is not mergeable. Please rebase and repush. |
remove resource action always set entry point
moving some methods to the model
default to ServiceTemplate
Checked commits jntullo/manageiq@ec70f64~...98d8237 with ruby 2.2.6, rubocop 0.47.1, and haml-lint 0.20.0 |
request_type = data['prov_type'] | ||
if request_type.include?('generic_') | ||
generic_type = request_type.split('generic_').last | ||
"ServiceTemplate#{generic_type.camelize}".constantize |
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.
/cc @bzwei @gmcculloug you guys ok with this constantize ?
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.
👍
@jntullo Good job! |
create service template REST api for use in the SUI - only doing atomic initially. Will follow up with composite.
@miq-bot add_label api, enhancement, wip, euwe/no
@miq-bot assign @abellotti