Hi, this patch adds very simple UI for launching a deployment from a deployable show page (tabs deployment).
From: Jan Provaznik jprovazn@redhat.com
Added frontend_realm dependency and deployment launch method --- src/app/models/deployable.rb | 9 +++++ src/app/models/deployment.rb | 33 ++++++++++++++++++++ src/db/migrate/20110419121917_update_deployment.rb | 9 +++++ src/spec/models/deployment_spec.rb | 16 +++++++++ 4 files changed, 67 insertions(+), 0 deletions(-) create mode 100644 src/db/migrate/20110419121917_update_deployment.rb
diff --git a/src/app/models/deployable.rb b/src/app/models/deployable.rb index f3d954b..25a8737 100644 --- a/src/app/models/deployable.rb +++ b/src/app/models/deployable.rb @@ -83,4 +83,13 @@ class Deployable < ActiveRecord::Base deployments.all? {|d| d.destroyable? } end
+ def launchable? + return false if assemblies.empty? + assemblies.each do |a| + return false if a.templates.empty? + # TODO: should we check if there is an uploaded image for each template in + # assembly? + end + return true + end end diff --git a/src/app/models/deployment.rb b/src/app/models/deployment.rb index 02324f8..85698d6 100644 --- a/src/app/models/deployment.rb +++ b/src/app/models/deployment.rb @@ -48,6 +48,7 @@ class Deployment < ActiveRecord::Base has_many :instances
belongs_to :realm + belongs_to :frontend_realm belongs_to :owner, :class_name => "User", :foreign_key => "owner_id"
has_many :permissions, :as => :permission_object, :dependent => :destroy, @@ -60,6 +61,7 @@ class Deployment < ActiveRecord::Base validates_presence_of :name validates_uniqueness_of :name, :scope => :pool_id validates_length_of :name, :maximum => 1024 + validates_presence_of :owner_id
before_destroy :destroyable?
@@ -102,4 +104,35 @@ class Deployment < ActiveRecord::Base instances.all? {|i| i.destroyable? } end
+ def launch(hw_profiles, user) + errors = [] + raise "the deployable must have at least one assembly and each assembly must have at least one template" unless deployable.launchable? + deployable.assemblies.each do |assembly| + # TODO: for now we try to start all instances even if some of them fails + begin + Instance.transaction do + instance = Instance.create!( + :deployment => self, + :name => "#{name}/#{assembly.name}", + :frontend_realm => realm, + :pool => pool, + :assembly => assembly, + :state => Instance::STATE_NEW, + :owner => user, + :hardware_profile => HardwareProfile.find(hw_profiles[assembly.id.to_s]) + ) + instance.assign_owner_roles(user) + task = InstanceTask.create!({:user => user, + :task_target => instance, + :action => InstanceTask::ACTION_CREATE}) + condormatic_instance_create(task) + end + rescue + logger.error $! + logger.error $!.backtrace.join("\n ") + errors << "#{assembly.name}: #{$!}" + end + end + errors + end end diff --git a/src/db/migrate/20110419121917_update_deployment.rb b/src/db/migrate/20110419121917_update_deployment.rb new file mode 100644 index 0000000..a33c4ef --- /dev/null +++ b/src/db/migrate/20110419121917_update_deployment.rb @@ -0,0 +1,9 @@ +class UpdateDeployment < ActiveRecord::Migration + def self.up + add_column :deployments, :frontend_realm_id, :integer + end + + def self.down + remove_column :deployments, :frontend_realm_id + end +end diff --git a/src/spec/models/deployment_spec.rb b/src/spec/models/deployment_spec.rb index c8eda13..0d91cd9 100644 --- a/src/spec/models/deployment_spec.rb +++ b/src/spec/models/deployment_spec.rb @@ -55,5 +55,21 @@ describe Deployment do @deployment.get_action_list.should eql(["start", "stop", "reboot"]) end
+ it "should launch instances when launching deployment" do + hwp = Factory(:mock_hwp1) + hwp_ids = {} + @deployment.save! + @deployment.deployable.assemblies.each {|a| hwp_ids[a.id.to_s] = hwp.id} + @deployment.instances.should be_empty + errs = @deployment.launch(hwp_ids, Factory(:user)) + errs.should be_empty + @deployment.instances.count.should == @deployment.deployable.assemblies.count + end + + it "should not launch a deployment if deployable has not assemblies" do + @deployment.save! + @deployment.deployable.assemblies = [] + lambda {@deployment.launch}.should raise_exception + end
end
From: Jan Provaznik jprovazn@redhat.com
--- .../resources/deployments_controller.rb | 57 +++++++++++++++++++- src/app/util/condormatic.rb | 5 ++- .../image_factory/deployables/_properties.haml | 4 ++ src/app/views/resources/deployments/_list.haml | 18 ++++++- src/app/views/resources/deployments/new.haml | 23 ++++++++ src/app/views/resources/instances/_list.haml | 2 +- src/app/views/resources/instances/_properties.haml | 17 ++++-- src/config/routes.rb | 2 +- src/features/deployable.feature | 17 ++++++ src/features/step_definitions/deployable_steps.rb | 11 ++++ 10 files changed, 145 insertions(+), 11 deletions(-) create mode 100644 src/app/views/resources/deployments/new.haml
diff --git a/src/app/controllers/resources/deployments_controller.rb b/src/app/controllers/resources/deployments_controller.rb index 3e76bb0..4357d48 100644 --- a/src/app/controllers/resources/deployments_controller.rb +++ b/src/app/controllers/resources/deployments_controller.rb @@ -5,7 +5,42 @@ class Resources::DeploymentsController < ApplicationController def index end
+ def new + require_privilege(Privilege::CREATE, Deployment) + @deployment = Deployment.new(:deployable_id => params[:deployable_id]) + if @deployment.deployable.assemblies.empty? + flash[:warning] = "Deployable must have at least one assembly" + redirect_to resources_deployments_path + else + init_new_deployment_attrs + end + end + + def create + require_privilege(Privilege::CREATE, Deployment) + @deployment = Deployment.new(params[:deployment]) + @deployment.owner = current_user + if @deployment.save + flash[:notice] = "Deployment launched" + errors = @deployment.launch(params[:hw_profiles], current_user) + unless errors.empty? + flash[:error] = { + :summary => "Failed to launch following assemblies:", + :failures => errors + } + end + redirect_to resources_deployment_path(@deployment) + else + flash.now[:warning] = "Deployment launch failed" + init_new_deployment_attrs + render :new + end + end + def show + @deployment = Deployment.find(params[:id]) + require_privilege(Privilege::VIEW, @deployment) + init_new_deployment_attrs @tab_captions = ['Properties', 'Instances', 'Provider Services', 'Required Services', 'History', 'Permissions'] @details_tab = params[:details_tab].blank? ? 'properties' : params[:details_tab] respond_to do |format| @@ -30,5 +65,25 @@ class Resources::DeploymentsController < ApplicationController { :name => "Heath Metric", :sort_attr => :health }, { :name => "Pool", :sort_attr => "pool.name" } ] + @pools = Pool.list_for_user(current_user, Privilege::CREATE, :target_type => Deployment) + @deployments = Deployment.all(:include => :owner, + :conditions => {:pool_id => @pools}, + :order => (params[:order_field] || 'name') +' '+ (params[:order_dir] || 'asc') + ) + end + + def init_new_deployment_attrs + @pools = Pool.list_for_user(@current_user, Privilege::CREATE, :target_type => Deployment) + @realms = FrontendRealm.all + # FIXME: temporary for debugging + #arch = @deployment.deployable.assemblies.first.templates.first.architecture + arch = 'x86_64' + @hardware_profiles = HardwareProfile.all( + :include => :architecture, + :conditions => { + :provider_id => nil, + 'hardware_profile_properties.value' => arch + } + ) end -end \ No newline at end of file +end diff --git a/src/app/util/condormatic.rb b/src/app/util/condormatic.rb index c095ff5..7857cd2 100644 --- a/src/app/util/condormatic.rb +++ b/src/app/util/condormatic.rb @@ -63,7 +63,10 @@ def condormatic_instance_create(task) Rails.logger.error "DeltacloudRealmId = $$(realm_key)\n" end
- requirements = "requirements = front_end_hardware_profile_id == "#{instance.hardware_profile.id}" && image == "#{instance.template.id}"" + # TODO: for assemblies we grab first template in an assembly + template_id = instance.template ? instance.template.id : instance.assembly.templates.first.id + requirements = "requirements = front_end_hardware_profile_id == "#{instance.hardware_profile.id}" && image == "#{template_id}"" + requirements += " && realm == "#{realm.id}"" if realm != nil # Call into the deltacloud quota plugin. This uses a REST API to call back into the # conductor to check quotas as the last thing in the logical AND to match a provider diff --git a/src/app/views/image_factory/deployables/_properties.haml b/src/app/views/image_factory/deployables/_properties.haml index e8b513c..ce50669 100644 --- a/src/app/views/image_factory/deployables/_properties.haml +++ b/src/app/views/image_factory/deployables/_properties.haml @@ -2,3 +2,7 @@ %h2 #{@deployable.name}
= link_to 'Edit', edit_image_factory_deployable_path(@deployable), :class => 'button' + - if @deployable.launchable? + = link_to "Launch", new_resources_deployment_path(:deployable_id => @deployable.id), {:class => 'button'} + - else + The deployable can't be launched - the deployable must have at least one assembly and each assembly must have at least one template. diff --git a/src/app/views/resources/deployments/_list.haml b/src/app/views/resources/deployments/_list.haml index 6f62520..1619f3d 100644 --- a/src/app/views/resources/deployments/_list.haml +++ b/src/app/views/resources/deployments/_list.haml @@ -16,5 +16,21 @@ %span> , = link_to "None", @url_params.merge(:select => 'none')
-%table +%table#deployments_table = sortable_table_header @header + - @deployments.each do |deployment| + %tr + %td + - selected = @url_params[:select] == 'all' + %input{:name => "deployments_selected[]", :type => "checkbox", :value => deployment.id, :id => "deployment_checkbox_#{deployment.id}", :checked => selected } + = link_to deployment.name, resources_deployment_path(deployment) + %td + = deployment.deployable.name + %td + = deployment.owner.login + %td + = deployment.created_at.strftime("%d-%b-%Y %H:%M:%S") + %td + TODO + %td + = deployment.pool.name diff --git a/src/app/views/resources/deployments/new.haml b/src/app/views/resources/deployments/new.haml new file mode 100644 index 0000000..6e5a1ff --- /dev/null +++ b/src/app/views/resources/deployments/new.haml @@ -0,0 +1,23 @@ += error_messages_for 'deployment' +%h2 Launch deployable +- form_for @deployment, :url => resources_deployments_path do + = hidden_field :deployment, :deployable_id + %ul + %li + = label :deployment, :name + = text_field :deployment, :name + %li + = label :deployment, :pool + = select :deployment, :pool_id, @pools.map {|p| [ p.name, p.id ]}, { :include_blank => true } + %li + = label :deployment, :frontend_realm + = select :deployment, :frontend_realm_id, @realms.map {|r| [ r.name, r.id ]}, { :include_blank => true } + %p + %h3 Assemblies hardware profiles + %ul + - @deployment.deployable.assemblies.each do |assembly| + = label_tag "hw_profiles[#{assembly.id}]", assembly.name + = select_tag "hw_profiles[#{assembly.id}]", options_from_collection_for_select(@hardware_profiles, 'id', 'name') + + = submit_tag 'Cancel', :name => 'cancel' + = submit_tag 'Launch', :name => 'launch' diff --git a/src/app/views/resources/instances/_list.haml b/src/app/views/resources/instances/_list.haml index 157dc97..7a6cbc4 100644 --- a/src/app/views/resources/instances/_list.haml +++ b/src/app/views/resources/instances/_list.haml @@ -17,7 +17,7 @@ %input{:name => 'instance_selected[]', :type => 'checkbox', :value => inst.id, :id => "instance_checkbox_#{inst.id}", :checked => selected } = link_to inst.name, resources_instance_path(inst) %td= inst.state - %td= inst.template.name + %td= inst.template ? inst.template.name : "#{inst.assembly.name} (assembly)" %td= inst.public_addresses %td= inst.provider_account ? inst.provider_account.provider.name : '' %td= owner_name(inst) diff --git a/src/app/views/resources/instances/_properties.haml b/src/app/views/resources/instances/_properties.haml index 995cc00..2d93dbf 100644 --- a/src/app/views/resources/instances/_properties.haml +++ b/src/app/views/resources/instances/_properties.haml @@ -14,16 +14,21 @@ %li = label_tag :private_addresses, 'Private Addresses' %span= @instance.private_addresses - %li - = label_tag :operating_system, 'Operating system' - %span= "#{@instance.template.xml.platform} #{@instance.template.xml.platform_version}" + - if @instance.template + %li + = label_tag :operating_system, 'Operating system' + %span= "#{@instance.template.xml.platform} #{@instance.template.xml.platform_version}" + %li + = label_tag :base_template, 'Base Template' + %span= @instance.template.name + - else + %li + = label_tag :assembly, 'Assembly' + %span= @instance.assembly.name %li = label_tag :provider, 'Provider' %span= @instance.provider_account.provider.name if @instance.provider_account %li - = label_tag :base_template, 'Base Template' - %span= @instance.template.name - %li = label_tag :architecture, 'Architecture' %span= @instance.hardware_profile.architecture.value %li diff --git a/src/config/routes.rb b/src/config/routes.rb index 54cb7c1..2b44595 100644 --- a/src/config/routes.rb +++ b/src/config/routes.rb @@ -44,7 +44,7 @@ ActionController::Routing::Routes.draw do |map| map.namespace 'image_factory' do |r| r.resources :assemblies r.resources :image_imports - r.resources :deployables, :collection => { :multi_destroy => :delete }, :member => { :pick_assemblies => :get, :remove_assemblies => :delete, :add_assemblies => :put } + r.resources :deployables, :collection => { :multi_destroy => :delete }, :member => { :pick_assemblies => :get, :remove_assemblies => :delete, :add_assemblies => :put, :launch => :post } r.resources :templates, :collection => {:collections => :get, :add_selected => :get, :metagroup_packages => :get, :remove_package => :get, :multi_destroy => :delete} r.connect "/builds/update_status.:format", :controller => :builds, :action => :update_status r.resources :builds, :collection => { :delete => :delete, :upload => :get, :retry => :post } diff --git a/src/features/deployable.feature b/src/features/deployable.feature index 7e1da9a..c34e987 100644 --- a/src/features/deployable.feature +++ b/src/features/deployable.feature @@ -6,6 +6,7 @@ Feature: Manage Deployables Background: Given I am an authorised user And I am logged in + And There is a mock pulp repository
Scenario: List deployables Given I am on the homepage @@ -72,3 +73,19 @@ Feature: Manage Deployables And I press "Remove Selected" Then I should see "Assemblies removed." And I should not see "Apache" + + Scenario: Create and launch a deployment from a deployable + Given there is a factory deployable named "test1" + And I am on the image factory deployables page + And there is "mock_profile" conductor hardware profile + And there is "mock_realm" frontend realm + And there is "mock_pool" pool + When I follow "test1" + And I follow "Launch" + Then I should be on the new resources deployment page + When I fill in "deployment_name" with "depl1" + And I select "mock_pool" from "deployment_pool_id" + And I select "mock_realm" from "deployment_frontend_realm_id" + And I select default hardware profile for assemblies in "test1" + And I press "Launch" + Then I should see "Deployment launched" diff --git a/src/features/step_definitions/deployable_steps.rb b/src/features/step_definitions/deployable_steps.rb index d4b6af6..1077cf5 100644 --- a/src/features/step_definitions/deployable_steps.rb +++ b/src/features/step_definitions/deployable_steps.rb @@ -18,3 +18,14 @@ When /^I check the "([^"]*)" deployable$/ do |name| deployable = Deployable.find_by_name(name) check("deployable_checkbox_#{deployable.id}") end + +Given /^there is a factory deployable named "([^"]*)"$/ do |arg1| + Factory(:deployable, :name => arg1) +end + +When /^I select default hardware profile for assemblies in "([^"]*)"$/ do |arg1| + deployable = Deployable.find_by_name(arg1) + deployable.assemblies.each do |a| + select('mock_profile', :from => "hw_profiles_#{a.id}") + end +end
aeolus-devel@lists.fedorahosted.org