fluke/devops-service/lib/executors/server_executor.rb
Tim Lianov 03dc3d8d99 v3
2018-04-04 22:44:39 +03:00

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