295 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			295 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| require "lib/knife/knife_factory"
 | |
| require "lib/puts_and_flush"
 | |
| require "hooks"
 | |
| require_relative "server_executor/expiration_scheduler"
 | |
| require_relative 'server_executor/server_operation_result'
 | |
| require "lib/ssh/ssh_utils"
 | |
| require "exceptions/bootstrap_error"
 | |
| require "exceptions/deploy_error"
 | |
| require "exceptions/deploy_info_error"
 | |
| require "exceptions/creation_error"
 | |
| require "db/mongo/models/image"
 | |
| require "db/mongo/models/server"
 | |
| require "db/mongo/models/key"
 | |
| require "db/mongo/models/project"
 | |
| 
 | |
| module Devops
 | |
|   module Executor
 | |
|     class ServerExecutor
 | |
|       include Hooks
 | |
|       include PutsAndFlush
 | |
|       include ServerOperationResultCode
 | |
| 
 | |
|       # waiting for 5*60 seconds (5 min)
 | |
|       MAX_SSH_RETRIES_AMOUNT = 60
 | |
| 
 | |
|       define_hook :before_create
 | |
|       define_hook :after_create
 | |
| 
 | |
|       #params:
 | |
|       #  out - container for output data
 | |
|       define_hook :before_bootstrap
 | |
|       define_hook :after_bootstrap
 | |
| 
 | |
|       #before_deploy :add_run_list_to_deploy_info
 | |
| 
 | |
|       attr_accessor :server, :environment, :job_task, :project
 | |
|       attr_reader :out, :current_user
 | |
| 
 | |
|       def initialize server, out, current_user, options={}
 | |
|         if server
 | |
|           @project = Devops::Model::Project.find_with_environment(server.project, server.environment)
 | |
|           @environment = @project.environment(server.environment)
 | |
|           @category = @environment.get_category(server.category)
 | |
|           @server = server
 | |
|         end
 | |
|         @out = out
 | |
|         @out.class.send(:define_method, :flush) { } unless @out.respond_to?(:flush)
 | |
|         @current_user = current_user
 | |
|       end
 | |
| 
 | |
|       # TODO: refactore attr_accessors to constructor options,
 | |
|       # because these values don't change after object initializing
 | |
|       def job_task= task
 | |
|         @job_task = task
 | |
|       end
 | |
| 
 | |
|       def project= p
 | |
|         @project = p
 | |
|       end
 | |
| 
 | |
|       def environment= e
 | |
|         @environment = e
 | |
|       end
 | |
| 
 | |
|       def server
 | |
|         @server
 | |
|       end
 | |
| 
 | |
|       def category= category
 | |
|         @category = category
 | |
|       end
 | |
| 
 | |
|       def create_server_object options
 | |
|         Devops::Model::Server.new({
 | |
|           "project" => @project.id,
 | |
|           "environment" => @environment.id,
 | |
|           "category" => @category.id,
 | |
|           "created_by" => @current_user,
 | |
|           "provider" => @category.provider.name,
 | |
|           "provider_account" => @category.provider.account,
 | |
|           "private_ip" => options["private_ip"]
 | |
|         })
 | |
|       end
 | |
| 
 | |
|       def create_server options
 | |
|         @server = create_server_object(options)
 | |
|         @server.set_last_operation(Devops::Model::Server::OperationType::CREATION, @current_user, @job_task.id)
 | |
|         provider = @category.provider
 | |
|         @out.puts "Create server..."
 | |
|         @server.run_list = options["run_list"] || []
 | |
|         @server.ssh_key = options["ssh_key"] || provider.account_instance.ssh_key
 | |
|         @server.name = options["name"] || @server.provider_instance.create_default_server_name(@server)
 | |
| 
 | |
|         image = Devops::Model::Image.where(image_id: provider.image, provider: provider.name, provider_account: provider.account).first
 | |
|         @server.remote_user = image.remote_user
 | |
| 
 | |
|         res = {}
 | |
|         @out << "\nBefore create hooks...\n"
 | |
|         res[:before] = self.run_hook :before_create
 | |
|         @out << "Done\n"
 | |
| 
 | |
|         @out.puts "Running cloud instance...\nUsing provider '#{provider.name}' and account '#{provider.account}'\n"
 | |
|         @out.flush
 | |
