391 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
		
		
			
		
	
	
			391 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
|   | 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 | ||
|  | 
 |