refuse to change ec2_instance_id once an EC2 instance has claimed a Node
[arvados.git] / app / models / node.rb
index 9c56c31e47c0b240900811c3f6bfe34497c23538..725344d826d354bd3baf4342576ccd7dd993835f 100644 (file)
@@ -1,4 +1,4 @@
-class Node < ActiveRecord::Base
+class Node < OrvosModel
   include AssignUuid
   include KindAndEtag
   include CommonApiTemplate
@@ -61,6 +61,8 @@ class Node < ActiveRecord::Base
     end
     self.last_ping_at = Time.now
 
+    @bypass_orvos_authorization = true
+
     # Record IP address
     if self.ip_address.nil?
       logger.info "#{self.uuid} ip_address= #{o[:ip]}"
@@ -69,9 +71,14 @@ class Node < ActiveRecord::Base
     end
 
     # Record instance ID if not already known
-    if !self.info[:ec2_instance_id] and o[:ec2_instance_id]
-      self.info[:ec2_instance_id] = o[:ec2_instance_id]
-      `ec2-create-tags #{self.info[:ec2_instance_id]} --tag 'Name=#{self.uuid}'`
+    if o[:ec2_instance_id]
+      if !self.info[:ec2_instance_id] 
+        self.info[:ec2_instance_id] = o[:ec2_instance_id]
+        `ec2-create-tags #{o[:ec2_instance_id]} --tag 'Name=#{self.uuid}'`
+      elsif self.info[:ec2_instance_id] != o[:ec2_instance_id]
+        logger.debug "Multiple nodes have credentials for #{self.uuid}"
+        raise "#{self.uuid} is already running at #{self.info[:ec2_instance_id]} so rejecting ping from #{o[:ec2_instance_id]}"
+      end
     end
 
     # Assign hostname
@@ -93,20 +100,28 @@ class Node < ActiveRecord::Base
       end
     end
 
-    save
+    save!
   end
 
   def start!(ping_url_method)
+    ensure_permission_to_update
     ping_url = ping_url_method.call({ uuid: self.uuid, ping_secret: self.info[:ping_secret] })
-    cmd = ["ec2-run-instances",
-           "--user-data '#{ping_url}'",
-           "-t c1.xlarge -n 1 -g orvos-compute",
-           "--client-token", self.uuid,
-           Rails.configuration.compute_node_ami
-          ].join(' ')
-    self.info[:ec2_start_command] = cmd
-    logger.info "#{self.uuid} ec2_start_command= #{cmd.inspect}"
-    result = `#{cmd} 2>&1`
+    ec2_args = ["--user-data '#{ping_url}'",
+                "-t c1.xlarge -n 1",
+                "-g", Rails.configuration.compute_node_security_group,
+                Rails.configuration.compute_node_ami
+               ]
+    ec2run_cmd = ["ec2-run-instances",
+                  "--client-token", self.uuid,
+                  ec2_args].flatten.join(' ')
+    ec2spot_cmd = ["ec2-request-spot-instances",
+                   "-p #{Rails.configuration.compute_node_spot_bid} --type one-time",
+                   ec2_args].flatten.join(' ')
+    self.info[:ec2_run_command] = ec2run_cmd
+    self.info[:ec2_spot_command] = ec2spot_cmd
+    self.info[:ec2_start_command] = ec2spot_cmd
+    logger.info "#{self.uuid} ec2_start_command= #{ec2spot_cmd.inspect}"
+    result = `#{ec2spot_cmd} 2>&1`
     self.info[:ec2_start_result] = result
     logger.info "#{self.uuid} ec2_start_result= #{result.inspect}"
     result.match(/INSTANCE\s*(i-[0-9a-f]+)/) do |m|
@@ -114,6 +129,11 @@ class Node < ActiveRecord::Base
       self.info[:ec2_instance_id] = instance_id
       `ec2-create-tags #{instance_id} --tag 'Name=#{self.uuid}'`
     end
+    result.match(/SPOTINSTANCEREQUEST\s*(sir-[0-9a-f]+)/) do |m|
+      sir_id = m[1]
+      self.info[:ec2_sir_id] = sir_id
+      `ec2-create-tags #{sir_id} --tag 'Name=#{self.uuid}'`
+    end
     self.save!
   end
 
@@ -162,4 +182,12 @@ class Node < ActiveRecord::Base
       end
     end
   end
+
+  def permission_to_update
+    @bypass_orvos_authorization or super
+  end
+
+  def permission_to_create
+    current_user and current_user.is_admin
+  end
 end