| 
 | |
|         unless provider.create_server(@server, @out)
 | |
|           raise Devops::Exception::CreationError.new("Can not create server in cloud with provider '#{provider.name}' and account '#{provider.account}'")
 | |
|         end
 | |
|         @server.save
 | |
| 
 | |
|         provider.waiting_server @server, @out
 | |
|         @server.save
 | |
| 
 | |
|         @out << @server.info
 | |
|         @out.flush
 | |
| 
 | |
|         @out << "\n\nAfter create hooks...\n"
 | |
|         res[:after] = self.run_hook :after_create
 | |
|         @out << "Done\n"
 | |
|         @out.flush
 | |
|         DevopsLogger.logger.info "Server with parameters: #{@server.to_hash.inspect} is running"
 | |
| 
 | |
|         schedule_expiration()
 | |
|         0
 | |
|       end
 | |
| 
 | |
|       def deploy_info options
 | |
|         @server.set_last_operation(Devops::Model::Server::OperationType::DEPLOY_INFO, @current_user, @job_task.id)
 | |
|         options[:deploy_info] || @project.deploy_info(@environment)
 | |
|       end
 | |
| 
 | |
|       def bootstrap_server options, deploy_info
 | |
|         @server.set_last_operation(Devops::Model::Server::OperationType::BOOTSTRAP, @current_user, @job_task.id)
 | |
| =begin
 | |
| <<<<<<< HEAD
 | |
|             if @project.is_sandbox?
 | |
|               bootstrap_options[:deployers] = [options['created_by']]
 | |
|               bootstrap_options[:deployers] += (options['project_info']['deployers'] || []) if options['project_info']
 | |
|             end
 | |
| =======
 | |
| =end
 | |
| 
 | |
|         @job_task.bootstrap_info = @category.cm_tool.bootstrap_instance(@server, deploy_info, @out, options)
 | |
|         @job_task.save
 | |
|         @server.save
 | |
|         DevopsLogger.logger.info "Server #{@server.id} has been bootstraped with cm name '#{@server.cm_name}'"
 | |
|         0
 | |
|       end
 | |
| 
 | |
|       def deploy_server options, deploy_info
 | |
|         @server.set_last_operation(Devops::Model::Server::OperationType::DEPLOY, @current_user, @job_task.id)
 | |
|         deploy_info[:run_list] = compute_run_list
 | |
|         @job_task.deploy_info = deploy_info
 | |
|         @job_task.save
 | |
|         @category.cm_tool.deploy_instance(@server, deploy_info, @out)
 | |
|         DevopsLogger.logger.info "Server #{@server.id} has been deployed"
 | |
|         0
 | |
|       end
 | |
| 
 | |
|       def unbootstrap_server
 | |
|         res = @category.cm_tool.delete_instance(@server, @out)
 | |
|         res
 | |
|       end
 | |
| 
 | |
|       def deploy_server_with_tags tags, deploy_info
 | |
|         unless tags.empty?
 | |
|           deploy_info[tags: tags]
 | |
|         end
 | |
| 
 | |
|         @job_task.deploy_info = deploy_info
 | |
|         @job_task.save
 | |
|         r = @category.cm_tool.deploy_instance(@server, deploy_info, @out)
 | |
| 
 | |
|         @server.set_last_operation(Devops::Model::Server::OperationType::DEPLOY, @current_user, @job_task.id)
 | |
|         @server.save
 | |
|         return r
 | |
|       end
 | |
| 
 | |
|       def delete_server
 | |
|         if @server.static?
 | |
|           @out << "'#{@server.id}' is a static server"
 | |
|           @out.flush
 | |
|           unless @server.cm_name.nil?
 | |
|             unbootstrap_server
 | |
|           end
 | |
|           @server.delete
 | |
|           msg = "Static server '#{@server.id}' is removed"
 | |
|           DevopsLogger.logger.info msg
 | |
|           return msg, nil
 | |
|         end
 | |
|         puts_and_flush "'#{@server.id}' is not a static server"
 | |
|         puts_and_flush @server.to_hash
 | |
|         @category.cm_tool.delete_instance(@server, @out)
 | |
|         provider = @server.provider_instance
 | |
|         msg = nil
 | |
|         begin
 | |
|           msg = provider.delete_server @server
 | |
