require "uri" require "commands/status" require "commands/bootstrap_templates" require 'exceptions/conflict_exception' require "lib/executors/server_executor" require "db/mongo/models/server" require "workers/create_server_worker" require "workers/delete_server_worker" require "workers/bootstrap_worker" require "workers/unbootstrap_worker" require "app/api3/parsers/server" require_relative "request_handler" module Devops module API3 module Handler class Server < RequestHandler set_parser Devops::API3::Parser::ServerParser extend StatusCommands extend BootstrapTemplatesCommands def servers Devops::Model::Server.where(parser.servers).all end def provider_servers provider provider_servers_with_account provider, nil end def provider_servers_with_account provider, account ::Provider.get_connector(provider, account).servers end def server id Devops::Model::Server.find(id) rescue Mongoid::Errors::DocumentNotFound raise Devops::Exception::RecordNotFound.new("Server '#{id}' not found") end # ids - array of servers ids # returns array of jid def delete ids user = parser.current_user jids = [] ids.each do |id| DevopsLogger.logger.debug("Trying to delete server '#{id}'") begin s = server(id) Devops::Model::Project.check_user_authorization(s.project, s.environment, user) jid = Worker.start_async(DeleteServerWorker, server_id: s.id, user: user ) jids << jid rescue Devops::Exception::RecordNotFound, Devops::Exception::Unauthorized => e DevopsLogger.logger.warn(e.message) end end jids end def server_id_by_name name s = Devops::Model::Server.find_by(name: name) s.id rescue Mongoid::Errors::DocumentNotFound raise Devops::Exception::RecordNotFound.new("Server with name '#{name}' not found") end # ids - array of servers ids def delete_stream ids, out user = parser.current_user ids.each do |id| begin s = server(id) ### Authorization Devops::Model::Project.check_user_authorization(s.project, s.environment, user) Devops::Executor::ServerExecutor.new(s, out).delete_server rescue Devops::Exception::RecordNotFound out.puts "Server '#{id}' not found" rescue Devops::Exception::Unauthorized out.puts "User '#{user}' can not delete server '#{id}'" end end end def create_server_stream out status = [] prepare_create_server do |project, env, user, body| e = Devops::Executor::ServerExecutor.new(nil, out) e.project = project e.environment = env body["created_by"] = user res = e.create_server(body) status.push res end status end def create_server body = parser.create user = parser.current_user check_if_server_attrs_are_valid(body, user) jid = Worker.start_async(CreateServerWorker, server_attrs: body, owner: user ) [jid] end def deploy id call_deploy(id) do |options| [ Worker.start_async(DeployWorker, options) ] end end def call_stream out yield rescue Devops::Exception::RecordNotFound => e out << e.message NOT_FOUND rescue Devops::Exception::ConflictError => e out << e.message CONFLICT rescue Devops::Exception::Unauthorized => e out << e.message UNAUTHORIZED end def deploy_stream id, out call_stream(out) do call_deploy(id) do |options| Worker.start_sync(DeployWorker, options, out) end end end def call_deploy id body = parser.deploy tags = body["tags"] || [] run_list = body["run_list"] owner = parser.current_user s = server(id) project = Devops::Model::Project.check_user_authorization(s.project, s.environment, owner) deploy_info = create_deploy_info(s, project, body["build_number"]) deploy_info["run_list"] = run_list if run_list yield({ server_id: id, owner: owner, tags: tags, deploy_info: deploy_info }) end def create_deploy_info server, project, build_number env_model = project.environment(server.environment) project.deploy_info(env_model, build_number) end def pause_server id s = Devops::Model::Server.find(id) ## Authorization Devops::Model::Project.check_user_authorization(s.project, s.environment, parser.current_user) provider = s.provider_instance r = provider.pause_server s if r.nil? set_last_operation_and_save(s, Devops::Model::Server::OperationType::PAUSE) "Server with instance ID '#{s.id}' has been paused" else raise Devops::Exception::ConflictError.new("Server with instance ID '#{s.id}' can not be paused, It in state '#{r}'") end end def unpause_server id s = Devops::Model::Server.find(id) ## Authorization Devops::Model::Project.check_user_authorization(s.project, s.environment, parser.current_user) provider = s.provider_instance r = provider.unpause_server s if r.nil? set_last_operation_and_save(s, Devops::Model::Server::OperationType::UNPAUSE) "Server with instance ID '#{s.id}' has been unpaused" else raise Devops::Exception::ConflictError.new("Server with instance ID '#{s.id}' can not be unpaused, It in state '#{r}'") end end def reserve_server id s = Devops::Model::Server.find(id) user = parser.current_user Devops::Model::Project.check_user_authorization(s.project, s.environment, user) raise Devops::Exception::ConflictError.new("Server '#{id}' already reserved") unless s.reserved_by.nil? s.reserved_by = user set_last_operation_and_save(s, Devops::Model::Server::OperationType::RESERVE) end def unreserve_server id s = Devops::Model::Server.find(id) user = parser.current_user Devops::Model::Project.check_user_authorization(s.project, s.environment, user) raise Devops::Exception::ConflictError.new("Server '#{id}' is not reserved") if s.reserved_by.nil? s.reserved_by = nil set_last_operation_and_save(s, Devops::Model::Server::OperationType::UNRESERVE) end # TODO: check bootstrap template name def bootstrap_server_stream id, out call_stream(out) do call_bootstrap(id) do |options| Worker.start_sync(BootstrapWorker, options, out) end end end def bootstrap_server(id) call_bootstrap(id) do |options| Worker.start_async(BootstrapWorker, options) end end def call_bootstrap id body = parser.bootstrap name = body["cm_name"] rl = body["run_list"] t = body["bootstrap_template"] s = server(id) user = parser.current_user p = Devops::Model::Project.check_user_authorization(s.project, s.environment, user) raise Devops::Exception::ConflictError.new("Server '#{id}' already bootstrapped") unless s.cm_name.nil? attrs = { server_id: id, owner: user } d = p.environment(s.environment) provider = s.provider_instance if name d.get_category(s.category).cm_tool.check_node_name(name) attrs[:cm_name] = name end unless t.nil? templates = get_templates raise ValidationError.new("Invalid bootstrap template '#{t}', available values: #{templates.join(", ")}") unless templates.include?(t) attrs[:bootstrap_template] = t end attrs[:run_list] = rl if rl yield(attrs) end def unbootstrap_server id call_unbootstrap(id) do |options| Worker.start_async(UnbootstrapWorker, options) end end def unbootstrap_server_stream id, out call_stream(out) do call_unbootstrap(id) do |options| Worker.start_sync(UnbootstrapWorker, options) end end end def call_unbootstrap id s = server(id) user = parser.current_user ### Authorization Devops::Model::Project.check_user_authorization(s.project, s.environment, user) raise Devops::Exception::ConflictError.new("Server '#{id}' is not bootstrapped") if s.cm_name.nil? attrs = { server_id: id, owner: parser.current_user } yield(attrs) end def add_server body = parser.add_server user = parser.current_user project = Devops::Model::Project.check_user_authorization(body[:project], body[:environment], user) env = project.environment(body[:environment]) category = env.get_category!(body[:category]) raise 'Provider should be static' if category.provider.name != 'static' provider_account = Model::ProviderAccount.find(category.provider.account) provider = ::Provider.provider('static') Devops::Model::Server.create!( "provider" => provider.name, "provider_account" => provider_account.id, "project" => project.id, "environment" => env.id, "category" => category.id, "remote_user" => body[:remote_user], "private_ip" => body[:private_ip], "public_ip" => body[:public_ip], "id" => "static_#{provider_account.ssh_key}-#{Time.now.to_i}", "ssh_key" => provider_account.ssh_key, 'created_by' => user, 'cm_name' => nil, 'name' => body[:name] ) end def set_tags id tags = parser.tags prepare_tags(id) do |provider| provider.set_tags id, tags end end def unset_tags id tags = parser.tags prepare_tags(id) do |provider| provider.unset_tags id, tags end end def prepare_tags id s = Devops::Model::Server.find(id) p = Devops::Model::Project.check_user_authorization(s.project, s.environment, parser.current_user) provider = s.provider_instance yield provider end def set_run_list id s = Devops::Model::Server.find(id) Devops::Model::Project.check_user_authorization(s.project, s.environment, parser.current_user) s.set(run_list: parser.run_list) end private def check_if_server_attrs_are_valid(body, user) if body["project"].nil? or body["environment"].nil? or body["category"].nil? raise Devops::Exception::ValidationError.new("Properties 'project', 'environment' and 'category' are mandatory") end key_name = body["ssh_key"] unless key_name.nil? begin Devops::Model::Key.find(key_name) rescue Mongoid::Errors::DocumentNotFound raise Devops::Exception::ValidationError.new("Key with id '#{key_name}' not found") end end project = Devops::Model::Project.check_user_authorization(body["project"], body["environment"], user) env = project.environment(body["environment"]) category = env.get_category(body["category"]) if body["name"] begin Devops::Model::Server.find_by(name: body["name"]) raise Devops::Exception::ValidationError.new("Server with name '#{body["name"]}' already exists" ) rescue Mongoid::Errors::DocumentNotFound category.provider.provider_instance.check_node_name(body["name"]) category.cm_tool.check_node_name(body["name"]) end end =begin provider = env.provider_instance server_name = body["name"] check_chef_node_name(server_name, provider, KnifeFactory.instance) unless server_name.nil? groups = body["groups"] unless groups.nil? buf = groups - provider.groups.keys halt_response("Invalid security groups '#{buf.join("', '")}' for provider '#{provider.name}'") if buf.empty? end =end end def set_last_operation_and_save(server, operation_type) server.set_last_operation(operation_type, parser.current_user, nil) server.save end end end end end