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
 | |
| 
 | 