|         rescue Fog::Compute::OpenStack::NotFound, Fog::Compute::AWS::NotFound
 | |
|           msg = "Server with id '#{@server.id}' not found in '#{provider.name}' servers"
 | |
|           DevopsLogger.logger.warn msg
 | |
|         end
 | |
|         @out.puts msg
 | |
|         @server.delete
 | |
|         info = "Server '#{@server.id}' with name '#{@server.name}' for project '#{@server.project}-#{@server.environment}' is removed"
 | |
|         puts_and_flush info
 | |
|         DevopsLogger.logger.info info
 | |
|         return 0
 | |
|       end
 | |
| 
 | |
|       def roll_back
 | |
|         @out.puts "Trying to roll back..."
 | |
|         unless @server.id.nil?
 | |
|           unless @server.name.nil?
 | |
|             @out.puts "Server '#{@server.name}' with id '#{@server.id}' is not created"
 | |
|             @category.cm_tool.delete_instance(@server, @out)
 | |
|           end
 | |
|           begin
 | |
|             @out.puts @server.provider_instance.delete_server(@server)
 | |
|           rescue => e
 | |
|             @out.puts e.message
 | |
|           end
 | |
|           @out << "\nRolled back\n"
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       def add_run_list_to_deploy_info out, deploy_info
 | |
|         out << "\nGenerate run list hook...\n"
 | |
|         if deploy_info["run_list"]
 | |
|           out << "Deploy info already contains 'run_list': #{deploy_info["run_list"].join(", ")}\n"
 | |
|           return
 | |
|         end
 | |
|         out << "Project run list: #{@project.run_list.join(", ")}\n"
 | |
|         out << "Deploy environment run list: #{@environment.run_list.join(", ")}\n"
 | |
|         out << "Server run list: #{@server.run_list.join(", ")}\n"
 | |
|         deploy_info["run_list"] = compute_run_list
 | |
|         out << "New deploy run list: #{deploy_info["run_list"].join(", ")}\nRun list has been generated\n\n"
 | |
|       end
 | |
| 
 | |
|       def compute_run_list
 | |
|         rlist = []
 | |
|         [@server.provider_instance.run_list, @project.run_list, @environment.run_list, @server.run_list].each do |sub_run_list|
 | |
|           rlist += sub_run_list if sub_run_list.is_a?(Array)
 | |
|         end
 | |
|         if @server.stack
 | |
|           #TODO
 | |
|           stack = Devops::Db.connector.stack(@server.stack)
 | |
|           srl = stack.run_list
 | |
|           rlist += srl if srl
 | |
|         end
 | |
|         rlist.uniq
 | |
|       end
 | |
| 
 | |
|       def waiting_ssh
 | |
|         ip = @server.private_ip
 | |
|         unless @server.public_ip.nil?
 | |
|           ip = server.public_ip
 | |
|           @out.puts "\nPublic IP is present"
 | |
|         end
 | |
| 
 | |
|         @out << "Waiting for SSH"
 | |
|         k = Devops::Model::Key.find(@server.ssh_key)
 | |
| 
 | |
|         retries_amount = 0
 | |
|         begin
 | |
|           sleep(5)
 | |
|           res = Devops::SSH::Utils.try_ssh(ip, @server.remote_user, k.path)
 | |
|           if res
 | |
|             @out.puts "\n"
 | |
|             @out.flush
 | |
|             return true
 | |
|           else
 | |
|             retries_amount += 1
 | |
|             if retries_amount > MAX_SSH_RETRIES_AMOUNT
 | |
|               raise Devops::Exception::BootstrapError.new("Can not connect to #{@server.remote_user}@#{ip}")
 | |
|             end
 | |
|             raise ArgumentError.new
 | |
|           end
 | |
|         rescue ArgumentError => e
 | |
|           @out << "."
 | |
|           @out.flush
 | |
|           retry
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       private
 | |
|       def schedule_expiration
 | |
|         if @environment.expires
 | |
|           job_id = ExpirationScheduler.new(@environment.expires, @server).schedule_expiration!
 | |
|           puts_and_flush "Planning expiration in #{@environment.expires}, job_id: #{job_id}"
 | |
|         end
 | |
|       end
 | |
| 
 | |
|     end
 | |
|   end
 | |
| end
 | 
